diff options
-rw-r--r-- | GNUmakefile.llvm | 4 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | docs/Changelog.md | 1 | ||||
-rw-r--r-- | frida_mode/README.md | 3 | ||||
-rw-r--r-- | frida_mode/frida.map | 2 | ||||
-rw-r--r-- | frida_mode/include/instrument.h | 5 | ||||
-rw-r--r-- | frida_mode/src/instrument/instrument.c | 47 | ||||
-rw-r--r-- | frida_mode/src/instrument/instrument_coverage.c | 723 | ||||
-rw-r--r-- | frida_mode/src/js/api.js | 9 | ||||
-rw-r--r-- | frida_mode/src/js/js_api.c | 7 | ||||
-rw-r--r-- | frida_mode/test/unstable/GNUmakefile | 15 | ||||
-rw-r--r-- | frida_mode/ts/lib/afl.ts | 14 | ||||
-rw-r--r-- | include/envs.h | 1 | ||||
-rw-r--r-- | instrumentation/README.llvm.md | 2 | ||||
-rw-r--r-- | instrumentation/README.lto.md | 8 | ||||
-rw-r--r-- | unicorn_mode/UNICORNAFL_VERSION | 2 | ||||
m--------- | unicorn_mode/unicornafl | 0 | ||||
-rw-r--r-- | utils/afl_network_proxy/GNUmakefile | 6 |
18 files changed, 736 insertions, 115 deletions
diff --git a/GNUmakefile.llvm b/GNUmakefile.llvm index a6f646f3..b802ef16 100644 --- a/GNUmakefile.llvm +++ b/GNUmakefile.llvm @@ -36,7 +36,7 @@ ifeq "$(SYS)" "OpenBSD" LLVM_CONFIG ?= $(BIN_PATH)/llvm-config HAS_OPT = $(shell test -x $(BIN_PATH)/opt && echo 0 || echo 1) ifeq "$(HAS_OPT)" "1" - $(warning llvm_mode needs a complete llvm installation (versions 6.0 up to 12) -> e.g. "pkg_add llvm-7.0.1p9") + $(warning llvm_mode needs a complete llvm installation (versions 6.0 up to 13) -> e.g. "pkg_add llvm-7.0.1p9") endif else LLVM_CONFIG ?= llvm-config @@ -46,7 +46,7 @@ LLVMVER = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/git//' | sed 's LLVM_MAJOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/\..*//' ) LLVM_MINOR = $(shell $(LLVM_CONFIG) --version 2>/dev/null | sed 's/.*\.//' | sed 's/git//' | sed 's/svn//' | sed 's/ .*//' ) LLVM_UNSUPPORTED = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^[0-2]\.|^3.[0-7]\.' && echo 1 || echo 0 ) -LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[3-9]' && echo 1 || echo 0 ) +LLVM_TOO_NEW = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[4-9]' && echo 1 || echo 0 ) LLVM_NEW_API = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[0-9]' && echo 1 || echo 0 ) LLVM_10_OK = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]|^10\.[1-9]|^10\.0.[1-9]' && echo 1 || echo 0 ) LLVM_HAVE_LTO = $(shell $(LLVM_CONFIG) --version 2>/dev/null | egrep -q '^1[1-9]' && echo 1 || echo 0 ) diff --git a/README.md b/README.md index 19d3a866..1f1fd3b2 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ behaviours and defaults: ## Important features of AFL++ - AFL++ supports llvm from 3.8 up to version 12, very fast binary fuzzing with QEMU 5.1 + AFL++ supports llvm from 3.8 up to version 13, very fast binary fuzzing with QEMU 5.1 with laf-intel and redqueen, frida mode, unicorn mode, gcc plugin, full *BSD, Mac OS, Solaris and Android support and much, much, much more. diff --git a/docs/Changelog.md b/docs/Changelog.md index 7ccae7c2..7d72b2df 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -15,6 +15,7 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. information on how to deal with instrumenting libraries - afl-cc: - fix for shared linking on MacOS + - llvm and LTO mode verified to work with new llvm 14-dev - added the very good grammar mutator "GramaTron" to the custom_mutators - added optimin, a faster and better corpus minimizer by diff --git a/frida_mode/README.md b/frida_mode/README.md index 9f00c294..45d90ceb 100644 --- a/frida_mode/README.md +++ b/frida_mode/README.md @@ -175,6 +175,9 @@ useful for debugging purposes, e.g. investigating unstable edges. implies `AFL_FRIDA_INST_NO_OPTIMIZE`. * `AFL_FRIDA_INST_TRACE_UNIQUE` - As per `AFL_FRIDA_INST_TRACE`, but each edge is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`. +* `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE` - File to write DynamoRio format +coverage information for unstable edges (e.g. to be loaded within IDA +lighthouse). * `AFL_FRIDA_OUTPUT_STDOUT` - Redirect the standard output of the target application to the named file (supersedes the setting of `AFL_DEBUG_CHILD`) * `AFL_FRIDA_OUTPUT_STDERR` - Redirect the standard error of the target diff --git a/frida_mode/frida.map b/frida_mode/frida.map index 7ad5e682..e753570f 100644 --- a/frida_mode/frida.map +++ b/frida_mode/frida.map @@ -18,6 +18,7 @@ js_api_set_instrument_seed; js_api_set_instrument_trace; js_api_set_instrument_trace_unique; + js_api_set_instrument_unstable_coverage_file; js_api_set_persistent_address; js_api_set_persistent_count; js_api_set_persistent_debug; @@ -26,6 +27,7 @@ js_api_set_prefetch_backpatch_disable; js_api_set_prefetch_disable; js_api_set_stalker_callback; + js_api_set_stalker_ic_entries; js_api_set_stats_file; js_api_set_stats_interval; js_api_set_stderr; diff --git a/frida_mode/include/instrument.h b/frida_mode/include/instrument.h index 2e8d6b6d..909b2a2c 100644 --- a/frida_mode/include/instrument.h +++ b/frida_mode/include/instrument.h @@ -12,6 +12,7 @@ extern gboolean instrument_optimize; extern gboolean instrument_unique; extern __thread guint64 instrument_previous_pc; extern guint64 instrument_hash_zero; +extern char * instrument_coverage_unstable_filename; extern gboolean instrument_use_fixed_seed; extern guint64 instrument_fixed_seed; @@ -44,6 +45,10 @@ void instrument_coverage_init(void); void instrument_coverage_start(uint64_t address); void instrument_coverage_end(uint64_t address); +void instrument_coverage_unstable(guint64 edge, guint64 previous_rip, + guint64 previous_end, guint64 current_rip, + guint64 current_end); + void instrument_on_fork(); guint64 instrument_get_offset_hash(GumAddress current_rip); diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c index 9e4dd191..fd0982f8 100644 --- a/frida_mode/src/instrument/instrument.c +++ b/frida_mode/src/instrument/instrument.c @@ -29,14 +29,23 @@ guint64 instrument_hash_seed = 0; gboolean instrument_use_fixed_seed = FALSE; guint64 instrument_fixed_seed = 0; +char * instrument_coverage_unstable_filename = NULL; static GumStalkerTransformer *transformer = NULL; __thread guint64 instrument_previous_pc = 0; static GumAddress previous_rip = 0; +static GumAddress previous_end = 0; static u8 * edges_notified = NULL; +typedef struct { + + GumAddress address; + GumAddress end; + +} block_ctx_t; + static void trace_debug(char *format, ...) { va_list ap; @@ -91,9 +100,11 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context, UNUSED_PARAMETER(context); - GumAddress current_rip = GUM_ADDRESS(user_data); - guint64 current_pc = instrument_get_offset_hash(current_rip); - guint64 edge; + block_ctx_t *ctx = (block_ctx_t *)user_data; + GumAddress current_rip = ctx->address; + guint16 current_end = ctx->end; + guint64 current_pc = instrument_get_offset_hash(current_rip); + guint64 edge; edge = current_pc ^ instrument_previous_pc; @@ -112,10 +123,18 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context, if (instrument_unique) { edges_notified[edge] = 1; } - previous_rip = current_rip; + } + + if (unlikely(instrument_coverage_unstable_filename != NULL)) { + + instrument_coverage_unstable(edge, previous_rip, previous_end, current_rip, + current_end); } + previous_rip = current_rip; + previous_end = current_end; + instrument_previous_pc = ((current_pc & (MAP_SIZE - 1) >> 1)) | ((current_pc & 0x1) << (MAP_SIZE_POW2 - 1)); @@ -130,6 +149,7 @@ static void instrument_basic_block(GumStalkerIterator *iterator, const cs_insn *instr; gboolean begin = TRUE; gboolean excluded; + block_ctx_t * ctx = NULL; while (gum_stalker_iterator_next(iterator, &instr)) { @@ -183,8 +203,9 @@ static void instrument_basic_block(GumStalkerIterator *iterator, } else { - gum_stalker_iterator_put_callout( - iterator, on_basic_block, GSIZE_TO_POINTER(instr->address), NULL); + ctx = gum_malloc0(sizeof(block_ctx_t)); + ctx->address = GUM_ADDRESS(instr->address); + gum_stalker_iterator_put_callout(iterator, on_basic_block, ctx, NULL); } @@ -211,6 +232,8 @@ static void instrument_basic_block(GumStalkerIterator *iterator, } + if (ctx != NULL) { ctx->end = (instr->address + instr->size); } + instrument_flush(output); instrument_debug_end(output); instrument_coverage_end(instr->address + instr->size); @@ -224,6 +247,8 @@ void instrument_config(void) { instrument_unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL); instrument_use_fixed_seed = (getenv("AFL_FRIDA_INST_SEED") != NULL); instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED"); + instrument_coverage_unstable_filename = + (getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE")); instrument_debug_config(); instrument_coverage_config(); @@ -241,6 +266,9 @@ void instrument_init(void) { OKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' '); OKF("Instrumentation - fixed seed [%c] [0x%016" G_GINT64_MODIFIER "x]", instrument_use_fixed_seed ? 'X' : ' ', instrument_fixed_seed); + OKF("Instrumentation - unstable coverage [%c] [%s]", + instrument_coverage_unstable_filename == NULL ? ' ' : 'X', + instrument_coverage_unstable_filename); if (instrument_tracing && instrument_optimize) { @@ -249,6 +277,13 @@ void instrument_init(void) { } + if (instrument_coverage_unstable_filename && instrument_optimize) { + + WARNF("AFL_FRIDA_INST_COVERAGE_FILE implies AFL_FRIDA_INST_NO_OPTIMIZE"); + instrument_optimize = FALSE; + + } + if (instrument_unique && instrument_optimize) { WARNF("AFL_FRIDA_INST_TRACE_UNIQUE implies AFL_FRIDA_INST_NO_OPTIMIZE"); diff --git a/frida_mode/src/instrument/instrument_coverage.c b/frida_mode/src/instrument/instrument_coverage.c index 4c0d1a14..46c816bc 100644 --- a/frida_mode/src/instrument/instrument_coverage.c +++ b/frida_mode/src/instrument/instrument_coverage.c @@ -12,33 +12,52 @@ char *instrument_coverage_filename = NULL; -static int coverage_fd = -1; -static int coverage_pipes[2] = {0}; -static uint64_t coverage_last_start = 0; -static GHashTable *coverage_hash = NULL; -static GArray * coverage_modules = NULL; -static guint coverage_marked_modules = 0; -static guint coverage_marked_entries = 0; +static int normal_coverage_fd = -1; +static int normal_coverage_pipes[2] = {-1, -1}; + +static int unstable_coverage_fd = -1; +static int unstable_coverage_pipes[2] = {-1, -1}; + +static uint64_t normal_coverage_last_start = 0; +static gchar * unstable_coverage_fuzzer_stats = NULL; typedef struct { GumAddress base_address; GumAddress limit; gsize size; - char name[PATH_MAX + 1]; char path[PATH_MAX + 1]; - bool referenced; + guint64 offset; + gboolean is_executable; + guint count; guint16 id; -} coverage_module_t; +} coverage_range_t; typedef struct { - uint64_t start; - uint64_t end; - coverage_module_t *module; + uint64_t start; + uint64_t end; + coverage_range_t *module; -} coverage_data_t; +} normal_coverage_data_t; + +typedef struct { + + guint64 edge; + guint64 from; + guint64 from_end; + guint64 to; + guint64 to_end; + +} unstable_coverage_data_t; + +typedef struct { + + GArray *modules; + guint count; + +} coverage_mark_ctx_t; typedef struct { @@ -48,32 +67,44 @@ typedef struct { } coverage_event_t; -static gboolean coverage_module(const GumModuleDetails *details, - gpointer user_data) { +static gboolean coverage_range(const GumRangeDetails *details, + gpointer user_data) { - UNUSED_PARAMETER(user_data); - coverage_module_t coverage = {0}; + GArray * coverage_ranges = (GArray *)user_data; + coverage_range_t coverage = {0}; + + if (details->file == NULL) { return TRUE; } + if (details->protection == GUM_PAGE_NO_ACCESS) { return TRUE; } coverage.base_address = details->range->base_address; coverage.size = details->range->size; coverage.limit = coverage.base_address + coverage.size; - if (details->name != NULL) strncpy(coverage.name, details->name, PATH_MAX); + strncpy(coverage.path, details->file->path, PATH_MAX); + coverage.offset = details->file->offset; + + if ((details->protection & GUM_PAGE_EXECUTE) == 0) { - if (details->path != NULL) strncpy(coverage.path, details->path, PATH_MAX); + coverage.is_executable = false; + + } else { + + coverage.is_executable = true; + + } - coverage.referenced = false; + coverage.count = 0; coverage.id = 0; - g_array_append_val(coverage_modules, coverage); + g_array_append_val(coverage_ranges, coverage); return TRUE; } static gint coverage_sort(gconstpointer a, gconstpointer b) { - coverage_module_t *ma = (coverage_module_t *)a; - coverage_module_t *mb = (coverage_module_t *)b; + coverage_range_t *ma = (coverage_range_t *)a; + coverage_range_t *mb = (coverage_range_t *)b; if (ma->base_address < mb->base_address) return -1; @@ -83,52 +114,128 @@ static gint coverage_sort(gconstpointer a, gconstpointer b) { } -static void coverage_get_ranges(void) { +void instrument_coverage_print(char *format, ...) { + + char buffer[4096] = {0}; + int len; + + va_list ap; + va_start(ap, format); + + if (vsnprintf(buffer, sizeof(buffer) - 1, format, ap) < 0) { return; } + + len = strnlen(buffer, sizeof(buffer)); + IGNORED_RETURN(write(STDOUT_FILENO, buffer, len)); + va_end(ap); + +} + +static GArray *coverage_get_ranges(void) { + + instrument_coverage_print("Coverage - Collecting ranges\n"); + + GArray *coverage_ranges = + g_array_sized_new(false, false, sizeof(coverage_range_t), 100); + gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, coverage_range, + coverage_ranges); + g_array_sort(coverage_ranges, coverage_sort); + + for (guint i = 0; i < coverage_ranges->len; i++) { + + coverage_range_t *range = + &g_array_index(coverage_ranges, coverage_range_t, i); + instrument_coverage_print("Coverage Range - %3u: 0x%016" G_GINT64_MODIFIER + "X - 0x%016" G_GINT64_MODIFIER "X (%s)\n", + i, range->base_address, range->limit, + range->path); + + } + + return coverage_ranges; + +} + +static GArray *coverage_get_modules(void) { + + instrument_coverage_print("Coverage - Collecting modules\n"); + GArray *coverage_ranges = coverage_get_ranges(); + GArray *coverage_modules = + g_array_sized_new(false, false, sizeof(coverage_range_t), 100); + + coverage_range_t current = {0}; + + for (guint i = 0; i < coverage_ranges->len; i++) { + + coverage_range_t *range = + &g_array_index(coverage_ranges, coverage_range_t, i); - OKF("Coverage - Collecting ranges"); + if (range->offset == 0 || + (strncmp(range->path, current.path, PATH_MAX) != 0)) { - coverage_modules = - g_array_sized_new(false, false, sizeof(coverage_module_t), 100); - gum_process_enumerate_modules(coverage_module, NULL); - g_array_sort(coverage_modules, coverage_sort); + if (current.is_executable) { + + g_array_append_val(coverage_modules, current); + memset(¤t, '\0', sizeof(coverage_range_t)); + + } + + memcpy(¤t, range, sizeof(coverage_range_t)); + + } else { + + current.limit = range->limit; + current.size = current.limit - current.base_address; + if (range->is_executable) { current.is_executable = true; } + + } + + } + + if (current.is_executable) { g_array_append_val(coverage_modules, current); } + g_array_free(coverage_ranges, TRUE); for (guint i = 0; i < coverage_modules->len; i++) { - coverage_module_t *module = - &g_array_index(coverage_modules, coverage_module_t, i); - OKF("Coverage Module - %3u: 0x%016" G_GINT64_MODIFIER - "X - 0x%016" G_GINT64_MODIFIER "X", - i, module->base_address, module->limit); + coverage_range_t *module = + &g_array_index(coverage_modules, coverage_range_t, i); + instrument_coverage_print("Coverage Module - %3u: 0x%016" G_GINT64_MODIFIER + "X - 0x%016" G_GINT64_MODIFIER "X (%s)\n", + i, module->base_address, module->limit, + module->path); } + return coverage_modules; + } static void instrument_coverage_mark(void *key, void *value, void *user_data) { UNUSED_PARAMETER(key); - UNUSED_PARAMETER(user_data); - coverage_data_t *val = (coverage_data_t *)value; - guint i; + coverage_mark_ctx_t * ctx = (coverage_mark_ctx_t *)user_data; + GArray * coverage_modules = ctx->modules; + normal_coverage_data_t *val = (normal_coverage_data_t *)value; + guint i; for (i = 0; i < coverage_modules->len; i++) { - coverage_module_t *module = - &g_array_index(coverage_modules, coverage_module_t, i); + coverage_range_t *module = + &g_array_index(coverage_modules, coverage_range_t, i); if (val->start > module->limit) continue; if (val->end >= module->limit) break; val->module = module; - coverage_marked_entries++; - module->referenced = true; + ctx->count = ctx->count + 1; + module->count++; return; } - OKF("Coverage cannot find module for: 0x%016" G_GINT64_MODIFIER - "X - 0x%016" G_GINT64_MODIFIER "X %u %u", - val->start, val->end, i, coverage_modules->len); + instrument_coverage_print( + "Coverage cannot find module for: 0x%016" G_GINT64_MODIFIER + "X - 0x%016" G_GINT64_MODIFIER "X\n", + val->start, val->end); } @@ -140,7 +247,7 @@ static void coverage_write(void *data, size_t size) { for (char *cursor = (char *)data; remain > 0; remain -= written, cursor += written) { - written = write(coverage_fd, cursor, remain); + written = write(normal_coverage_fd, cursor, remain); if (written < 0) { @@ -171,14 +278,14 @@ static void coverage_format(char *format, ...) { } -static void coverage_write_modules() { +static void coverage_write_modules(GArray *coverage_modules) { guint emitted = 0; for (guint i = 0; i < coverage_modules->len; i++) { - coverage_module_t *module = - &g_array_index(coverage_modules, coverage_module_t, i); - if (!module->referenced) continue; + coverage_range_t *module = + &g_array_index(coverage_modules, coverage_range_t, i); + if (module->count == 0) continue; coverage_format("%3u, ", emitted); coverage_format("%016" G_GINT64_MODIFIER "X, ", module->base_address); @@ -200,7 +307,10 @@ static void coverage_write_events(void *key, void *value, void *user_data) { UNUSED_PARAMETER(key); UNUSED_PARAMETER(user_data); - coverage_data_t *val = (coverage_data_t *)value; + normal_coverage_data_t *val = (normal_coverage_data_t *)value; + + if (val->module == NULL) { return; } + coverage_event_t evt = { .offset = val->start - val->module->base_address, @@ -213,7 +323,7 @@ static void coverage_write_events(void *key, void *value, void *user_data) { } -static void coverage_write_header() { +static void coverage_write_header(guint coverage_marked_modules) { char version[] = "DRCOV VERSION: 2\n"; char flavour[] = "DRCOV FLAVOR: frida\n"; @@ -223,77 +333,321 @@ static void coverage_write_header() { coverage_format("Module Table: version 2, count %u\n", coverage_marked_modules); coverage_write(columns, sizeof(columns) - 1); - coverage_write_modules(); - coverage_format("BB Table: %u bbs\n", coverage_marked_entries); - g_hash_table_foreach(coverage_hash, coverage_write_events, NULL); } -static void coverage_mark_modules() { +static guint coverage_mark_modules(GArray *coverage_modules) { + guint coverage_marked_modules = 0; guint i; for (i = 0; i < coverage_modules->len; i++) { - coverage_module_t *module = - &g_array_index(coverage_modules, coverage_module_t, i); + coverage_range_t *module = + &g_array_index(coverage_modules, coverage_range_t, i); - OKF("Coverage Module - %3u: [%c] 0x%016" G_GINT64_MODIFIER - "X - 0x%016" G_GINT64_MODIFIER "X (%u:%s)", - i, module->referenced ? 'X' : ' ', module->base_address, module->limit, - module->id, module->path); + instrument_coverage_print( + "Coverage Module - %3u: [%c] 0x%016" G_GINT64_MODIFIER + "X - 0x%016" G_GINT64_MODIFIER "X [%u] (%u:%s)\n", + i, module->count == 0 ? ' ' : 'X', module->base_address, module->limit, + module->count, module->id, module->path); - if (!module->referenced) { continue; } + if (module->count == 0) { continue; } module->id = coverage_marked_modules; coverage_marked_modules++; } + return coverage_marked_modules; + } -static void instrument_coverage_run() { +static void instrument_coverage_normal_run() { - int bytes; - coverage_data_t data; - coverage_data_t *value; - OKF("Coverage - Running"); + int bytes; + normal_coverage_data_t data; + normal_coverage_data_t *value; + instrument_coverage_print("Coverage - Running\n"); - if (close(coverage_pipes[STDOUT_FILENO]) != 0) { + if (close(normal_coverage_pipes[STDOUT_FILENO]) != 0) { FATAL("Failed to close parent read pipe"); } - for (bytes = - read(coverage_pipes[STDIN_FILENO], &data, sizeof(coverage_data_t)); - bytes == sizeof(coverage_data_t); - bytes = - read(coverage_pipes[STDIN_FILENO], &data, sizeof(coverage_data_t))) { + GHashTable *coverage_hash = + g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + if (coverage_hash == NULL) { + + FATAL("Failed to g_hash_table_new, errno: %d", errno); + + } + + for (bytes = read(normal_coverage_pipes[STDIN_FILENO], &data, + sizeof(normal_coverage_data_t)); + bytes == sizeof(normal_coverage_data_t); + bytes = read(normal_coverage_pipes[STDIN_FILENO], &data, + sizeof(normal_coverage_data_t))) { - value = (coverage_data_t *)gum_malloc0(sizeof(coverage_data_t)); - memcpy(value, &data, sizeof(coverage_data_t)); + value = + (normal_coverage_data_t *)gum_malloc0(sizeof(normal_coverage_data_t)); + memcpy(value, &data, sizeof(normal_coverage_data_t)); g_hash_table_insert(coverage_hash, GSIZE_TO_POINTER(data.start), value); } if (bytes != 0) { FATAL("Coverage data truncated"); } - OKF("Coverage - Preparing"); + instrument_coverage_print("Coverage - Preparing\n"); - coverage_get_ranges(); + GArray *coverage_modules = coverage_get_modules(); guint size = g_hash_table_size(coverage_hash); - OKF("Coverage - Total Entries: %u", size); + instrument_coverage_print("Coverage - Total Entries: %u\n", size); + + coverage_mark_ctx_t ctx = {.modules = coverage_modules, .count = 0}; + + g_hash_table_foreach(coverage_hash, instrument_coverage_mark, &ctx); + instrument_coverage_print("Coverage - Marked Entries: %u\n", ctx.count); - g_hash_table_foreach(coverage_hash, instrument_coverage_mark, NULL); - OKF("Coverage - Marked Entries: %u", coverage_marked_entries); + guint coverage_marked_modules = coverage_mark_modules(coverage_modules); + instrument_coverage_print("Coverage - Marked Modules: %u\n", + coverage_marked_modules); - coverage_mark_modules(); - OKF("Coverage - Marked Modules: %u", coverage_marked_modules); + coverage_write_header(coverage_marked_modules); + coverage_write_modules(coverage_modules); + coverage_format("BB Table: %u bbs\n", ctx.count); + g_hash_table_foreach(coverage_hash, coverage_write_events, NULL); + + g_hash_table_unref(coverage_hash); + + instrument_coverage_print("Coverage - Completed\n"); + +} + +static GArray *instrument_coverage_unstable_read_unstable_ids(void) { + + gchar * contents = NULL; + gsize length = 0; + GArray *unstable_edge_ids = + g_array_sized_new(false, false, sizeof(gpointer), 100); + + if (!g_file_get_contents(unstable_coverage_fuzzer_stats, &contents, &length, + NULL)) { + + FATAL("Failed to read fuzzer_stats"); + + } + + instrument_coverage_print("\n"); + instrument_coverage_print("Unstable coverage stats:\n"); + instrument_coverage_print("========================\n"); + instrument_coverage_print("%s\n", contents); + instrument_coverage_print("\n"); + + gchar **lines = g_strsplit(contents, "\n", -1); + gchar **values = NULL; + + for (guint i = 0; lines[i] != NULL; i++) { + + gchar **fields = g_strsplit(lines[i], ":", 2); + if (fields[0] == NULL) { + + g_strfreev(fields); + continue; + + } + + g_strstrip(fields[0]); + if (g_strcmp0(fields[0], "var_bytes") != 0) { + + g_strfreev(fields); + continue; + + } + + if (fields[1] == NULL) { + + g_strfreev(fields); + continue; + + } + + g_strstrip(fields[1]); + values = g_strsplit(fields[1], " ", -1); + g_strfreev(fields); + + break; + + } + + if (values == NULL) { + + instrument_coverage_print( + "Failed to find var_bytes, did you set AFL_DEBUG?\n"); + + } + + for (guint i = 0; values[i] != NULL; i++) { + + g_strstrip(values[i]); + gpointer val = GSIZE_TO_POINTER(g_ascii_strtoull(values[i], NULL, 10)); + g_array_append_val(unstable_edge_ids, val); + + } - coverage_write_header(); + g_strfreev(values); + g_strfreev(lines); + g_free(contents); - OKF("Coverage - Completed"); + for (guint i = 0; i < unstable_edge_ids->len; i++) { + + gpointer *id = &g_array_index(unstable_edge_ids, gpointer, i); + + instrument_coverage_print("Unstable edge (%10u): %" G_GINT64_MODIFIER "u\n", + i, GPOINTER_TO_SIZE(*id)); + + } + + return unstable_edge_ids; + +} + +static GHashTable *instrument_collect_unstable_blocks( + GHashTable *unstable_coverage_hash, GArray *unstable_edge_ids) { + + GHashTable *unstable_blocks = + g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + + for (guint i = 0; i < unstable_edge_ids->len; i++) { + + gpointer *id = &g_array_index(unstable_edge_ids, gpointer, i); + + GHashTable *child = + (GHashTable *)g_hash_table_lookup(unstable_coverage_hash, *id); + + if (child == NULL) { FATAL("Failed to find edge ID"); } + + GHashTableIter iter = {0}; + gpointer value; + g_hash_table_iter_init(&iter, child); + while (g_hash_table_iter_next(&iter, NULL, &value)) { + + unstable_coverage_data_t *unstable = (unstable_coverage_data_t *)value; + normal_coverage_data_t * from = + gum_malloc0(sizeof(normal_coverage_data_t)); + normal_coverage_data_t *to = gum_malloc0(sizeof(normal_coverage_data_t)); + from->start = unstable->from; + from->end = unstable->from_end; + from->module = NULL; + + to->start = unstable->to; + to->end = unstable->to_end; + to->module = NULL; + + g_hash_table_insert(unstable_blocks, GSIZE_TO_POINTER(from->start), from); + g_hash_table_insert(unstable_blocks, GSIZE_TO_POINTER(to->start), to); + + } + + } + + return unstable_blocks; + +} + +static void instrument_coverage_unstable_run(void) { + + int bytes; + unstable_coverage_data_t data; + unstable_coverage_data_t *value; + instrument_coverage_print("Unstable coverage - Running\n"); + + if (close(unstable_coverage_pipes[STDOUT_FILENO]) != 0) { + + FATAL("Failed to close parent read pipe"); + + } + + GHashTable *unstable_coverage_hash = g_hash_table_new_full( + g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)g_hash_table_unref); + if (unstable_coverage_hash == NULL) { + + FATAL("Failed to g_hash_table_new, errno: %d", errno); + + } + + guint edges = 0; + + for (bytes = read(unstable_coverage_pipes[STDIN_FILENO], &data, + sizeof(unstable_coverage_data_t)); + bytes == sizeof(unstable_coverage_data_t); + bytes = read(unstable_coverage_pipes[STDIN_FILENO], &data, + sizeof(unstable_coverage_data_t))) { + + value = (unstable_coverage_data_t *)gum_malloc0( + sizeof(unstable_coverage_data_t)); + memcpy(value, &data, sizeof(unstable_coverage_data_t)); + + gpointer hash_value = g_hash_table_lookup(unstable_coverage_hash, + GSIZE_TO_POINTER(value->edge)); + if (hash_value == NULL) { + + hash_value = + g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); + + if (!g_hash_table_insert(unstable_coverage_hash, + GSIZE_TO_POINTER(value->edge), hash_value)) { + + FATAL("Entry already in hashtable"); + + } + + } + + if (g_hash_table_insert(hash_value, GSIZE_TO_POINTER(value->from), value)) { + + edges++; + + } + + } + + if (bytes != 0) { FATAL("Unstable coverage data truncated"); } + + instrument_coverage_print("Coverage - Preparing\n"); + + GArray *coverage_modules = coverage_get_modules(); + + instrument_coverage_print("Found edges: %u\n", edges); + + GArray *unstable_edge_ids = instrument_coverage_unstable_read_unstable_ids(); + + GHashTable *unstable_blocks = instrument_collect_unstable_blocks( + unstable_coverage_hash, unstable_edge_ids); + + guint size = g_hash_table_size(unstable_blocks); + instrument_coverage_print("Unstable blocks: %u\n", size); + + coverage_mark_ctx_t ctx = {.modules = coverage_modules, .count = 0}; + + g_hash_table_foreach(unstable_blocks, instrument_coverage_mark, &ctx); + instrument_coverage_print("Coverage - Marked Entries: %u\n", ctx.count); + + guint coverage_marked_modules = coverage_mark_modules(coverage_modules); + instrument_coverage_print("Coverage - Marked Modules: %u\n", + coverage_marked_modules); + + coverage_write_header(coverage_marked_modules); + coverage_write_modules(coverage_modules); + coverage_format("BB Table: %u bbs\n", ctx.count); + g_hash_table_foreach(unstable_blocks, coverage_write_events, NULL); + + g_hash_table_unref(unstable_blocks); + g_array_free(unstable_edge_ids, TRUE); + g_hash_table_unref(unstable_coverage_hash); + + instrument_coverage_print("Coverage - Completed\n"); } @@ -303,7 +657,7 @@ void instrument_coverage_config(void) { } -void instrument_coverage_init(void) { +void instrument_coverage_normal_init(void) { OKF("Coverage - enabled [%c]", instrument_coverage_filename == NULL ? ' ' : 'X'); @@ -317,19 +671,147 @@ void instrument_coverage_init(void) { OKF("Coverage - path [%s]", path); - coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, - S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + normal_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); - if (coverage_fd < 0) { FATAL("Failed to open coverage file '%s'", path); } + if (normal_coverage_fd < 0) { + + FATAL("Failed to open coverage file '%s'", path); + + } g_free(path); - if (pipe(coverage_pipes) != 0) { FATAL("Failed to create pipes"); } + if (pipe(normal_coverage_pipes) != 0) { FATAL("Failed to create pipes"); } - coverage_hash = g_hash_table_new(g_direct_hash, g_direct_equal); - if (coverage_hash == NULL) { + pid_t pid = fork(); + if (pid == -1) { FATAL("Failed to start coverage process"); } - FATAL("Failed to g_hash_table_new, errno: %d", errno); + if (pid == 0) { + + instrument_coverage_normal_run(); + kill(getpid(), SIGKILL); + _exit(0); + + } + + if (close(normal_coverage_fd) < 0) { + + FATAL("Failed to close coverage output file"); + + } + + if (close(normal_coverage_pipes[STDIN_FILENO]) != 0) { + + FATAL("Failed to close parent read pipe"); + + } + +} + +void instrument_coverage_unstable_find_output(void) { + + pid_t parent = getpid(); + gchar *fds_name = g_strdup_printf("/proc/%d/fd/", getppid()); + + gchar *root = g_file_read_link("/proc/self/root", NULL); + if (root == NULL) { FATAL("Failed to read link"); } + + GDir *dir = g_dir_open(fds_name, 0, NULL); + + OKF("Coverage Unstable - fds: %s", fds_name); + + for (const gchar *filename = g_dir_read_name(dir); filename != NULL; + filename = g_dir_read_name(dir)) { + + gchar *fullname = g_build_path("/", fds_name, filename, NULL); + + gchar *link = g_file_read_link(fullname, NULL); + if (link == NULL) { FATAL("Failed to read link: %s", fullname); } + + gchar *basename = g_path_get_basename(link); + if (g_strcmp0(basename, "default") != 0) { + + g_free(basename); + g_free(link); + g_free(fullname); + continue; + + } + + gchar *relative = NULL; + size_t root_len = strnlen(root, PATH_MAX); + if (g_str_has_suffix(link, root)) { + + relative = g_build_path("/", &link[root_len], NULL); + + } else { + + relative = g_build_path("/", link, NULL); + + } + + gchar *cmdline = g_build_path("/", relative, "cmdline", NULL); + if (!g_file_test(cmdline, G_FILE_TEST_EXISTS)) { + + g_free(cmdline); + g_free(basename); + g_free(relative); + g_free(link); + g_free(fullname); + continue; + + } + + unstable_coverage_fuzzer_stats = + g_build_path("/", relative, "fuzzer_stats", NULL); + g_free(cmdline); + g_free(basename); + g_free(relative); + g_free(link); + g_free(fullname); + break; + + } + + g_dir_close(dir); + g_free(fds_name); + + if (unstable_coverage_fuzzer_stats == NULL) { + + FATAL("Failed to find fuzzer stats"); + + } + + OKF("Fuzzer stats: %s", unstable_coverage_fuzzer_stats); + +} + +void instrument_coverage_unstable_init(void) { + + if (instrument_coverage_unstable_filename == NULL) { return; } + + char *path = g_canonicalize_filename(instrument_coverage_unstable_filename, + g_get_current_dir()); + + OKF("Coverage - unstable path [%s]", instrument_coverage_unstable_filename); + + unstable_coverage_fd = open(path, O_RDWR | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + + if (unstable_coverage_fd < 0) { + + FATAL("Failed to open unstable coverage file '%s'", path); + + } + + g_free(path); + + instrument_coverage_unstable_find_output(); + + if (pipe(unstable_coverage_pipes) != 0) { + + FATAL("Failed to create unstable pipes"); } @@ -338,12 +820,19 @@ void instrument_coverage_init(void) { if (pid == 0) { - instrument_coverage_run(); + instrument_coverage_unstable_run(); + kill(getpid(), SIGKILL); _exit(0); } - if (close(coverage_pipes[STDIN_FILENO]) != 0) { + if (close(unstable_coverage_fd) < 0) { + + FATAL("Failed to close unstable coverage output file"); + + } + + if (close(unstable_coverage_pipes[STDIN_FILENO]) != 0) { FATAL("Failed to close parent read pipe"); @@ -351,20 +840,31 @@ void instrument_coverage_init(void) { } +void instrument_coverage_init(void) { + + instrument_coverage_normal_init(); + instrument_coverage_unstable_init(); + +} + void instrument_coverage_start(uint64_t address) { - coverage_last_start = address; + if (instrument_coverage_filename == NULL) { return; } + + normal_coverage_last_start = address; } void instrument_coverage_end(uint64_t address) { - coverage_data_t data = { + if (instrument_coverage_filename == NULL) { return; } + + normal_coverage_data_t data = { - .start = coverage_last_start, .end = address, .module = NULL}; + .start = normal_coverage_last_start, .end = address, .module = NULL}; - if (write(coverage_pipes[STDOUT_FILENO], &data, sizeof(coverage_data_t)) != - sizeof(coverage_data_t)) { + if (write(normal_coverage_pipes[STDOUT_FILENO], &data, + sizeof(normal_coverage_data_t)) != sizeof(normal_coverage_data_t)) { FATAL("Coverage I/O error"); @@ -372,3 +872,26 @@ void instrument_coverage_end(uint64_t address) { } +void instrument_coverage_unstable(guint64 edge, guint64 previous_rip, + guint64 previous_end, guint64 current_rip, + guint64 current_end) { + + if (instrument_coverage_unstable_filename == NULL) { return; } + unstable_coverage_data_t data = { + + .edge = edge, + .from = previous_rip, + .from_end = previous_end, + .to = current_rip, + .to_end = current_end}; + + if (write(unstable_coverage_pipes[STDOUT_FILENO], &data, + sizeof(unstable_coverage_data_t)) != + sizeof(unstable_coverage_data_t)) { + + FATAL("Unstable coverage I/O error"); + + } + +} + diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js index 71b5e4a4..cf422ba9 100644 --- a/frida_mode/src/js/api.js +++ b/frida_mode/src/js/api.js @@ -138,6 +138,14 @@ class Afl { Afl.jsApiSetInstrumentTraceUnique(); } /** + * See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single + * `string` as an argument. + */ + static setInstrumentUnstableCoverageFile(file) { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentUnstableCoverageFile(buf); + } + /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a * `NativePointer` should be provided as it's argument. */ @@ -255,6 +263,7 @@ Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_n Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]); Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []); Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []); +Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]); Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]); Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]); Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []); diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c index c2746d13..49ca3baa 100644 --- a/frida_mode/src/js/js_api.c +++ b/frida_mode/src/js/js_api.c @@ -164,6 +164,13 @@ __attribute__((visibility("default"))) void js_api_set_instrument_trace_unique( } +__attribute__((visibility("default"))) void +js_api_set_instrument_unstable_coverage_file(char *path) { + + instrument_coverage_unstable_filename = g_strdup(path); + +} + __attribute__((visibility("default"))) void js_api_set_stdout(char *file) { output_stdout = g_strdup(file); diff --git a/frida_mode/test/unstable/GNUmakefile b/frida_mode/test/unstable/GNUmakefile index c5fe1b88..0ccc5fb1 100644 --- a/frida_mode/test/unstable/GNUmakefile +++ b/frida_mode/test/unstable/GNUmakefile @@ -85,6 +85,21 @@ frida: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) -- \ $(UNSTABLE_BIN) @@ +frida_coverage: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE) + AFL_DEBUG=1 \ + AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \ + AFL_FRIDA_OUTPUT_STDOUT=/tmp/stdout.txt \ + AFL_FRIDA_OUTPUT_STDERR=/tmp/stderr.txt \ + AFL_FRIDA_INST_COVERAGE_FILE=/tmp/coverage.dat \ + AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE=/tmp/unstable.dat \ + $(ROOT)afl-fuzz \ + -D \ + -O \ + -i $(UNSTABLE_DATA_DIR) \ + -o $(FRIDA_OUT) \ + -- \ + $(UNSTABLE_BIN) @@ + debug: gdb \ --ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \ diff --git a/frida_mode/ts/lib/afl.ts b/frida_mode/ts/lib/afl.ts index d7f8b7bc..c722f558 100644 --- a/frida_mode/ts/lib/afl.ts +++ b/frida_mode/ts/lib/afl.ts @@ -164,6 +164,15 @@ class Afl { } /** + * See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single + * `string` as an argument. + */ + public static setInstrumentUnstableCoverageFile(file: string): void { + const buf = Memory.allocUtf8String(file); + Afl.jsApiSetInstrumentUnstableCoverageFile(buf); + } + + /** * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a * `NativePointer` should be provided as it's argument. */ @@ -338,6 +347,11 @@ class Afl { "void", []); + private static readonly jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction( + "js_api_set_instrument_unstable_coverage_file", + "void", + ["pointer"]); + private static readonly jsApiSetPersistentAddress = Afl.jsApiGetFunction( "js_api_set_persistent_address", "void", diff --git a/include/envs.h b/include/envs.h index dd84748e..e3957147 100644 --- a/include/envs.h +++ b/include/envs.h @@ -65,6 +65,7 @@ static char *afl_environment_variables[] = { "AFL_FRIDA_INST_SEED", "AFL_FRIDA_INST_TRACE", "AFL_FRIDA_INST_TRACE_UNIQUE", + "AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE", "AFL_FRIDA_JS_SCRIPT", "AFL_FRIDA_OUTPUT_STDOUT", "AFL_FRIDA_OUTPUT_STDERR", diff --git a/instrumentation/README.llvm.md b/instrumentation/README.llvm.md index 2d428e6d..6e210a7c 100644 --- a/instrumentation/README.llvm.md +++ b/instrumentation/README.llvm.md @@ -6,7 +6,7 @@ ## 1) Introduction -! llvm_mode works with llvm versions 3.8 up to 12 ! +! llvm_mode works with llvm versions 3.8 up to 13 ! The code in this directory allows you to instrument programs for AFL using true compiler-level instrumentation, instead of the more crude diff --git a/instrumentation/README.lto.md b/instrumentation/README.lto.md index 3e9d7585..6174cdc0 100644 --- a/instrumentation/README.lto.md +++ b/instrumentation/README.lto.md @@ -60,12 +60,12 @@ AUTODICTIONARY: 11 strings found ## Getting llvm 11+ -### Installing llvm version 11 +### Installing llvm version 11 or 12 -llvm 11 should be available in all current Linux repositories. +llvm 11 or even 12 should be available in all current Linux repositories. If you use an outdated Linux distribution read the next section. -### Installing llvm from the llvm repository (version 12) +### Installing llvm from the llvm repository (version 12+) Installing the llvm snapshot builds is easy and mostly painless: @@ -85,7 +85,7 @@ apt-get install -y clang-12 clang-tools-12 libc++1-12 libc++-12-dev \ libomp5-12 lld-12 lldb-12 llvm-12 llvm-12-dev llvm-12-runtime llvm-12-tools ``` -### Building llvm yourself (version 12) +### Building llvm yourself (version 12+) Building llvm from github takes quite some long time and is not painless: ```sh diff --git a/unicorn_mode/UNICORNAFL_VERSION b/unicorn_mode/UNICORNAFL_VERSION index da9641d5..0db54339 100644 --- a/unicorn_mode/UNICORNAFL_VERSION +++ b/unicorn_mode/UNICORNAFL_VERSION @@ -1 +1 @@ -9064bca9ba +c0e03d2c diff --git a/unicorn_mode/unicornafl b/unicorn_mode/unicornafl -Subproject 019b871539fe9ed3f41d882385a8b02c243d49a +Subproject c0e03d2c6b55a22025324f121746b41b1e756fb diff --git a/utils/afl_network_proxy/GNUmakefile b/utils/afl_network_proxy/GNUmakefile index 0b55dc2c..7c8c22ff 100644 --- a/utils/afl_network_proxy/GNUmakefile +++ b/utils/afl_network_proxy/GNUmakefile @@ -3,6 +3,8 @@ BIN_PATH = $(PREFIX)/bin HELPER_PATH = $(PREFIX)/lib/afl DOC_PATH = $(PREFIX)/share/doc/afl +SYS = $(shell uname -s) + PROGRAMS = afl-network-client afl-network-server HASH=\# @@ -13,6 +15,10 @@ ifdef STATIC CFLAGS += -static endif +ifeq "$(SYS)" "SunOS" + LDFLAGS += -lnsl -lsocket +endif + ifeq "$(shell echo '$(HASH)include <libdeflate.h>@int main() { struct libdeflate_compressor *d = libdeflate_alloc_compressor(1); return 0;}' | tr @ '\n' | $(CC) $(CFLAGS) -x c - -o .test2 -ldeflate 2>/dev/null && echo 1 || echo 0 ; rm -f .test2 )" "1" CFLAGS += -DUSE_DEFLATE=1 LDFLAGS += -ldeflate |