51 lines
3.8 KiB
Markdown
51 lines
3.8 KiB
Markdown
### 对比一下 QEMU 模式的 Fuzzing 和源码模式的 Fuzzing
|
|
|
|
**1. 源码模式 Fuzzing**
|
|
|
|
源码模式 Fuzzing(也称为**插桩式 Fuzzing**)是在编译时对目标程序进行修改,插入额外的代码(即“插桩”)。这些桩点会在程序运行时收集代码覆盖率等信息,并反馈给 Fuzzer,指导其生成更有效的输入
|
|
|
|
**工作原理**
|
|
|
|
- **编译插桩**:Fuzzer 使用专门的编译器前端(如 AFL-Clang 或 LLVM-sanitizer)来编译目标程序的源码
|
|
- **插入探针**:编译器会在每个基本块(Basic Block)的开头插入一个探针。当程序执行到一个新的基本块时,探针会向 Fuzzer 反馈这个信息
|
|
- **反馈循环**:Fuzzer 根据这些覆盖率信息,判断哪些输入探索了新的代码路径。它会保留这些有价值的输入,并对其进行变异,以期能找到更深层次的代码逻辑
|
|
|
|
**优点**
|
|
|
|
- **高效**:插桩非常轻量级,几乎不会引入额外的性能开销。Fuzzer 能够以极高的速度运行和测试样本,每秒可达数千甚至数万次
|
|
- **精确的代码覆盖率**:由于插桩在编译时完成,Fuzzer 能够获得非常精确的、基本块级别的代码覆盖率,这使得它能够更有效地探索代码路径
|
|
- **直接定位崩溃点**:由于 Fuzzer 能够知道输入触发了哪段代码,一旦发生崩溃,它能迅速定位到崩溃发生的基本块
|
|
|
|
**缺点**
|
|
|
|
- **需要源码**:这是最大的局限性。如果目标程序是闭源的,你就无法使用这种方法
|
|
- **编译复杂**:对于复杂的项目,编译过程可能会很复杂,需要处理各种依赖和编译选项
|
|
|
|
**2. QEMU 模式 Fuzzing**
|
|
|
|
QEMU 模式 Fuzzing(也称为**黑盒或二进制 Fuzzing**)是在**二进制级别**进行插桩和监控。它使用 QEMU 模拟器来运行目标程序,并通过修改 QEMU 的代码来收集代码覆盖率信息。
|
|
|
|
**工作原理**
|
|
|
|
- **二进制插桩**:Fuzzer 启动一个修改过的 QEMU 用户模式模拟器
|
|
- **动态翻译**:QEMU 在运行目标程序时,会动态地将目标程序的机器码翻译成宿主机的机器码。在翻译过程中,Fuzzer 的插桩逻辑会被嵌入到生成的代码中
|
|
- **收集覆盖率**:当翻译后的代码执行时,插桩逻辑会收集代码覆盖率信息,并反馈给 Fuzzer。
|
|
|
|
**优点**
|
|
|
|
- **无需源码**:这是 QEMU 模式最大的优势。它能够 Fuzz 任何闭源的、可执行的二进制文件,这在分析恶意软件或商业软件时至关重要
|
|
- **全系统覆盖**:除了用户态程序,QEMU 还可以模拟整个系统。这意味着你可以用它来 Fuzz 驱动程序或内核
|
|
|
|
**缺点**
|
|
|
|
- **性能开销大**:由于 QEMU 是一个模拟器,它会引入大量的性能开销。Fuzzing 速度比源码模式慢得多,通常每秒只能运行几十到几百次
|
|
- **覆盖率信息粗糙**:QEMU 模式通常只能提供基本块级别的覆盖率,但可能无法像源码模式那样精确地追踪到每一条指令的执行
|
|
- **不稳定性**:由于 QEMU 本身的复杂性,Fuzzing 过程中可能会出现一些不稳定的情况
|
|
|
|
| 特性 | 源码模式 Fuzzing | QEMU 模式 Fuzzing |
|
|
| ------------ | ---------------------- | ---------------------------------------- |
|
|
| 是否需要源码 | 是 | 否 |
|
|
| 性能 | 极高(每秒数千次) | 较低(每秒数十次) |
|
|
| 代码覆盖率 | 非常精确(基本块级别) | 较精确(基本块级别) |
|
|
| 适用场景 | 开源项目、内部代码审计 | 闭源软件、恶意软件分析、驱动程序 Fuzzing |
|
|
| 代表工具 | AFL++、LibFuzzer | AFL-QEMU | |