about summary refs log tree commit diff
path: root/frida_mode
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode')
-rw-r--r--frida_mode/README.md5
-rw-r--r--frida_mode/frida.map1
-rw-r--r--frida_mode/include/stalker.h1
-rw-r--r--frida_mode/src/entry.c2
-rw-r--r--frida_mode/src/instrument/instrument_coverage.c56
-rw-r--r--frida_mode/src/instrument/instrument_x64.c99
-rw-r--r--frida_mode/src/js/api.js7
-rw-r--r--frida_mode/src/js/js_api.c15
-rw-r--r--frida_mode/src/stalker.c18
-rw-r--r--frida_mode/test/png/persistent/hook/GNUmakefile1
-rw-r--r--frida_mode/test/unstable/GNUmakefile14
-rw-r--r--frida_mode/ts/lib/afl.ts12
12 files changed, 141 insertions, 90 deletions
diff --git a/frida_mode/README.md b/frida_mode/README.md
index 8211224d..a75324d5 100644
--- a/frida_mode/README.md
+++ b/frida_mode/README.md
@@ -215,6 +215,11 @@ gdb \
 ```
 * `AFL_FRIDA_SECCOMP_FILE` - Write a log of any syscalls made by the target to
 the specified file.
+* `AFL_FRIDA_STALKER_ADJACENT_BLOCKS` - Configure the number of adjacent blocks
+ to fetch when generating instrumented code. By fetching blocks in the same
+ order they appear in the original program, rather than the order of execution
+ should help reduce locallity and adjacency. This includes allowing us to vector
+ between adjancent blocks using a NOP slide rather than an immediate branch.
 * `AFL_FRIDA_STALKER_IC_ENTRIES` - Configure the number of inline cache entries
 stored along-side branch instructions which provide a cache to avoid having to
 call back into FRIDA to find the next block. Default is 32.
diff --git a/frida_mode/frida.map b/frida_mode/frida.map
index 7be41aa0..61eb19ee 100644
--- a/frida_mode/frida.map
+++ b/frida_mode/frida.map
@@ -29,6 +29,7 @@
     js_api_set_prefetch_disable;
     js_api_set_seccomp_file;
     js_api_set_stalker_callback;
+    js_api_set_stalker_adjacent_blocks;
     js_api_set_stalker_ic_entries;
     js_api_set_stats_file;
     js_api_set_stats_interval;
diff --git a/frida_mode/include/stalker.h b/frida_mode/include/stalker.h
index 8a111b90..666787e9 100644
--- a/frida_mode/include/stalker.h
+++ b/frida_mode/include/stalker.h
@@ -5,6 +5,7 @@
 
 extern guint    stalker_ic_entries;
 extern gboolean backpatch_enable;
+extern guint    stalker_adjacent_blocks;
 
 void        stalker_config(void);
 void        stalker_init(void);
diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c
index a36daf88..562e74eb 100644
--- a/frida_mode/src/entry.c
+++ b/frida_mode/src/entry.c
@@ -36,7 +36,7 @@ static void entry_launch(void) {
 
 }
 
-#if defined(__linux__) && !defined(__ANDROID__)
+#if defined(__linux__) && defined(PR_SET_PTRACER) && !defined(__ANDROID__)
 void entry_on_fork(void) {
 
   if (traceable) {
diff --git a/frida_mode/src/instrument/instrument_coverage.c b/frida_mode/src/instrument/instrument_coverage.c
index 95a24808..c1984eb2 100644
--- a/frida_mode/src/instrument/instrument_coverage.c
+++ b/frida_mode/src/instrument/instrument_coverage.c
@@ -237,7 +237,7 @@ static void instrument_coverage_mark(void *key, void *value, void *user_data) {
 
 }
 
-static void coverage_write(void *data, size_t size) {
+static void coverage_write(int fd, void *data, size_t size) {
 
   ssize_t written;
   size_t  remain = size;
@@ -245,7 +245,7 @@ static void coverage_write(void *data, size_t size) {
   for (char *cursor = (char *)data; remain > 0;
        remain -= written, cursor += written) {
 
-    written = write(normal_coverage_fd, cursor, remain);
+    written = write(fd, cursor, remain);
 
     if (written < 0) {
 
@@ -257,7 +257,7 @@ static void coverage_write(void *data, size_t size) {
 
 }
 
-static void coverage_format(char *format, ...) {
+static void coverage_format(int fd, char *format, ...) {
 
   va_list ap;
   char    buffer[4096] = {0};
@@ -272,11 +272,11 @@ static void coverage_format(char *format, ...) {
 
   len = strnlen(buffer, sizeof(buffer));
 
-  coverage_write(buffer, len);
+  coverage_write(fd, buffer, len);
 
 }
 
-static void coverage_write_modules(GArray *coverage_modules) {
+static void coverage_write_modules(int fd, GArray *coverage_modules) {
 
   guint emitted = 0;
   for (guint i = 0; i < coverage_modules->len; i++) {
@@ -285,16 +285,16 @@ static void coverage_write_modules(GArray *coverage_modules) {
         &g_array_index(coverage_modules, coverage_range_t, i);
     if (module->count == 0) continue;
 
-    coverage_format("%3u, ", emitted);
-    coverage_format("%016" G_GINT64_MODIFIER "X, ", module->base_address);
-    coverage_format("%016" G_GINT64_MODIFIER "X, ", module->limit);
+    coverage_format(fd, "%3u, ", emitted);
+    coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", module->base_address);
+    coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", module->limit);
     /* entry */
-    coverage_format("%016" G_GINT64_MODIFIER "X, ", 0);
+    coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0);
     /* checksum */
-    coverage_format("%016" G_GINT64_MODIFIER "X, ", 0);
+    coverage_format(fd, "%016" G_GINT64_MODIFIER "X, ", 0);
     /* timestamp */
-    coverage_format("%08" G_GINT32_MODIFIER "X, ", 0);
-    coverage_format("%s\n", module->path);
+    coverage_format(fd, "%08" G_GINT32_MODIFIER "X, ", 0);
+    coverage_format(fd, "%s\n", module->path);
     emitted++;
 
   }
@@ -304,7 +304,7 @@ static void coverage_write_modules(GArray *coverage_modules) {
 static void coverage_write_events(void *key, void *value, void *user_data) {
 
   UNUSED_PARAMETER(key);
-  UNUSED_PARAMETER(user_data);
+  int                     fd = *((int *)user_data);
   normal_coverage_data_t *val = (normal_coverage_data_t *)value;
 
   if (val->module == NULL) { return; }
@@ -317,20 +317,20 @@ static void coverage_write_events(void *key, void *value, void *user_data) {
 
   };
 
-  coverage_write(&evt, sizeof(coverage_event_t));
+  coverage_write(fd, &evt, sizeof(coverage_event_t));
 
 }
 
-static void coverage_write_header(guint coverage_marked_modules) {
+static void coverage_write_header(int fd, guint coverage_marked_modules) {
 
   char version[] = "DRCOV VERSION: 2\n";
   char flavour[] = "DRCOV FLAVOR: frida\n";
   char columns[] = "Columns: id, base, end, entry, checksum, timestamp, path\n";
-  coverage_write(version, sizeof(version) - 1);
-  coverage_write(flavour, sizeof(flavour) - 1);
-  coverage_format("Module Table: version 2, count %u\n",
+  coverage_write(fd, version, sizeof(version) - 1);
+  coverage_write(fd, flavour, sizeof(flavour) - 1);
+  coverage_format(fd, "Module Table: version 2, count %u\n",
                   coverage_marked_modules);
-  coverage_write(columns, sizeof(columns) - 1);
+  coverage_write(fd, columns, sizeof(columns) - 1);
 
 }
 
@@ -412,10 +412,11 @@ static void instrument_coverage_normal_run() {
   instrument_coverage_print("Coverage - Marked Modules: %u\n",
                             coverage_marked_modules);
 
-  coverage_write_header(coverage_marked_modules);
-  coverage_write_modules(coverage_modules);
-  coverage_format("BB Table: %u bbs\n", ctx.count);
-  g_hash_table_foreach(coverage_hash, coverage_write_events, NULL);
+  coverage_write_header(normal_coverage_fd, coverage_marked_modules);
+  coverage_write_modules(normal_coverage_fd, coverage_modules);
+  coverage_format(normal_coverage_fd, "BB Table: %u bbs\n", ctx.count);
+  g_hash_table_foreach(coverage_hash, coverage_write_events,
+                       &normal_coverage_fd);
 
   g_hash_table_unref(coverage_hash);
 
@@ -636,10 +637,11 @@ static void instrument_coverage_unstable_run(void) {
   instrument_coverage_print("Coverage - Marked Modules: %u\n",
                             coverage_marked_modules);
 
-  coverage_write_header(coverage_marked_modules);
-  coverage_write_modules(coverage_modules);
-  coverage_format("BB Table: %u bbs\n", ctx.count);
-  g_hash_table_foreach(unstable_blocks, coverage_write_events, NULL);
+  coverage_write_header(unstable_coverage_fd, coverage_marked_modules);
+  coverage_write_modules(unstable_coverage_fd, coverage_modules);
+  coverage_format(unstable_coverage_fd, "BB Table: %u bbs\n", ctx.count);
+  g_hash_table_foreach(unstable_blocks, coverage_write_events,
+                       &unstable_coverage_fd);
 
   g_hash_table_unref(unstable_blocks);
   g_array_free(unstable_edge_ids, TRUE);
diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c
index c474d034..c271adc1 100644
--- a/frida_mode/src/instrument/instrument_x64.c
+++ b/frida_mode/src/instrument/instrument_x64.c
@@ -52,54 +52,41 @@ typedef struct {
   // shared_mem[cur_location ^ prev_location]++;
   // prev_location = cur_location >> 1;
 
-  //  0x7ffff6cbb9b6:      lea    rsp,[rsp-0x80]
-  //
-  //  0x7ffff6cbb9bb:      push   rax
-  //  0x7ffff6cbb9bc:      lahf
-  //  0x7ffff6cbb9bd:      push   rax
-  //  0x7ffff6cbb9be:      push   rbx
-  //
-  //  0x7ffff6cbb9bf:      mov    eax,DWORD PTR [rip+0x33bd7b]
-  //  0x7ffff6cbb9c5:      xor    eax,0x3f77
-  //  0x7ffff6cbb9ca:      add    eax,0x10000
-  //  0x7ffff6cbb9cf:      mov    bl,BYTE PTR [rax]
-  //  0x7ffff6cbb9d1:      add    bl,0x1
-  //  0x7ffff6cbb9d4:      adc    bl,0x0
-  //  0x7ffff6cbb9d7:      mov    BYTE PTR [rax],bl
-  //
-  //  0x7ffff6cbb9d9:      mov    DWORD PTR [rip+0x33bd5d],0x9fbb
-  //
-  //  0x7ffff6cbb9e3:      pop    rbx
-  //  0x7ffff6cbb9e4:      pop    rax
-  //  0x7ffff6cbb9e5:      sahf
-  //  0x7ffff6cbb9e6:      pop    rax
-  //
-  //  0x7ffff6cbb9e7:      lea    rsp,[rsp+0x80]
-
-  uint8_t lea_rsp_rsp_sub_rz[5];
-
-  uint8_t push_rax;
+  //  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]
+
+  uint8_t mov_rax_rsp_80[5];
   uint8_t lahf;
-  uint8_t push_rax2;
-  uint8_t push_rbx;
+  uint8_t mov_rax_rsp_88[8];
+  uint8_t mov_rbx_rsp_90[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 add_eax_afl_area[5];
 
   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];
 
-  uint8_t mov_prev_loc_curr_loc_shr1[10];
-
-  uint8_t pop_rbx;
-  uint8_t pop_rax2;
+  uint8_t mov_rsp_90_rbx[8];
+  uint8_t mov_rsp_88_rax[8];
   uint8_t sahf;
-  uint8_t pop_rax;
-
-  uint8_t lsa_rsp_rsp_add_rz[8];
+  uint8_t mov_rsp_80_rax[5];
 
 } afl_log_code_asm_t;
 
@@ -115,29 +102,24 @@ typedef union {
 static const afl_log_code_asm_t template =
     {
 
-        .lea_rsp_rsp_sub_rz = {0x48, 0x8D, 0x64, 0x24, 0x80},
-        .push_rax = 0x50,
+        .mov_rax_rsp_80 = {0x48, 0x89, 0x44, 0x24, 0x80},
         .lahf = 0x9f,
-        .push_rax2 = 0x50,
-        .push_rbx = 0x53,
+        .mov_rax_rsp_88 = {0x48, 0x89, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
+        .mov_rbx_rsp_90 = {0x48, 0x89, 0x9C, 0x24, 0x70, 0xFF, 0xFF, 0xFF},
 
         .mov_eax_prev_loc = {0x8b, 0x05},
-        .xor_eax_curr_loc = {0x35},
+        .mov_prev_loc_curr_loc_shr1 = {0xc7, 0x05},
 
-        .add_eax_afl_area = {0x05},
+        .xor_eax_curr_loc = {0x35},
         .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_prev_loc_curr_loc_shr1 = {0xc7, 0x05},
-
-        .pop_rbx = 0x5b,
-        .pop_rax2 = 0x58,
+        .mov_rsp_90_rbx = {0x48, 0x8B, 0x9C, 0x24, 0x70, 0xFF, 0xFF, 0xFF},
+        .mov_rsp_88_rax = {0x48, 0x8B, 0x84, 0x24, 0x78, 0xFF, 0xFF, 0xFF},
         .sahf = 0x9e,
-        .pop_rax = 0x58,
-
-        .lsa_rsp_rsp_add_rz = {0x48, 0x8D, 0xA4, 0x24, 0x80, 0x00, 0x00, 0x00},
+        .mov_rsp_80_rax = {0x48, 0x8B, 0x44, 0x24, 0x80},
 
 }
 
@@ -162,7 +144,13 @@ static gboolean instrument_coverage_find_low(const GumRangeDetails *details,
 
   }
 
-  last_limit = details->range->base_address + details->range->size;
+  /*
+   * 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, (64ULL << 10));
   return TRUE;
 
 }
@@ -421,13 +409,8 @@ void instrument_coverage_optimize(const cs_insn *   instr,
                                sizeof(code.code.xor_eax_curr_loc) -
                                sizeof(guint32);
 
-  *((guint32 *)&code.bytes[xor_curr_loc_offset]) = (guint32)(area_offset);
-
-  gssize lea_rax_offset = offsetof(afl_log_code, code.add_eax_afl_area) +
-                          sizeof(code.code.add_eax_afl_area) - sizeof(guint32);
-
-  *((guint32 *)&code.bytes[lea_rax_offset]) =
-      (guint32)GPOINTER_TO_SIZE(__afl_area_ptr);
+  *((guint32 *)&code.bytes[xor_curr_loc_offset]) =
+      (guint32)(GPOINTER_TO_SIZE(__afl_area_ptr) | area_offset);
 
   gum_x86_writer_put_bytes(cw, code.bytes, sizeof(afl_log_code));
 
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
index 5db62389..8e810d09 100644
--- a/frida_mode/src/js/api.js
+++ b/frida_mode/src/js/api.js
@@ -205,6 +205,12 @@ class Afl {
         const buf = Memory.allocUtf8String(file);
         Afl.jsApiSetSeccompFile(buf);
     }
+    /**
+     * See `AFL_FRIDA_STALKER_ADJACENT_BLOCKS`.
+     */
+    static setStalkerAdjacentBlocks(val) {
+        Afl.jsApiSetStalkerAdjacentBlocks(val);
+    }
     /*
      * Set a function to be called for each instruction which is instrumented
      * by AFL FRIDA mode.
@@ -294,6 +300,7 @@ Afl.jsApiSetPrefetchBackpatchDisable = Afl.jsApiGetFunction("js_api_set_prefetch
 Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []);
 Afl.jsApiSetSeccompFile = Afl.jsApiGetFunction("js_api_set_seccomp_file", "void", ["pointer"]);
 Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]);
+Afl.jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction("js_api_set_stalker_adjacent_blocks", "void", ["uint32"]);
 Afl.jsApiSetStalkerIcEntries = Afl.jsApiGetFunction("js_api_set_stalker_ic_entries", "void", ["uint32"]);
 Afl.jsApiSetStatsFile = Afl.jsApiGetFunction("js_api_set_stats_file", "void", ["pointer"]);
 Afl.jsApiSetStatsInterval = Afl.jsApiGetFunction("js_api_set_stats_interval", "void", ["uint64"]);
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
index 570da335..102423d9 100644
--- a/frida_mode/src/js/js_api.c
+++ b/frida_mode/src/js/js_api.c
@@ -47,6 +47,14 @@ __attribute__((visibility("default"))) void js_api_set_persistent_address(
 
   persistent_start = GPOINTER_TO_SIZE(address);
 
+  if (getenv("__AFL_PERSISTENT") == NULL) {
+
+    FATAL(
+        "You must set __AFL_PERSISTENT manually if using persistent mode "
+        "configured using JS");
+
+  }
+
 }
 
 __attribute__((visibility("default"))) void js_api_set_persistent_return(
@@ -242,3 +250,10 @@ __attribute__((visibility("default"))) void js_api_set_backpatch_disable(void) {
 
 }
 
+__attribute__((visibility("default"))) void js_api_set_stalker_adjacent_blocks(
+    guint val) {
+
+  stalker_adjacent_blocks = val;
+
+}
+
diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c
index 35a9d856..65ed5d50 100644
--- a/frida_mode/src/stalker.c
+++ b/frida_mode/src/stalker.c
@@ -7,6 +7,7 @@
 
 guint    stalker_ic_entries = 0;
 gboolean backpatch_enable = TRUE;
+guint    stalker_adjacent_blocks = 0;
 
 static GumStalker *stalker = NULL;
 
@@ -60,7 +61,9 @@ void stalker_config(void) {
 
   backpatch_enable = (getenv("AFL_FRIDA_INST_NO_BACKPATCH") == NULL);
 
-  stalker_ic_entries = util_read_num("AFL_FRIDA_STALKER_IC_ENTRIES");
+  stalker_ic_entries = util_read_num("AFL_FRIDA_STALKER_ADJACENT_BLOCKS");
+
+  stalker_adjacent_blocks = util_read_num("AFL_FRIDA_STALKER_IC_ENTRIES");
 
   observer = g_object_new(GUM_TYPE_AFL_STALKER_OBSERVER, NULL);
 
@@ -92,6 +95,7 @@ void stalker_init(void) {
   FOKF("Instrumentation - backpatch [%c]", backpatch_enable ? 'X' : ' ');
 
   FOKF("Stalker - ic_entries [%u]", stalker_ic_entries);
+  FOKF("Stalker - adjacent_blocks [%u]", stalker_adjacent_blocks);
 
 #if !(defined(__x86_64__) || defined(__i386__))
   if (stalker_ic_entries != 0) {
@@ -100,13 +104,21 @@ void stalker_init(void) {
 
   }
 
+  if (stalker_adjacent_blocks != 0) {
+
+    FFATAL("AFL_FRIDA_STALKER_ADJACENT_BLOCKS not supported");
+
+  }
+
 #endif
 
   if (stalker_ic_entries == 0) { stalker_ic_entries = 32; }
 
+  if (stalker_adjacent_blocks == 0) { stalker_adjacent_blocks = 32; }
+
 #if defined(__x86_64__) || defined(__i386__)
-  stalker =
-      g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries, NULL);
+  stalker = g_object_new(GUM_TYPE_STALKER, "ic-entries", stalker_ic_entries,
+                         "adjacent-blocks", stalker_adjacent_blocks, NULL);
 #else
   stalker = gum_stalker_new();
 #endif
diff --git a/frida_mode/test/png/persistent/hook/GNUmakefile b/frida_mode/test/png/persistent/hook/GNUmakefile
index 5010662b..23aa94d0 100644
--- a/frida_mode/test/png/persistent/hook/GNUmakefile
+++ b/frida_mode/test/png/persistent/hook/GNUmakefile
@@ -144,6 +144,7 @@ frida_entry_slow: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $
 
 frida_js_load: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
 	AFL_PRELOAD=$(AFL_PRELOAD) \
+	__AFL_PERSISTENT=1 \
 	AFL_FRIDA_JS_SCRIPT=load.js \
 	$(ROOT)afl-fuzz \
 		-D \
diff --git a/frida_mode/test/unstable/GNUmakefile b/frida_mode/test/unstable/GNUmakefile
index 0ccc5fb1..54bbe662 100644
--- a/frida_mode/test/unstable/GNUmakefile
+++ b/frida_mode/test/unstable/GNUmakefile
@@ -86,11 +86,23 @@ frida: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE)
 			$(UNSTABLE_BIN) @@
 
 frida_coverage: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE)
-	AFL_DEBUG=1 \
 	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
 	AFL_FRIDA_OUTPUT_STDOUT=/tmp/stdout.txt \
     AFL_FRIDA_OUTPUT_STDERR=/tmp/stderr.txt \
 	AFL_FRIDA_INST_COVERAGE_FILE=/tmp/coverage.dat \
+	$(ROOT)afl-fuzz \
+		-D \
+		-O \
+		-i $(UNSTABLE_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-- \
+			$(UNSTABLE_BIN) @@
+
+frida_unstable: $(UNSTABLE_BIN) $(UNSTABLE_DATA_FILE)
+	AFL_DEBUG=1 \
+	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	AFL_FRIDA_OUTPUT_STDOUT=/tmp/stdout.txt \
+    AFL_FRIDA_OUTPUT_STDERR=/tmp/stderr.txt \
     AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE=/tmp/unstable.dat \
 	$(ROOT)afl-fuzz \
 		-D \
diff --git a/frida_mode/ts/lib/afl.ts b/frida_mode/ts/lib/afl.ts
index 3639d670..e20ad3ec 100644
--- a/frida_mode/ts/lib/afl.ts
+++ b/frida_mode/ts/lib/afl.ts
@@ -241,6 +241,13 @@ class Afl {
     Afl.jsApiSetSeccompFile(buf);
   }
 
+  /**
+   * See `AFL_FRIDA_STALKER_ADJACENT_BLOCKS`.
+   */
+  public static setStalkerAdjacentBlocks(val: number): void {
+    Afl.jsApiSetStalkerAdjacentBlocks(val);
+  }
+
   /*
    * Set a function to be called for each instruction which is instrumented
    * by AFL FRIDA mode.
@@ -425,6 +432,11 @@ class Afl {
     "void",
     ["pointer"]);
 
+  private static readonly jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction(
+    "js_api_set_stalker_adjacent_blocks",
+    "void",
+    ["uint32"]);
+
   private static readonly jsApiSetStalkerIcEntries = Afl.jsApiGetFunction(
     "js_api_set_stalker_ic_entries",
     "void",