mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-13 10:38:07 +00:00
Improve JS bindings for hooking functions
This commit is contained in:
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -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;
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
@ -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"]);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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)) {
|
||||||
|
@ -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 \
|
||||||
|
44
frida_mode/test/js/main.js
Normal file
44
frida_mode/test/js/main.js
Normal 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();
|
@ -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",
|
||||||
|
Reference in New Issue
Block a user