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/GNUmakefile75
-rw-r--r--frida_mode/MapDensity.md147
-rw-r--r--frida_mode/README.md9
-rw-r--r--frida_mode/Scripting.md13
-rw-r--r--frida_mode/frida.map34
-rw-r--r--frida_mode/hook/frida_hook.c64
-rw-r--r--frida_mode/hook/hook.c50
-rw-r--r--frida_mode/hook/qemu_hook.c195
-rw-r--r--frida_mode/include/entry.h3
-rw-r--r--frida_mode/include/instrument.h16
-rw-r--r--frida_mode/include/persistent.h3
-rw-r--r--frida_mode/include/ranges.h1
-rw-r--r--frida_mode/include/stalker.h1
-rw-r--r--frida_mode/include/stats.h1
-rw-r--r--frida_mode/many-linux/Dockerfile24
-rw-r--r--frida_mode/many-linux/GNUmakefile21
-rw-r--r--frida_mode/many-linux/Makefile9
-rw-r--r--frida_mode/many-linux/README.md8
-rw-r--r--frida_mode/many-linux/realpath2
-rw-r--r--frida_mode/src/entry.c19
-rw-r--r--frida_mode/src/instrument/instrument.c77
-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.js7
-rw-r--r--frida_mode/src/js/js.c69
-rw-r--r--frida_mode/src/js/js_api.c89
-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.c11
-rw-r--r--frida_mode/src/stats/stats.c6
-rw-r--r--frida_mode/src/stats/stats_x64.c18
-rw-r--r--frida_mode/test/deferred/GNUmakefile14
-rw-r--r--frida_mode/test/deferred/testinstr.c1
-rw-r--r--frida_mode/test/entry_point/GNUmakefile14
-rw-r--r--frida_mode/test/jpeg/GNUmakefile18
-rw-r--r--frida_mode/test/js/GNUmakefile20
-rw-r--r--frida_mode/test/js/entry.js14
-rw-r--r--frida_mode/test/libpcap/GNUmakefile23
-rwxr-xr-xfrida_mode/test/libpcap/get_symbol_addr.py36
-rw-r--r--frida_mode/test/persistent_ret/GNUmakefile25
-rwxr-xr-xfrida_mode/test/persistent_ret/get_symbol_addr.py36
-rw-r--r--frida_mode/test/png/GNUmakefile8
-rw-r--r--frida_mode/test/png/Makefile3
-rw-r--r--frida_mode/test/png/persistent/GNUmakefile10
-rwxr-xr-xfrida_mode/test/png/persistent/get_symbol_addr.py36
-rw-r--r--frida_mode/test/png/persistent/hook/GNUmakefile42
-rw-r--r--frida_mode/test/png/persistent/hook/load.js2
-rw-r--r--frida_mode/test/proj4/GNUmakefile16
-rw-r--r--frida_mode/test/re2/GNUmakefile21
-rwxr-xr-xfrida_mode/test/re2/get_symbol_addr.py36
-rw-r--r--frida_mode/test/sqlite/GNUmakefile166
-rw-r--r--frida_mode/test/sqlite/Makefile17
-rw-r--r--frida_mode/test/testinstr/GNUmakefile7
-rw-r--r--frida_mode/test/unstable/GNUmakefile10
-rwxr-xr-xfrida_mode/test/unstable/get_symbol_addr.py36
-rw-r--r--frida_mode/ts/lib/afl.ts14
-rw-r--r--frida_mode/ts/package.json60
-rw-r--r--frida_mode/util/bin2c.c117
-rwxr-xr-xfrida_mode/util/get_symbol_addr.sh32
65 files changed, 1519 insertions, 494 deletions
diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile
index f5a96501..44dfafe3 100644
--- a/frida_mode/GNUmakefile
+++ b/frida_mode/GNUmakefile
@@ -19,13 +19,14 @@ CFLAGS+=-fPIC \
 		-g \
 		-O3 \
 		-funroll-loops \
+		-ffunction-sections \
 
-RT_CFLAGS:=-Wno-unused-parameter \
+AFL_CFLAGS:=-Wno-unused-parameter \
 		   -Wno-sign-compare \
 		   -Wno-unused-function \
 		   -Wno-unused-result \
 		   -Wno-int-to-pointer-cast \
-		   -Wno-pointer-sign \
+		   -Wno-pointer-sign
 
 LDFLAGS+=-shared \
 		 -lpthread \
@@ -59,12 +60,15 @@ endif
 
 ifeq "$(shell uname)" "Darwin"
  OS:=macos
- RT_CFLAGS:=$(RT_CFLAGS) -Wno-deprecated-declarations
+ AFL_CFLAGS:=$(AFL_CFLAGS) -Wno-deprecated-declarations
 else
 ifdef DEBUG
- RT_CFLAGS:=$(RT_CFLAGS) -Wno-prio-ctor-dtor
+ AFL_CFLAGS:=$(AFL_CFLAGS) -Wno-prio-ctor-dtor
 endif
-LDFLAGS+=-z noexecstack
+LDFLAGS+=	-z noexecstack \
+			-Wl,--gc-sections \
+			-Wl,--exclude-libs,ALL
+LDSCRIPT:=-Wl,--version-script=$(PWD)frida.map
 endif
 
 ifeq "$(shell uname)" "Linux"
@@ -75,7 +79,12 @@ ifndef OS
  $(error "Operating system unsupported")
 endif
 
+ifeq "$(ARCH)" "arm64"
+# 15.0.0 Not released for aarch64 yet
 GUM_DEVKIT_VERSION=14.2.18
+else
+GUM_DEVKIT_VERSION=15.0.0
+endif
 GUM_DEVKIT_FILENAME=frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz
 GUM_DEVKIT_URL="https://github.com/frida/frida/releases/download/$(GUM_DEVKIT_VERSION)/$(GUM_DEVKIT_FILENAME)"
 
@@ -94,15 +103,24 @@ FRIDA_GUM_DEVKIT_COMPRESSED_TARBALL:=$(FRIDA_DIR)build/$(GUM_DEVKIT_FILENAME)
 AFL_COMPILER_RT_SRC:=$(ROOT)instrumentation/afl-compiler-rt.o.c
 AFL_COMPILER_RT_OBJ:=$(OBJ_DIR)afl-compiler-rt.o
 
+AFL_PERFORMANCE_SRC:=$(ROOT)src/afl-performance.c
+AFL_PERFORMANCE_OBJ:=$(OBJ_DIR)afl-performance.o
+
 HOOK_DIR:=$(PWD)hook/
-AFLPP_DRIVER_HOOK_SRC=$(HOOK_DIR)hook.c
-AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)hook.so
+AFLPP_FRIDA_DRIVER_HOOK_SRC=$(HOOK_DIR)frida_hook.c
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(BUILD_DIR)frida_hook.so
+
+AFLPP_QEMU_DRIVER_HOOK_SRC:=$(HOOK_DIR)qemu_hook.c
+AFLPP_QEMU_DRIVER_HOOK_OBJ:=$(BUILD_DIR)qemu_hook.so
+
+BIN2C:=$(BUILD_DIR)bin2c
+BIN2C_SRC:=$(PWD)util/bin2c.c
 
 .PHONY: all 32 clean format hook $(FRIDA_GUM)
 
 ############################## ALL #############################################
 
-all: $(FRIDA_TRACE) $(AFLPP_DRIVER_HOOK_OBJ)
+all: $(FRIDA_TRACE) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_QEMU_DRIVER_HOOK_OBJ)
 
 32:
 	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
@@ -143,17 +161,26 @@ $(GUM_DEVKIT_TARBALL): | $(FRIDA_BUILD_DIR)
 	wget -O $@ $(GUM_DEVKIT_URL)
 endif
 
-$(GUM_DEVIT_LIBRARY): | $(GUM_DEVKIT_TARBALL)
-	tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
+$(GUM_DEVIT_LIBRARY): $(GUM_DEVKIT_TARBALL)
+	tar Jxvfm $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
 
-$(GUM_DEVIT_HEADER): | $(GUM_DEVKIT_TARBALL)
-	tar Jxvf $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
+$(GUM_DEVIT_HEADER): $(GUM_DEVKIT_TARBALL)
+	tar Jxvfm $(GUM_DEVKIT_TARBALL) -C $(FRIDA_BUILD_DIR)
 
 ############################## AFL #############################################
 $(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC)
 	$(CC) \
 		$(CFLAGS) \
-		$(RT_CFLAGS) \
+		$(AFL_CFLAGS) \
+		-I $(ROOT) \
+		-I $(ROOT)include \
+		-o $@ \
+		-c $<
+
+$(AFL_PERFORMANCE_OBJ): $(AFL_PERFORMANCE_SRC)
+	$(CC) \
+		$(CFLAGS) \
+		$(AFL_CFLAGS) \
 		-I $(ROOT) \
 		-I $(ROOT)include \
 		-o $@ \
@@ -161,10 +188,13 @@ $(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC)
 
 ############################### JS #############################################
 
-$(JS_SRC): $(JS) | $(BUILD_DIR)
-	cd $(JS_DIR) && xxd -i $(JS_NAME) $@
+$(BIN2C): $(BIN2C_SRC)
+	$(CC) -D_GNU_SOURCE -o $@ $<
+
+$(JS_SRC): $(JS) $(BIN2C)| $(BUILD_DIR)
+	cd $(JS_DIR) && $(BIN2C) api_js $(JS) $@
 
-$(JS_OBJ): $(JS_SRC)
+$(JS_OBJ): $(JS_SRC) GNUmakefile
 	$(CC) \
 		$(CFLAGS) \
 		-I $(ROOT)include \
@@ -190,23 +220,28 @@ $(foreach src,$(SOURCES),$(eval $(call BUILD_SOURCE,$(src),$(OBJ_DIR)$(notdir $(
 
 ######################## AFL-FRIDA-TRACE #######################################
 
-$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL_COMPILER_RT_OBJ) GNUmakefile | $(BUILD_DIR)
+$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL_COMPILER_RT_OBJ) $(AFL_PERFORMANCE_OBJ) GNUmakefile | $(BUILD_DIR)
 	$(CXX) \
 		$(OBJS) \
 		$(JS_OBJ) \
 		$(GUM_DEVIT_LIBRARY) \
 		$(AFL_COMPILER_RT_OBJ) \
+		$(AFL_PERFORMANCE_OBJ) \
 		$(LDFLAGS) \
+		$(LDSCRIPT) \
 		-o $@ \
 
 	cp -v $(FRIDA_TRACE) $(ROOT)
 
 ############################# HOOK #############################################
 
-$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
+$(AFLPP_FRIDA_DRIVER_HOOK_OBJ): $(AFLPP_FRIDA_DRIVER_HOOK_SRC) $(GUM_DEVIT_HEADER) | $(BUILD_DIR)
 	$(CC) $(CFLAGS) $(LDFLAGS) -I $(FRIDA_BUILD_DIR) $< -o $@
 
-hook: $(AFLPP_DRIVER_HOOK_OBJ)
+$(AFLPP_QEMU_DRIVER_HOOK_OBJ): $(AFLPP_QEMU_DRIVER_HOOK_SRC) | $(BUILD_DIR)
+	$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
+
+hook: $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_QEMU_DRIVER_HOOK_OBJ)
 
 ############################# CLEAN ############################################
 clean:
@@ -214,7 +249,7 @@ clean:
 
 ############################# FORMAT ###########################################
 format:
-	cd $(ROOT) && echo $(SOURCES) $(AFLPP_DRIVER_HOOK_SRC) | xargs -L1 ./.custom-format.py -i
+	cd $(ROOT) && echo $(SOURCES) $(AFLPP_FRIDA_DRIVER_HOOK_SRC) $(BIN2C_SRC) | xargs -L1 ./.custom-format.py -i
 	cd $(ROOT) && echo $(INCLUDES) | xargs -L1 ./.custom-format.py -i
 
 ############################# RUN #############################################
diff --git a/frida_mode/MapDensity.md b/frida_mode/MapDensity.md
new file mode 100644
index 00000000..f4ae3ace
--- /dev/null
+++ b/frida_mode/MapDensity.md
@@ -0,0 +1,147 @@
+# Map Density
+
+# How Coverage Works
+The coverage in AFL++ works by assigning each basic block of code a unique ID
+and during execution when transitioning between blocks (e.g. by calls or jumps)
+assigning each of these edges an ID based upon the source and destination block
+ID.
+
+For each individual execution of the target, a single dimensional byte array
+indexed by the edge ID is used to count how many times each edge is traversed.
+
+A single dimensional cumulative byte array is also constructed where each byte
+again represents an individual edge ID, but this time, the value of the byte
+represents a range of how many times that edge has been traversed.
+
+```1, 2, 3, 4-7, 8-15, 16-31, 32-127, 128+```
+
+The theory is that a new path isn't particularly interesting if an edge has been
+traversed `23` instead of `24` times for example, but is interesting if an edge
+has been traversed for the very first time, or the number of times fits within a different bucket.
+
+After each run, the count of times each edge is hit is compared to the values in
+the cumulative map and if it is different, then the input is kept as a new seed
+and the cumulative map is updated.
+
+This mechanism is described in greater detail in the seminal
+[paper](https://lcamtuf.coredump.cx/afl/technical_details.txt) on AFL by
+[lcamtuf](https://github.com/lcamtuf).
+
+# Collisions
+In black-box fuzzing, we must assume that control may flow from any block to any
+other block, since we don't know any better. Thus for a target with `n` basic
+blocks of code, there are `n * n` potential edges. As we can see, even with a
+small number of edges, a very large map will be required so that we have space
+to fit them all. Even if our target only had `1024` blocks, this would require a
+map containing `1048576` entries (or 1Mb in size).
+
+Whilst this may not seem like a lot of memory, it causes problems for two reasons. Firstly, the processing step after each execution must now process much more
+data, and secondly a map this size is unlikely to fit within the L2 cache of the processor. Since this is a very hot code path, we are likely to pay a very heavy
+performance cost.
+
+Therefore, we must accept that not all edges can have a unique and that
+therefore there will be collisions. This means that if the fuzzer finds a new
+path by uncovering an edge which was not previously found, but that the same
+edge ID is used by another edge, then it may go completely unnoticed. This is
+obviously undesirable, but equally if our map is too large, then we will not be
+able to process as many potential inputs in the same time and hence not uncover
+edges for that reason. Thus a careful trade-off of map size must be made.
+
+# Block & Edge Numbering
+Since the original AFL, blocks and edges have always been numbered in the same
+way as we can see from the following C snippet from the whitepaper.
+
+```c
+    cur_location = (block_address >> 4) ^ (block_address << 8);
+    shared_mem[cur_location ^ prev_location]++;
+    prev_location = cur_location >> 1;
+
+```
+
+Each block ID is generated by performing a shift and XOR on its address. Then
+the edge ID is calculated as `E = B ^ (B' >> 1)`. Here, we can make two
+observations. In fact, the edge ID is also masked to ensure it is less than the
+size of the map being used.
+
+## Block IDs
+Firstly, the block ID doesn't have very good entropy. If we consider the address
+of the block, then whilst each block has a unique ID, it isn't necessarily very
+evenly distributed.
+
+We start with a large address, and need to discard a large number of the bits to
+generate a block ID which is within range. But how do we choose the unique bits
+of the address verus those which are the same for every block? The high bits of
+the address may simply be all `0s` or all `1s` to make the address cannonical,
+the middle portion of the address may be the same for all blocks (since if they
+are all within the same binary, then they will all be adjacent in memory), and
+on some systems, even the low bits may have poor entropy as some use fixed
+length aligned instructions. Then we need to consider that a portion of each
+binary may contain the `.data` or `.bss` sections and so may not contain any
+blocks of code at all.
+
+## Edge IDs
+Secondly, we can observe that when we generate an edge ID from the source and
+destination block IDs, we perform a right shift on the source block ID. Whilst
+there are good reasons as set out in the whitepaper why such a transform is
+applied, in so doing, we dispose of `1` bit of precious entropy in our source
+block ID.
+
+All together, this means that some edge IDs may be more popular than others.
+This means that some portions of the map may be very densly populated with large
+numbers of edges, whilst others may be very sparsely populated, or not populated
+at all.
+
+# Improvements
+One of the main reaons why this algorithm selected, is performance. All of the
+operations are very quick to perform and given we may be carrying this out for
+every block of code we execute, performance is critical.
+
+However, the design of the binary instrumentation modes of AFL++ has moved on.
+Both QEMU and FRIDA modes use a two stage process when executing a target
+application. Each block is first compiled or instrumented, and then it is
+executed. The compiled blocks can be re-used each time the target executes them.
+
+Since a blocks ID is based on its address, and this is known at compile time, we
+only need to generate this ID once per block and so this ID generation no longer
+needs to be as performant. We can therefore use a hash algorithm to generate
+this ID and therefore ensure that the block IDs are more evenly distributed.
+
+Edge IDs however, can only be determined at run-time. Since we don't know which
+blocks a given input will traverse until we run it. However, given our block IDs
+are now evenly distributed, generating an evenly distributed edge ID becomes
+simple. Here, the only change we make is to use a rotate operation rather than
+a shift operation so we don't lose a bit of entropy from the source ID.
+
+So our new algorithm becomes:
+```c
+    cur_location = hash(block_address)
+    shared_mem[cur_location ^ prev_location]++;
+    prev_location = rotate(cur_location, 1);
+```
+
+Lastly, in the original design, the `cur_location` was always set to `0`, at the
+beginning of a run, we instead set the value of `cur_location` to `hash(0)`.
+
+# Parallel Fuzzing
+Another sub-optimal aspect of the original design is that no matter how many
+instances of the fuzzer you ran in parallel, each instance numbered each block
+and so each edge with the same ID. Each instance would therefore find the same
+subset of edges collide with each other. In the event of a collision, all
+instances will hit the same road block.
+
+However, if we instead use a different seed for our hashing function for each
+instance, then each will ascribe each block a different ID and hence each edge
+will be given a different edge ID. This means that whilst one instance of the
+fuzzer may find a given pair of edges collide, it is very unlikely that another
+instance will find the same pair also collide.
+
+Due to the collaborative nature of parallel fuzzing, this means that whilst one
+instance may struggle to find a particular new path because the new edge
+collides, another instance will likely not encounter the same collision and thus
+be able to differentiate this new path and share it with the other instances.
+
+If only a single new edge is found, and the new path is shared with an instance
+for which that edge collides, that instance may disregard it as irrelevant. In
+practice, however, the discovery of a single new edge, likely leads to several
+more edges beneath it also being found and therefore the likelihood of all of
+these being collisions is very slim.
diff --git a/frida_mode/README.md b/frida_mode/README.md
index 6bed52b7..6cbb4c4c 100644
--- a/frida_mode/README.md
+++ b/frida_mode/README.md
@@ -153,6 +153,9 @@ Generated block 0x7ffff75e98e2
 
 ***
 ```
+* `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_OPTIMIZE` - Don't use optimized inline assembly coverage
 instrumentation (the default where available). Required to use
 `AFL_FRIDA_INST_TRACE`.
@@ -160,7 +163,7 @@ instrumentation (the default where available). Required to use
 report instrumented blocks back to the parent so that it can also instrument
 them and they be inherited by the next child on fork.
 * `AFL_FRIDA_INST_TRACE` - Log to stdout the address of executed blocks,
-requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
+implies `AFL_FRIDA_INST_NO_OPTIMIZE`.
 * `AFL_FRIDA_INST_TRACE_UNIQUE` - As per `AFL_FRIDA_INST_TRACE`, but each edge
 is logged only once, requires `AFL_FRIDA_INST_NO_OPTIMIZE`.
 * `AFL_FRIDA_OUTPUT_STDOUT` - Redirect the standard output of the target
@@ -290,6 +293,10 @@ FASAN then adds instrumentation for any instrucutions which use memory operands
 then calls into the `__asan_loadN` and `__asan_storeN` functions provided by the DSO
 to validate memory accesses against the shadow memory.
 
+# Collisions
+FRIDA mode has also introduced some improvements to reduce collisions in the map.
+See [here](MapDensity.md) for details.
+
 ## TODO
 
 The next features to be added are Aarch32 support as well as looking at
diff --git a/frida_mode/Scripting.md b/frida_mode/Scripting.md
index 4c6fe6b2..5467db99 100644
--- a/frida_mode/Scripting.md
+++ b/frida_mode/Scripting.md
@@ -605,6 +605,19 @@ difficult to diagnose. The code above only prints the instructions when running
 in the parent process (the one provided by `Process.id` when the JS script is
 executed).
 
+# OSX
+Note that the JavaScript debug symbol api for OSX makes use of the
+`CoreSymbolication` APIs and as such the `CoreFoundation` module must be loaded
+into the target to make use of it. This can be done by setting:
+
+```
+AFL_PRELOAD=/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
+```
+
+It should be noted that `CoreSymbolication` API may take a while to initialize
+and build its caches. For this reason, it may be nescessary to also increase the
+value of the `-t` flag passed to `afl-fuzz`.
+
 # API
 ```js
 class Afl {
diff --git a/frida_mode/frida.map b/frida_mode/frida.map
new file mode 100644
index 00000000..8fc0b174
--- /dev/null
+++ b/frida_mode/frida.map
@@ -0,0 +1,34 @@
+{
+  global:
+    __afl_fuzz_len;
+    __afl_fuzz_ptr;
+    __afl_sharedmem_fuzzing;
+    afl_frida_start;
+    js_api_add_exclude_range;
+    js_api_add_include_range;
+    js_api_done;
+    js_api_error;
+    js_api_set_debug_maps;
+    js_api_set_entrypoint;
+    js_api_set_instrument_debug_file;
+    js_api_set_instrument_jit;
+    js_api_set_instrument_libraries;
+    js_api_set_instrument_no_optimize;
+    js_api_set_instrument_trace;
+    js_api_set_instrument_trace_unique;
+    js_api_set_persistent_address;
+    js_api_set_persistent_count;
+    js_api_set_persistent_debug;
+    js_api_set_persistent_hook;
+    js_api_set_persistent_return;
+    js_api_set_prefetch_disable;
+    js_api_set_stalker_callback;
+    js_api_set_stats_file;
+    js_api_set_stats_interval;
+    js_api_set_stats_transitions;
+    js_api_set_stderr;
+    js_api_set_stdout;
+
+  local:
+    *;
+};
diff --git a/frida_mode/hook/frida_hook.c b/frida_mode/hook/frida_hook.c
new file mode 100644
index 00000000..3bfdb207
--- /dev/null
+++ b/frida_mode/hook/frida_hook.c
@@ -0,0 +1,64 @@
+/*
+ *
+ * Modify this file to set the right registers with the fuzz input and length.
+ * It is a good idea to check input_buf_len to be not larger than the
+ * destination buffer!
+ *
+ */
+
+#include <stdint.h>
+#include <string.h>
+
+#include "frida-gumjs.h"
+
+#if defined(__x86_64__)
+
+__attribute__((visibility("default"))) void afl_persistent_hook(
+    GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) {
+
+  // do a length check matching the target!
+
+  memcpy((void *)regs->rdi, input_buf, input_buf_len);
+  regs->rsi = input_buf_len;
+
+}
+
+#elif defined(__i386__)
+
+__attribute__((visibility("default"))) void afl_persistent_hook(
+    GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) {
+
+  // do a length check matching the target!
+
+  void **esp = (void **)regs->esp;
+  void * arg1 = esp[0];
+  void **arg2 = &esp[1];
+  memcpy(arg1, input_buf, input_buf_len);
+  *arg2 = (void *)input_buf_len;
+
+}
+
+#elif defined(__aarch64__)
+
+__attribute__((visibility("default"))) void afl_persistent_hook(
+    GumCpuContext *regs, uint8_t *input_buf, uint32_t input_buf_len) {
+
+  // do a length check matching the target!
+
+  memcpy((void *)regs->x[0], input_buf, input_buf_len);
+  regs->x[1] = input_buf_len;
+
+}
+
+#else
+  #pragma error "Unsupported architecture"
+#endif
+
+__attribute__((visibility("default"))) int afl_persistent_hook_init(void) {
+
+  // 1 for shared memory input (faster), 0 for normal input (you have to use
+  // read(), input_buf will be NULL)
+  return 1;
+
+}
+
diff --git a/frida_mode/hook/hook.c b/frida_mode/hook/hook.c
deleted file mode 100644
index 7d08101f..00000000
--- a/frida_mode/hook/hook.c
+++ /dev/null
@@ -1,50 +0,0 @@
-#include <stdint.h>
-#include <string.h>
-
-#include "frida-gumjs.h"
-
-#if defined(__x86_64__)
-
-void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
-                         uint32_t input_buf_len) {
-
-  memcpy((void *)regs->rdi, input_buf, input_buf_len);
-  regs->rsi = input_buf_len;
-
-}
-
-#elif defined(__i386__)
-
-void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
-                         uint32_t input_buf_len) {
-
-  void **esp = (void **)regs->esp;
-  void * arg1 = esp[0];
-  void **arg2 = &esp[1];
-  memcpy(arg1, input_buf, input_buf_len);
-  *arg2 = (void *)input_buf_len;
-
-}
-
-#elif defined(__aarch64__)
-
-void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
-                         uint32_t input_buf_len) {
-
-  memcpy((void *)regs->x[0], input_buf, input_buf_len);
-  regs->x[1] = input_buf_len;
-
-}
-
-#else
-  #pragma error "Unsupported architecture"
-#endif
-
-int afl_persistent_hook_init(void) {
-
-  // 1 for shared memory input (faster), 0 for normal input (you have to use
-  // read(), input_buf will be NULL)
-  return 1;
-
-}
-
diff --git a/frida_mode/hook/qemu_hook.c b/frida_mode/hook/qemu_hook.c
new file mode 100644
index 00000000..56e787e3
--- /dev/null
+++ b/frida_mode/hook/qemu_hook.c
@@ -0,0 +1,195 @@
+#include <stdint.h>
+#include <string.h>
+
+#if defined(__x86_64__)
+
+struct x86_64_regs {
+
+  uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
+      r15;
+
+  union {
+
+    uint64_t rip;
+    uint64_t pc;
+
+  };
+
+  union {
+
+    uint64_t rsp;
+    uint64_t sp;
+
+  };
+
+  union {
+
+    uint64_t rflags;
+    uint64_t flags;
+
+  };
+
+  uint8_t zmm_regs[32][64];
+
+};
+
+void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  (void)guest_base; /* unused */
+  memcpy((void *)regs->rdi, input_buf, input_buf_len);
+  regs->rsi = input_buf_len;
+
+}
+
+#elif defined(__i386__)
+
+struct x86_regs {
+
+  uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
+
+  union {
+
+    uint32_t eip;
+    uint32_t pc;
+
+  };
+
+  union {
+
+    uint32_t esp;
+    uint32_t sp;
+
+  };
+
+  union {
+
+    uint32_t eflags;
+    uint32_t flags;
+
+  };
+
+  uint8_t xmm_regs[8][16];
+
+};
+
+void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  (void)guest_base; /* unused */
+  void **esp = (void **)regs->esp;
+  void * arg1 = esp[1];
+  void **arg2 = &esp[2];
+  memcpy(arg1, input_buf, input_buf_len);
+  *arg2 = (void *)input_buf_len;
+
+}
+#elif defined(__aarch64__)
+
+struct arm64_regs {
+
+  uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10;
+
+  union {
+
+    uint64_t x11;
+    uint32_t fp_32;
+
+  };
+
+  union {
+
+    uint64_t x12;
+    uint32_t ip_32;
+
+  };
+
+  union {
+
+    uint64_t x13;
+    uint32_t sp_32;
+
+  };
+
+  union {
+
+    uint64_t x14;
+    uint32_t lr_32;
+
+  };
+
+  union {
+
+    uint64_t x15;
+    uint32_t pc_32;
+
+  };
+
+  union {
+
+    uint64_t x16;
+    uint64_t ip0;
+
+  };
+
+  union {
+
+    uint64_t x17;
+    uint64_t ip1;
+
+  };
+
+  uint64_t x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
+
+  union {
+
+    uint64_t x29;
+    uint64_t fp;
+
+  };
+
+  union {
+
+    uint64_t x30;
+    uint64_t lr;
+
+  };
+
+  union {
+
+    uint64_t x31;
+    uint64_t sp;
+
+  };
+
+  // the zero register is not saved here ofc
+
+  uint64_t pc;
+
+  uint32_t cpsr;
+
+  uint8_t  vfp_zregs[32][16 * 16];
+  uint8_t  vfp_pregs[17][32];
+  uint32_t vfp_xregs[16];
+
+};
+
+void afl_persistent_hook(struct arm64_regs *regs, uint64_t guest_base,
+                         uint8_t *input_buf, uint32_t input_buf_len) {
+
+  (void)guest_base; /* unused */
+  memcpy((void *)regs->x0, input_buf, input_buf_len);
+  regs->x1 = input_buf_len;
+}
+
+#else
+  #pragma error "Unsupported architecture"
+#endif
+
+int afl_persistent_hook_init(void) {
+
+  // 1 for shared memory input (faster), 0 for normal input (you have to use
+  // read(), input_buf will be NULL)
+  return 1;
+
+}
diff --git a/frida_mode/include/entry.h b/frida_mode/include/entry.h
index 801c2bbe..cbc5c8c7 100644
--- a/frida_mode/include/entry.h
+++ b/frida_mode/include/entry.h
@@ -3,7 +3,8 @@
 
 #include "frida-gumjs.h"
 
-extern guint64 entry_point;
+extern guint64  entry_point;
+extern gboolean entry_reached;
 
 void entry_config(void);
 
diff --git a/frida_mode/include/instrument.h b/frida_mode/include/instrument.h
index 9c8d3a5d..695b46af 100644
--- a/frida_mode/include/instrument.h
+++ b/frida_mode/include/instrument.h
@@ -5,11 +5,12 @@
 
 #include "config.h"
 
-extern char *            instrument_debug_filename;
-extern gboolean          instrument_tracing;
-extern gboolean          instrument_optimize;
-extern gboolean          instrument_unique;
-extern __thread uint64_t instrument_previous_pc;
+extern char *           instrument_debug_filename;
+extern gboolean         instrument_tracing;
+extern gboolean         instrument_optimize;
+extern gboolean         instrument_unique;
+extern __thread guint64 instrument_previous_pc;
+extern guint64          instrument_hash_zero;
 
 extern uint8_t *__afl_area_ptr;
 extern uint32_t __afl_map_size;
@@ -33,5 +34,10 @@ void     instrument_debug_instruction(uint64_t address, uint16_t size);
 void     instrument_debug_end(GumStalkerOutput *output);
 void     instrument_flush(GumStalkerOutput *output);
 gpointer instrument_cur(GumStalkerOutput *output);
+
+void instrument_on_fork();
+
+guint64 instrument_get_offset_hash(GumAddress current_rip);
+
 #endif
 
diff --git a/frida_mode/include/persistent.h b/frida_mode/include/persistent.h
index 8f00196c..c79f0143 100644
--- a/frida_mode/include/persistent.h
+++ b/frida_mode/include/persistent.h
@@ -30,7 +30,10 @@ void persistent_init(void);
 gboolean persistent_is_supported(void);
 
 void persistent_prologue(GumStalkerOutput *output);
+void persistent_prologue_arch(GumStalkerOutput *output);
+
 void persistent_epilogue(GumStalkerOutput *output);
+void persistent_epilogue_arch(GumStalkerOutput *output);
 
 #endif
 
diff --git a/frida_mode/include/ranges.h b/frida_mode/include/ranges.h
index a667fb76..2eb9b355 100644
--- a/frida_mode/include/ranges.h
+++ b/frida_mode/include/ranges.h
@@ -5,6 +5,7 @@
 
 extern gboolean ranges_debug_maps;
 extern gboolean ranges_inst_libs;
+extern gboolean ranges_inst_jit;
 
 void ranges_config(void);
 void ranges_init(void);
diff --git a/frida_mode/include/stalker.h b/frida_mode/include/stalker.h
index 2136fe52..b5e05d5a 100644
--- a/frida_mode/include/stalker.h
+++ b/frida_mode/include/stalker.h
@@ -7,6 +7,7 @@ void        stalker_config(void);
 void        stalker_init(void);
 GumStalker *stalker_get(void);
 void        stalker_start(void);
+void        stalker_trust(void);
 
 #endif
 
diff --git a/frida_mode/include/stats.h b/frida_mode/include/stats.h
index 1cfd6b8f..cd2350ea 100644
--- a/frida_mode/include/stats.h
+++ b/frida_mode/include/stats.h
@@ -28,6 +28,7 @@ gboolean stats_is_supported_arch(void);
 size_t   stats_data_size_arch(void);
 void     stats_collect_arch(const cs_insn *instr);
 void     stats_write_arch(void);
+void     stats_on_fork(void);
 
 #endif
 
diff --git a/frida_mode/many-linux/Dockerfile b/frida_mode/many-linux/Dockerfile
new file mode 100644
index 00000000..2cd56bc8
--- /dev/null
+++ b/frida_mode/many-linux/Dockerfile
@@ -0,0 +1,24 @@
+FROM fridadotre/manylinux-x86_64
+
+COPY realpath /bin/realpath
+RUN chmod +x /bin/realpath
+
+RUN yum -y install xz
+RUN yum -y install vim-common
+
+WORKDIR /
+RUN git clone https://github.com/AFLplusplus/AFLplusplus.git
+
+WORKDIR /AFLplusplus
+RUN mkdir -p /AFLplusplus/frida_mode/build/frida/
+RUN curl -L -o /AFLplusplus/frida_mode/build/frida/frida-gumjs-devkit-15.0.0-linux-x86_64.tar.xz "https://github.com/frida/frida/releases/download/15.0.0/frida-gumjs-devkit-15.0.0-linux-x86_64.tar.xz"
+
+WORKDIR /AFLplusplus
+RUN git checkout dev
+WORKDIR /AFLplusplus/frida_mode
+ENV CFLAGS="\
+    -DADDR_NO_RANDOMIZE=0x0040000 \
+    -Wno-implicit-function-declaration \
+    "
+ENV CXX=$CC
+RUN make
diff --git a/frida_mode/many-linux/GNUmakefile b/frida_mode/many-linux/GNUmakefile
new file mode 100644
index 00000000..2860f20c
--- /dev/null
+++ b/frida_mode/many-linux/GNUmakefile
@@ -0,0 +1,21 @@
+PWD:=$(shell pwd)/
+BUILD_DIR:=$(PWD)build/
+
+.PHONY: all clean shell
+
+all: | $(BUILD_DIR)
+	docker build --tag many-afl-frida .
+	docker run --rm \
+		-v $(PWD)build/:/export \
+		many-afl-frida \
+		cp /AFLplusplus/afl-frida-trace.so /export
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+clean:
+	rm -rf $(BUILD_DIR)
+	docker images --filter 'dangling=true' -q --no-trunc | xargs -L1 docker rmi --force
+
+shell:
+	docker run -ti --rm many-afl-frida /bin/bash
diff --git a/frida_mode/many-linux/Makefile b/frida_mode/many-linux/Makefile
new file mode 100644
index 00000000..f3c3cd55
--- /dev/null
+++ b/frida_mode/many-linux/Makefile
@@ -0,0 +1,9 @@
+all:
+	@echo trying to use GNU make...
+	@gmake all || echo please install GNUmake
+
+clean:
+	@gmake clean
+
+shell:
+	@gmake shell
diff --git a/frida_mode/many-linux/README.md b/frida_mode/many-linux/README.md
new file mode 100644
index 00000000..2c7b6823
--- /dev/null
+++ b/frida_mode/many-linux/README.md
@@ -0,0 +1,8 @@
+# many-linux
+
+This folder contains a Docker image to allow the building of
+`afl-frida-trace.so` using the `many-linux` docker image. This docker image is
+based on CentOS Linux 5. By building `afl-frida-trace.so` for such an old
+version of Linux, given the strong backward compatibility of Linux, this should
+work on the majority of Linux environments. This may be useful for targetting
+Linux distributions other than your development environment.
\ No newline at end of file
diff --git a/frida_mode/many-linux/realpath b/frida_mode/many-linux/realpath
new file mode 100644
index 00000000..1fdc49a7
--- /dev/null
+++ b/frida_mode/many-linux/realpath
@@ -0,0 +1,2 @@
+#!/bin/sh
+readlink -f -- "$@"
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..e1dabf92 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,12 @@
 gboolean instrument_tracing = false;
 gboolean instrument_optimize = false;
 gboolean instrument_unique = false;
+guint64  instrument_hash_zero = 0;
+guint64  instrument_hash_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 +53,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 +80,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 +113,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 +167,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)) {
 
@@ -214,13 +238,15 @@ void instrument_init(void) {
 
   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;
 
   }
 
@@ -257,6 +283,19 @@ void instrument_init(void) {
 
   }
 
+  /*
+   * 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 +309,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..1d843024 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() {
@@ -222,6 +228,7 @@ 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.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
diff --git a/frida_mode/src/js/js.c b/frida_mode/src/js/js.c
index ed378d2c..86ae6d29 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,54 @@ 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);
 
@@ -119,4 +145,3 @@ gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
   return js_user_callback(insn, begin, excluded, output);
 
 }
-
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
index 91dccab2..fd8128c5 100644
--- a/frida_mode/src/js/js_api.c
+++ b/frida_mode/src/js/js_api.c
@@ -9,142 +9,183 @@
 #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_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..5520b73a 100644
--- a/frida_mode/src/stalker.c
+++ b/frida_mode/src/stalker.c
@@ -38,7 +38,10 @@ 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);
 
   /* *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 +62,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);
 
 }
 
diff --git a/frida_mode/test/deferred/GNUmakefile b/frida_mode/test/deferred/GNUmakefile
index ae580e3f..f7520051 100644
--- a/frida_mode/test/deferred/GNUmakefile
+++ b/frida_mode/test/deferred/GNUmakefile
@@ -10,7 +10,7 @@ TESTINSTSRC:=$(PWD)testinstr.c
 QEMU_OUT:=$(BUILD_DIR)qemu-out
 FRIDA_OUT:=$(BUILD_DIR)frida-out
 
-GET_SYMBOL_ADDR:=$(ROOT)frida_mode/test/png/persistent/get_symbol_addr.py
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
 
 ifndef ARCH
 
@@ -24,17 +24,21 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
+ifeq "$(shell uname)" "Darwin"
+TEST_BIN_LDFLAGS:=-Wl,-no_pie
+endif
+
 ARCH=$(shell uname -m)
 ifeq "$(ARCH)" "aarch64"
- AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000aaaaaaaaa000)
+ AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) run 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000555555554000)
+ AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) run 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x56555000)
+ AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) run 0x56555000)
 endif
 
 .PHONY: all clean frida
@@ -55,7 +59,7 @@ $(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
 	echo -n "000" > $@
 
 $(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
-	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+	$(CC) $(CFLAGS) $(LDFLAGS) $(TEST_BIN_LDFLAGS) -o $@ $<
 
 clean:
 	rm -rf $(BUILD_DIR)
diff --git a/frida_mode/test/deferred/testinstr.c b/frida_mode/test/deferred/testinstr.c
index 8b3688d7..c7a05ac5 100644
--- a/frida_mode/test/deferred/testinstr.c
+++ b/frida_mode/test/deferred/testinstr.c
@@ -51,7 +51,6 @@ int run(char *file) {
 
     fd = open(file, O_RDONLY);
     if (fd < 0) {
-
       perror("open");
       break;
 
diff --git a/frida_mode/test/entry_point/GNUmakefile b/frida_mode/test/entry_point/GNUmakefile
index c99bcecb..5453c1ad 100644
--- a/frida_mode/test/entry_point/GNUmakefile
+++ b/frida_mode/test/entry_point/GNUmakefile
@@ -10,7 +10,7 @@ TESTINSTSRC:=$(PWD)testinstr.c
 QEMU_OUT:=$(BUILD_DIR)qemu-out
 FRIDA_OUT:=$(BUILD_DIR)frida-out
 
-GET_SYMBOL_ADDR:=$(ROOT)frida_mode/test/png/persistent/get_symbol_addr.py
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
 
 ifndef ARCH
 
@@ -24,17 +24,21 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
+ifeq "$(shell uname)" "Darwin"
+TEST_BIN_LDFLAGS:=-Wl,-no_pie
+endif
+
 ARCH=$(shell uname -m)
 ifeq "$(ARCH)" "aarch64"
- AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000aaaaaaaaa000)
+ AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) run 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x0000555555554000)
+ AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) run 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x56555000)
+ AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) run 0x56555000)
 endif
 
 .PHONY: all clean qemu frida
@@ -55,7 +59,7 @@ $(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
 	echo -n "000" > $@
 
 $(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
-	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+	$(CC) $(CFLAGS) $(LDFLAGS) $(TEST_BIN_LDFLAGS) -o $@ $<
 
 clean:
 	rm -rf $(BUILD_DIR)
diff --git a/frida_mode/test/jpeg/GNUmakefile b/frida_mode/test/jpeg/GNUmakefile
index e3a8f321..1c124743 100644
--- a/frida_mode/test/jpeg/GNUmakefile
+++ b/frida_mode/test/jpeg/GNUmakefile
@@ -2,7 +2,7 @@ PWD:=$(shell pwd)/
 ROOT:=$(shell realpath $(PWD)../../..)/
 BUILD_DIR:=$(PWD)build/
 
-AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
 
 LIBJPEG_BUILD_DIR:=$(BUILD_DIR)libjpeg/
 HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
@@ -26,7 +26,7 @@ LDFLAGS += -lpthread
 
 TEST_BIN:=$(BUILD_DIR)test
 ifeq "$(shell uname)" "Darwin"
-TEST_BIN_LDFLAGS:=-undefined dynamic_lookup
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup -Wl,-no_pie
 endif
 
 TEST_DATA_DIR:=$(BUILD_DIR)in/
@@ -46,16 +46,18 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
 ifeq "$(ARCH)" "aarch64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x56555000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x56555000)
 endif
 
 .PHONY: all clean frida hook
@@ -77,7 +79,7 @@ $(HARNESS_FILE): | $(HARNESS_BUILD_DIR)
 	wget -O $@ $(HARNESS_URL)
 
 $(HARNESS_OBJ): $(HARNESS_FILE)
-	$(CC) $(CXXFLAGS) $(LDFLAGS) -o $@ -c $<
+	$(CC) $(CXXFLAGS) $(LDFLAGS) $(TEST_BIN_LDFLAGS) -o $@ -c $<
 
 ######### JPEGTEST ########
 
@@ -130,7 +132,7 @@ $(TEST_DATA_FILE): | $(TEST_DATA_DIR)
 clean:
 	rm -rf $(BUILD_DIR)
 
-frida: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE)
+frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE)
 	AFL_DEBUG_CHILD=1 \
 	AFL_DISABLE_TRIM=1 \
 	AFL_FRIDA_PERSISTENT_CNT=1000000 \
@@ -142,7 +144,7 @@ frida: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE)
 	AFL_SKIP_CPUFREQ=1 \
 	AFL_SKIP_CRASHES=1 \
 	AFL_TESTCACHE_SIZE=2 \
-	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+	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 \
diff --git a/frida_mode/test/js/GNUmakefile b/frida_mode/test/js/GNUmakefile
index af40c1c4..ee8d4ebc 100644
--- a/frida_mode/test/js/GNUmakefile
+++ b/frida_mode/test/js/GNUmakefile
@@ -13,7 +13,11 @@ TESTINSTSRC2:=$(PWD)test2.c
 QEMU_OUT:=$(BUILD_DIR)qemu-out
 FRIDA_OUT:=$(BUILD_DIR)frida-out
 
-.PHONY: all 32 clean qemu frida
+ifeq "$(shell uname)" "Darwin"
+AFL_PRELOAD=/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
+endif
+
+.PHONY: all 32 clean qemu frida debug
 
 all: $(TESTINSTBIN) $(TESTINSTBIN2)
 	make -C $(ROOT)frida_mode/
@@ -40,12 +44,14 @@ clean:
 	rm -rf $(BUILD_DIR)
 
 frida_js_entry: $(TESTINSTBIN) $(TEST_DATA_FILE)
+	AFL_PRELOAD=$(AFL_PRELOAD) \
 	AFL_FRIDA_JS_SCRIPT=entry.js \
 	$(ROOT)afl-fuzz \
 		-D \
 		-O \
 		-i $(TEST_DATA_DIR) \
 		-o $(FRIDA_OUT) \
+		-t 10000+ \
 		-- \
 			$(TESTINSTBIN) @@
 
@@ -78,3 +84,15 @@ frida_js_stalker: $(TESTINSTBIN2) $(TEST_DATA_FILE)
 		-o $(FRIDA_OUT) \
 		-- \
 			$(TESTINSTBIN2) @@
+
+debug: $(TEST_DATA_FILE)
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set environment AFL_FRIDA_JS_SCRIPT=entry.js' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TESTINSTBIN) $(TEST_DATA_FILE)
+
+strace: $(TEST_DATA_FILE)
+	LD_PRELOAD=$(ROOT)afl-frida-trace.so \
+	AFL_FRIDA_JS_SCRIPT=entry.js \
+	strace $(TESTINSTBIN) $(TEST_DATA_FILE)
diff --git a/frida_mode/test/js/entry.js b/frida_mode/test/js/entry.js
index f10ef2d1..2bdd7d13 100644
--- a/frida_mode/test/js/entry.js
+++ b/frida_mode/test/js/entry.js
@@ -9,12 +9,18 @@ new ModuleMap().values().forEach(m => {
     Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`);
 });
 
-const entry_point = DebugSymbol.fromName('run');
-Afl.print(`entry_point: ${entry_point.address}`);
+const name = Process.enumerateModules()[0].name;
+Afl.print(`Name: ${name}`);
 
-Afl.setEntryPoint(entry_point.address);
+if (name === 'test') {
 
-// Afl.error('HARD NOPE');
+    Afl.print('Searching...\n');
+    const entry_point = DebugSymbol.fromName('run');
+    Afl.print(`entry_point: ${entry_point}`);
+
+    Afl.setEntryPoint(entry_point.address);
+
+}
 
 Afl.done();
 Afl.print("done");
diff --git a/frida_mode/test/libpcap/GNUmakefile b/frida_mode/test/libpcap/GNUmakefile
index 8a10be07..f1ad06e4 100644
--- a/frida_mode/test/libpcap/GNUmakefile
+++ b/frida_mode/test/libpcap/GNUmakefile
@@ -2,7 +2,8 @@ PWD:=$(shell pwd)/
 ROOT:=$(shell realpath $(PWD)../../..)/
 BUILD_DIR:=$(PWD)build/
 
-AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
+AFLPP_QEMU_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/qemu_hook.so
 
 LIBPCAP_BUILD_DIR:=$(BUILD_DIR)libpcap/
 HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
@@ -34,7 +35,7 @@ LDFLAGS += -lpthread
 
 TEST_BIN:=$(BUILD_DIR)test
 ifeq "$(shell uname)" "Darwin"
-TEST_BIN_LDFLAGS:=-undefined dynamic_lookup
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup -Wl,-no_pie
 endif
 
 AFLPP_DRIVER_DUMMY_INPUT:=$(TCPDUMP_TESTS_DIR)in
@@ -54,18 +55,20 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
-AFL_QEMU_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x4000000000)
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+AFL_QEMU_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x4000000000)
 
 ifeq "$(ARCH)" "aarch64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x56555000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x56555000)
 endif
 
 .PHONY: all clean qemu frida hook
@@ -146,8 +149,8 @@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(TCPDUMP_TESTS_DIR)
 clean:
 	rm -rf $(BUILD_DIR)
 
-qemu: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | $(TCPDUMP_TESTS_DIR)
-	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+qemu: $(TEST_BIN) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | $(TCPDUMP_TESTS_DIR)
+	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_QEMU_DRIVER_HOOK_OBJ) \
 	AFL_ENTRYPOINT=$(AFL_QEMU_PERSISTENT_ADDR) \
 	AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \
 	AFL_QEMU_PERSISTENT_GPR=1 \
@@ -160,8 +163,8 @@ qemu: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | $(TCPDU
 		-- \
 			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
 
-frida: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | $(TCPDUMP_TESTS_DIR)
-	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | $(TCPDUMP_TESTS_DIR)
+	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 \
diff --git a/frida_mode/test/libpcap/get_symbol_addr.py b/frida_mode/test/libpcap/get_symbol_addr.py
deleted file mode 100755
index 1c46e010..00000000
--- a/frida_mode/test/libpcap/get_symbol_addr.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/python3
-import argparse
-from elftools.elf.elffile import ELFFile
-
-def process_file(file, symbol, base):
-    with open(file, 'rb') as f:
-        elf = ELFFile(f)
-        symtab = elf.get_section_by_name('.symtab')
-        mains = symtab.get_symbol_by_name(symbol)
-        if len(mains) != 1:
-            print ("Failed to find main")
-            return 1
-
-        main_addr = mains[0]['st_value']
-        main = base + main_addr
-        print ("0x%016x" % main)
-        return 0
-
-def hex_value(x):
-    return int(x, 16)
-
-def main():
-    parser = argparse.ArgumentParser(description='Process some integers.')
-    parser.add_argument('-f', '--file', dest='file', type=str,
-                    help='elf file name', required=True)
-    parser.add_argument('-s', '--symbol', dest='symbol', type=str,
-                    help='symbol name', required=True)
-    parser.add_argument('-b', '--base', dest='base', type=hex_value,
-                    help='elf base address', required=True)
-
-    args = parser.parse_args()
-    return process_file (args.file, args.symbol, args.base)
-
-if __name__ == "__main__":
-    ret = main()
-    exit(ret)
diff --git a/frida_mode/test/persistent_ret/GNUmakefile b/frida_mode/test/persistent_ret/GNUmakefile
index f11269e3..adcacf5a 100644
--- a/frida_mode/test/persistent_ret/GNUmakefile
+++ b/frida_mode/test/persistent_ret/GNUmakefile
@@ -22,20 +22,30 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+ifeq "$(shell uname)" "Darwin"
+TEST_BIN_LDFLAGS:=-Wl,-no_pie
+endif
+
 ARCH=$(shell uname -m)
 ifeq "$(ARCH)" "aarch64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TESTINSTBIN) -s main -b 0x0000aaaaaaaaa000)
- AFL_FRIDA_PERSISTENT_RET=$(shell $(PWD)get_symbol_addr.py -f $(TESTINSTBIN) -s slow -b 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) main 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_RET=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) slow 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TESTINSTBIN) -s main -b 0x0000555555554000)
- AFL_FRIDA_PERSISTENT_RET=$(shell $(PWD)get_symbol_addr.py -f $(TESTINSTBIN) -s slow -b 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) main 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_RET=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) slow 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TESTINSTBIN) -s main -b 0x56555000)
- AFL_FRIDA_PERSISTENT_RET=$(shell $(PWD)get_symbol_addr.py -f $(TESTINSTBIN) -s slow -b 0x56555000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) main 0x56555000)
+ AFL_FRIDA_PERSISTENT_RET=$(shell $(GET_SYMBOL_ADDR) $(TESTINSTBIN) slow 0x56555000)
+endif
+
+ifeq "$(shell uname)" "Darwin"
+AFL_PRELOAD=/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
 endif
 
 .PHONY: all 32 clean qemu frida
@@ -56,7 +66,7 @@ $(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
 	echo -n "000" > $@
 
 $(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
-	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+	$(CC) $(CFLAGS) $(LDFLAGS) $(TEST_BIN_LDFLAGS) -o $@ $<
 
 clean:
 	rm -rf $(BUILD_DIR)
@@ -83,6 +93,7 @@ frida_ret: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
 			$(TESTINSTBIN) @@
 
 frida_js: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+	AFL_PRELOAD=$(AFL_PRELOAD) \
 	AFL_FRIDA_JS_SCRIPT=test.js \
 	$(ROOT)afl-fuzz \
 		-D \
diff --git a/frida_mode/test/persistent_ret/get_symbol_addr.py b/frida_mode/test/persistent_ret/get_symbol_addr.py
deleted file mode 100755
index 1c46e010..00000000
--- a/frida_mode/test/persistent_ret/get_symbol_addr.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/python3
-import argparse
-from elftools.elf.elffile import ELFFile
-
-def process_file(file, symbol, base):
-    with open(file, 'rb') as f:
-        elf = ELFFile(f)
-        symtab = elf.get_section_by_name('.symtab')
-        mains = symtab.get_symbol_by_name(symbol)
-        if len(mains) != 1:
-            print ("Failed to find main")
-            return 1
-
-        main_addr = mains[0]['st_value']
-        main = base + main_addr
-        print ("0x%016x" % main)
-        return 0
-
-def hex_value(x):
-    return int(x, 16)
-
-def main():
-    parser = argparse.ArgumentParser(description='Process some integers.')
-    parser.add_argument('-f', '--file', dest='file', type=str,
-                    help='elf file name', required=True)
-    parser.add_argument('-s', '--symbol', dest='symbol', type=str,
-                    help='symbol name', required=True)
-    parser.add_argument('-b', '--base', dest='base', type=hex_value,
-                    help='elf base address', required=True)
-
-    args = parser.parse_args()
-    return process_file (args.file, args.symbol, args.base)
-
-if __name__ == "__main__":
-    ret = main()
-    exit(ret)
diff --git a/frida_mode/test/png/GNUmakefile b/frida_mode/test/png/GNUmakefile
index e05bade2..a1a7f1a5 100644
--- a/frida_mode/test/png/GNUmakefile
+++ b/frida_mode/test/png/GNUmakefile
@@ -22,7 +22,7 @@ PNGTEST_URL:="https://raw.githubusercontent.com/google/fuzzbench/master/benchmar
 
 TEST_BIN:=$(BUILD_DIR)test
 ifeq "$(shell uname)" "Darwin"
-TEST_BIN_LDFLAGS:=-undefined dynamic_lookup
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup -Wl,-no_pie
 endif
 
 TEST_DATA_DIR:=$(LIBPNG_DIR)contrib/pngsuite/
@@ -112,3 +112,9 @@ frida: $(TEST_BIN)
 		-o $(FRIDA_OUT) \
 		-- \
 			$(TEST_BIN) @@
+
+debug:
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TEST_BIN) $(TEST_DATA_DIR)basn0g01.png
diff --git a/frida_mode/test/png/Makefile b/frida_mode/test/png/Makefile
index 4bef1ccb..f843af19 100644
--- a/frida_mode/test/png/Makefile
+++ b/frida_mode/test/png/Makefile
@@ -14,3 +14,6 @@ qemu:
 
 frida:
 	@gmake frida
+
+debug:
+	@gmake debug
diff --git a/frida_mode/test/png/persistent/GNUmakefile b/frida_mode/test/png/persistent/GNUmakefile
index 5af64822..c1ad86e5 100644
--- a/frida_mode/test/png/persistent/GNUmakefile
+++ b/frida_mode/test/png/persistent/GNUmakefile
@@ -21,18 +21,20 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
-AFL_QEMU_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s main -b 0x4000000000)
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+AFL_QEMU_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) main 0x4000000000)
 
 ifeq "$(ARCH)" "arm64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s main -b 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) main 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s main -b 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) main 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s main -b 0x56555000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) main 0x56555000)
 endif
 
 .PHONY: all 32 clean qemu qemu_entry frida frida_entry
diff --git a/frida_mode/test/png/persistent/get_symbol_addr.py b/frida_mode/test/png/persistent/get_symbol_addr.py
deleted file mode 100755
index 1c46e010..00000000
--- a/frida_mode/test/png/persistent/get_symbol_addr.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/python3
-import argparse
-from elftools.elf.elffile import ELFFile
-
-def process_file(file, symbol, base):
-    with open(file, 'rb') as f:
-        elf = ELFFile(f)
-        symtab = elf.get_section_by_name('.symtab')
-        mains = symtab.get_symbol_by_name(symbol)
-        if len(mains) != 1:
-            print ("Failed to find main")
-            return 1
-
-        main_addr = mains[0]['st_value']
-        main = base + main_addr
-        print ("0x%016x" % main)
-        return 0
-
-def hex_value(x):
-    return int(x, 16)
-
-def main():
-    parser = argparse.ArgumentParser(description='Process some integers.')
-    parser.add_argument('-f', '--file', dest='file', type=str,
-                    help='elf file name', required=True)
-    parser.add_argument('-s', '--symbol', dest='symbol', type=str,
-                    help='symbol name', required=True)
-    parser.add_argument('-b', '--base', dest='base', type=hex_value,
-                    help='elf base address', required=True)
-
-    args = parser.parse_args()
-    return process_file (args.file, args.symbol, args.base)
-
-if __name__ == "__main__":
-    ret = main()
-    exit(ret)
diff --git a/frida_mode/test/png/persistent/hook/GNUmakefile b/frida_mode/test/png/persistent/hook/GNUmakefile
index 0ff9fe86..ddf63a96 100644
--- a/frida_mode/test/png/persistent/hook/GNUmakefile
+++ b/frida_mode/test/png/persistent/hook/GNUmakefile
@@ -2,7 +2,8 @@ PWD:=$(shell pwd)/
 ROOT:=$(shell realpath $(PWD)../../../../..)/
 BUILD_DIR:=$(PWD)build/
 
-AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
+AFLPP_QEMU_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/qemu_hook.so
 
 CFLAGS+=-O3 \
 		-funroll-loops \
@@ -31,18 +32,24 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
-AFL_QEMU_PERSISTENT_ADDR=$(shell $(PWD)../get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x4000000000)
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+AFL_QEMU_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x4000000000)
 
 ifeq "$(ARCH)" "arm64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)../get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)../get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)../get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x56555000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x56555000)
+endif
+
+ifeq "$(shell uname)" "Darwin"
+AFL_PRELOAD=/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
 endif
 
 .PHONY: all 32 clean format qemu qemu_entry frida frida_entry debug
@@ -67,8 +74,8 @@ $(TEST_DATA_DIR): | $(BUILD_DIR)
 $(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR)
 	truncate -s 1M $@
 
-qemu: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
-	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+qemu: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
+	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_QEMU_DRIVER_HOOK_OBJ) \
 	AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \
 	AFL_QEMU_PERSISTENT_GPR=1 \
 	$(ROOT)/afl-fuzz \
@@ -80,8 +87,8 @@ qemu: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
 		-- \
 			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
 
-qemu_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
-	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+qemu_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
+	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_QEMU_DRIVER_HOOK_OBJ) \
 	AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \
 	AFL_ENTRYPOINT=$(AFL_QEMU_PERSISTENT_ADDR) \
 	AFL_QEMU_PERSISTENT_GPR=1 \
@@ -94,8 +101,8 @@ qemu_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
 		-- \
 			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
 
-frida: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
-	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+frida: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
+	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \
 	AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
 	$(ROOT)afl-fuzz \
 		-D \
@@ -107,8 +114,8 @@ frida: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
 			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
 
 
-frida_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
-	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+frida_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
+	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 \
@@ -120,7 +127,8 @@ frida_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
 		-- \
 			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
 
-frida_js_load: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
+frida_js_load: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
+	AFL_PRELOAD=$(AFL_PRELOAD) \
 	AFL_FRIDA_JS_SCRIPT=load.js \
 	$(ROOT)afl-fuzz \
 		-D \
@@ -128,10 +136,12 @@ frida_js_load: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DI
 		-O \
 		-i $(TEST_DATA_DIR) \
 		-o $(FRIDA_OUT) \
+		-t 10000+ \
 		-- \
 			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
 
-frida_js_cmodule: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
+frida_js_cmodule: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
+	AFL_PRELOAD=$(AFL_PRELOAD) \
 	AFL_FRIDA_JS_SCRIPT=cmodule.js \
 	$(ROOT)afl-fuzz \
 		-D \
@@ -146,7 +156,7 @@ debug: $(AFLPP_DRIVER_DUMMY_INPUT)
 	echo $(AFL_FRIDA_PERSISTENT_ADDR)
 	gdb \
 		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
-		--ex 'set environment AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ)' \
+		--ex 'set environment AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ)' \
 		--ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR)' \
 		--ex 'set disassembly-flavor intel' \
 		--args $(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
diff --git a/frida_mode/test/png/persistent/hook/load.js b/frida_mode/test/png/persistent/hook/load.js
index ce4374ae..ea4d28c3 100644
--- a/frida_mode/test/png/persistent/hook/load.js
+++ b/frida_mode/test/png/persistent/hook/load.js
@@ -19,7 +19,7 @@ Afl.setPersistentAddress(persistent_addr);
 
 const path = Afl.module.path;
 const dir = path.substring(0, path.lastIndexOf("/"));
-const mod = Module.load(`${dir}/frida_mode/build/hook.so`);
+const mod = Module.load(`${dir}/frida_mode/build/frida_hook.so`);
 const hook = mod.getExportByName('afl_persistent_hook');
 Afl.setPersistentHook(hook);
 
diff --git a/frida_mode/test/proj4/GNUmakefile b/frida_mode/test/proj4/GNUmakefile
index e324a5d0..8555ebad 100644
--- a/frida_mode/test/proj4/GNUmakefile
+++ b/frida_mode/test/proj4/GNUmakefile
@@ -2,7 +2,7 @@ PWD:=$(shell pwd)/
 ROOT:=$(shell realpath $(PWD)../../..)/
 BUILD_DIR:=$(PWD)build/
 
-AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
 
 LIBPROJ4_BUILD_DIR:=$(BUILD_DIR)libproj4/
 HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
@@ -26,7 +26,7 @@ LDFLAGS += -lpthread
 
 TEST_BIN:=$(BUILD_DIR)test
 ifeq "$(shell uname)" "Darwin"
-TEST_BIN_LDFLAGS:=-undefined dynamic_lookup
+TEST_BIN_LDFLAGS:=-undefined dynamic_lookup -Wl,-no_pie
 endif
 
 TEST_DATA_DIR:=$(BUILD_DIR)in/
@@ -46,16 +46,18 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
 ifeq "$(ARCH)" "aarch64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x56555000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x56555000)
 endif
 
 .PHONY: all clean frida hook
@@ -130,7 +132,7 @@ $(TEST_DATA_FILE): | $(TEST_DATA_DIR)
 clean:
 	rm -rf $(BUILD_DIR)
 
-frida: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE)
+frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE)
 	AFL_DEBUG_CHILD=1 \
 	AFL_DISABLE_TRIM=1 \
 	AFL_FRIDA_PERSISTENT_CNT=1000000 \
@@ -142,7 +144,7 @@ frida: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(TEST_DATA_FILE)
 	AFL_SKIP_CPUFREQ=1 \
 	AFL_SKIP_CRASHES=1 \
 	AFL_TESTCACHE_SIZE=2 \
-	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+	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 \
diff --git a/frida_mode/test/re2/GNUmakefile b/frida_mode/test/re2/GNUmakefile
index e1c5347d..ce95df3b 100644
--- a/frida_mode/test/re2/GNUmakefile
+++ b/frida_mode/test/re2/GNUmakefile
@@ -2,7 +2,8 @@ PWD:=$(shell pwd)/
 ROOT:=$(shell realpath $(PWD)../../..)/
 BUILD_DIR:=$(PWD)build/
 
-AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
+AFLPP_QEMU_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/qemu_hook.so
 
 LIBRE2_BUILD_DIR:=$(BUILD_DIR)libre2/
 HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
@@ -46,18 +47,20 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
-AFL_QEMU_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x4000000000)
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+AFL_QEMU_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x4000000000)
 
 ifeq "$(ARCH)" "aarch64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(TEST_BIN) -s LLVMFuzzerTestOneInput -b 0x56555000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x56555000)
 endif
 
 .PHONY: all clean qemu frida hook
@@ -128,8 +131,8 @@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(TEST_DATA_DIR)
 clean:
 	rm -rf $(BUILD_DIR)
 
-qemu: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT)
-	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+qemu: $(TEST_BIN) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT)
+	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_QEMU_DRIVER_HOOK_OBJ) \
 	AFL_ENTRYPOINT=$(AFL_QEMU_PERSISTENT_ADDR) \
 	AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \
 	AFL_QEMU_PERSISTENT_GPR=1 \
@@ -142,8 +145,8 @@ qemu: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT)
 		-- \
 			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
 
-frida: $(TEST_BIN) $(AFLPP_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT)
-	AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
+frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT)
+	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 \
diff --git a/frida_mode/test/re2/get_symbol_addr.py b/frida_mode/test/re2/get_symbol_addr.py
deleted file mode 100755
index 1c46e010..00000000
--- a/frida_mode/test/re2/get_symbol_addr.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/python3
-import argparse
-from elftools.elf.elffile import ELFFile
-
-def process_file(file, symbol, base):
-    with open(file, 'rb') as f:
-        elf = ELFFile(f)
-        symtab = elf.get_section_by_name('.symtab')
-        mains = symtab.get_symbol_by_name(symbol)
-        if len(mains) != 1:
-            print ("Failed to find main")
-            return 1
-
-        main_addr = mains[0]['st_value']
-        main = base + main_addr
-        print ("0x%016x" % main)
-        return 0
-
-def hex_value(x):
-    return int(x, 16)
-
-def main():
-    parser = argparse.ArgumentParser(description='Process some integers.')
-    parser.add_argument('-f', '--file', dest='file', type=str,
-                    help='elf file name', required=True)
-    parser.add_argument('-s', '--symbol', dest='symbol', type=str,
-                    help='symbol name', required=True)
-    parser.add_argument('-b', '--base', dest='base', type=hex_value,
-                    help='elf base address', required=True)
-
-    args = parser.parse_args()
-    return process_file (args.file, args.symbol, args.base)
-
-if __name__ == "__main__":
-    ret = main()
-    exit(ret)
diff --git a/frida_mode/test/sqlite/GNUmakefile b/frida_mode/test/sqlite/GNUmakefile
new file mode 100644
index 00000000..80e0a939
--- /dev/null
+++ b/frida_mode/test/sqlite/GNUmakefile
@@ -0,0 +1,166 @@
+PWD:=$(shell pwd)/
+ROOT:=$(shell realpath $(PWD)../../..)/
+BUILD_DIR:=$(PWD)build/
+
+SQLITE_BUILD_DIR:=$(BUILD_DIR)sqlite/
+SQLITE_BUILD_SRC_DIR:=$(SQLITE_BUILD_DIR)src/
+
+AFLPP_DRIVER:=$(ROOT)utils/aflpp_driver/libAFLQemuDriver.a
+
+AFLPP_DRIVER:=$(ROOT)utils/aflpp_driver/libAFLQemuDriver.a
+AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
+AFLPP_QEMU_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/qemu_hook.so
+
+
+CFLAGS += -fpermissive
+
+LDFLAGS += -lpthread
+
+TEST_BIN:=$(SQLITE_BUILD_DIR)ossfuzz
+SQLITE_TEST_DIR:=$(BUILD_DIR)in/
+AFLPP_DRIVER_DUMMY_INPUT:=$(SQLITE_TEST_DIR)in
+
+SQLITE_CFLAGS:= -DSQLITE_MAX_LENGTH=128000000 \
+                 -DSQLITE_MAX_SQL_LENGTH=128000000 \
+                 -DSQLITE_MAX_MEMORY=25000000 \
+                 -DSQLITE_PRINTF_PRECISION_LIMIT=1048576 \
+                 -DSQLITE_DEBUG=1 \
+                 -DSQLITE_MAX_PAGE_COUNT=16384
+
+QEMU_OUT:=$(BUILD_DIR)qemu-out
+FRIDA_OUT:=$(BUILD_DIR)frida-out
+
+ifndef ARCH
+
+ARCH=$(shell uname -m)
+ifeq "$(ARCH)" "aarch64"
+ ARCH:=arm64
+endif
+
+ifeq "$(ARCH)" "i686"
+ ARCH:=x86
+endif
+endif
+
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+AFL_QEMU_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x4000000000)
+
+ifeq "$(ARCH)" "aarch64"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000aaaaaaaaa000)
+endif
+
+ifeq "$(ARCH)" "x86_64"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x0000555555554000)
+endif
+
+ifeq "$(ARCH)" "x86"
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_BIN) LLVMFuzzerTestOneInput 0x56555000)
+endif
+
+.PHONY: all clean qemu frida hook sqlite
+
+all: $(TEST_BIN)
+	make -C $(ROOT)frida_mode/
+
+32:
+	CXXFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+########## SQLITE #######
+
+$(AFLPP_DRIVER):
+	make -C $(ROOT)
+
+$(SQLITE_BUILD_DIR): | $(BUILD_DIR)
+	mkdir $@
+
+$(SQLITE_BUILD_DIR)sqlite3.tar.gz: | $(SQLITE_BUILD_DIR)
+	curl 'https://sqlite.org/src/tarball/sqlite.tar.gz?r=c78cbf2e86850cc6' -o $@
+
+$(SQLITE_BUILD_SRC_DIR): $(SQLITE_BUILD_DIR)sqlite3.tar.gz
+	mkdir -p $@
+	tar xzvf $< --strip-components 1 -C $@
+
+$(SQLITE_TEST_DIR): | $(SQLITE_BUILD_SRC_DIR)
+	mkdir -p $@
+	find $(SQLITE_BUILD_SRC_DIR) -name "*.test" | xargs -L1 -I%% cp -v %% $@
+
+$(SQLITE_BUILD_SRC_DIR)Makefile: | $(SQLITE_BUILD_SRC_DIR)
+	cd $(SQLITE_BUILD_SRC_DIR) && \
+	CFLAGS="$(SQLITE_CFLAGS)" \
+	ASAN_OPTIONS=detect_leaks=0 \
+		./configure
+
+$(SQLITE_BUILD_SRC_DIR).libs/libsqlite3.so: $(SQLITE_BUILD_SRC_DIR)Makefile
+	CFLAGS="$(SQLITE_CFLAGS)" \
+	ASAN_OPTIONS=detect_leaks=0 \
+		make -C $(SQLITE_BUILD_SRC_DIR) -j $(shell nproc)
+
+$(SQLITE_BUILD_SRC_DIR)sqlite3.o: $(SQLITE_BUILD_SRC_DIR).libs/libsqlite3.so
+	CFLAGS="$(SQLITE_CFLAGS)" \
+	ASAN_OPTIONS=detect_leaks=0 \
+		make -C $(SQLITE_BUILD_SRC_DIR) -j $(shell nproc) sqlite3.c
+
+$(SQLITE_BUILD_DIR)ossfuzz.o: $(SQLITE_BUILD_SRC_DIR)sqlite3.o
+	$(CC) -I $(SQLITE_BUILD_SRC_DIR) -c $(SQLITE_BUILD_SRC_DIR)test/ossfuzz.c -o $@
+
+$(TEST_BIN): $(SQLITE_BUILD_DIR)ossfuzz.o
+	$(CXX) -o $(TEST_BIN) \
+		$(SQLITE_BUILD_DIR)ossfuzz.o \
+		$(SQLITE_BUILD_SRC_DIR)sqlite3.o \
+		$(AFLPP_DRIVER) \
+		-l pthread \
+		-l dl
+
+sqlite: $(SQLITE_TEST_DIR) $(TEST_BIN)
+
+########## DUMMY #######
+
+$(AFLPP_DRIVER_DUMMY_INPUT): | $(SQLITE_TEST_DIR)
+	truncate -s 1M $@
+
+###### TEST DATA #######
+
+clean:
+	rm -rf $(BUILD_DIR)
+
+qemu: $(TEST_BIN) $(AFLPP_QEMU_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | $(SQLITE_TEST_DIR)
+	AFL_QEMU_PERSISTENT_CNT=1000000 \
+	AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_QEMU_DRIVER_HOOK_OBJ) \
+	AFL_ENTRYPOINT=$(AFL_QEMU_PERSISTENT_ADDR) \
+	AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \
+	AFL_QEMU_PERSISTENT_GPR=1 \
+	$(ROOT)afl-fuzz \
+		-D \
+		-V 30 \
+		-Q \
+		-i $(SQLITE_TEST_DIR) \
+		-o $(QEMU_OUT) \
+		-- \
+			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
+
+frida: $(TEST_BIN) $(AFLPP_FRIDA_DRIVER_HOOK_OBJ) $(AFLPP_DRIVER_DUMMY_INPUT) | $(SQLITE_TEST_DIR)
+	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 \
+		-D \
+		-V 30 \
+		-O \
+		-i $(SQLITE_TEST_DIR) \
+		-o $(FRIDA_OUT) \
+		-- \
+			$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
+
+debug:
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set environment AFL_QEMU_DRIVER_NO_HOOK=1' \
+		--ex 'set disassembly-flavor intel' \
+		--ex 'b main' \
+		--ex 'r < $(SQLITE_TEST_DIR)0034ecacd5427aafc6b97413da2053b36de5059f' \
+		$(TEST_BIN)
diff --git a/frida_mode/test/sqlite/Makefile b/frida_mode/test/sqlite/Makefile
new file mode 100644
index 00000000..f83e2992
--- /dev/null
+++ b/frida_mode/test/sqlite/Makefile
@@ -0,0 +1,17 @@
+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
+
+debug:
+	@gmake debug
+
diff --git a/frida_mode/test/testinstr/GNUmakefile b/frida_mode/test/testinstr/GNUmakefile
index a35073ab..3701ddc8 100644
--- a/frida_mode/test/testinstr/GNUmakefile
+++ b/frida_mode/test/testinstr/GNUmakefile
@@ -53,6 +53,13 @@ frida: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
 			$(TESTINSTBIN) @@
 
 debug:
+	echo $(AFL_FRIDA_PERSISTENT_ADDR)
+	gdb \
+		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
+		--ex 'set disassembly-flavor intel' \
+		--args $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+
+debug:
 	gdb \
 		--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
 		--ex 'set disassembly-flavor intel' \
diff --git a/frida_mode/test/unstable/GNUmakefile b/frida_mode/test/unstable/GNUmakefile
index fed417a3..938d7c17 100644
--- a/frida_mode/test/unstable/GNUmakefile
+++ b/frida_mode/test/unstable/GNUmakefile
@@ -22,18 +22,20 @@ ifeq "$(ARCH)" "i686"
 endif
 endif
 
-AFL_QEMU_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x4000000000)
+GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
+
+AFL_QEMU_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(UNSTABLE_BIN) run_test 0x4000000000)
 
 ifeq "$(ARCH)" "aarch64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x0000aaaaaaaaa000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(UNSTABLE_BIN) run_test 0x0000aaaaaaaaa000)
 endif
 
 ifeq "$(ARCH)" "x86_64"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x0000555555554000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(UNSTABLE_BIN) run_test 0x0000555555554000)
 endif
 
 ifeq "$(ARCH)" "x86"
- AFL_FRIDA_PERSISTENT_ADDR=$(shell $(PWD)get_symbol_addr.py -f $(UNSTABLE_BIN) -s run_test -b 0x56555000)
+ AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(UNSTABLE_BIN) run_test 0x56555000)
 endif
 
 .PHONY: all 32 clean qemu frida
diff --git a/frida_mode/test/unstable/get_symbol_addr.py b/frida_mode/test/unstable/get_symbol_addr.py
deleted file mode 100755
index 1c46e010..00000000
--- a/frida_mode/test/unstable/get_symbol_addr.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/python3
-import argparse
-from elftools.elf.elffile import ELFFile
-
-def process_file(file, symbol, base):
-    with open(file, 'rb') as f:
-        elf = ELFFile(f)
-        symtab = elf.get_section_by_name('.symtab')
-        mains = symtab.get_symbol_by_name(symbol)
-        if len(mains) != 1:
-            print ("Failed to find main")
-            return 1
-
-        main_addr = mains[0]['st_value']
-        main = base + main_addr
-        print ("0x%016x" % main)
-        return 0
-
-def hex_value(x):
-    return int(x, 16)
-
-def main():
-    parser = argparse.ArgumentParser(description='Process some integers.')
-    parser.add_argument('-f', '--file', dest='file', type=str,
-                    help='elf file name', required=True)
-    parser.add_argument('-s', '--symbol', dest='symbol', type=str,
-                    help='symbol name', required=True)
-    parser.add_argument('-b', '--base', dest='base', type=hex_value,
-                    help='elf base address', required=True)
-
-    args = parser.parse_args()
-    return process_file (args.file, args.symbol, args.base)
-
-if __name__ == "__main__":
-    ret = main()
-    exit(ret)
diff --git a/frida_mode/ts/lib/afl.ts b/frida_mode/ts/lib/afl.ts
index 6da7fabc..67e21beb 100644
--- a/frida_mode/ts/lib/afl.ts
+++ b/frida_mode/ts/lib/afl.ts
@@ -120,6 +120,13 @@ class Afl {
   }
 
   /**
+   * See `AFL_FRIDA_INST_JIT`.
+   */
+  public static setInstrumentJit(): void {
+    Afl.jsApiSetInstrumentJit();
+  }
+
+  /**
    * See `AFL_INST_LIBS`.
    */
   public static setInstrumentLibraries(): void {
@@ -273,6 +280,11 @@ class Afl {
     "void",
     ["pointer"]);
 
+  private static readonly jsApiSetInstrumentJit = Afl.jsApiGetFunction(
+    "js_api_set_instrument_jit",
+    "void",
+    []);
+
   private static readonly jsApiSetInstrumentLibraries = Afl.jsApiGetFunction(
     "js_api_set_instrument_libraries",
     "void",
@@ -371,3 +383,5 @@ class Afl {
   }
 
 }
+
+export { Afl };
diff --git a/frida_mode/ts/package.json b/frida_mode/ts/package.json
index 47b693ed..191eb597 100644
--- a/frida_mode/ts/package.json
+++ b/frida_mode/ts/package.json
@@ -1,32 +1,32 @@
 {
-    "name": "@worksbutnottested/aflplusplus-frida",
-    "version": "1.0.0",
-    "description": "AFLplusplus Frida Mode",
-    "main": "./dist/frida.js",
-    "types": "./dist/frida.d.ts",
-    "files": [
-      "/dist/"
-    ],
-    "repository": {
-      "type": "git",
-      "url": "git@github.com:worksbutnottested/AFLplusplus.git"
-    },
-    "publishConfig": {
-      "cache": "~/.npm",
-      "registry": "https://npm.pkg.github.com/@worksbutnottested"
-    },
-    "scripts": {
-      "prepare": "npm run build",
-      "build": "tsc",
-      "lint": "tslint -p tslint.json"
-    },
-    "devDependencies": {
-      "@types/node": "^14.14.2",
-      "typescript": "^4.0.3",
-      "typescript-tslint-plugin": "^0.5.5",
-      "tslint": "^6.1.3"
-    },
-    "dependencies": {
-      "@types/frida-gum": "^16.2.0"
-    }
+  "name": "@worksbutnottested/aflplusplus-frida",
+  "version": "1.0.1",
+  "description": "AFLplusplus Frida Mode",
+  "main": "./dist/afl.js",
+  "types": "./dist/afl.d.ts",
+  "files": [
+    "/dist/"
+  ],
+  "repository": {
+    "type": "git",
+    "url": "git@github.com:worksbutnottested/AFLplusplus.git"
+  },
+  "publishConfig": {
+    "cache": "~/.npm",
+    "registry": "https://npm.pkg.github.com/@worksbutnottested"
+  },
+  "scripts": {
+    "prepare": "npm run build",
+    "build": "tsc",
+    "lint": "tslint -p tslint.json"
+  },
+  "devDependencies": {
+    "@types/node": "^14.14.2",
+    "typescript": "^4.0.3",
+    "typescript-tslint-plugin": "^0.5.5",
+    "tslint": "^6.1.3"
+  },
+  "dependencies": {
+    "@types/frida-gum": "^16.2.0"
   }
+}
diff --git a/frida_mode/util/bin2c.c b/frida_mode/util/bin2c.c
new file mode 100644
index 00000000..899d0101
--- /dev/null
+++ b/frida_mode/util/bin2c.c
@@ -0,0 +1,117 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+void fatal(char *msg) {
+
+  perror(msg);
+  exit(1);
+
+}
+
+void bin2c_write(char *name, char *output, unsigned char *buff, size_t size) {
+
+  int fd = open(output, O_CREAT | O_WRONLY | O_TRUNC, 00660);
+  if (fd < 0) { fatal("open"); }
+
+  /* Write the array definition */
+  dprintf(fd, "unsigned char %s[] = {\n", name);
+
+  /* 12 bytes per row, just like xxd means we fit an 80 character width */
+  for (size_t i = 0; i < size; i += 12) {
+
+    for (size_t j = 0; j < 12; j++) {
+
+      size_t idx = i + j;
+
+      /* If we get to the end of the input, then break */
+      if (idx >= size) { break; }
+
+      /* If we are writing the first column, then we need a leading indent */
+      if (j == 0) { dprintf(fd, "  "); }
+
+      /* Write the hexadecimal byte value */
+      dprintf(fd, "0x%02x", buff[idx]);
+
+      /* If we have just written the last byte, then stop */
+      if (idx == size - 1) { break; }
+
+      /*
+       * If we have written the last byte in a row, then follow with a comma
+       * and a newline
+       */
+      if (j == 11) {
+
+        dprintf(fd, ",\n");
+
+        /*
+         * Otherwise, follow with a command and a space
+         */
+
+      } else {
+
+        dprintf(fd, ", ");
+
+      }
+
+    }
+
+  }
+
+  /* Write the closing brace for the array */
+  dprintf(fd, "\n};\n");
+
+  /* Write a parameter describing the length of the array */
+  dprintf(fd, "unsigned int %s_len = %lu;\n", name, size);
+
+  if (close(fd) < 0) { fatal("close"); }
+
+}
+
+void bin2c(char *name, char *input, char *output) {
+
+  int fd = open(input, O_RDONLY);
+  if (fd < 0) { fatal("open(input)"); }
+
+  size_t size = lseek(fd, 0, SEEK_END);
+  if (size < 0) { fatal("lseek(SEEK_END)"); }
+
+  if (lseek(fd, 0, SEEK_SET) < 0) { fatal("lseek(SEEK_SET)"); }
+
+  unsigned char *buff = malloc(size);
+  if (buff == NULL) { fatal("malloc(size)"); }
+
+  if (read(fd, buff, size) != size) { fatal("read(size)"); }
+
+  bin2c_write(name, output, buff, size);
+
+  free(buff);
+  if (close(fd) < 0) { fatal("close(fd_in)"); }
+
+}
+
+int main(int argc, char **argv) {
+
+  if (argc < 4) {
+
+    dprintf(STDERR_FILENO, "%s <name> <input> <output>\n", argv[0]);
+    return 1;
+
+  }
+
+  char *name = argv[1];
+  char *input = argv[2];
+  char *output = argv[3];
+
+  dprintf(STDOUT_FILENO, "bin2c:\n");
+  dprintf(STDOUT_FILENO, "\tname: %s\n", name);
+  dprintf(STDOUT_FILENO, "\tinput: %s\n", input);
+  dprintf(STDOUT_FILENO, "\toutput: %s\n", output);
+
+  bin2c(name, input, output);
+
+  return 0;
+
+}
+
diff --git a/frida_mode/util/get_symbol_addr.sh b/frida_mode/util/get_symbol_addr.sh
new file mode 100755
index 00000000..f5d8df91
--- /dev/null
+++ b/frida_mode/util/get_symbol_addr.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+# Copyright 2020 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# set -x
+target="$1"
+symbol="$2"
+base="$3"
+
+test -z "$target" -o -z "$symbol" -o '!' -e "$target" && exit 0
+
+test $(uname -s) = "Darwin" && symbol=_"$symbol"
+
+file "$target" | grep -q executable && {
+  nm "$target" | grep -i "T $symbol" | awk '{print"0x"$1}'
+  exit 0
+}
+
+hex_base=$(echo "$3" | awk '{sub("^0x","");print $0}' | tr a-f A-F )
+nm "$target" | grep -i "T $symbol" | awk '{print$1}' | tr a-f A-F | \
+  xargs echo "ibase=16;obase=10;$hex_base + " | bc | tr A-F a-f | awk '{print "0x"$0}'
+exit 0