38 lines
2.1 KiB
Markdown
38 lines
2.1 KiB
Markdown
### 讲一下 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` |