main 首先从 main 来看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 int main (int argc, char ** argv) { if (isatty(2 ) && !getenv("AFL_QUIET" )) { SAYF(cCYA "afl-cc " cBRI VERSION cRST " by <lcamtuf@google.com>\n" ); } else be_quiet = 1 ; if (argc < 2 ) { SAYF("\n" "This is a helper application for afl-fuzz. It serves as a drop-in replacement\n" "for gcc or clang, letting you recompile third-party code with the required\n" "runtime instrumentation. A common use pattern would be one of the following:\n\n" " CC=%s/afl-gcc ./configure\n" " CXX=%s/afl-g++ ./configure\n\n" "You can specify custom next-stage toolchain via AFL_CC, AFL_CXX, and AFL_AS.\n" "Setting AFL_HARDEN enables hardening optimizations in the compiled code.\n\n" , BIN_PATH, BIN_PATH); exit (1 ); } find_as(argv[0 ]); edit_params(argc, argv); execvp(cc_params[0 ], (char **)cc_params); FATAL("Oops, failed to execute '%s' - check your PATH" , cc_params[0 ]); return 0 ; }
会首先检查
isatty(2) 是否为 1 // stdout
设置了 AFL_QUIET 环境变量
如果有一个不满足则切换为 quiet 模式
接下来主要流程分配三个阶段:
find_as
edit_params
execvp
find_as 该函数会去寻找所使用的汇编器,传入的 argv0 就是 afl-gcc s:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 static void find_as (u8* argv0) { u8 *afl_path = getenv("AFL_PATH" ); u8 *slash, *tmp; if (afl_path) { tmp = alloc_printf("%s/as" , afl_path); if (!access(tmp, X_OK)) { as_path = afl_path; ck_free(tmp); return ; } ck_free(tmp); } slash = strrchr (argv0, '/' ); if (slash) { u8 *dir; *slash = 0 ; dir = ck_strdup(argv0); *slash = '/' ; tmp = alloc_printf("%s/afl-as" , dir); if (!access(tmp, X_OK)) { as_path = dir; ck_free(tmp); return ; } ck_free(tmp); ck_free(dir); } if (!access(AFL_PATH "/as" , X_OK)) { as_path = AFL_PATH; return ; } FATAL("Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH" ); }
其步骤如下:
获得环境变量 AFL_PATH 并赋值给 afl_path
如果 afl_path 存在,则将 afl_path 拼接上 “/as” 作为 as 的路径,检查其是否可访问,可访问则返回
从 argv0 中寻找是否有 “/” 字符
如果有则将 “/” 字符之前的内容拆分,并拼接上 “/afl-as” 作为 as 的路径,检查其是否可访问,可访问则返回
如果都不行则最后抛出异常
edit_params edit_params 函数主要用于更改一些参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 static void edit_params (u32 argc, char ** argv) { u8 fortify_set = 0 , asan_set = 0 ; u8 *name; #if defined(__FreeBSD__) && defined(__x86_64__) u8 m32_set = 0 ; #endif cc_params = ck_alloc((argc + 128 ) * sizeof (u8*)); name = strrchr (argv[0 ], '/' ); if (!name) name = argv[0 ]; else name++; if (!strncmp (name, "afl-clang" , 9 )) { clang_mode = 1 ; setenv(CLANG_ENV_VAR, "1" , 1 ); if (!strcmp (name, "afl-clang++" )) { u8* alt_cxx = getenv("AFL_CXX" ); cc_params[0 ] = alt_cxx ? alt_cxx : (u8*)"clang++" ; } else { u8* alt_cc = getenv("AFL_CC" ); cc_params[0 ] = alt_cc ? alt_cc : (u8*)"clang" ; } } else { #ifdef __APPLE__ if (!strcmp (name, "afl-g++" )) cc_params[0 ] = getenv("AFL_CXX" ); else if (!strcmp (name, "afl-gcj" )) cc_params[0 ] = getenv("AFL_GCJ" ); else cc_params[0 ] = getenv("AFL_CC" ); if (!cc_params[0 ]) { SAYF("\n" cLRD "[-] " cRST "On Apple systems, 'gcc' is usually just a wrapper for clang. Please use the\n" " 'afl-clang' utility instead of 'afl-gcc'. If you really have GCC installed,\n" " set AFL_CC or AFL_CXX to specify the correct path to that compiler.\n" ); FATAL("AFL_CC or AFL_CXX required on MacOS X" ); } #else if (!strcmp (name, "afl-g++" )) { u8* alt_cxx = getenv("AFL_CXX" ); cc_params[0 ] = alt_cxx ? alt_cxx : (u8*)"g++" ; } else if (!strcmp (name, "afl-gcj" )) { u8* alt_cc = getenv("AFL_GCJ" ); cc_params[0 ] = alt_cc ? alt_cc : (u8*)"gcj" ; } else { u8* alt_cc = getenv("AFL_CC" ); cc_params[0 ] = alt_cc ? alt_cc : (u8*)"gcc" ; } #endif } while (--argc) { u8* cur = *(++argv); if (!strncmp (cur, "-B" , 2 )) { if (!be_quiet) WARNF("-B is already set, overriding" ); if (!cur[2 ] && argc > 1 ) { argc--; argv++; } continue ; } if (!strcmp (cur, "-integrated-as" )) continue ; if (!strcmp (cur, "-pipe" )) continue ; #if defined(__FreeBSD__) && defined(__x86_64__) if (!strcmp (cur, "-m32" )) m32_set = 1 ; #endif if (!strcmp (cur, "-fsanitize=address" ) || !strcmp (cur, "-fsanitize=memory" )) asan_set = 1 ; if (strstr (cur, "FORTIFY_SOURCE" )) fortify_set = 1 ; cc_params[cc_par_cnt++] = cur; } cc_params[cc_par_cnt++] = "-B" ; cc_params[cc_par_cnt++] = as_path; if (clang_mode) cc_params[cc_par_cnt++] = "-no-integrated-as" ; if (getenv("AFL_HARDEN" )) { cc_params[cc_par_cnt++] = "-fstack-protector-all" ; if (!fortify_set) cc_params[cc_par_cnt++] = "-D_FORTIFY_SOURCE=2" ; } if (asan_set) { setenv("AFL_USE_ASAN" , "1" , 1 ); } else if (getenv("AFL_USE_ASAN" )) { if (getenv("AFL_USE_MSAN" )) FATAL("ASAN and MSAN are mutually exclusive" ); if (getenv("AFL_HARDEN" )) FATAL("ASAN and AFL_HARDEN are mutually exclusive" ); cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE" ; cc_params[cc_par_cnt++] = "-fsanitize=address" ; } else if (getenv("AFL_USE_MSAN" )) { if (getenv("AFL_USE_ASAN" )) FATAL("ASAN and MSAN are mutually exclusive" ); if (getenv("AFL_HARDEN" )) FATAL("MSAN and AFL_HARDEN are mutually exclusive" ); cc_params[cc_par_cnt++] = "-U_FORTIFY_SOURCE" ; cc_params[cc_par_cnt++] = "-fsanitize=memory" ; } if (!getenv("AFL_DONT_OPTIMIZE" )) { #if defined(__FreeBSD__) && defined(__x86_64__) if (!clang_mode || !m32_set) cc_params[cc_par_cnt++] = "-g" ; #else cc_params[cc_par_cnt++] = "-g" ; #endif cc_params[cc_par_cnt++] = "-O3" ; cc_params[cc_par_cnt++] = "-funroll-loops" ; cc_params[cc_par_cnt++] = "-D__AFL_COMPILER=1" ; cc_params[cc_par_cnt++] = "-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1" ; } if (getenv("AFL_NO_BUILTIN" )) { cc_params[cc_par_cnt++] = "-fno-builtin-strcmp" ; cc_params[cc_par_cnt++] = "-fno-builtin-strncmp" ; cc_params[cc_par_cnt++] = "-fno-builtin-strcasecmp" ; cc_params[cc_par_cnt++] = "-fno-builtin-strncasecmp" ; cc_params[cc_par_cnt++] = "-fno-builtin-memcmp" ; cc_params[cc_par_cnt++] = "-fno-builtin-strstr" ; cc_params[cc_par_cnt++] = "-fno-builtin-strcasestr" ; } cc_params[cc_par_cnt] = NULL ; }
首先为 cc_params 分配空间,大小为 (argc + 128) * sizeof(u8*)
随后判断 argv[0] 是否包含 afl-clang,如果包含
设置 CLANG_ENV_VAR 为 1
判断 argv[0] 包含的是 afl-clang 还是 afl-clang++
如果是 afl-clang ,则获取环境变量 AFL_CC
没有环境变量,将 cc_params[0] 设为 clang
否则设置为 AFL_CC
如果是 afl-clang++ ,则获取环境变量 AFL_CXX
没有环境变量,将 cc_params[0] 设为 clang++
否则设置为 AFL_CXX
如果不包含
如果是 apple 平台,分别判断
为 afl-g++ ,设置 cc_params[0] 为环境变量中的 AFL_CXX
为 afl-gcj ,设置 cc_params[0] 为环境变量中的 AFL_GCJ
设置 cc_params[0] 为环境变量中的 AFL_CC
如果都不行,抛出错误
否则
为 afl-g++ ,设置 cc_params[0] 为环境变量中的 AFL_CXX 或 g++
为 afl-gcj ,设置 cc_params[0] 为环境变量中的 AFL_GCJ 或 gcj
设置 cc_params[0] 为环境变量中的 AFL_CC 或 gcc
随后根据 argc 进行 argv 的遍历
默认设置 -B ,且跟着的参数为 as 的路径
忽略 -integrated-as 和 -pipe
如果是 clang 模式,则设置 -no-integrated-as
如果有环境变量 AFL_HARDEN ,则设置 -fstack-protector-all
如果遍历的时候有 FORTIFY_SOURCE ,则变为 -D_FORTIFY_SOURCE=2
如果遍历的时候有 -fsanitize=address 或 -fsanitize=memory
否则,如果有设置环境变量 AFL_USE_ASAN
判断是否有环境变量 AFL_USE_MSAN 和 FL_HARDEN ,有的话则抛出错误
设置 -U_FORTIFY_SOURCE 和 -fsanitize=address
否则,如果有设置环境变量 AFL_USE_MSAN
判断是否有环境变量 AFL_USE_ASAN 和 FL_HARDEN ,有的话则抛出错误
设置 -U_FORTIFY_SOURCE 和 -fsanitize=memory
接下来判断是否有环境变量 AFL_DONT_OPTIMIZE ,如果没有的话
判断是否为 64-bit FreeBSD 平台,如果是的话则 clang -g -m32 会导致错误,但是 -m32 却可以,为了修复该 bug 直接加 -g 即可
之后分别添加以下参数
-O3
-funroll-loops
-D__AFL_COMPILER=1
-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1
最后判断是否有环境变量 AFL_NO_BUILTIN ,如果有的话添加以下参数
-fno-builtin-strcmp
-fno-builtin-strncmp
-fno-builtin-strcasecmp
-fno-builtin-strncasecmp
-fno-builtin-memcmp
-fno-builtin-strstr
-fno-builtin-strcasestr
execvp 最后的 execvp 则是将替换过后的执行运行,我们可以使用以下代码打印出来进行对比:
1 2 3 4 5 6 7 8 9 for (int i = 0 ; i < argc; i++){ printf ("\t[*] argv[%d] : %s\n" ,i,argv[i]); } printf ("\n" );for (int i = 0 ; i < sizeof (cc_params); i++){ printf ("\t[*] cc_params[%d] : %s\n" ,i,cc_params[i]); }
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 afl-cc 2.57b by <lcamtuf@google.com> [*] argv[0 ] : afl-gcc [*] argv[1 ] : test.c [*] argv[2 ] : -o [*] argv[3 ] : test [*] cc_params[0 ] : gcc [*] cc_params[1 ] : test.c [*] cc_params[2 ] : -o [*] cc_params[3 ] : test [*] cc_params[4 ] : -B [*] cc_params[5 ] : /usr/local/lib/afl [*] cc_params[6 ] : -g [*] cc_params[7 ] : -O3
可以看得出来 afl-gcc 的本质就是一层 wrapper