about summary refs log tree commit diff
path: root/frida_mode/Scripting.md
diff options
context:
space:
mode:
Diffstat (limited to 'frida_mode/Scripting.md')
-rw-r--r--frida_mode/Scripting.md335
1 files changed, 228 insertions, 107 deletions
diff --git a/frida_mode/Scripting.md b/frida_mode/Scripting.md
index f6017fad..2ee0c858 100644
--- a/frida_mode/Scripting.md
+++ b/frida_mode/Scripting.md
@@ -246,7 +246,7 @@ FRIDA mode supports the replacement of any function, with an implementation
 generated by CModule. This allows for a bespoke harness to be written as
 follows:
 
-```
+```js
 const slow = DebugSymbol.fromName('slow').address;
 Afl.print(`slow: ${slow}`);
 
@@ -281,13 +281,90 @@ Afl.done();
 Here, we replace the function `slow` with our own code. This code is then
 selected as the entry point as well as the persistent loop address.
 
-**WARNING** There are two key limitations in replacing a function in this way:
-- The function which is to be replaced must not be `main` this is because this
-is the point at which FRIDA mode is initialized and at the point the the JS has
-been run, the start of the `main` function has already been instrumented and
-cached.
-- The replacement function must not call itself. e.g. in this example we
-couldn't replace `LLVMFuzzerTestOneInput` and call itself.
+## Replacing LLVMFuzzerTestOneInput
+The function `LLVMFuzzerTestOneInput` can be replaced just like any other. Also
+any replaced function can also call itself. In the example below, we replace
+`LLVMFuzzerTestOneInput` with `My_LLVMFuzzerTestOneInput` which ignores the
+parameters `buf` and `len` and then calls the original `LLVMFuzzerTestOneInput`
+with the paramaters `__afl_fuzz_ptr` and `__afl_fuzz_len`. This allows us to
+carry out in-memory fuzzing without the need for any hook function. It should be
+noted that the replacement function and the original can *NOT* share the same
+name, since otherwise the `C` code in the `CModule` will not compile due to a
+symbol name collision.
+
+```js
+const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
+Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);
+
+const cm = new CModule(`
+
+    extern unsigned char * __afl_fuzz_ptr;
+    extern unsigned int * __afl_fuzz_len;
+    extern void LLVMFuzzerTestOneInput(char *buf, int len);
+
+    void My_LLVMFuzzerTestOneInput(char *buf, int len) {
+
+      LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
+
+    }
+    `,
+    {
+        LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
+        __afl_fuzz_ptr: Afl.getAflFuzzPtr(),
+        __afl_fuzz_len: Afl.getAflFuzzLen()
+    });
+
+Afl.setEntryPoint(cm.My_LLVMFuzzerTestOneInput);
+Afl.setPersistentAddress(cm.My_LLVMFuzzerTestOneInput);
+Afl.setInMemoryFuzzing();
+Interceptor.replace(LLVMFuzzerTestOneInput, cm.My_LLVMFuzzerTestOneInput);
+```
+
+## Hooking `main`
+Lastly, it should be noted that using FRIDA mode's scripting support to hook
+the `main` function is a special case. This is because the `main` function is
+already hooked by the FRIDA mode engine itself and hence the function `main` (or
+at least the first basic block already been compiled by Stalker ready for
+execution). Hence any attempt to use `Interceptor.replace` like in the example
+above will not work. Instead the JS bindings provide a function `setJsMainHook`
+for just this scenario as demonstrated in the example below.
+
+```js
+const main = DebugSymbol.fromName('main').address;
+Afl.print(`main: ${main}`);
+
+const LLVMFuzzerTestOneInput = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
+Afl.print(`LLVMFuzzerTestOneInput: ${LLVMFuzzerTestOneInput}`);
+
+const cm = new CModule(`
+
+    extern unsigned char * __afl_fuzz_ptr;
+    extern unsigned int * __afl_fuzz_len;
+    extern void LLVMFuzzerTestOneInput(char *buf, int len);
+
+    int main(int argc, char **argv)  {
+
+      LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
+
+    }
+    `,
+    {
+        LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
+        __afl_fuzz_ptr: Afl.getAflFuzzPtr(),
+        __afl_fuzz_len: Afl.getAflFuzzLen()
+    });
+
+Afl.setEntryPoint(cm.main);
+Afl.setPersistentAddress(cm.main);
+Afl.setInMemoryFuzzing();
+Afl.setJsMainHook(cm.main);
+```
+## Library Fuzzing
+
+It doesn't take too much imagination to see that the above example can be
+extended to use FRIDA's `Module.load` API so that the replaced `main` function
+can then call an arbitrary function. In this way, if we have a library which we
+wish to fuzz rather than an execuatble, then a surrogate executable can be used.
 
 # Patching
 Consider the [following](test/js/test2.c) test code...
@@ -302,7 +379,7 @@ Consider the [following](test/js/test2.c) test code...
    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
+     https://www.apache.org/licenses/LICENSE-2.0
  */
 
 #include <fcntl.h>
@@ -620,41 +697,31 @@ value of the `-t` flag passed to `afl-fuzz`.
 # API
 ```js
 class Afl {
-
-  /**
-   * Field containing the `Module` object for `afl-frida-trace.so` (the FRIDA mode
-   * implementation).
-   */
-  public static module: Module = Process.getModuleByName("afl-frida-trace.so");
-
   /**
    * 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.
    */
-  public static addExcludedRange(addressess: NativePointer, size: number): void {
-    Afl.jsApiAddExcludeRange(addressess, size);
+  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.
    */
-  public static addIncludedRange(addressess: NativePointer, size: number): void {
-    Afl.jsApiAddIncludeRange(addressess, size);
+  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.
    */
-  public static done(): void {
-    Afl.jsApiDone();
+  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
@@ -662,49 +729,48 @@ class Afl {
    * stop. The user will need to enable `AFL_DEBUG_CHILD=1` to view
    * this error message.
    */
-  public static error(msg: string): void {
-    const buf = Memory.allocUtf8String(msg);
-    Afl.jsApiError(buf);
+  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.
    */
-  public static getAflFuzzLen(): NativePointer {
-
-    return Afl.jsApiGetSymbol("__afl_fuzz_len");
+  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.
    */
-  public static getAflFuzzPtr(): NativePointer {
-
-    return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
+  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.
    */
-  public static print(msg: string): void {
-    const STDOUT_FILENO = 2;
-    const log = `${msg}\n`;
-    const buf = Memory.allocUtf8String(log);
-    Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
+  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_INST_NO_BACKPATCH`.
+   */
+  static setBackpatchDisable() {
+      Afl.jsApiSetBackpatchDisable();
   }
-
   /**
    * See `AFL_FRIDA_DEBUG_MAPS`.
    */
-  public static setDebugMaps(): void {
-    Afl.jsApiSetDebugMaps();
+  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
@@ -713,143 +779,198 @@ class Afl {
    * function should be called with a `NativePointer` as its
    * argument.
    */
-  public static setEntryPoint(address: NativePointer): void {
-    Afl.jsApiSetEntryPoint(address);
+  static setEntryPoint(address) {
+      Afl.jsApiSetEntryPoint(address);
   }
-
   /**
    * Function used to enable in-memory test cases for fuzzing.
    */
-  public static setInMemoryFuzzing(): void {
-    Afl.jsApiAflSharedMemFuzzing.writeInt(1);
+  static setInMemoryFuzzing() {
+      Afl.jsApiAflSharedMemFuzzing.writeInt(1);
+  }
+  /**
+   * See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string`
+   * as an argument.
+   */
+  static setInstrumentCoverageFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetInstrumentCoverageFile(buf);
   }
-
   /**
    * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
    * an argument.
    */
-  public static setInstrumentDebugFile(file: string): void {
-    const buf = Memory.allocUtf8String(file);
-    Afl.jsApiSetInstrumentDebugFile(buf);
+  static setInstrumentDebugFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetInstrumentDebugFile(buf);
   }
-
   /**
    * See `AFL_FRIDA_INST_TRACE`.
    */
-  public static setInstrumentEnableTracing(): void {
-    Afl.jsApiSetInstrumentTrace();
+  static setInstrumentEnableTracing() {
+      Afl.jsApiSetInstrumentTrace();
+  }
+  /**
+   * See `AFL_FRIDA_INST_JIT`.
+   */
+  static setInstrumentJit() {
+      Afl.jsApiSetInstrumentJit();
   }
-
   /**
    * See `AFL_INST_LIBS`.
    */
-  public static setInstrumentLibraries(): void {
-    Afl.jsApiSetInstrumentLibraries();
+  static setInstrumentLibraries() {
+      Afl.jsApiSetInstrumentLibraries();
   }
-
   /**
    * See `AFL_FRIDA_INST_NO_OPTIMIZE`
    */
-  public static setInstrumentNoOptimize(): void {
-    Afl.jsApiSetInstrumentNoOptimize();
+  static setInstrumentNoOptimize() {
+      Afl.jsApiSetInstrumentNoOptimize();
+  }
+  /*
+    * See `AFL_FRIDA_INST_SEED`
+    */
+  static setInstrumentSeed(seed) {
+      Afl.jsApiSetInstrumentSeed(seed);
   }
-
   /**
    * See `AFL_FRIDA_INST_TRACE_UNIQUE`.
    */
-  public static setInstrumentTracingUnique(): void {
-    Afl.jsApiSetInstrumentTraceUnique();
+  static setInstrumentTracingUnique() {
+      Afl.jsApiSetInstrumentTraceUnique();
+  }
+  /**
+   * See `AFL_FRIDA_INST_UNSTABLE_COVERAGE_FILE`. This function takes a single
+   * `string` as an argument.
+   */
+  static setInstrumentUnstableCoverageFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
+  }
+  /*
+    * Set a callback to be called in place of the usual `main` function. This see
+    * `Scripting.md` for details.
+    */
+  static setJsMainHook(address) {
+      Afl.jsApiSetJsMainHook(address);
   }
-
   /**
    * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
    * `NativePointer` should be provided as it's argument.
    */
-  public static setPersistentAddress(address: NativePointer): void {
-    Afl.jsApiSetPersistentAddress(address);
+  static setPersistentAddress(address) {
+      Afl.jsApiSetPersistentAddress(address);
   }
-
   /**
    * This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
    * `number` should be provided as it's argument.
    */
-  public static setPersistentCount(count: number): void {
-    Afl.jsApiSetPersistentCount(count);
+  static setPersistentCount(count) {
+      Afl.jsApiSetPersistentCount(count);
   }
-
   /**
    * See `AFL_FRIDA_PERSISTENT_DEBUG`.
    */
-  public static setPersistentDebug(): void {
-    Afl.jsApiSetPersistentDebug();
+  static setPersistentDebug() {
+      Afl.jsApiSetPersistentDebug();
   }
-
   /**
    * See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an
    * argument. See above for examples of use.
    */
-  public static setPersistentHook(address: NativePointer): void {
-    Afl.jsApiSetPersistentHook(address);
+  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.
    */
-  public static setPersistentReturn(address: NativePointer): void {
-    Afl.jsApiSetPersistentReturn(address);
+  static setPersistentReturn(address) {
+      Afl.jsApiSetPersistentReturn(address);
+  }
+  /**
+   * See `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`.
+   */
+  static setPrefetchBackpatchDisable() {
+      Afl.jsApiSetPrefetchBackpatchDisable();
   }
-
   /**
    * See `AFL_FRIDA_INST_NO_PREFETCH`.
    */
-  public static setPrefetchDisable(): void {
-    Afl.jsApiSetPrefetchDisable();
+  static setPrefetchDisable() {
+      Afl.jsApiSetPrefetchDisable();
+  }
+  /**
+   * See `AFL_FRIDA_SECCOMP_FILE`. This function takes a single `string` as
+   * an argument.
+   */
+  static setSeccompFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetSeccompFile(buf);
+  }
+  /**
+   * See `AFL_FRIDA_STALKER_ADJACENT_BLOCKS`.
+   */
+  static setStalkerAdjacentBlocks(val) {
+      Afl.jsApiSetStalkerAdjacentBlocks(val);
   }
-
   /*
-   * Set a function to be called for each instruction which is instrumented
-   * by AFL FRIDA mode.
+    * 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_STALKER_IC_ENTRIES`.
    */
-  public static setStalkerCallback(callback: NativePointer): void {
-    Afl.jsApiSetStalkerCallback(callback);
+  static setStalkerIcEntries(val) {
+      Afl.jsApiSetStalkerIcEntries(val);
   }
-
   /**
    * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
    * an argument.
    */
-  public static setStatsFile(file: string): void {
-    const buf = Memory.allocUtf8String(file);
-    Afl.jsApiSetStatsFile(buf);
+  static setStatsFile(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetStatsFile(buf);
   }
-
   /**
    * See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
    * argument
    */
-  public static setStatsInterval(interval: number): void {
-    Afl.jsApiSetStatsInterval(interval);
+  static setStatsInterval(interval) {
+      Afl.jsApiSetStatsInterval(interval);
   }
-
   /**
    * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
    * an argument.
    */
-  public static setStdErr(file: string): void {
-    const buf = Memory.allocUtf8String(file);
-    Afl.jsApiSetStdErr(buf);
+  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.
    */
-  public static setStdOut(file: string): void {
-    const buf = Memory.allocUtf8String(file);
-    Afl.jsApiSetStdOut(buf);
+  static setStdOut(file) {
+      const buf = Memory.allocUtf8String(file);
+      Afl.jsApiSetStdOut(buf);
+  }
+  /**
+   * See `AFL_FRIDA_TRACEABLE`.
+   */
+  static setTraceable() {
+      Afl.jsApiSetTraceable();
+  }
+  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);
   }
-
 }
-
 ```