aboutsummaryrefslogtreecommitdiff
path: root/frida_mode/src
diff options
context:
space:
mode:
authorvanhauser-thc <vh@thc.org>2021-07-19 10:48:41 +0200
committervanhauser-thc <vh@thc.org>2021-07-19 10:48:41 +0200
commitcc57cc5f463e9b79980c2087d19b4a1e1360ec52 (patch)
tree69a89651deefc660b481e9c964f4cb97ab9073b6 /frida_mode/src
parent3d1cc8ec57f0bf07d7834b652ec2db24e7914624 (diff)
parentc55f7af65700e3d11c368072d39ba6670efa477b (diff)
downloadafl++-cc57cc5f463e9b79980c2087d19b4a1e1360ec52.tar.gz
fix merge conflicts
Diffstat (limited to 'frida_mode/src')
-rw-r--r--frida_mode/src/asan/asan_arm32.c28
-rw-r--r--frida_mode/src/cmplog/cmplog_arm32.c19
-rw-r--r--frida_mode/src/ctx/ctx_arm32.c16
-rw-r--r--frida_mode/src/ctx/ctx_arm64.c303
-rw-r--r--frida_mode/src/intercept.c35
-rw-r--r--frida_mode/src/js/api.js257
-rw-r--r--frida_mode/src/js/js.c142
-rw-r--r--frida_mode/src/js/js_api.c201
-rw-r--r--frida_mode/src/stats/stats_arm32.c36
9 files changed, 1037 insertions, 0 deletions
diff --git a/frida_mode/src/asan/asan_arm32.c b/frida_mode/src/asan/asan_arm32.c
new file mode 100644
index 00000000..f5fa4713
--- /dev/null
+++ b/frida_mode/src/asan/asan_arm32.c
@@ -0,0 +1,28 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "asan.h"
+#include "util.h"
+
+#if defined(__arm__)
+void asan_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
+
+ UNUSED_PARAMETER(instr);
+ UNUSED_PARAMETER(iterator);
+ if (asan_initialized) {
+
+ FATAL("ASAN mode not supported on this architecture");
+
+ }
+
+}
+
+void asan_arch_init(void) {
+
+ FATAL("ASAN mode not supported on this architecture");
+
+}
+
+#endif
+
diff --git a/frida_mode/src/cmplog/cmplog_arm32.c b/frida_mode/src/cmplog/cmplog_arm32.c
new file mode 100644
index 00000000..ac703408
--- /dev/null
+++ b/frida_mode/src/cmplog/cmplog_arm32.c
@@ -0,0 +1,19 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "frida_cmplog.h"
+#include "util.h"
+
+#if defined(__arm__)
+void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) {
+
+ UNUSED_PARAMETER(instr);
+ UNUSED_PARAMETER(iterator);
+ if (__afl_cmp_map == NULL) { return; }
+ FATAL("CMPLOG mode not supported on this architecture");
+
+}
+
+#endif
+
diff --git a/frida_mode/src/ctx/ctx_arm32.c b/frida_mode/src/ctx/ctx_arm32.c
new file mode 100644
index 00000000..9fc70fb4
--- /dev/null
+++ b/frida_mode/src/ctx/ctx_arm32.c
@@ -0,0 +1,16 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "ctx.h"
+
+#if defined(__arm__)
+
+gsize ctx_read_reg(GumArmCpuContext *ctx, arm_reg reg) {
+
+ FATAL("ctx_read_reg unimplemented for this architecture");
+
+}
+
+#endif
+
diff --git a/frida_mode/src/ctx/ctx_arm64.c b/frida_mode/src/ctx/ctx_arm64.c
new file mode 100644
index 00000000..a735401b
--- /dev/null
+++ b/frida_mode/src/ctx/ctx_arm64.c
@@ -0,0 +1,303 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "ctx.h"
+
+#if defined(__aarch64__)
+
+ #define ARM64_REG_8(LABEL, REG) \
+ case LABEL: { \
+ \
+ return REG & GUM_INT8_MASK; \
+ \
+ }
+
+ #define ARM64_REG_16(LABEL, REG) \
+ case LABEL: { \
+ \
+ return (REG & GUM_INT16_MASK); \
+ \
+ }
+
+ #define ARM64_REG_32(LABEL, REG) \
+ case LABEL: { \
+ \
+ return (REG & GUM_INT32_MASK); \
+ \
+ }
+
+ #define ARM64_REG_64(LABEL, REG) \
+ case LABEL: { \
+ \
+ return (REG); \
+ \
+ }
+
+gsize ctx_read_reg(GumArm64CpuContext *ctx, arm64_reg reg) {
+
+ switch (reg) {
+
+ case ARM64_REG_WZR:
+ case ARM64_REG_XZR:
+ return 0;
+
+ ARM64_REG_8(ARM64_REG_B0, ctx->x[0])
+ ARM64_REG_8(ARM64_REG_B1, ctx->x[1])
+ ARM64_REG_8(ARM64_REG_B2, ctx->x[2])
+ ARM64_REG_8(ARM64_REG_B3, ctx->x[3])
+ ARM64_REG_8(ARM64_REG_B4, ctx->x[4])
+ ARM64_REG_8(ARM64_REG_B5, ctx->x[5])
+ ARM64_REG_8(ARM64_REG_B6, ctx->x[6])
+ ARM64_REG_8(ARM64_REG_B7, ctx->x[7])
+ ARM64_REG_8(ARM64_REG_B8, ctx->x[8])
+ ARM64_REG_8(ARM64_REG_B9, ctx->x[9])
+ ARM64_REG_8(ARM64_REG_B10, ctx->x[10])
+ ARM64_REG_8(ARM64_REG_B11, ctx->x[11])
+ ARM64_REG_8(ARM64_REG_B12, ctx->x[12])
+ ARM64_REG_8(ARM64_REG_B13, ctx->x[13])
+ ARM64_REG_8(ARM64_REG_B14, ctx->x[14])
+ ARM64_REG_8(ARM64_REG_B15, ctx->x[15])
+ ARM64_REG_8(ARM64_REG_B16, ctx->x[16])
+ ARM64_REG_8(ARM64_REG_B17, ctx->x[17])
+ ARM64_REG_8(ARM64_REG_B18, ctx->x[18])
+ ARM64_REG_8(ARM64_REG_B19, ctx->x[19])
+ ARM64_REG_8(ARM64_REG_B20, ctx->x[20])
+ ARM64_REG_8(ARM64_REG_B21, ctx->x[21])
+ ARM64_REG_8(ARM64_REG_B22, ctx->x[22])
+ ARM64_REG_8(ARM64_REG_B23, ctx->x[23])
+ ARM64_REG_8(ARM64_REG_B24, ctx->x[24])
+ ARM64_REG_8(ARM64_REG_B25, ctx->x[25])
+ ARM64_REG_8(ARM64_REG_B26, ctx->x[26])
+ ARM64_REG_8(ARM64_REG_B27, ctx->x[27])
+ ARM64_REG_8(ARM64_REG_B28, ctx->x[28])
+ ARM64_REG_8(ARM64_REG_B29, ctx->fp)
+ ARM64_REG_8(ARM64_REG_B30, ctx->lr)
+ ARM64_REG_8(ARM64_REG_B31, ctx->sp)
+
+ ARM64_REG_16(ARM64_REG_H0, ctx->x[0])
+ ARM64_REG_16(ARM64_REG_H1, ctx->x[1])
+ ARM64_REG_16(ARM64_REG_H2, ctx->x[2])
+ ARM64_REG_16(ARM64_REG_H3, ctx->x[3])
+ ARM64_REG_16(ARM64_REG_H4, ctx->x[4])
+ ARM64_REG_16(ARM64_REG_H5, ctx->x[5])
+ ARM64_REG_16(ARM64_REG_H6, ctx->x[6])
+ ARM64_REG_16(ARM64_REG_H7, ctx->x[7])
+ ARM64_REG_16(ARM64_REG_H8, ctx->x[8])
+ ARM64_REG_16(ARM64_REG_H9, ctx->x[9])
+ ARM64_REG_16(ARM64_REG_H10, ctx->x[10])
+ ARM64_REG_16(ARM64_REG_H11, ctx->x[11])
+ ARM64_REG_16(ARM64_REG_H12, ctx->x[12])
+ ARM64_REG_16(ARM64_REG_H13, ctx->x[13])
+ ARM64_REG_16(ARM64_REG_H14, ctx->x[14])
+ ARM64_REG_16(ARM64_REG_H15, ctx->x[15])
+ ARM64_REG_16(ARM64_REG_H16, ctx->x[16])
+ ARM64_REG_16(ARM64_REG_H17, ctx->x[17])
+ ARM64_REG_16(ARM64_REG_H18, ctx->x[18])
+ ARM64_REG_16(ARM64_REG_H19, ctx->x[19])
+ ARM64_REG_16(ARM64_REG_H20, ctx->x[20])
+ ARM64_REG_16(ARM64_REG_H21, ctx->x[21])
+ ARM64_REG_16(ARM64_REG_H22, ctx->x[22])
+ ARM64_REG_16(ARM64_REG_H23, ctx->x[23])
+ ARM64_REG_16(ARM64_REG_H24, ctx->x[24])
+ ARM64_REG_16(ARM64_REG_H25, ctx->x[25])
+ ARM64_REG_16(ARM64_REG_H26, ctx->x[26])
+ ARM64_REG_16(ARM64_REG_H27, ctx->x[27])
+ ARM64_REG_16(ARM64_REG_H28, ctx->x[28])
+ ARM64_REG_16(ARM64_REG_H29, ctx->fp)
+ ARM64_REG_16(ARM64_REG_H30, ctx->lr)
+ ARM64_REG_16(ARM64_REG_H31, ctx->sp)
+
+ ARM64_REG_32(ARM64_REG_W0, ctx->x[0])
+ ARM64_REG_32(ARM64_REG_W1, ctx->x[1])
+ ARM64_REG_32(ARM64_REG_W2, ctx->x[2])
+ ARM64_REG_32(ARM64_REG_W3, ctx->x[3])
+ ARM64_REG_32(ARM64_REG_W4, ctx->x[4])
+ ARM64_REG_32(ARM64_REG_W5, ctx->x[5])
+ ARM64_REG_32(ARM64_REG_W6, ctx->x[6])
+ ARM64_REG_32(ARM64_REG_W7, ctx->x[7])
+ ARM64_REG_32(ARM64_REG_W8, ctx->x[8])
+ ARM64_REG_32(ARM64_REG_W9, ctx->x[9])
+ ARM64_REG_32(ARM64_REG_W10, ctx->x[10])
+ ARM64_REG_32(ARM64_REG_W11, ctx->x[11])
+ ARM64_REG_32(ARM64_REG_W12, ctx->x[12])
+ ARM64_REG_32(ARM64_REG_W13, ctx->x[13])
+ ARM64_REG_32(ARM64_REG_W14, ctx->x[14])
+ ARM64_REG_32(ARM64_REG_W15, ctx->x[15])
+ ARM64_REG_32(ARM64_REG_W16, ctx->x[16])
+ ARM64_REG_32(ARM64_REG_W17, ctx->x[17])
+ ARM64_REG_32(ARM64_REG_W18, ctx->x[18])
+ ARM64_REG_32(ARM64_REG_W19, ctx->x[19])
+ ARM64_REG_32(ARM64_REG_W20, ctx->x[20])
+ ARM64_REG_32(ARM64_REG_W21, ctx->x[21])
+ ARM64_REG_32(ARM64_REG_W22, ctx->x[22])
+ ARM64_REG_32(ARM64_REG_W23, ctx->x[23])
+ ARM64_REG_32(ARM64_REG_W24, ctx->x[24])
+ ARM64_REG_32(ARM64_REG_W25, ctx->x[25])
+ ARM64_REG_32(ARM64_REG_W26, ctx->x[26])
+ ARM64_REG_32(ARM64_REG_W27, ctx->x[27])
+ ARM64_REG_32(ARM64_REG_W28, ctx->x[28])
+ ARM64_REG_32(ARM64_REG_W29, ctx->fp)
+ ARM64_REG_32(ARM64_REG_W30, ctx->lr)
+
+ ARM64_REG_64(ARM64_REG_X0, ctx->x[0])
+ ARM64_REG_64(ARM64_REG_X1, ctx->x[1])
+ ARM64_REG_64(ARM64_REG_X2, ctx->x[2])
+ ARM64_REG_64(ARM64_REG_X3, ctx->x[3])
+ ARM64_REG_64(ARM64_REG_X4, ctx->x[4])
+ ARM64_REG_64(ARM64_REG_X5, ctx->x[5])
+ ARM64_REG_64(ARM64_REG_X6, ctx->x[6])
+ ARM64_REG_64(ARM64_REG_X7, ctx->x[7])
+ ARM64_REG_64(ARM64_REG_X8, ctx->x[8])
+ ARM64_REG_64(ARM64_REG_X9, ctx->x[9])
+ ARM64_REG_64(ARM64_REG_X10, ctx->x[10])
+ ARM64_REG_64(ARM64_REG_X11, ctx->x[11])
+ ARM64_REG_64(ARM64_REG_X12, ctx->x[12])
+ ARM64_REG_64(ARM64_REG_X13, ctx->x[13])
+ ARM64_REG_64(ARM64_REG_X14, ctx->x[14])
+ ARM64_REG_64(ARM64_REG_X15, ctx->x[15])
+ ARM64_REG_64(ARM64_REG_X16, ctx->x[16])
+ ARM64_REG_64(ARM64_REG_X17, ctx->x[17])
+ ARM64_REG_64(ARM64_REG_X18, ctx->x[18])
+ ARM64_REG_64(ARM64_REG_X19, ctx->x[19])
+ ARM64_REG_64(ARM64_REG_X20, ctx->x[20])
+ ARM64_REG_64(ARM64_REG_X21, ctx->x[21])
+ ARM64_REG_64(ARM64_REG_X22, ctx->x[22])
+ ARM64_REG_64(ARM64_REG_X23, ctx->x[23])
+ ARM64_REG_64(ARM64_REG_X24, ctx->x[24])
+ ARM64_REG_64(ARM64_REG_X25, ctx->x[25])
+ ARM64_REG_64(ARM64_REG_X26, ctx->x[26])
+ ARM64_REG_64(ARM64_REG_X27, ctx->x[27])
+ ARM64_REG_64(ARM64_REG_X28, ctx->x[28])
+ ARM64_REG_64(ARM64_REG_FP, ctx->fp)
+ ARM64_REG_64(ARM64_REG_LR, ctx->lr)
+ ARM64_REG_64(ARM64_REG_SP, ctx->sp)
+
+ default:
+ FATAL("Failed to read register: %d", reg);
+ return 0;
+
+ }
+
+}
+
+size_t ctx_get_size(const cs_insn *instr, cs_arm64_op *operand) {
+
+ uint8_t num_registers;
+ uint8_t count_byte;
+ char vas_digit;
+ size_t mnemonic_len;
+
+ switch (instr->id) {
+
+ case ARM64_INS_STP:
+ case ARM64_INS_STXP:
+ case ARM64_INS_STNP:
+ case ARM64_INS_STLXP:
+ case ARM64_INS_LDP:
+ case ARM64_INS_LDXP:
+ case ARM64_INS_LDNP:
+ num_registers = 2;
+ break;
+ default:
+ num_registers = 1;
+ break;
+
+ }
+
+ mnemonic_len = strlen(instr->mnemonic);
+ if (mnemonic_len == 0) { FATAL("No mnemonic found"); };
+
+ char last = instr->mnemonic[mnemonic_len - 1];
+ switch (last) {
+
+ case 'b':
+ return 1;
+ case 'h':
+ return 2;
+ case 'w':
+ return 4 * num_registers;
+
+ }
+
+ if (operand->vas == ARM64_VAS_INVALID) {
+
+ if (operand->type == ARM64_OP_REG) {
+
+ switch (operand->reg) {
+
+ case ARM64_REG_WZR:
+ case ARM64_REG_WSP:
+ case ARM64_REG_W0 ... ARM64_REG_W30:
+ case ARM64_REG_S0 ... ARM64_REG_S31:
+ return 4 * num_registers;
+ case ARM64_REG_D0 ... ARM64_REG_D31:
+ return 8 * num_registers;
+ case ARM64_REG_Q0 ... ARM64_REG_Q31:
+ return 16;
+ default:
+ return 8 * num_registers;
+ ;
+
+ }
+
+ }
+
+ return 8 * num_registers;
+
+ }
+
+ if (g_str_has_prefix(instr->mnemonic, "st") ||
+ g_str_has_prefix(instr->mnemonic, "ld")) {
+
+ if (mnemonic_len < 3) {
+
+ FATAL("VAS Mnemonic too short: %s\n", instr->mnemonic);
+
+ }
+
+ vas_digit = instr->mnemonic[2];
+ if (vas_digit < '0' || vas_digit > '9') {
+
+ FATAL("VAS Mnemonic digit out of range: %s\n", instr->mnemonic);
+
+ }
+
+ count_byte = vas_digit - '0';
+
+ } else {
+
+ count_byte = 1;
+
+ }
+
+ switch (operand->vas) {
+
+ case ARM64_VAS_1B:
+ return 1 * count_byte;
+ case ARM64_VAS_1H:
+ return 2 * count_byte;
+ case ARM64_VAS_4B:
+ case ARM64_VAS_1S:
+ case ARM64_VAS_1D:
+ case ARM64_VAS_2H:
+ return 4 * count_byte;
+ case ARM64_VAS_8B:
+ case ARM64_VAS_4H:
+ case ARM64_VAS_2S:
+ case ARM64_VAS_2D:
+ case ARM64_VAS_1Q:
+ return 8 * count_byte;
+ case ARM64_VAS_8H:
+ case ARM64_VAS_4S:
+ case ARM64_VAS_16B:
+ return 16 * count_byte;
+ default:
+ FATAL("Unexpected VAS type: %s %d", instr->mnemonic, operand->vas);
+
+ }
+
+}
+
+#endif
+
diff --git a/frida_mode/src/intercept.c b/frida_mode/src/intercept.c
new file mode 100644
index 00000000..ed8d27bd
--- /dev/null
+++ b/frida_mode/src/intercept.c
@@ -0,0 +1,35 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "intercept.h"
+
+void intercept_hook(void *address, gpointer replacement, gpointer user_data) {
+
+ GumInterceptor *interceptor = gum_interceptor_obtain();
+ gum_interceptor_begin_transaction(interceptor);
+ GumReplaceReturn ret =
+ gum_interceptor_replace(interceptor, address, replacement, user_data);
+ if (ret != GUM_REPLACE_OK) { FATAL("gum_interceptor_attach: %d", ret); }
+ gum_interceptor_end_transaction(interceptor);
+
+}
+
+void intercept_unhook(void *address) {
+
+ GumInterceptor *interceptor = gum_interceptor_obtain();
+
+ gum_interceptor_begin_transaction(interceptor);
+ gum_interceptor_revert(interceptor, address);
+ gum_interceptor_end_transaction(interceptor);
+ gum_interceptor_flush(interceptor);
+
+}
+
+void intercept_unhook_self(void) {
+
+ GumInvocationContext *ctx = gum_interceptor_get_current_invocation();
+ 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..b8f2d39a
--- /dev/null
+++ b/frida_mode/src/js/api.js
@@ -0,0 +1,257 @@
+"use strict";
+class Afl {
+ /**
+ * 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.
+ */
+ static addExcludedRange(addressess, size) {
+ Afl.jsApiAddExcludeRange(addressess, size);
+ }
+ /**
+ * 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.
+ */
+ static addIncludedRange(addressess, size) {
+ Afl.jsApiAddIncludeRange(addressess, size);
+ }
+ /**
+ * 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.
+ */
+ static done() {
+ Afl.jsApiDone();
+ }
+ /**
+ * 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.
+ */
+ static error(msg) {
+ const buf = Memory.allocUtf8String(msg);
+ Afl.jsApiError(buf);
+ }
+ /**
+ * Function used to provide access to `__afl_fuzz_ptr`, which contains the length of
+ * fuzzing data when using in-memory test case fuzzing.
+ */
+ static getAflFuzzLen() {
+ return Afl.jsApiGetSymbol("__afl_fuzz_len");
+ }
+ /**
+ * Function used to provide access to `__afl_fuzz_ptr`, which contains the fuzzing
+ * data when using in-memory test case fuzzing.
+ */
+ static getAflFuzzPtr() {
+ return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
+ }
+ /**
+ * 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.
+ */
+ static print(msg) {
+ const STDOUT_FILENO = 2;
+ const log = `${msg}\n`;
+ const buf = Memory.allocUtf8String(log);
+ Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
+ }
+ /**
+ * See `AFL_FRIDA_DEBUG_MAPS`.
+ */
+ static setDebugMaps() {
+ Afl.jsApiSetDebugMaps();
+ }
+ /**
+ * 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.
+ */
+ static setEntryPoint(address) {
+ Afl.jsApiSetEntryPoint(address);
+ }
+ /**
+ * Function used to enable in-memory test cases for fuzzing.
+ */
+ static setInMemoryFuzzing() {
+ Afl.jsApiAflSharedMemFuzzing.writeInt(1);
+ }
+ /**
+ * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
+ * an argument.
+ */
+ static setInstrumentDebugFile(file) {
+ const buf = Memory.allocUtf8String(file);
+ Afl.jsApiSetInstrumentDebugFile(buf);
+ }
+ /**
+ * See `AFL_FRIDA_INST_TRACE`.
+ */
+ static setInstrumentEnableTracing() {
+ Afl.jsApiSetInstrumentTrace();
+ }
+ /**
+ * See `AFL_FRIDA_INST_JIT`.
+ */
+ static setInstrumentJit() {
+ Afl.jsApiSetInstrumentJit();
+ }
+ /**
+ * See `AFL_INST_LIBS`.
+ */
+ static setInstrumentLibraries() {
+ Afl.jsApiSetInstrumentLibraries();
+ }
+ /**
+ * See `AFL_FRIDA_INST_NO_OPTIMIZE`
+ */
+ static setInstrumentNoOptimize() {
+ Afl.jsApiSetInstrumentNoOptimize();
+ }
+ /*
+ * See `AFL_FRIDA_INST_SEED`
+ */
+ static setInstrumentSeed(seed) {
+ Afl.jsApiSetInstrumentSeed(seed);
+ }
+ /**
+ * See `AFL_FRIDA_INST_TRACE_UNIQUE`.
+ */
+ static setInstrumentTracingUnique() {
+ Afl.jsApiSetInstrumentTraceUnique();
+ }
+ /**
+ * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
+ * `NativePointer` should be provided as it's argument.
+ */
+ static setPersistentAddress(address) {
+ Afl.jsApiSetPersistentAddress(address);
+ }
+ /**
+ * This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
+ * `number` should be provided as it's argument.
+ */
+ static setPersistentCount(count) {
+ Afl.jsApiSetPersistentCount(count);
+ }
+ /**
+ * See `AFL_FRIDA_PERSISTENT_DEBUG`.
+ */
+ static setPersistentDebug() {
+ Afl.jsApiSetPersistentDebug();
+ }
+ /**
+ * See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an
+ * argument. See above for examples of use.
+ */
+ static setPersistentHook(address) {
+ Afl.jsApiSetPersistentHook(address);
+ }
+ /**
+ * This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a
+ * `NativePointer` should be provided as it's argument.
+ */
+ static setPersistentReturn(address) {
+ Afl.jsApiSetPersistentReturn(address);
+ }
+ /**
+ * See `AFL_FRIDA_INST_NO_PREFETCH`.
+ */
+ static setPrefetchDisable() {
+ Afl.jsApiSetPrefetchDisable();
+ }
+ /*
+ * Set a function to be called for each instruction which is instrumented
+ * by AFL FRIDA mode.
+ */
+ static setStalkerCallback(callback) {
+ Afl.jsApiSetStalkerCallback(callback);
+ }
+ /**
+ * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
+ * an argument.
+ */
+ static setStatsFile(file) {
+ const buf = Memory.allocUtf8String(file);
+ Afl.jsApiSetStatsFile(buf);
+ }
+ /**
+ * See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
+ * argument
+ */
+ static setStatsInterval(interval) {
+ Afl.jsApiSetStatsInterval(interval);
+ }
+ /**
+ * See `AFL_FRIDA_STATS_TRANSITIONS`
+ */
+ static setStatsTransitions() {
+ Afl.jsApiSetStatsTransitions();
+ }
+ /**
+ * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
+ * an argument.
+ */
+ static setStdErr(file) {
+ const buf = Memory.allocUtf8String(file);
+ Afl.jsApiSetStdErr(buf);
+ }
+ /**
+ * See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as
+ * an argument.
+ */
+ static setStdOut(file) {
+ const buf = Memory.allocUtf8String(file);
+ Afl.jsApiSetStdOut(buf);
+ }
+ static jsApiGetFunction(name, retType, argTypes) {
+ const addr = Afl.module.getExportByName(name);
+ return new NativeFunction(addr, retType, argTypes);
+ }
+ static jsApiGetSymbol(name) {
+ return Afl.module.getExportByName(name);
+ }
+}
+/**
+ * Field containing the `Module` object for `afl-frida-trace.so` (the FRIDA mode
+ * implementation).
+ */
+Afl.module = Process.getModuleByName("afl-frida-trace.so");
+Afl.jsApiAddExcludeRange = Afl.jsApiGetFunction("js_api_add_exclude_range", "void", ["pointer", "size_t"]);
+Afl.jsApiAddIncludeRange = Afl.jsApiGetFunction("js_api_add_include_range", "void", ["pointer", "size_t"]);
+Afl.jsApiAflSharedMemFuzzing = Afl.jsApiGetSymbol("__afl_sharedmem_fuzzing");
+Afl.jsApiDone = Afl.jsApiGetFunction("js_api_done", "void", []);
+Afl.jsApiError = Afl.jsApiGetFunction("js_api_error", "void", ["pointer"]);
+Afl.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.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed", "void", ["uint64"]);
+Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
+Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []);
+Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]);
+Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]);
+Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []);
+Afl.jsApiSetPersistentHook = Afl.jsApiGetFunction("js_api_set_persistent_hook", "void", ["pointer"]);
+Afl.jsApiSetPersistentReturn = Afl.jsApiGetFunction("js_api_set_persistent_return", "void", ["pointer"]);
+Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []);
+Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]);
+Afl.jsApiSetStatsFile = Afl.jsApiGetFunction("js_api_set_stats_file", "void", ["pointer"]);
+Afl.jsApiSetStatsInterval = Afl.jsApiGetFunction("js_api_set_stats_interval", "void", ["uint64"]);
+Afl.jsApiSetStatsTransitions = Afl.jsApiGetFunction("js_api_set_stats_transitions", "void", []);
+Afl.jsApiSetStdErr = Afl.jsApiGetFunction("js_api_set_stderr", "void", ["pointer"]);
+Afl.jsApiSetStdOut = Afl.jsApiGetFunction("js_api_set_stdout", "void", ["pointer"]);
+Afl.jsApiWrite = new NativeFunction(
+/* tslint:disable-next-line:no-null-keyword */
+Module.getExportByName(null, "write"), "int", ["int", "pointer", "int"]);
diff --git a/frida_mode/src/js/js.c b/frida_mode/src/js/js.c
new file mode 100644
index 00000000..e3cd4933
--- /dev/null
+++ b/frida_mode/src/js/js.c
@@ -0,0 +1,142 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "js.h"
+#include "util.h"
+
+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 GumScriptScheduler *scheduler;
+static GMainContext * context;
+static GMainLoop * main_loop;
+
+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);
+
+}
+
+static void load_cb(GObject *source_object, GAsyncResult *result,
+ gpointer user_data) {
+
+ 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); }
+
+}
+
+static void create_cb(GObject *source_object, GAsyncResult *result,
+ gpointer user_data) {
+
+ 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); }
+
+ gum_script_set_message_handler(script, js_msg, NULL, NULL);
+
+ gum_script_load(script, cancellable, load_cb, 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_backend_create(backend, "example", source, cancellable, create_cb,
+ &error);
+
+ while (g_main_context_pending(context))
+ g_main_context_iteration(context, FALSE);
+
+ if (!js_done) { FATAL("Script didn't call Afl.done()"); }
+
+}
+
+gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
+ gboolean excluded, GumStalkerOutput *output) {
+
+ if (js_user_callback == NULL) { return TRUE; }
+ 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
new file mode 100644
index 00000000..930a6dc0
--- /dev/null
+++ b/frida_mode/src/js/js_api.c
@@ -0,0 +1,201 @@
+#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"
+__attribute__((visibility("default"))) void js_api_done() {
+
+ js_done = TRUE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_error(char *msg) {
+
+ FATAL("%s", msg);
+
+}
+
+__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);
+
+}
+
+__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);
+
+}
+
+__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);
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_persistent_count(
+ uint64_t count) {
+
+ persistent_count = count;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_persistent_debug() {
+
+ persistent_debug = TRUE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_debug_maps() {
+
+ ranges_debug_maps = TRUE;
+
+}
+
+__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);
+
+}
+
+__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);
+
+}
+
+__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;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_instrument_debug_file(
+ char *path) {
+
+ instrument_debug_filename = g_strdup(path);
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_prefetch_disable(void) {
+
+ prefetch_enable = FALSE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_instrument_no_optimize(
+ void) {
+
+ instrument_optimize = FALSE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_instrument_seed(
+ guint64 seed) {
+
+ instrument_use_fixed_seed = TRUE;
+ instrument_fixed_seed = seed;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_instrument_trace(void) {
+
+ instrument_tracing = TRUE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_instrument_trace_unique(
+ void) {
+
+ instrument_unique = TRUE;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_stdout(char *file) {
+
+ output_stdout = g_strdup(file);
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_stderr(char *file) {
+
+ output_stderr = g_strdup(file);
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_stats_file(char *file) {
+
+ stats_filename = g_strdup(file);
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_stats_interval(
+ uint64_t interval) {
+
+ stats_interval = interval;
+
+}
+
+__attribute__((visibility("default"))) void js_api_set_stats_transitions() {
+
+ stats_transitions = TRUE;
+
+}
+
+__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;
+
+}
+
+__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/stats/stats_arm32.c b/frida_mode/src/stats/stats_arm32.c
new file mode 100644
index 00000000..71953af3
--- /dev/null
+++ b/frida_mode/src/stats/stats_arm32.c
@@ -0,0 +1,36 @@
+#include "frida-gumjs.h"
+
+#include "debug.h"
+
+#include "stats.h"
+#include "util.h"
+
+#if defined(__arm__)
+
+gboolean stats_is_supported_arch(void) {
+
+ return FALSE;
+
+}
+
+size_t stats_data_size_arch(void) {
+
+ FATAL("Stats not supported on this architecture");
+
+}
+
+void stats_write_arch(void) {
+
+ FATAL("Stats not supported on this architecture");
+
+}
+
+void stats_collect_arch(const cs_insn *instr) {
+
+ UNUSED_PARAMETER(instr);
+ FATAL("Stats not supported on this architecture");
+
+}
+
+#endif
+