AFL 源码阅读——part 2 afl-tmin、afl-showmap 和 afl-analyze
tmin
通过这一部分代码可以看到,共享内存里面存放的 bitmap 数据是怎么被用来统计覆盖率的。
afl-tmin 的背景
这是一个用来精简输入样例的工具 (trimming)。
- 为什么需要精简输入
大的输入执行起来速度比较慢,而且如果文件太大了,那么编译更可能发生在不重要的部分。
两种模式
有两种模式来进行精简,Non-Crashing Input Mode
能够在路径完全相同的情况下尽量减少输入大小,而 Crashing Input Mode 可以精简能够制造 crash 的 poc 代码。
Crashing Input Mode | Non-Crashing Input Mode | |
---|---|---|
目的 | 找到可以稳定复现崩溃的文件,不在乎路径是否变化 | 找到 保持执行路径完全相同的更小文件 |
例子 | 将 10 MB 的崩溃 PoC 文件精简为 100 字节,仍能触发同一漏洞 | 将 1 MB 的 JPEG 测试用例精简为 50 KB,且仍覆盖相同的图像解析逻辑 |
是否需要插桩 | 不用插桩 | 需要 |
不仅仅是 afl-tmin,afl-fuzz 也内置了精简器:以一些固定长度尝试删除数据块,如果删除之后的路径 checksum 保持不变,就认为可以删减。
四阶段算法
tmin 采用四阶段算法来删减数据,四个阶段删减数据的粒度从大到小,从删减粗粒度数据,到删减极细的字节大小的粒度,达到 trimming 的效果。
- 粗粒度归零
大刀阔斧简化数据,使用大块步长将数据归零,这是为了减少后续细粒度操作次数。
- 逐级细化删除
以递减的块长和步长进行块删除,像「二分查找」一样,不断缩小切割范围,直到无法再切。
- 字母表归一化
统计唯一字符,尝试批量替换为 '0'
(ASCII 字符 0x30),'0'
在文本格式中(如配置文件、日志)通常不会破坏结构