afl-fuzz.c main函数简单流程
afl-fuzz.c main函数简单流程
Ivoripuionafl-fuzz.c main函数简单流程(关键步骤省略)
预处理
- 打印afl程序信息;
c
1 | SAYF(cCYA "afl-fuzz " cBRI VERSION cRST " by <lcamtuf@google.com>\n"); |
- 检查文件是否存在;
c
1 | doc_path = access(DOC_PATH, F_OK) ? "docs" : DOC_PATH; |
- 设置一个种子,方便后续随机量的设置;
c
1 | gettimeofday(&tv, &tz); |
根据命令行初始化并检查一些参数;
- i:输入语料文件夹,对应变量
in_dir
; - o:fuzz输出文件夹,对应变量
out_dir
; - M,S:多线程模式下的Master和Server,Master会采取强制确定性变异,然后进行随进行变异,而Server会采取dumb mode进行fuzz,即不进行确定性变异;
- f:用来进行fuzz的文件,对应变量
out_file
; - x:确定性变异阶段的字典(?),对应变量
extras_dir
; - t:目标程序运行case的限制时间,对应变量
timeout_given
; - m:目标程序运行内存的限制,对应变量
mem_limit
; - d:跳过确定性变异,对应变量
skip_deterministic
; - B:指定fuzz_bitmap来跳过该case;
- C:crash mode;
- n:dumb mode(随机模式,且不进行插桩);
- T:text banner;
- Q;QEMU mode;
- i:输入语料文件夹,对应变量
设置信号句柄
sa
:
c
1 | setup_signal_handlers(); |
- 检查ASAN设置:
c
1 | check_asan_opts(); |
- 修正fuzzer ID:
c
1 | if (sync_id) fix_up_sync(); |
- 检查输入输出文件夹:
c
1 | if (!strcmp(in_dir, out_dir)) |
- 若设置了dumb mode则检查与其冲突的模式:
c
1 | if (dumb_mode) { |
一些环境变量的设置以及检查。
保存命令行:
c
1 | save_cmdline(argc, argv); |
- 调整banner的展示效果(将fuzzer id写上):
c
1 | fix_up_banner(argv[optind]); |
- 检查是否在TTY终端:
c
1 | check_if_tty(); |
- 查询
/proc/stat
得知内核信息并打印:
c
1 | get_core_count(); |
- 构建绑定到特定核心的进程列表:
c
1 |
|
- 检查crash的转存以及CPU的管理者(主要通过检查
/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
这类的设备文件):
c
1 | check_crash_handling(); |
- 检查环境变量
AFL_POST_LIBRARY
,该环境变量用于对变异后的testcases格式进行修正,如计算校验和等:
c
1 | setup_post(); |
使用后会进行如下编译:
bash
1 | gcc -shared -Wall -O3 post_library.so.c -o post_library.so |
- 设置共享内存块以及virgin_bits(用来记录总分支路径信息:Regions yet untouched by fuzzing):
c
1 | setup_shm(); |
- 扩展路径记录的表:
c
1 | init_count_class16(); |
喂数据
- 设置输出文件夹和文件描述符:
c
1 | setup_dirs_fds(); |
- 读取测试用例进入队列中,包括对输入文件夹权限、内容等的检查:
c
1 | read_testcases(); |
- 自动载入token,即检查输入文件夹中是否有token文件夹,有则载入。这里查阅资料后:
- 使用token是用来执行bitflip时降低消耗资源的一种策略,即将一个token代表一系列变异后覆盖路径未变化的语料。
c
1 | load_auto(); |
- 在输出文件中为输入文件(变异后的)创建硬链接,以”id:”开头:
# define CASE_PREFIX "id:"
,并根据其进行适当的调整:
c
1 | pivot_inputs(); |
- 若存在token文件夹则载入该文件夹:
c
1 | if (extras_dir) load_extras(extras_dir); |
- 若没有设置”-t”的参数,则找到一个合理的”timeout”值,以防止不断地收缩这个”timeout”值:
c
1 | if (!timeout_given) find_timeout(); |
- 若未设置输出文件夹,则为输出数据设置输出文件夹:
c
1 | if (!out_file) setup_stdio_file(); |
- 检查目标程序的存在,且不是一个shell脚本,通过检查ELF头来判断是否为一个ELF文件,可以通过设置”AFL_SKIP_BIN_CHECK”环境变量来跳过该项检查:
c
1 | check_binary(argv[optind]); |
- 获取当前时间为开始时间:
c
1 | start_time = get_cur_time(); |
- 判断是否为qemu模式的fuzz:
c
1 | if (qemu_mode) |
执行fuzz(具体步骤后续详细分析)
- dry run,将种子文件直接作为输入文件喂给目标程序:
c
1 | perform_dry_run(use_argv); |
- 从队列中找到最合适的testcase,赋值给top_rated[],并且设置q->favored:
c
1 | cull_queue(); |
- 处理完初始语料后显示提示信息:
c
1 | show_init_stats(); |
- 找到队列开始的位置:
c
1 | seek_to = find_start_position(); |
- 更新统计信息文件:
c
1 | write_stats_file(0, 0, 0); |
- 自动保存token:
c
1 | save_auto(); |
- contrl+C,结束fuzz:
c
1 | if (stop_soon) goto stop_fuzzing; |
- 不在TTy终端也结束fuzz:
c
1 | if (!not_on_tty) { |
- fuzz_one循环:
c
1 | while(1){ |
- 收尾工作,包括关闭文件描述符,销毁队列、token文件夹等:
c
1 | fclose(plot_file); |
参考链接:
https://www.jianshu.com/p/487f5e451325