about summary refs log tree commit diff
path: root/frida_mode/src
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode/src')
-rw-r--r--frida_mode/src/ctx/ctx_arm32.c2
-rw-r--r--frida_mode/src/entry.c19
-rw-r--r--frida_mode/src/instrument/instrument.c99
-rw-r--r--frida_mode/src/instrument/instrument_arm64.c16
-rw-r--r--frida_mode/src/instrument/instrument_x64.c36
-rw-r--r--frida_mode/src/instrument/instrument_x86.c22
-rw-r--r--frida_mode/src/js/api.js14
-rw-r--r--frida_mode/src/js/js.c62
-rw-r--r--frida_mode/src/js/js_api.c97
-rw-r--r--frida_mode/src/main.c2
-rw-r--r--frida_mode/src/persistent/persistent.c20
-rw-r--r--frida_mode/src/persistent/persistent_arm32.c4
-rw-r--r--frida_mode/src/persistent/persistent_arm64.c6
-rw-r--r--frida_mode/src/persistent/persistent_x64.c6
-rw-r--r--frida_mode/src/persistent/persistent_x86.c6
-rw-r--r--frida_mode/src/ranges.c89
-rw-r--r--frida_mode/src/stalker.c8
-rw-r--r--frida_mode/src/stats/stats.c6
-rw-r--r--frida_mode/src/stats/stats_x64.c18
19 files changed, 407 insertions, 125 deletions
diff --git a/frida_mode/src/ctx/ctx_arm32.c b/frida_mode/src/ctx/ctx_arm32.c
index a354c117..9fc70fb4 100644
--- a/frida_mode/src/ctx/ctx_arm32.c
+++ b/frida_mode/src/ctx/ctx_arm32.c
@@ -6,7 +6,7 @@
 
 #if defined(__arm__)
 
-gsize ctx_read_reg(GumIA32CpuContext *ctx, x86_reg reg) {
+gsize ctx_read_reg(GumArmCpuContext *ctx, arm_reg reg) {
 
   FATAL("ctx_read_reg unimplemented for this architecture");
 
diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c
index e95b923b..a0ffd028 100644
--- a/frida_mode/src/entry.c
+++ b/frida_mode/src/entry.c
@@ -4,12 +4,16 @@
 
 #include "entry.h"
 #include "instrument.h"
+#include "persistent.h"
+#include "ranges.h"
 #include "stalker.h"
+#include "stats.h"
 #include "util.h"
 
 extern void __afl_manual_init();
 
-guint64 entry_point = 0;
+guint64  entry_point = 0;
+gboolean entry_reached = FALSE;
 
 static void entry_launch(void) {
 
@@ -17,7 +21,8 @@ static void entry_launch(void) {
   __afl_manual_init();
 
   /* Child here */
-  instrument_previous_pc = 0;
+  instrument_on_fork();
+  stats_on_fork();
 
 }
 
@@ -50,6 +55,16 @@ static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) {
 void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) {
 
   UNUSED_PARAMETER(output);
+  OKF("AFL_ENTRYPOINT reached");
+
+  if (persistent_start == 0) {
+
+    entry_reached = TRUE;
+    ranges_exclude();
+    stalker_trust();
+
+  }
+
   gum_stalker_iterator_put_callout(iterator, entry_callout, NULL, NULL);
 
 }
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index 2a217d96..67aafa5a 100644
--- a/frida_mode/src/instrument/instrument.c
+++ b/frida_mode/src/instrument/instrument.c
@@ -1,11 +1,13 @@
 #include <unistd.h>
 #include <sys/shm.h>
 #include <sys/mman.h>
+#include <sys/syscall.h>
 
 #include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
+#include "hash.h"
 
 #include "asan.h"
 #include "entry.h"
@@ -22,10 +24,15 @@
 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 instrument_previous_pc = 0;
+__thread guint64 instrument_previous_pc = 0;
 
 static GumAddress previous_rip = 0;
 static u8 *       edges_notified = NULL;
@@ -49,21 +56,18 @@ static void trace_debug(char *format, ...) {
 
 }
 
-__attribute__((hot)) static void on_basic_block(GumCpuContext *context,
-                                                gpointer       user_data) {
+guint64 instrument_get_offset_hash(GumAddress current_rip) {
 
-  UNUSED_PARAMETER(context);
+  guint64 area_offset = hash64((unsigned char *)&current_rip,
+                               sizeof(GumAddress), instrument_hash_seed);
+  return area_offset &= MAP_SIZE - 1;
 
-  GumAddress current_rip = GUM_ADDRESS(user_data);
-  GumAddress current_pc;
-  GumAddress edge;
-  uint8_t *  cursor;
-  uint64_t   value;
+}
 
-  current_pc = (current_rip >> 4) ^ (current_rip << 8);
-  current_pc &= MAP_SIZE - 1;
+__attribute__((hot)) static void instrument_increment_map(GumAddress edge) {
 
-  edge = current_pc ^ instrument_previous_pc;
+  uint8_t *cursor;
+  uint64_t value;
 
   cursor = &__afl_area_ptr[edge];
   value = *cursor;
@@ -79,7 +83,21 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
   }
 
   *cursor = value;
-  instrument_previous_pc = current_pc >> 1;
+
+}
+
+__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)) {
 
@@ -98,6 +116,9 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
 
   }
 
+  instrument_previous_pc =
+      ((current_pc & (MAP_SIZE - 1) >> 1)) | ((current_pc & 0x1) << 15);
+
 }
 
 static void instrument_basic_block(GumStalkerIterator *iterator,
@@ -149,7 +170,13 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
 
     if (unlikely(begin)) {
 
-      prefetch_write(GSIZE_TO_POINTER(instr->address));
+      instrument_debug_start(instr->address, output);
+
+      if (likely(entry_reached)) {
+
+        prefetch_write(GSIZE_TO_POINTER(instr->address));
+
+      }
 
       if (likely(!excluded)) {
 
@@ -197,6 +224,8 @@ 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();
@@ -211,16 +240,20 @@ void instrument_init(void) {
   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);
 
   if (instrument_tracing && instrument_optimize) {
 
-    FATAL("AFL_FRIDA_INST_TRACE requires AFL_FRIDA_INST_NO_OPTIMIZE");
+    WARNF("AFL_FRIDA_INST_TRACE implies AFL_FRIDA_INST_NO_OPTIMIZE");
+    instrument_optimize = FALSE;
 
   }
 
   if (instrument_unique && instrument_optimize) {
 
-    FATAL("AFL_FRIDA_INST_TRACE_UNIQUE requires AFL_FRIDA_INST_NO_OPTIMIZE");
+    WARNF("AFL_FRIDA_INST_TRACE_UNIQUE implies AFL_FRIDA_INST_NO_OPTIMIZE");
+    instrument_optimize = FALSE;
 
   }
 
@@ -244,7 +277,8 @@ void instrument_init(void) {
     g_assert(edges_notified != MAP_FAILED);
 
     /*
-     * Configure the shared memory region to be removed once the process dies.
+     * Configure the shared memory region to be removed once the process
+     * dies.
      */
     if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
 
@@ -257,6 +291,31 @@ void instrument_init(void) {
 
   }
 
+  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();
   cmplog_init();
@@ -270,3 +329,9 @@ GumStalkerTransformer *instrument_get_transformer(void) {
 
 }
 
+void instrument_on_fork() {
+
+  instrument_previous_pc = instrument_hash_zero;
+
+}
+
diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c
index 17f97c97..cf37e048 100644
--- a/frida_mode/src/instrument/instrument_arm64.c
+++ b/frida_mode/src/instrument/instrument_arm64.c
@@ -12,15 +12,15 @@ static GumAddress current_log_impl = GUM_ADDRESS(0);
 static const guint8 afl_log_code[] = {
 
     // __afl_area_ptr[current_pc ^ previous_pc]++;
-    // previous_pc = current_pc >> 1;
+    // previous_pc = current_pc ROR 1;
     0xE1, 0x0B, 0xBF, 0xA9,  // stp x1, x2, [sp, -0x10]!
     0xE3, 0x13, 0xBF, 0xA9,  // stp x3, x4, [sp, -0x10]!
 
     // x0 = current_pc
-    0xe1, 0x01, 0x00, 0x58,  // ldr x1, #0x3c, =&__afl_area_ptr
+    0x21, 0x02, 0x00, 0x58,  // ldr x1, #0x44, =&__afl_area_ptr
     0x21, 0x00, 0x40, 0xf9,  // ldr x1, [x1] (=__afl_area_ptr)
 
-    0xe2, 0x01, 0x00, 0x58,  // ldr x2, #0x3c, =&previous_pc
+    0x22, 0x02, 0x00, 0x58,  // ldr x2, #0x44, =&previous_pc
     0x42, 0x00, 0x40, 0xf9,  // ldr x2, [x2] (=previous_pc)
 
     // __afl_area_ptr[current_pc ^ previous_pc]++;
@@ -30,8 +30,11 @@ static const guint8 afl_log_code[] = {
     0x63, 0x00, 0x1f, 0x9a,  // adc x3, x3, xzr
     0x23, 0x68, 0x22, 0xf8,  // str x3, [x1, x2]
 
-    // previous_pc = current_pc >> 1;
-    0xe0, 0x07, 0x40, 0x8b,  // add x0, xzr, x0, LSR #1
+    // previous_pc = current_pc ROR 1;
+    0xe4, 0x07, 0x40, 0x8b,  // add x4, xzr, x0, LSR #1
+    0xe0, 0xff, 0x00, 0x8b,  // add x0, xzr, x0, LSL #63
+    0x80, 0xc0, 0x40, 0x8b,  // add x0, x4, x0, LSR #48
+
     0xe2, 0x00, 0x00, 0x58,  // ldr x2, #0x1c, =&previous_pc
     0x40, 0x00, 0x00, 0xf9,  // str x0, [x2]
 
@@ -54,8 +57,7 @@ void instrument_coverage_optimize(const cs_insn *   instr,
                                   GumStalkerOutput *output) {
 
   guint64 current_pc = instr->address;
-  guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
-  area_offset &= MAP_SIZE - 1;
+  guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
   GumArm64Writer *cw = output->writer.arm64;
 
   if (current_log_impl == 0 ||
diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c
index a38b5b14..fec8afbb 100644
--- a/frida_mode/src/instrument/instrument_x64.c
+++ b/frida_mode/src/instrument/instrument_x64.c
@@ -10,23 +10,21 @@ static GumAddress current_log_impl = GUM_ADDRESS(0);
 
 static const guint8 afl_log_code[] = {
 
-    // 0xcc,
-
     0x9c,                                                         /* pushfq */
     0x51,                                                       /* push rcx */
     0x52,                                                       /* push rdx */
 
-    0x48, 0x8b, 0x0d, 0x28,
+    0x48, 0x8b, 0x0d, 0x26,
     0x00, 0x00, 0x00,                          /* mov rcx, sym.&previous_pc */
     0x48, 0x8b, 0x11,                               /* mov rdx, qword [rcx] */
     0x48, 0x31, 0xfa,                                       /* xor rdx, rdi */
 
-    0x48, 0x03, 0x15, 0x13,
+    0x48, 0x03, 0x15, 0x11,
     0x00, 0x00, 0x00,                     /* add rdx, sym._afl_area_ptr_ptr */
 
     0x80, 0x02, 0x01,                              /* add byte ptr [rdx], 1 */
     0x80, 0x12, 0x00,                              /* adc byte ptr [rdx], 0 */
-    0x48, 0xd1, 0xef,                                         /* shr rdi, 1 */
+    0x66, 0xd1, 0xcf,                                          /* ror di, 1 */
     0x48, 0x89, 0x39,                               /* mov qword [rcx], rdi */
 
     0x5a,                                                        /* pop rdx */
@@ -34,7 +32,8 @@ static const guint8 afl_log_code[] = {
     0x9d,                                                          /* popfq */
 
     0xc3,                                                            /* ret */
-    0x90, 0x90, 0x90                                             /* nop pad */
+
+    0x90
 
     /* Read-only data goes here: */
     /* uint8_t* __afl_area_ptr */
@@ -48,12 +47,11 @@ gboolean instrument_is_coverage_optimize_supported(void) {
 
 }
 
-void instrument_coverage_optimize(const cs_insn *   instr,
-                                  GumStalkerOutput *output) {
+static guint8 align_pad[] = {0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
 
-  guint64 current_pc = instr->address;
-  guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
-  area_offset &= MAP_SIZE - 1;
+static void instrument_coverate_write_function(GumStalkerOutput *output) {
+
+  guint64       misalign = 0;
   GumX86Writer *cw = output->writer.x86;
 
   if (current_log_impl == 0 ||
@@ -65,6 +63,13 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
     gum_x86_writer_put_jmp_near_label(cw, after_log_impl);
 
+    misalign = (cw->pc & 0x7);
+    if (misalign != 0) {
+
+      gum_x86_writer_put_bytes(cw, align_pad, 8 - misalign);
+
+    }
+
     current_log_impl = cw->pc;
     gum_x86_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
 
@@ -78,6 +83,15 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
   }
 
+}
+
+void instrument_coverage_optimize(const cs_insn *   instr,
+                                  GumStalkerOutput *output) {
+
+  GumX86Writer *cw = output->writer.x86;
+  guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
+  instrument_coverate_write_function(output);
+
   gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
                                         -GUM_RED_ZONE_SIZE);
   gum_x86_writer_put_push_reg(cw, GUM_REG_RDI);
diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c
index 3c3dc272..7bf48f96 100644
--- a/frida_mode/src/instrument/instrument_x86.c
+++ b/frida_mode/src/instrument/instrument_x86.c
@@ -30,7 +30,8 @@ static void instrument_coverage_function(GumX86Writer *cw) {
   uint8_t adc_byte_ptr_edx_0[] = {0x80, 0x12, 0x00};
   gum_x86_writer_put_bytes(cw, adc_byte_ptr_edx_0, sizeof(adc_byte_ptr_edx_0));
 
-  gum_x86_writer_put_shr_reg_u8(cw, GUM_REG_EDI, 1);
+  uint8_t ror_di_1[] = {0x66, 0xd1, 0xcf};
+  gum_x86_writer_put_bytes(cw, ror_di_1, sizeof(ror_di_1));
   gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_ECX, GUM_REG_EDI);
 
   gum_x86_writer_put_pop_reg(cw, GUM_REG_EDX);
@@ -46,15 +47,8 @@ gboolean instrument_is_coverage_optimize_supported(void) {
 
 }
 
-void instrument_coverage_optimize(const cs_insn *   instr,
-                                  GumStalkerOutput *output) {
-
-  UNUSED_PARAMETER(instr);
-  UNUSED_PARAMETER(output);
+static void instrument_coverate_write_function(GumStalkerOutput *output) {
 
-  guint64 current_pc = instr->address;
-  guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8);
-  area_offset &= MAP_SIZE - 1;
   GumX86Writer *cw = output->writer.x86;
 
   if (current_log_impl == 0 ||
@@ -73,7 +67,15 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
   }
 
-  // gum_x86_writer_put_breakpoint(cw);
+}
+
+void instrument_coverage_optimize(const cs_insn *   instr,
+                                  GumStalkerOutput *output) {
+
+  GumX86Writer *cw = output->writer.x86;
+  guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
+  instrument_coverate_write_function(output);
+
   gum_x86_writer_put_push_reg(cw, GUM_REG_EDI);
   gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EDI, area_offset);
   gum_x86_writer_put_call_address(cw, current_log_impl);
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
index 4cb04704..b8f2d39a 100644
--- a/frida_mode/src/js/api.js
+++ b/frida_mode/src/js/api.js
@@ -100,6 +100,12 @@ class Afl {
         Afl.jsApiSetInstrumentTrace();
     }
     /**
+     * See `AFL_FRIDA_INST_JIT`.
+     */
+    static setInstrumentJit() {
+        Afl.jsApiSetInstrumentJit();
+    }
+    /**
      * See `AFL_INST_LIBS`.
      */
     static setInstrumentLibraries() {
@@ -111,6 +117,12 @@ class Afl {
     static setInstrumentNoOptimize() {
         Afl.jsApiSetInstrumentNoOptimize();
     }
+    /*
+     * See `AFL_FRIDA_INST_SEED`
+     */
+    static setInstrumentSeed(seed) {
+        Afl.jsApiSetInstrumentSeed(seed);
+    }
     /**
      * See `AFL_FRIDA_INST_TRACE_UNIQUE`.
      */
@@ -222,8 +234,10 @@ Afl.jsApiError = Afl.jsApiGetFunction("js_api_error", "void", ["pointer"]);
 Afl.jsApiSetDebugMaps = Afl.jsApiGetFunction("js_api_set_debug_maps", "void", []);
 Afl.jsApiSetEntryPoint = Afl.jsApiGetFunction("js_api_set_entrypoint", "void", ["pointer"]);
 Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_debug_file", "void", ["pointer"]);
+Afl.jsApiSetInstrumentJit = Afl.jsApiGetFunction("js_api_set_instrument_jit", "void", []);
 Afl.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []);
 Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []);
+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.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]);
diff --git a/frida_mode/src/js/js.c b/frida_mode/src/js/js.c
index ed378d2c..e3cd4933 100644
--- a/frida_mode/src/js/js.c
+++ b/frida_mode/src/js/js.c
@@ -9,12 +9,15 @@ static char *             js_script = NULL;
 gboolean                  js_done = FALSE;
 js_api_stalker_callback_t js_user_callback = NULL;
 
-static gchar *           filename = "afl.js";
-static gchar *           contents;
-static GumScriptBackend *backend;
-static GCancellable *    cancellable = NULL;
-static GError *          error = NULL;
-static GumScript *       script;
+static gchar *             filename = "afl.js";
+static gchar *             contents;
+static GumScriptBackend *  backend;
+static GCancellable *      cancellable = NULL;
+static GError *            error = NULL;
+static GumScript *         script;
+static GumScriptScheduler *scheduler;
+static GMainContext *      context;
+static GMainLoop *         main_loop;
 
 static void js_msg(GumScript *script, const gchar *message, GBytes *data,
                    gpointer user_data) {
@@ -80,31 +83,48 @@ static void js_print_script(gchar *source) {
 
 }
 
-void js_start(void) {
+static void load_cb(GObject *source_object, GAsyncResult *result,
+                    gpointer user_data) {
 
-  GMainContext *context;
+  UNUSED_PARAMETER(source_object);
+  UNUSED_PARAMETER(user_data);
+  gum_script_load_finish(script, result);
+  if (error != NULL) { FATAL("Failed to load script - %s", error->message); }
 
-  gchar *source = js_get_script();
-  if (source == NULL) { return; }
-  js_print_script(source);
+}
 
-  backend = gum_script_backend_obtain_qjs();
+static void create_cb(GObject *source_object, GAsyncResult *result,
+                      gpointer user_data) {
 
-  script = gum_script_backend_create_sync(backend, "example", source,
-                                          cancellable, &error);
+  UNUSED_PARAMETER(source_object);
+  UNUSED_PARAMETER(user_data);
+  script = gum_script_backend_create_finish(backend, result, &error);
+  if (error != NULL) { FATAL("Failed to create script: %s", error->message); }
 
-  if (error != NULL) {
+  gum_script_set_message_handler(script, js_msg, NULL, NULL);
 
-    g_printerr("%s\n", error->message);
-    FATAL("Error processing script");
+  gum_script_load(script, cancellable, load_cb, NULL);
 
-  }
+}
 
-  gum_script_set_message_handler(script, js_msg, NULL, NULL);
+void js_start(void) {
+
+  gchar *source = js_get_script();
+  if (source == NULL) { return; }
+  js_print_script(source);
+
+  scheduler = gum_script_backend_get_scheduler();
+  gum_script_scheduler_disable_background_thread(scheduler);
+
+  backend = gum_script_backend_obtain_qjs();
+
+  context = gum_script_scheduler_get_js_context(scheduler);
+  main_loop = g_main_loop_new(context, true);
+  g_main_context_push_thread_default(context);
 
-  gum_script_load_sync(script, cancellable);
+  gum_script_backend_create(backend, "example", source, cancellable, create_cb,
+                            &error);
 
-  context = g_main_context_get_thread_default();
   while (g_main_context_pending(context))
     g_main_context_iteration(context, FALSE);
 
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
index 91dccab2..930a6dc0 100644
--- a/frida_mode/src/js/js_api.c
+++ b/frida_mode/src/js/js_api.c
@@ -9,142 +9,191 @@
 #include "ranges.h"
 #include "stats.h"
 #include "util.h"
-
-void js_api_done() {
+__attribute__((visibility("default"))) void js_api_done() {
 
   js_done = TRUE;
 
 }
 
-void js_api_error(char *msg) {
+__attribute__((visibility("default"))) void js_api_error(char *msg) {
 
   FATAL("%s", msg);
 
 }
 
-void js_api_set_entrypoint(void *address) {
+__attribute__((visibility("default"))) void js_api_set_entrypoint(
+    void *address) {
+
+  if (address == NULL) {
+
+    js_api_error("js_api_set_entrypoint called with NULL");
+
+  }
 
   entry_point = GPOINTER_TO_SIZE(address);
 
 }
 
-void js_api_set_persistent_address(void *address) {
+__attribute__((visibility("default"))) void js_api_set_persistent_address(
+    void *address) {
+
+  if (address == NULL) {
+
+    js_api_error("js_api_set_persistent_address called with NULL");
+
+  }
 
   persistent_start = GPOINTER_TO_SIZE(address);
 
 }
 
-void js_api_set_persistent_return(void *address) {
+__attribute__((visibility("default"))) void js_api_set_persistent_return(
+    void *address) {
+
+  if (address == NULL) {
+
+    js_api_error("js_api_set_persistent_return called with NULL");
+
+  }
 
   persistent_ret = GPOINTER_TO_SIZE(address);
 
 }
 
-void js_api_set_persistent_count(uint64_t count) {
+__attribute__((visibility("default"))) void js_api_set_persistent_count(
+    uint64_t count) {
 
   persistent_count = count;
 
 }
 
-void js_api_set_persistent_debug() {
+__attribute__((visibility("default"))) void js_api_set_persistent_debug() {
 
   persistent_debug = TRUE;
 
 }
 
-void js_api_set_debug_maps() {
+__attribute__((visibility("default"))) void js_api_set_debug_maps() {
 
   ranges_debug_maps = TRUE;
 
 }
 
-void js_api_add_include_range(void *address, gsize size) {
+__attribute__((visibility("default"))) void js_api_add_include_range(
+    void *address, gsize size) {
 
   GumMemoryRange range = {.base_address = GUM_ADDRESS(address), .size = size};
   ranges_add_include(&range);
 
 }
 
-void js_api_add_exclude_range(void *address, gsize size) {
+__attribute__((visibility("default"))) void js_api_add_exclude_range(
+    void *address, gsize size) {
 
   GumMemoryRange range = {.base_address = GUM_ADDRESS(address), .size = size};
   ranges_add_exclude(&range);
 
 }
 
-void js_api_set_instrument_libraries() {
+__attribute__((visibility("default"))) void js_api_set_instrument_jit() {
+
+  ranges_inst_jit = TRUE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_instrument_libraries() {
 
   ranges_inst_libs = TRUE;
 
 }
 
-void js_api_set_instrument_debug_file(char *path) {
+__attribute__((visibility("default"))) void js_api_set_instrument_debug_file(
+    char *path) {
 
   instrument_debug_filename = g_strdup(path);
 
 }
 
-void js_api_set_prefetch_disable(void) {
+__attribute__((visibility("default"))) void js_api_set_prefetch_disable(void) {
 
   prefetch_enable = FALSE;
 
 }
 
-void js_api_set_instrument_no_optimize(void) {
+__attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
+    void) {
 
   instrument_optimize = FALSE;
 
 }
 
-void js_api_set_instrument_trace(void) {
+__attribute__((visibility("default"))) void js_api_set_instrument_seed(
+    guint64 seed) {
+
+  instrument_use_fixed_seed = TRUE;
+  instrument_fixed_seed = seed;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_instrument_trace(void) {
 
   instrument_tracing = TRUE;
 
 }
 
-void js_api_set_instrument_trace_unique(void) {
+__attribute__((visibility("default"))) void js_api_set_instrument_trace_unique(
+    void) {
 
   instrument_unique = TRUE;
 
 }
 
-void js_api_set_stdout(char *file) {
+__attribute__((visibility("default"))) void js_api_set_stdout(char *file) {
 
   output_stdout = g_strdup(file);
 
 }
 
-void js_api_set_stderr(char *file) {
+__attribute__((visibility("default"))) void js_api_set_stderr(char *file) {
 
   output_stderr = g_strdup(file);
 
 }
 
-void js_api_set_stats_file(char *file) {
+__attribute__((visibility("default"))) void js_api_set_stats_file(char *file) {
 
   stats_filename = g_strdup(file);
 
 }
 
-void js_api_set_stats_interval(uint64_t interval) {
+__attribute__((visibility("default"))) void js_api_set_stats_interval(
+    uint64_t interval) {
 
   stats_interval = interval;
 
 }
 
-void js_api_set_stats_transitions() {
+__attribute__((visibility("default"))) void js_api_set_stats_transitions() {
 
   stats_transitions = TRUE;
 
 }
 
-void js_api_set_persistent_hook(void *address) {
+__attribute__((visibility("default"))) void js_api_set_persistent_hook(
+    void *address) {
+
+  if (address == NULL) {
+
+    js_api_error("js_api_set_persistent_hook called with NULL");
+
+  }
 
   persistent_hook = address;
 
 }
 
-void js_api_set_stalker_callback(const js_api_stalker_callback_t callback) {
+__attribute__((visibility("default"))) void js_api_set_stalker_callback(
+    const js_api_stalker_callback_t callback) {
 
   js_user_callback = callback;
 
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index 85b0bbf3..91687046 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -163,7 +163,7 @@ static void afl_print_env(void) {
 
 }
 
-void afl_frida_start(void) {
+__attribute__((visibility("default"))) void afl_frida_start(void) {
 
   afl_print_cmdline();
   afl_print_env();
diff --git a/frida_mode/src/persistent/persistent.c b/frida_mode/src/persistent/persistent.c
index bcc59ea7..639a694e 100644
--- a/frida_mode/src/persistent/persistent.c
+++ b/frida_mode/src/persistent/persistent.c
@@ -5,7 +5,10 @@
 #include "config.h"
 #include "debug.h"
 
+#include "entry.h"
 #include "persistent.h"
+#include "ranges.h"
+#include "stalker.h"
 #include "util.h"
 
 int          __afl_sharedmem_fuzzing = 0;
@@ -83,3 +86,20 @@ void persistent_init(void) {
 
 }
 
+void persistent_prologue(GumStalkerOutput *output) {
+
+  OKF("AFL_FRIDA_PERSISTENT_ADDR reached");
+  entry_reached = TRUE;
+  ranges_exclude();
+  stalker_trust();
+  persistent_prologue_arch(output);
+
+}
+
+void persistent_epilogue(GumStalkerOutput *output) {
+
+  OKF("AFL_FRIDA_PERSISTENT_RET reached");
+  persistent_epilogue_arch(output);
+
+}
+
diff --git a/frida_mode/src/persistent/persistent_arm32.c b/frida_mode/src/persistent/persistent_arm32.c
index f12f1af8..769f1505 100644
--- a/frida_mode/src/persistent/persistent_arm32.c
+++ b/frida_mode/src/persistent/persistent_arm32.c
@@ -61,14 +61,14 @@ gboolean persistent_is_supported(void) {
 
 }
 
-void persistent_prologue(GumStalkerOutput *output) {
+void persistent_prologue_arch(GumStalkerOutput *output) {
 
   UNUSED_PARAMETER(output);
   FATAL("Persistent mode not supported on this architecture");
 
 }
 
-void persistent_epilogue(GumStalkerOutput *output) {
+void persistent_epilogue_arch(GumStalkerOutput *output) {
 
   UNUSED_PARAMETER(output);
   FATAL("Persistent mode not supported on this architecture");
diff --git a/frida_mode/src/persistent/persistent_arm64.c b/frida_mode/src/persistent/persistent_arm64.c
index 003f058a..3cd61cd5 100644
--- a/frida_mode/src/persistent/persistent_arm64.c
+++ b/frida_mode/src/persistent/persistent_arm64.c
@@ -237,7 +237,7 @@ static void instrument_exit(GumArm64Writer *cw) {
 static int instrument_afl_persistent_loop_func(void) {
 
   int ret = __afl_persistent_loop(persistent_count);
-  instrument_previous_pc = 0;
+  instrument_previous_pc = instrument_hash_zero;
   return ret;
 
 }
@@ -299,7 +299,7 @@ static void instrument_persitent_save_lr(GumArm64Writer *cw) {
 
 }
 
-void persistent_prologue(GumStalkerOutput *output) {
+void persistent_prologue_arch(GumStalkerOutput *output) {
 
   /*
    *  SAVE REGS
@@ -366,7 +366,7 @@ void persistent_prologue(GumStalkerOutput *output) {
 
 }
 
-void persistent_epilogue(GumStalkerOutput *output) {
+void persistent_epilogue_arch(GumStalkerOutput *output) {
 
   GumArm64Writer *cw = output->writer.arm64;
 
diff --git a/frida_mode/src/persistent/persistent_x64.c b/frida_mode/src/persistent/persistent_x64.c
index b2186db1..c0bd9a09 100644
--- a/frida_mode/src/persistent/persistent_x64.c
+++ b/frida_mode/src/persistent/persistent_x64.c
@@ -174,7 +174,7 @@ static void instrument_exit(GumX86Writer *cw) {
 static int instrument_afl_persistent_loop_func(void) {
 
   int ret = __afl_persistent_loop(persistent_count);
-  instrument_previous_pc = 0;
+  instrument_previous_pc = instrument_hash_zero;
   return ret;
 
 }
@@ -244,7 +244,7 @@ static void instrument_persitent_save_ret(GumX86Writer *cw) {
 
 }
 
-void persistent_prologue(GumStalkerOutput *output) {
+void persistent_prologue_arch(GumStalkerOutput *output) {
 
   /*
    *  SAVE REGS
@@ -313,7 +313,7 @@ void persistent_prologue(GumStalkerOutput *output) {
 
 }
 
-void persistent_epilogue(GumStalkerOutput *output) {
+void persistent_epilogue_arch(GumStalkerOutput *output) {
 
   GumX86Writer *cw = output->writer.x86;
 
diff --git a/frida_mode/src/persistent/persistent_x86.c b/frida_mode/src/persistent/persistent_x86.c
index f50bccb0..b911676a 100644
--- a/frida_mode/src/persistent/persistent_x86.c
+++ b/frida_mode/src/persistent/persistent_x86.c
@@ -130,7 +130,7 @@ static void instrument_exit(GumX86Writer *cw) {
 static int instrument_afl_persistent_loop_func(void) {
 
   int ret = __afl_persistent_loop(persistent_count);
-  instrument_previous_pc = 0;
+  instrument_previous_pc = instrument_hash_zero;
   return ret;
 
 }
@@ -184,7 +184,7 @@ static void instrument_persitent_save_ret(GumX86Writer *cw) {
 
 }
 
-void persistent_prologue(GumStalkerOutput *output) {
+void persistent_prologue_arch(GumStalkerOutput *output) {
 
   /*
    *  SAVE REGS
@@ -251,7 +251,7 @@ void persistent_prologue(GumStalkerOutput *output) {
 
 }
 
-void persistent_epilogue(GumStalkerOutput *output) {
+void persistent_epilogue_arch(GumStalkerOutput *output) {
 
   GumX86Writer *cw = output->writer.x86;
 
diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c
index 534f202b..6fdd65a7 100644
--- a/frida_mode/src/ranges.c
+++ b/frida_mode/src/ranges.c
@@ -19,9 +19,11 @@ typedef struct {
 
 gboolean ranges_debug_maps = FALSE;
 gboolean ranges_inst_libs = FALSE;
+gboolean ranges_inst_jit = FALSE;
 
 static GArray *module_ranges = NULL;
 static GArray *libs_ranges = NULL;
+static GArray *jit_ranges = NULL;
 static GArray *include_ranges = NULL;
 static GArray *exclude_ranges = NULL;
 static GArray *ranges = NULL;
@@ -145,11 +147,13 @@ static void convert_name_token(gchar *token, GumMemoryRange *range) {
 
 static void convert_token(gchar *token, GumMemoryRange *range) {
 
-  if (g_strrstr(token, "-")) {
+  if (g_str_has_prefix(token, "0x")) {
 
     convert_address_token(token, range);
 
-  } else {
+  }
+
+  else {
 
     convert_name_token(token, range);
 
@@ -172,19 +176,27 @@ static gboolean print_ranges_callback(const GumRangeDetails *details,
                                       gpointer               user_data) {
 
   UNUSED_PARAMETER(user_data);
+
   if (details->file == NULL) {
 
-    OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER "X",
+    OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
+        "X %c%c%c",
         details->range->base_address,
-        details->range->base_address + details->range->size);
+        details->range->base_address + details->range->size,
+        details->protection & GUM_PAGE_READ ? 'R' : '-',
+        details->protection & GUM_PAGE_WRITE ? 'W' : '-',
+        details->protection & GUM_PAGE_EXECUTE ? 'X' : '-');
 
   } else {
 
     OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER
-        "X %s(0x%016" G_GINT64_MODIFIER "x)",
+        "X %c%c%c %s(0x%016" G_GINT64_MODIFIER "x)",
         details->range->base_address,
         details->range->base_address + details->range->size,
-        details->file->path, details->file->offset);
+        details->protection & GUM_PAGE_READ ? 'R' : '-',
+        details->protection & GUM_PAGE_WRITE ? 'W' : '-',
+        details->protection & GUM_PAGE_EXECUTE ? 'X' : '-', details->file->path,
+        details->file->offset);
 
   }
 
@@ -329,6 +341,39 @@ static GArray *collect_libs_ranges(void) {
 
 }
 
+static gboolean collect_jit_ranges_callback(const GumRangeDetails *details,
+                                            gpointer               user_data) {
+
+  GArray *ranges = (GArray *)user_data;
+
+  /* If the executable code isn't backed by a file, it's probably JIT */
+  if (details->file == NULL) {
+
+    GumMemoryRange range = *details->range;
+    g_array_append_val(ranges, range);
+
+  }
+
+  return TRUE;
+
+}
+
+static GArray *collect_jit_ranges(void) {
+
+  GArray *result;
+  result = g_array_new(false, false, sizeof(GumMemoryRange));
+  if (!ranges_inst_jit) {
+
+    gum_process_enumerate_ranges(GUM_PAGE_EXECUTE, collect_jit_ranges_callback,
+                                 result);
+
+  }
+
+  print_ranges("JIT", result);
+  return result;
+
+}
+
 static gboolean intersect_range(GumMemoryRange *rr, GumMemoryRange *ra,
                                 GumMemoryRange *rb) {
 
@@ -508,6 +553,14 @@ void ranges_config(void) {
 
   if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { ranges_debug_maps = TRUE; }
   if (getenv("AFL_INST_LIBS") != NULL) { ranges_inst_libs = TRUE; }
+  if (getenv("AFL_FRIDA_INST_JIT") != NULL) { ranges_inst_jit = TRUE; }
+
+  if (ranges_debug_maps) {
+
+    gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback,
+                                 NULL);
+
+  }
 
   include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
   exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
@@ -521,13 +574,13 @@ void ranges_init(void) {
   GArray *       step2;
   GArray *       step3;
   GArray *       step4;
+  GArray *       step5;
 
-  if (ranges_debug_maps) {
-
-    gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback,
-                                 NULL);
+  OKF("Ranges - Instrument jit [%c]", ranges_inst_jit ? 'X' : ' ');
+  OKF("Ranges - Instrument libraries [%c]", ranges_inst_libs ? 'X' : ' ');
 
-  }
+  print_ranges("AFL_FRIDA_INST_RANGES", include_ranges);
+  print_ranges("AFL_FRIDA_EXCLUDE_RANGES", exclude_ranges);
 
   OKF("Ranges - Instrument libraries [%c]", ranges_inst_libs ? 'X' : ' ');
 
@@ -536,6 +589,7 @@ void ranges_init(void) {
 
   module_ranges = collect_module_ranges();
   libs_ranges = collect_libs_ranges();
+  jit_ranges = collect_jit_ranges();
 
   /* If include ranges is empty, then assume everything is included */
   if (include_ranges->len == 0) {
@@ -558,17 +612,20 @@ void ranges_init(void) {
   step3 = subtract_ranges(step2, exclude_ranges);
   print_ranges("step3", step3);
 
+  step4 = subtract_ranges(step3, jit_ranges);
+  print_ranges("step4", step4);
+
   /*
-   * After step3, we have the total ranges to be instrumented, we now subtract
+   * After step4, we have the total ranges to be instrumented, we now subtract
    * that from the original ranges of the modules to configure stalker.
    */
+  step5 = subtract_ranges(module_ranges, step4);
+  print_ranges("step5", step5);
 
-  step4 = subtract_ranges(module_ranges, step3);
-  print_ranges("step4", step4);
-
-  ranges = merge_ranges(step4);
+  ranges = merge_ranges(step5);
   print_ranges("final", ranges);
 
+  g_array_free(step5, TRUE);
   g_array_free(step4, TRUE);
   g_array_free(step3, TRUE);
   g_array_free(step2, TRUE);
diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c
index 98483cde..5df0386f 100644
--- a/frida_mode/src/stalker.c
+++ b/frida_mode/src/stalker.c
@@ -38,7 +38,7 @@ void stalker_init(void) {
   stalker = gum_stalker_new();
   if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
 
-  gum_stalker_set_trust_threshold(stalker, 0);
+  gum_stalker_set_trust_threshold(stalker, -1);
 
   /* *NEVER* stalk the stalker, only bad things will ever come of this! */
   gum_process_enumerate_ranges(GUM_PAGE_EXECUTE, stalker_exclude_self, NULL);
@@ -59,3 +59,9 @@ void stalker_start(void) {
 
 }
 
+void stalker_trust(void) {
+
+  gum_stalker_set_trust_threshold(stalker, 0);
+
+}
+
diff --git a/frida_mode/src/stats/stats.c b/frida_mode/src/stats/stats.c
index 0dd8be70..91a58741 100644
--- a/frida_mode/src/stats/stats.c
+++ b/frida_mode/src/stats/stats.c
@@ -178,10 +178,12 @@ void stats_write(void) {
 
 }
 
-static void stats_maybe_write(void) {
+void stats_on_fork(void) {
 
   guint64 current_time;
 
+  if (stats_filename == NULL) { return; }
+
   if (stats_interval == 0) { return; }
 
   current_time = g_get_monotonic_time();
@@ -208,7 +210,5 @@ void stats_collect(const cs_insn *instr, gboolean begin) {
 
   stats_collect_arch(instr);
 
-  stats_maybe_write();
-
 }
 
diff --git a/frida_mode/src/stats/stats_x64.c b/frida_mode/src/stats/stats_x64.c
index 7c3a90d7..11464a2a 100644
--- a/frida_mode/src/stats/stats_x64.c
+++ b/frida_mode/src/stats/stats_x64.c
@@ -31,6 +31,9 @@ typedef struct {
 
   guint64 num_rip_relative;
 
+  guint64 num_rip_relative_type[X86_INS_ENDING];
+  char    name_rip_relative_type[X86_INS_ENDING][CS_MNEMONIC_SIZE];
+
 } stats_data_arch_t;
 
 gboolean stats_is_supported_arch(void) {
@@ -136,6 +139,18 @@ void stats_write_arch(void) {
               stats_data_arch->num_rip_relative,
               (stats_data_arch->num_rip_relative * 100 / num_instructions));
 
+  for (size_t i = 0; i < X86_INS_ENDING; i++) {
+
+    if (stats_data_arch->num_rip_relative_type[i] != 0) {
+
+      stats_print("                     %10d %s\n",
+                  stats_data_arch->num_rip_relative_type[i],
+                  stats_data_arch->name_rip_relative_type[i]);
+
+    }
+
+  }
+
   stats_print("\n");
   stats_print("\n");
 
@@ -256,6 +271,9 @@ static void stats_collect_rip_relative_arch(const cs_insn *instr) {
   if (rm != 5) { return; }
 
   stats_data_arch->num_rip_relative++;
+  stats_data_arch->num_rip_relative_type[instr->id]++;
+  memcpy(stats_data_arch->name_rip_relative_type[instr->id], instr->mnemonic,
+         CS_MNEMONIC_SIZE);
 
 }