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/instrument/instrument.c14
-rw-r--r--frida_mode/src/instrument/instrument_arm32.c34
-rw-r--r--frida_mode/src/instrument/instrument_arm64.c32
-rw-r--r--frida_mode/src/instrument/instrument_x64.c542
-rw-r--r--frida_mode/src/instrument/instrument_x64_cache.c435
-rw-r--r--frida_mode/src/instrument/instrument_x86.c33
-rw-r--r--frida_mode/src/js/api.js22
-rw-r--r--frida_mode/src/js/js_api.c20
-rw-r--r--frida_mode/src/main.c3
-rw-r--r--frida_mode/src/module.c106
-rw-r--r--frida_mode/src/ranges.c2
11 files changed, 928 insertions, 315 deletions
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index 418b35e8..003c3d00 100644
--- a/frida_mode/src/instrument/instrument.c
+++ b/frida_mode/src/instrument/instrument.c
@@ -29,6 +29,7 @@ guint64  instrument_hash_seed = 0;
 gboolean instrument_use_fixed_seed = FALSE;
 guint64  instrument_fixed_seed = 0;
 char *   instrument_coverage_unstable_filename = NULL;
+gboolean instrument_coverage_insn = FALSE;
 
 static GumStalkerTransformer *transformer = NULL;
 
@@ -233,6 +234,12 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
 
     }
 
+    if (instrument_coverage_insn) {
+
+      instrument_coverage_optimize_insn(instr, output);
+
+    }
+
     instrument_debug_instruction(instr->address, instr->size, output);
 
     if (likely(!excluded)) {
@@ -242,6 +249,8 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
 
     }
 
+    instrument_cache(instr, output);
+
     if (js_stalker_callback(instr, begin, excluded, output)) {
 
       gum_stalker_iterator_keep(iterator);
@@ -269,11 +278,13 @@ void instrument_config(void) {
   instrument_fixed_seed = util_read_num("AFL_FRIDA_INST_SEED", 0);
   instrument_coverage_unstable_filename =
       (getenv("AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE"));
+  instrument_coverage_insn = (getenv("AFL_FRIDA_INST_INSN") != NULL);
 
   instrument_debug_config();
   instrument_coverage_config();
   asan_config();
   cmplog_config();
+  instrument_cache_config();
 
 }
 
@@ -294,6 +305,8 @@ void instrument_init(void) {
        instrument_coverage_unstable_filename == NULL
            ? " "
            : instrument_coverage_unstable_filename);
+  FOKF(cBLU "Instrumentation" cRST " - " cGRN "instructions:" cYEL " [%c]",
+       instrument_coverage_insn ? 'X' : ' ');
 
   if (instrument_tracing && instrument_optimize) {
 
@@ -382,6 +395,7 @@ void instrument_init(void) {
   instrument_coverage_init();
   instrument_coverage_optimize_init();
   instrument_debug_init();
+  instrument_cache_init();
 
 }
 
diff --git a/frida_mode/src/instrument/instrument_arm32.c b/frida_mode/src/instrument/instrument_arm32.c
index 16e8eaab..80f3c26d 100644
--- a/frida_mode/src/instrument/instrument_arm32.c
+++ b/frida_mode/src/instrument/instrument_arm32.c
@@ -5,6 +5,9 @@
 
 #if defined(__arm__)
 
+gboolean instrument_cache_enabled = FALSE;
+gsize    instrument_cache_size = 0;
+
 gboolean instrument_is_coverage_optimize_supported(void) {
 
   return false;
@@ -20,6 +23,15 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
 }
 
+void instrument_coverage_optimize_insn(const cs_insn *   instr,
+                                       GumStalkerOutput *output) {
+
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(output);
+  FFATAL("Optimized coverage not supported on this architecture");
+
+}
+
 void instrument_coverage_optimize_init(void) {
 
   FWARNF("Optimized coverage not supported on this architecture");
@@ -46,5 +58,27 @@ gpointer instrument_cur(GumStalkerOutput *output) {
 
 }
 
+void instrument_cache_config(void) {
+
+}
+
+void instrument_cache_init(void) {
+
+}
+
+void instrument_cache_insert(gpointer real_address, gpointer code_address) {
+
+  UNUSED_PARAMETER(real_address);
+  UNUSED_PARAMETER(code_address);
+
+}
+
+void instrument_cache(const cs_insn *instr, GumStalkerOutput *output) {
+
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(output);
+
+}
+
 #endif
 
diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c
index 1a704585..e6251cb4 100644
--- a/frida_mode/src/instrument/instrument_arm64.c
+++ b/frida_mode/src/instrument/instrument_arm64.c
@@ -18,6 +18,8 @@
 
 #if defined(__aarch64__)
 
+gboolean instrument_cache_enabled = FALSE;
+gsize    instrument_cache_size = 0;
 static GHashTable *coverage_blocks = NULL;
 
 __attribute__((aligned(0x1000))) static guint8 area_ptr_dummy[MAP_SIZE];
@@ -341,6 +343,14 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
 }
 
+void instrument_coverage_optimize_insn(const cs_insn *   instr,
+                                       GumStalkerOutput *output) {
+
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(output);
+
+}
+
 void instrument_coverage_optimize_init(void) {
 
   char *shm_env = getenv(SHM_ENV_VAR);
@@ -371,5 +381,27 @@ gpointer instrument_cur(GumStalkerOutput *output) {
 
 }
 
+void instrument_cache_config(void) {
+
+}
+
+void instrument_cache_init(void) {
+
+}
+
+void instrument_cache_insert(gpointer real_address, gpointer code_address) {
+
+  UNUSED_PARAMETER(real_address);
+  UNUSED_PARAMETER(code_address);
+
+}
+
+void instrument_cache(const cs_insn *instr, GumStalkerOutput *output) {
+
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(output);
+
+}
+
 #endif
 
diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c
index fb84d6d2..d54c8353 100644
--- a/frida_mode/src/instrument/instrument_x64.c
+++ b/frida_mode/src/instrument/instrument_x64.c
@@ -23,13 +23,39 @@
 
 #if defined(__x86_64__)
 
-  #ifndef MAP_FIXED_NOREPLACE
-    #ifdef MAP_EXCL
-      #define MAP_FIXED_NOREPLACE MAP_EXCL | MAP_FIXED
-    #else
-      #define MAP_FIXED_NOREPLACE MAP_FIXED
-    #endif
-  #endif
+enum jcc_opcodes {
+
+  OPC_JO = 0x70,
+  OPC_JNO = 0x71,
+  OPC_JB = 0x72,
+  OPC_JAE = 0x73,
+  OPC_JE = 0x74,
+  OPC_JNE = 0x75,
+  OPC_JBE = 0x76,
+  OPC_JA = 0x77,
+  OPC_JS = 0x78,
+  OPC_JNS = 0x79,
+  OPC_JP = 0x7a,
+  OPC_JNP = 0x7b,
+  OPC_JL = 0x7c,
+  OPC_JGE = 0x7d,
+  OPC_JLE = 0x7e,
+  OPC_JG = 0x7f,
+
+};
+
+typedef union {
+
+  struct {
+
+    uint8_t opcode;
+    uint8_t distance;
+
+  };
+
+  uint8_t bytes[0];
+
+} jcc_insn;
 
 static GHashTable *coverage_blocks = NULL;
 
@@ -45,8 +71,7 @@ static gboolean instrument_coverage_in_range(gssize offset) {
 
 }
 
-  #ifdef __APPLE__
-    #pragma pack(push, 1)
+  #pragma pack(push, 1)
 
 typedef struct {
 
@@ -54,95 +79,28 @@ typedef struct {
   // shared_mem[cur_location ^ prev_location]++;
   // prev_location = cur_location >> 1;
 
-  //  mov    QWORD PTR [rsp-0x80],rax
-  //  lahf
-  //  mov    QWORD PTR [rsp-0x88],rax
-  //  mov    QWORD PTR [rsp-0x90],rbx
-  //  mov    eax,DWORD PTR [rip+0x333d5a]        # 0x7ffff6ff2740
-  //  mov    DWORD PTR [rip+0x333d3c],0x9fbb        # 0x7ffff6ff2740
-  //  lea    rax,[rip + 0x103f77]
-  //  mov    bl,BYTE PTR [rax]
-  //  add    bl,0x1
-  //  adc    bl,0x0
-  //  mov    BYTE PTR [rax],bl
-  //  mov    rbx,QWORD PTR [rsp-0x90]
-  //  mov    rax,QWORD PTR [rsp-0x88]
-  //  sahf
-  //  mov    rax,QWORD PTR [rsp-0x80]
-
-  uint8_t mov_rax_rsp_88[8];
-  uint8_t lahf;
-  uint8_t mov_rax_rsp_90[8];
-  uint8_t mov_rbx_rsp_98[8];
-
-  uint8_t mov_eax_prev_loc[6];
-  uint8_t mov_prev_loc_curr_loc_shr1[10];
-
-  uint8_t leax_eax_curr_loc[7];
+  // mov    QWORD PTR [rsp-0x88],rax
+  // lahf
+  // mov    QWORD PTR [rsp-0x90],rax
+  // mov    QWORD PTR [rsp-0x98],rbx
 
-  uint8_t mov_rbx_ptr_rax[2];
-  uint8_t add_bl_1[3];
-  uint8_t adc_bl_0[3];
-  uint8_t mov_ptr_rax_rbx[2];
+  // mov    eax,DWORD PTR [rip+0x1312334]
+  // xor    eax,0x3f77
 
-  uint8_t mov_rsp_98_rbx[8];
-  uint8_t mov_rsp_90_rax[8];
-  uint8_t sahf;
-  uint8_t mov_rsp_88_rax[8];
+  // lea    rbx,[rip+0x132338]
+  // add    rax,rbx
 
-} afl_log_code_asm_t;
+  // mov    bl,BYTE PTR [rax]
+  // add    bl,0x1
+  // adc    bl,0x0
+  // mov    BYTE PTR [rax],bl
 
-    #pragma pack(pop)
+  // mov    rbx,QWORD PTR [rsp-0x98]
+  // mov    rax,QWORD PTR [rsp-0x90]
+  // sahf
+  // mov    rax,QWORD PTR [rsp-0x88]
 
-static const afl_log_code_asm_t template =
-    {
-
-        .mov_rax_rsp_88 = {0x48, 0x89, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
-        .lahf = 0x9f,
-        .mov_rax_rsp_90 = {0x48, 0x89, 0x84, 0x24, 0x70, 0xFF, 0xFF, 0xFF},
-        .mov_rbx_rsp_98 = {0x48, 0x89, 0x9C, 0x24, 0x68, 0xFF, 0xFF, 0xFF},
-
-        .mov_eax_prev_loc = {0x8b, 0x05},
-        .mov_prev_loc_curr_loc_shr1 = {0xc7, 0x05},
-
-        .leax_eax_curr_loc = {0x48, 0x8d, 0x05},
-        .mov_rbx_ptr_rax = {0x8a, 0x18},
-        .add_bl_1 = {0x80, 0xc3, 0x01},
-        .adc_bl_0 = {0x80, 0xd3, 0x00},
-        .mov_ptr_rax_rbx = {0x88, 0x18},
-
-        .mov_rsp_98_rbx = {0x48, 0x8B, 0x9C, 0x24, 0x68, 0xFF, 0xFF, 0xFF},
-        .mov_rsp_90_rax = {0x48, 0x8B, 0x84, 0x24, 0x70, 0xFF, 0xFF, 0xFF},
-        .sahf = 0x9e,
-        .mov_rsp_88_rax = {0x48, 0x8B, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
-
-}
-
-;
-
-  #else
-    #pragma pack(push, 1)
-typedef struct {
-
-  // cur_location = (block_address >> 4) ^ (block_address << 8);
-  // shared_mem[cur_location ^ prev_location]++;
-  // prev_location = cur_location >> 1;
-
-  //  mov    QWORD PTR [rsp-0x80],rax
-  //  lahf
-  //  mov    QWORD PTR [rsp-0x88],rax
-  //  mov    QWORD PTR [rsp-0x90],rbx
-  //  mov    eax,DWORD PTR [rip+0x333d5a]        # 0x7ffff6ff2740
-  //  mov    DWORD PTR [rip+0x333d3c],0x9fbb        # 0x7ffff6ff2740
-  //  xor    eax,0x103f77
-  //  mov    bl,BYTE PTR [rax]
-  //  add    bl,0x1
-  //  adc    bl,0x0
-  //  mov    BYTE PTR [rax],bl
-  //  mov    rbx,QWORD PTR [rsp-0x90]
-  //  mov    rax,QWORD PTR [rsp-0x88]
-  //  sahf
-  //  mov    rax,QWORD PTR [rsp-0x80]
+  // mov    DWORD PTR [rip+0x13122f8],0x9fbb
 
   uint8_t mov_rax_rsp_88[8];
   uint8_t lahf;
@@ -150,10 +108,11 @@ typedef struct {
   uint8_t mov_rbx_rsp_98[8];
 
   uint8_t mov_eax_prev_loc[6];
-  uint8_t mov_prev_loc_curr_loc_shr1[10];
-
   uint8_t xor_eax_curr_loc[5];
 
+  uint8_t lea_rbx_area_ptr[7];
+  uint8_t add_rax_rbx[3];
+
   uint8_t mov_rbx_ptr_rax[2];
   uint8_t add_bl_1[3];
   uint8_t adc_bl_0[3];
@@ -164,9 +123,11 @@ typedef struct {
   uint8_t sahf;
   uint8_t mov_rsp_88_rax[8];
 
+  uint8_t mov_prev_loc_curr_loc_shr1[10];
+
 } afl_log_code_asm_t;
 
-    #pragma pack(pop)
+  #pragma pack(pop)
 
 static const afl_log_code_asm_t template =
     {
@@ -177,9 +138,10 @@ static const afl_log_code_asm_t template =
         .mov_rbx_rsp_98 = {0x48, 0x89, 0x9C, 0x24, 0x68, 0xFF, 0xFF, 0xFF},
 
         .mov_eax_prev_loc = {0x8b, 0x05},
-        .mov_prev_loc_curr_loc_shr1 = {0xc7, 0x05},
-
         .xor_eax_curr_loc = {0x35},
+        .lea_rbx_area_ptr = {0x48, 0x8d, 0x1d},
+        .add_rax_rbx = {0x48, 0x01, 0xd8},
+
         .mov_rbx_ptr_rax = {0x8a, 0x18},
         .add_bl_1 = {0x80, 0xc3, 0x01},
         .adc_bl_0 = {0x80, 0xd3, 0x00},
@@ -190,10 +152,11 @@ static const afl_log_code_asm_t template =
         .sahf = 0x9e,
         .mov_rsp_88_rax = {0x48, 0x8B, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
 
+        .mov_prev_loc_curr_loc_shr1 = {0xc7, 0x05},
+
 }
 
 ;
-  #endif
 
 typedef union {
 
@@ -202,171 +165,12 @@ typedef union {
 
 } afl_log_code;
 
-  #ifdef __APPLE__
-
 void instrument_coverage_optimize_init(void) {
 
-}
-
-  #else
-
-static gboolean instrument_coverage_find_low(const GumRangeDetails *details,
-                                             gpointer               user_data) {
-
-  static GumAddress last_limit = (64ULL << 10);
-  gpointer *        address = (gpointer *)user_data;
-
-  last_limit = GUM_ALIGN_SIZE(last_limit, __afl_map_size);
-
-  if ((details->range->base_address - last_limit) > __afl_map_size) {
-
-    *address = GSIZE_TO_POINTER(last_limit);
-    return FALSE;
-
-  }
-
-  if (details->range->base_address > ((2ULL << 30) - __afl_map_size)) {
-
-    return FALSE;
-
-  }
-
-  /*
-   * Align our buffer on a 64k boundary so that the low 16-bits of the address
-   * are zero, then we can just XOR the base address in, when we XOR with the
-   * current block ID.
-   */
-  last_limit = GUM_ALIGN_SIZE(
-      details->range->base_address + details->range->size, __afl_map_size);
-  return TRUE;
-
-}
-
-static void instrument_coverage_optimize_map_mmap_anon(gpointer address) {
-
-  __afl_area_ptr =
-      mmap(address, __afl_map_size, PROT_READ | PROT_WRITE,
-           MAP_FIXED_NOREPLACE | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
-  if (__afl_area_ptr != address) {
-
-    FATAL("Failed to map mmap __afl_area_ptr: %d", errno);
-
-  }
-
-}
-
-static void instrument_coverage_optimize_map_mmap(char *   shm_file_path,
-                                                  gpointer address) {
-
-  int shm_fd = -1;
-
-  if (munmap(__afl_area_ptr, __afl_map_size) != 0) {
-
-    FATAL("Failed to unmap previous __afl_area_ptr");
-
-  }
-
-  __afl_area_ptr = NULL;
-
-    #if !defined(__ANDROID__)
-  shm_fd = shm_open(shm_file_path, O_RDWR, DEFAULT_PERMISSION);
-  if (shm_fd == -1) { FATAL("shm_open() failed\n"); }
-    #else
-  shm_fd = open("/dev/ashmem", O_RDWR);
-  if (shm_fd == -1) { FATAL("open() failed\n"); }
-  if (ioctl(shm_fd, ASHMEM_SET_NAME, shm_file_path) == -1) {
-
-    FATAL("ioctl(ASHMEM_SET_NAME) failed");
-
-  }
-
-  if (ioctl(shm_fd, ASHMEM_SET_SIZE, __afl_map_size) == -1) {
-
-    FATAL("ioctl(ASHMEM_SET_SIZE) failed");
-
-  }
-
-    #endif
-
-  __afl_area_ptr = mmap(address, __afl_map_size, PROT_READ | PROT_WRITE,
-                        MAP_FIXED_NOREPLACE | MAP_SHARED, shm_fd, 0);
-  if (__afl_area_ptr != address) {
-
-    FATAL("Failed to map mmap __afl_area_ptr: %d", errno);
-
-  }
-
-  if (close(shm_fd) != 0) { FATAL("Failed to close shm_fd"); }
-
-}
-
-static void instrument_coverage_optimize_map_shm(guint64  shm_env_val,
-                                                 gpointer address) {
-
-  if (shmdt(__afl_area_ptr) != 0) {
-
-    FATAL("Failed to detach previous __afl_area_ptr");
-
-  }
-
-  __afl_area_ptr = shmat(shm_env_val, address, 0);
-  if (__afl_area_ptr != address) {
-
-    FATAL("Failed to map shm __afl_area_ptr: %d", errno);
-
-  }
-
-}
-
-void instrument_coverage_optimize_init(void) {
-
-  gpointer low_address = NULL;
-
-  gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, instrument_coverage_find_low,
-                               &low_address);
-
-  FVERBOSE("Low address: %p", low_address);
-
-  if (low_address == 0 ||
-      GPOINTER_TO_SIZE(low_address) > ((2UL << 30) - __afl_map_size)) {
-
-    FATAL("Invalid low_address: %p", low_address);
-
-  }
-
-  ranges_print_debug_maps();
-
-  char *shm_env = getenv(SHM_ENV_VAR);
-  FVERBOSE("SHM_ENV_VAR: %s", shm_env);
-
-  if (shm_env == NULL) {
-
-    FWARNF("SHM_ENV_VAR not set, using anonymous map for debugging purposes");
-
-    instrument_coverage_optimize_map_mmap_anon(low_address);
-
-  } else {
-
-    guint64 shm_env_val = g_ascii_strtoull(shm_env, NULL, 10);
-
-    if (shm_env_val == 0) {
-
-      instrument_coverage_optimize_map_mmap(shm_env, low_address);
-
-    } else {
-
-      instrument_coverage_optimize_map_shm(shm_env_val, low_address);
-
-    }
-
-  }
-
   FVERBOSE("__afl_area_ptr: %p", __afl_area_ptr);
 
 }
 
-  #endif
-
 static void instrument_coverage_switch(GumStalkerObserver *self,
                                        gpointer            start_address,
                                        const cs_insn *     from_insn,
@@ -398,10 +202,17 @@ static void instrument_coverage_switch(GumStalkerObserver *self,
 
       }
 
-      if (op[0].type != X86_OP_IMM) { return; }
+      if (op[0].type != X86_OP_IMM) {
+
+        instrument_cache_insert(start_address, *target);
+        return;
+
+      }
 
       break;
     case X86_INS_RET:
+      instrument_cache_insert(start_address,
+                              (guint8 *)*target + sizeof(afl_log_code));
       break;
     default:
       return;
@@ -431,48 +242,19 @@ static void instrument_coverage_suppress_init(void) {
 
 }
 
-void instrument_coverage_optimize(const cs_insn *   instr,
-                                  GumStalkerOutput *output) {
+static void instrument_coverage_write(GumAddress        address,
+                                      GumStalkerOutput *output) {
 
   afl_log_code  code = {0};
   GumX86Writer *cw = output->writer.x86;
-  guint64 area_offset = instrument_get_offset_hash(GUM_ADDRESS(instr->address));
-  gsize   map_size_pow2;
-  gsize   area_offset_ror;
-  GumAddress code_addr = 0;
-  if (instrument_previous_pc_addr == NULL) {
-
-    GumAddressSpec spec = {.near_address = cw->code,
-                           .max_distance = 1ULL << 30};
-
-    instrument_previous_pc_addr = gum_memory_allocate_near(
-        &spec, sizeof(guint64), 0x1000, GUM_PAGE_READ | GUM_PAGE_WRITE);
-    *instrument_previous_pc_addr = instrument_hash_zero;
-    FVERBOSE("instrument_previous_pc_addr: %p", instrument_previous_pc_addr);
-    FVERBOSE("code_addr: %p", cw->code);
-
-  }
-
-  instrument_coverage_suppress_init();
-
-  // gum_x86_writer_put_breakpoint(cw);
-  code_addr = cw->pc;
-  if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
-
-    FATAL("Failed - g_hash_table_add");
-
-  }
+  guint64       area_offset = instrument_get_offset_hash(address);
+  gsize         map_size_pow2;
+  gsize         area_offset_ror;
+  GumAddress    code_addr = cw->pc;
 
   code.code = template;
 
-  gssize curr_loc_shr_1_offset =
-      offsetof(afl_log_code, code.mov_prev_loc_curr_loc_shr1) +
-      sizeof(code.code.mov_prev_loc_curr_loc_shr1) - sizeof(guint32);
-
-  map_size_pow2 = util_log2(__afl_map_size);
-  area_offset_ror = util_rotate(area_offset, 1, map_size_pow2);
-
-  *((guint32 *)&code.bytes[curr_loc_shr_1_offset]) = (guint32)(area_offset_ror);
+  /* mov_prev_loc_curr_loc_shr1 */
 
   gssize prev_loc_value =
       GPOINTER_TO_SIZE(instrument_previous_pc_addr) -
@@ -490,6 +272,8 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
   *((gint *)&code.bytes[prev_loc_value_offset]) = (gint)prev_loc_value;
 
+  /* mov_eax_prev_loc */
+
   gssize prev_loc_value2 =
       GPOINTER_TO_SIZE(instrument_previous_pc_addr) -
       (code_addr + offsetof(afl_log_code, code.mov_eax_prev_loc) +
@@ -505,40 +289,172 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
   *((gint *)&code.bytes[prev_loc_value_offset2]) = (gint)prev_loc_value2;
 
-  #ifdef __APPLE__
+  /* xor_eax_curr_loc */
 
-  gssize xor_curr_loc_offset = offsetof(afl_log_code, code.leax_eax_curr_loc) +
-                               sizeof(code.code.leax_eax_curr_loc) -
+  gssize xor_curr_loc_offset = offsetof(afl_log_code, code.xor_eax_curr_loc) +
+                               sizeof(code.code.xor_eax_curr_loc) -
                                sizeof(guint32);
 
-  gssize xor_curr_loc_value =
-      ((GPOINTER_TO_SIZE(__afl_area_ptr) | area_offset) -
-       (code_addr + offsetof(afl_log_code, code.mov_eax_prev_loc) +
-        sizeof(code.code.mov_eax_prev_loc)));
+  *((guint32 *)&code.bytes[xor_curr_loc_offset]) = area_offset;
+
+  /* lea_rbx_area_ptr */
+
+  gssize lea_rbx_area_ptr_offset =
+      offsetof(afl_log_code, code.lea_rbx_area_ptr) +
+      sizeof(code.code.lea_rbx_area_ptr) - sizeof(guint32);
 
-  if (!instrument_coverage_in_range(xor_curr_loc_value)) {
+  gssize lea_rbx_area_ptr_value =
+      (GPOINTER_TO_SIZE(__afl_area_ptr) -
+       (code_addr + offsetof(afl_log_code, code.lea_rbx_area_ptr) +
+        sizeof(code.code.lea_rbx_area_ptr)));
 
-    FATAL("Patch out of range (xor_curr_loc_value): 0x%016lX",
-          xor_curr_loc_value);
+  if (!instrument_coverage_in_range(lea_rbx_area_ptr_value)) {
+
+    FATAL("Patch out of range (lea_rbx_area_ptr_value): 0x%016lX",
+          lea_rbx_area_ptr_value);
 
   }
 
-  *((guint32 *)&code.bytes[xor_curr_loc_offset]) = xor_curr_loc_value;
+  *((guint32 *)&code.bytes[lea_rbx_area_ptr_offset]) = lea_rbx_area_ptr_value;
 
-  #else
+  /* mov_prev_loc_curr_loc_shr1 */
 
-  gssize xor_curr_loc_offset = offsetof(afl_log_code, code.xor_eax_curr_loc) +
-                               sizeof(code.code.xor_eax_curr_loc) -
-                               sizeof(guint32);
+  gssize curr_loc_shr_1_offset =
+      offsetof(afl_log_code, code.mov_prev_loc_curr_loc_shr1) +
+      sizeof(code.code.mov_prev_loc_curr_loc_shr1) - sizeof(guint32);
 
-  *((guint32 *)&code.bytes[xor_curr_loc_offset]) =
-      (guint32)(GPOINTER_TO_SIZE(__afl_area_ptr) | area_offset);
-  #endif
+  map_size_pow2 = util_log2(__afl_map_size);
+  area_offset_ror = util_rotate(area_offset, 1, map_size_pow2);
+
+  *((guint32 *)&code.bytes[curr_loc_shr_1_offset]) = (guint32)(area_offset_ror);
 
   gum_x86_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
 
 }
 
+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)); */
+  if (instrument_previous_pc_addr == NULL) {
+
+    GumAddressSpec spec = {.near_address = cw->code,
+                           .max_distance = 1ULL << 30};
+
+    instrument_previous_pc_addr = gum_memory_allocate_near(
+        &spec, sizeof(guint64), 0x1000, GUM_PAGE_READ | GUM_PAGE_WRITE);
+    *instrument_previous_pc_addr = instrument_hash_zero;
+    FVERBOSE("instrument_previous_pc_addr: %p", instrument_previous_pc_addr);
+    FVERBOSE("code_addr: %p", cw->code);
+
+  }
+
+  instrument_coverage_suppress_init();
+
+  if (!g_hash_table_add(coverage_blocks, GSIZE_TO_POINTER(cw->code))) {
+
+    FATAL("Failed - g_hash_table_add");
+
+  }
+
+  instrument_coverage_write(GUM_ADDRESS(instr->address), output);
+
+}
+
+void instrument_coverage_optimize_insn(const cs_insn *   instr,
+                                       GumStalkerOutput *output) {
+
+  GumX86Writer *cw = output->writer.x86;
+  jcc_insn      taken, not_taken;
+
+  switch (instr->id) {
+
+    case X86_INS_CMOVA:
+      taken.opcode = OPC_JA;
+      not_taken.opcode = OPC_JBE;
+      break;
+    case X86_INS_CMOVAE:
+      taken.opcode = OPC_JAE;
+      not_taken.opcode = OPC_JB;
+      break;
+    case X86_INS_CMOVB:
+      taken.opcode = OPC_JB;
+      not_taken.opcode = OPC_JAE;
+      break;
+    case X86_INS_CMOVBE:
+      taken.opcode = OPC_JBE;
+      not_taken.opcode = OPC_JA;
+      break;
+    case X86_INS_CMOVE:
+      taken.opcode = OPC_JE;
+      not_taken.opcode = OPC_JNE;
+      break;
+    case X86_INS_CMOVG:
+      taken.opcode = OPC_JG;
+      not_taken.opcode = OPC_JLE;
+      break;
+    case X86_INS_CMOVGE:
+      taken.opcode = OPC_JGE;
+      not_taken.opcode = OPC_JL;
+      break;
+    case X86_INS_CMOVL:
+      taken.opcode = OPC_JL;
+      not_taken.opcode = OPC_JGE;
+      break;
+    case X86_INS_CMOVLE:
+      taken.opcode = OPC_JLE;
+      not_taken.opcode = OPC_JG;
+      break;
+    case X86_INS_CMOVNE:
+      taken.opcode = OPC_JNE;
+      not_taken.opcode = OPC_JE;
+      break;
+    case X86_INS_CMOVNO:
+      taken.opcode = OPC_JNO;
+      not_taken.opcode = OPC_JO;
+      break;
+    case X86_INS_CMOVNP:
+      taken.opcode = OPC_JNP;
+      not_taken.opcode = OPC_JP;
+      break;
+    case X86_INS_CMOVNS:
+      taken.opcode = OPC_JNS;
+      not_taken.opcode = OPC_JS;
+      break;
+    case X86_INS_CMOVO:
+      taken.opcode = OPC_JO;
+      not_taken.opcode = OPC_JNO;
+      break;
+    case X86_INS_CMOVP:
+      taken.opcode = OPC_JP;
+      not_taken.opcode = OPC_JNP;
+      break;
+    case X86_INS_CMOVS:
+      taken.opcode = OPC_JS;
+      not_taken.opcode = OPC_JNS;
+      break;
+    default:
+      return;
+
+  }
+
+  taken.distance = sizeof(afl_log_code);
+  not_taken.distance = sizeof(afl_log_code);
+
+  // gum_x86_writer_put_breakpoint(cw);
+
+  gum_x86_writer_put_bytes(cw, taken.bytes, sizeof(jcc_insn));
+  instrument_coverage_write(GUM_ADDRESS(instr->address), output);
+
+  gum_x86_writer_put_bytes(cw, not_taken.bytes, sizeof(jcc_insn));
+  instrument_coverage_write(GUM_ADDRESS(instr->address + instr->size), output);
+
+  FVERBOSE("Instrument - 0x%016lx: %s %s", instr->address, instr->mnemonic,
+           instr->op_str);
+
+}
+
 void instrument_flush(GumStalkerOutput *output) {
 
   gum_x86_writer_flush(output->writer.x86);
diff --git a/frida_mode/src/instrument/instrument_x64_cache.c b/frida_mode/src/instrument/instrument_x64_cache.c
new file mode 100644
index 00000000..3ea4421a
--- /dev/null
+++ b/frida_mode/src/instrument/instrument_x64_cache.c
@@ -0,0 +1,435 @@
+#include <sys/mman.h>
+#include <sys/resource.h>
+
+#include "instrument.h"
+#include "util.h"
+
+#if defined(__x86_64__)
+
+  #define INVALID 1
+  #define DEFAULT_CACHE_SIZE (256ULL << 20)
+
+gboolean         instrument_cache_enabled = TRUE;
+gsize            instrument_cache_size = DEFAULT_CACHE_SIZE;
+static gpointer *map_base = MAP_FAILED;
+
+void instrument_cache_config(void) {
+
+  instrument_cache_enabled = (getenv("AFL_FRIDA_INST_NO_CACHE") == NULL);
+
+  if (getenv("AFL_FRIDA_INST_CACHE_SIZE") != NULL) {
+
+    if (!instrument_cache_enabled) {
+
+      FFATAL(
+          "AFL_FRIDA_INST_CACHE_SIZE incomatible with "
+          "AFL_FRIDA_INST_NO_CACHE");
+
+    }
+
+    instrument_cache_size =
+        util_read_address("AFL_FRIDA_INST_CACHE_SIZE", DEFAULT_CACHE_SIZE);
+    util_log2(instrument_cache_size);
+
+  }
+
+}
+
+void instrument_cache_init(void) {
+
+  FOKF(cBLU "Instrumentation" cRST " - " cGRN "cache:" cYEL " [%c]",
+       instrument_cache_enabled ? 'X' : ' ');
+  if (!instrument_cache_enabled) { return; }
+
+  FOKF(cBLU "Instrumentation" cRST " - " cGRN "cache size:" cYEL " [0x%016lX]",
+       instrument_cache_size);
+
+  const struct rlimit data_limit = {.rlim_cur = RLIM_INFINITY,
+                                    .rlim_max = RLIM_INFINITY};
+
+  if (setrlimit(RLIMIT_AS, &data_limit) != 0) {
+
+    FFATAL("Failed to setrlimit: %d", errno);
+
+  }
+
+  map_base =
+      gum_memory_allocate(NULL, instrument_cache_size, instrument_cache_size,
+                          GUM_PAGE_READ | GUM_PAGE_WRITE);
+  if (map_base == MAP_FAILED) { FFATAL("Failed to map segment: %d", errno); }
+
+  FOKF(cBLU "Instrumentation" cRST " - " cGRN "cache addr:" cYEL " [0x%016lX]",
+       GUM_ADDRESS(map_base));
+
+}
+
+static gpointer *instrument_cache_get_addr(gpointer addr) {
+
+  gsize mask = (instrument_cache_size / sizeof(gpointer)) - 1;
+  return &map_base[GPOINTER_TO_SIZE(addr) & mask];
+
+}
+
+void instrument_cache_insert(gpointer real_address, gpointer code_address) {
+
+  if (!instrument_cache_enabled) { return; }
+
+  gpointer *target = instrument_cache_get_addr(real_address);
+  if (*target == code_address) {
+
+    return;
+
+  } else if (*target == NULL) {
+
+    *target = code_address;
+
+  } else {
+
+    *target = GSIZE_TO_POINTER(INVALID);
+
+  }
+
+}
+
+static gboolean instrument_cache_relocate(GumAddress old_pc, GumAddress new_pc,
+                                          gint32  old_offset,
+                                          gint32 *new_offset) {
+
+  guint64 old_target = old_pc + old_offset;
+  gint64  relocated = old_target - new_pc;
+
+  if (relocated > G_MAXINT32 || relocated < G_MININT32) { return FALSE; }
+
+  *new_offset = relocated;
+  return TRUE;
+
+}
+
+static void instrument_cache_rewrite_branch_insn(const cs_insn *   instr,
+                                                 GumStalkerOutput *output) {
+
+  GumX86Writer *cw = output->writer.x86;
+  cs_x86 *      x86 = &instr->detail->x86;
+  guint8        modified[sizeof(instr->bytes)] = {0};
+  guint8        offset = 0;
+  guint8        skip = 0;
+
+  g_assert(sizeof(x86->prefix) == 4);
+  g_assert(sizeof(x86->opcode) == 4);
+
+  /*
+   * If the target is simply RAX, we can skip writing the code to load the
+   * RIP
+   */
+  if (x86->operands[0].type == X86_OP_REG ||
+      x86->operands[0].reg == X86_REG_RAX) {
+
+    return;
+
+  }
+
+  /* Write the prefix */
+  for (gsize i = 0; i < sizeof(x86->prefix); i++) {
+
+    if (x86->prefix[i] != 0) {
+
+      if (x86->prefix[i] == 0xf2) {
+
+        skip++;
+
+      } else {
+
+        modified[offset++] = x86->prefix[i];
+        skip++;
+
+      }
+
+    }
+
+  }
+
+  /* Write the REX */
+  if (x86->rex == 0) {
+
+    /*
+     * CALL (near) and JMP (near) default to 64-bit operands, MOV does not,
+     * write REX.W
+     */
+    modified[offset++] = 0x48;
+
+  } else {
+
+    if ((x86->rex & 0xF8) != 0x40) {
+
+      FATAL("Unexpected REX byte: 0x%02x", x86->rex);
+
+    }
+
+    modified[offset++] = x86->rex | 0x08;
+    skip++;
+
+  }
+
+  /*
+   * CALL is FF /2, JMP is FF /4. The remaining op-code fields should thus be
+   * unused
+   */
+
+  if (x86->opcode[0] != 0xFF || x86->opcode[1] != 0x00 ||
+      x86->opcode[2] != 0x00 || x86->opcode[3] != 0x00) {
+
+    FFATAL("Unexpected Op-code: 0x%02x 0x%02x 0x%02x 0x%02x", x86->opcode[0],
+           x86->opcode[1], x86->opcode[2], x86->opcode[3]);
+
+  }
+
+  /* The reg field of the ModRM should be set to 2 for CALL and 4 for JMP */
+  guint8 reg = (x86->modrm >> 3) & 7;
+  if (reg != 0x4 && reg != 0x2) {
+
+    FFATAL("Unexpected Reg: 0x%02x, ModRM: 0x%02x", reg, x86->modrm);
+
+  }
+
+  /* MOV */
+  modified[offset++] = 0x8b;
+  skip++;
+
+  /* Clear the reg field (RAX) */
+  modified[offset++] = x86->modrm & 0xc7;
+  skip++;
+
+  /* Operands */
+  guint8 op_len = instr->size - skip;
+
+  /* If our branch was RIP relative, we'll need to fix-up the offset */
+  if (x86->operands[0].type == X86_OP_MEM &&
+      x86->operands[0].mem.base == X86_REG_RIP) {
+
+    /* RIP relative offsets should be 32-bits */
+    if (op_len != sizeof(gint32)) {
+
+      FFATAL("Unexpected operand length: %d\n", op_len);
+
+    }
+
+    gint32 old_offset = *(gint32 *)&instr->bytes[skip];
+    gint32 new_offset = 0;
+    if (instrument_cache_relocate(instr->address, cw->pc, old_offset,
+                                  &new_offset)) {
+
+      gint32 *output = (gint32 *)&modified[offset];
+      *output = new_offset;
+      offset += sizeof(gint32);
+
+    } else {
+
+      GumAddress target = instr->address + old_offset;
+      gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, target);
+      gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_RAX, GUM_REG_RAX);
+      return;
+
+    }
+
+  } else {
+
+    for (int i = 0; i < op_len; i++) {
+
+      guint8 val = instr->bytes[i + skip];
+      modified[offset++] = val;
+
+    }
+
+  }
+
+  gum_x86_writer_put_bytes(cw, modified, offset);
+
+}
+
+static void instrument_cache_write_push_frame(GumX86Writer *cw) {
+
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(
+      cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))),
+      GUM_REG_XAX);
+  gum_x86_writer_put_lahf(cw);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(
+      cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (2 * sizeof(gpointer))),
+      GUM_REG_XAX);
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(
+      cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (3 * sizeof(gpointer))),
+      GUM_REG_XBX);
+
+}
+
+static void instrument_cache_write_pop_frame(GumX86Writer *cw) {
+
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(
+      cw, GUM_REG_XBX, GUM_REG_XSP,
+      -(GUM_RED_ZONE_SIZE + (3 * sizeof(gpointer))));
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(
+      cw, GUM_REG_XAX, GUM_REG_XSP,
+      -(GUM_RED_ZONE_SIZE + (2 * sizeof(gpointer))));
+  gum_x86_writer_put_sahf(cw);
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(
+      cw, GUM_REG_XAX, GUM_REG_XSP,
+      -(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))));
+
+}
+
+static void instrument_cache_write_lookup(GumX86Writer *cw) {
+
+  /* &map_base[GPOINTER_TO_SIZE(addr) & MAP_MASK]; */
+
+  gsize mask = (instrument_cache_size / sizeof(gpointer)) - 1;
+  gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_XBX, mask);
+  gum_x86_writer_put_and_reg_reg(cw, GUM_REG_XAX, GUM_REG_XBX);
+  gum_x86_writer_put_shl_reg_u8(cw, GUM_REG_XAX, util_log2(sizeof(gpointer)));
+  gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_XBX, GPOINTER_TO_SIZE(map_base));
+  gum_x86_writer_put_add_reg_reg(cw, GUM_REG_XAX, GUM_REG_XBX);
+
+  /* Read the return address lookup */
+  gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_XAX, GUM_REG_XAX);
+
+}
+
+void instrument_cache_jmp_call(const cs_insn *instr, GumStalkerOutput *output) {
+
+  GumX86Writer *cw = output->writer.x86;
+  cs_x86 *      x86 = &instr->detail->x86;
+
+  if (x86->op_count != 1) { FFATAL("Unexpected operand count"); }
+
+  if (x86->operands[0].type == X86_OP_IMM) { return; }
+
+  gconstpointer null = cw->code;
+
+  instrument_cache_write_push_frame(cw);
+
+  /*
+   * We are about to re-write the CALL or JMP instruction, but replace the
+   * op-code with that for a MOV into RAX. Since we are keeping the operand from
+   * the JMP exactly the same, it is imperative that the target register state
+   * be exactly the same as how the target left it. Since `LAHF` spoils `RAX` we
+   * must restore it from the stack. We also must avoid adjusting `RSP`, so we
+   * use `MOV` instructions to store our context into the stack beyond the
+   * red-zone.
+   */
+  gum_x86_writer_put_mov_reg_reg_offset_ptr(
+      cw, GUM_REG_XAX, GUM_REG_XSP,
+      -(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))));
+
+  instrument_cache_rewrite_branch_insn(instr, output);
+
+  instrument_cache_write_lookup(cw);
+
+  /* Test if its set*/
+  gum_x86_writer_put_cmp_reg_i32(cw, GUM_REG_XAX, INVALID);
+  gum_x86_writer_put_jcc_short_label(cw, X86_INS_JLE, null, GUM_UNLIKELY);
+
+  /* If it's set, then stash the address beyond the red-zone */
+  gum_x86_writer_put_mov_reg_offset_ptr_reg(
+      cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (4 * sizeof(gpointer))),
+      GUM_REG_XAX);
+
+  if (instr->id == X86_INS_JMP) {
+
+    instrument_cache_write_pop_frame(cw);
+    gum_x86_writer_put_jmp_reg_offset_ptr(
+        cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (4 * sizeof(gpointer))));
+
+  } else {
+
+    gum_x86_writer_put_mov_reg_address(
+        cw, GUM_REG_XAX, GUM_ADDRESS(instr->address + instr->size));
+    gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_XSP,
+                                              -sizeof(gpointer), GUM_REG_XAX);
+
+    instrument_cache_write_pop_frame(cw);
+
+    gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_XSP, GUM_REG_XSP,
+                                          -sizeof(gpointer));
+    gum_x86_writer_put_jmp_reg_offset_ptr(
+        cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + ((4 - 1) * sizeof(gpointer))));
+
+  }
+
+  /* Tidy up our mess and let FRIDA handle it */
+  gum_x86_writer_put_label(cw, null);
+  instrument_cache_write_pop_frame(cw);
+
+}
+
+void instrument_cache_ret(const cs_insn *instr, GumStalkerOutput *output) {
+
+  GumX86Writer *cw = output->writer.x86;
+  cs_x86 *      x86 = &instr->detail->x86;
+  guint16       n = 0;
+
+  if (x86->op_count != 0) {
+
+    if (x86->operands[0].type != X86_OP_IMM) {
+
+      FFATAL("Unexpected operand type");
+
+    }
+
+    n = x86->operands[0].imm;
+
+  }
+
+  gconstpointer null = cw->code;
+
+  instrument_cache_write_push_frame(cw);
+
+  gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_XAX, GUM_REG_XSP);
+
+  instrument_cache_write_lookup(cw);
+
+  /* Test if its set*/
+  gum_x86_writer_put_cmp_reg_i32(cw, GUM_REG_XAX, INVALID);
+  gum_x86_writer_put_jcc_short_label(cw, X86_INS_JLE, null, GUM_UNLIKELY);
+
+  /* If it's set, then overwrite our return address and return */
+  gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_XSP, GUM_REG_XAX);
+  instrument_cache_write_pop_frame(cw);
+
+  if (n == 0) {
+
+    gum_x86_writer_put_ret(cw);
+
+  } else {
+
+    gum_x86_writer_put_ret_imm(cw, n);
+
+  }
+
+  /* Tidy up our mess and let FRIDA handle it */
+  gum_x86_writer_put_label(cw, null);
+  instrument_cache_write_pop_frame(cw);
+
+}
+
+void instrument_cache(const cs_insn *instr, GumStalkerOutput *output) {
+
+  if (!instrument_cache_enabled) { return; }
+
+  switch (instr->id) {
+
+    case X86_INS_RET:
+      instrument_cache_ret(instr, output);
+      break;
+
+    case X86_INS_CALL:
+    case X86_INS_JMP:
+      instrument_cache_jmp_call(instr, output);
+      break;
+
+    default:
+      return;
+
+  }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c
index c4e93324..6a899248 100644
--- a/frida_mode/src/instrument/instrument_x86.c
+++ b/frida_mode/src/instrument/instrument_x86.c
@@ -6,6 +6,9 @@
 
 #if defined(__i386__)
 
+gboolean instrument_cache_enabled = FALSE;
+gsize    instrument_cache_size = 0;
+
 static GHashTable *coverage_blocks = NULL;
 
   #pragma pack(push, 1)
@@ -218,6 +221,14 @@ void instrument_coverage_optimize(const cs_insn *   instr,
 
 }
 
+void instrument_coverage_optimize_insn(const cs_insn *   instr,
+                                       GumStalkerOutput *output) {
+
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(output);
+
+}
+
 void instrument_coverage_optimize_init(void) {
 
 }
@@ -234,5 +245,27 @@ gpointer instrument_cur(GumStalkerOutput *output) {
 
 }
 
+void instrument_cache_config(void) {
+
+}
+
+void instrument_cache_init(void) {
+
+}
+
+void instrument_cache_insert(gpointer real_address, gpointer code_address) {
+
+  UNUSED_PARAMETER(real_address);
+  UNUSED_PARAMETER(code_address);
+
+}
+
+void instrument_cache(const cs_insn *instr, GumStalkerOutput *output) {
+
+  UNUSED_PARAMETER(instr);
+  UNUSED_PARAMETER(output);
+
+}
+
 #endif
 
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
index 52e9e45c..c1c9d36f 100644
--- a/frida_mode/src/js/api.js
+++ b/frida_mode/src/js/api.js
@@ -69,6 +69,12 @@ class Afl {
         Afl.jsApiSetBackpatchDisable();
     }
     /**
+     * See `AFL_FRIDA_INST_NO_CACHE`.
+     */
+    static setCacheDisable() {
+        Afl.jsApiSetCacheDisable();
+    }
+    /**
      * See `AFL_FRIDA_DEBUG_MAPS`.
      */
     static setDebugMaps() {
@@ -92,6 +98,13 @@ class Afl {
         Afl.jsApiAflSharedMemFuzzing.writeInt(1);
     }
     /**
+     * See `AFL_FRIDA_INST_CACHE_SIZE`. This function takes a single `number`
+     * as an argument.
+     */
+    static setInstrumentCacheSize(size) {
+        Afl.jsApiSetInstrumentCacheSize(size);
+    }
+    /**
      * See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string`
      * as an argument.
      */
@@ -114,6 +127,12 @@ class Afl {
         Afl.jsApiSetInstrumentTrace();
     }
     /**
+     * See `AFL_FRIDA_INST_INSN`
+     */
+    static setInstrumentInstructions() {
+        Afl.jsApiSetInstrumentInstructions();
+    }
+    /**
      * See `AFL_FRIDA_INST_JIT`.
      */
     static setInstrumentJit() {
@@ -293,10 +312,13 @@ Afl.jsApiAflSharedMemFuzzing = Afl.jsApiGetSymbol("__afl_sharedmem_fuzzing");
 Afl.jsApiDone = Afl.jsApiGetFunction("js_api_done", "void", []);
 Afl.jsApiError = Afl.jsApiGetFunction("js_api_error", "void", ["pointer"]);
 Afl.jsApiSetBackpatchDisable = Afl.jsApiGetFunction("js_api_set_backpatch_disable", "void", []);
+Afl.jsApiSetCacheDisable = Afl.jsApiGetFunction("js_api_set_cache_disable", "void", []);
 Afl.jsApiSetDebugMaps = Afl.jsApiGetFunction("js_api_set_debug_maps", "void", []);
 Afl.jsApiSetEntryPoint = Afl.jsApiGetFunction("js_api_set_entrypoint", "void", ["pointer"]);
+Afl.jsApiSetInstrumentCacheSize = Afl.jsApiGetFunction("js_api_set_instrument_cache_size", "void", ["size_t"]);
 Afl.jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_coverage_file", "void", ["pointer"]);
 Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_debug_file", "void", ["pointer"]);
+Afl.jsApiSetInstrumentInstructions = Afl.jsApiGetFunction("js_api_set_instrument_instructions", "void", []);
 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", []);
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
index 94ec8842..7cc8ffc7 100644
--- a/frida_mode/src/js/js_api.c
+++ b/frida_mode/src/js/js_api.c
@@ -142,6 +142,13 @@ js_api_set_prefetch_backpatch_disable(void) {
 
 }
 
+__attribute__((visibility("default"))) void js_api_set_instrument_instructions(
+    void) {
+
+  instrument_coverage_insn = TRUE;
+
+}
+
 __attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
     void) {
 
@@ -255,6 +262,19 @@ __attribute__((visibility("default"))) void js_api_set_stalker_adjacent_blocks(
 
 }
 
+__attribute__((visibility("default"))) void js_api_set_cache_disable(void) {
+
+  instrument_cache_enabled = FALSE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_instrument_cache_size(
+    gsize size) {
+
+  instrument_cache_size = size;
+
+}
+
 __attribute__((visibility("default"))) void js_api_set_js_main_hook(
     const js_main_hook_t hook) {
 
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index d8521300..bb6e4109 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -21,6 +21,7 @@
 #include "intercept.h"
 #include "js.h"
 #include "lib.h"
+#include "module.h"
 #include "output.h"
 #include "persistent.h"
 #include "prefetch.h"
@@ -197,6 +198,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
   instrument_config();
   js_config();
   lib_config();
+  module_config();
   output_config();
   persistent_config();
   prefetch_config();
@@ -214,6 +216,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
   entry_init();
   instrument_init();
   lib_init();
+  module_init();
   persistent_init();
   prefetch_init();
   seccomp_init();
diff --git a/frida_mode/src/module.c b/frida_mode/src/module.c
new file mode 100644
index 00000000..65b394cd
--- /dev/null
+++ b/frida_mode/src/module.c
@@ -0,0 +1,106 @@
+#include "intercept.h"
+#include "module.h"
+#include "util.h"
+
+#if defined(__linux__)
+  #include <dlfcn.h>
+  #include <link.h>
+#endif
+
+static guint    page_size = 0;
+static gboolean handle_dlclose = FALSE;
+
+void module_config(void) {
+
+#if defined(__linux__)
+  handle_dlclose = (getenv("AFL_FRIDA_NO_MODULE") == NULL);
+#else
+  FWARNF("AFL_FRIDA_MODULE not supported");
+#endif
+
+}
+
+typedef struct {
+
+  GumMemoryRange    range;
+  GumPageProtection protection;
+  GumFileMapping    file;
+
+} gum_range_t;
+
+gboolean found_range(const GumRangeDetails *details, gpointer user_data) {
+
+  gum_range_t range = {0};
+  GArray *    ranges = (GArray *)user_data;
+
+  range.range = *details->range;
+  range.protection = details->protection;
+  if (details->file != NULL) { range.file = *details->file; }
+
+  g_array_append_val(ranges, range);
+  return FALSE;
+
+}
+
+#if defined(__linux__)
+static int on_dlclose(void *handle) {
+
+  GArray *         ranges = NULL;
+  struct link_map *lm = NULL;
+  gum_range_t *    range = NULL;
+  GumAddress       base;
+  GumAddress       limit;
+  gpointer         mem;
+
+  if (dlinfo(handle, RTLD_DI_LINKMAP, &lm) < 0) {
+
+    FFATAL("Failed to dlinfo: %s", dlerror());
+
+  }
+
+  FVERBOSE("on_dlclose: %s", lm->l_name);
+
+  ranges = g_array_new(FALSE, TRUE, sizeof(gum_range_t));
+  gum_module_enumerate_ranges(lm->l_name, GUM_PAGE_EXECUTE, found_range,
+                              ranges);
+
+  int ret = dlclose(handle);
+  if (ret != 0) {
+
+    FWARNF("dlclose returned: %d (%s)", ret, dlerror());
+    return ret;
+
+  }
+
+  for (guint i = 0; i < ranges->len; i++) {
+
+    range = &g_array_index(ranges, gum_range_t, i);
+    base = range->range.base_address;
+    limit = base + range->range.size;
+    FVERBOSE("Reserving range: 0x%016lx, 0x%016lX", base, limit);
+    mem = gum_memory_allocate(GSIZE_TO_POINTER(base), range->range.size,
+                              page_size, GUM_PAGE_NO_ACCESS);
+    if (mem == NULL) { FATAL("Failed to allocate %p (%d)", mem, errno); }
+
+  }
+
+  g_array_free(ranges, TRUE);
+  return 0;
+
+}
+
+#endif
+
+void module_init(void) {
+
+  FOKF(cBLU "Module" cRST " - " cYEL " [%c]", handle_dlclose ? 'X' : ' ');
+
+#if defined(__linux__)
+  if (!handle_dlclose) { return; }
+
+  page_size = gum_query_page_size();
+  intercept_hook(dlclose, on_dlclose, NULL);
+#endif
+
+}
+
diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c
index 84803453..d47d1c14 100644
--- a/frida_mode/src/ranges.c
+++ b/frida_mode/src/ranges.c
@@ -595,8 +595,6 @@ void ranges_init(void) {
        ranges_inst_jit ? 'X' : ' ');
   FOKF(cBLU "Ranges" cRST " - " cGRN "instrument libraries:" cYEL " [%c]",
        ranges_inst_libs ? 'X' : ' ');
-  FOKF(cBLU "Ranges" cRST " - " cGRN "instrument libraries:" cYEL " [%c]",
-       ranges_inst_libs ? 'X' : ' ');
 
   print_ranges("include", include_ranges);
   print_ranges("exclude", exclude_ranges);