一篇毫不负责的afl-fuzz源码学习指北

read_testcases

nl_cnt是统计inputs目录下的目录数,然后从这些目录中读取测试用例加入

cull_queue

这个函数好像也没看懂是啥。。

先把队列中的实体favored全置为0

然后。。。

好像是精简输入数目

calculate_score

计算得分(对于输入的fuzz次数)

run_target

执行目标应用,监控timeouts。返回状态信息,调用的程序会更新trace_bits[]

calibrate_case

从函数名直接翻译是校准用例。函数注释说的是:
校准一个新的测试用例,在处理输入字典时尽早警告有关测试用例的问题;并且在发现新路径时检测变量行为等。

这个函数里面调用了update_bitmap_score

虽然看完我还是不知道是干啥的

update_bitmap_score

当发现一个新路径时,调用这个方法判断这个路径是否比已有路径more “favorable”。这个”favorables”的意图是最小化路径的集合来触发至今已发现到的bitmap的bits

save_if_interesting

检查fuzzing期间execve()的结果是否interesting,如果是则保存或把输入放到队列中,返回1,反之返回0

技术细节

详见technical_details.txt

4 Culling the corpus

算法根据执行延迟和文件大小赋予队列中的每个实体一个得分;然后选择每个tuple得分最低的候选:

  1. 找到下一个没有在temporary working set中的tuple
  2. 定位这个tuple的winning queue entry
  3. working set中注册该entry中所有的tuples
  4. 如果还有working set中还有遗漏的tuples,则返回1.

通常用这种方法生成的”favored”种子集合比原来的集合小5-10倍。剩下的”Non-favored”不是直接弃用,而是以一定概率跳过:

  • 如果有新的,yet-to-be-fuzzed的种子在队列中,那么non-favored entries以99%概率跳过该种子
  • 若没有新的
    • 若当前non-favored之前fuzz过,则95%的概率跳过该种子
    • 否则,以75%的概率跳过该种子

实证证明,这个策略实现了队列循环速度和种子多样性的平衡

5 剪枝输入

trim不保证输入剪到最优,而且在精度和execve()数之间找到一个均衡点,保证tracemap的checksum不受影响。

剪枝算法具体为:

  1. 尝试把大块的数据比特置为0(zero large blocks of data with large stepovers),在之后这可以减少执行的次数
  2. 通过不断减少块大小和步长尝试块删除,二进制搜索风格
  3. 通过统计unique characters进行alphabet normalization和尝试批量替换零值
  4. 最后,尝试在non-zero的字节上进行normalization

这里zero过程不用0x00,afl-min使用ASCII字符’0’。这样做的原因是修改一般不会干扰文本解析。

6 Fuzzing策略

确定性的策略包括:

  • 位翻转,使用不同的长度和步长
    • 翻转单个bit
    • 一行翻转两个bit
    • 一行翻转四个bit
  • 加和减一个小整数
  • 插入一个有趣的整数(0,1,INT_MAX等)

使用这些确定性变异原因是non-crashing和crashing输入间可能只有微小的差距

非确定性的步骤包括stacked位翻转,插入,删除,算数(加/减)和不同测试用例的拼接

8 crashes去重

仅仅查看faulting address可能导致完全不相关的两个issue聚类到一起了,因为fault发生在同一个公共的哭函数中(比如strcmpstrcpy等)

在AFL中,认为crash是unique的当下面任意一个条件成立:

  1. crash trace包含了一个之前没有出现过的tuple
  2. crash遗漏了一个之前经常出现的tuple

9 调查crashes

afl-fuzz提供crash exploration mode对已知错误的测试输入进行fuzz

与普通的queue entries不同,crashing inputs不会trim,他们会保持原状方便与parent比较(non-crashing entry)。

参考文献:afl-fuzz: crash exploration mode

crash exploration mode下,可以让程序接收一个crashing test case,afl-fuzz从crashing的种子出发开始跑。然后程序就会看在保持程序crash的状态下能运行多远。让程序停止产生crash的变异会被排除。