diff --git a/Makefile b/Makefile index b8a7096d..afcb61a6 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ # PROGNAME = afl -VERSION = 1.98b +VERSION = 1.99b PREFIX ?= /usr/local BIN_PATH = $(PREFIX)/bin diff --git a/afl-analyze.c b/afl-analyze.c index e94f74da..426f76cf 100644 --- a/afl-analyze.c +++ b/afl-analyze.c @@ -52,10 +52,9 @@ static u8 *in_file, /* Analyzer input test case */ *target_path, /* Path to target binary */ *doc_path; /* Path to docs */ -static u8* in_data; /* Input data for trimming */ +static u8 *in_data; /* Input data for analysis */ static u32 in_len, /* Input data length */ - boring_len, /* Bytes that don't do anything */ orig_cksum, /* Original checksum */ total_execs, /* Total number of execs */ exec_hangs, /* Total number of hangs */ @@ -74,6 +73,18 @@ static volatile u8 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 */ + + /* Classify tuple counts. This is a slow & naive version, but good enough here. */ #define AREP4(_sym) (_sym), (_sym), (_sym), (_sym) @@ -131,7 +142,6 @@ static inline u8 anything_set(void) { } - /* Get rid of shared memory and temp files (atexit handler). */ static void remove_shm(void) { @@ -316,7 +326,7 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { total_execs++; if (stop_soon) { - SAYF(cLRD "\n+++ Analysis aborted by user +++\n" cRST); + SAYF(cRST cLRD "\n+++ Analysis aborted by user +++\n" cRST); exit(1); } @@ -349,6 +359,8 @@ static u32 run_target(char** argv, u8* mem, u32 len, u8 first_run) { } +#ifdef USE_COLOR + /* Helper function to display a human-readable character. */ static void show_char(u8 val) { @@ -356,166 +368,176 @@ static void show_char(u8 val) { switch (val) { case 0 ... 32: - case 127 ... 255: SAYF(cMGN "%02x " cNOR, val); break; + case 127 ... 255: SAYF("#%02x", val); break; - default: SAYF(cCYA "%c " cNOR, val); + default: SAYF(" %c ", val); } } -/* Constants used for describing byte runs. */ +/* Show the legend */ -#define RUN_BORING 0 /* A no-op run. */ -#define RUN_VARIABLE 1 /* A run with variable checksums. */ -#define RUN_FIXED 2 /* A run with constant checksums. */ +static void show_legend(void) { -/* Interpret and report a pattern in the input file. */ + SAYF(" " bgGRA cLGR " 01 " cRST " - no-op block " + bgLGN cBLK " 01 " cRST " - suspected length field\n" + " " bgGRA cBRI " 01 " cRST " - superficial content " + bgYEL cBLK " 01 " cRST " - suspected cksum or magic int\n" + " " bgCYA cBLK " 01 " cRST " - critical stream " + bgLRD cBLK " 01 " cRST " - suspected checksummed block\n" + " " bgMGN cBLK " 01 " cRST " - \"magic value\" section\n\n"); -static void report_run(u32 st_pos, u32 len, u8 type, u8 boring_01) { +} - u32 i; +#endif /* USE_COLOR */ - /* Start by showing a sample of data. */ - SAYF(cGRA "[%06u] " cNOR, st_pos); +/* Interpret and report a pattern in the input file. */ - if (len <= 22 || len >= 44) { +static void dump_hex(u8* buf, u32 len, u8* b_data) { - /* Very short or very long buffer. Start by showing head. */ + u32 i; - for (i = 0; i < MIN(len, 22); i++) show_char(in_data[st_pos + i]); - SAYF("\n"); + for (i = 0; i < len; i++) { - /* Show tail, if any. */ +#ifdef USE_COLOR + u32 rlen = 1, off; +#else + u32 rlen = 1; +#endif /* ^USE_COLOR */ - if (len >= 44) { + u8 rtype = b_data[i] & 0x0f; - u32 tail_pos = st_pos + len - 22; + /* Look ahead to determine the length of run. */ - if (len > 44) SAYF(cGRA " ....\n"); - SAYF(cGRA "[%06u] " cNOR, tail_pos); + while (i + rlen < len && (b_data[i] >> 7) == (b_data[i + rlen] >> 7)) { - for (i = 0; i < 22; i++) show_char(in_data[tail_pos + i]); - SAYF("\n"); + if (rtype < (b_data[i + rlen] & 0x0f)) rtype = b_data[i + rlen] & 0x0f; + rlen++; } - } else { + /* Try to do some further classification based on length & value. */ - /* Intermediate length buffer (23-43 bytes). Show abridged - single-line version. */ + if (rtype == RESP_FIXED) { - u32 tail_pos = st_pos + len - 10; + switch (rlen) { - for (i = 0; i < 10; i++) show_char(in_data[st_pos + i]); - SAYF(cGRA "(...) " cNOR); + case 2: { - for (i = 0; i < 10; i++) show_char(in_data[tail_pos + i]); - SAYF("\n"); + u16 val = *(u16*)(in_data + i); - } + /* Small integers may be length fields. */ - /* Now, attempt to classify. */ + if (val && (val <= in_len || SWAP16(val) <= in_len)) { + rtype = RESP_LEN; + break; + } - if (type == RUN_BORING) { + /* Uniform integers may be checksums. */ - SAYF(cBRI " `-> Apparent no-op %s (len = %u)\n" cNOR, - (len == 1) ? "byte" : "blob", len); - return; + if (val && abs(in_data[i] - in_data[i + 1]) > 32) { + rtype = RESP_CKSUM; + break; + } - } + break; - if (type == RUN_VARIABLE) { + } - SAYF(cBRI " `-> Critical %s (len = %u)\n" cNOR, - (len == 1) ? "byte" : "data blob", len); - return; + case 4: { - } + u32 val = *(u32*)(in_data + i); - if (len > 2 && boring_01) { + /* Small integers may be length fields. */ - SAYF(cBRI " `-> Possibly no-op string (len = %u)\n" cNOR, len); - return; + if (val && (val <= in_len || SWAP32(val) <= in_len)) { + rtype = RESP_LEN; + break; + } - } + /* Uniform integers may be checksums. */ - switch (len) { + 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; + } - /* Lengths 2 and 4 may be checksums, magic values, or length fields. Let's - be smart about classifying them. */ + break; - case 2: { + } - u16 val = *(u16*)(in_data + st_pos); + case 1: case 3: case 5 ... MAX_AUTO_EXTRA - 1: break; - if (val && (val <= in_len || SWAP16(val) <= in_len)) { + default: rtype = RESP_SUSPECT; - SAYF(cBRI " `-> Potential length field (len = 2)\n" cNOR); - break; + } - } + } - if (in_data[st_pos] && in_data[st_pos + 1] && - !(in_data[st_pos] < 32 && in_data[st_pos + 1] < 32) && - !(isalnum(in_data[st_pos]) && isalnum(in_data[st_pos + 1])) && - !(in_data[st_pos] > 128 && in_data[st_pos + 1] > 128)) { + /* Print out the entire run. */ - SAYF(cBRI " `-> Potential checksum or magic value (len = 2)" - "\n" cNOR); +#ifdef USE_COLOR - break; + for (off = 0; off < rlen; off++) { - } + /* Every 16 digits, display offset. */ - SAYF(cBRI " `-> Atomically compared value (len = 2)\n" cNOR); - break; + if (!((i + off) % 16)) { + + if (off) SAYF(cRST cLCY ">"); + SAYF(cRST cGRA "%s[%06u] " cRST, (i + off) ? "\n" : "", i + off); } - case 4: { + switch (rtype) { - u32 val = *(u32*)(in_data + st_pos); + case RESP_NONE: SAYF(bgGRA cLGR); break; + case RESP_MINOR: SAYF(bgGRA cBRI); break; + case RESP_VARIABLE: SAYF(bgCYA cBLK); break; + case RESP_FIXED: SAYF(bgMGN cBLK); break; + case RESP_LEN: SAYF(bgLGN cBLK); break; + case RESP_CKSUM: SAYF(bgYEL cBLK); break; + case RESP_SUSPECT: SAYF(bgLRD cBLK); break; - if (val && (val <= in_len || SWAP32(val) <= in_len)) { + } - SAYF(cBRI " `-> Potential length field (len = 2)\n" cNOR); - break; + show_char(in_data[i + off]); - } + if (off != rlen - 1 && (i + off + 1) % 16) SAYF(" "); else SAYF(cRST " "); - if (in_data[st_pos] && in_data[st_pos + 1] && - in_data[st_pos + 2] && in_data[st_pos + 3] && - !(in_data[st_pos] < 128 && in_data[st_pos + 1] < 128 && - in_data[st_pos + 2] < 128 && in_data[st_pos + 3] < 128) && - !(in_data[st_pos] > 128 && in_data[st_pos + 1] > 128 && - in_data[st_pos + 2] > 128 && in_data[st_pos + 3] > 128)) { + } - SAYF(cBRI " `-> Potential checksum or magic value (len = 4)" - "\n" cNOR); +#else - break; + SAYF(" Offset %u, length %u: ", i, rlen); - } + switch (rtype) { - SAYF(cBRI " `-> Atomically compared value (len = 4)\n" cNOR); - 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 3: case 5 ... MAX_AUTO_EXTRA - 1: - SAYF(cBRI " `-> Atomically compared token (len = %u)\n" cNOR, len); - break; +#endif /* ^USE_COLOR */ - default: - SAYF(cLRD " `-> Potential checksummed or encrypted blob " - "(len = %u)\n" cNOR, len); - break; + i += rlen - 1; } +#ifdef USE_COLOR + SAYF(cRST "\n"); +#endif /* USE_COLOR */ + } @@ -525,135 +547,79 @@ static void report_run(u32 st_pos, u32 len, u8 type, u8 boring_01) { static void analyze(char** argv) { u32 i; - u32 cur_run_len = RUN_BORING, prev_ck01 = 0, cur_01_boring = 0; - u8 cur_run_type = 0; + u32 boring_len = 0, prev_xff = 0, prev_x01 = 0, prev_s10 = 0, prev_a10 = 0; - ACTF("Analyzing input file..."); + u8* b_data = ck_alloc(in_len + 1); + u8 seq_byte = 0; - SAYF("\n"); - - /* Do walking byte flips. We flip all bits (xor 0xff) to get a definite - answer if the byte is meaningful to the tested program; but later - also flip the least significant bit (or 0x01) to better detect text-based - syntax tokens. - - We use the 0x01-flip data in two ways: + b_data[in_len] = 0xff; /* Intentional terminator. */ - - To classify some runs of bytes with identical post-0x01-flip exec - paths as corresponding to a single syntax token, a blob of checksummed - data, etc. + ACTF("Analyzing input file (this may take a while)...\n"); - - To demote some such runs to "no-op strings" when 0xff flips produce - different exec paths, but 0x01 flips consistently match baseline. - - */ +#ifdef USE_COLOR + show_legend(); +#endif /* USE_COLOR */ for (i = 0; i < in_len; i++) { - u32 cksum_ff = 0, cksum_01 = 0; - u8 saw_change; - - in_data[i] ^= 0xff; - cksum_ff = run_target(argv, in_data, in_len, 0); - - if (cksum_ff != orig_cksum) { - - saw_change = 1; - - in_data[i] ^= 0xfe; - cksum_01 = run_target(argv, in_data, in_len, 0); - in_data[i] ^= 0x01; - - } else { - - saw_change = 0; - in_data[i] ^= 0xff; - prev_ck01 = 0; - boring_len++; - - } - - /* Check for transitions between types of byte runs. */ - - if (cur_run_len) { - - /* Previous run was boring, but we're now seeing checksum changes. */ - - if (cur_run_type == RUN_BORING && saw_change) { + u32 xor_ff, xor_01, sub_10, add_10; + u8 xff_orig, x01_orig, s10_orig, a10_orig; - report_run(i - cur_run_len, cur_run_len, RUN_BORING, 1); + /* Perform walking byte adjustments across the file. We perform four + operations designed to elicit some response from the underlying + code. */ - cur_run_len = 0; - cur_run_type = RUN_VARIABLE; - cur_01_boring = 0; - - } else - - /* Previous run was non-boring, but we no longer see changes. */ - - if (cur_run_type != RUN_BORING && !saw_change) { - - report_run(i - cur_run_len, cur_run_len, cur_run_type, - (cur_01_boring == cur_run_len)); - - cur_run_len = 0; - cur_run_type = RUN_BORING; - cur_01_boring = 0; - - } else - - /* Current run was fixed, but we're now seeing different cksums. */ - - if (cur_run_type == RUN_FIXED && prev_ck01 != cksum_01) { - - if (cur_run_len > 1) { + in_data[i] ^= 0xff; + xor_ff = run_target(argv, in_data, in_len, 0); - report_run(i - cur_run_len, cur_run_len, RUN_FIXED, - (cur_01_boring == cur_run_len)); + in_data[i] ^= 0xfe; + xor_01 = run_target(argv, in_data, in_len, 0); - cur_run_len = 0; - cur_01_boring = 0; + in_data[i] = (in_data[i] ^ 0x01) - 0x10; + sub_10 = run_target(argv, in_data, in_len, 0); - } + in_data[i] += 0x20; + add_10 = run_target(argv, in_data, in_len, 0); + in_data[i] -= 0x10; - cur_run_type = RUN_VARIABLE; + /* Classify current behavior. */ - } else + xff_orig = (xor_ff == orig_cksum); + x01_orig = (xor_01 == orig_cksum); + s10_orig = (sub_10 == orig_cksum); + a10_orig = (add_10 == orig_cksum); - /* Current run was variable, but we're now seeing const cksums. */ + if (xff_orig && x01_orig && s10_orig && a10_orig) { - if (cur_run_type == RUN_VARIABLE && prev_ck01 == cksum_01) { + b_data[i] = RESP_NONE; + boring_len++; - if (cur_run_len > 1) - report_run(i - cur_run_len, cur_run_len - 1, RUN_VARIABLE, - (cur_01_boring == cur_run_len)); + } else if (xff_orig || x01_orig || s10_orig || a10_orig) { - cur_run_len = 1; - cur_run_type = RUN_FIXED; + b_data[i] = RESP_MINOR; + boring_len++; - if (prev_ck01 == orig_cksum) cur_01_boring = 1; - else cur_01_boring = 0; - - } + } else if (xor_ff == xor_01 && xor_ff == sub_10 && xor_ff == add_10) { - } else { + b_data[i] = RESP_FIXED; - if (saw_change) cur_run_type = RUN_VARIABLE; - else cur_run_type = RUN_BORING; + } else b_data[i] = RESP_VARIABLE; - } + /* When all checksums change, flip most significant bit of b_data. */ - if (cksum_01 == orig_cksum) cur_01_boring++; + if (prev_xff != xor_ff && prev_x01 != xor_01 && + prev_s10 != sub_10 && prev_a10 != add_10) seq_byte ^= 0x80; - cur_run_len++; - prev_ck01 = cksum_01; + b_data[i] |= seq_byte; - } + prev_xff = xor_ff; + prev_x01 = xor_01; + prev_s10 = sub_10; + prev_a10 = add_10; - /* Report any tail... */ + } - report_run(in_len - cur_run_len, cur_run_len, cur_run_type, - (cur_01_boring == cur_run_len)); + dump_hex(in_data, in_len, b_data); SAYF("\n"); @@ -664,6 +630,8 @@ static void analyze(char** argv) { WARNF(cLRD "Encountered %u timeouts - results may be skewed." cRST, exec_hangs); + ck_free(b_data); + } diff --git a/afl-fuzz.c b/afl-fuzz.c index 790c36d7..0daa2677 100644 --- a/afl-fuzz.c +++ b/afl-fuzz.c @@ -3751,7 +3751,7 @@ static void show_stats(void) { if (term_too_small) { SAYF(cBRI "Your terminal is too small to display the UI.\n" - "Please resize terminal window to at least 80x25.\n" cNOR); + "Please resize terminal window to at least 80x25.\n" cRST); return; @@ -3788,7 +3788,7 @@ static void show_stats(void) { if (dumb_mode) { - strcpy(tmp, cNOR); + strcpy(tmp, cRST); } else { @@ -3806,7 +3806,7 @@ static void show_stats(void) { } - SAYF(bV bSTOP " run time : " cNOR "%-34s " bSTG bV bSTOP + SAYF(bV bSTOP " run time : " cRST "%-34s " bSTG bV bSTOP " cycles done : %s%-5s " bSTG bV "\n", DTD(cur_ms, start_time), tmp, DI(queue_cycle - 1)); @@ -3816,24 +3816,24 @@ static void show_stats(void) { if (!dumb_mode && (last_path_time || resuming_fuzz || queue_cycle == 1 || in_bitmap || crash_mode)) { - SAYF(bV bSTOP " last new path : " cNOR "%-34s ", + SAYF(bV bSTOP " last new path : " cRST "%-34s ", DTD(cur_ms, last_path_time)); } else { if (dumb_mode) - SAYF(bV bSTOP " last new path : " cPIN "n/a" cNOR + SAYF(bV bSTOP " last new path : " cPIN "n/a" cRST " (non-instrumented mode) "); else - SAYF(bV bSTOP " last new path : " cNOR "none yet " cLRD + SAYF(bV bSTOP " last new path : " cRST "none yet " cLRD "(odd, check syntax!) "); } - SAYF(bSTG bV bSTOP " total paths : " cNOR "%-5s " bSTG bV "\n", + SAYF(bSTG bV bSTOP " total paths : " cRST "%-5s " bSTG bV "\n", DI(queued_paths)); /* Highlight crashes in red if found, denote going over the KEEP_UNIQUE_CRASH @@ -3842,16 +3842,16 @@ static void show_stats(void) { sprintf(tmp, "%s%s", DI(unique_crashes), (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); - SAYF(bV bSTOP " last uniq crash : " cNOR "%-34s " bSTG bV bSTOP + SAYF(bV bSTOP " last uniq crash : " cRST "%-34s " bSTG bV bSTOP " uniq crashes : %s%-6s " bSTG bV "\n", - DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cNOR, + DTD(cur_ms, last_crash_time), unique_crashes ? cLRD : cRST, tmp); sprintf(tmp, "%s%s", DI(unique_hangs), (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); - SAYF(bV bSTOP " last uniq hang : " cNOR "%-34s " bSTG bV bSTOP - " uniq hangs : " cNOR "%-6s " bSTG bV "\n", + SAYF(bV bSTOP " last uniq hang : " cRST "%-34s " bSTG bV bSTOP + " uniq hangs : " cRST "%-6s " bSTG bV "\n", DTD(cur_ms, last_hang_time), tmp); SAYF(bVR bH bSTOP cCYA " cycle progress " bSTG bH20 bHB bH bSTOP cCYA @@ -3865,23 +3865,23 @@ static void show_stats(void) { queue_cur->favored ? "" : "*", ((double)current_entry * 100) / queued_paths); - SAYF(bV bSTOP " now processing : " cNOR "%-17s " bSTG bV bSTOP, tmp); + SAYF(bV bSTOP " now processing : " cRST "%-17s " bSTG bV bSTOP, tmp); sprintf(tmp, "%s (%0.02f%%)", DI(t_bytes), t_byte_ratio); SAYF(" map density : %s%-21s " bSTG bV "\n", t_byte_ratio > 70 ? cLRD : - ((t_bytes < 200 && !dumb_mode) ? cPIN : cNOR), tmp); + ((t_bytes < 200 && !dumb_mode) ? cPIN : cRST), tmp); sprintf(tmp, "%s (%0.02f%%)", DI(cur_skipped_paths), ((double)cur_skipped_paths * 100) / queued_paths); - SAYF(bV bSTOP " paths timed out : " cNOR "%-17s " bSTG bV, tmp); + SAYF(bV bSTOP " paths timed out : " cRST "%-17s " bSTG bV, tmp); sprintf(tmp, "%0.02f bits/tuple", t_bytes ? (((double)t_bits) / t_bytes) : 0); - SAYF(bSTOP " count coverage : " cNOR "%-21s " bSTG bV "\n", tmp); + SAYF(bSTOP " count coverage : " cRST "%-21s " bSTG bV "\n", tmp); SAYF(bVR bH bSTOP cCYA " stage progress " bSTG bH20 bX bH bSTOP cCYA " findings in depth " bSTG bH20 bVL "\n"); @@ -3891,8 +3891,8 @@ static void show_stats(void) { /* Yeah... it's still going on... halp? */ - SAYF(bV bSTOP " now trying : " cNOR "%-21s " bSTG bV bSTOP - " favored paths : " cNOR "%-22s " bSTG bV "\n", stage_name, tmp); + SAYF(bV bSTOP " now trying : " cRST "%-21s " bSTG bV bSTOP + " favored paths : " cRST "%-22s " bSTG bV "\n", stage_name, tmp); if (!stage_max) { @@ -3905,27 +3905,27 @@ static void show_stats(void) { } - SAYF(bV bSTOP " stage execs : " cNOR "%-21s " bSTG bV bSTOP, tmp); + SAYF(bV bSTOP " stage execs : " cRST "%-21s " bSTG bV bSTOP, tmp); sprintf(tmp, "%s (%0.02f%%)", DI(queued_with_cov), ((double)queued_with_cov) * 100 / queued_paths); - SAYF(" new edges on : " cNOR "%-22s " bSTG bV "\n", tmp); + SAYF(" new edges on : " cRST "%-22s " bSTG bV "\n", tmp); sprintf(tmp, "%s (%s%s unique)", DI(total_crashes), DI(unique_crashes), (unique_crashes >= KEEP_UNIQUE_CRASH) ? "+" : ""); if (crash_mode) { - SAYF(bV bSTOP " total execs : " cNOR "%-21s " bSTG bV bSTOP + SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP " new crashes : %s%-22s " bSTG bV "\n", DI(total_execs), - unique_crashes ? cLRD : cNOR, tmp); + unique_crashes ? cLRD : cRST, tmp); } else { - SAYF(bV bSTOP " total execs : " cNOR "%-21s " bSTG bV bSTOP + SAYF(bV bSTOP " total execs : " cRST "%-21s " bSTG bV bSTOP " total crashes : %s%-22s " bSTG bV "\n", DI(total_execs), - unique_crashes ? cLRD : cNOR, tmp); + unique_crashes ? cLRD : cRST, tmp); } @@ -3941,14 +3941,14 @@ static void show_stats(void) { } else { sprintf(tmp, "%s/sec", DF(avg_exec)); - SAYF(bV bSTOP " exec speed : " cNOR "%-21s ", tmp); + SAYF(bV bSTOP " exec speed : " cRST "%-21s ", tmp); } sprintf(tmp, "%s (%s%s unique)", DI(total_hangs), DI(unique_hangs), (unique_hangs >= KEEP_UNIQUE_HANG) ? "+" : ""); - SAYF (bSTG bV bSTOP " total hangs : " cNOR "%-22s " bSTG bV "\n", tmp); + SAYF (bSTG bV bSTOP " total hangs : " cRST "%-22s " bSTG bV "\n", tmp); /* Aaaalmost there... hold on! */ @@ -3968,8 +3968,8 @@ static void show_stats(void) { } - SAYF(bV bSTOP " bit flips : " cNOR "%-37s " bSTG bV bSTOP " levels : " - cNOR "%-10s " bSTG bV "\n", tmp, DI(max_depth)); + SAYF(bV bSTOP " bit flips : " cRST "%-37s " bSTG bV bSTOP " levels : " + cRST "%-10s " bSTG bV "\n", tmp, DI(max_depth)); if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", @@ -3977,8 +3977,8 @@ static void show_stats(void) { DI(stage_finds[STAGE_FLIP16]), DI(stage_cycles[STAGE_FLIP16]), DI(stage_finds[STAGE_FLIP32]), DI(stage_cycles[STAGE_FLIP32])); - SAYF(bV bSTOP " byte flips : " cNOR "%-37s " bSTG bV bSTOP " pending : " - cNOR "%-10s " bSTG bV "\n", tmp, DI(pending_not_fuzzed)); + SAYF(bV bSTOP " byte flips : " cRST "%-37s " bSTG bV bSTOP " pending : " + cRST "%-10s " bSTG bV "\n", tmp, DI(pending_not_fuzzed)); if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", @@ -3986,8 +3986,8 @@ static void show_stats(void) { DI(stage_finds[STAGE_ARITH16]), DI(stage_cycles[STAGE_ARITH16]), DI(stage_finds[STAGE_ARITH32]), DI(stage_cycles[STAGE_ARITH32])); - SAYF(bV bSTOP " arithmetics : " cNOR "%-37s " bSTG bV bSTOP " pend fav : " - cNOR "%-10s " bSTG bV "\n", tmp, DI(pending_favored)); + SAYF(bV bSTOP " arithmetics : " cRST "%-37s " bSTG bV bSTOP " pend fav : " + cRST "%-10s " bSTG bV "\n", tmp, DI(pending_favored)); if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", @@ -3995,8 +3995,8 @@ static void show_stats(void) { DI(stage_finds[STAGE_INTEREST16]), DI(stage_cycles[STAGE_INTEREST16]), DI(stage_finds[STAGE_INTEREST32]), DI(stage_cycles[STAGE_INTEREST32])); - SAYF(bV bSTOP " known ints : " cNOR "%-37s " bSTG bV bSTOP " own finds : " - cNOR "%-10s " bSTG bV "\n", tmp, DI(queued_discovered)); + SAYF(bV bSTOP " known ints : " cRST "%-37s " bSTG bV bSTOP " own finds : " + cRST "%-10s " bSTG bV "\n", tmp, DI(queued_discovered)); if (!skip_deterministic) sprintf(tmp, "%s/%s, %s/%s, %s/%s", @@ -4004,16 +4004,16 @@ static void show_stats(void) { DI(stage_finds[STAGE_EXTRAS_UI]), DI(stage_cycles[STAGE_EXTRAS_UI]), DI(stage_finds[STAGE_EXTRAS_AO]), DI(stage_cycles[STAGE_EXTRAS_AO])); - SAYF(bV bSTOP " dictionary : " cNOR "%-37s " bSTG bV bSTOP - " imported : " cNOR "%-10s " bSTG bV "\n", tmp, + SAYF(bV bSTOP " dictionary : " cRST "%-37s " bSTG bV bSTOP + " imported : " cRST "%-10s " bSTG bV "\n", tmp, sync_id ? DI(queued_imported) : (u8*)"n/a"); sprintf(tmp, "%s/%s, %s/%s", DI(stage_finds[STAGE_HAVOC]), DI(stage_cycles[STAGE_HAVOC]), DI(stage_finds[STAGE_SPLICE]), DI(stage_cycles[STAGE_SPLICE])); - SAYF(bV bSTOP " havoc : " cNOR "%-37s " bSTG bV bSTOP - " variable : %s%-10s " bSTG bV "\n", tmp, queued_variable ? cLRD : cNOR, + SAYF(bV bSTOP " havoc : " cRST "%-37s " bSTG bV bSTOP + " variable : %s%-10s " bSTG bV "\n", tmp, queued_variable ? cLRD : cRST, no_var_check ? (u8*)"n/a" : DI(queued_variable)); if (!bytes_trim_out) { @@ -4047,7 +4047,7 @@ static void show_stats(void) { } - SAYF(bV bSTOP " trim : " cNOR "%-37s " bSTG bVR bH20 bH2 bH2 bRB "\n" + SAYF(bV bSTOP " trim : " cRST "%-37s " bSTG bVR bH20 bH2 bH2 bRB "\n" bLB bH30 bH20 bH2 bH bRB bSTOP cRST RESET_G1, tmp); /* Provide some CPU utilization stats. */ @@ -4141,9 +4141,9 @@ static void show_init_stats(void) { OKF("Here are some useful stats:\n\n" - cGRA " Test case count : " cNOR "%u favored, %u variable, %u total\n" - cGRA " Bitmap range : " cNOR "%u to %u bits (average: %0.02f bits)\n" - cGRA " Exec timing : " cNOR "%s to %s us (average: %s us)\n", + cGRA " Test case count : " cRST "%u favored, %u variable, %u total\n" + cGRA " Bitmap range : " cRST "%u to %u bits (average: %0.02f bits)\n" + cGRA " Exec timing : " cRST "%s to %s us (average: %s us)\n", queued_favored, queued_variable, queued_paths, min_bits, max_bits, ((double)total_bitmap_size) / (total_bitmap_entries ? total_bitmap_entries : 1), DI(min_us), DI(max_us), DI(avg_us)); diff --git a/afl-showmap.c b/afl-showmap.c index a534f5b9..2ecd1dc4 100644 --- a/afl-showmap.c +++ b/afl-showmap.c @@ -215,7 +215,7 @@ static void run_target(char** argv) { int status = 0; if (!quiet_mode) - SAYF("-- Program output begins --\n"); + SAYF("-- Program output begins --\n" cRST); MEM_BARRIER(); @@ -295,7 +295,7 @@ static void run_target(char** argv) { classify_counts(trace_bits); if (!quiet_mode) - SAYF("-- Program output ends --\n"); + SAYF(cRST "-- Program output ends --\n"); if (!child_timed_out && !stop_soon && WIFSIGNALED(status)) child_crashed = 1; @@ -443,7 +443,7 @@ static void usage(u8* argv0) { " -e - show edge coverage only, ignore hit counts\n\n" "This tool displays raw tuple data captured by AFL instrumentation.\n" - "For additional help, consult %s/README.\n\n", + "For additional help, consult %s/README.\n\n" cRST, argv0, MEM_LIMIT, doc_path); @@ -705,8 +705,8 @@ int main(int argc, char** argv) { if (!quiet_mode) { - if (!tcnt) FATAL("No instrumentation detected"); - OKF("Captured %u tuples in '%s'.", tcnt, out_file); + if (!tcnt) FATAL("No instrumentation detected" cRST); + OKF("Captured %u tuples in '%s'." cRST, tcnt, out_file); } diff --git a/afl-tmin.c b/afl-tmin.c index c9a678b3..d1abf0fc 100644 --- a/afl-tmin.c +++ b/afl-tmin.c @@ -321,7 +321,7 @@ static u8 run_target(char** argv, u8* mem, u32 len, u8 first_run) { total_execs++; if (stop_soon) { - SAYF(cLRD "\n+++ Minimization aborted by user +++\n" cRST); + SAYF(cRST cLRD "\n+++ Minimization aborted by user +++\n" cRST); exit(1); } @@ -409,7 +409,7 @@ static void minimize(char** argv) { if (set_len < TMIN_SET_MIN_SIZE) set_len = TMIN_SET_MIN_SIZE; - ACTF(cBRI "Stage #0: " cNOR "One-time block normalization..."); + ACTF(cBRI "Stage #0: " cRST "One-time block normalization..."); while (set_pos < in_len) { @@ -457,7 +457,7 @@ static void minimize(char** argv) { del_len = next_p2(in_len / TRIM_START_STEPS); stage_o_len = in_len; - ACTF(cBRI "Stage #1: " cNOR "Removing blocks of data..."); + ACTF(cBRI "Stage #1: " cRST "Removing blocks of data..."); next_del_blksize: @@ -465,7 +465,7 @@ static void minimize(char** argv) { del_pos = 0; prev_del = 1; - SAYF(cGRA " Block length = %u, remaining size = %u\n" cNOR, + SAYF(cGRA " Block length = %u, remaining size = %u\n" cRST, del_len, in_len); while (del_pos < in_len) { @@ -540,7 +540,7 @@ static void minimize(char** argv) { alpha_map[in_data[i]]++; } - ACTF(cBRI "Stage #2: " cNOR "Minimizing symbols (%u code point%s)...", + ACTF(cBRI "Stage #2: " cRST "Minimizing symbols (%u code point%s)...", alpha_size, alpha_size == 1 ? "" : "s"); for (i = 0; i < 256; i++) { @@ -580,7 +580,7 @@ static void minimize(char** argv) { alpha_del2 = 0; - ACTF(cBRI "Stage #3: " cNOR "Character minimization..."); + ACTF(cBRI "Stage #3: " cRST "Character minimization..."); memcpy(tmp_buf, in_data, in_len); @@ -613,10 +613,10 @@ static void minimize(char** argv) { finalize_all: SAYF("\n" - cGRA " File size reduced by : " cNOR "%0.02f%% (to %u byte%s)\n" - cGRA " Characters simplified : " cNOR "%0.02f%%\n" - cGRA " Number of execs done : " cNOR "%u\n" - cGRA " Fruitless execs : " cNOR "path=%u crash=%u hang=%s%u\n\n", + cGRA " File size reduced by : " cRST "%0.02f%% (to %u byte%s)\n" + cGRA " Characters simplified : " cRST "%0.02f%%\n" + cGRA " Number of execs done : " cRST "%u\n" + cGRA " Fruitless execs : " cRST "path=%u crash=%u hang=%s%u\n\n", 100 - ((double)in_len) * 100 / orig_len, in_len, in_len == 1 ? "" : "s", ((double)(alpha_d_total)) * 100 / (in_len ? in_len : 1), total_execs, missed_paths, missed_crashes, missed_hangs ? cLRD : "", @@ -1039,12 +1039,15 @@ int main(int argc, char** argv) { if (!crash_mode) { - OKF("Program terminates normally, minimizing in " cCYA "instrumented" cNOR " mode."); + OKF("Program terminates normally, minimizing in " + cCYA "instrumented" cRST " mode."); + if (!anything_set()) FATAL("No instrumentation detected."); } else { - OKF("Program exits with a signal, minimizing in " cMGN "crash" cNOR " mode."); + OKF("Program exits with a signal, minimizing in " cMGN "crash" cRST + " mode."); } diff --git a/config.h b/config.h index 9f16afbc..4610f933 100644 --- a/config.h +++ b/config.h @@ -25,7 +25,8 @@ * * ******************************************************/ -/* Comment out to disable terminal colors: */ +/* Comment out to disable terminal colors (note that this makes afl-analyze + a lot less nice): */ #define USE_COLOR diff --git a/debug.h b/debug.h index 4fb143fa..54eb8cb9 100644 --- a/debug.h +++ b/debug.h @@ -28,24 +28,41 @@ #ifdef USE_COLOR -# define cBLK "\x1b[0;30m" -# define cRED "\x1b[0;31m" -# define cGRN "\x1b[0;32m" -# define cBRN "\x1b[0;33m" -# define cBLU "\x1b[0;34m" -# define cMGN "\x1b[0;35m" -# define cCYA "\x1b[0;36m" -# define cNOR "\x1b[0;37m" -# define cGRA "\x1b[1;30m" -# define cLRD "\x1b[1;31m" -# define cLGN "\x1b[1;32m" -# define cYEL "\x1b[1;33m" -# define cLBL "\x1b[1;34m" -# define cPIN "\x1b[1;35m" -# define cLCY "\x1b[1;36m" -# define cBRI "\x1b[1;37m" +# define cBLK "\x1b[30m" +# define cRED "\x1b[31m" +# define cGRN "\x1b[32m" +# define cBRN "\x1b[33m" +# define cBLU "\x1b[34m" +# define cMGN "\x1b[35m" +# define cCYA "\x1b[36m" +# define cLGR "\x1b[37m" +# define cGRA "\x1b[1;90m" +# define cLRD "\x1b[1;91m" +# define cLGN "\x1b[1;92m" +# define cYEL "\x1b[1;93m" +# define cLBL "\x1b[1;94m" +# define cPIN "\x1b[1;95m" +# define cLCY "\x1b[1;96m" +# define cBRI "\x1b[1;97m" # define cRST "\x1b[0m" +# define bgBLK "\x1b[40m" +# define bgRED "\x1b[41m" +# define bgGRN "\x1b[42m" +# define bgBRN "\x1b[43m" +# define bgBLU "\x1b[44m" +# define bgMGN "\x1b[45m" +# define bgCYA "\x1b[46m" +# define bgLGR "\x1b[47m" +# define bgGRA "\x1b[100m" +# define bgLRD "\x1b[101m" +# define bgLGN "\x1b[102m" +# define bgYEL "\x1b[103m" +# define bgLBL "\x1b[104m" +# define bgPIN "\x1b[105m" +# define bgLCY "\x1b[106m" +# define bgBRI "\x1b[107m" + #else # define cBLK "" @@ -55,7 +72,7 @@ # define cBLU "" # define cMGN "" # define cCYA "" -# define cNOR "" +# define cLGR "" # define cGRA "" # define cLRD "" # define cLGN "" @@ -66,6 +83,23 @@ # define cBRI "" # define cRST "" +# define bgBLK "" +# define bgRED "" +# define bgGRN "" +# define bgBRN "" +# define bgBLU "" +# define bgMGN "" +# define bgCYA "" +# define bgLGR "" +# define bgGRA "" +# define bgLRD "" +# define bgLGN "" +# define bgYEL "" +# define bgLBL "" +# define bgPIN "" +# define bgLCY "" +# define bgBRI "" + #endif /* ^USE_COLOR */ /************************* @@ -163,7 +197,8 @@ /* Die with a verbose non-OS fatal error message. */ #define FATAL(x...) do { \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cLRD "\n[-] PROGRAM ABORT : " cBRI x); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \ + cBRI x); \ SAYF(cLRD "\n Location : " cRST "%s(), %s:%u\n\n", \ __FUNCTION__, __FILE__, __LINE__); \ exit(1); \ @@ -172,7 +207,8 @@ /* Die by calling abort() to provide a core dump. */ #define ABORT(x...) do { \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cLRD "\n[-] PROGRAM ABORT : " cBRI x); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] PROGRAM ABORT : " \ + cBRI x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n\n", \ __FUNCTION__, __FILE__, __LINE__); \ abort(); \ @@ -182,7 +218,8 @@ #define PFATAL(x...) do { \ fflush(stdout); \ - SAYF(bSTOP RESET_G1 CURSOR_SHOW cLRD "\n[-] SYSTEM ERROR : " cBRI x); \ + SAYF(bSTOP RESET_G1 CURSOR_SHOW cRST cLRD "\n[-] SYSTEM ERROR : " \ + cBRI x); \ SAYF(cLRD "\n Stop location : " cRST "%s(), %s:%u\n", \ __FUNCTION__, __FILE__, __LINE__); \ SAYF(cLRD " OS message : " cRST "%s\n", strerror(errno)); \ diff --git a/docs/ChangeLog b/docs/ChangeLog index 9c61c391..8d63ab8c 100644 --- a/docs/ChangeLog +++ b/docs/ChangeLog @@ -16,6 +16,19 @@ Not sure if you should upgrade? The lowest currently recommended version is 1.92b. If you're stuck on an earlier release, it's strongly advisable to get on with the times. +-------------- +Version 1.99b: +-------------- + + - Substantially revamped the output and the internal logic of afl-analyze. + + - Cleaned up some of the color handling code and added support for + background colors. + + - Removed some stray files (oops). + + - Updated docs to better explain afl-analyze. + -------------- Version 1.98b: -------------- diff --git a/docs/README b/docs/README index 5fb831fc..4bdc3dca 100644 --- a/docs/README +++ b/docs/README @@ -328,7 +328,8 @@ afl-fuzz. Another recent addition to AFL is the afl-analyze tool. It takes an input file, attempts to sequentially flip bytes, and observes the behavior of the tested program. It then annotates the structure of the input data by showing -which sections appear to be critical, and which are not. +which sections appear to be critical, and which are not. More info about its +output can be found near the end of technical_details.txt. 11) Common-sense risks ---------------------- diff --git a/docs/technical_details.txt b/docs/technical_details.txt index 0528915d..ec789a31 100644 --- a/docs/technical_details.txt +++ b/docs/technical_details.txt @@ -480,16 +480,36 @@ roughly 2-5x, compared to 100x+ for PIN. The file format analyzer is a simple extension of the minimization algorithm discussed earlier on; instead of attempting to remove no-op blocks, the tool -performs a walking byte flip and then annotates runs of bytes in the input -file, diving them into three broad buckets: +performs a series of walking byte flips and then annotates runs of bytes +in the input file. - - Segments that appear to have no effect on the control flow in the target - binary (e.g., comments, image data). +It uses the following classification scheme: - - Segments that produce highly variable changes to control flow (e.g., - vital headers, compression dictionaries). + - "No-op blocks" - segments where bit flips cause no apparent changes to + control flow. Common examples may be comment sections, pixel data within + a bitmap file, etc. - - Segments where any change always produces the same change to control flow - (checksums, "magic" values, syntax tokens, checksummed blocks). + - "Superficial content" - segments where some, but not all, bitflips + produce some control flow changes. Examples may include strings in rich + documents (e.g., XML, RTF). -The runs are additionally categorized based on length and arithmetic value. + - "Critical stream" - a sequence of bytes where all bit flips alter control + flow in different but correlated ways. This may be compressed data, + non-atomically compared keywords or magic values, etc. + + - "Suspected length field" - small, atomic integer that, when touched in + any way, causes a consistent change to program control flow, suggestive + of a failed length check. + + - "Suspected cksum or magic int" - an integer that behaves similarly to a + length field, but has a numerical value that makes the length explanation + unlikely. This is suggestive of a checksum or other "magic" integer. + + - "Suspected checksummed block" - a long block of data where any change + always triggers the same new execution path. Likely caused by failing + a checksum or a similar integrity check before any subsequent parsing + takes place. + + - "Magic value section" - a generic token where changes cause the type + of binary behavior outlined earlier, but that doesn't meet any of the + other criteria. May be an atomically compared keyword or so.