fuzz原始碼閱讀
這個月的目標就是把afl程式碼讀完,不著急慢慢看(最快估計一週),大致分為3個模組afl-gcc,afl-as,afl-fuzz
至於為什麼要閱讀這些原始碼,大概是為了今後更好的利用和理解fuzz和對以後魔改原始碼做準備~~~
1.除錯afl-gcc.c原始碼
1.find_as
這裡解釋一下,as是什麼,as是linux下常用的一種彙編器,負責把生成的彙編程式碼生成二進位制程式碼
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 | #define AFL_MAIN #include "config.h" #include "types.h" #include "debug.h" #include "alloc-inl.h" #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> static u8 * as_path; / * Path to the AFL 'as' wrapper * / static u8 * * cc_params; / * Parameters passed to the real CC * / static u32 cc_par_cnt = 1 ; / * Param count, including argv0 * / static u8 be_quiet, / * Quiet mode * / clang_mode; / * Invoked as afl - clang * ? * / / * Try to find our "fake" GNU assembler in AFL_PATH or at the location derived from argv[ 0 ]. If that fails, abort. * / static void find_as(u8 * argv0) { / / 透過argv[ 0 ](當前檔案的路徑)來尋找對應的彙編器as(linux上as是常用的一個彙編器,負責把生成的彙編程式碼翻譯到二進位制) u8 * afl_path = getenv( "AFL_PATH" ); / / getenv是搜尋其引數所指向的環境字串 u8 * slash, * tmp; if (afl_path) { / / 如果獲取成功 tmp = alloc_printf( "%s/as" , afl_path); / / 動態分配空間來儲存afl_path的路徑 if (!access(tmp, X_OK)) { / / 如果此路徑有可執行許可權,就將路徑賦值為as_path as_path = afl_path; ck_free(tmp); return ; } ck_free(tmp); / / 若不可訪問,直接free掉 } / / / / 獲取AFL_PATH路徑,檢驗路徑是否可以訪問 slash = strrchr(argv0, '/' ); / / strrchr是從argv0的右側開始查詢 "/" 的出現並向右獲取,也就是說如果afl_path沒有獲取成功的話,就獲取當前路徑 dir if (slash) { / / 若獲取到當前路徑 u8 * dir ; * slash = 0 ; dir = ck_strdup(argv0); / / 將argv0所代表的路徑複製給 dir * 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 ); } / / 和上面的函式差不多,如果上面的沒獲取成功,則讀取當前路徑,並賦值給as_path,然後free, return ,不可訪問的話就直接free掉 if (!access(AFL_PATH "/as" , X_OK)) { / / 若上述兩種都沒有成功,就找 "/as" ,若可訪問,就賦值給as_path,再返回 as_path = AFL_PATH; return ; } FATAL( "Unable to find AFL wrapper binary for 'as'. Please set AFL_PATH" ); / / 若都沒找到,則輸出錯誤資訊 } |
2.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 | / * Copy argv to cc_params, making the necessary edits. * / 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 * )); / / 為cc_params開闢空間 name = strrchr(argv[ 0 ], '/' ); / / 獲取右數第一個 "/" 後的編譯器名稱賦給name if (!name) name = argv[ 0 ]; else name + + ; if (!strncmp(name, "afl-clang" , 9 )) { / / 如果name是以 "afl-clang" 開頭,進入迴圈 clang_mode = 1 ; setenv(CLANG_ENV_VAR, "1" , 1 ); / / 設定環境變數 if (!strcmp(name, "afl-clang++" )) { / / 如果name是以 "afl-clang++" 開頭,進入迴圈 u8 * alt_cxx = getenv( "AFL_CXX" ); cc_params[ 0 ] = alt_cxx ? alt_cxx : (u8 * ) "clang++" ; / / 若獲取到環境變數則直接將環境變數賦值給cc_params,若沒有,將 "clang++" 賦值給cc_params } else { / / 如果name不是以 "afl-clang++" 開頭 u8 * alt_cc = getenv( "AFL_CC" ); / / 獲得環境變數 "AFL_CC" cc_params[ 0 ] = alt_cc ? alt_cc : (u8 * ) "clang" ; / / 若獲取到環境變數則直接將環境變數賦值給cc_params,若沒有,將 "clang" 賦值給cc_params } / / cc_params[]是儲存編譯引數的陣列 } else { / / 如果name不是以 "afl-clang" 開頭,則進入如下迴圈 #ifdef __APPLE__ //參考漫牛師傅的資料說如果是蘋果平臺則進入如下分支 if (!strcmp(name, "afl-g++" )) cc_params[ 0 ] = getenv( "AFL_CXX" ); / / 如果name是以 "afl-g++" 開頭,將cc_params賦值為 "AFL_CXX" 的環境變數 else if (!strcmp(name, "afl-gcj" )) cc_params[ 0 ] = getenv( "AFL_GCJ" ); / / 如果name是以 "afl-gcj" 開頭,將cc_params賦值為 "AFL_GCJ" 的環境變數 else cc_params[ 0 ] = getenv( "AFL_CC" ); / / 如果name的值不是如上兩個,就賦值為 "AFL_CC" 的環境變數 if (!cc_params[ 0 ]) { / / 若cc_params值為 0 ,則提示Mac下要有限使用afl - clang,如果要使用aflgcc需要配置路徑 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 //若不是Apple平臺 if (!strcmp(name, "afl-g++" )) { / / 如果name是 "afl-g++" ,則將alt_cxx賦值為 "AFL_CXX" 的環境變數 u8 * alt_cxx = getenv( "AFL_CXX" ); cc_params[ 0 ] = alt_cxx ? alt_cxx : (u8 * ) "g++" ; / / 如果獲取到值則直接將環境變數值付給cc_params[ 0 ],如果沒有獲取到則直接將字串“g + + ”付給cc_params[ 0 ] } else if (!strcmp(name, "afl-gcj" )) { / / 如果name是 "afl-gcj" ,則將alt_cxx賦值為 "AFL-GCJ" 的環境變數 u8 * alt_cc = getenv( "AFL_GCJ" ); cc_params[ 0 ] = alt_cc ? alt_cc : (u8 * ) "gcj" ; / / 如果獲取到值則直接將環境變數值付給cc_params[ 0 ],如果沒有獲取到則直接將字串“gcj”付給cc_params[ 0 ] } else { u8 * alt_cc = getenv( "AFL_CC" ); / / 獲取AFL_CC環境變數 cc_params[ 0 ] = alt_cc ? alt_cc : (u8 * ) "gcc" ; / / 如果獲取到值則直接將環境變數值付給cc_params[ 0 ],如果沒有獲取到則直接將字串“gcc”付給cc_params[ 0 ] } #endif /* __APPLE__ */ } while ( - - argc) { / / 引數 - - u8 * cur = * ( + + argv); if (!strncmp(cur, "-B" , 2 )) { / / 如果cur為 "-B" if (!be_quiet) WARNF( "-B is already set, overriding" ); / / 如果靜默模式已經關閉,則輸出資訊 if (!cur[ 2 ] && argc > 1 ) { argc - - ; argv + + ; } continue ; } if (!strcmp(cur, "-integrated-as" )) continue ; / / 當前引數為 "-integrated-as" 時跳過本次迴圈 if (!strcmp(cur, "-pipe" )) continue ; / / 當前引數為 "-pipe" 時跳過本次迴圈 #if defined(__FreeBSD__) && defined(__x86_64__) if (!strcmp(cur, "-m32" )) m32_set = 1 ; / / 判斷當前引數為“ - m32”時,設定m32_set標誌引數為 1 #endif if (!strcmp(cur, "-fsanitize=address" ) || !strcmp(cur, "-fsanitize=memory" )) asan_set = 1 ; / * 判斷當前引數為 "-fsanitize=address" 或" - fsanitize = memory"時,並設定asan_set標誌引數為 1 (這兩個引數為了告訴gcc要檢查記憶體訪問錯誤) * / if (strstr(cur, "FORTIFY_SOURCE" )) fortify_set = 1 ; / / 判斷當前引數為“FORTIFY_SOURCE”時,設定fortify_set標誌引數為 1 (此引數為fortify保護是否開啟) cc_params[cc_par_cnt + + ] = cur; } cc_params[cc_par_cnt + + ] = "-B" ; cc_params[cc_par_cnt + + ] = as_path; / / 取出find_as()函式中找到的as_path,組成 "-B as_path" if (clang_mode) / / 若clang_mode為 1 cc_params[cc_par_cnt + + ] = "-no-integrated-as" ; / / 賦值cc_params追加引數 "-no-integrated-as" if (getenv( "AFL_HARDEN" )) { / / 如果可以獲取到該環境變數,進入分支 cc_params[cc_par_cnt + + ] = "-fstack-protector-all" ; if (!fortify_set) / / 檢查是否設定fortify引數,如果沒有,進入分支 cc_params[cc_par_cnt + + ] = "-D_FORTIFY_SOURCE=2" ; } if (asan_set) { / / 判斷是否檢查記憶體,如果已經設定為 1 ,進入迴圈 setenv( "AFL_USE_ASAN" , "1" , 1 ); / / 設定環境變數為 1 } else if (getenv( "AFL_USE_ASAN" )) { / / 如果 "AFL_USE_ASAN" 被設為 1 ,進入迴圈 if (getenv( "AFL_USE_MSAN" )) / / 判斷 "AFL_USE_MSAN" 是否是 1 ,若是則進入迴圈 FATAL( "ASAN and MSAN are mutually exclusive" ); / / 會提示ASAN和MSAN存在互斥 if (getenv( "AFL_HARDEN" )) / / 判斷 "AFL_HARDEN" 是否為 1 ,若是則進入迴圈 FATAL( "ASAN and AFL_HARDEN are mutually exclusive" ); / / 會提示ASAN和AFL_HARDEN存在互斥 cc_params[cc_par_cnt + + ] = "-U_FORTIFY_SOURCE" ; cc_params[cc_par_cnt + + ] = "-fsanitize=address" ; / / 若上述兩個環境變數都沒有設定,則追加這兩個引數 } else if (getenv( "AFL_USE_MSAN" )) { / / 如果 "AFL_USE_MSAN" 為 1 ,進入迴圈 if (getenv( "AFL_USE_ASAN" )) / / 判斷 "AFL_USE_ASAN" 是否為 1 ,若是則進入迴圈 FATAL( "ASAN and MSAN are mutually exclusive" ); / / 會提示ASAN和MSAN是互斥的 if (getenv( "AFL_HARDEN" )) / / 判斷 "AFL_HARDEN" 是否為 1 ,若是則進入迴圈 FATAL( "MSAN and AFL_HARDEN are mutually exclusive" ); / / 會提示MASN和AFL_HARDEN存在互斥 cc_params[cc_par_cnt + + ] = "-U_FORTIFY_SOURCE" ; cc_params[cc_par_cnt + + ] = "-fsanitize=memory" ; / / 若上述兩個環境變數都沒有設定,則追加這兩個引數 } if (!getenv( "AFL_DONT_OPTIMIZE" )) { / / 如果沒有成功獲取 "AFL_DONT_OPTIMIZE" 環境變數,就進入分支 #if defined(__FreeBSD__) && defined(__x86_64__) / * On 64 - bit FreeBSD systems, clang - g - m32 is broken, but - m32 itself works OK. This has nothing to do with us, but let's avoid triggering that bug. * / if (!clang_mode || !m32_set) / / 若沒有設定clang_mode模式或者沒有加m32,則進入分支 cc_params[cc_par_cnt + + ] = "-g" ; / / 設定 - g引數 #else //若不是_FreeBSD和__x86_64__系統,則進入分支 cc_params[cc_par_cnt + + ] = "-g" ; / / 追加引數 - g #endif cc_params[cc_par_cnt + + ] = "-O3" ; cc_params[cc_par_cnt + + ] = "-funroll-loops" ; / * Two indicators that you're building for fuzzing; one of them is AFL - specific, the other is shared with libfuzzer. * / 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" )) { / / 如果能獲取到 "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; / / 表示陣列結束 } |
3.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 | 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); / / execvp()會從PATH 環境變數所指的目錄中查詢符合引數 file 的檔名, 找到後便執行該檔案, 然後將第二個引數argv 傳給該欲執行的檔案。 / / 呼叫該函式執行afl - gcc( cc_params[ 0 ]為編譯器,(char * * )cc_params為編譯器引數 ) FATAL( "Oops, failed to execute '%s' - check your PATH" , cc_params[ 0 ]); return 0 ; } |
2.除錯afl-fuzz
(⊙o⊙)… 大概8000多行程式碼,看到這麼多程式碼頭都大了,也是第一次分析
我是用的gdb+source insight4.0+大佬分析的文章來進行一個大致瀏覽
1.setup_signal_handlers
該函式是註冊四個訊號處理函式handle_stop_sig,handle_timeout,handle_resize,handle_skipreq
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 | EXP_ST void setup_signal_handlers(void) { struct sigaction sa; sa.sa_handler = NULL; sa.sa_flags = SA_RESTART; sa.sa_sigaction = NULL; sigemptyset(&sa.sa_mask); / / 初始化訊號集,並清空 / * Various ways of saying "stop" . * / sa.sa_handler = handle_stop_sig; / / 註冊處理類似 "stop" 操作的函式 sigaction(SIGHUP, &sa, NULL); / / SIGHUP訊號是終端結束時發出 sigaction(SIGINT, &sa, NULL); / / SIGINT訊號是ctrl + c sigaction(SIGTERM, &sa, NULL); / / SIGTERM訊號是透過kill產生,比較友好,不像SIGKILL直接關閉程式 / * Exec timeout notifications. * / sa.sa_handler = handle_timeout; / / 註冊超時處理函式 sigaction(SIGALRM, &sa, NULL); / / SIGALRM訊號是定時器終止時傳送給程式的訊號 / * Window resize * / sa.sa_handler = handle_resize; / / 註冊視窗變化處理函式 sigaction(SIGWINCH, &sa, NULL); / / SIGWINCH訊號是視窗大小改變時發出 / * SIGUSR1: skip entry * / sa.sa_handler = handle_skipreq; / / 註冊跳過使用者請求函式 sigaction(SIGUSR1, &sa, NULL); / / SIGUSR1訊號是留給使用者使用的訊號 / * Things we don't care about. * / sa.sa_handler = SIG_IGN; / / 用SIG_IGN表示忽略SIGTSTP和SIGPIPE訊號 sigaction(SIGTSTP, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); } |
2.check_asan_opts
該函式的主要作用是讀取環境變數"ASAN_OPTIONS"和"MSAN_OPTIONS"
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 | static void check_asan_opts(void) { u8 * x = getenv( "ASAN_OPTIONS" ); if (x) { if (!strstr(x, "abort_on_error=1" )) FATAL( "Custom ASAN_OPTIONS set without abort_on_error=1 - please fix!" ); if (!strstr(x, "symbolize=0" )) FATAL( "Custom ASAN_OPTIONS set without symbolize=0 - please fix!" ); } x = getenv( "MSAN_OPTIONS" ); if (x) { if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) FATAL( "Custom MSAN_OPTIONS set without exit_code=" STRINGIFY(MSAN_ERROR) " - please fix!" ); if (!strstr(x, "symbolize=0" )) FATAL( "Custom MSAN_OPTIONS set without symbolize=0 - please fix!" ); } } |
3.fix_up_sync
該函式是定義了一些引數衝突和一些引數的要求啥的
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 | static void fix_up_sync(void) { u8 * x = sync_id; if (dumb_mode) FATAL( "-S / -M and -n are mutually exclusive" ); if (skip_deterministic) { if (force_deterministic) FATAL( "use -S instead of -M -d" ); else FATAL( "-S already implies -d" ); } while ( * x) { if (!isalnum( * x) && * x ! = '_' && * x ! = '-' ) FATAL( "Non-alphanumeric fuzzer ID specified via -S or -M" ); x + + ; } if (strlen(sync_id) > 32 ) FATAL( "Fuzzer ID too long" ); x = alloc_printf( "%s/%s" , out_dir, sync_id); sync_dir = out_dir; out_dir = x; if (!force_deterministic) { skip_deterministic = 1 ; use_splicing = 1 ; } } |
4.save_cmdline
該函式的作用是將命令列引數複製到orig_cmdline,起到一個儲存作用
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 | static void save_cmdline(u32 argc, char * * argv) { u32 len = 1 , i; u8 * buf; for (i = 0 ; i < argc; i + + ) len + = strlen(argv[i]) + 1 ; buf = orig_cmdline = ck_alloc( len ); for (i = 0 ; i < argc; i + + ) { u32 l = strlen(argv[i]); memcpy(buf, argv[i], l); buf + = l; if (i ! = argc - 1 ) * (buf + + ) = ' ' ; } * buf = 0 ; } |
5.fix_up_banner
建立一個執行中的banner
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 | static void fix_up_banner(u8 * name) { if (!use_banner) { if (sync_id) { use_banner = sync_id; } else { u8 * trim = strrchr(name, '/' ); if (!trim) use_banner = name; else use_banner = trim + 1 ; } } if (strlen(use_banner) > 40 ) { u8 * tmp = ck_alloc( 44 ); sprintf(tmp, "%.40s..." , use_banner); use_banner = tmp; } } |
6.check_if_tty
該函式的作用是檢查是否在tty終端中執行
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 | / * Check if we're on TTY. * / static void check_if_tty(void) { struct winsize ws; if (getenv( "AFL_NO_UI" )) { OKF( "Disabling the UI because AFL_NO_UI is set." ); not_on_tty = 1 ; return ; } if (ioctl( 1 , TIOCGWINSZ, &ws)) { if (errno = = ENOTTY) { OKF( "Looks like we're not running on a tty, so I'll be a bit less verbose." ); not_on_tty = 1 ; } return ; } } |
7.get_core_count
計算cpu核心的數量
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 | static void get_core_count(void) { u32 cur_runnable = 0 ; #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) size_t s = sizeof(cpu_core_count); / * On * BSD systems, we can just use a sysctl to get the number of CPUs. * / #ifdef __APPLE__ if (sysctlbyname( "hw.logicalcpu" , &cpu_core_count, &s, NULL, 0 ) < 0 ) return ; #else int s_name[ 2 ] = {CTL_HW, HW_NCPU}; if (sysctl(s_name, 2 , &cpu_core_count, &s, NULL, 0 ) < 0 ) return ; #endif /* ^__APPLE__ */ #else #ifdef HAVE_AFFINITY cpu_core_count = sysconf(_SC_NPROCESSORS_ONLN); #else FILE * f = fopen( "/proc/stat" , "r" ); u8 tmp[ 1024 ]; if (!f) return ; while (fgets(tmp, sizeof(tmp), f)) if (!strncmp(tmp, "cpu" , 3 ) && isdigit(tmp[ 3 ])) cpu_core_count + + ; fclose(f); #endif /* ^HAVE_AFFINITY */ #endif /* ^(__APPLE__ || __FreeBSD__ || __OpenBSD__) */ if (cpu_core_count > 0 ) { cur_runnable = (u32)get_runnable_processes(); #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) / * Add ourselves, since the 1 - minute average doesn't include that yet. * / cur_runnable + + ; #endif /* __APPLE__ || __FreeBSD__ || __OpenBSD__ */ OKF( "You have %u CPU core%s and %u runnable tasks (utilization: %0.0f%%)." , cpu_core_count, cpu_core_count > 1 ? "s" : "", cur_runnable, cur_runnable * 100.0 / cpu_core_count); if (cpu_core_count > 1 ) { if (cur_runnable > cpu_core_count * 1.5 ) { WARNF( "System under apparent load, performance may be spotty." ); } else if (cur_runnable + 1 < = cpu_core_count) { OKF( "Try parallel jobs - see %s/parallel_fuzzing.txt." , doc_path); } } } else { cpu_core_count = 0 ; WARNF( "Unable to figure out the number of CPU cores." ); } } |
8.check_crash_handling
確保核心轉儲不會進入另外一個程式執行
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 | static void check_crash_handling(void) { #ifdef __APPLE__ / * Yuck! There appears to be no simple C API to query for the state of loaded daemons on MacOS X, and I'm a bit hesitant to do something more sophisticated, such as disabling crash reporting via Mach ports, until I get a box to test the code. So, for now, we check for crash reporting the awful way. * / if (system( "launchctl list 2>/dev/null | grep -q '\\.ReportCrash$'" )) return ; SAYF( "\n" cLRD "[-] " cRST "Whoops, your system is configured to forward crash notifications to an\n" " external crash reporting utility. This will cause issues due to the\n" " extended delay between the fuzzed binary malfunctioning and this fact\n" " being relayed to the fuzzer via the standard waitpid() API.\n\n" " To avoid having crashes misinterpreted as timeouts, please run the\n" " following commands:\n\n" " SL=/System/Library; PL=com.apple.ReportCrash\n" " launchctl unload -w ${SL}/LaunchAgents/${PL}.plist\n" " sudo launchctl unload -w ${SL}/LaunchDaemons/${PL}.Root.plist\n" ); if (!getenv( "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES" )) FATAL( "Crash reporter detected" ); #else / * This is Linux specific, but I don 't think there' s anything equivalent on * BSD, so we can just let it slide for now. * / s32 fd = open ( "/proc/sys/kernel/core_pattern" , O_RDONLY); u8 fchar; if (fd < 0 ) return ; ACTF( "Checking core_pattern..." ); if (read(fd, &fchar, 1 ) = = 1 && fchar = = '|' ) { SAYF( "\n" cLRD "[-] " cRST "Hmm, your system is configured to send core dump notifications to an\n" " external utility. This will cause issues: there will be an extended delay\n" " between stumbling upon a crash and having this information relayed to the\n" " fuzzer via the standard waitpid() API.\n\n" " To avoid having crashes misinterpreted as timeouts, please log in as root\n" " and temporarily modify /proc/sys/kernel/core_pattern, like so:\n\n" " echo core >/proc/sys/kernel/core_pattern\n" ); if (!getenv( "AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES" )) FATAL( "Pipe at the beginning of 'core_pattern'" ); } close(fd); #endif /* ^__APPLE__ */ } |
9.check_cpu_governor
檢查cpu管理者,我也不太理解嗚嗚嗚
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 | static void check_cpu_governor(void) { FILE * f; u8 tmp[ 128 ]; u64 min = 0 , max = 0 ; if (getenv( "AFL_SKIP_CPUFREQ" )) return ; f = fopen( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor" , "r" ); if (!f) return ; ACTF( "Checking CPU scaling governor..." ); if (!fgets(tmp, 128 , f)) PFATAL( "fgets() failed" ); fclose(f); if (!strncmp(tmp, "perf" , 4 )) return ; f = fopen( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq" , "r" ); if (f) { if (fscanf(f, "%llu" , & min ) ! = 1 ) min = 0 ; fclose(f); } f = fopen( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" , "r" ); if (f) { if (fscanf(f, "%llu" , & max ) ! = 1 ) max = 0 ; fclose(f); } if ( min = = max ) return ; SAYF( "\n" cLRD "[-] " cRST "Whoops, your system uses on-demand CPU frequency scaling, adjusted\n" " between %llu and %llu MHz. Unfortunately, the scaling algorithm in the\n" " kernel is imperfect and can miss the short-lived processes spawned by\n" " afl-fuzz. To keep things moving, run these commands as root:\n\n" " cd /sys/devices/system/cpu\n" " echo performance | tee cpu*/cpufreq/scaling_governor\n\n" " You can later go back to the original state by replacing 'performance' with\n" " 'ondemand'. If you don't want to change the settings, set AFL_SKIP_CPUFREQ\n" " to make afl-fuzz skip this check - but expect some performance drop.\n" , min / 1024 , max / 1024 ); FATAL( "Suboptimal CPU scaling governor" ); } |
10.setup_shm
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 | EXP_ST void setup_shm(void) { u8 * shm_str; if (!in_bitmap) memset(virgin_bits, 255 , MAP_SIZE); memset(virgin_tmout, 255 , MAP_SIZE); memset(virgin_crash, 255 , MAP_SIZE); / / 上面都是初始化變數 shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600 ); / / 建立共享記憶體,大小為MAP_SIZE / / IPC_EXCL:只有共享記憶體不存在時,才會建立新的,否則發生錯誤 if (shm_id < 0 ) PFATAL( "shmget() failed" ); atexit(remove_shm); shm_str = alloc_printf( "%d" , shm_id); / * If somebody is asking us to fuzz instrumented binaries in dumb mode, we don 't want them to detect instrumentation, since we won' t be sending fork server commands. This should be replaced with better auto - detection later on, perhaps? * / if (!dumb_mode) / / 非dump_mode模式下 setenv(SHM_ENV_VAR, shm_str, 1 ); ck_free(shm_str); trace_bits = shmat(shm_id, NULL, 0 ); / / 啟動對該共享記憶體的訪問,並將其第一位元組的指標返回給trace_bits if (trace_bits = = (void * ) - 1 ) PFATAL( "shmat() failed" ); } |
11.init_count_class16
初始化陣列
1 2 3 4 5 6 7 8 9 10 11 | EXP_ST void init_count_class16(void) { u32 b1, b2; for (b1 = 0 ; b1 < 256 ; b1 + + ) for (b2 = 0 ; b2 < 256 ; b2 + + ) count_class_lookup16[(b1 << 8 ) + b2] = (count_class_lookup8[b1] << 8 ) | count_class_lookup8[b2]; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | static const u8 count_class_lookup8[ 256 ] = { [ 0 ] = 0 , [ 1 ] = 1 , [ 2 ] = 2 , [ 3 ] = 4 , [ 4 ... 7 ] = 8 , [ 8 ... 15 ] = 16 , [ 16 ... 31 ] = 32 , [ 32 ... 127 ] = 64 , [ 128 ... 255 ] = 128 }; |
trace_bits是用於記錄是否到達該路徑以及到達的次數,到達的次數用count_class_lookup8/count_class_lookup16進行等級劃分,並透過等級來體現是否存在路徑更新
12.setup_dirs_fds
設定輸出目錄和檔案描述符
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 | EXP_ST void setup_dirs_fds(void) { u8 * tmp; s32 fd; ACTF( "Setting up output directories..." ); if (sync_id && mkdir(sync_dir, 0700 ) && errno ! = EEXIST) / / 若sync不為空,就建立輸出資料夾 PFATAL( "Unable to create '%s'" , sync_dir); if (mkdir(out_dir, 0700 )) / / sync不為空的情況下,建立資料夾,建立失敗返回 - 1 ,進入失敗分支 { if (errno ! = EEXIST) PFATAL( "Unable to create '%s'" , out_dir); maybe_delete_out_dir(); } else / / 建立成功返回 0 ,進入成功分支 { if (in_place_resume) FATAL( "Resume attempted but old output directory not found" ); out_dir_fd = open (out_dir, O_RDONLY); / / 開啟輸出資料夾目錄 #ifndef __sun if (out_dir_fd < 0 || flock(out_dir_fd, LOCK_EX | LOCK_NB)) / / PFATAL( "Unable to flock() output directory." ); #endif /* !__sun */ } / * Queue directory for any starting & discovered paths. * / tmp = alloc_printf( "%s/queue" , out_dir); if (mkdir(tmp, 0700 )) / / 如果建立失敗,進入失敗分支 PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); / * Top - level directory for queue metadata used for session resume and related tasks. * / tmp = alloc_printf( "%s/queue/.state/" , out_dir); / / 儲存queue資料並用於恢復會話和相關任務 if (mkdir(tmp, 0700 )) / / PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); / * Directory for flagging queue entries that went through deterministic fuzzing in the past. * / tmp = alloc_printf( "%s/queue/.state/deterministic_done/" , out_dir); / / 用於標記過去經過確定性模糊處理的佇列條目的目錄 if (mkdir(tmp, 0700 )) PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); / * Directory with the auto - selected dictionary entries. * / tmp = alloc_printf( "%s/queue/.state/auto_extras/" , out_dir); / / 自動選擇的字典目錄 if (mkdir(tmp, 0700 )) PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); / * The set of paths currently deemed redundant. * / tmp = alloc_printf( "%s/queue/.state/redundant_edges/" , out_dir); / / 當前被視為冗雜的路徑集 if (mkdir(tmp, 0700 )) PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); / * The set of paths showing variable behavior. * / tmp = alloc_printf( "%s/queue/.state/variable_behavior/" , out_dir); / / 顯示可變行為的路徑集 if (mkdir(tmp, 0700 )) PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); / * Sync directory for keeping track of cooperating fuzzers. * / if (sync_id) / / 建立目錄,用於跟蹤cooperating fuzzers { tmp = alloc_printf( "%s/.synced/" , out_dir); if (mkdir(tmp, 0700 ) && (!in_place_resume || errno ! = EEXIST)) PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); } / * All recorded crashes. * / tmp = alloc_printf( "%s/crashes" , out_dir); / / 建立crashes崩潰目錄 if (mkdir(tmp, 0700 )) PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); / * All recorded hangs. * / tmp = alloc_printf( "%s/hangs" , out_dir); / / 建立hangs掛起目錄 if (mkdir(tmp, 0700 )) PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); / * Generally useful file descriptors. * / dev_null_fd = open ( "/dev/null" , O_RDWR); if (dev_null_fd < 0 ) PFATAL( "Unable to open /dev/null" ); dev_urandom_fd = open ( "/dev/urandom" , O_RDONLY); if (dev_urandom_fd < 0 ) PFATAL( "Unable to open /dev/urandom" ); / * Gnuplot output file . * / tmp = alloc_printf( "%s/plot_data" , out_dir); fd = open (tmp, O_WRONLY | O_CREAT | O_EXCL, 0600 ); if (fd < 0 ) PFATAL( "Unable to create '%s'" , tmp); ck_free(tmp); plot_file = fdopen(fd, "w" ); if (!plot_file) PFATAL( "fdopen() failed" ); fprintf(plot_file, "# unix_time, cycles_done, cur_path, paths_total, " "pending_total, pending_favs, map_size, unique_crashes, " "unique_hangs, max_depth, execs_per_sec\n" ); / * ignore errors * / } |
13.read_testcases
從輸入檔案中讀取testcases,排成佇列用於測試
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 | static void read_testcases(void) { struct dirent * * nl; s32 nl_cnt; u32 i; u8 * fn; / * Auto - detect non - in - place resumption attempts. * / / / 檢視是否可以訪問輸入檔案,若可訪問,則讀入,若不可訪問,則釋放 fn = alloc_printf( "%s/queue" , in_dir); if (!access(fn, F_OK)) in_dir = fn; else ck_free(fn); ACTF( "Scanning '%s'..." , in_dir); / * We use scandir() + alphasort() rather than readdir() because otherwise, the ordering of test cases would vary somewhat randomly and would be difficult to control. * / nl_cnt = scandir(in_dir, &nl, NULL, alphasort); / / 按字母順序掃描 if (nl_cnt < 0 ) { if (errno = = ENOENT || errno = = ENOTDIR) SAYF( "\n" cLRD "[-] " cRST "The input directory does not seem to be valid - try again. The fuzzer needs\n" " one or more test case to start with - ideally, a small file under 1 kB\n" " or so. The cases must be stored as regular files directly in the input\n" " directory.\n" ); PFATAL( "Unable to open '%s'" , in_dir); } if (shuffle_queue && nl_cnt > 1 ) / / 對輸入進行打亂 { ACTF( "Shuffling queue..." ); shuffle_ptrs((void * * )nl, nl_cnt); } for (i = 0 ; i < nl_cnt; i + + ) / / 依次新增到輸入佇列 { struct stat st; u8 * fn = alloc_printf( "%s/%s" , in_dir, nl[i] - >d_name); u8 * dfn = alloc_printf( "%s/.state/deterministic_done/%s" , in_dir, nl[i] - >d_name); u8 passed_det = 0 ; free(nl[i]); / * not tracked * / if (lstat(fn, &st) || access(fn, R_OK)) PFATAL( "Unable to access '%s'" , fn); / * This also takes care of . and .. * / if (!S_ISREG(st.st_mode) || !st.st_size || strstr(fn, "/README.txt" )) { ck_free(fn); ck_free(dfn); continue ; } if (st.st_size > MAX_FILE) / / 限制檔案大小 FATAL( "Test case '%s' is too big (%s, limit is %s)" , fn, DMS(st.st_size), DMS(MAX_FILE)); / * Check for metadata that indicates that deterministic fuzzing is complete for this entry. We don't want to repeat deterministic fuzzing when resuming aborted scans, because it would be pointless and probably very time - consuming. * / if (!access(dfn, F_OK)) passed_det = 1 ; ck_free(dfn); add_to_queue(fn, st.st_size, passed_det); } free(nl); / * not tracked * / if (!queued_paths) { SAYF( "\n" cLRD "[-] " cRST "Looks like there are no valid test cases in the input directory! The fuzzer\n" " needs one or more test case to start with - ideally, a small file under\n" " 1 kB or so. The cases must be stored as regular files directly in the\n" " input directory.\n" ); FATAL( "No usable test cases in '%s'" , in_dir); } last_path_time = 0 ; queued_at_start = queued_paths; } |
14.add_to_queue
把新的testcase新增到queue
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 | static void add_to_queue(u8 * fname, u32 len , u8 passed_det) { struct queue_entry * q = ck_alloc(sizeof(struct queue_entry)); q - >fname = fname; q - > len = len ; q - >depth = cur_depth + 1 ; q - >passed_det = passed_det; if (q - >depth > max_depth) max_depth = q - >depth; if (queue_top) { queue_top - > next = q; queue_top = q; } else q_prev100 = queue = queue_top = q; queued_paths + + ; pending_not_fuzzed + + ; cycles_wo_finds = 0 ; / * Set next_100 pointer for every 100th element (index 0 , 100 , etc) to allow faster iteration. * / if ((queued_paths - 1 ) % 100 = = 0 && queued_paths > 1 ) { q_prev100 - >next_100 = q; q_prev100 = q; } last_path_time = get_cur_time(); } |
15.load_auto
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 | static void load_auto(void) { u32 i; for (i = 0 ; i < USE_AUTO_EXTRAS; i + + ) { u8 tmp[MAX_AUTO_EXTRA + 1 ]; u8 * fn = alloc_printf( "%s/.state/auto_extras/auto_%06u" , in_dir, i); s32 fd, len ; fd = open (fn, O_RDONLY, 0600 ); if (fd < 0 ) { if (errno ! = ENOENT) PFATAL( "Unable to open '%s'" , fn); ck_free(fn); break ; } / * We read one byte more to cheaply detect tokens that are too long ( and skip them). * / len = read(fd, tmp, MAX_AUTO_EXTRA + 1 ); if ( len < 0 ) PFATAL( "Unable to read from '%s'" , fn); if ( len > = MIN_AUTO_EXTRA && len < = MAX_AUTO_EXTRA) maybe_add_auto(tmp, len ); close(fd); ck_free(fn); } if (i) OKF( "Loaded %u auto-discovered dictionary tokens." , i); else OKF( "No auto-generated dictionary tokens to reuse." ); } |
今天就分析到這裡,由於原始碼太多太長,加上自己沒有對fuzz有更深的理解,所以寫出來的註釋可能會有錯誤,接下來幾天應該是會一邊閱讀原始碼,一邊進行fuzzing了,加深理解
未完待續!!!
相關文章
- 【原始碼閱讀】AndPermission原始碼閱讀2019-05-09原始碼
- 【原始碼閱讀】Glide原始碼閱讀之with方法(一)2019-04-17原始碼IDE
- 【原始碼閱讀】Glide原始碼閱讀之into方法(三)2019-04-18原始碼IDE
- ReactorKit原始碼閱讀2019-03-03React原始碼
- AQS原始碼閱讀2022-04-22AQS原始碼
- CountDownLatch原始碼閱讀2021-12-25CountDownLatch原始碼
- HashMap 原始碼閱讀2021-09-09HashMap原始碼
- delta原始碼閱讀2021-09-01原始碼
- 原始碼閱讀-HashMap2018-08-15原始碼HashMap
- NGINX原始碼閱讀2019-01-19Nginx原始碼
- Mux 原始碼閱讀2020-11-23UX原始碼
- HashMap原始碼閱讀2020-11-26HashMap原始碼
- RunLoop 原始碼閱讀2018-04-17OOP原始碼
- express 原始碼閱讀2017-09-19Express原始碼
- muduo原始碼閱讀2016-04-07原始碼
- stack原始碼閱讀2024-06-02原始碼
- 【原始碼閱讀】Glide原始碼閱讀之load方法(二)2019-04-18原始碼IDE
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼2018-08-02SQL原始碼
- JDK原始碼閱讀:Object類閱讀筆記2021-09-18JDK原始碼Object筆記
- Laravel 原始碼閱讀 - Queue2019-08-06Laravel原始碼
- Vollery原始碼閱讀(—)2019-02-22原始碼
- 使用OpenGrok閱讀原始碼2019-03-01原始碼
- 如何閱讀Java原始碼?2019-03-25Java原始碼
- buffer 原始碼包閱讀2019-03-20原始碼
- 原始碼閱讀技巧篇2019-03-04原始碼
- 如何閱讀框架原始碼2019-03-04框架原始碼
- 再談原始碼閱讀2019-07-21原始碼
- Laravel 原始碼閱讀 - Eloquent2019-08-02Laravel原始碼
- 如何閱讀jdk原始碼?2019-04-13JDK原始碼
- express 原始碼閱讀(全)2019-02-16Express原始碼
- Vuex原始碼閱讀分析2019-02-16Vue原始碼
- React原始碼閱讀:setState2018-08-13React原始碼
- ArrayList原始碼閱讀(增)2019-01-19原始碼
- ThreadLocal原始碼閱讀2018-12-03thread原始碼
- snabbdom 原始碼閱讀分析2018-09-07原始碼
- koa原始碼閱讀[0]2018-07-23原始碼
- Hive原始碼閱讀之路2020-11-08Hive原始碼
- ConcurrentHashMap原始碼閱讀2020-11-26HashMap原始碼