about summary refs log tree commit diff
path: root/frida_mode/src/js
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode/src/js')
-rw-r--r--frida_mode/src/js/api.js243
-rw-r--r--frida_mode/src/js/js.c122
-rw-r--r--frida_mode/src/js/js_api.c152
3 files changed, 517 insertions, 0 deletions
diff --git a/frida_mode/src/js/api.js b/frida_mode/src/js/api.js
new file mode 100644
index 00000000..4cb04704
--- /dev/null
+++ b/frida_mode/src/js/api.js
@@ -0,0 +1,243 @@
+"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_INST_LIBS`.
+     */
+    static setInstrumentLibraries() {
+        Afl.jsApiSetInstrumentLibraries();
+    }
+    /**
+     * See `AFL_FRIDA_INST_NO_OPTIMIZE`
+     */
+    static setInstrumentNoOptimize() {
+        Afl.jsApiSetInstrumentNoOptimize();
+    }
+    /**
+     * 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.jsApiSetInstrumentLibraries = Afl.jsApiGetFunction("js_api_set_instrument_libraries", "void", []);
+Afl.jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction("js_api_set_instrument_no_optimize", "void", []);
+Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
+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..ed378d2c
--- /dev/null
+++ b/frida_mode/src/js/js.c
@@ -0,0 +1,122 @@
+#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 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()"); }
+
+}
+
+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..91dccab2
--- /dev/null
+++ b/frida_mode/src/js/js_api.c
@@ -0,0 +1,152 @@
+#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;
+
+}
+
+void js_api_set_persistent_hook(void *address) {
+
+  persistent_hook = address;
+
+}
+
+void js_api_set_stalker_callback(const js_api_stalker_callback_t callback) {
+
+  js_user_callback = callback;
+
+}
+