### 讲一下 AFL 的插桩原理 **插桩的本质:反馈导向的模糊测试** AFL 的插桩(instrumentation)就是一套用于收集**代码覆盖率**的探针。通过这些探针,AFL 可以知道某个输入执行了哪些代码路径 它的工作流程是这样的: 1. **编译时插桩**:用 AFL 提供的特殊编译器(`afl-clang-fast` 或 `afl-gcc`)来编译目标程序 2. **运行时反馈**:当程序执行时,插桩代码会向 AFL 反馈代码执行路径信息 3. **智能变异**:AFL 根据这些反馈,判断哪些输入“有价值”(即探索了新的代码路径),然后对这些有价值的输入进行更多的变异 这个反馈循环是 AFL 高效的关键。它使得 AFL 能够自动绕过复杂的输入校验,深入到程序更深层次的逻辑中,从而找到隐藏的漏洞 **插桩的原理实现** AFL 的插桩非常轻量级,它采用了一种基于**基本块(Basic Block)**的简单而巧妙的方案。 **1. 什么是基本块?** 在程序中,一个基本块是一段连续的代码,它只有一个入口点(第一条指令)和一个出口点(最后一条指令),且中间没有任何分支跳转 你可以把基本块看作是代码中的最小“执行单元” **2. AFL 的插桩步骤** AFL 在编译时,会在**每个基本块的入口**插入一段代码。这段代码会做两件事: - **获取当前基本块的 ID**:AFL 在编译时会给每个基本块分配一个唯一的随机 ID - **记录基本块的 ID**:AFL 维护一个**共享内存区域**,通常是一个大小为 64KB 的位图(bitmap) 当程序执行到一个新的基本块时,插入的代码会执行以下操作: 1. 获取当前基本块的 ID(假设是 `current_id`) 2. 获取**上一个**执行的基本块的 ID(假设是 `prev_id`)。AFL 用一个全局变量来保存这个 `prev_id` 3. 计算一个哈希值:`index = current_id XOR prev_id` 4. 将这个 `index` 映射到位图的某个位置,并将该位置的值加 1 5. 更新 `prev_id`,使其等于 `current_id`