diff options
-rw-r--r-- | frida_mode/README.md | 3 | ||||
-rw-r--r-- | frida_mode/frida.map | 1 | ||||
-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 | 588 | ||||
-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 |
10 files changed, 603 insertions, 87 deletions
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 288633ab..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; 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 495791c7..46c816bc 100644 --- a/frida_mode/src/instrument/instrument_coverage.c +++ b/frida_mode/src/instrument/instrument_coverage.c @@ -12,14 +12,14 @@ char *instrument_coverage_filename = NULL; -static int coverage_fd = -1; -static int coverage_pipes[2] = {-1, -1}; -static uint64_t coverage_last_start = 0; -static GHashTable *coverage_hash = NULL; -static GArray * coverage_modules = NULL; -static GArray * coverage_ranges = 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 { @@ -40,7 +40,24 @@ typedef struct { 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 { @@ -53,7 +70,7 @@ typedef struct { static gboolean coverage_range(const GumRangeDetails *details, gpointer user_data) { - UNUSED_PARAMETER(user_data); + GArray * coverage_ranges = (GArray *)user_data; coverage_range_t coverage = {0}; if (details->file == NULL) { return TRUE; } @@ -113,11 +130,36 @@ void instrument_coverage_print(char *format, ...) { } -static void coverage_get_modules(void) { +static GArray *coverage_get_ranges(void) { - instrument_coverage_print("Coverage - Collecting modules\n"); + instrument_coverage_print("Coverage - Collecting ranges\n"); - coverage_modules = + 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}; @@ -150,6 +192,7 @@ static void coverage_get_modules(void) { } 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++) { @@ -162,36 +205,17 @@ static void coverage_get_modules(void) { } -} - -static void coverage_get_ranges(void) { - - instrument_coverage_print("Coverage - Collecting ranges\n"); - - coverage_ranges = - g_array_sized_new(false, false, sizeof(coverage_range_t), 100); - gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, coverage_range, NULL); - 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_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++) { @@ -202,7 +226,7 @@ static void instrument_coverage_mark(void *key, void *value, void *user_data) { if (val->end >= module->limit) break; val->module = module; - coverage_marked_entries++; + ctx->count = ctx->count + 1; module->count++; return; @@ -223,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) { @@ -254,7 +278,7 @@ 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++) { @@ -283,7 +307,7 @@ 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; } @@ -299,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"; @@ -309,14 +333,12 @@ 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++) { @@ -336,29 +358,40 @@ static void coverage_mark_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; + 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); } @@ -367,21 +400,252 @@ static void instrument_coverage_run() { instrument_coverage_print("Coverage - Preparing\n"); - coverage_get_ranges(); - coverage_get_modules(); + GArray *coverage_modules = coverage_get_modules(); guint size = g_hash_table_size(coverage_hash); instrument_coverage_print("Coverage - Total Entries: %u\n", size); - g_hash_table_foreach(coverage_hash, instrument_coverage_mark, NULL); - instrument_coverage_print("Coverage - Marked Entries: %u\n", - coverage_marked_entries); + 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); - coverage_mark_modules(); + guint coverage_marked_modules = coverage_mark_modules(coverage_modules); instrument_coverage_print("Coverage - Marked Modules: %u\n", coverage_marked_modules); - coverage_write_header(); + 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); + + } + + g_strfreev(values); + g_strfreev(lines); + g_free(contents); + + 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"); @@ -393,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'); @@ -407,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 (normal_coverage_fd < 0) { - if (coverage_fd < 0) { FATAL("Failed to open coverage file '%s'", path); } + 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"); } @@ -428,15 +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_fd) < 0) { FATAL("Failed to close coverage output file"); } + if (close(unstable_coverage_fd) < 0) { + + FATAL("Failed to close unstable coverage output file"); - if (close(coverage_pipes[STDIN_FILENO]) != 0) { + } + + if (close(unstable_coverage_pipes[STDIN_FILENO]) != 0) { FATAL("Failed to close parent read pipe"); @@ -444,11 +840,18 @@ 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) { if (instrument_coverage_filename == NULL) { return; } - coverage_last_start = address; + normal_coverage_last_start = address; } @@ -456,12 +859,12 @@ void instrument_coverage_end(uint64_t address) { if (instrument_coverage_filename == NULL) { return; } - coverage_data_t data = { + 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"); @@ -469,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", |