diff options
Diffstat (limited to 'src/afl-analyze.c')
-rw-r--r-- | src/afl-analyze.c | 364 |
1 files changed, 188 insertions, 176 deletions
diff --git a/src/afl-analyze.c b/src/afl-analyze.c index 5bb96154..e3014256 100644 --- a/src/afl-analyze.c +++ b/src/afl-analyze.c @@ -22,7 +22,7 @@ #define AFL_MAIN #ifdef __ANDROID__ - #include "android-ashmem.h" +# include "android-ashmem.h" #endif #include "config.h" #include "types.h" @@ -50,61 +50,59 @@ #include <sys/types.h> #include <sys/resource.h> -static s32 child_pid; /* PID of the tested program */ +static s32 child_pid; /* PID of the tested program */ - u8* trace_bits; /* SHM with instrumentation bitmap */ +u8* trace_bits; /* SHM with instrumentation bitmap */ -static u8 *in_file, /* Analyzer input test case */ - *prog_in, /* Targeted program input file */ - *target_path, /* Path to target binary */ - *doc_path; /* Path to docs */ +static u8 *in_file, /* Analyzer input test case */ + *prog_in, /* Targeted program input file */ + *target_path, /* Path to target binary */ + *doc_path; /* Path to docs */ -static u8 *in_data; /* Input data for analysis */ +static u8* in_data; /* Input data for analysis */ -static u32 in_len, /* Input data length */ - orig_cksum, /* Original checksum */ - total_execs, /* Total number of execs */ - exec_hangs, /* Total number of hangs */ - exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */ +static u32 in_len, /* Input data length */ + orig_cksum, /* Original checksum */ + total_execs, /* Total number of execs */ + exec_hangs, /* Total number of hangs */ + exec_tmout = EXEC_TIMEOUT; /* Exec timeout (ms) */ -static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ +static u64 mem_limit = MEM_LIMIT; /* Memory limit (MB) */ -static s32 dev_null_fd = -1; /* FD to /dev/null */ +static s32 dev_null_fd = -1; /* FD to /dev/null */ -static u8 edges_only, /* Ignore hit counts? */ - use_hex_offsets, /* Show hex offsets? */ - use_stdin = 1; /* Use stdin for program input? */ - -static volatile u8 - stop_soon, /* Ctrl-C pressed? */ - child_timed_out; /* Child timed out? */ +static u8 edges_only, /* Ignore hit counts? */ + use_hex_offsets, /* Show hex offsets? */ + use_stdin = 1; /* Use stdin for program input? */ +static volatile u8 stop_soon, /* Ctrl-C pressed? */ + child_timed_out; /* Child timed out? */ /* Constants used for describing byte behavior. */ -#define RESP_NONE 0x00 /* Changing byte is a no-op. */ -#define RESP_MINOR 0x01 /* Some changes have no effect. */ -#define RESP_VARIABLE 0x02 /* Changes produce variable paths. */ -#define RESP_FIXED 0x03 /* Changes produce fixed patterns. */ - -#define RESP_LEN 0x04 /* Potential length field */ -#define RESP_CKSUM 0x05 /* Potential checksum */ -#define RESP_SUSPECT 0x06 /* Potential "suspect" blob */ +#define RESP_NONE 0x00 /* Changing byte is a no-op. */ +#define RESP_MINOR 0x01 /* Some changes have no effect. */ +#define RESP_VARIABLE 0x02 /* Changes produce variable paths. */ +#define RESP_FIXED 0x03 /* Changes produce fixed patterns. */ +#define RESP_LEN 0x04 /* Potential length field */ +#define RESP_CKSUM 0x05 /* Potential checksum */ +#define RESP_SUSPECT 0x06 /* Potential "suspect" blob */ -/* Classify tuple counts. This is a slow & naive version, but good enough here. */ +/* Classify tuple counts. This is a slow & naive version, but good enough here. + */ static u8 count_class_lookup[256] = { - [0] = 0, - [1] = 1, - [2] = 2, - [3] = 4, - [4 ... 7] = 8, - [8 ... 15] = 16, - [16 ... 31] = 32, - [32 ... 127] = 64, - [128 ... 255] = 128 + [0] = 0, + [1] = 1, + [2] = 2, + [3] = 4, + [4 ... 7] = 8, + [8 ... 15] = 16, + [16 ... 31] = 32, + [32 ... 127] = 64, + [128 ... 255] = 128 }; @@ -115,61 +113,62 @@ static void classify_counts(u8* mem) { if (edges_only) { while (i--) { + if (*mem) *mem = 1; mem++; + } } else { while (i--) { + *mem = count_class_lookup[*mem]; mem++; + } } } - /* See if any bytes are set in the bitmap. */ static inline u8 anything_set(void) { u32* ptr = (u32*)trace_bits; - u32 i = (MAP_SIZE >> 2); + u32 i = (MAP_SIZE >> 2); - while (i--) if (*(ptr++)) return 1; + while (i--) + if (*(ptr++)) return 1; return 0; } - /* Get rid of temp files (atexit handler). */ static void at_exit_handler(void) { - unlink(prog_in); /* Ignore errors */ + unlink(prog_in); /* Ignore errors */ } - /* Read initial file. */ static void read_initial_file(void) { struct stat st; - s32 fd = open(in_file, O_RDONLY); + s32 fd = open(in_file, O_RDONLY); if (fd < 0) PFATAL("Unable to open '%s'", in_file); - if (fstat(fd, &st) || !st.st_size) - FATAL("Zero-sized input file."); + if (fstat(fd, &st) || !st.st_size) FATAL("Zero-sized input file."); if (st.st_size >= TMIN_MAX_FILE) FATAL("Input file is too large (%u MB max)", TMIN_MAX_FILE / 1024 / 1024); - in_len = st.st_size; + in_len = st.st_size; in_data = ck_alloc_nozero(in_len); ck_read(fd, in_data, in_len, in_file); @@ -180,14 +179,13 @@ static void read_initial_file(void) { } - /* Write output file. */ static s32 write_to_file(u8* path, u8* mem, u32 len) { s32 ret; - unlink(path); /* Ignore errors */ + unlink(path); /* Ignore errors */ ret = open(path, O_RDWR | O_CREAT | O_EXCL, 0600); @@ -201,7 +199,6 @@ static s32 write_to_file(u8* path, u8* mem, u32 len) { } - /* Handle timeout signal. */ static void handle_timeout(int sig) { @@ -211,14 +208,13 @@ static void handle_timeout(int sig) { } - /* Execute target application. Returns exec checksum, or 0 if program times out. */ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { static struct itimerval it; - int status = 0; + int status = 0; s32 prog_in_fd; u32 cksum; @@ -237,8 +233,7 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { struct rlimit r; if (dup2(use_stdin ? prog_in_fd : dev_null_fd, 0) < 0 || - dup2(dev_null_fd, 1) < 0 || - dup2(dev_null_fd, 2) < 0) { + dup2(dev_null_fd, 1) < 0 || dup2(dev_null_fd, 2) < 0) { *(u32*)trace_bits = EXEC_FAIL_SIG; PFATAL("dup2() failed"); @@ -254,18 +249,18 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { #ifdef RLIMIT_AS - setrlimit(RLIMIT_AS, &r); /* Ignore errors */ + setrlimit(RLIMIT_AS, &r); /* Ignore errors */ #else - setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ + setrlimit(RLIMIT_DATA, &r); /* Ignore errors */ #endif /* ^RLIMIT_AS */ } r.rlim_max = r.rlim_cur = 0; - setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ + setrlimit(RLIMIT_CORE, &r); /* Ignore errors */ execv(target_path, argv); @@ -303,8 +298,10 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { total_execs++; if (stop_soon) { + SAYF(cRST cLRD "\n+++ Analysis aborted by user +++\n" cRST); exit(1); + } /* Always discard inputs that time out. */ @@ -335,7 +332,6 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { } - #ifdef USE_COLOR /* Helper function to display a human-readable character. */ @@ -353,24 +349,25 @@ static void show_char(u8 val) { } - /* Show the legend */ static void show_legend(void) { - SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block " - cBLK bgLGN " 01 " cRST " - suspected length field\n" - " " cBRI bgGRA " 01 " cRST " - superficial content " - cBLK bgYEL " 01 " cRST " - suspected cksum or magic int\n" - " " cBLK bgCYA " 01 " cRST " - critical stream " - cBLK bgLRD " 01 " cRST " - suspected checksummed block\n" + SAYF(" " cLGR bgGRA " 01 " cRST " - no-op block " cBLK bgLGN + " 01 " cRST + " - suspected length field\n" + " " cBRI bgGRA " 01 " cRST " - superficial content " cBLK bgYEL + " 01 " cRST + " - suspected cksum or magic int\n" + " " cBLK bgCYA " 01 " cRST " - critical stream " cBLK bgLRD + " 01 " cRST + " - suspected checksummed block\n" " " cBLK bgMGN " 01 " cRST " - \"magic value\" section\n\n"); } #endif /* USE_COLOR */ - /* Interpret and report a pattern in the input file. */ static void dump_hex(u8* buf, u32 len, u8* b_data) { @@ -385,7 +382,7 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { u32 rlen = 1; #endif /* ^USE_COLOR */ - u8 rtype = b_data[i] & 0x0f; + u8 rtype = b_data[i] & 0x0f; /* Look ahead to determine the length of run. */ @@ -404,51 +401,61 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { case 2: { - u16 val = *(u16*)(in_data + i); + u16 val = *(u16*)(in_data + i); + + /* Small integers may be length fields. */ - /* Small integers may be length fields. */ + if (val && (val <= in_len || SWAP16(val) <= in_len)) { - if (val && (val <= in_len || SWAP16(val) <= in_len)) { - rtype = RESP_LEN; - break; - } + rtype = RESP_LEN; + break; + + } - /* Uniform integers may be checksums. */ + /* Uniform integers may be checksums. */ - if (val && abs(in_data[i] - in_data[i + 1]) > 32) { - rtype = RESP_CKSUM; - break; - } + if (val && abs(in_data[i] - in_data[i + 1]) > 32) { + rtype = RESP_CKSUM; break; } + break; + + } + case 4: { - u32 val = *(u32*)(in_data + i); + u32 val = *(u32*)(in_data + i); - /* Small integers may be length fields. */ + /* Small integers may be length fields. */ - if (val && (val <= in_len || SWAP32(val) <= in_len)) { - rtype = RESP_LEN; - break; - } + if (val && (val <= in_len || SWAP32(val) <= in_len)) { - /* Uniform integers may be checksums. */ + rtype = RESP_LEN; + break; - if (val && (in_data[i] >> 7 != in_data[i + 1] >> 7 || - in_data[i] >> 7 != in_data[i + 2] >> 7 || - in_data[i] >> 7 != in_data[i + 3] >> 7)) { - rtype = RESP_CKSUM; - break; - } + } + /* Uniform integers may be checksums. */ + + if (val && (in_data[i] >> 7 != in_data[i + 1] >> 7 || + in_data[i] >> 7 != in_data[i + 2] >> 7 || + in_data[i] >> 7 != in_data[i + 3] >> 7)) { + + rtype = RESP_CKSUM; break; } - case 1: case 3: case 5 ... MAX_AUTO_EXTRA - 1: break; + break; + + } + + case 1: + case 3: + case 5 ... MAX_AUTO_EXTRA - 1: break; default: rtype = RESP_SUSPECT; @@ -477,19 +484,22 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { switch (rtype) { - case RESP_NONE: SAYF(cLGR bgGRA); break; - case RESP_MINOR: SAYF(cBRI bgGRA); break; + case RESP_NONE: SAYF(cLGR bgGRA); break; + case RESP_MINOR: SAYF(cBRI bgGRA); break; case RESP_VARIABLE: SAYF(cBLK bgCYA); break; - case RESP_FIXED: SAYF(cBLK bgMGN); break; - case RESP_LEN: SAYF(cBLK bgLGN); break; - case RESP_CKSUM: SAYF(cBLK bgYEL); break; - case RESP_SUSPECT: SAYF(cBLK bgLRD); break; + case RESP_FIXED: SAYF(cBLK bgMGN); break; + case RESP_LEN: SAYF(cBLK bgLGN); break; + case RESP_CKSUM: SAYF(cBLK bgYEL); break; + case RESP_SUSPECT: SAYF(cBLK bgLRD); break; } show_char(in_data[i + off]); - if (off != rlen - 1 && (i + off + 1) % 16) SAYF(" "); else SAYF(cRST " "); + if (off != rlen - 1 && (i + off + 1) % 16) + SAYF(" "); + else + SAYF(cRST " "); } @@ -502,13 +512,13 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { switch (rtype) { - case RESP_NONE: SAYF("no-op block\n"); break; - case RESP_MINOR: SAYF("superficial content\n"); break; + case RESP_NONE: SAYF("no-op block\n"); break; + case RESP_MINOR: SAYF("superficial content\n"); break; case RESP_VARIABLE: SAYF("critical stream\n"); break; - case RESP_FIXED: SAYF("\"magic value\" section\n"); break; - case RESP_LEN: SAYF("suspected length field\n"); break; - case RESP_CKSUM: SAYF("suspected cksum or magic int\n"); break; - case RESP_SUSPECT: SAYF("suspected checksummed block\n"); break; + case RESP_FIXED: SAYF("\"magic value\" section\n"); break; + case RESP_LEN: SAYF("suspected length field\n"); break; + case RESP_CKSUM: SAYF("suspected cksum or magic int\n"); break; + case RESP_SUSPECT: SAYF("suspected checksummed block\n"); break; } @@ -524,8 +534,6 @@ static void dump_hex(u8* buf, u32 len, u8* b_data) { } - - /* Actually analyze! */ static void analyze(char** argv) { @@ -536,7 +544,7 @@ static void analyze(char** argv) { u8* b_data = ck_alloc(in_len + 1); u8 seq_byte = 0; - b_data[in_len] = 0xff; /* Intentional terminator. */ + b_data[in_len] = 0xff; /* Intentional terminator. */ ACTF("Analyzing input file (this may take a while)...\n"); @@ -587,12 +595,15 @@ static void analyze(char** argv) { b_data[i] = RESP_FIXED; - } else b_data[i] = RESP_VARIABLE; + } else + + b_data[i] = RESP_VARIABLE; /* When all checksums change, flip most significant bit of b_data. */ - if (prev_xff != xor_ff && prev_x01 != xor_01 && - prev_s10 != sub_10 && prev_a10 != add_10) seq_byte ^= 0x80; + if (prev_xff != xor_ff && prev_x01 != xor_01 && prev_s10 != sub_10 && + prev_a10 != add_10) + seq_byte ^= 0x80; b_data[i] |= seq_byte; @@ -601,7 +612,7 @@ static void analyze(char** argv) { prev_s10 = sub_10; prev_a10 = add_10; - } + } dump_hex(in_data, in_len, b_data); @@ -618,8 +629,6 @@ static void analyze(char** argv) { } - - /* Handle Ctrl-C and the like. */ static void handle_stop_sig(int sig) { @@ -630,7 +639,6 @@ static void handle_stop_sig(int sig) { } - /* Do basic preparations - persistent fds, filenames, etc. */ static void set_up_environment(void) { @@ -674,18 +682,20 @@ static void set_up_environment(void) { if (x) { if (!strstr(x, "exit_code=" STRINGIFY(MSAN_ERROR))) - FATAL("Custom MSAN_OPTIONS set without exit_code=" - STRINGIFY(MSAN_ERROR) " - please fix!"); + 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!"); } - setenv("ASAN_OPTIONS", "abort_on_error=1:" - "detect_leaks=0:" - "symbolize=0:" - "allocator_may_return_null=1", 0); + setenv("ASAN_OPTIONS", + "abort_on_error=1:" + "detect_leaks=0:" + "symbolize=0:" + "allocator_may_return_null=1", + 0); setenv("MSAN_OPTIONS", "exit_code=" STRINGIFY(MSAN_ERROR) ":" "symbolize=0:" @@ -694,21 +704,22 @@ static void set_up_environment(void) { "msan_track_origins=0", 0); if (getenv("AFL_PRELOAD")) { + setenv("LD_PRELOAD", getenv("AFL_PRELOAD"), 1); setenv("DYLD_INSERT_LIBRARIES", getenv("AFL_PRELOAD"), 1); + } } - /* Setup signal handlers, duh. */ static void setup_signal_handlers(void) { struct sigaction sa; - sa.sa_handler = NULL; - sa.sa_flags = SA_RESTART; + sa.sa_handler = NULL; + sa.sa_flags = SA_RESTART; sa.sa_sigaction = NULL; sigemptyset(&sa.sa_mask); @@ -727,43 +738,42 @@ static void setup_signal_handlers(void) { } - /* Display usage hints. */ static void usage(u8* argv0) { - SAYF("\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" + SAYF( + "\n%s [ options ] -- /path/to/target_app [ ... ]\n\n" - "Required parameters:\n\n" + "Required parameters:\n\n" - " -i file - input test case to be analyzed by the tool\n" + " -i file - input test case to be analyzed by the tool\n" - "Execution control settings:\n\n" + "Execution control settings:\n\n" - " -f file - input file read by the tested program (stdin)\n" - " -t msec - timeout for each run (%d ms)\n" - " -m megs - memory limit for child process (%d MB)\n" - " -Q - use binary-only instrumentation (QEMU mode)\n" - " -U - use unicorn-based instrumentation (Unicorn mode)\n\n" + " -f file - input file read by the tested program (stdin)\n" + " -t msec - timeout for each run (%d ms)\n" + " -m megs - memory limit for child process (%d MB)\n" + " -Q - use binary-only instrumentation (QEMU mode)\n" + " -U - use unicorn-based instrumentation (Unicorn mode)\n\n" - "Analysis settings:\n\n" + "Analysis settings:\n\n" - " -e - look for edge coverage only, ignore hit counts\n\n" + " -e - look for edge coverage only, ignore hit counts\n\n" - "For additional tips, please consult %s/README.\n\n", + "For additional tips, please consult %s/README.\n\n", - argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); + argv0, EXEC_TIMEOUT, MEM_LIMIT, doc_path); exit(1); } - /* Find binary. */ static void find_binary(u8* fname) { - u8* env_path = 0; + u8* env_path = 0; struct stat st; if (strchr(fname, '/') || !(env_path = getenv("PATH"))) { @@ -786,7 +796,9 @@ static void find_binary(u8* fname) { memcpy(cur_elem, env_path, delim - env_path); delim++; - } else cur_elem = ck_strdup(env_path); + } else + + cur_elem = ck_strdup(env_path); env_path = delim; @@ -798,7 +810,8 @@ static void find_binary(u8* fname) { ck_free(cur_elem); if (!stat(target_path, &st) && S_ISREG(st.st_mode) && - (st.st_mode & 0111) && st.st_size >= 4) break; + (st.st_mode & 0111) && st.st_size >= 4) + break; ck_free(target_path); target_path = 0; @@ -811,13 +824,12 @@ static void find_binary(u8* fname) { } - /* Fix up argv for QEMU. */ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { char** new_argv = ck_alloc(sizeof(char*) * (argc + 4)); - u8 *tmp, *cp, *rsl, *own_copy; + u8 * tmp, *cp, *rsl, *own_copy; memcpy(new_argv + 3, argv + 1, sizeof(char*) * argc); @@ -832,8 +844,7 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { cp = alloc_printf("%s/afl-qemu-trace", tmp); - if (access(cp, X_OK)) - FATAL("Unable to find '%s'", tmp); + if (access(cp, X_OK)) FATAL("Unable to find '%s'", tmp); target_path = new_argv[0] = cp; return new_argv; @@ -857,7 +868,9 @@ static char** get_qemu_argv(u8* own_loc, char** argv, int argc) { } - } else ck_free(own_copy); + } else + + ck_free(own_copy); if (!access(BIN_PATH "/afl-qemu-trace", X_OK)) { @@ -882,7 +895,7 @@ int main(int argc, char** argv) { SAYF(cCYA "afl-analyze" VERSION cRST " by <lcamtuf@google.com>\n"); - while ((opt = getopt(argc,argv,"+i:f:m:t:eQU")) > 0) + while ((opt = getopt(argc, argv, "+i:f:m:t:eQU")) > 0) switch (opt) { @@ -896,7 +909,7 @@ int main(int argc, char** argv) { if (prog_in) FATAL("Multiple -f options not supported"); use_stdin = 0; - prog_in = optarg; + prog_in = optarg; break; case 'e': @@ -907,40 +920,41 @@ int main(int argc, char** argv) { case 'm': { - u8 suffix = 'M'; + u8 suffix = 'M'; - if (mem_limit_given) FATAL("Multiple -m options not supported"); - mem_limit_given = 1; + if (mem_limit_given) FATAL("Multiple -m options not supported"); + mem_limit_given = 1; - if (!strcmp(optarg, "none")) { + if (!strcmp(optarg, "none")) { - mem_limit = 0; - break; + mem_limit = 0; + break; - } + } - if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || - optarg[0] == '-') FATAL("Bad syntax used for -m"); + if (sscanf(optarg, "%llu%c", &mem_limit, &suffix) < 1 || + optarg[0] == '-') + FATAL("Bad syntax used for -m"); - switch (suffix) { + switch (suffix) { - case 'T': mem_limit *= 1024 * 1024; break; - case 'G': mem_limit *= 1024; break; - case 'k': mem_limit /= 1024; break; - case 'M': break; + 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"); + default: FATAL("Unsupported suffix or bad syntax for -m"); - } + } - if (mem_limit < 5) FATAL("Dangerously low value of -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"); + if (sizeof(rlim_t) == 4 && mem_limit > 2000) + FATAL("Value of -m out of range on 32-bit systems"); - } + } - break; + break; case 't': @@ -970,9 +984,7 @@ int main(int argc, char** argv) { unicorn_mode = 1; break; - default: - - usage(argv[0]); + default: usage(argv[0]); } |