感谢大佬们的文章:
https://eternalsakura13.com/2020/08/23/afl/#more
https://bbs.pediy.com/thread-265936.htm
https://bbs.pediy.com/thread-269534.htm
http://rk700.github.io/2017/12/28/afl-internals/
接下来是关于AFL插桩部分的内容,这篇记录 afl-gcc.c 的代码解读,先记录一小部分吧,有些小细节理解可能还差点意思。
afl-gcc.c
1. find_as() 寻找as路径
说明:as是Linux下常用的汇编器。编译完成AFL后,在其目录下也会存在一个as
文件,并作为符号链接指向afl-as
-
获取环境变量“AFL_PATH” 赋值到
afl_path
,如果没有设置环境变量AFL_PATH的话,会为null。-
这里手动export设置一下环境变量AFL_PATH 为AFL存放位置,算是一个小尝试(当然必须在一个终端内设置并运行,我就犯了小迷糊,因为export设置的变量只在当前终端有效)
-
export AFL_PATH="/home/xu/fuzz/AFL"
-
在gcc.c中加入一行代码,打印出来看看
-
u8 *afl_path = getenv("AFL_PATH"); printf("afl_path: %s \n", afl_path);
-
加入代码后需要重新 make && make install, 然后重新输入下列命令
-
afl-gcc test.c -o test
-
可以看到会输出 afl_path=/home/xu/fuzz/AFL
-
-
如果环境变量获取成功
- 动态分配一段空间存储对应的路径
- 如果有可执行权限
as_path
即设置为该路径- free并return
- 如果没有可执行权限
- 直接free
-
如果环境变量获取失败或者没有可执行权限,
slash
置为 argv0 中最后一个 ‘/’ 的位置 -
如果argv0 中有 ‘/’
- 取’/’ 前面的字符串为
dir
- 检查
dir/afl-as
这个文件是否具有可执行权限,如果有- 赋值,free,return
- 如果没有,直接free
- 取’/’ 前面的字符串为
-
如果以上两种情况都失败了,直接去默认文件夹(AFL_PATH = /usr/local/lib/afl)下找 as
- 找到了且可访问,则赋值并return
- 否则抛出异常
2. edit_params() 设置CC的参数
-
为
cc_params
分配大小为 (argc + 128) * 8 的空间 -
检查 argv[0] 中是否存在 ‘/’ (如afl-gcc test.c -o test ,则argv[0] = afl-gcc)
- 如果不存在则
name
= argv[0] - 如果存在则一直找到最后一个/,并将其后面的字符串赋值给 name
- 如果不存在则
-
如果name 以 “afl-clang” 开头
- clang_mode = 1;
- 设置环境变量CLANG_ENV_VAR为1
- 如果
name
为 “afl-clang++”- 则获取环境变量AFL_CXX的值赋值给
alt_cxx
alt_cxx
不为null,则cc_params[0] 即第一个参数置成alt_cxx
,否则置成 ”clang++“
- 则获取环境变量AFL_CXX的值赋值给
- 如果
name
不为 “afl-clang++”,那name
就是 “afl-clang”- 则获取环境变量AFL_CC的值赋值给
alt_cc
alt_cc
不为null,则cc_params[0] 即第一个参数置成alt_cc
,否则置成 ”clang“
- 则获取环境变量AFL_CC的值赋值给
-
如果name 不是以 “afl-clang” 开头
-
如果
name
为 “afl-g++”- 则获取环境变量AFL_CXX的值赋值给
alt_cxx
alt_cxx
不为null,则cc_params[0] 即第一个参数置成alt_cxx
,否则置成 ”g++“
- 则获取环境变量AFL_CXX的值赋值给
-
如果
name
为 “afl-gcj” (gcj 是针对JAVA的)- 则获取环境变量AFL_GCJ的值赋值给
alt_cc
alt_cc
不为null,则cc_params[0] 即第一个参数置成alt_cc
,否则置成 ”gcj“
- 则获取环境变量AFL_GCJ的值赋值给
-
如果
name
为 “afl-gcc”- 则获取环境变量AFL_CC的值赋值给
alt_cc
alt_cc
不为null,则cc_params[0] 即第一个参数置成alt_cc
,否则置成 ”gcc“
- 则获取环境变量AFL_CC的值赋值给
-
-
进入 while 循环,遍历从argv[1]开始的argv参数
- 如果传入的第二个参数为 -B
- 如果
be_quiet
为0,则警告-B已经设置 - 如果这个参数后面还有参数,直接去扫描下一个参数(好像不是这样)
- 如果
- 如果扫描到 -integrated-as,跳过
- 如果扫描到 -pipe,跳过
- 如果扫描到 -fsanitize=address 或 -fsanitize=memory ,设置 asan_set = 1
- 如果扫描到 FORTIFY_SOURCE ,设置 fortify_set = 1 (FORTIFY_SOURCE 主要进行缓冲区溢出问题的检查,检查的常见函数有memcpy, mempcpy, memmove, memset 等)
- 将参数保存到
cc_params
数组内
- 如果传入的第二个参数为 -B
-
-B 保存到
cc_params
数组内 -
as_path 保存到
cc_params
数组内 (这两步指定汇编器) -
如果为 clang模式 ,则设置-no-integrated-as
-
如果存在环境变量 AFL_HARDEN,
- 则设置-fstack-protector-all
- 如果没有设置 fortify_set ,追加 -D_FORTIFY_SOURCE=2
-
如果 asan_set 在前面被设置为1,则设置环境变量 AFL_USE_ASAN 为1
-
如果 asan_set 不为1且存在 AFL_USE_ASAN 环境变量
- 如果存在 AFL_USE_ASAN 环境变量,又存在 AFL_USE_MSAN 环境变量,抛出异常不能同时存在
- 如果存在 AFL_USE_ASAN 环境变量,又存在 AFL_HARDEN环境变量,抛出异常不能同时存在
- -U_FORTIFY_SOURCE 保存到
cc_params
数组内 - -fsanitize=address 保存到
cc_params
数组内
-
如果存在 AFL_USE_MSAN 环境变量
- 如果存在 AFL_USE_ASAN 环境变量, 抛出异常不能同时存在
- 如果存在 AFL_HARDEN 环境变量, 抛出异常不能同时存在
- -U_FORTIFY_SOURCE 保存到
cc_params
数组内 - -fsanitize=memory 保存到
cc_params
数组内
-
如果不存在 AFL_DONT_OPTIMIZE 环境变量
- -g 保存到
cc_params
数组内 - -O3 保存到
cc_params
数组内 - -funroll-loops 保存到
cc_params
数组内 - -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1 保存到
cc_params
数组内 - D__AFL_COMPILER=1 保存到
cc_params
数组内
- -g 保存到
-
如果存在 AFL_NO_BUILTIN 环境变量
- …
-
最后加入结束标志
3. main 函数
-
如果没有环境变量 AFL_QUIET,打印信息
-
否则 be_quiet = 1
-
如果参数数量小于2,提示退出
-
调用
find_as
查找汇编器 -
调用
edit_params
处理传入的参数,将确定好的参数放入 cc_params[] 数组 -
看一下处理后的参数数组
// 输入 afl-gcc test.c -o h.out // 结果(不知道后面为啥没有 -funroll-loops、-D__AFL_COMPILER=1、-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1) arg0: gcc arg1: test.c arg2: -o arg3: h.out arg4: -B arg5: /usr/local/lib/afl arg6: -g arg7: -O3
-
调用execvp 执行命令,该函数如果执行成功则不返回