about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--frida_mode/README.md2
-rw-r--r--frida_mode/frida.map1
-rw-r--r--frida_mode/include/instrument.h3
-rw-r--r--frida_mode/src/instrument/instrument.c8
-rw-r--r--frida_mode/src/instrument/instrument_arm32.c9
-rw-r--r--frida_mode/src/instrument/instrument_arm64.c8
-rw-r--r--frida_mode/src/instrument/instrument_x64.c191
-rw-r--r--frida_mode/src/instrument/instrument_x86.c8
-rw-r--r--frida_mode/src/js/api.js7
-rw-r--r--frida_mode/src/js/js_api.c7
-rw-r--r--frida_mode/test/cmov/GNUmakefile87
-rw-r--r--frida_mode/test/cmov/Makefile19
-rw-r--r--frida_mode/test/cmov/cmov.c122
-rw-r--r--frida_mode/ts/lib/afl.ts12
-rw-r--r--include/envs.h1
15 files changed, 457 insertions, 28 deletions
diff --git a/frida_mode/README.md b/frida_mode/README.md
index e5b46baf..bf5cffec 100644
--- a/frida_mode/README.md
+++ b/frida_mode/README.md
@@ -173,6 +173,8 @@ instances run CMPLOG mode and instrumentation of the binary is less frequent
 * `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
   code. Code is considered to be JIT if the executable segment is not backed by
   a file.
+* `AFL_FRIDA_INST_NO_INSN` - Don't generate instrumentation for conditional
+  instructions (e.g. `CMOV` instructions on x64).
 * `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
   instrumentation (the default where available). Required to use
   `AFL_FRIDA_INST_TRACE`.
diff --git a/frida_mode/frida.map b/frida_mode/frida.map
index d059bdb6..e9afac1b 100644
--- a/frida_mode/frida.map
+++ b/frida_mode/frida.map
@@ -15,6 +15,7 @@
     js_api_set_instrument_debug_file;
     js_api_set_instrument_jit;
     js_api_set_instrument_libraries;
+    js_api_set_instrument_no_instructions;
     js_api_set_instrument_no_optimize;
     js_api_set_instrument_seed;
     js_api_set_instrument_trace;
diff --git a/frida_mode/include/instrument.h b/frida_mode/include/instrument.h
index abb89c9f..20312a4b 100644
--- a/frida_mode/include/instrument.h
+++ b/frida_mode/include/instrument.h
@@ -12,6 +12,7 @@ extern gboolean instrument_optimize;
 extern gboolean instrument_unique;
 extern guint64  instrument_hash_zero;
 extern char *   instrument_coverage_unstable_filename;
+extern gboolean instrument_coverage_insn;
 
 extern gboolean instrument_use_fixed_seed;
 extern guint64  instrument_fixed_seed;
@@ -33,6 +34,8 @@ gboolean instrument_is_coverage_optimize_supported(void);
 void instrument_coverage_optimize_init(void);
 void instrument_coverage_optimize(const cs_insn *   instr,
                                   GumStalkerOutput *output);
+void instrument_coverage_optimize_insn(const cs_insn *   instr,
+                                       GumStalkerOutput *output);
 
 void     instrument_debug_config(void);
 void     instrument_debug_init(void);
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index 418b35e8..4877f4fb 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)) {
@@ -269,6 +276,7 @@ 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_NO_INSN") == NULL);
 
   instrument_debug_config();
   instrument_coverage_config();
diff --git a/frida_mode/src/instrument/instrument_arm32.c b/frida_mode/src/instrument/instrument_arm32.c
index 16e8eaab..705faa64 100644
--- a/frida_mode/src/instrument/instrument_arm32.c
+++ b/frida_mode/src/instrument/instrument_arm32.c
@@ -20,6 +20,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");
diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c
index 1a704585..4abc0625 100644
--- a/frida_mode/src/instrument/instrument_arm64.c
+++ b/frida_mode/src/instrument/instrument_arm64.c
@@ -341,6 +341,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);
diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c
index c28285ff..4b1a2d68 100644
--- a/frida_mode/src/instrument/instrument_x64.c
+++ b/frida_mode/src/instrument/instrument_x64.c
@@ -23,6 +23,40 @@
 
 #if defined(__x86_64__)
 
+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;
 
 gboolean instrument_is_coverage_optimize_supported(void) {
@@ -201,37 +235,15 @@ 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;
 
@@ -313,6 +325,129 @@ 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));
+  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_x86.c b/frida_mode/src/instrument/instrument_x86.c
index c4e93324..916cd0e2 100644
--- a/frida_mode/src/instrument/instrument_x86.c
+++ b/frida_mode/src/instrument/instrument_x86.c
@@ -218,6 +218,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) {
 
 }
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
index 52e9e45c..c2d9a2d4 100644
--- a/frida_mode/src/js/api.js
+++ b/frida_mode/src/js/api.js
@@ -126,6 +126,12 @@ class Afl {
         Afl.jsApiSetInstrumentLibraries();
     }
     /**
+     * See `AFL_FRIDA_INST_NO_INSN`
+     */
+    static setInstrumentNoInstructions() {
+        Afl.jsApiSetInstrumentNoInstructions();
+    }
+    /**
      * See `AFL_FRIDA_INST_NO_OPTIMIZE`
      */
     static setInstrumentNoOptimize() {
@@ -299,6 +305,7 @@ Afl.jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument
 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.jsApiSetInstrumentNoInstructions = Afl.jsApiGetFunction("js_api_set_instrument_no_instructions", "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", []);
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
index 94ec8842..613747b8 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_no_instructions(void) {
+
+  instrument_coverage_insn = FALSE;
+
+}
+
 __attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
     void) {
 
diff --git a/frida_mode/test/cmov/GNUmakefile b/frida_mode/test/cmov/GNUmakefile
new file mode 100644
index 00000000..318b2ad0
--- /dev/null
+++ b/frida_mode/test/cmov/GNUmakefile
@@ -0,0 +1,87 @@
+PWD:=$(shell pwd)/
+ROOT:=$(PWD)../../../
+BUILD_DIR:=$(PWD)build/
+
+TEST_CMOV_SRC:=$(PWD)cmov.c
+TEST_CMOV_OBJ:=$(BUILD_DIR)cmov
+
+TEST_DATA_DIR:=$(BUILD_DIR)in/
+CMP_LOG_INPUT:=$(TEST_DATA_DIR)in
+QEMU_OUT:=$(BUILD_DIR)qemu-out
+FRIDA_OUT:=$(BUILD_DIR)frida-out
+
+ADDR_BIN:=$(ROOT)frida_mode/build/addr
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
+
+AFL_FRIDA_BASE_ADDR:=$(shell $(ADDR_BIN))
+AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_CMOV_OBJ) LLVMFuzzerTestOneInput $(AFL_FRIDA_BASE_ADDR))
+
+DUMMY_DATA_FILE:=$(BUILD_DIR)dummy.dat
+
+.PHONY: all 32 clean frida frida_noinst debug format
+
+all: $(TEST_CMOV_OBJ)
+	make -C $(ROOT)frida_mode/
+
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+$(TEST_DATA_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(CMP_LOG_INPUT): | $(TEST_DATA_DIR)
+	echo -n "ABC" > $@
+
+$(TEST_CMOV_OBJ): $(TEST_CMOV_SRC) | $(BUILD_DIR)
+	$(CC) -g $(CFLAGS) $(LDFLAGS) $< -o $@
+
+########## DUMMY #######
+
+$(DUMMY_DATA_FILE): | $(BUILD_DIR)
+	dd if=/dev/zero bs=1048576 count=1 of=$@
+
+frida: $(TEST_CMOV_OBJ) $(CMP_LOG_INPUT) $(DUMMY_DATA_FILE)
+	AFL_FRIDA_PERSISTENT_CNT=1000000 \
+	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \
+	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	$(ROOT)afl-fuzz \
+		-O \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-Z \
+		-t 10000+ \
+		-- \
+			$(TEST_CMOV_OBJ) $(DUMMY_DATA_FILE)
+
+frida_noinst: $(TEST_CMOV_OBJ) $(CMP_LOG_INPUT) $(DUMMY_DATA_FILE)
+	AFL_FRIDA_INST_NO_INSN=1 \
+	AFL_FRIDA_PERSISTENT_CNT=1000000 \
+	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \
+	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
+	$(ROOT)afl-fuzz \
+		-O \
+		-i $(TEST_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-Z \
+		-- \
+			$(TEST_CMOV_OBJ) $(DUMMY_DATA_FILE)
+
+debug: $(TEST_CMOV_OBJ) $(CMP_LOG_INPUT)
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--ex 'r $(CMP_LOG_INPUT)' \
+		--args $(TEST_CMOV_OBJ) $(CMP_LOG_INPUT)
+
+clean:
+	rm -rf $(BUILD_DIR)
+
+format:
+	cd $(ROOT) && echo $(TEST_CMOV_SRC) | xargs -L1 ./.custom-format.py -i
diff --git a/frida_mode/test/cmov/Makefile b/frida_mode/test/cmov/Makefile
new file mode 100644
index 00000000..9ef6fc8f
--- /dev/null
+++ b/frida_mode/test/cmov/Makefile
@@ -0,0 +1,19 @@
+all:
+	@echo trying to use GNU make...
+	@gmake all || echo please install GNUmake
+
+32:
+	@echo trying to use GNU make...
+	@gmake 32 || echo please install GNUmake
+
+clean:
+	@gmake clean
+
+frida:
+	@gmake frida
+
+format:
+	@gmake format
+
+debug:
+	@gmake debug
diff --git a/frida_mode/test/cmov/cmov.c b/frida_mode/test/cmov/cmov.c
new file mode 100644
index 00000000..08c7c132
--- /dev/null
+++ b/frida_mode/test/cmov/cmov.c
@@ -0,0 +1,122 @@
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static bool cmov_test(char *x, char *y, size_t len) {
+
+  register char * __rdi __asm__("rdi") = x;
+  register char * __rsi __asm__("rsi") = y;
+  register size_t __rcx __asm__("rcx") = len;
+
+  register long __rax __asm__("rax");
+
+  __asm__ __volatile__(
+      "mov $0x1, %%rax\n"
+      "mov $0x0, %%r8\n"
+      "1:\n"
+      "mov (%%rsi), %%bl\n"
+      "mov (%%rdi), %%dl\n"
+      "cmp %%bl, %%dl\n"
+      "cmovne %%r8, %%rax\n"
+      "inc %%rsi\n"
+      "inc %%rdi\n"
+      "dec %%rcx\n"
+      "jnz 1b\n"
+      : "=r"(__rax)
+      : "r"(__rdi), "r"(__rsi)
+      : "r8", "bl", "dl", "memory");
+
+  return __rax;
+
+}
+
+void LLVMFuzzerTestOneInput(char *buf, int len) {
+
+  char match[] = "CBAABC";
+
+  if (len > sizeof(match)) { return; }
+
+  if (cmov_test(buf, match, sizeof(buf)) != 0) {
+
+    printf("Puzzle solved, congrats!\n");
+    abort();
+
+  }
+
+}
+
+int main(int argc, char **argv) {
+
+  char * file;
+  int    fd = -1;
+  off_t  len;
+  char * buf = NULL;
+  size_t n_read;
+  int    result = -1;
+
+  if (argc != 2) { return 1; }
+
+  do {
+
+    file = argv[1];
+
+    dprintf(STDERR_FILENO, "Running: %s\n", file);
+
+    fd = open(file, O_RDONLY);
+    if (fd < 0) {
+
+      perror("open");
+      break;
+
+    }
+
+    len = lseek(fd, 0, SEEK_END);
+    if (len < 0) {
+
+      perror("lseek (SEEK_END)");
+      break;
+
+    }
+
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+
+      perror("lseek (SEEK_SET)");
+      break;
+
+    }
+
+    buf = (char *)malloc(len);
+    if (buf == NULL) {
+
+      perror("malloc");
+      break;
+
+    }
+
+    n_read = read(fd, buf, len);
+    if (n_read != len) {
+
+      perror("read");
+      break;
+
+    }
+
+    dprintf(STDERR_FILENO, "Running:    %s: (%zd bytes)\n", file, n_read);
+
+    LLVMFuzzerTestOneInput(buf, len);
+    dprintf(STDERR_FILENO, "Done:    %s: (%zd bytes)\n", file, n_read);
+
+    result = 0;
+
+  } while (false);
+
+  if (buf != NULL) { free(buf); }
+
+  if (fd != -1) { close(fd); }
+
+  return result;
+
+}
+
diff --git a/frida_mode/ts/lib/afl.ts b/frida_mode/ts/lib/afl.ts
index 57d7b5b5..9d31370e 100644
--- a/frida_mode/ts/lib/afl.ts
+++ b/frida_mode/ts/lib/afl.ts
@@ -150,6 +150,13 @@ class Afl {
   }
 
   /**
+   * See `AFL_FRIDA_INST_NO_INSN`
+   */
+  public static setInstrumentNoInstructions(): void {
+    Afl.jsApiSetInstrumentNoInstructions();
+  }
+
+  /**
    * See `AFL_FRIDA_INST_NO_OPTIMIZE`
    */
   public static setInstrumentNoOptimize(): void {
@@ -377,6 +384,11 @@ class Afl {
     "void",
     []);
 
+  private static readonly jsApiSetInstrumentNoInstructions = Afl.jsApiGetFunction(
+    "js_api_set_instrument_no_instructions",
+    "void",
+    []);
+
   private static readonly jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction(
     "js_api_set_instrument_no_optimize",
     "void",
diff --git a/include/envs.h b/include/envs.h
index f4327d8c..2ab3a387 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -60,6 +60,7 @@ static char *afl_environment_variables[] = {
     "AFL_FRIDA_INST_COVERAGE_FILE",
     "AFL_FRIDA_INST_DEBUG_FILE",
     "AFL_FRIDA_INST_JIT",
+    "AFL_FRIDA_INST_NO_INSN",
     "AFL_FRIDA_INST_NO_OPTIMIZE",
     "AFL_FRIDA_INST_NO_PREFETCH",
     "AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH",