首先从queue获取测试样例(fn == "id:000000,orig:case"),然后读取之后调用calibrate_case函数参数说明:argv: 传递给目标程序的命令行参数数组q: 指向当前测试用例的队列条目use_mem: 测试用例的内容(内存中)handicap: 测试用例的性能惩罚值from_queue: 标志位,表示是否从队列中调用(而非初始处理)返回值:FAULT_NONE: 正常执行FAULT_TMOUT: 执行超时FAULT_CRASH: 程序崩溃FAULT_ERROR: 执行错误FAULT_NOINST: 未检测到插桩FAULT_NOBITS: 未产生新的覆盖位一些变量:first_trace: 存储第一次运行时的覆盖率信息fault: 执行结果状态new_bits: 是否发现新的覆盖位var_detected: 是否检测到变异行为hnb: 临时存储 has_new_bits 的结果first_run: 是否是第一次运行此测试用例start_us, stop_us: 记录执行时间保存当前阶段信息,以便后续恢复这里初始化forkserver如果测试用例已有校验和,那么直接覆盖然后检查是否有新的覆盖位记录开始和结束的时间如果不是首次运行,定期更新统计信息将测试用例写入文件运行目标程序并获取执行结果如果收到停止信号或执行结果与崩溃模式不符,中止校准如果是第一次循环且未检测到覆盖位,设置 FAULT_NOINST 并中止校准如果已有校验和但当前校验和不同,则逐位比较覆盖位图,标记变异位置,并延长校准循环之后会计算统计的数据如果是首次运行,且未产生新的覆盖位,就会标记为FAULT_NOBITS如果产生了新的路径覆盖,则mark如果有变异行为,计算变异字节数,并mark测试用例为变异行为总的来说就是:运行目标程序多次(通常是8次,由 CAL_CYCLES 定义)收集执行信息,如执行时间、代码覆盖率等检测程序行为是否稳定(每次运行是否产生相同的覆盖率)返回执行结果状态(如正常、超时、崩溃等)该函数是AFL优化算法的第一阶段,负责为每个覆盖率位置维护一个"最优"测试用例评分因子=执行时间*测试用例文件的大小评分改变只在以下情况发生:新的覆盖率位置:当 trace_bits[i] != 0 且 top_rated[i] == NULL即发现了全新的代码覆盖路径更优的测试用例:现有位置有竞争者,但新测试用例表现更好new_fav_factor < old_fav_factor减少旧的用例的引用次数,如果为0(就是bitmap上每一个路径都不是这个旧的用例的最佳用例),就会释放trace_mini,然后标记trace_mini=0,然后如果新的最佳没有trace_mini就会分配一个,然后将64KB的覆盖率信息压缩为8KB的位向量参数是char **argv指向目标程序的完整路径以及一些参数创建两个管道用户进程间的通信st_pipe: 状态管道,fork server → AFL主进程ctl_pipe: 控制管道,AFL主进程 → fork server确保有足够的文件描述符不同系统使用不同的内存限制类型核心转储影响性能,且在模糊测试中不需要,因此禁用了将管道映射到固定的文件描述符198和199上在程序启动时解析所有符号,避免fork后的链接工作然后就直接执行execv函数执行完之后这个新的进程就会替换掉这个子进程,但是文件描述符、PID以及相应的资源限制都会继承如果当时是对test程序进行fuzz的话,此时target_path和argv都会指向该程序的绝对路径因此这个子进程就会变成一个test进程execv参数设置:设置管道的端点握手等待成功会这样超时a4是这个函数的唯一标识符,也可以说是一个时间戳的随机数连接共享内存基于边的覆盖位图跟踪:如果是盲fuzz模式或者评分没有改变(改变就是出现了新的路径或者有一个更好的抵达某个路径的测试用例的出现)就会直接return将 temp_v 初始化为全1,表示所有覆盖率位置都需要被覆盖,重置所有测试用例的 favored 标记贪心选择算法,如果bitmap上的某一个位置有最佳的测试用例而且该位置也未被覆盖,将该测试用例标记位优选,然后从temp_v中移除该测试用例能覆盖到的所有位置将未被标记为优选的测试用例标记为冗余被标记为优选的测试样例会被优先变异最小/最大执行时间 min_us/max_us最小/最大位图大小 min_bits/max_bits最大输入长度 max_len平均执行时间 avg_us = total_cal_us / total_cal_cyclesqemu模式下的平均执行时间给的更加宽松,也能看出qemu模式下的fuzz效率更低平均执行时间更慢则降低变异强度(增大 havoc_div)在非恢复对话下,给出一些样本的可读性建议根据平均执行时间/最大执行时间推导出较佳的exec_tmout如果是盲fuzz且未设环境变量则设定更保守的挂起时间恢复会话模式下用于寻找从队列中的哪个位置开始继续测试读取fuzzer_stats文件下的最多4095字节到缓冲区,然后解析cur_path : 字符串(strstr返回cur_path出现的第一个字符的指针),提取冒号后的数值(源码上是通过偏移20来获取的,但是该字符串就是20,就是提取紧接着的数值)在程序main函数的参数是(0,0,0)统计的信息: 时间相关:start_time: 开始时间(Unix时间戳)last_update: 最后更新时间exec_timeout: 执行超时设置执行统计:execs_done: 总执行次数execs_per_sec: 每秒执行数cycles_done: 完成的队列循环数路径发现:paths_total: 队列中总路径数paths_favored: 优选路径数paths_found: 本地发现的路径paths_imported: 从其他实例导入的路径覆盖率与稳定性:bitmap_cvg: 位图覆盖率stability: 执行稳定性variable_paths: 表现不稳定的测试案例数崩溃与挂起:unique_crashes: 唯一崩溃数unique_hangs: 唯一挂起数last_crash/last_hang: 最后崩溃/挂起时间只保存前MIN(USE_AUTO_EXTRAS, a_extras_cnt)个auto extrasAuto extras 是 AFL 在模糊测试过程中自动发现的有价值的字节序列,这些序列:在测试用例中频繁出现可能触发程序的不同执行路径用于后续的变异操作中,提高发现新路径的效率首先调用cull_queue函数找出最优样例集合,当queue_cur为空时,表示完成了一轮队列遍历如果完整一轮队列循环后没有发现新路径(queued_paths == prev_queued):如果有新发现,重置无发现计数器如果设置了AFL_IMPORT_FIRST且是首次循环,则与其他fuzzer实例同步fuzz_one函数是AFL的核心,对当前队列条目执行各种变异策略:位翻转(bit flips)字节翻转(byte flips)算术运算
...(已截断)
---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-288072.htm
[原创]AFL源码分析(3)
203 浏览
4 回复
可以私聊吗
mb_yqsvfqmf
可以私聊吗
有问题可以直接问
可以私聊吗
有问题可以直接问
s1nec-1o
有问题可以直接问
接私活吗
有问题可以直接问
接私活吗
mb_yqsvfqmf
接私活吗
?
接私活吗
?