-
-
[原创]AFL源码分析(2)
-
发表于: 2025-8-16 15:29 4943
-
因为是整个AFL的核心区域,函数长度有点长,我们从以下几个部分分析
跟afl-as使用相同的策略,确保整个AFL工具的随机性一致
支持的主要选项:
extern char *optarg;
optarg定义在<unistd.h>头文件中,由 getopt() 函数维护,当 getopt() 解析到一个需要参数的选项时,会将该参数的地址赋值给 optarg,例如此时有-i /tmp/test这个选项,如果getopt的第三个参数有i:的话,那么此时*optarg="/tmp/test"
可以看到是主/从同步模式,afl-fuzz的单个fuzzer只占用一个进程,因此只能占用一个核心,而在多核系统中可以同时运行多个fuzzer实例
实例与实例之间的共享方式是通过将信息输入到同一个文件夹下,每个fuzzer周期性的check文件夹下其他fuzzer生成的用例,将有用的用例加入到自己的用例集中,这里可以详细分析一下:
目录结构大致会是这样的
可以看到同步时机是5次
可以看到,保留用例来自于save_if_interesting函数:
ret=0的话代表没有新覆盖率,ret=1代表发现已知路径的新的命中次数,ret=2代表发现全新的执行路径
bash
Apply to afl-fuzz.c
Run
解读:
0x0000-0x000F: 全部为0xFF,表示这些路径完全未被触及
0x0010: 0xFE,表示这个路径被触及过1次
0x0012: 0xFC,表示这个路径被触及过2-3次
0x0014: 0xF8,表示这个路径被触及过4-7次
以此类推...
接下来就是一些参数的验证和环境的检查
同时dubm模式与crash和qemu模式不兼容
重要环境变量:
设置stop_soon标志为1,表示AFL应该开始优雅退出
终止所有子进程和fork服务器进程
响应SIGHUP(终端断开)、SIGINT(Ctrl+C)和SIGTERM(终止请求)信号
设置child_timed_out标志,表示目标程序执行超时
强制终止超时的子进程
响应SIGALRM信号,用于执行时间限制控制
设置skip_requested标志,允许跳过当前测试用例
响应SIGUSR1信号,提供手动干预模糊测试过程的能力
SIGTSTP(Ctrl+Z):防止AFL被挂起
SIGPIPE(管道破裂):防止在写入已关闭的管道时程序终止
用于验证内存安全工具的环境变量配置是否符合要求。这个函数主要检查ASAN(AddressSanitizer)和MSAN(MemorySanitizer)的配置选项,确保它们以最适合模糊测试的方式设置。
AFL要求ASAN(AddressSanitizer)配置包含以下选项:
abort_on_error=1
功能:当ASAN检测到内存错误时立即终止程序
重要性:确保AFL能够捕获到崩溃,而不是让程序继续执行
如果缺少:AFL会显示致命错误并终止
symbolize=0
功能:禁用ASAN的符号化错误报告
重要性:防止ASAN生成详细的错误报告,这会干扰AFL的崩溃检测机制
如果缺少:AFL会显示致命错误并终止
对于MSAN(MemorySanitizer),AFL要求:
exit_code=MSAN_ERROR
功能:设置MSAN检测到错误时的退出代码为特定值(MSAN_ERROR)
重要性:允许AFL准确识别由未初始化内存使用导致的崩溃
如果缺少:AFL会显示致命错误并终止
symbolize=0
功能:与ASAN相同,禁用符号化错误报告
重要性:确保错误报告不会干扰AFL的崩溃检测
如果缺少:AFL会显示致命错误并终止
当使用AFL与ASAN/MSAN一起进行模糊测试时,应该设置以下环境变量:
主-从模式:
主节点(-M):执行确定性测试,系统地探索输入空间
从节点(-S):专注于随机变异,提高覆盖率和发现率
共享发现:
所有节点共享一个顶级同步目录
每个节点定期检查其他节点的发现
有趣的测试用例会被所有节点采纳
工作分配:
主节点处理计算密集型的确定性阶段
从节点可以更快地进入随机变异阶段
整体提高了模糊测试的效率
该函数将命令行参数数组(argc/argv)转换为单个连续的字符串,并将其存储在全局变量orig_cmdline中,以便在程序的其他部分使用。
就是afl用来设置横幅(banner)的函数
检查是否是在tty即终端下运行的函数
剩下的就挑几个有点意思的分析吧
读取输入目录中的所有测试用例并将其排队等待测试,此函数在启动时调用。
检查是否存在<in-dir>/queue目录,存在的话设置为新的in_dir,为了支持恢复之前的模糊测试会话
使用scandir()函数遍历in_dir,通过alphasort()确保测试用例按字母顺序排序
如果启用了shuffle_queue且同时存在多个测试用例,使用shuffle_ptrs函数随机排列测试用例
过滤非常规文件、空文件和README文件;将有效的测试用例添加到队列
将之前模糊测试会话中发现的有价值的字节序列(token)加入字典中,用于新的模糊测试会话
会发现上述涉及两个概念:token 和 字典
字典里存储着afl-fuzz认定的有效token,存在必要性
例如:png文件的文件头是IHDR,当afl-fuzz变异到I字节后,因为发生了错误,所以走到了一个不同的执行路径,然后继续H D R的变异,会发现与字节I变异走到了同一个执行路径,都发生了同一个错误,那么afl-fuzz会认定IHDR是一个有效的、不可分割的字节单元,将其加入字典后,再次用fuzz遇到它就不会再进行逐字节的变异了
pivot_inputs:规范命名,优先使用硬链接而非复制,记录测试用例之间的派生关系
load_extras:可以通过设置-x选项加载任意一个dict文件(字典)或者目录
find_timeout:设置超时时间
detect_file_args&&setup_stdio_file:检测末尾是否有@@
check_binary:检查二进制文件的合法性
get_cur_time:获取当前的准确时间
get_qemu_argv:解析qemu的参数
例如:./afl-fuzz -Q -i testcases/input -o findings -- ./target_binary @@运行这个命令
那么在该函数中就会将其解析成:./afl-qemu-trace -- ./target_binary @@
/* Main entry point */int main(int argc, char** argv) { s32 opt; u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to; u8 *extras_dir = 0; u8 mem_limit_given = 0; u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); char** use_argv; struct timeval tv; struct timezone tz; SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n"); doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; gettimeofday(&tv, &tz); srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0) switch (opt) { case 'i': /* input dir */ if (in_dir) FATAL("Multiple -i options not supported"); in_dir = optarg; if (!strcmp(in_dir, "-")) in_place_resume = 1; break; case 'o': /* output dir */ if (out_dir) FATAL("Multiple -o options not supported"); out_dir = optarg; break; case 'M': { /* master sync ID */ u8* c; if (sync_id) FATAL("Multiple -S or -M options not supported"); sync_id = ck_strdup(optarg); if ((c = strchr(sync_id, ':'))) { *c = 0; if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || !master_id || !master_max || master_id > master_max || master_max > 1000000) FATAL("Bogus master ID passed to -M"); } force_deterministic = 1; } break; case 'S': if (sync_id) FATAL("Multiple -S or -M options not supported"); sync_id = ck_strdup(optarg); break; case 'f': /* target file */ if (out_file) FATAL("Multiple -f options not supported"); out_file = optarg; break; case 'x': /* dictionary */ if (extras_dir) FATAL("Multiple -x options not supported"); extras_dir = optarg; break; case 't': { /* timeout */ u8 suffix = 0; if (timeout_given) FATAL("Multiple -t options not supported"); if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -t"); if (exec_tmout < 5) FATAL("Dangerously low value of -t"); if (suffix == '+') timeout_given = 2; else timeout_given = 1; break; } case 'm': { /* mem limit */ u8 suffix = 'M'; if (mem_limit_given) FATAL("Multiple -m options not supported"); mem_limit_given = 1; if (!strcmp(optarg, "none")) { mem_limit = 0; break; } if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -m"); switch (suffix) { case 'T': mem_limit *= 1024 * 1024; break; case 'G': mem_limit *= 1024; break; case 'k': mem_limit /= 1024; break; case 'M': break; default: FATAL("Unsupported suffix or bad syntax for -m"); } if (mem_limit < 5) FATAL("Dangerously low value of -m"); if (sizeof(rlim_t) == 4 && mem_limit > 2000) FATAL("Value of -m out of range on 32-bit systems"); } break; case 'b': { /* bind CPU core */ if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); cpu_to_bind_given = 1; if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -b"); break; } case 'd': /* skip deterministic */ if (skip_deterministic) FATAL("Multiple -d options not supported"); skip_deterministic = 1; use_splicing = 1; break; case 'B': /* load bitmap */ /* This is a secret undocumented option! It is useful if you find an interesting test case during a normal fuzzing process, and want to mutate it without rediscovering any of the test cases already found during an earlier run. To use this mode, you need to point -B to the fuzz_bitmap produced by an earlier run for the exact same binary... and that's it. I only used this once or twice to get variants of a particular file, so I'm not making this an official setting. */ if (in_bitmap) FATAL("Multiple -B options not supported"); in_bitmap = optarg; read_bitmap(in_bitmap); break; case 'C': /* crash mode */ if (crash_mode) FATAL("Multiple -C options not supported"); crash_mode = FAULT_CRASH; break; case 'n': /* dumb mode */ if (dumb_mode) FATAL("Multiple -n options not supported"); if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1; break; case 'T': /* banner */ if (use_banner) FATAL("Multiple -T options not supported"); use_banner = optarg; break; case 'Q': /* QEMU mode */ if (qemu_mode) FATAL("Multiple -Q options not supported"); qemu_mode = 1; if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; break; case 'V': /* Show version number */ /* Version number has been printed already, just quit. */ exit(0); default: usage(argv[0]); } if (optind == argc || !in_dir || !out_dir) usage(argv[0]); setup_signal_handlers(); check_asan_opts(); if (sync_id) fix_up_sync(); if (!strcmp(in_dir, out_dir)) FATAL("Input and output directories can't be the same"); if (dumb_mode) { if (crash_mode) FATAL("-C and -n are mutually exclusive"); if (qemu_mode) FATAL("-Q and -n are mutually exclusive"); } if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; if (getenv("AFL_NO_ARITH")) no_arith = 1; if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; if (getenv("AFL_FAST_CAL")) fast_cal = 1; if (getenv("AFL_HANG_TMOUT")) { hang_tmout = atoi(getenv("AFL_HANG_TMOUT")); if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT"); } if (dumb_mode == 2 && no_forkserver) FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); if (getenv("AFL_PRELOAD")) { setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); } if (getenv("AFL_LD_PRELOAD")) FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); save_cmdline(argc, argv); fix_up_banner(argv[optind]); check_if_tty(); get_core_count();#ifdef HAVE_AFFINITY bind_to_free_cpu();#endif /* HAVE_AFFINITY */ check_crash_handling(); check_cpu_governor(); setup_post(); setup_shm(); init_count_class16(); setup_dirs_fds(); read_testcases(); load_auto(); pivot_inputs(); if (extras_dir) load_extras(extras_dir); if (!timeout_given) find_timeout(); detect_file_args(argv + optind + 1); if (!out_file) setup_stdio_file(); check_binary(argv[optind]); start_time = get_cur_time(); if (qemu_mode) use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); else use_argv = argv + optind; perform_dry_run(use_argv); cull_queue(); show_init_stats(); seek_to = find_start_position(); write_stats_file(0, 0, 0); save_auto(); if (stop_soon) goto stop_fuzzing; /* Woop woop woop */ if (!not_on_tty) { sleep(4); start_time += 4000; if (stop_soon) goto stop_fuzzing; } while (1) { u8 skipped_fuzz; cull_queue(); if (!queue_cur) { queue_cycle++; current_entry = 0; cur_skipped_paths = 0; queue_cur = queue; while (seek_to) { current_entry++; seek_to--; queue_cur = queue_cur->next; } show_stats(); if (not_on_tty) { ACTF("Entering queue cycle %llu.", queue_cycle); fflush(stdout); } /* If we had a full queue cycle with no new finds, try recombination strategies next. */ if (queued_paths == prev_queued) { if (use_splicing) cycles_wo_finds++; else use_splicing = 1; } else cycles_wo_finds = 0; prev_queued = queued_paths; if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) sync_fuzzers(use_argv); } skipped_fuzz = fuzz_one(use_argv); if (!stop_soon && sync_id && !skipped_fuzz) { if (!(sync_interval_cnt++ % SYNC_INTERVAL)) sync_fuzzers(use_argv); } if (!stop_soon && exit_1) stop_soon = 2; if (stop_soon) break; queue_cur = queue_cur->next; current_entry++; } if (queue_cur) show_stats(); /* If we stopped programmatically, we kill the forkserver and the current runner. If we stopped manually, this is done by the signal handler. */ if (stop_soon == 2) { if (child_pid > 0) kill(child_pid, SIGKILL); if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); } /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */ if (waitpid(forksrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); } write_bitmap(); write_stats_file(0, 0, 0); save_auto();stop_fuzzing: SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST, stop_soon == 2 ? "programmatically" : "by user"); /* Running for more than 30 minutes but still doing first cycle? */ if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) { SAYF("\n" cYEL "[!] " cRST "Stopped during the first cycle, results may be incomplete.\n" " (For info on resuming, see %s/README.)\n", doc_path); } fclose(plot_file); destroy_queue(); destroy_extras(); ck_free(target_path); ck_free(sync_id); alloc_report(); OKF("We're done here. Have a nice day!\n"); exit(0);}#endif /* !AFL_LIB */* Main entry point */int main(int argc, char** argv) { s32 opt; u64 prev_queued = 0; u32 sync_interval_cnt = 0, seek_to; u8 *extras_dir = 0; u8 mem_limit_given = 0; u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); char** use_argv; struct timeval tv; struct timezone tz; SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n"); doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; gettimeofday(&tv, &tz); srandom(tv.tv_sec ^ tv.tv_usec ^ getpid()); while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0) switch (opt) { case 'i': /* input dir */ if (in_dir) FATAL("Multiple -i options not supported"); in_dir = optarg; if (!strcmp(in_dir, "-")) in_place_resume = 1; break; case 'o': /* output dir */ if (out_dir) FATAL("Multiple -o options not supported"); out_dir = optarg; break; case 'M': { /* master sync ID */ u8* c; if (sync_id) FATAL("Multiple -S or -M options not supported"); sync_id = ck_strdup(optarg); if ((c = strchr(sync_id, ':'))) { *c = 0; if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || !master_id || !master_max || master_id > master_max || master_max > 1000000) FATAL("Bogus master ID passed to -M"); } force_deterministic = 1; } break; case 'S': if (sync_id) FATAL("Multiple -S or -M options not supported"); sync_id = ck_strdup(optarg); break; case 'f': /* target file */ if (out_file) FATAL("Multiple -f options not supported"); out_file = optarg; break; case 'x': /* dictionary */ if (extras_dir) FATAL("Multiple -x options not supported"); extras_dir = optarg; break; case 't': { /* timeout */ u8 suffix = 0; if (timeout_given) FATAL("Multiple -t options not supported"); if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -t"); if (exec_tmout < 5) FATAL("Dangerously low value of -t"); if (suffix == '+') timeout_given = 2; else timeout_given = 1; break; } case 'm': { /* mem limit */ u8 suffix = 'M'; if (mem_limit_given) FATAL("Multiple -m options not supported"); mem_limit_given = 1; if (!strcmp(optarg, "none")) { mem_limit = 0; break; } if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -m"); switch (suffix) { case 'T': mem_limit *= 1024 * 1024; break; case 'G': mem_limit *= 1024; break; case 'k': mem_limit /= 1024; break; case 'M': break; default: FATAL("Unsupported suffix or bad syntax for -m"); } if (mem_limit < 5) FATAL("Dangerously low value of -m"); if (sizeof(rlim_t) == 4 && mem_limit > 2000) FATAL("Value of -m out of range on 32-bit systems"); } break; case 'b': { /* bind CPU core */ if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); cpu_to_bind_given = 1; if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -b"); break; } case 'd': /* skip deterministic */ if (skip_deterministic) FATAL("Multiple -d options not supported"); skip_deterministic = 1; use_splicing = 1; break; case 'B': /* load bitmap */ /* This is a secret undocumented option! It is useful if you find an interesting test case during a normal fuzzing process, and want to mutate it without rediscovering any of the test cases already found during an earlier run. To use this mode, you need to point -B to the fuzz_bitmap produced by an earlier run for the exact same binary... and that's it. I only used this once or twice to get variants of a particular file, so I'm not making this an official setting. */ if (in_bitmap) FATAL("Multiple -B options not supported"); in_bitmap = optarg; read_bitmap(in_bitmap); break; case 'C': /* crash mode */ if (crash_mode) FATAL("Multiple -C options not supported"); crash_mode = FAULT_CRASH; break; case 'n': /* dumb mode */ if (dumb_mode) FATAL("Multiple -n options not supported"); if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1; break; case 'T': /* banner */ if (use_banner) FATAL("Multiple -T options not supported"); use_banner = optarg; break; case 'Q': /* QEMU mode */ if (qemu_mode) FATAL("Multiple -Q options not supported"); qemu_mode = 1; if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; break; case 'V': /* Show version number */ /* Version number has been printed already, just quit. */ exit(0); default: usage(argv[0]); } if (optind == argc || !in_dir || !out_dir) usage(argv[0]); setup_signal_handlers(); check_asan_opts(); if (sync_id) fix_up_sync(); if (!strcmp(in_dir, out_dir)) FATAL("Input and output directories can't be the same"); if (dumb_mode) { if (crash_mode) FATAL("-C and -n are mutually exclusive"); if (qemu_mode) FATAL("-Q and -n are mutually exclusive"); } if (getenv("AFL_NO_FORKSRV")) no_forkserver = 1; if (getenv("AFL_NO_CPU_RED")) no_cpu_meter_red = 1; if (getenv("AFL_NO_ARITH")) no_arith = 1; if (getenv("AFL_SHUFFLE_QUEUE")) shuffle_queue = 1; if (getenv("AFL_FAST_CAL")) fast_cal = 1; if (getenv("AFL_HANG_TMOUT")) { hang_tmout = atoi(getenv("AFL_HANG_TMOUT")); if (!hang_tmout) FATAL("Invalid value of AFL_HANG_TMOUT"); } if (dumb_mode == 2 && no_forkserver) FATAL("AFL_DUMB_FORKSRV and AFL_NO_FORKSRV are mutually exclusive"); if (getenv("AFL_PRELOAD")) { setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); } if (getenv("AFL_LD_PRELOAD")) FATAL("Use AFL_PRELOAD instead of AFL_LD_PRELOAD"); save_cmdline(argc, argv); fix_up_banner(argv[optind]); check_if_tty(); get_core_count();#ifdef HAVE_AFFINITY bind_to_free_cpu();#endif /* HAVE_AFFINITY */ check_crash_handling(); check_cpu_governor(); setup_post(); setup_shm(); init_count_class16(); setup_dirs_fds(); read_testcases(); load_auto(); pivot_inputs(); if (extras_dir) load_extras(extras_dir); if (!timeout_given) find_timeout(); detect_file_args(argv + optind + 1); if (!out_file) setup_stdio_file(); check_binary(argv[optind]); start_time = get_cur_time(); if (qemu_mode) use_argv = get_qemu_argv(argv[0], argv + optind, argc - optind); else use_argv = argv + optind; perform_dry_run(use_argv); cull_queue(); show_init_stats(); seek_to = find_start_position(); write_stats_file(0, 0, 0); save_auto(); if (stop_soon) goto stop_fuzzing; /* Woop woop woop */ if (!not_on_tty) { sleep(4); start_time += 4000; if (stop_soon) goto stop_fuzzing; } while (1) { u8 skipped_fuzz; cull_queue(); if (!queue_cur) { queue_cycle++; current_entry = 0; cur_skipped_paths = 0; queue_cur = queue; while (seek_to) { current_entry++; seek_to--; queue_cur = queue_cur->next; } show_stats(); if (not_on_tty) { ACTF("Entering queue cycle %llu.", queue_cycle); fflush(stdout); } /* If we had a full queue cycle with no new finds, try recombination strategies next. */ if (queued_paths == prev_queued) { if (use_splicing) cycles_wo_finds++; else use_splicing = 1; } else cycles_wo_finds = 0; prev_queued = queued_paths; if (sync_id && queue_cycle == 1 && getenv("AFL_IMPORT_FIRST")) sync_fuzzers(use_argv); } skipped_fuzz = fuzz_one(use_argv); if (!stop_soon && sync_id && !skipped_fuzz) { if (!(sync_interval_cnt++ % SYNC_INTERVAL)) sync_fuzzers(use_argv); } if (!stop_soon && exit_1) stop_soon = 2; if (stop_soon) break; queue_cur = queue_cur->next; current_entry++; } if (queue_cur) show_stats(); /* If we stopped programmatically, we kill the forkserver and the current runner. If we stopped manually, this is done by the signal handler. */ if (stop_soon == 2) { if (child_pid > 0) kill(child_pid, SIGKILL); if (forksrv_pid > 0) kill(forksrv_pid, SIGKILL); } /* Now that we've killed the forkserver, we wait for it to be able to get rusage stats. */ if (waitpid(forksrv_pid, NULL, 0) <= 0) { WARNF("error waitpid\n"); } write_bitmap(); write_stats_file(0, 0, 0); save_auto();stop_fuzzing: SAYF(CURSOR_SHOW cLRD "\n\n+++ Testing aborted %s +++\n" cRST, stop_soon == 2 ? "programmatically" : "by user"); /* Running for more than 30 minutes but still doing first cycle? */ if (queue_cycle == 1 && get_cur_time() - start_time > 30 * 60 * 1000) { SAYF("\n" cYEL "[!] " cRST "Stopped during the first cycle, results may be incomplete.\n" " (For info on resuming, see %s/README.)\n", doc_path); } fclose(plot_file); destroy_queue(); destroy_extras(); ck_free(target_path); ck_free(sync_id); alloc_report(); OKF("We're done here. Have a nice day!\n"); exit(0);}#endif /* !AFL_LIB *s32 opt; // getopt选项解析u64 prev_queued = 0; // 上一轮队列中的测试用例数u32 sync_interval_cnt = 0, // 同步间隔计数器 seek_to; // 队列中的起始位置u8 *extras_dir = 0; // 额外字典目录u8 mem_limit_given = 0; // 内存限制是否设置u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); // 基准测试模式char** use_argv; // 实际使用的命令行参数struct timeval tv; // 时间结构struct timezone tz; // 时区结构s32 opt; // getopt选项解析u64 prev_queued = 0; // 上一轮队列中的测试用例数u32 sync_interval_cnt = 0, // 同步间隔计数器 seek_to; // 队列中的起始位置u8 *extras_dir = 0; // 额外字典目录u8 mem_limit_given = 0; // 内存限制是否设置u8 exit_1 = !!getenv("AFL_BENCH_JUST_ONE"); // 基准测试模式char** use_argv; // 实际使用的命令行参数struct timeval tv; // 时间结构struct timezone tz; // 时区结构// 1. 显示版本信息SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");// 2. 设置文档路径doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;// 3. 初始化随机数生成器gettimeofday(&tv, &tz);srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());// 1. 显示版本信息SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n");// 2. 设置文档路径doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH;// 3. 初始化随机数生成器gettimeofday(&tv, &tz);srandom(tv.tv_sec ^ tv.tv_usec ^ getpid());while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0) switch (opt) { // 各种选项处理 }while ((opt = getopt(argc, argv, "+i:o:f:m:b:t:T:dnCB:S:M:x:QV")) > 0) switch (opt) { // 各种选项处理 }case 'i': /* input dir */ if (in_dir) FATAL("Multiple -i options not supported"); in_dir = optarg; if (!strcmp(in_dir, "-")) in_place_resume = 1; break;case 'o': /* output dir */ if (out_dir) FATAL("Multiple -o options not supported"); out_dir = optarg; break;case 'M': { /* master sync ID */ u8* c; if (sync_id) FATAL("Multiple -S or -M options not supported"); sync_id = ck_strdup(optarg); if ((c = strchr(sync_id, ':'))) { *c = 0; if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || !master_id || !master_max || master_id > master_max || master_max > 1000000) FATAL("Bogus master ID passed to -M"); } force_deterministic = 1; } break;case 'S': if (sync_id) FATAL("Multiple -S or -M options not supported"); sync_id = ck_strdup(optarg); break;case 'f': /* target file */ if (out_file) FATAL("Multiple -f options not supported"); out_file = optarg; break;case 'x': /* dictionary */ if (extras_dir) FATAL("Multiple -x options not supported"); extras_dir = optarg; break;case 't': { /* timeout */ u8 suffix = 0; if (timeout_given) FATAL("Multiple -t options not supported"); if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -t"); if (exec_tmout < 5) FATAL("Dangerously low value of -t"); if (suffix == '+') timeout_given = 2; else timeout_given = 1; break;}case 'm': { /* mem limit */ u8 suffix = 'M'; if (mem_limit_given) FATAL("Multiple -m options not supported"); mem_limit_given = 1; if (!strcmp(optarg, "none")) { mem_limit = 0; break; } if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -m"); switch (suffix) { case 'T': mem_limit *= 1024 * 1024; break; case 'G': mem_limit *= 1024; break; case 'k': mem_limit /= 1024; break; case 'M': break; default: FATAL("Unsupported suffix or bad syntax for -m"); } if (mem_limit < 5) FATAL("Dangerously low value of -m"); if (sizeof(rlim_t) == 4 && mem_limit > 2000) FATAL("Value of -m out of range on 32-bit systems"); } break;case 'b': { /* bind CPU core */ if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); cpu_to_bind_given = 1; if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -b"); break;}case 'd': /* skip deterministic */ if (skip_deterministic) FATAL("Multiple -d options not supported"); skip_deterministic = 1; use_splicing = 1; break;case 'B': /* load bitmap */ /* This is a secret undocumented option! It is useful if you find an interesting test case during a normal fuzzing process, and want to mutate it without rediscovering any of the test cases already found during an earlier run. To use this mode, you need to point -B to the fuzz_bitmap produced by an earlier run for the exact same binary... and that's it. I only used this once or twice to get variants of a particular file, so I'm not making this an official setting. */ if (in_bitmap) FATAL("Multiple -B options not supported"); in_bitmap = optarg; read_bitmap(in_bitmap); break;case 'C': /* crash mode */ if (crash_mode) FATAL("Multiple -C options not supported"); crash_mode = FAULT_CRASH; break;case 'n': /* dumb mode */ if (dumb_mode) FATAL("Multiple -n options not supported"); if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1; break;case 'T': /* banner */ if (use_banner) FATAL("Multiple -T options not supported"); use_banner = optarg; break;case 'Q': /* QEMU mode */ if (qemu_mode) FATAL("Multiple -Q options not supported"); qemu_mode = 1; if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; break;case 'V': /* Show version number */ /* Version number has been printed already, just quit. */ exit(0);default: usage(argv[0]);case 'i': /* input dir */ if (in_dir) FATAL("Multiple -i options not supported"); in_dir = optarg; if (!strcmp(in_dir, "-")) in_place_resume = 1; break;case 'o': /* output dir */ if (out_dir) FATAL("Multiple -o options not supported"); out_dir = optarg; break;case 'M': { /* master sync ID */ u8* c; if (sync_id) FATAL("Multiple -S or -M options not supported"); sync_id = ck_strdup(optarg); if ((c = strchr(sync_id, ':'))) { *c = 0; if (sscanf(c + 1, "%u/%u", &master_id, &master_max) != 2 || !master_id || !master_max || master_id > master_max || master_max > 1000000) FATAL("Bogus master ID passed to -M"); } force_deterministic = 1; } break;case 'S': if (sync_id) FATAL("Multiple -S or -M options not supported"); sync_id = ck_strdup(optarg); break;case 'f': /* target file */ if (out_file) FATAL("Multiple -f options not supported"); out_file = optarg; break;case 'x': /* dictionary */ if (extras_dir) FATAL("Multiple -x options not supported"); extras_dir = optarg; break;case 't': { /* timeout */ u8 suffix = 0; if (timeout_given) FATAL("Multiple -t options not supported"); if (sscanf(optarg, "%u%c", &exec_tmout, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -t"); if (exec_tmout < 5) FATAL("Dangerously low value of -t"); if (suffix == '+') timeout_given = 2; else timeout_given = 1; break;}case 'm': { /* mem limit */ u8 suffix = 'M'; if (mem_limit_given) FATAL("Multiple -m options not supported"); mem_limit_given = 1; if (!strcmp(optarg, "none")) { mem_limit = 0; break; } if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -m"); switch (suffix) { case 'T': mem_limit *= 1024 * 1024; break; case 'G': mem_limit *= 1024; break; case 'k': mem_limit /= 1024; break; case 'M': break; default: FATAL("Unsupported suffix or bad syntax for -m"); } if (mem_limit < 5) FATAL("Dangerously low value of -m"); if (sizeof(rlim_t) == 4 && mem_limit > 2000) FATAL("Value of -m out of range on 32-bit systems"); } break;case 'b': { /* bind CPU core */ if (cpu_to_bind_given) FATAL("Multiple -b options not supported"); cpu_to_bind_given = 1; if (sscanf(optarg, "%u", &cpu_to_bind) < 1 || optarg[0] == '-') FATAL("Bad syntax used for -b"); break;}case 'd': /* skip deterministic */ if (skip_deterministic) FATAL("Multiple -d options not supported"); skip_deterministic = 1; use_splicing = 1; break;case 'B': /* load bitmap */ /* This is a secret undocumented option! It is useful if you find an interesting test case during a normal fuzzing process, and want to mutate it without rediscovering any of the test cases already found during an earlier run. To use this mode, you need to point -B to the fuzz_bitmap produced by an earlier run for the exact same binary... and that's it. I only used this once or twice to get variants of a particular file, so I'm not making this an official setting. */ if (in_bitmap) FATAL("Multiple -B options not supported"); in_bitmap = optarg; read_bitmap(in_bitmap); break;case 'C': /* crash mode */ if (crash_mode) FATAL("Multiple -C options not supported"); crash_mode = FAULT_CRASH; break;case 'n': /* dumb mode */ if (dumb_mode) FATAL("Multiple -n options not supported"); if (getenv("AFL_DUMB_FORKSRV")) dumb_mode = 2; else dumb_mode = 1; break;case 'T': /* banner */ if (use_banner) FATAL("Multiple -T options not supported"); use_banner = optarg; break;case 'Q': /* QEMU mode */ if (qemu_mode) FATAL("Multiple -Q options not supported"); qemu_mode = 1; if (!mem_limit_given) mem_limit = MEM_LIMIT_QEMU; break;case 'V': /* Show version number */ /* Version number has been printed already, just quit. */ exit(0);default: usage(argv[0]);| 选项 | 参数 | 功能 | 重要性 |
|---|---|---|---|
| -i | 目录 | 输入测试用例目录 | 必需 |
| -o | 目录 | 输出结果目录 | 必需 |
| -f | 文件 | 目标程序输入文件 | 可选 |
| -m | 内存 | 内存限制 | 重要 |
| -t | 时间 | 执行超时 | 重要 |
| -M/-S | ID | 主/从同步模式 | 并行 |
| -x | 目录 | 字典目录 | 优化 |
| -Q | - | QEMU模式 | 特殊 |
| -C | - | 崩溃模式 | 调试 |
| -n | - | 哑模式 | 特殊 |
| -B | bitmap | 基于已有覆盖率 |
# 启动主实例./afl-fuzz -i input -o sync_dir -M fuzzer01 ./target @@# 启动从实例./afl-fuzz -i input -o sync_dir -S fuzzer02 ./target @@./afl-fuzz -i input -o sync_dir -S fuzzer03 ./target @@# 启动主实例./afl-fuzz -i input -o sync_dir -M fuzzer01 ./target @@# 启动从实例./afl-fuzz -i input -o sync_dir -S fuzzer02 ./target @@./afl-fuzz -i input -o sync_dir -S fuzzer03 ./target @@sync_dir/├── fuzzer01/ # 主实例 (-M)│ ├── queue/ # 测试用例队列│ ├── crashes/ # 崩溃样本│ ├── hangs/ # 挂起样本│ └── .synced/ # 同步状态记录│ ├── fuzzer02 # 记录从fuzzer02同步到的位置│ └── fuzzer03 # 记录从fuzzer03同步到的位置├── fuzzer02/ # 从实例 (-S)│ ├── queue/│ └── .synced/└── fuzzer03/ # 从实例 (-S) ├── queue/ └── .synced/sync_dir/├── fuzzer01/ # 主实例 (-M)│ ├── queue/ # 测试用例队列│ ├── crashes/ # 崩溃样本│ ├── hangs/ # 挂起样本│ └── .synced/ # 同步状态记录│ ├── fuzzer02 # 记录从fuzzer02同步到的位置│ └── fuzzer03 # 记录从fuzzer03同步到的位置├── fuzzer02/ # 从实例 (-S)│ ├── queue/│ └── .synced/└── fuzzer03/ # 从实例 (-S) ├── queue/ └── .synced/// 主循环中的同步调用if (!stop_soon && sync_id && !skipped_fuzz) { if (!(sync_interval_cnt++ % SYNC_INTERVAL)) sync_fuzzers(use_argv);}#define SYNC_INTERVAL 5 // 每5次fuzz_one()调用后同步一次// 主循环中的同步调用if (!stop_soon && sync_id && !skipped_fuzz) { if (!(sync_interval_cnt++ % SYNC_INTERVAL)) sync_fuzzers(use_argv);}#define SYNC_INTERVAL 5 // 每5次fuzz_one()调用后同步一次static void sync_fuzzers(char** argv) { DIR* sd; struct dirent* sd_ent; u32 sync_cnt = 0; // 1. 打开同步目录 sd = opendir(sync_dir); if (!sd) PFATAL("Unable to open '%s'", sync_dir); // 2. 遍历同步目录中的所有fuzzer实例 while ((sd_ent = readdir(sd))) { // 跳过自己和隐藏文件 if (sd_ent->d_name[0] == '.' || !strcmp(sync_id, sd_ent->d_name)) continue; // 3. 检查是否有queue子目录 qd_path = alloc_printf("%s/%s/queue", sync_dir, sd_ent->d_name); if (!(qd = opendir(qd_path))) { ck_free(qd_path); continue; } // 4. 读取同步状态 qd_synced_path = alloc_printf("%s/.synced/%s", out_dir, sd_ent->d_name); id_fd = open(qd_synced_path, O_RDWR | O_CREAT, 0600); // 读取上次同步到的测试用例ID if (read(id_fd, &min_accept, sizeof(u32)) > 0) lseek(id_fd, 0, SEEK_SET);static void sync_fuzzers(char** argv) { DIR* sd; struct dirent* sd_ent; u32 sync_cnt = 0;赞赏
- [原创]某短视频指纹和纯算 sig3 逆向分析 29179
- [原创]AFL源码分析(3) 5442
- [原创]AFL源码分析(2) 4944
- [原创]AFL源码分析(1) 5423
- [原创]初探Qiling framework 11905