Improve JS bindings for hooking functions

This commit is contained in:
Your Name
2021-11-18 17:08:39 +00:00
parent 3b9545854f
commit af02fa1670
10 changed files with 338 additions and 120 deletions

View File

@ -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 generated by CModule. This allows for a bespoke harness to be written as
follows: follows:
``` ```js
const slow = DebugSymbol.fromName('slow').address; const slow = DebugSymbol.fromName('slow').address;
Afl.print(`slow: ${slow}`); Afl.print(`slow: ${slow}`);
@ -281,13 +281,90 @@ Afl.done();
Here, we replace the function `slow` with our own code. This code is then 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. 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: ## Replacing LLVMFuzzerTestOneInput
- The function which is to be replaced must not be `main` this is because this The function `LLVMFuzzerTestOneInput` can be replaced just like any other. Also
is the point at which FRIDA mode is initialized and at the point the the JS has any replaced function can also call itself. In the example below, we replace
been run, the start of the `main` function has already been instrumented and `LLVMFuzzerTestOneInput` with `My_LLVMFuzzerTestOneInput` which ignores the
cached. parameters `buf` and `len` and then calls the original `LLVMFuzzerTestOneInput`
- The replacement function must not call itself. e.g. in this example we with the paramaters `__afl_fuzz_ptr` and `__afl_fuzz_len`. This allows us to
couldn't replace `LLVMFuzzerTestOneInput` and call itself. 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 # Patching
Consider the [following](test/js/test2.c) test code... Consider the [following](test/js/test2.c) test code...
@ -620,41 +697,31 @@ value of the `-t` flag passed to `afl-fuzz`.
# API # API
```js ```js
class Afl { 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`, * This is equivalent to setting a value in `AFL_FRIDA_EXCLUDE_RANGES`,
* it takes as arguments a `NativePointer` and a `number`. It can be * it takes as arguments a `NativePointer` and a `number`. It can be
* called multiple times to exclude several ranges. * called multiple times to exclude several ranges.
*/ */
public static addExcludedRange(addressess: NativePointer, size: number): void { static addExcludedRange(addressess, size) {
Afl.jsApiAddExcludeRange(addressess, size); Afl.jsApiAddExcludeRange(addressess, size);
} }
/** /**
* This is equivalent to setting a value in `AFL_FRIDA_INST_RANGES`, * This is equivalent to setting a value in `AFL_FRIDA_INST_RANGES`,
* it takes as arguments a `NativePointer` and a `number`. It can be * it takes as arguments a `NativePointer` and a `number`. It can be
* called multiple times to include several ranges. * called multiple times to include several ranges.
*/ */
public static addIncludedRange(addressess: NativePointer, size: number): void { static addIncludedRange(addressess, size) {
Afl.jsApiAddIncludeRange(addressess, size); Afl.jsApiAddIncludeRange(addressess, size);
} }
/** /**
* This must always be called at the end of your script. This lets * This must always be called at the end of your script. This lets
* FRIDA mode know that your configuration is finished and that * FRIDA mode know that your configuration is finished and that
* execution has reached the end of your script. Failure to call * execution has reached the end of your script. Failure to call
* this will result in a fatal error. * this will result in a fatal error.
*/ */
public static done(): void { static done() {
Afl.jsApiDone(); Afl.jsApiDone();
} }
/** /**
* This function can be called within your script to cause FRIDA * This function can be called within your script to cause FRIDA
* mode to trigger a fatal error. This is useful if for example you * 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 * stop. The user will need to enable `AFL_DEBUG_CHILD=1` to view
* this error message. * this error message.
*/ */
public static error(msg: string): void { static error(msg) {
const buf = Memory.allocUtf8String(msg); const buf = Memory.allocUtf8String(msg);
Afl.jsApiError(buf); Afl.jsApiError(buf);
} }
/** /**
* Function used to provide access to `__afl_fuzz_ptr`, which contains the length of * Function used to provide access to `__afl_fuzz_ptr`, which contains the length of
* fuzzing data when using in-memory test case fuzzing. * fuzzing data when using in-memory test case fuzzing.
*/ */
public static getAflFuzzLen(): NativePointer { static getAflFuzzLen() {
return Afl.jsApiGetSymbol("__afl_fuzz_len");
return Afl.jsApiGetSymbol("__afl_fuzz_len");
} }
/** /**
* Function used to provide access to `__afl_fuzz_ptr`, which contains the fuzzing * Function used to provide access to `__afl_fuzz_ptr`, which contains the fuzzing
* data when using in-memory test case fuzzing. * data when using in-memory test case fuzzing.
*/ */
public static getAflFuzzPtr(): NativePointer { static getAflFuzzPtr() {
return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
} }
/** /**
* Print a message to the STDOUT. This should be preferred to * Print a message to the STDOUT. This should be preferred to
* FRIDA's `console.log` since FRIDA will queue it's log messages. * FRIDA's `console.log` since FRIDA will queue it's log messages.
* If `console.log` is used in a callback in particular, then there * If `console.log` is used in a callback in particular, then there
* may no longer be a thread running to service this queue. * may no longer be a thread running to service this queue.
*/ */
public static print(msg: string): void { static print(msg) {
const STDOUT_FILENO = 2; const STDOUT_FILENO = 2;
const log = `${msg}\n`; const log = `${msg}\n`;
const buf = Memory.allocUtf8String(log); const buf = Memory.allocUtf8String(log);
Afl.jsApiWrite(STDOUT_FILENO, buf, log.length); Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
}
/**
* See `AFL_FRIDA_INST_NO_BACKPATCH`.
*/
static setBackpatchDisable() {
Afl.jsApiSetBackpatchDisable();
} }
/** /**
* See `AFL_FRIDA_DEBUG_MAPS`. * See `AFL_FRIDA_DEBUG_MAPS`.
*/ */
public static setDebugMaps(): void { static setDebugMaps() {
Afl.jsApiSetDebugMaps(); Afl.jsApiSetDebugMaps();
} }
/** /**
* This has the same effect as setting `AFL_ENTRYPOINT`, but has the * This has the same effect as setting `AFL_ENTRYPOINT`, but has the
* convenience of allowing you to use FRIDAs APIs to determine 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 * function should be called with a `NativePointer` as its
* argument. * argument.
*/ */
public static setEntryPoint(address: NativePointer): void { static setEntryPoint(address) {
Afl.jsApiSetEntryPoint(address); Afl.jsApiSetEntryPoint(address);
} }
/** /**
* Function used to enable in-memory test cases for fuzzing. * Function used to enable in-memory test cases for fuzzing.
*/ */
public static setInMemoryFuzzing(): void { static setInMemoryFuzzing() {
Afl.jsApiAflSharedMemFuzzing.writeInt(1); 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 * See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
* an argument. * an argument.
*/ */
public static setInstrumentDebugFile(file: string): void { static setInstrumentDebugFile(file) {
const buf = Memory.allocUtf8String(file); const buf = Memory.allocUtf8String(file);
Afl.jsApiSetInstrumentDebugFile(buf); Afl.jsApiSetInstrumentDebugFile(buf);
} }
/** /**
* See `AFL_FRIDA_INST_TRACE`. * See `AFL_FRIDA_INST_TRACE`.
*/ */
public static setInstrumentEnableTracing(): void { static setInstrumentEnableTracing() {
Afl.jsApiSetInstrumentTrace(); Afl.jsApiSetInstrumentTrace();
}
/**
* See `AFL_FRIDA_INST_JIT`.
*/
static setInstrumentJit() {
Afl.jsApiSetInstrumentJit();
} }
/** /**
* See `AFL_INST_LIBS`. * See `AFL_INST_LIBS`.
*/ */
public static setInstrumentLibraries(): void { static setInstrumentLibraries() {
Afl.jsApiSetInstrumentLibraries(); Afl.jsApiSetInstrumentLibraries();
} }
/** /**
* See `AFL_FRIDA_INST_NO_OPTIMIZE` * See `AFL_FRIDA_INST_NO_OPTIMIZE`
*/ */
public static setInstrumentNoOptimize(): void { static setInstrumentNoOptimize() {
Afl.jsApiSetInstrumentNoOptimize(); Afl.jsApiSetInstrumentNoOptimize();
}
/*
* See `AFL_FRIDA_INST_SEED`
*/
static setInstrumentSeed(seed) {
Afl.jsApiSetInstrumentSeed(seed);
} }
/** /**
* See `AFL_FRIDA_INST_TRACE_UNIQUE`. * See `AFL_FRIDA_INST_TRACE_UNIQUE`.
*/ */
public static setInstrumentTracingUnique(): void { static setInstrumentTracingUnique() {
Afl.jsApiSetInstrumentTraceUnique(); 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 * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
* `NativePointer` should be provided as it's argument. * `NativePointer` should be provided as it's argument.
*/ */
public static setPersistentAddress(address: NativePointer): void { static setPersistentAddress(address) {
Afl.jsApiSetPersistentAddress(address); Afl.jsApiSetPersistentAddress(address);
} }
/** /**
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a * This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
* `number` should be provided as it's argument. * `number` should be provided as it's argument.
*/ */
public static setPersistentCount(count: number): void { static setPersistentCount(count) {
Afl.jsApiSetPersistentCount(count); Afl.jsApiSetPersistentCount(count);
} }
/** /**
* See `AFL_FRIDA_PERSISTENT_DEBUG`. * See `AFL_FRIDA_PERSISTENT_DEBUG`.
*/ */
public static setPersistentDebug(): void { static setPersistentDebug() {
Afl.jsApiSetPersistentDebug(); Afl.jsApiSetPersistentDebug();
} }
/** /**
* See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an * See `AFL_FRIDA_PERSISTENT_ADDR`. This function takes a NativePointer as an
* argument. See above for examples of use. * argument. See above for examples of use.
*/ */
public static setPersistentHook(address: NativePointer): void { static setPersistentHook(address) {
Afl.jsApiSetPersistentHook(address); Afl.jsApiSetPersistentHook(address);
} }
/** /**
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a * This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a
* `NativePointer` should be provided as it's argument. * `NativePointer` should be provided as it's argument.
*/ */
public static setPersistentReturn(address: NativePointer): void { static setPersistentReturn(address) {
Afl.jsApiSetPersistentReturn(address); Afl.jsApiSetPersistentReturn(address);
}
/**
* See `AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH`.
*/
static setPrefetchBackpatchDisable() {
Afl.jsApiSetPrefetchBackpatchDisable();
} }
/** /**
* See `AFL_FRIDA_INST_NO_PREFETCH`. * See `AFL_FRIDA_INST_NO_PREFETCH`.
*/ */
public static setPrefetchDisable(): void { static setPrefetchDisable() {
Afl.jsApiSetPrefetchDisable(); Afl.jsApiSetPrefetchDisable();
} }
/**
/* * See `AFL_FRIDA_SECCOMP_FILE`. This function takes a single `string` as
* Set a function to be called for each instruction which is instrumented * an argument.
* by AFL FRIDA mode.
*/ */
public static setStalkerCallback(callback: NativePointer): void { static setSeccompFile(file) {
Afl.jsApiSetStalkerCallback(callback); 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.
*/
static setStalkerCallback(callback) {
Afl.jsApiSetStalkerCallback(callback);
}
/**
* See `AFL_FRIDA_STALKER_IC_ENTRIES`.
*/
static setStalkerIcEntries(val) {
Afl.jsApiSetStalkerIcEntries(val);
} }
/** /**
* See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as * See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
* an argument. * an argument.
*/ */
public static setStatsFile(file: string): void { static setStatsFile(file) {
const buf = Memory.allocUtf8String(file); const buf = Memory.allocUtf8String(file);
Afl.jsApiSetStatsFile(buf); Afl.jsApiSetStatsFile(buf);
} }
/** /**
* See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an * See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
* argument * argument
*/ */
public static setStatsInterval(interval: number): void { static setStatsInterval(interval) {
Afl.jsApiSetStatsInterval(interval); Afl.jsApiSetStatsInterval(interval);
} }
/** /**
* See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as * See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
* an argument. * an argument.
*/ */
public static setStdErr(file: string): void { static setStdErr(file) {
const buf = Memory.allocUtf8String(file); const buf = Memory.allocUtf8String(file);
Afl.jsApiSetStdErr(buf); Afl.jsApiSetStdErr(buf);
} }
/** /**
* See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as * See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as
* an argument. * an argument.
*/ */
public static setStdOut(file: string): void { static setStdOut(file) {
const buf = Memory.allocUtf8String(file); const buf = Memory.allocUtf8String(file);
Afl.jsApiSetStdOut(buf); 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);
} }
} }
``` ```

View File

@ -20,6 +20,7 @@
js_api_set_instrument_trace; js_api_set_instrument_trace;
js_api_set_instrument_trace_unique; js_api_set_instrument_trace_unique;
js_api_set_instrument_unstable_coverage_file; js_api_set_instrument_unstable_coverage_file;
js_api_set_js_main_hook;
js_api_set_persistent_address; js_api_set_persistent_address;
js_api_set_persistent_count; js_api_set_persistent_count;
js_api_set_persistent_debug; js_api_set_persistent_debug;

View File

@ -7,11 +7,14 @@ typedef gboolean (*js_api_stalker_callback_t)(const cs_insn *insn,
gboolean begin, gboolean excluded, gboolean begin, gboolean excluded,
GumStalkerOutput *output); GumStalkerOutput *output);
typedef int (*js_main_hook_t)(int argc, char **argv, char **envp);
extern unsigned char api_js[]; extern unsigned char api_js[];
extern unsigned int api_js_len; extern unsigned int api_js_len;
extern gboolean js_done; extern gboolean js_done;
extern js_api_stalker_callback_t js_user_callback; extern js_api_stalker_callback_t js_user_callback;
extern js_main_hook_t js_main_hook;
/* Frida Mode */ /* Frida Mode */

View File

@ -151,6 +151,13 @@ class Afl {
const buf = Memory.allocUtf8String(file); const buf = Memory.allocUtf8String(file);
Afl.jsApiSetInstrumentUnstableCoverageFile(buf); 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 * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
* `NativePointer` should be provided as it's argument. * `NativePointer` should be provided as it's argument.
@ -291,6 +298,7 @@ Afl.jsApiSetInstrumentSeed = Afl.jsApiGetFunction("js_api_set_instrument_seed",
Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []); Afl.jsApiSetInstrumentTrace = Afl.jsApiGetFunction("js_api_set_instrument_trace", "void", []);
Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []); Afl.jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction("js_api_set_instrument_trace_unique", "void", []);
Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]); Afl.jsApiSetInstrumentUnstableCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_unstable_coverage_file", "void", ["pointer"]);
Afl.jsApiSetJsMainHook = Afl.jsApiGetFunction("js_api_set_js_main_hook", "void", ["pointer"]);
Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]); Afl.jsApiSetPersistentAddress = Afl.jsApiGetFunction("js_api_set_persistent_address", "void", ["pointer"]);
Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]); Afl.jsApiSetPersistentCount = Afl.jsApiGetFunction("js_api_set_persistent_count", "void", ["uint64"]);
Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []); Afl.jsApiSetPersistentDebug = Afl.jsApiGetFunction("js_api_set_persistent_debug", "void", []);
@ -299,8 +307,8 @@ Afl.jsApiSetPersistentReturn = Afl.jsApiGetFunction("js_api_set_persistent_retur
Afl.jsApiSetPrefetchBackpatchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_backpatch_disable", "void", []); Afl.jsApiSetPrefetchBackpatchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_backpatch_disable", "void", []);
Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []); Afl.jsApiSetPrefetchDisable = Afl.jsApiGetFunction("js_api_set_prefetch_disable", "void", []);
Afl.jsApiSetSeccompFile = Afl.jsApiGetFunction("js_api_set_seccomp_file", "void", ["pointer"]); Afl.jsApiSetSeccompFile = Afl.jsApiGetFunction("js_api_set_seccomp_file", "void", ["pointer"]);
Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]);
Afl.jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction("js_api_set_stalker_adjacent_blocks", "void", ["uint32"]); Afl.jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction("js_api_set_stalker_adjacent_blocks", "void", ["uint32"]);
Afl.jsApiSetStalkerCallback = Afl.jsApiGetFunction("js_api_set_stalker_callback", "void", ["pointer"]);
Afl.jsApiSetStalkerIcEntries = Afl.jsApiGetFunction("js_api_set_stalker_ic_entries", "void", ["uint32"]); Afl.jsApiSetStalkerIcEntries = Afl.jsApiGetFunction("js_api_set_stalker_ic_entries", "void", ["uint32"]);
Afl.jsApiSetStatsFile = Afl.jsApiGetFunction("js_api_set_stats_file", "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.jsApiSetStatsInterval = Afl.jsApiGetFunction("js_api_set_stats_interval", "void", ["uint64"]);

View File

@ -3,10 +3,11 @@
#include "js.h" #include "js.h"
#include "util.h" #include "util.h"
static char * js_script = NULL;
gboolean js_done = FALSE; gboolean js_done = FALSE;
js_api_stalker_callback_t js_user_callback = NULL; js_api_stalker_callback_t js_user_callback = NULL;
js_main_hook_t js_main_hook = NULL;
static char * js_script = NULL;
static gchar * filename = "afl.js"; static gchar * filename = "afl.js";
static gchar * contents; static gchar * contents;
static GumScriptBackend * backend; static GumScriptBackend * backend;

View File

@ -255,3 +255,10 @@ __attribute__((visibility("default"))) void js_api_set_stalker_adjacent_blocks(
} }
__attribute__((visibility("default"))) void js_api_set_js_main_hook(
const js_main_hook_t hook) {
js_main_hook = hook;
}

View File

@ -36,13 +36,13 @@
extern mach_port_t mach_task_self(); extern mach_port_t mach_task_self();
extern GumAddress gum_darwin_find_entrypoint(mach_port_t task); extern GumAddress gum_darwin_find_entrypoint(mach_port_t task);
#else #else
extern int __libc_start_main(int *(main)(int, char **, char **), int argc, extern int __libc_start_main(int (*main)(int, char **, char **), int argc,
char **ubp_av, void (*init)(void), char **ubp_av, void (*init)(void),
void (*fini)(void), void (*rtld_fini)(void), void (*fini)(void), void (*rtld_fini)(void),
void(*stack_end)); void(*stack_end));
#endif #endif
typedef int *(*main_fn_t)(int argc, char **argv, char **envp); typedef int (*main_fn_t)(int argc, char **argv, char **envp);
static main_fn_t main_fn = NULL; static main_fn_t main_fn = NULL;
@ -217,7 +217,7 @@ __attribute__((visibility("default"))) void afl_frida_start(void) {
} }
static int *on_main(int argc, char **argv, char **envp) { static int on_main(int argc, char **argv, char **envp) {
on_main_os(argc, argv, envp); on_main_os(argc, argv, envp);
@ -225,12 +225,20 @@ static int *on_main(int argc, char **argv, char **envp) {
afl_frida_start(); afl_frida_start();
return main_fn(argc, argv, envp); if (js_main_hook != NULL) {
return js_main_hook(argc, argv, envp);
} else {
return main_fn(argc, argv, envp);
}
} }
#if defined(EMBEDDED) #if defined(EMBEDDED)
extern int *main(int argc, char **argv, char **envp); extern int main(int argc, char **argv, char **envp);
static void intercept_main(void) { static void intercept_main(void) {
@ -253,7 +261,7 @@ static void intercept_main(void) {
} }
#else #else
static int on_libc_start_main(int *(main)(int, char **, char **), int argc, static int on_libc_start_main(int (*main)(int, char **, char **), int argc,
char **ubp_av, void (*init)(void), char **ubp_av, void (*init)(void),
void (*fini)(void), void (*rtld_fini)(void), void (*fini)(void), void (*rtld_fini)(void),
void(*stack_end)) { void(*stack_end)) {

View File

@ -47,6 +47,18 @@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR)
clean: clean:
rm -rf $(BUILD_DIR) rm -rf $(BUILD_DIR)
frida_js_main: $(TESTINSTBIN) $(TEST_DATA_FILE) $(AFLPP_DRIVER_DUMMY_INPUT)
AFL_PRELOAD=$(AFL_PRELOAD) \
AFL_FRIDA_JS_SCRIPT=main.js \
$(ROOT)afl-fuzz \
-D \
-O \
-i $(TEST_DATA_DIR) \
-o $(FRIDA_OUT) \
-t 10000+ \
-- \
$(TESTINSTBIN) $(AFLPP_DRIVER_DUMMY_INPUT)
frida_js_fuzz: $(TESTINSTBIN) $(TEST_DATA_FILE) $(AFLPP_DRIVER_DUMMY_INPUT) frida_js_fuzz: $(TESTINSTBIN) $(TEST_DATA_FILE) $(AFLPP_DRIVER_DUMMY_INPUT)
AFL_PRELOAD=$(AFL_PRELOAD) \ AFL_PRELOAD=$(AFL_PRELOAD) \
AFL_FRIDA_JS_SCRIPT=fuzz.js \ AFL_FRIDA_JS_SCRIPT=fuzz.js \

View File

@ -0,0 +1,44 @@
Afl.print('******************');
Afl.print('* AFL FRIDA MODE *');
Afl.print('******************');
Afl.print('');
Afl.print(`PID: ${Process.id}`);
const name = Process.enumerateModules()[0].name;
Afl.print(`Name: ${name}`);
new ModuleMap().values().forEach(m => {
Afl.print(`${m.base}-${m.base.add(m.size)} ${m.name}`);
});
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);
Afl.print("done");
Afl.done();

View File

@ -179,6 +179,14 @@ class Afl {
Afl.jsApiSetInstrumentUnstableCoverageFile(buf); Afl.jsApiSetInstrumentUnstableCoverageFile(buf);
} }
/*
* Set a callback to be called in place of the usual `main` function. This see
* `Scripting.md` for details.
*/
public static setJsMainHook(address: NativePointer): void {
Afl.jsApiSetJsMainHook(address);
}
/** /**
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a * This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
* `NativePointer` should be provided as it's argument. * `NativePointer` should be provided as it's argument.
@ -387,6 +395,11 @@ class Afl {
"void", "void",
["pointer"]); ["pointer"]);
private static readonly jsApiSetJsMainHook = Afl.jsApiGetFunction(
"js_api_set_js_main_hook",
"void",
["pointer"]);
private static readonly jsApiSetPersistentAddress = Afl.jsApiGetFunction( private static readonly jsApiSetPersistentAddress = Afl.jsApiGetFunction(
"js_api_set_persistent_address", "js_api_set_persistent_address",
"void", "void",
@ -427,16 +440,16 @@ class Afl {
"void", "void",
["pointer"]); ["pointer"]);
private static readonly jsApiSetStalkerCallback = Afl.jsApiGetFunction(
"js_api_set_stalker_callback",
"void",
["pointer"]);
private static readonly jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction( private static readonly jsApiSetStalkerAdjacentBlocks = Afl.jsApiGetFunction(
"js_api_set_stalker_adjacent_blocks", "js_api_set_stalker_adjacent_blocks",
"void", "void",
["uint32"]); ["uint32"]);
private static readonly jsApiSetStalkerCallback = Afl.jsApiGetFunction(
"js_api_set_stalker_callback",
"void",
["pointer"]);
private static readonly jsApiSetStalkerIcEntries = Afl.jsApiGetFunction( private static readonly jsApiSetStalkerIcEntries = Afl.jsApiGetFunction(
"js_api_set_stalker_ic_entries", "js_api_set_stalker_ic_entries",
"void", "void",