about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--frida_mode/GNUmakefile40
-rw-r--r--frida_mode/README.md4
-rw-r--r--frida_mode/Scripting.md240
-rw-r--r--frida_mode/include/asan.h3
-rw-r--r--frida_mode/include/ctx.h2
-rw-r--r--frida_mode/include/entry.h8
-rw-r--r--frida_mode/include/frida_cmplog.h1
-rw-r--r--frida_mode/include/instrument.h16
-rw-r--r--frida_mode/include/intercept.h11
-rw-r--r--frida_mode/include/interceptor.h11
-rw-r--r--frida_mode/include/js.h18
-rw-r--r--frida_mode/include/lib.h4
-rw-r--r--frida_mode/include/output.h6
-rw-r--r--frida_mode/include/persistent.h7
-rw-r--r--frida_mode/include/prefetch.h5
-rw-r--r--frida_mode/include/ranges.h9
-rw-r--r--frida_mode/include/stalker.h3
-rw-r--r--frida_mode/include/stats.h7
-rw-r--r--frida_mode/include/util.h2
-rw-r--r--frida_mode/src/asan/asan.c21
-rw-r--r--frida_mode/src/asan/asan_arm32.c2
-rw-r--r--frida_mode/src/asan/asan_arm64.c2
-rw-r--r--frida_mode/src/asan/asan_x64.c2
-rw-r--r--frida_mode/src/asan/asan_x86.c2
-rw-r--r--frida_mode/src/cmplog/cmplog.c16
-rw-r--r--frida_mode/src/cmplog/cmplog_arm32.c2
-rw-r--r--frida_mode/src/cmplog/cmplog_arm64.c2
-rw-r--r--frida_mode/src/cmplog/cmplog_x64.c2
-rw-r--r--frida_mode/src/cmplog/cmplog_x86.c2
-rw-r--r--frida_mode/src/ctx/ctx_arm32.c2
-rw-r--r--frida_mode/src/ctx/ctx_arm64.c2
-rw-r--r--frida_mode/src/ctx/ctx_x64.c2
-rw-r--r--frida_mode/src/ctx/ctx_x86.c2
-rw-r--r--frida_mode/src/entry.c19
-rw-r--r--frida_mode/src/instrument/instrument.c66
-rw-r--r--frida_mode/src/instrument/instrument_arm32.c2
-rw-r--r--frida_mode/src/instrument/instrument_arm64.c4
-rw-r--r--frida_mode/src/instrument/instrument_debug.c23
-rw-r--r--frida_mode/src/instrument/instrument_x64.c4
-rw-r--r--frida_mode/src/instrument/instrument_x86.c4
-rw-r--r--frida_mode/src/intercept.c (renamed from frida_mode/src/interceptor.c)12
-rw-r--r--frida_mode/src/js/api.js201
-rw-r--r--frida_mode/src/js/js.c113
-rw-r--r--frida_mode/src/js/js_api.c142
-rw-r--r--frida_mode/src/lib/lib.c6
-rw-r--r--frida_mode/src/lib/lib_apple.c6
-rw-r--r--frida_mode/src/main.c53
-rw-r--r--frida_mode/src/output.c28
-rw-r--r--frida_mode/src/persistent/persistent.c57
-rw-r--r--frida_mode/src/persistent/persistent_arm32.c2
-rw-r--r--frida_mode/src/persistent/persistent_arm64.c8
-rw-r--r--frida_mode/src/persistent/persistent_x64.c8
-rw-r--r--frida_mode/src/persistent/persistent_x86.c8
-rw-r--r--frida_mode/src/prefetch.c37
-rw-r--r--frida_mode/src/ranges.c120
-rw-r--r--frida_mode/src/stalker.c31
-rw-r--r--frida_mode/src/stats/stats.c33
-rw-r--r--frida_mode/src/stats/stats_arm32.c2
-rw-r--r--frida_mode/src/stats/stats_arm64.c2
-rw-r--r--frida_mode/src/stats/stats_x64.c2
-rw-r--r--frida_mode/src/stats/stats_x86.c2
-rw-r--r--frida_mode/test/deferred/GNUmakefile2
-rw-r--r--frida_mode/test/js/GNUmakefile44
-rw-r--r--frida_mode/test/js/Makefile16
-rw-r--r--frida_mode/test/js/test.js20
-rw-r--r--frida_mode/test/js/testinstr.c121
-rw-r--r--frida_mode/test/persistent_ret/GNUmakefile10
-rw-r--r--frida_mode/test/persistent_ret/test.js38
-rw-r--r--include/envs.h3
69 files changed, 1431 insertions, 276 deletions
diff --git a/frida_mode/GNUmakefile b/frida_mode/GNUmakefile
index 2f637412..fdacff62 100644
--- a/frida_mode/GNUmakefile
+++ b/frida_mode/GNUmakefile
@@ -6,6 +6,11 @@ INCLUDES:=$(wildcard $(INC_DIR)*.h)
 BUILD_DIR:=$(PWD)build/
 OBJ_DIR:=$(BUILD_DIR)obj/
 
+JS_DIR:=$(SRC_DIR)js/
+JS_NAME:=api.js
+JS:=$(JS_DIR)$(JS_NAME)
+JS_SRC:=$(BUILD_DIR)api.c
+JS_OBJ:=$(BUILD_DIR)api.o
 SOURCES:=$(wildcard $(SRC_DIR)**/*.c) $(wildcard $(SRC_DIR)*.c)
 OBJS:=$(foreach src,$(SOURCES),$(OBJ_DIR)$(notdir $(patsubst %.c, %.o, $(src))))
 CFLAGS+=-fPIC \
@@ -71,25 +76,25 @@ ifndef OS
 endif
 
 GUM_DEVKIT_VERSION=14.2.18
-GUM_DEVKIT_FILENAME=frida-gum-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar.xz
+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)"
 
 GUM_DEVKIT_TARBALL:=$(FRIDA_BUILD_DIR)$(GUM_DEVKIT_FILENAME)
-GUM_DEVIT_LIBRARY=$(FRIDA_BUILD_DIR)libfrida-gum.a
-GUM_DEVIT_HEADER=$(FRIDA_BUILD_DIR)frida-gum.h
+GUM_DEVIT_LIBRARY=$(FRIDA_BUILD_DIR)libfrida-gumjs.a
+GUM_DEVIT_HEADER=$(FRIDA_BUILD_DIR)frida-gumjs.h
 
 FRIDA_DIR:=$(PWD)build/frida-source/
 FRIDA_MAKEFILE:=$(FRIDA_DIR)Makefile
-FRIDA_GUM:=$(FRIDA_DIR)build/frida-linux-x86_64/lib/libfrida-gum-1.0.a
+FRIDA_GUM:=$(FRIDA_DIR)build/frida-linux-x86_64/lib/libfrida-gumjs-1.0.a
 FRIDA_GUM_DEVKIT_DIR:=$(FRIDA_DIR)build/gum-devkit/
-FRIDA_GUM_DEVKIT_HEADER:=$(FRIDA_GUM_DEVKIT_DIR)frida-gum.h
-FRIDA_GUM_DEVKIT_TARBALL:=$(FRIDA_DIR)build/frida-gum-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar
+FRIDA_GUM_DEVKIT_HEADER:=$(FRIDA_GUM_DEVKIT_DIR)frida-gumjs.h
+FRIDA_GUM_DEVKIT_TARBALL:=$(FRIDA_DIR)build/frida-gumjs-devkit-$(GUM_DEVKIT_VERSION)-$(OS)-$(ARCH).tar
 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
 
-.PHONY: all 32 clean format $(FRIDA_GUM)
+.PHONY: all 32 clean format $(FRIDA_GUM) quickjs
 
 ############################## ALL #############################################
 
@@ -113,7 +118,7 @@ $(FRIDA_GUM): $(FRIDA_MAKEFILE)
 	cd $(FRIDA_DIR) && make gum-linux-$(ARCH)
 
 $(FRIDA_GUM_DEVKIT_HEADER): $(FRIDA_GUM)
-	$(FRIDA_DIR)releng/devkit.py frida-gum linux-$(ARCH) $(FRIDA_DIR)build/gum-devkit/
+	$(FRIDA_DIR)releng/devkit.py frida-gumjs linux-$(ARCH) $(FRIDA_DIR)build/gum-devkit/
 
 $(FRIDA_GUM_DEVKIT_TARBALL): $(FRIDA_GUM_DEVKIT_HEADER)
 	cd $(FRIDA_GUM_DEVKIT_DIR) && tar cvf $(FRIDA_GUM_DEVKIT_TARBALL) .
@@ -150,6 +155,20 @@ $(AFL_COMPILER_RT_OBJ): $(AFL_COMPILER_RT_SRC)
 		-o $@ \
 		-c $<
 
+############################### JS #############################################
+
+$(JS_SRC): $(JS) | $(BUILD_DIR)
+	cd $(JS_DIR) && xxd -i $(JS_NAME) $@
+
+$(JS_OBJ): $(JS_SRC)
+	$(CC) \
+		$(CFLAGS) \
+		-I $(ROOT)include \
+		-I $(FRIDA_BUILD_DIR) \
+		-I $(INC_DIR) \
+		-c $< \
+		-o $@
+
 ############################# SOURCE ###########################################
 
 define BUILD_SOURCE
@@ -167,9 +186,10 @@ $(foreach src,$(SOURCES),$(eval $(call BUILD_SOURCE,$(src),$(OBJ_DIR)$(notdir $(
 
 ######################## AFL-FRIDA-TRACE #######################################
 
-$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(AFL_COMPILER_RT_OBJ) GNUmakefile | $(BUILD_DIR)
-	$(CC) \
+$(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL_COMPILER_RT_OBJ) GNUmakefile | $(BUILD_DIR)
+	$(CXX) \
 		$(OBJS) \
+		$(JS_OBJ) \
 		$(GUM_DEVIT_LIBRARY) \
 		$(AFL_COMPILER_RT_OBJ) \
 		$(LDFLAGS) \
diff --git a/frida_mode/README.md b/frida_mode/README.md
index 296e6405..6bed52b7 100644
--- a/frida_mode/README.md
+++ b/frida_mode/README.md
@@ -78,6 +78,10 @@ following options are currently supported:
 
 To enable the powerful CMPLOG mechanism, set `-c 0` for `afl-fuzz`.
 
+## Scripting
+
+One of the more powerful features of FRIDA mode is it's support for configuration by JavaScript, rather than using environment variables. For details of how this works see [here](Scripting.md).
+
 ## Performance
 
 Additionally, the intention is to be able to make a direct performance
diff --git a/frida_mode/Scripting.md b/frida_mode/Scripting.md
new file mode 100644
index 00000000..8b961e18
--- /dev/null
+++ b/frida_mode/Scripting.md
@@ -0,0 +1,240 @@
+# Scripting
+FRIDA now supports the ability to configure itself using JavaScript. This allows
+the user to make use of the convenience of FRIDA's scripting engine (along with
+it's support for debug symbols and exports) to configure all of the things which
+were traditionally configured using environment variables.
+
+By default FRIDA mode will look for the file `afl.js` in the current working
+directory of the target. Alternatively, a script file can be configured using
+the environment variable `AFL_FRIDA_JS_SCRIPT`.
+
+This script can make use of all of the standard [frida api functions](https://frida.re/docs/javascript-api/), but FRIDA mode adds some additional functions to allow
+you to interact with FRIDA mode itself. These can all be accessed via the global
+`Afl` parameter. e.g. `Afl.print("HELLO WORLD");`,
+
+If you encounter a problem with your script, then you should set the environment
+variable `AFL_DEBUG_CHILD=1` to view any diagnostic information.
+
+
+# Example
+Most of the time, users will likely be wanting to call the functions which configure an address (e.g. for the entry point, or the persistent address).
+
+The example below uses the API [`DebugSymbol.fromName()`](https://frida.re/docs/javascript-api/#debugsymbol). Another use API is [`Module.getExportByName()`](https://frida.re/docs/javascript-api/#module).
+
+```js
+/* Use Afl.print instead of console.log */
+Afl.print('******************');
+Afl.print('* AFL FRIDA MODE *');
+Afl.print('******************');
+Afl.print('');
+
+/* Print some useful diagnostics stuff */
+Afl.print(`PID: ${Process.id}`);
+
+new ModuleMap().values().forEach(m => {
+    Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`);
+});
+
+/*
+ * Configure entry-point, persistence etc. This will be what most
+ * people want to do.
+ */
+const persistent_addr = DebugSymbol.fromName('main');
+Afl.print(`persistent_addr: ${persistent_addr.address}`);
+
+if (persistent_addr.address.equals(ptr(0))) {
+    Afl.error('Cannot find symbol main');
+}
+
+const persistent_ret = DebugSymbol.fromName('slow');
+Afl.print(`persistent_ret: ${persistent_ret.address}`);
+
+if (persistent_ret.address.equals(ptr(0))) {
+    Afl.error('Cannot find symbol slow');
+}
+
+Afl.setPersistentAddress(persistent_addr.address);
+Afl.setPersistentReturn(persistent_ret.address);
+Afl.setPersistentCount(1000000);
+
+/* Control instrumentation, you may want to do this too */
+Afl.setInstrumentLibraries();
+const mod = Process.findModuleByName("libc-2.31.so")
+Afl.addExcludedRange(mod.base, mod.size);
+
+/* Some useful options to configure logging */
+Afl.setStdOut("/tmp/stdout.txt");
+Afl.setStdErr("/tmp/stderr.txt");
+
+/* Show the address layout. Sometimes helpful */
+Afl.setDebugMaps();
+
+/*
+ * If you are using these options, then things aren't going
+ * very well for you.
+ */
+Afl.setInstrumentDebugFile("/tmp/instr.log");
+Afl.setPrefetchDisable();
+Afl.setInstrumentNoOptimize();
+Afl.setInstrumentEnableTracing();
+Afl.setInstrumentTracingUnique();
+Afl.setStatsFile("/tmp/stats.txt");
+Afl.setStatsInterval(1);
+Afl.setStatsTransitions();
+
+/* *ALWAYS* call this when you have finished all your configuration */
+Afl.done();
+Afl.print("done");
+```
+
+# Stripped Binaries
+
+Lastly, if the binary you attempting to fuzz has no symbol information, and no
+exports, then the following approach can be used.
+
+```js
+const module = Process.getModuleByName('target.exe');
+/* Hardcoded offset within the target image */
+const address = module.base.add(0xdeadface);
+Afl.setPersistentAddress(address);
+```
+
+# API
+```js
+/*
+ * Print a message to the STDOUT. This should be preferred to
+ * FRIDA's `console.log` since FRIDA will queue it's log messages.
+ * If `console.log` is used in a callback in particular, then there
+ * may no longer be a thread running to service this queue.
+ */
+Afl.print(msg);
+
+/*
+ * This must always be called at the end of your script. This lets
+ * FRIDA mode know that your configuration is finished and that
+ * execution has reached the end of your script. Failure to call
+ * this will result in a fatal error.
+ */
+Afl.done();
+
+/*
+ * This function can be called within your script to cause FRIDA
+ * mode to trigger a fatal error. This is useful if for example you
+ * discover a problem you weren't expecting and want everything to
+ * stop. The user will need to enable `AFL_DEBUG_CHILD=1` to view
+ * this error message.
+ */
+Afl.error();
+
+/*
+ * This has the same effect as setting `AFL_ENTRYPOINT`, but has the
+ * convenience of allowing you to use FRIDAs APIs to determine the
+ * address you would like to configure, rather than having to grep
+ * the output of `readelf` or something similarly ugly. This
+ * function should be called with a `NativePointer` as its
+ * argument.
+ */
+Afl.setEntryPoint(address);
+
+/*
+ * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
+ * `NativePointer` should be provided as it's argument.
+ */
+Afl.setPersistentAddress(address);
+
+/*
+ * This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a
+ * `NativePointer` should be provided as it's argument.
+ */
+Afl.setPersistentReturn(address);
+
+/*
+ * This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
+ * `number` should be provided as it's argument.
+ */
+Afl.setPersistentCount(count);
+
+/*
+ * See `AFL_FRIDA_PERSISTENT_DEBUG`.
+ */
+Afl.setPersistentDebug();
+
+/*
+ * See `AFL_FRIDA_DEBUG_MAPS`.
+ */
+Afl.setDebugMaps();
+
+/*
+ * This is equivalent to setting a value in `AFL_FRIDA_INST_RANGES`,
+ * it takes as arguments a `NativePointer` and a `number`. It can be
+ * called multiple times to include several ranges.
+ */
+Afl.addIncludedRange(address, size);
+
+/*
+ * This is equivalent to setting a value in `AFL_FRIDA_EXCLUDE_RANGES`,
+ * it takes as arguments a `NativePointer` and a `number`. It can be
+ * called multiple times to exclude several ranges.
+ */
+Afl.addExcludedRange(address, size);
+
+/*
+ * See `AFL_INST_LIBS`.
+ */
+Afl.setInstrumentLibraries();
+
+/*
+ * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
+ * an argument.
+ */
+Afl.setInstrumentDebugFile(file);
+
+/*
+ * See `AFL_FRIDA_INST_NO_PREFETCH`.
+ */
+Afl.setPrefetchDisable();
+
+/*
+ * See `AFL_FRIDA_INST_NO_OPTIMIZE`
+ */
+Afl.setInstrumentNoOptimize();
+
+/*
+ * See `AFL_FRIDA_INST_TRACE`.
+ */
+Afl.setInstrumentEnableTracing();
+
+/*
+ * See `AFL_FRIDA_INST_TRACE_UNIQUE`.
+ */
+Afl.setInstrumentTracingUnique()
+
+/*
+ * See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as
+ * an argument.
+ */
+Afl.setStdOut(file);
+
+/*
+ * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
+ * an argument.
+ */
+Afl.setStdErr(file);
+
+/*
+ * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
+ * an argument.
+ */
+Afl.setStatsFile(file);
+
+/*
+ * See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
+ * argument
+ */
+Afl.setStatsInterval(interval);
+
+/*
+ * See `AFL_FRIDA_STATS_TRANSITIONS`
+ */
+Afl.setStatsTransitions()
+```
diff --git a/frida_mode/include/asan.h b/frida_mode/include/asan.h
index 7a8726e0..67d33591 100644
--- a/frida_mode/include/asan.h
+++ b/frida_mode/include/asan.h
@@ -1,10 +1,11 @@
 #ifndef _ASAN_H
 #define _ASAN_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 extern gboolean asan_initialized;
 
+void asan_config(void);
 void asan_init(void);
 void asan_arch_init(void);
 void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator);
diff --git a/frida_mode/include/ctx.h b/frida_mode/include/ctx.h
index 67274aee..c669478e 100644
--- a/frida_mode/include/ctx.h
+++ b/frida_mode/include/ctx.h
@@ -1,7 +1,7 @@
 #ifndef _CTX_H
 #define _CTX_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #if defined(__x86_64__)
 gsize ctx_read_reg(GumX64CpuContext *ctx, x86_reg reg);
diff --git a/frida_mode/include/entry.h b/frida_mode/include/entry.h
index 967831af..801c2bbe 100644
--- a/frida_mode/include/entry.h
+++ b/frida_mode/include/entry.h
@@ -1,13 +1,15 @@
 #ifndef _ENTRY_H
 #define _ENTRY_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
-extern guint64 entry_start;
+extern guint64 entry_point;
+
+void entry_config(void);
 
 void entry_init(void);
 
-void entry_run(void);
+void entry_start(void);
 
 void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output);
 
diff --git a/frida_mode/include/frida_cmplog.h b/frida_mode/include/frida_cmplog.h
index b620a472..a665e970 100644
--- a/frida_mode/include/frida_cmplog.h
+++ b/frida_mode/include/frida_cmplog.h
@@ -3,6 +3,7 @@
 
 extern struct cmp_map *__afl_cmp_map;
 
+void cmplog_config(void);
 void cmplog_init(void);
 
 /* Functions to be implemented by the different architectures */
diff --git a/frida_mode/include/instrument.h b/frida_mode/include/instrument.h
index 577481d1..9c8d3a5d 100644
--- a/frida_mode/include/instrument.h
+++ b/frida_mode/include/instrument.h
@@ -1,13 +1,20 @@
 #ifndef _INSTRUMENT_H
 #define _INSTRUMENT_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 
-extern __thread uint64_t previous_pc;
-extern uint8_t *         __afl_area_ptr;
-extern uint32_t          __afl_map_size;
+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 uint8_t *__afl_area_ptr;
+extern uint32_t __afl_map_size;
+
+void instrument_config(void);
 
 void instrument_init(void);
 
@@ -19,6 +26,7 @@ gboolean instrument_is_coverage_optimize_supported(void);
 void instrument_coverage_optimize(const cs_insn *   instr,
                                   GumStalkerOutput *output);
 
+void     instrument_debug_config(void);
 void     instrument_debug_init(void);
 void     instrument_debug_start(uint64_t address, GumStalkerOutput *output);
 void     instrument_debug_instruction(uint64_t address, uint16_t size);
diff --git a/frida_mode/include/intercept.h b/frida_mode/include/intercept.h
new file mode 100644
index 00000000..8fe93b10
--- /dev/null
+++ b/frida_mode/include/intercept.h
@@ -0,0 +1,11 @@
+#ifndef _INTERCEPTOR_H
+#define _INTERCEPTOR_H
+
+#include "frida-gumjs.h"
+
+void intercept_hook(void *address, gpointer replacement, gpointer user_data);
+void intercept_unhook(void *address);
+void intercept_unhook_self(void);
+
+#endif
+
diff --git a/frida_mode/include/interceptor.h b/frida_mode/include/interceptor.h
deleted file mode 100644
index 0ff754a4..00000000
--- a/frida_mode/include/interceptor.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _INTERCEPTOR_H
-#define _INTERCEPTOR_H
-
-#include "frida-gum.h"
-
-void intercept(void *address, gpointer replacement, gpointer user_data);
-void unintercept(void *address);
-void unintercept_self(void);
-
-#endif
-
diff --git a/frida_mode/include/js.h b/frida_mode/include/js.h
new file mode 100644
index 00000000..77237d55
--- /dev/null
+++ b/frida_mode/include/js.h
@@ -0,0 +1,18 @@
+#ifndef _JS_H
+#define _JS_H
+
+#include "frida-gumjs.h"
+
+extern unsigned char api_js[];
+extern unsigned int  api_js_len;
+
+extern gboolean js_done;
+
+/* Frida Mode */
+
+void js_config(void);
+
+void js_start(void);
+
+#endif
+
diff --git a/frida_mode/include/lib.h b/frida_mode/include/lib.h
index 237aecb0..a9d56e4e 100644
--- a/frida_mode/include/lib.h
+++ b/frida_mode/include/lib.h
@@ -1,7 +1,9 @@
 #ifndef _LIB_H
 #define _LIB_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
+
+void lib_config(void);
 
 void lib_init(void);
 
diff --git a/frida_mode/include/output.h b/frida_mode/include/output.h
index 53a9fdd3..743b2fe6 100644
--- a/frida_mode/include/output.h
+++ b/frida_mode/include/output.h
@@ -1,8 +1,12 @@
 #ifndef _OUTPUT_H
 #define _OUTPUT_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
+extern char *output_stdout;
+extern char *output_stderr;
+
+void output_config(void);
 void output_init(void);
 
 #endif
diff --git a/frida_mode/include/persistent.h b/frida_mode/include/persistent.h
index 25b44ab0..8f00196c 100644
--- a/frida_mode/include/persistent.h
+++ b/frida_mode/include/persistent.h
@@ -2,7 +2,7 @@
 #ifndef _PERSISTENT_H
 #define _PERSISTENT_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 #include "config.h"
 
 typedef struct arch_api_regs api_regs;
@@ -19,9 +19,10 @@ extern unsigned char *__afl_fuzz_ptr;
 extern guint64                persistent_start;
 extern guint64                persistent_count;
 extern guint64                persistent_ret;
-extern guint64                persistent_ret_offset;
 extern gboolean               persistent_debug;
-extern afl_persistent_hook_fn hook;
+extern afl_persistent_hook_fn persistent_hook;
+
+void persistent_config(void);
 
 void persistent_init(void);
 
diff --git a/frida_mode/include/prefetch.h b/frida_mode/include/prefetch.h
index 8f0cee68..835d5e8a 100644
--- a/frida_mode/include/prefetch.h
+++ b/frida_mode/include/prefetch.h
@@ -1,8 +1,11 @@
 #ifndef _PREFETCH_H
 #define _PREFETCH_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
+extern gboolean prefetch_enable;
+
+void prefetch_config(void);
 void prefetch_init(void);
 void prefetch_write(void *addr);
 void prefetch_read(void);
diff --git a/frida_mode/include/ranges.h b/frida_mode/include/ranges.h
index c623f473..a667fb76 100644
--- a/frida_mode/include/ranges.h
+++ b/frida_mode/include/ranges.h
@@ -1,13 +1,20 @@
 #ifndef _RANGES_H
 #define _RANGES_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
+extern gboolean ranges_debug_maps;
+extern gboolean ranges_inst_libs;
+
+void ranges_config(void);
 void ranges_init(void);
 
 gboolean range_is_excluded(gpointer address);
 
 void ranges_exclude();
 
+void ranges_add_include(GumMemoryRange *range);
+void ranges_add_exclude(GumMemoryRange *range);
+
 #endif
 
diff --git a/frida_mode/include/stalker.h b/frida_mode/include/stalker.h
index 186ead11..2136fe52 100644
--- a/frida_mode/include/stalker.h
+++ b/frida_mode/include/stalker.h
@@ -1,8 +1,9 @@
 #ifndef _STALKER_H
 #define _STALKER_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
+void        stalker_config(void);
 void        stalker_init(void);
 GumStalker *stalker_get(void);
 void        stalker_start(void);
diff --git a/frida_mode/include/stats.h b/frida_mode/include/stats.h
index 4271132a..1cfd6b8f 100644
--- a/frida_mode/include/stats.h
+++ b/frida_mode/include/stats.h
@@ -1,7 +1,7 @@
 #ifndef _STATS_H
 #define _STATS_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 typedef struct {
 
@@ -15,6 +15,11 @@ typedef struct {
 
 extern stats_data_header_t *stats_data;
 
+extern char *   stats_filename;
+extern guint64  stats_interval;
+extern gboolean stats_transitions;
+
+void stats_config(void);
 void stats_init(void);
 void stats_collect(const cs_insn *instr, gboolean begin);
 void stats_print(char *format, ...);
diff --git a/frida_mode/include/util.h b/frida_mode/include/util.h
index 7b443b5e..525e9d40 100644
--- a/frida_mode/include/util.h
+++ b/frida_mode/include/util.h
@@ -1,7 +1,7 @@
 #ifndef _UTIL_H
 #define _UTIL_H
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #define UNUSED_PARAMETER(x) (void)(x)
 #define IGNORED_RETURN(x) (void)!(x)
diff --git a/frida_mode/src/asan/asan.c b/frida_mode/src/asan/asan.c
index f78f690c..b2e763ca 100644
--- a/frida_mode/src/asan/asan.c
+++ b/frida_mode/src/asan/asan.c
@@ -1,18 +1,18 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
 #include "asan.h"
 
-gboolean asan_initialized = FALSE;
+static gboolean asan_enabled = FALSE;
+gboolean        asan_initialized = FALSE;
 
-void asan_init(void) {
+void asan_config(void) {
 
   if (getenv("AFL_USE_FASAN") != NULL) {
 
     OKF("Frida ASAN mode enabled");
-    asan_arch_init();
-    asan_initialized = TRUE;
+    asan_enabled = TRUE;
 
   } else {
 
@@ -22,3 +22,14 @@ void asan_init(void) {
 
 }
 
+void asan_init(void) {
+
+  if (asan_enabled) {
+
+    asan_arch_init();
+    asan_initialized = TRUE;
+
+  }
+
+}
+
diff --git a/frida_mode/src/asan/asan_arm32.c b/frida_mode/src/asan/asan_arm32.c
index 79475ced..f5fa4713 100644
--- a/frida_mode/src/asan/asan_arm32.c
+++ b/frida_mode/src/asan/asan_arm32.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/asan/asan_arm64.c b/frida_mode/src/asan/asan_arm64.c
index 66138e42..65524e03 100644
--- a/frida_mode/src/asan/asan_arm64.c
+++ b/frida_mode/src/asan/asan_arm64.c
@@ -1,5 +1,5 @@
 #include <dlfcn.h>
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/asan/asan_x64.c b/frida_mode/src/asan/asan_x64.c
index a2eabe3c..5c12669f 100644
--- a/frida_mode/src/asan/asan_x64.c
+++ b/frida_mode/src/asan/asan_x64.c
@@ -1,5 +1,5 @@
 #include <dlfcn.h>
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/asan/asan_x86.c b/frida_mode/src/asan/asan_x86.c
index 8490b490..6d2f9e2b 100644
--- a/frida_mode/src/asan/asan_x86.c
+++ b/frida_mode/src/asan/asan_x86.c
@@ -1,5 +1,5 @@
 #include <dlfcn.h>
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c
index 8814f7f3..a2609c8e 100644
--- a/frida_mode/src/cmplog/cmplog.c
+++ b/frida_mode/src/cmplog/cmplog.c
@@ -5,7 +5,7 @@
 #include <sys/syscall.h>
 #include <unistd.h>
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
@@ -50,6 +50,10 @@ static void cmplog_get_ranges(void) {
 
 }
 
+void cmplog_config(void) {
+
+}
+
 void cmplog_init(void) {
 
   if (__afl_cmp_map != NULL) { OKF("CMPLOG mode enabled"); }
@@ -94,10 +98,10 @@ static gboolean cmplog_contains(GumAddress inner_base, GumAddress inner_limit,
 
 gboolean cmplog_test_addr(guint64 addr, size_t size) {
 
-  if (g_hash_table_contains(hash_yes, (gpointer)addr)) { return true; }
-  if (g_hash_table_contains(hash_no, (gpointer)addr)) { return false; }
+  if (g_hash_table_contains(hash_yes, GSIZE_TO_POINTER(addr))) { return true; }
+  if (g_hash_table_contains(hash_no, GSIZE_TO_POINTER(addr))) { return false; }
 
-  void * page_addr = (void *)(addr & page_mask);
+  void * page_addr = GSIZE_TO_POINTER(addr & page_mask);
   size_t page_offset = addr & page_offset_mask;
 
   /* If it spans a page, then bail */
@@ -109,7 +113,7 @@ gboolean cmplog_test_addr(guint64 addr, size_t size) {
    */
   if (msync(page_addr, page_offset + size, MS_ASYNC) < 0) {
 
-    if (!g_hash_table_add(hash_no, (gpointer)addr)) {
+    if (!g_hash_table_add(hash_no, GSIZE_TO_POINTER(addr))) {
 
       FATAL("Failed - g_hash_table_add");
 
@@ -119,7 +123,7 @@ gboolean cmplog_test_addr(guint64 addr, size_t size) {
 
   } else {
 
-    if (!g_hash_table_add(hash_yes, (gpointer)addr)) {
+    if (!g_hash_table_add(hash_yes, GSIZE_TO_POINTER(addr))) {
 
       FATAL("Failed - g_hash_table_add");
 
diff --git a/frida_mode/src/cmplog/cmplog_arm32.c b/frida_mode/src/cmplog/cmplog_arm32.c
index 5af28f3f..ac703408 100644
--- a/frida_mode/src/cmplog/cmplog_arm32.c
+++ b/frida_mode/src/cmplog/cmplog_arm32.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/cmplog/cmplog_arm64.c b/frida_mode/src/cmplog/cmplog_arm64.c
index 04631ff8..dd97f38d 100644
--- a/frida_mode/src/cmplog/cmplog_arm64.c
+++ b/frida_mode/src/cmplog/cmplog_arm64.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 #include "cmplog.h"
diff --git a/frida_mode/src/cmplog/cmplog_x64.c b/frida_mode/src/cmplog/cmplog_x64.c
index ba16445d..0d18767a 100644
--- a/frida_mode/src/cmplog/cmplog_x64.c
+++ b/frida_mode/src/cmplog/cmplog_x64.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 #include "cmplog.h"
diff --git a/frida_mode/src/cmplog/cmplog_x86.c b/frida_mode/src/cmplog/cmplog_x86.c
index a27df0af..dd666c34 100644
--- a/frida_mode/src/cmplog/cmplog_x86.c
+++ b/frida_mode/src/cmplog/cmplog_x86.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 #include "cmplog.h"
diff --git a/frida_mode/src/ctx/ctx_arm32.c b/frida_mode/src/ctx/ctx_arm32.c
index a5c6f6d4..a354c117 100644
--- a/frida_mode/src/ctx/ctx_arm32.c
+++ b/frida_mode/src/ctx/ctx_arm32.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/ctx/ctx_arm64.c b/frida_mode/src/ctx/ctx_arm64.c
index d09896af..a735401b 100644
--- a/frida_mode/src/ctx/ctx_arm64.c
+++ b/frida_mode/src/ctx/ctx_arm64.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/ctx/ctx_x64.c b/frida_mode/src/ctx/ctx_x64.c
index 1772a252..da5cb13a 100644
--- a/frida_mode/src/ctx/ctx_x64.c
+++ b/frida_mode/src/ctx/ctx_x64.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/ctx/ctx_x86.c b/frida_mode/src/ctx/ctx_x86.c
index 9b50cb52..1a587702 100644
--- a/frida_mode/src/ctx/ctx_x86.c
+++ b/frida_mode/src/ctx/ctx_x86.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c
index e71386a0..186d5098 100644
--- a/frida_mode/src/entry.c
+++ b/frida_mode/src/entry.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
@@ -9,27 +9,32 @@
 
 extern void __afl_manual_init();
 
-guint64 entry_start = 0;
+guint64 entry_point = 0;
 
 static void entry_launch(void) {
 
   __afl_manual_init();
 
   /* Child here */
-  previous_pc = 0;
+  instrument_previous_pc = 0;
+
+}
+
+void entry_config(void) {
+
+  entry_point = util_read_address("AFL_ENTRYPOINT");
 
 }
 
 void entry_init(void) {
 
-  entry_start = util_read_address("AFL_ENTRYPOINT");
-  OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_start);
+  OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_point);
 
 }
 
-void entry_run(void) {
+void entry_start(void) {
 
-  if (entry_start == 0) { entry_launch(); }
+  if (entry_point == 0) { entry_launch(); }
 
 }
 
diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c
index ba82b89f..d6ae505d 100644
--- a/frida_mode/src/instrument/instrument.c
+++ b/frida_mode/src/instrument/instrument.c
@@ -2,7 +2,7 @@
 #include <sys/shm.h>
 #include <sys/mman.h>
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
@@ -18,12 +18,13 @@
 #include "stats.h"
 #include "util.h"
 
-static gboolean               tracing = false;
-static gboolean               optimize = false;
-static gboolean               unique = false;
+gboolean instrument_tracing = false;
+gboolean instrument_optimize = false;
+gboolean instrument_unique = false;
+
 static GumStalkerTransformer *transformer = NULL;
 
-__thread uint64_t previous_pc = 0;
+__thread uint64_t instrument_previous_pc = 0;
 
 static GumAddress previous_rip = 0;
 static u8 *       edges_notified = NULL;
@@ -61,7 +62,7 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
   current_pc = (current_rip >> 4) ^ (current_rip << 8);
   current_pc &= MAP_SIZE - 1;
 
-  edge = current_pc ^ previous_pc;
+  edge = current_pc ^ instrument_previous_pc;
 
   cursor = &__afl_area_ptr[edge];
   value = *cursor;
@@ -77,11 +78,11 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
   }
 
   *cursor = value;
-  previous_pc = current_pc >> 1;
+  instrument_previous_pc = current_pc >> 1;
 
-  if (unlikely(tracing)) {
+  if (unlikely(instrument_tracing)) {
 
-    if (!unique || edges_notified[edge] == 0) {
+    if (!instrument_unique || edges_notified[edge] == 0) {
 
       trace_debug("TRACE: edge: %10" G_GINT64_MODIFIER
                   "d, current_rip: 0x%016" G_GINT64_MODIFIER
@@ -90,7 +91,7 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
 
     }
 
-    if (unique) { edges_notified[edge] = 1; }
+    if (instrument_unique) { edges_notified[edge] = 1; }
 
     previous_rip = current_rip;
 
@@ -98,8 +99,9 @@ __attribute__((hot)) static void on_basic_block(GumCpuContext *context,
 
 }
 
-static void instr_basic_block(GumStalkerIterator *iterator,
-                              GumStalkerOutput *output, gpointer user_data) {
+static void instrument_basic_block(GumStalkerIterator *iterator,
+                                   GumStalkerOutput *  output,
+                                   gpointer            user_data) {
 
   UNUSED_PARAMETER(user_data);
 
@@ -111,7 +113,7 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
     if (unlikely(begin)) { instrument_debug_start(instr->address, output); }
 
-    if (instr->address == entry_start) { entry_prologue(iterator, output); }
+    if (instr->address == entry_point) { entry_prologue(iterator, output); }
     if (instr->address == persistent_start) { persistent_prologue(output); }
     if (instr->address == persistent_ret) { persistent_epilogue(output); }
 
@@ -150,7 +152,7 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
       if (likely(!excluded)) {
 
-        if (likely(optimize)) {
+        if (likely(instrument_optimize)) {
 
           instrument_coverage_optimize(instr, output);
 
@@ -185,31 +187,39 @@ static void instr_basic_block(GumStalkerIterator *iterator,
 
 }
 
-void instrument_init(void) {
+void instrument_config(void) {
+
+  instrument_optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
+  instrument_tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
+  instrument_unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL);
+
+  instrument_debug_config();
+  asan_config();
+  cmplog_config();
 
-  optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL);
-  tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL);
-  unique = (getenv("AFL_FRIDA_INST_TRACE_UNIQUE") != NULL);
+}
+
+void instrument_init(void) {
 
-  if (!instrument_is_coverage_optimize_supported()) optimize = false;
+  if (!instrument_is_coverage_optimize_supported()) instrument_optimize = false;
 
-  OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' ');
-  OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' ');
-  OKF("Instrumentation - unique [%c]", unique ? 'X' : ' ');
+  OKF("Instrumentation - optimize [%c]", instrument_optimize ? 'X' : ' ');
+  OKF("Instrumentation - tracing [%c]", instrument_tracing ? 'X' : ' ');
+  OKF("Instrumentation - unique [%c]", instrument_unique ? 'X' : ' ');
 
-  if (tracing && optimize) {
+  if (instrument_tracing && instrument_optimize) {
 
     FATAL("AFL_FRIDA_INST_TRACE requires AFL_FRIDA_INST_NO_OPTIMIZE");
 
   }
 
-  if (unique && optimize) {
+  if (instrument_unique && instrument_optimize) {
 
     FATAL("AFL_FRIDA_INST_TRACE_UNIQUE requires AFL_FRIDA_INST_NO_OPTIMIZE");
 
   }
 
-  if (unique) { tracing = TRUE; }
+  if (instrument_unique) { instrument_tracing = TRUE; }
 
   if (__afl_map_size != 0x10000) {
 
@@ -217,10 +227,10 @@ void instrument_init(void) {
 
   }
 
-  transformer =
-      gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL);
+  transformer = gum_stalker_transformer_make_from_callback(
+      instrument_basic_block, NULL, NULL);
 
-  if (unique) {
+  if (instrument_unique) {
 
     int shm_id = shmget(IPC_PRIVATE, MAP_SIZE, IPC_CREAT | IPC_EXCL | 0600);
     if (shm_id < 0) { FATAL("shm_id < 0 - errno: %d\n", errno); }
diff --git a/frida_mode/src/instrument/instrument_arm32.c b/frida_mode/src/instrument/instrument_arm32.c
index 450a69a3..0e15940a 100644
--- a/frida_mode/src/instrument/instrument_arm32.c
+++ b/frida_mode/src/instrument/instrument_arm32.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c
index 49ee86a2..17f97c97 100644
--- a/frida_mode/src/instrument/instrument_arm64.c
+++ b/frida_mode/src/instrument/instrument_arm64.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
@@ -72,7 +72,7 @@ void instrument_coverage_optimize(const cs_insn *   instr,
     gum_arm64_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
 
     uint8_t **afl_area_ptr_ptr = &__afl_area_ptr;
-    uint64_t *afl_prev_loc_ptr = &previous_pc;
+    uint64_t *afl_prev_loc_ptr = &instrument_previous_pc;
     gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr,
                                sizeof(afl_area_ptr_ptr));
     gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
diff --git a/frida_mode/src/instrument/instrument_debug.c b/frida_mode/src/instrument/instrument_debug.c
index 0ce26a1c..b8cca634 100644
--- a/frida_mode/src/instrument/instrument_debug.c
+++ b/frida_mode/src/instrument/instrument_debug.c
@@ -3,7 +3,7 @@
 #include <stdio.h>
 #include <unistd.h>
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
@@ -13,6 +13,8 @@
 static int      debugging_fd = -1;
 static gpointer instrument_gen_start = NULL;
 
+char *instrument_debug_filename = NULL;
+
 static void instrument_debug(char *format, ...) {
 
   va_list ap;
@@ -79,18 +81,25 @@ static void instrument_disasm(guint8 *start, guint8 *end) {
 
 }
 
+void instrument_debug_config(void) {
+
+  instrument_debug_filename = getenv("AFL_FRIDA_INST_DEBUG_FILE");
+
+}
+
 void instrument_debug_init(void) {
 
-  char *filename = getenv("AFL_FRIDA_INST_DEBUG_FILE");
-  OKF("Instrumentation debugging - enabled [%c]", filename == NULL ? ' ' : 'X');
+  OKF("Instrumentation debugging - enabled [%c]",
+      instrument_debug_filename == NULL ? ' ' : 'X');
 
-  if (filename == NULL) { return; }
+  if (instrument_debug_filename == NULL) { return; }
 
-  OKF("Instrumentation debugging - file [%s]", filename);
+  OKF("Instrumentation debugging - file [%s]", instrument_debug_filename);
 
-  if (filename == NULL) { return; }
+  if (instrument_debug_filename == NULL) { return; }
 
-  char *path = g_canonicalize_filename(filename, g_get_current_dir());
+  char *path =
+      g_canonicalize_filename(instrument_debug_filename, g_get_current_dir());
 
   OKF("Instrumentation debugging - path [%s]", path);
 
diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c
index 7000e65d..a38b5b14 100644
--- a/frida_mode/src/instrument/instrument_x64.c
+++ b/frida_mode/src/instrument/instrument_x64.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 
@@ -68,7 +68,7 @@ void instrument_coverage_optimize(const cs_insn *   instr,
     current_log_impl = cw->pc;
     gum_x86_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code));
 
-    uint64_t *afl_prev_loc_ptr = &previous_pc;
+    uint64_t *afl_prev_loc_ptr = &instrument_previous_pc;
     gum_x86_writer_put_bytes(cw, (const guint8 *)&__afl_area_ptr,
                              sizeof(__afl_area_ptr));
     gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr,
diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c
index 04a19e08..3c3dc272 100644
--- a/frida_mode/src/instrument/instrument_x86.c
+++ b/frida_mode/src/instrument/instrument_x86.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
@@ -16,7 +16,7 @@ static void instrument_coverage_function(GumX86Writer *cw) {
   gum_x86_writer_put_push_reg(cw, GUM_REG_EDX);
 
   gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
-                                     GUM_ADDRESS(&previous_pc));
+                                     GUM_ADDRESS(&instrument_previous_pc));
   gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_EDX, GUM_REG_ECX);
   gum_x86_writer_put_xor_reg_reg(cw, GUM_REG_EDX, GUM_REG_EDI);
 
diff --git a/frida_mode/src/interceptor.c b/frida_mode/src/intercept.c
index d2802752..ed8d27bd 100644
--- a/frida_mode/src/interceptor.c
+++ b/frida_mode/src/intercept.c
@@ -1,10 +1,10 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
-#include "interceptor.h"
+#include "intercept.h"
 
-void intercept(void *address, gpointer replacement, gpointer user_data) {
+void intercept_hook(void *address, gpointer replacement, gpointer user_data) {
 
   GumInterceptor *interceptor = gum_interceptor_obtain();
   gum_interceptor_begin_transaction(interceptor);
@@ -15,7 +15,7 @@ void intercept(void *address, gpointer replacement, gpointer user_data) {
 
 }
 
-void unintercept(void *address) {
+void intercept_unhook(void *address) {
 
   GumInterceptor *interceptor = gum_interceptor_obtain();
 
@@ -26,10 +26,10 @@ void unintercept(void *address) {
 
 }
 
-void unintercept_self(void) {
+void intercept_unhook_self(void) {
 
   GumInvocationContext *ctx = gum_interceptor_get_current_invocation();
-  unintercept(ctx->function);
+  intercept_unhook(ctx->function);
 
 }
 
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
new file mode 100644
index 00000000..983f1efa
--- /dev/null
+++ b/frida_mode/src/js/api.js
@@ -0,0 +1,201 @@
+const write = new NativeFunction(
+    Module.getExportByName(null, 'write'),
+    'int',
+    ['int', 'pointer', 'int']
+);
+
+const afl_frida_trace = Process.findModuleByName('afl-frida-trace.so');
+
+function get_api(name, ret, args) {
+    const addr = afl_frida_trace.findExportByName(name);
+    return new NativeFunction(addr, ret, args);
+}
+
+const js_api_done = get_api(
+    'js_api_done',
+    'void',
+    []);
+
+const js_api_error = get_api(
+    'js_api_error',
+    'void',
+    ['pointer']);
+
+const js_api_set_entrypoint = get_api(
+    'js_api_set_entrypoint',
+    'void',
+    ['pointer']);
+
+const js_api_set_persistent_address = get_api(
+    'js_api_set_persistent_address',
+    'void',
+    ['pointer']);
+
+const js_api_set_persistent_return = get_api(
+    'js_api_set_persistent_return',
+    'void',
+    ['pointer']);
+
+const js_api_set_persistent_count = get_api(
+    'js_api_set_persistent_count',
+    'void',
+    ['uint64']);
+
+const js_api_set_persistent_debug = get_api(
+    'js_api_set_persistent_debug',
+    'void',
+    []);
+
+const js_api_set_debug_maps = get_api(
+    'js_api_set_debug_maps',
+    'void',
+    []);
+
+const js_api_add_include_range = get_api(
+    'js_api_add_include_range',
+    'void',
+    ['pointer', 'size_t']);
+
+const js_api_add_exclude_range = get_api(
+    'js_api_add_exclude_range',
+    'void',
+    ['pointer', 'size_t']);
+
+const js_api_set_instrument_libraries = get_api(
+    'js_api_set_instrument_libraries',
+    'void',
+    []);
+
+const js_api_set_instrument_debug_file = get_api(
+    'js_api_set_instrument_debug_file',
+    'void',
+    ['pointer']);
+
+const js_api_set_prefetch_disable = get_api(
+    'js_api_set_prefetch_disable',
+    'void',
+    []);
+
+const js_api_set_instrument_no_optimize = get_api(
+    'js_api_set_instrument_no_optimize',
+    'void',
+    []);
+
+const js_api_set_instrument_trace = get_api(
+    'js_api_set_instrument_trace',
+    'void',
+    []);
+
+const js_api_set_instrument_trace_unique = get_api(
+    'js_api_set_instrument_trace_unique',
+    'void',
+    []);
+
+const js_api_set_stdout = get_api(
+    'js_api_set_stdout',
+    'void',
+    ['pointer']);
+
+const js_api_set_stderr = get_api(
+    'js_api_set_stderr',
+    'void',
+    ['pointer']);
+
+const js_api_set_stats_file = get_api(
+    'js_api_set_stats_file',
+    'void',
+    ['pointer']);
+
+const js_api_set_stats_interval = get_api(
+    'js_api_set_stats_interval',
+    'void',
+    ['uint64']);
+
+const js_api_set_stats_transitions = get_api(
+    'js_api_set_stats_transitions',
+    'void',
+    []);
+
+const afl = {
+    print: function (msg) {
+        const STDOUT_FILENO = 2;
+        const log = `${msg}\n`;
+        const buf = Memory.allocUtf8String(log);
+        write(STDOUT_FILENO, buf, log.length);
+    },
+    done: function() {
+        js_api_done();
+    },
+    error: function(msg) {
+        const buf = Memory.allocUtf8String(msg);
+        js_api_error(buf);
+    },
+    setEntryPoint: function(addr) {
+        js_api_set_entrypoint(addr);
+    },
+    setPersistentAddress: function(addr) {
+        js_api_set_persistent_address(addr);
+    },
+    setPersistentReturn: function(addr) {
+        js_api_set_persistent_return(addr);
+    },
+    setPersistentCount: function(addr) {
+        js_api_set_persistent_count(addr);
+    },
+    setPersistentDebug: function() {
+        js_api_set_persistent_debug();
+    },
+    setDebugMaps: function() {
+        js_api_set_debug_maps();
+    },
+    addIncludedRange: function(address, size) {
+        js_api_add_include_range(address, size);
+    },
+    addExcludedRange: function(address, size) {
+        js_api_add_exclude_range(address, size);
+    },
+    setInstrumentLibraries: function() {
+        js_api_set_instrument_libraries();
+    },
+    setInstrumentDebugFile: function(file) {
+        const buf = Memory.allocUtf8String(file);
+        js_api_set_instrument_debug_file(buf)
+    },
+    setPrefetchDisable: function() {
+        js_api_set_prefetch_disable();
+    },
+    setInstrumentNoOptimize: function() {
+        js_api_set_instrument_no_optimize();
+    },
+    setInstrumentEnableTracing: function() {
+        js_api_set_instrument_trace();
+    },
+    setInstrumentTracingUnique: function() {
+        js_api_set_instrument_trace_unique();
+    },
+    setStdOut: function(file) {
+        const buf = Memory.allocUtf8String(file);
+        js_api_set_stdout(buf)
+    },
+    setStdErr: function(file) {
+        const buf = Memory.allocUtf8String(file);
+        js_api_set_stderr(buf)
+    },
+    setStatsFile: function(file) {
+        const buf = Memory.allocUtf8String(file);
+        js_api_set_stats_file(buf)
+    },
+    setStatsInterval: function(interval) {
+        js_api_set_stats_interval(interval);
+    },
+    setStatsTransitions: function() {
+        js_api_set_stats_transitions();
+    }
+
+};
+
+Object.defineProperty(global, 'Afl', {value: afl, writeable: false});
+
+////////////////////////////////////////////////////////////////////////////////
+//                          END OF API                                        //
+////////////////////////////////////////////////////////////////////////////////
diff --git a/frida_mode/src/js/js.c b/frida_mode/src/js/js.c
new file mode 100644
index 00000000..79e716ad
--- /dev/null
+++ b/frida_mode/src/js/js.c
@@ -0,0 +1,113 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "js.h"
+#include "util.h"
+
+static char *js_script = NULL;
+gboolean     js_done = FALSE;
+
+static gchar *           filename = "afl.js";
+static gchar *           contents;
+static GumScriptBackend *backend;
+static GCancellable *    cancellable = NULL;
+static GError *          error = NULL;
+static GumScript *       script;
+
+static void js_msg(GumScript *script, const gchar *message, GBytes *data,
+                   gpointer user_data) {
+
+  UNUSED_PARAMETER(script);
+  UNUSED_PARAMETER(data);
+  UNUSED_PARAMETER(user_data);
+  OKF("%s", message);
+
+}
+
+void js_config(void) {
+
+  js_script = getenv("AFL_FRIDA_JS_SCRIPT");
+
+}
+
+static gchar *js_get_script() {
+
+  gsize length;
+  if (js_script != NULL) { filename = js_script; }
+
+  filename = g_canonicalize_filename(filename, g_get_current_dir());
+
+  if (!g_file_get_contents(filename, &contents, &length, NULL)) {
+
+    if (js_script == NULL) {
+
+      return NULL;
+
+    } else {
+
+      FATAL("Could not load script file: %s", filename);
+
+    }
+
+  } else {
+
+    OKF("Loaded AFL script: %s, %" G_GSIZE_MODIFIER "d bytes", filename,
+        length);
+
+    gchar *source = g_malloc0(api_js_len + length + 1);
+    memcpy(source, api_js, api_js_len);
+    memcpy(&source[api_js_len], contents, length);
+
+    return source;
+
+  }
+
+}
+
+static void js_print_script(gchar *source) {
+
+  gchar **split = g_strsplit(source, "\n", 0);
+
+  for (size_t i = 0; split[i] != NULL; i++) {
+
+    OKF("%3" G_GSIZE_MODIFIER "d. %s", i + 1, split[i]);
+
+  }
+
+  g_strfreev(split);
+
+}
+
+void js_start(void) {
+
+  GMainContext *context;
+
+  gchar *source = js_get_script();
+  if (source == NULL) { return; }
+  js_print_script(source);
+
+  backend = gum_script_backend_obtain_qjs();
+
+  script = gum_script_backend_create_sync(backend, "example", source,
+                                          cancellable, &error);
+
+  if (error != NULL) {
+
+    g_printerr("%s\n", error->message);
+    FATAL("Error processing script");
+
+  }
+
+  gum_script_set_message_handler(script, js_msg, NULL, NULL);
+
+  gum_script_load_sync(script, cancellable);
+
+  context = g_main_context_get_thread_default();
+  while (g_main_context_pending(context))
+    g_main_context_iteration(context, FALSE);
+
+  if (!js_done) { FATAL("Script didn't call Afl.done()"); }
+
+}
+
diff --git a/frida_mode/src/js/js_api.c b/frida_mode/src/js/js_api.c
new file mode 100644
index 00000000..018c0b9a
--- /dev/null
+++ b/frida_mode/src/js/js_api.c
@@ -0,0 +1,142 @@
+#include "debug.h"
+
+#include "entry.h"
+#include "instrument.h"
+#include "js.h"
+#include "output.h"
+#include "persistent.h"
+#include "prefetch.h"
+#include "ranges.h"
+#include "stats.h"
+#include "util.h"
+
+void js_api_done() {
+
+  js_done = TRUE;
+
+}
+
+void js_api_error(char *msg) {
+
+  FATAL("%s", msg);
+
+}
+
+void js_api_set_entrypoint(void *address) {
+
+  entry_point = GPOINTER_TO_SIZE(address);
+
+}
+
+void js_api_set_persistent_address(void *address) {
+
+  persistent_start = GPOINTER_TO_SIZE(address);
+
+}
+
+void js_api_set_persistent_return(void *address) {
+
+  persistent_ret = GPOINTER_TO_SIZE(address);
+
+}
+
+void js_api_set_persistent_count(uint64_t count) {
+
+  persistent_count = count;
+
+}
+
+void js_api_set_persistent_debug() {
+
+  persistent_debug = TRUE;
+
+}
+
+void js_api_set_debug_maps() {
+
+  ranges_debug_maps = TRUE;
+
+}
+
+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) {
+
+  GumMemoryRange range = {.base_address = GUM_ADDRESS(address), .size = size};
+  ranges_add_exclude(&range);
+
+}
+
+void js_api_set_instrument_libraries() {
+
+  ranges_inst_libs = TRUE;
+
+}
+
+void js_api_set_instrument_debug_file(char *path) {
+
+  instrument_debug_filename = g_strdup(path);
+
+}
+
+void js_api_set_prefetch_disable(void) {
+
+  prefetch_enable = FALSE;
+
+}
+
+void js_api_set_instrument_no_optimize(void) {
+
+  instrument_optimize = FALSE;
+
+}
+
+void js_api_set_instrument_trace(void) {
+
+  instrument_tracing = TRUE;
+
+}
+
+void js_api_set_instrument_trace_unique(void) {
+
+  instrument_unique = TRUE;
+
+}
+
+void js_api_set_stdout(char *file) {
+
+  output_stdout = g_strdup(file);
+
+}
+
+void js_api_set_stderr(char *file) {
+
+  output_stderr = g_strdup(file);
+
+}
+
+void js_api_set_stats_file(char *file) {
+
+  stats_filename = g_strdup(file);
+
+}
+
+void js_api_set_stats_interval(uint64_t interval) {
+
+  stats_interval = interval;
+
+}
+
+void js_api_set_stats_transitions() {
+
+  stats_transitions = TRUE;
+
+}
+
+// "AFL_FRIDA_PERSISTENT_HOOK",
+
diff --git a/frida_mode/src/lib/lib.c b/frida_mode/src/lib/lib.c
index 13a7d1e7..59a3fcf9 100644
--- a/frida_mode/src/lib/lib.c
+++ b/frida_mode/src/lib/lib.c
@@ -6,7 +6,7 @@
   #include <sys/mman.h>
   #include <unistd.h>
 
-  #include "frida-gum.h"
+  #include "frida-gumjs.h"
 
   #include "debug.h"
 
@@ -151,6 +151,10 @@ static void lib_get_text_section(lib_details_t *details) {
 
 }
 
+void lib_config(void) {
+
+}
+
 void lib_init(void) {
 
   lib_details_t lib_details;
diff --git a/frida_mode/src/lib/lib_apple.c b/frida_mode/src/lib/lib_apple.c
index 8f863861..2aa48a13 100644
--- a/frida_mode/src/lib/lib_apple.c
+++ b/frida_mode/src/lib/lib_apple.c
@@ -1,5 +1,5 @@
 #ifdef __APPLE__
-  #include "frida-gum.h"
+  #include "frida-gumjs.h"
 
   #include "debug.h"
 
@@ -56,6 +56,10 @@ gboolean lib_get_text_section(const GumDarwinSectionDetails *details,
 
 }
 
+void lib_config(void) {
+
+}
+
 void lib_init(void) {
 
   GumDarwinModule *module = NULL;
diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c
index b17d9f49..85b0bbf3 100644
--- a/frida_mode/src/main.c
+++ b/frida_mode/src/main.c
@@ -11,14 +11,15 @@
   #include <sys/personality.h>
 #endif
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
 
 #include "entry.h"
 #include "instrument.h"
-#include "interceptor.h"
+#include "intercept.h"
+#include "js.h"
 #include "lib.h"
 #include "output.h"
 #include "persistent.h"
@@ -44,13 +45,6 @@ typedef int *(*main_fn_t)(int argc, char **argv, char **envp);
 
 static main_fn_t main_fn = NULL;
 
-static int on_fork(void) {
-
-  prefetch_read();
-  return fork();
-
-}
-
 #ifdef __APPLE__
 static void on_main_os(int argc, char **argv, char **envp) {
 
@@ -174,23 +168,36 @@ void afl_frida_start(void) {
   afl_print_cmdline();
   afl_print_env();
 
+  /* Configure */
+  entry_config();
+  instrument_config();
+  js_config();
+  lib_config();
+  output_config();
+  persistent_config();
+  prefetch_config();
+  ranges_config();
+  stalker_config();
+  stats_config();
+
+  js_start();
+
+  /* Initialize */
+  output_init();
+
   embedded_init();
-  stalker_init();
-  lib_init();
   entry_init();
   instrument_init();
-  output_init();
+  lib_init();
   persistent_init();
   prefetch_init();
+  stalker_init();
   ranges_init();
   stats_init();
 
-  void *fork_addr =
-      GSIZE_TO_POINTER(gum_module_find_export_by_name(NULL, "fork"));
-  intercept(fork_addr, on_fork, NULL);
-
+  /* Start */
   stalker_start();
-  entry_run();
+  entry_start();
 
 }
 
@@ -198,7 +205,7 @@ static int *on_main(int argc, char **argv, char **envp) {
 
   on_main_os(argc, argv, envp);
 
-  unintercept_self();
+  intercept_unhook_self();
 
   afl_frida_start();
 
@@ -212,7 +219,7 @@ extern int *main(int argc, char **argv, char **envp);
 static void intercept_main(void) {
 
   main_fn = main;
-  intercept(main, on_main, NULL);
+  intercept_hook(main, on_main, NULL);
 
 }
 
@@ -225,7 +232,7 @@ static void intercept_main(void) {
   OKF("Entry Point: 0x%016" G_GINT64_MODIFIER "x", entry);
   void *main = GSIZE_TO_POINTER(entry);
   main_fn = main;
-  intercept(main, on_main, NULL);
+  intercept_hook(main, on_main, NULL);
 
 }
 
@@ -236,8 +243,8 @@ static int on_libc_start_main(int *(main)(int, char **, char **), int argc,
                               void(*stack_end)) {
 
   main_fn = main;
-  unintercept_self();
-  intercept(main, on_main, NULL);
+  intercept_unhook_self();
+  intercept_hook(main, on_main, NULL);
   return __libc_start_main(main, argc, ubp_av, init, fini, rtld_fini,
                            stack_end);
 
@@ -245,7 +252,7 @@ static int on_libc_start_main(int *(main)(int, char **, char **), int argc,
 
 static void intercept_main(void) {
 
-  intercept(__libc_start_main, on_libc_start_main, NULL);
+  intercept_hook(__libc_start_main, on_libc_start_main, NULL);
 
 }
 
diff --git a/frida_mode/src/output.c b/frida_mode/src/output.c
index 8a222b25..e2b744e7 100644
--- a/frida_mode/src/output.c
+++ b/frida_mode/src/output.c
@@ -2,17 +2,17 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
 #include "output.h"
 
-static int output_fd = -1;
+char *output_stdout = NULL;
+char *output_stderr = NULL;
 
-static void output_redirect(int fd, char *variable) {
+static void output_redirect(int fd, char *filename) {
 
-  char *filename = getenv(variable);
   char *path = NULL;
 
   if (filename == NULL) { return; }
@@ -21,8 +21,8 @@ static void output_redirect(int fd, char *variable) {
 
   OKF("Redirect %d -> '%s'", fd, path);
 
-  output_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
-                   S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+  int output_fd = open(path, O_RDWR | O_CREAT | O_TRUNC,
+                       S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
 
   g_free(path);
 
@@ -34,12 +34,24 @@ static void output_redirect(int fd, char *variable) {
 
   }
 
+  close(output_fd);
+
+}
+
+void output_config(void) {
+
+  output_stdout = getenv("AFL_FRIDA_OUTPUT_STDOUT");
+  output_stderr = getenv("AFL_FRIDA_OUTPUT_STDERR");
+
 }
 
 void output_init(void) {
 
-  output_redirect(STDOUT_FILENO, "AFL_FRIDA_OUTPUT_STDOUT");
-  output_redirect(STDERR_FILENO, "AFL_FRIDA_OUTPUT_STDERR");
+  OKF("Output - StdOut: %s", output_stdout);
+  OKF("Output - StdErr: %s", output_stderr);
+
+  output_redirect(STDOUT_FILENO, output_stdout);
+  output_redirect(STDERR_FILENO, output_stderr);
 
 }
 
diff --git a/frida_mode/src/persistent/persistent.c b/frida_mode/src/persistent/persistent.c
index 243d501d..e3e0b0ca 100644
--- a/frida_mode/src/persistent/persistent.c
+++ b/frida_mode/src/persistent/persistent.c
@@ -1,6 +1,6 @@
 #include <dlfcn.h>
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
@@ -8,17 +8,18 @@
 #include "persistent.h"
 #include "util.h"
 
-int                    __afl_sharedmem_fuzzing = 0;
-afl_persistent_hook_fn hook = NULL;
+int          __afl_sharedmem_fuzzing = 0;
+static char *hook_name = NULL;
+
+afl_persistent_hook_fn persistent_hook = NULL;
 guint64                persistent_start = 0;
 guint64                persistent_count = 0;
 guint64                persistent_ret = 0;
 gboolean               persistent_debug = FALSE;
 
-void persistent_init(void) {
-
-  char *hook_name = getenv("AFL_FRIDA_PERSISTENT_HOOK");
+void persistent_config(void) {
 
+  hook_name = getenv("AFL_FRIDA_PERSISTENT_HOOK");
   persistent_start = util_read_address("AFL_FRIDA_PERSISTENT_ADDR");
   persistent_count = util_read_num("AFL_FRIDA_PERSISTENT_CNT");
   persistent_ret = util_read_address("AFL_FRIDA_PERSISTENT_RET");
@@ -33,6 +34,11 @@ void persistent_init(void) {
 
   }
 
+  if (persistent_start != 0 && persistent_count == 0) persistent_count = 1000;
+
+  if (persistent_start != 0 && !persistent_is_supported())
+    FATAL("Persistent mode not supported on this architecture");
+
   if (persistent_ret != 0 && persistent_start == 0) {
 
     FATAL(
@@ -41,13 +47,9 @@ void persistent_init(void) {
 
   }
 
-  if (persistent_start != 0 && persistent_count == 0) persistent_count = 1000;
-
-  if (persistent_count != 0 && persistent_count < 100)
-    WARNF("Persistent count out of recommended range (<100)");
+}
 
-  if (persistent_start != 0 && !persistent_is_supported())
-    FATAL("Persistent mode not supported on this architecture");
+void persistent_init(void) {
 
   OKF("Instrumentation - persistent mode [%c] (0x%016" G_GINT64_MODIFIER "X)",
       persistent_start == 0 ? ' ' : 'X', persistent_start);
@@ -58,27 +60,26 @@ void persistent_init(void) {
   OKF("Instrumentation - persistent ret [%c] (0x%016" G_GINT64_MODIFIER "X)",
       persistent_ret == 0 ? ' ' : 'X', persistent_ret);
 
-  if (hook_name != NULL) {
+  if (hook_name == NULL) { return; }
 
-    void *hook_obj = dlopen(hook_name, RTLD_NOW);
-    if (hook_obj == NULL)
-      FATAL("Failed to load AFL_FRIDA_PERSISTENT_HOOK (%s)", hook_name);
+  void *hook_obj = dlopen(hook_name, RTLD_NOW);
+  if (hook_obj == NULL)
+    FATAL("Failed to load AFL_FRIDA_PERSISTENT_HOOK (%s)", hook_name);
 
-    int (*afl_persistent_hook_init_ptr)(void) =
-        dlsym(hook_obj, "afl_persistent_hook_init");
-    if (afl_persistent_hook_init_ptr == NULL)
-      FATAL("Failed to find afl_persistent_hook_init in %s", hook_name);
+  int (*afl_persistent_hook_init_ptr)(void) =
+      dlsym(hook_obj, "afl_persistent_hook_init");
+  if (afl_persistent_hook_init_ptr == NULL)
+    FATAL("Failed to find afl_persistent_hook_init in %s", hook_name);
 
-    if (afl_persistent_hook_init_ptr() == 0)
-      FATAL("afl_persistent_hook_init returned a failure");
+  if (afl_persistent_hook_init_ptr() == 0)
+    FATAL("afl_persistent_hook_init returned a failure");
 
-    hook = (afl_persistent_hook_fn)dlsym(hook_obj, "afl_persistent_hook");
-    if (hook == NULL)
-      FATAL("Failed to find afl_persistent_hook in %s", hook_name);
+  persistent_hook =
+      (afl_persistent_hook_fn)dlsym(hook_obj, "afl_persistent_hook");
+  if (persistent_hook == NULL)
+    FATAL("Failed to find afl_persistent_hook in %s", hook_name);
 
-    __afl_sharedmem_fuzzing = 1;
-
-  }
+  __afl_sharedmem_fuzzing = 1;
 
 }
 
diff --git a/frida_mode/src/persistent/persistent_arm32.c b/frida_mode/src/persistent/persistent_arm32.c
index 6a3c06fa..f12f1af8 100644
--- a/frida_mode/src/persistent/persistent_arm32.c
+++ b/frida_mode/src/persistent/persistent_arm32.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/persistent/persistent_arm64.c b/frida_mode/src/persistent/persistent_arm64.c
index d7c6c76b..e618fbac 100644
--- a/frida_mode/src/persistent/persistent_arm64.c
+++ b/frida_mode/src/persistent/persistent_arm64.c
@@ -1,5 +1,5 @@
 #include <unistd.h>
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
@@ -318,7 +318,7 @@ static void instrument_exit(GumArm64Writer *cw) {
 static int instrument_afl_persistent_loop_func(void) {
 
   int ret = __afl_persistent_loop(persistent_count);
-  previous_pc = 0;
+  instrument_previous_pc = 0;
   return ret;
 
 }
@@ -337,7 +337,7 @@ static void instrument_afl_persistent_loop(GumArm64Writer *cw) {
 static void persistent_prologue_hook(GumArm64Writer *   cw,
                                      struct arm64_regs *regs) {
 
-  if (hook == NULL) return;
+  if (persistent_hook == NULL) return;
 
   gum_arm64_writer_put_sub_reg_reg_imm(cw, ARM64_REG_SP, ARM64_REG_SP,
                                        GUM_RED_ZONE_SIZE);
@@ -354,7 +354,7 @@ static void persistent_prologue_hook(GumArm64Writer *   cw,
   gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X2, 0);
 
   gum_arm64_writer_put_call_address_with_arguments(
-      cw, GUM_ADDRESS(hook), 4, GUM_ARG_ADDRESS, GUM_ADDRESS(regs),
+      cw, GUM_ADDRESS(persistent_hook), 4, GUM_ARG_ADDRESS, GUM_ADDRESS(regs),
       GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_REGISTER, ARM64_REG_X2,
       GUM_ARG_REGISTER, ARM64_REG_X3);
 
diff --git a/frida_mode/src/persistent/persistent_x64.c b/frida_mode/src/persistent/persistent_x64.c
index 653acefe..a91abc1c 100644
--- a/frida_mode/src/persistent/persistent_x64.c
+++ b/frida_mode/src/persistent/persistent_x64.c
@@ -1,5 +1,5 @@
 #include <unistd.h>
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
@@ -199,7 +199,7 @@ static void instrument_exit(GumX86Writer *cw) {
 static int instrument_afl_persistent_loop_func(void) {
 
   int ret = __afl_persistent_loop(persistent_count);
-  previous_pc = 0;
+  instrument_previous_pc = 0;
   return ret;
 
 }
@@ -220,7 +220,7 @@ static void instrument_afl_persistent_loop(GumX86Writer *cw) {
 static void persistent_prologue_hook(GumX86Writer *      cw,
                                      struct x86_64_regs *regs) {
 
-  if (hook == NULL) return;
+  if (persistent_hook == NULL) return;
   gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
                                         -(GUM_RED_ZONE_SIZE));
 
@@ -236,7 +236,7 @@ static void persistent_prologue_hook(GumX86Writer *      cw,
   gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 0);
 
   gum_x86_writer_put_call_address_with_arguments(
-      cw, GUM_CALL_CAPI, GUM_ADDRESS(hook), 4, GUM_ARG_ADDRESS,
+      cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 4, GUM_ARG_ADDRESS,
       GUM_ADDRESS(regs), GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_REGISTER,
       GUM_REG_RDX, GUM_ARG_REGISTER, GUM_REG_RCX);
 
diff --git a/frida_mode/src/persistent/persistent_x86.c b/frida_mode/src/persistent/persistent_x86.c
index 7add6e99..1d01d8e4 100644
--- a/frida_mode/src/persistent/persistent_x86.c
+++ b/frida_mode/src/persistent/persistent_x86.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 
@@ -152,7 +152,7 @@ static void instrument_exit(GumX86Writer *cw) {
 static int instrument_afl_persistent_loop_func(void) {
 
   int ret = __afl_persistent_loop(persistent_count);
-  previous_pc = 0;
+  instrument_previous_pc = 0;
   return ret;
 
 }
@@ -167,7 +167,7 @@ static void instrument_afl_persistent_loop(GumX86Writer *cw) {
 
 static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) {
 
-  if (hook == NULL) return;
+  if (persistent_hook == NULL) return;
 
   gum_x86_writer_put_mov_reg_address(cw, GUM_REG_ECX,
                                      GUM_ADDRESS(&__afl_fuzz_len));
@@ -180,7 +180,7 @@ static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) {
 
   /* Base address is 64-bits (hence two zero arguments) */
   gum_x86_writer_put_call_address_with_arguments(
-      cw, GUM_CALL_CAPI, GUM_ADDRESS(hook), 5, GUM_ARG_ADDRESS,
+      cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 5, GUM_ARG_ADDRESS,
       GUM_ADDRESS(regs), GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_ADDRESS,
       GUM_ADDRESS(0), GUM_ARG_REGISTER, GUM_REG_EDX, GUM_ARG_REGISTER,
       GUM_REG_ECX);
diff --git a/frida_mode/src/prefetch.c b/frida_mode/src/prefetch.c
index 65c09fba..50d10c9e 100644
--- a/frida_mode/src/prefetch.c
+++ b/frida_mode/src/prefetch.c
@@ -2,10 +2,11 @@
 #include <sys/shm.h>
 #include <sys/mman.h>
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
+#include "intercept.h"
 #include "prefetch.h"
 #include "stalker.h"
 
@@ -20,9 +21,10 @@ typedef struct {
 
 } prefetch_data_t;
 
-static prefetch_data_t *prefetch_data = NULL;
+gboolean prefetch_enable = TRUE;
 
-static int prefetch_shm_id = -1;
+static prefetch_data_t *prefetch_data = NULL;
+static int              prefetch_shm_id = -1;
 
 /*
  * We do this from the transformer since we need one anyway for coverage, this
@@ -72,14 +74,33 @@ void prefetch_read(void) {
 
 }
 
+void prefetch_config(void) {
+
+  prefetch_enable = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL);
+
+}
+
+static int prefetch_on_fork(void) {
+
+  prefetch_read();
+  return fork();
+
+}
+
+static void prefetch_hook_fork(void) {
+
+  void *fork_addr =
+      GSIZE_TO_POINTER(gum_module_find_export_by_name(NULL, "fork"));
+  intercept_hook(fork_addr, prefetch_on_fork, NULL);
+
+}
+
 void prefetch_init(void) {
 
   g_assert_cmpint(sizeof(prefetch_data_t), ==, PREFETCH_SIZE);
-  gboolean prefetch = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL);
+  OKF("Instrumentation - prefetch [%c]", prefetch_enable ? 'X' : ' ');
 
-  OKF("Instrumentation - prefetch [%c]", prefetch ? 'X' : ' ');
-
-  if (!prefetch) { return; }
+  if (!prefetch_enable) { return; }
   /*
    * Make our shared memory, we can attach before we fork, just like AFL does
    * with the coverage bitmap region and fork will take care of ensuring both
@@ -108,5 +129,7 @@ void prefetch_init(void) {
   /* Clear it, not sure it's necessary, just seems like good practice */
   memset(prefetch_data, '\0', sizeof(prefetch_data_t));
 
+  prefetch_hook_fork();
+
 }
 
diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c
index ef25b371..534f202b 100644
--- a/frida_mode/src/ranges.c
+++ b/frida_mode/src/ranges.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
@@ -17,11 +17,14 @@ typedef struct {
 
 } convert_name_ctx_t;
 
-GArray *module_ranges = NULL;
-GArray *libs_ranges = NULL;
-GArray *include_ranges = NULL;
-GArray *exclude_ranges = NULL;
-GArray *ranges = NULL;
+gboolean ranges_debug_maps = FALSE;
+gboolean ranges_inst_libs = FALSE;
+
+static GArray *module_ranges = NULL;
+static GArray *libs_ranges = NULL;
+static GArray *include_ranges = NULL;
+static GArray *exclude_ranges = NULL;
+static GArray *ranges = NULL;
 
 static void convert_address_token(gchar *token, GumMemoryRange *range) {
 
@@ -225,6 +228,43 @@ static GArray *collect_module_ranges(void) {
 
 }
 
+static void check_for_overlaps(GArray *array) {
+
+  for (guint i = 1; i < array->len; i++) {
+
+    GumMemoryRange *prev = &g_array_index(array, GumMemoryRange, i - 1);
+    GumMemoryRange *curr = &g_array_index(array, GumMemoryRange, i);
+    GumAddress      prev_limit = prev->base_address + prev->size;
+    GumAddress      curr_limit = curr->base_address + curr->size;
+    if (prev_limit > curr->base_address) {
+
+      FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER
+            "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER
+            "x-0x%016" G_GINT64_MODIFIER "x",
+            prev->base_address, prev_limit, curr->base_address, curr_limit);
+
+    }
+
+  }
+
+}
+
+void ranges_add_include(GumMemoryRange *range) {
+
+  g_array_append_val(include_ranges, *range);
+  g_array_sort(include_ranges, range_sort);
+  check_for_overlaps(include_ranges);
+
+}
+
+void ranges_add_exclude(GumMemoryRange *range) {
+
+  g_array_append_val(exclude_ranges, *range);
+  g_array_sort(exclude_ranges, range_sort);
+  check_for_overlaps(exclude_ranges);
+
+}
+
 static GArray *collect_ranges(char *env_key) {
 
   char *         env_val;
@@ -253,23 +293,7 @@ static GArray *collect_ranges(char *env_key) {
 
   g_array_sort(result, range_sort);
 
-  /* Check for overlaps */
-  for (i = 1; i < token_count; i++) {
-
-    GumMemoryRange *prev = &g_array_index(result, GumMemoryRange, i - 1);
-    GumMemoryRange *curr = &g_array_index(result, GumMemoryRange, i);
-    GumAddress      prev_limit = prev->base_address + prev->size;
-    GumAddress      curr_limit = curr->base_address + curr->size;
-    if (prev_limit > curr->base_address) {
-
-      FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER
-            "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER
-            "x-0x%016" G_GINT64_MODIFIER "x",
-            prev->base_address, prev_limit, curr->base_address, curr_limit);
-
-    }
-
-  }
+  check_for_overlaps(result);
 
   print_ranges(env_key, result);
 
@@ -285,15 +309,15 @@ static GArray *collect_libs_ranges(void) {
   GumMemoryRange range;
   result = g_array_new(false, false, sizeof(GumMemoryRange));
 
-  if (getenv("AFL_INST_LIBS") == NULL) {
+  if (ranges_inst_libs) {
 
-    range.base_address = lib_get_text_base();
-    range.size = lib_get_text_limit() - lib_get_text_base();
+    range.base_address = 0;
+    range.size = G_MAXULONG;
 
   } else {
 
-    range.base_address = 0;
-    range.size = G_MAXULONG;
+    range.base_address = lib_get_text_base();
+    range.size = lib_get_text_limit() - lib_get_text_base();
 
   }
 
@@ -480,30 +504,13 @@ static GArray *merge_ranges(GArray *a) {
 
 }
 
-static gboolean exclude_ranges_callback(const GumRangeDetails *details,
-                                        gpointer               user_data) {
+void ranges_config(void) {
 
-  UNUSED_PARAMETER(user_data);
-  gchar *     name;
-  gboolean    found;
-  GumStalker *stalker;
-  if (details->file == NULL) { return TRUE; }
-  name = g_path_get_basename(details->file->path);
-
-  found = (g_strcmp0(name, "afl-frida-trace.so") == 0);
-  g_free(name);
-  if (!found) { return TRUE; }
-
-  stalker = stalker_get();
-  gum_stalker_exclude(stalker, details->range);
-
-  return FALSE;
+  if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { ranges_debug_maps = TRUE; }
+  if (getenv("AFL_INST_LIBS") != NULL) { ranges_inst_libs = TRUE; }
 
-}
-
-static void ranges_exclude_self(void) {
-
-  gum_process_enumerate_ranges(GUM_PAGE_EXECUTE, exclude_ranges_callback, NULL);
+  include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
+  exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
 
 }
 
@@ -515,16 +522,20 @@ void ranges_init(void) {
   GArray *       step3;
   GArray *       step4;
 
-  if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) {
+  if (ranges_debug_maps) {
 
     gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback,
                                  NULL);
 
   }
 
+  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);
+
   module_ranges = collect_module_ranges();
   libs_ranges = collect_libs_ranges();
-  include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES");
 
   /* If include ranges is empty, then assume everything is included */
   if (include_ranges->len == 0) {
@@ -535,8 +546,6 @@ void ranges_init(void) {
 
   }
 
-  exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES");
-
   /* Intersect with .text section of main executable unless AFL_INST_LIBS */
   step1 = intersect_ranges(module_ranges, libs_ranges);
   print_ranges("step1", step1);
@@ -565,9 +574,6 @@ void ranges_init(void) {
   g_array_free(step2, TRUE);
   g_array_free(step1, TRUE);
 
-  /* *NEVER* stalk the stalker, only bad things will ever come of this! */
-  ranges_exclude_self();
-
   ranges_exclude();
 
 }
diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c
index 63f3c529..98483cde 100644
--- a/frida_mode/src/stalker.c
+++ b/frida_mode/src/stalker.c
@@ -2,18 +2,47 @@
 
 #include "instrument.h"
 #include "stalker.h"
+#include "util.h"
 
 static GumStalker *stalker = NULL;
 
-void stalker_init(void) {
+void stalker_config(void) {
 
   if (!gum_stalker_is_supported()) { FATAL("Failed to initialize embedded"); }
 
+}
+
+static gboolean stalker_exclude_self(const GumRangeDetails *details,
+                                     gpointer               user_data) {
+
+  UNUSED_PARAMETER(user_data);
+  gchar *     name;
+  gboolean    found;
+  GumStalker *stalker;
+  if (details->file == NULL) { return TRUE; }
+  name = g_path_get_basename(details->file->path);
+
+  found = (g_strcmp0(name, "afl-frida-trace.so") == 0);
+  g_free(name);
+  if (!found) { return TRUE; }
+
+  stalker = stalker_get();
+  gum_stalker_exclude(stalker, details->range);
+
+  return FALSE;
+
+}
+
+void stalker_init(void) {
+
   stalker = gum_stalker_new();
   if (stalker == NULL) { FATAL("Failed to initialize stalker"); }
 
   gum_stalker_set_trust_threshold(stalker, 0);
 
+  /* *NEVER* stalk the stalker, only bad things will ever come of this! */
+  gum_process_enumerate_ranges(GUM_PAGE_EXECUTE, stalker_exclude_self, NULL);
+
 }
 
 GumStalker *stalker_get(void) {
diff --git a/frida_mode/src/stats/stats.c b/frida_mode/src/stats/stats.c
index 0d7b9fb0..0dd8be70 100644
--- a/frida_mode/src/stats/stats.c
+++ b/frida_mode/src/stats/stats.c
@@ -5,7 +5,7 @@
 #include <sys/shm.h>
 #include <sys/mman.h>
 
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "config.h"
 #include "debug.h"
@@ -17,15 +17,16 @@
 
 stats_data_header_t *stats_data = NULL;
 
-static int      stats_parent_pid = -1;
-static int      stats_fd = -1;
-static gboolean stats_transitions = FALSE;
-static guint64  stats_interval = 0;
+static int stats_parent_pid = -1;
+static int stats_fd = -1;
 
-void stats_init(void) {
+char *   stats_filename = NULL;
+guint64  stats_interval = 0;
+gboolean stats_transitions = FALSE;
 
-  stats_parent_pid = getpid();
-  char *filename = getenv("AFL_FRIDA_STATS_FILE");
+void stats_config(void) {
+
+  stats_filename = getenv("AFL_FRIDA_STATS_FILE");
   stats_interval = util_read_num("AFL_FRIDA_STATS_INTERVAL");
   if (getenv("AFL_FRIDA_STATS_TRANSITIONS") != NULL) {
 
@@ -33,10 +34,16 @@ void stats_init(void) {
 
   }
 
-  OKF("Stats - file [%s]", filename);
+}
+
+void stats_init(void) {
+
+  stats_parent_pid = getpid();
+
+  OKF("Stats - file [%s]", stats_filename);
   OKF("Stats - interval [%" G_GINT64_MODIFIER "u]", stats_interval);
 
-  if (stats_interval != 0 && filename == NULL) {
+  if (stats_interval != 0 && stats_filename == NULL) {
 
     FATAL(
         "AFL_FRIDA_STATS_FILE must be specified if "
@@ -46,7 +53,7 @@ void stats_init(void) {
 
   if (stats_interval == 0) { stats_interval = 10; }
 
-  if (filename == NULL) { return; }
+  if (stats_filename == NULL) { return; }
 
   if (!stats_is_supported_arch()) {
 
@@ -56,11 +63,11 @@ void stats_init(void) {
 
   char *path = NULL;
 
-  if (filename == NULL) { return; }
+  if (stats_filename == NULL) { return; }
 
   if (stats_transitions) { gum_stalker_set_counters_enabled(TRUE); }
 
-  path = g_canonicalize_filename(filename, g_get_current_dir());
+  path = g_canonicalize_filename(stats_filename, g_get_current_dir());
 
   OKF("Stats - path [%s]", path);
 
diff --git a/frida_mode/src/stats/stats_arm32.c b/frida_mode/src/stats/stats_arm32.c
index 7eea7f91..71953af3 100644
--- a/frida_mode/src/stats/stats_arm32.c
+++ b/frida_mode/src/stats/stats_arm32.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/stats/stats_arm64.c b/frida_mode/src/stats/stats_arm64.c
index 592af87a..d9d374a4 100644
--- a/frida_mode/src/stats/stats_arm64.c
+++ b/frida_mode/src/stats/stats_arm64.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/stats/stats_x64.c b/frida_mode/src/stats/stats_x64.c
index c3e8742a..7c3a90d7 100644
--- a/frida_mode/src/stats/stats_x64.c
+++ b/frida_mode/src/stats/stats_x64.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/src/stats/stats_x86.c b/frida_mode/src/stats/stats_x86.c
index 1906e809..d9c4f652 100644
--- a/frida_mode/src/stats/stats_x86.c
+++ b/frida_mode/src/stats/stats_x86.c
@@ -1,4 +1,4 @@
-#include "frida-gum.h"
+#include "frida-gumjs.h"
 
 #include "debug.h"
 
diff --git a/frida_mode/test/deferred/GNUmakefile b/frida_mode/test/deferred/GNUmakefile
index c268ef66..ae580e3f 100644
--- a/frida_mode/test/deferred/GNUmakefile
+++ b/frida_mode/test/deferred/GNUmakefile
@@ -37,7 +37,7 @@ ifeq "$(ARCH)" "x86"
  AFL_ENTRYPOINT=$(shell $(GET_SYMBOL_ADDR) -f $(TESTINSTBIN) -s run -b 0x56555000)
 endif
 
-.PHONY: all clean qemu frida
+.PHONY: all clean frida
 
 all: $(TESTINSTBIN)
 	make -C $(ROOT)frida_mode/
diff --git a/frida_mode/test/js/GNUmakefile b/frida_mode/test/js/GNUmakefile
new file mode 100644
index 00000000..8ea71656
--- /dev/null
+++ b/frida_mode/test/js/GNUmakefile
@@ -0,0 +1,44 @@
+PWD:=$(shell pwd)/
+ROOT:=$(shell realpath $(PWD)../../..)/
+BUILD_DIR:=$(PWD)build/
+TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/
+TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in
+
+TESTINSTBIN:=$(BUILD_DIR)testinstr
+TESTINSTSRC:=$(PWD)testinstr.c
+
+QEMU_OUT:=$(BUILD_DIR)qemu-out
+FRIDA_OUT:=$(BUILD_DIR)frida-out
+
+.PHONY: all 32 clean qemu frida
+
+all: $(TESTINSTBIN)
+	make -C $(ROOT)frida_mode/
+
+32:
+	CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
+
+$(BUILD_DIR):
+	mkdir -p $@
+
+$(TESTINSTR_DATA_DIR): | $(BUILD_DIR)
+	mkdir -p $@
+
+$(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
+	echo -n "000" > $@
+
+$(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+clean:
+	rm -rf $(BUILD_DIR)
+
+frida: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+	AFL_FRIDA_JS_SCRIPT=test.js \
+	$(ROOT)afl-fuzz \
+		-D \
+		-O \
+		-i $(TESTINSTR_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-- \
+			$(TESTINSTBIN) @@
diff --git a/frida_mode/test/js/Makefile b/frida_mode/test/js/Makefile
new file mode 100644
index 00000000..7a237f99
--- /dev/null
+++ b/frida_mode/test/js/Makefile
@@ -0,0 +1,16 @@
+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/js/test.js b/frida_mode/test/js/test.js
new file mode 100644
index 00000000..f10ef2d1
--- /dev/null
+++ b/frida_mode/test/js/test.js
@@ -0,0 +1,20 @@
+Afl.print('******************');
+Afl.print('* AFL FRIDA MODE *');
+Afl.print('******************');
+Afl.print('');
+
+Afl.print(`PID: ${Process.id}`);
+
+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}`);
+
+Afl.setEntryPoint(entry_point.address);
+
+// Afl.error('HARD NOPE');
+
+Afl.done();
+Afl.print("done");
diff --git a/frida_mode/test/js/testinstr.c b/frida_mode/test/js/testinstr.c
new file mode 100644
index 00000000..bd605c52
--- /dev/null
+++ b/frida_mode/test/js/testinstr.c
@@ -0,0 +1,121 @@
+/*
+   american fuzzy lop++ - a trivial program to test the build
+   --------------------------------------------------------
+   Originally written by Michal Zalewski
+   Copyright 2014 Google Inc. All rights reserved.
+   Copyright 2019-2020 AFLplusplus Project. All rights reserved.
+   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
+ */
+
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifdef __APPLE__
+  #define TESTINSTR_SECTION
+#else
+  #define TESTINSTR_SECTION __attribute__((section(".testinstr")))
+#endif
+
+void testinstr(char *buf, int len) {
+
+  if (len < 1) return;
+  buf[len] = 0;
+
+  // we support three input cases
+  if (buf[0] == '0')
+    printf("Looks like a zero to me!\n");
+  else if (buf[0] == '1')
+    printf("Pretty sure that is a one!\n");
+  else
+    printf("Neither one or zero? How quaint!\n");
+
+}
+
+int run(char *file) {
+
+  int    fd = -1;
+  off_t  len;
+  char * buf = NULL;
+  size_t n_read;
+  int    result = -1;
+
+  do {
+
+    dprintf(STDERR_FILENO, "Running: %s\n", file);
+
+    fd = open(file, O_RDONLY);
+    if (fd < 0) {
+
+      perror("open");
+      break;
+
+    }
+
+    len = lseek(fd, 0, SEEK_END);
+    if (len < 0) {
+
+      perror("lseek (SEEK_END)");
+      break;
+
+    }
+
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+
+      perror("lseek (SEEK_SET)");
+      break;
+
+    }
+
+    buf = malloc(len);
+    if (buf == NULL) {
+
+      perror("malloc");
+      break;
+
+    }
+
+    n_read = read(fd, buf, len);
+    if (n_read != len) {
+
+      perror("read");
+      break;
+
+    }
+
+    dprintf(STDERR_FILENO, "Running:    %s: (%zd bytes)\n", file, n_read);
+
+    testinstr(buf, len);
+    dprintf(STDERR_FILENO, "Done:    %s: (%zd bytes)\n", file, n_read);
+
+    result = 0;
+
+  } while (false);
+
+  if (buf != NULL) { free(buf); }
+
+  if (fd != -1) { close(fd); }
+
+  return result;
+
+}
+
+void slow() {
+
+  usleep(100000);
+
+}
+
+int main(int argc, char **argv) {
+
+  if (argc != 2) { return 1; }
+  slow();
+  return run(argv[1]);
+
+}
+
diff --git a/frida_mode/test/persistent_ret/GNUmakefile b/frida_mode/test/persistent_ret/GNUmakefile
index 2de51d86..81fdd069 100644
--- a/frida_mode/test/persistent_ret/GNUmakefile
+++ b/frida_mode/test/persistent_ret/GNUmakefile
@@ -82,6 +82,16 @@ frida_ret: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
 		-- \
 			$(TESTINSTBIN) @@
 
+frida_js: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
+	AFL_FRIDA_JS_SCRIPT=test.js \
+	$(ROOT)afl-fuzz \
+		-D \
+		-O \
+		-i $(TESTINSTR_DATA_DIR) \
+		-o $(FRIDA_OUT) \
+		-- \
+			$(TESTINSTBIN) @@
+
 debug: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
 	gdb \
 		--ex 'set environment AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR)' \
diff --git a/frida_mode/test/persistent_ret/test.js b/frida_mode/test/persistent_ret/test.js
new file mode 100644
index 00000000..43c6ad7c
--- /dev/null
+++ b/frida_mode/test/persistent_ret/test.js
@@ -0,0 +1,38 @@
+Afl.print('******************');
+Afl.print('* AFL FRIDA MODE *');
+Afl.print('******************');
+Afl.print('');
+
+Afl.print(`PID: ${Process.id}`);
+
+new ModuleMap().values().forEach(m => {
+    Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`);
+});
+
+const persistent_addr = DebugSymbol.fromName('main');
+Afl.print(`persistent_addr: ${persistent_addr.address}`);
+
+const persistent_ret = DebugSymbol.fromName('slow');
+Afl.print(`persistent_ret: ${persistent_ret.address}`);
+
+Afl.setPersistentAddress(persistent_addr.address);
+Afl.setPersistentReturn(persistent_ret.address);
+Afl.setPersistentCount(1000000);
+
+Afl.setDebugMaps();
+
+const mod = Process.findModuleByName("libc-2.31.so")
+Afl.addExcludedRange(mod.base, mod.size);
+Afl.setInstrumentLibraries();
+Afl.setInstrumentDebugFile("/tmp/instr.log");
+Afl.setPrefetchDisable();
+Afl.setInstrumentNoOptimize();
+Afl.setInstrumentEnableTracing();
+Afl.setInstrumentTracingUnique();
+Afl.setStdOut("/tmp/stdout.txt");
+Afl.setStdErr("/tmp/stderr.txt");
+Afl.setStatsFile("/tmp/stats.txt");
+Afl.setStatsInterval(1);
+Afl.setStatsTransitions();
+Afl.done();
+Afl.print("done");
diff --git a/include/envs.h b/include/envs.h
index 54bb6597..f89e8e62 100644
--- a/include/envs.h
+++ b/include/envs.h
@@ -60,7 +60,8 @@ static char *afl_environment_variables[] = {
     "AFL_FRIDA_INST_NO_PREFETCH",
     "AFL_FRIDA_INST_RANGES",
     "AFL_FRIDA_INST_TRACE",
-    "AFL_FRIDA_INST_UNSTABLE",
+    "AFL_FRIDA_INST_TRACE_UNIQUE",
+    "AFL_FRIDA_JS_SCRIPT",
     "AFL_FRIDA_OUTPUT_STDOUT",
     "AFL_FRIDA_OUTPUT_STDERR",
     "AFL_FRIDA_PERSISTENT_ADDR",