about summary refs log tree commit diff
path: root/frida_mode/src/instrument/instrument.c
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode/src/instrument/instrument.c')
-rw-r--r--frida_mode/src/instrument/instrument.c242
1 files changed, 190 insertions, 52 deletions
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index cd1ac0be..67aafa5a 100644
--- a/frida_mode/src/instrument/instrument.c
+++ b/frida_mode/src/instrument/instrument.c
@@ -1,14 +1,19 @@
 #include <unistd.h>
+#include <sys/shm.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
+#include "hash.h"
 
 #include "asan.h"
 #include "entry.h"
 #include "frida_cmplog.h"
 #include "instrument.h"
+#include "js.h"
 #include "persistent.h"
 #include "prefetch.h"
 #include "ranges.h"
@@ -16,46 +21,55 @@
 #include "stats.h"
 #include "util.h"
 
-static gboolean               tracing = false;
-static gboolean               optimize = false;
+gboolean instrument_tracing = false;
+gboolean instrument_optimize = false;
+gboolean instrument_unique = false;
+guint64  instrument_hash_zero = 0;
+guint64  instrument_hash_seed = 0;
+
+gboolean instrument_use_fixed_seed = FALSE;
+guint64  instrument_fixed_seed = 0;
+
 static GumStalkerTransformer *transformer = NULL;
 
-__thread uint64_t previous_pc = 0;
+__thread guint64 instrument_previous_pc = 0;
 
-__attribute__((hot)) static void on_basic_block(GumCpuContext *context,
-                                                gpointer       user_data) {
+static GumAddress previous_rip = 0;
+static u8 *       edges_notified = NULL;
 
-  UNUSED_PARAMETER(context);
-  /*
-   * This function is performance critical as it is called to instrument every
-   * basic block. By moving our print buffer to a global, we avoid it affecting
-   * the critical path with additional stack adjustments if tracing is not
-   * enabled. If tracing is enabled, then we're printing a load of diagnostic
-   * information so this overhead is unlikely to be noticeable.
-   */
-  static char buffer[200];
-  int         len;
-  GumAddress  current_pc = GUM_ADDRESS(user_data);
-  uint8_t *   cursor;
-  uint64_t    value;
-  if (unlikely(tracing)) {
-
-    /* Avoid any functions which may cause an allocation since the target app
-     * may already be running inside malloc and it isn't designed to be
-     * re-entrant on a single thread */
-    len = snprintf(buffer, sizeof(buffer),
-                   "current_pc: 0x%016" G_GINT64_MODIFIER
-                   "x, previous_pc: 0x%016" G_GINT64_MODIFIER "x\n",
-                   current_pc, previous_pc);
-
-    IGNORED_RETURN(write(STDOUT_FILENO, buffer, len + 1));
+static void trace_debug(char *format, ...) {
 
-  }
+  va_list ap;
+  char    buffer[4096] = {0};
+  int     ret;
+  int     len;
 
-  current_pc = (current_pc >> 4) ^ (current_pc << 8);
-  current_pc &= MAP_SIZE - 1;
+  va_start(ap, format);
+  ret = vsnprintf(buffer, sizeof(buffer) - 1, format, ap);
+  va_end(ap);
+
+  if (ret < 0) { return; }
+
+  len = strnlen(buffer, sizeof(buffer));
+
+  IGNORED_RETURN(write(STDOUT_FILENO, buffer, len));
+
+}
 
-  cursor = &__afl_area_ptr[current_pc ^ previous_pc];
+guint64 instrument_get_offset_hash(GumAddress current_rip) {
+
+  guint64 area_offset = hash64((unsigned char *)&current_rip,
+                               sizeof(GumAddress), instrument_hash_seed);
+  return area_offset &= MAP_SIZE - 1;
+
+}
+
+__attribute__((hot)) static void instrument_increment_map(GumAddress edge) {
+
+  uint8_t *cursor;
+  uint64_t value;
+
+  cursor = &__afl_area_ptr[edge];
   value = *cursor;
 
   if (value == 0xff) {
@@ -69,12 +83,47 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
   }
 
   *cursor = value;
-  previous_pc = current_pc >> 1;
 
 }
 
-static void instr_basic_block(GumStalkerIterator *iterator,
-                              GumStalkerOutput *output, gpointer user_data) {
+__attribute__((hot)) static void on_basic_block(GumCpuContext *context,
+                                                gpointer       user_data) {
+
+  UNUSED_PARAMETER(context);
+
+  GumAddress current_rip = GUM_ADDRESS(user_data);
+  guint64    current_pc = instrument_get_offset_hash(current_rip);
+  guint64    edge;
+
+  edge = current_pc ^ instrument_previous_pc;
+
+  instrument_increment_map(edge);
+
+  if (unlikely(instrument_tracing)) {
+
+    if (!instrument_unique || edges_notified[edge] == 0) {
+
+      trace_debug("TRACE: edge: %10" G_GINT64_MODIFIER
+                  "d, current_rip: 0x%016" G_GINT64_MODIFIER
+                  "x, previous_rip: 0x%016" G_GINT64_MODIFIER "x\n",
+                  edge, current_rip, previous_rip);
+
+    }
+
+    if (instrument_unique) { edges_notified[edge] = 1; }
+
+    previous_rip = current_rip;
+
+  }
+
+  instrument_previous_pc =
+      ((current_pc & (MAP_SIZE - 1) >> 1)) | ((current_pc & 0x1) << 15);
+
+}
+
+static void instrument_basic_block(GumStalkerIterator *iterator,
+                                   GumStalkerOutput *  output,
+                                   gpointer            user_data) {
 
   UNUSED_PARAMETER(user_data);
 
@@ -84,7 +133,9 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
   while (gum_stalker_iterator_next(iterator, &instr)) {
 
-    if (instr->address == entry_start) { entry_prologue(iterator, output); }
+    if (unlikely(begin)) { instrument_debug_start(instr->address, output); }
+
+    if (instr->address == entry_point) { entry_prologue(iterator, output); }
     if (instr->address == persistent_start) { persistent_prologue(output); }
     if (instr->address == persistent_ret) { persistent_epilogue(output); }
 
@@ -121,11 +172,15 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
       instrument_debug_start(instr->address, output);
 
-      prefetch_write(GSIZE_TO_POINTER(instr->address));
+      if (likely(entry_reached)) {
+
+        prefetch_write(GSIZE_TO_POINTER(instr->address));
+
+      }
 
       if (likely(!excluded)) {
 
-        if (likely(optimize)) {
+        if (likely(instrument_optimize)) {
 
           instrument_coverage_optimize(instr, output);
 
@@ -138,8 +193,6 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
       }
 
-      begin = FALSE;
-
     }
 
     instrument_debug_instruction(instr->address, instr->size);
@@ -151,38 +204,117 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
     }
 
-    gum_stalker_iterator_keep(iterator);
+    if (js_stalker_callback(instr, begin, excluded, output)) {
+
+      gum_stalker_iterator_keep(iterator);
+
+    }
+
+    begin = FALSE;
 
   }
 
+  instrument_flush(output);
   instrument_debug_end(output);
 
 }
 
+void instrument_config(void) {
+
+  instrument_optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
+  instrument_tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
+  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_debug_config();
+  asan_config();
+  cmplog_config();
+
+}
+
 void instrument_init(void) {
 
-  optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
-  tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
+  if (!instrument_is_coverage_optimize_supported()) instrument_optimize = false;
 
-  if (!instrument_is_coverage_optimize_supported()) optimize = false;
+  OKF("Instrumentation - optimize [%c]", instrument_optimize ? 'X' : ' ');
+  OKF("Instrumentation - tracing [%c]", instrument_tracing ? 'X' : ' ');
+  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 - optimize [%c]", optimize ? 'X' : ' ');
-  OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' ');
+  if (instrument_tracing && instrument_optimize) {
 
-  if (tracing && optimize) {
+    WARNF("AFL_FRIDA_INST_TRACE implies AFL_FRIDA_INST_NO_OPTIMIZE");
+    instrument_optimize = FALSE;
+
+  }
 
-    FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible");
+  if (instrument_unique && instrument_optimize) {
+
+    WARNF("AFL_FRIDA_INST_TRACE_UNIQUE implies AFL_FRIDA_INST_NO_OPTIMIZE");
+    instrument_optimize = FALSE;
 
   }
 
+  if (instrument_unique) { instrument_tracing = TRUE; }
+
   if (__afl_map_size != 0x10000) {
 
     FATAL("Bad map size: 0x%08x", __afl_map_size);
 
   }
 
-  transformer =
-      gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL);
+  transformer = gum_stalker_transformer_make_from_callback(
+      instrument_basic_block, NULL, NULL);
+
+  if (instrument_unique) {
+
+    int shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
+    if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
+
+    edges_notified = shmat(shm_id, NULL, 0);
+    g_assert(edges_notified != MAP_FAILED);
+
+    /*
+     * Configure the shared memory region to be removed once the process
+     * dies.
+     */
+    if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
+
+      FATAL("shmctl (IPC_RMID) < 0 - errno: %d\n", errno);
+
+    }
+
+    /* Clear it, not sure it's necessary, just seems like good practice */
+    memset(edges_notified, '\0', MAP_SIZE);
+
+  }
+
+  if (instrument_use_fixed_seed) {
+
+    /*
+     * This configuration option may be useful for diagnostics or
+     * debugging.
+     */
+    instrument_hash_seed = instrument_fixed_seed;
+
+  } else {
+
+    /*
+     * By using a different seed value for the hash, we can make different
+     * instances have edge collisions in different places when carrying out
+     * parallel fuzzing. The seed itself, doesn't have to be random, it
+     * just needs to be different for each instance.
+     */
+    instrument_hash_seed = g_get_monotonic_time() ^
+                           (((guint64)getpid()) << 32) ^ syscall(SYS_gettid);
+
+  }
+
+  OKF("Instrumentation - seed [0x%016" G_GINT64_MODIFIER "x]",
+      instrument_hash_seed);
+  instrument_hash_zero = instrument_get_offset_hash(0);
 
   instrument_debug_init();
   asan_init();
@@ -197,3 +329,9 @@ GumStalkerTransformer *instrument_get_transformer(void) {
 
 }
 
+void instrument_on_fork() {
+
+  instrument_previous_pc = instrument_hash_zero;
+
+}
+