最近winafl
增加支持对Intel PT
的支持的,但是只支持x64
,且覆盖率计算不全,比如条件跳转等,所以它现在还是不如直接用插桩去hook的方式来得准确完整,这里主要想分析也是基于 DynamoRIO
插桩的覆盖率反馈原理。
之前曾有人在《初识 Fuzzing 工具 WinAFL》(https://paper.seebug.org/323/#32)中“3.2.2 插桩模块”一节中简单分析过其插桩原理,但没有找到我想要的答案,因此只好自动动手分析下源码。
比如,我想知道:
DynamoRIO
的覆盖率信息是如何同步给fuzzer主进程的?第3个问题发现已经有人分析过afl
,可以参见这里《AFL内部实现细节小记》(http://rk700.github.io/2017/12/28/afl-internals/),简单总结下:
u32 cksum **=** hash32(trace_bits, MAP_SIZE常量, HASH_CONST常量);
)是否发生变化来实现的
3. 在插桩模块winafl.dll中打开前面创建的命名管道,然后通过管道与fuzzer主进程进行交互:
4. 当插桩模块winafl.dll监测到程序首次运行至目标函数入口时,pre_fuzz_handler
函数会被执行,然后通过管道写入'P'命令,代表开始进入目标函数,afl-fuzz.exe进程收到命令后,会向目标进程写入管道命令'F',并监测超时时间和循环调用次数。afl-fuzz.exe与目标进程正是通过读写管道命令来交互的,主要有'F'(退出目标函数)、'P'(进入目标函数)、'K'(超时中断进程)、'C'(崩溃)、'Q'(退出进程)。覆盖信息通过文件映射方法(内存共享)写入winafl_data.afl_area
:
此步的关键就在于进入目标函数前调用的pre_fuzz_handler
函数,以及函数退出后调用的post_fuzz_handler
函数。
进入pre_fuzz_handler
函数时,winafl.dll会先获取以下信息
其中内存上下文信息支持各平台的寄存器记录:
接下来就是获取和设置fuzzed的目标函数参数:
当目标函数退出后,执行post_fuzz_handler
函数,会恢复栈顶指针和pc地址,以此实现目标函数的循环调用:
总结下整个winafl
执行流程:
drmgr_register_bb_instrumentation_event
去设置BB执行的回调函数,通过instrument_bb_coverage
或者instrument_edge_coverage
来记录覆盖率情况,如果发现新的执行路径,就将样本放入队列目录中,用于后续文件变异,以提高代码覆盖率;pre_fuzz_handler
来存储上下文信息,包括寄存器和运行参数;post_fuzz_handler
函数,记录恢复上下文信息,以执行回原目标函数,又回到第2步;