如何让QEMU-AFL跟踪被模糊测试进程fork出的子进程?
我查阅AFL和QEMU文档很久,仍未找到答案:能否让QEMU-AFL(即AFL的QEMU分支,运行afl-fuzz -Q时启用)跟踪被模糊测试进程创建的fork子进程?
举个简单场景:我有parent和child两个二进制文件,parent通过fork + exec启动child;child从stdin读取字符,若第一个字符为'c'则触发崩溃,且崩溃会传播到parent。
当我执行命令:
afl-fuzz -i in/ -o out/ -Q -- ./parent
得到如下输出:
last new find : none yet (odd, check syntax!)
这说明AFL并未实际跟踪子进程的代码覆盖。已知插桩二进制可以实现子进程跟踪,因此推测限制来自QEMU-AFL而非AFL本身。请问有没有办法让QEMU-AFL(或QEMU)跟踪fork出的子进程?
parent.c代码:
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> int main(int argc, char* argv[]) { printf("Hello from the parent!\n"); pid_t new_pid = fork(); if (new_pid == 0) { execve("./child", argv, NULL); return 0; } // 等待子进程结束 int child_retcode = 0; while (wait(&child_retcode) > 0); return child_retcode; }
child.c代码:
#include <stdio.h> #include <stdlib.h> int main(int argc, char* argv[]) { printf("Hello from the child!\n"); char c1 = getchar(); char c2 = getchar(); if (c1 == 'c') { printf("Oh no, we're crashing!\n"); abort(); } return 0; }
解决方案
默认情况下,QEMU-AFL不会自动跟踪fork+exec创建的子进程,因为QEMU用户态实例默认绑定初始启动的进程。以下是几种可行的解决方法:
1. 启用AFL_QEMU_ALLOW_EXEC环境变量
这是最简单的方法,通过设置该环境变量,让QEMU-AFL允许跟踪exec替换后的新进程:
AFL_QEMU_ALLOW_EXEC=1 afl-fuzz -i in/ -o out/ -Q -- ./parent
该变量会触发QEMU-AFL在目标进程调用exec时,重新初始化插桩逻辑,从而跟踪子进程的代码覆盖。
2. 修改父进程,让子进程通过QEMU-AFL启动
如果方案1不生效,可以修改parent.c中的execve调用,直接用AFL++提供的afl-qemu-trace二进制启动子进程:
// 替换原有的execve行,注意替换为你系统中afl-qemu-trace的实际路径 execve("/usr/local/bin/afl-qemu-trace", (char*[]){"afl-qemu-trace", "./child", NULL}, NULL);
这样子进程会直接被QEMU-AFL实例跟踪,代码覆盖数据会被正确收集。
3. 手动初始化forkserver(需修改子进程代码)
如果允许修改子进程代码,可以在child.c的main函数开头添加forkserver初始化代码,配合AFL++的插桩逻辑(即使使用QEMU模式也能生效):
#ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif
该方法需要修改目标代码,适合无法使用前两种方案的场景。
验证:使用方案1或2启动模糊测试后,你会看到代码覆盖开始更新,当输入包含字符'c'时,AFL++会正确捕获到崩溃。
内容的提问来源于stack exchange,提问作者jimkokko5

