mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-16 11:58:08 +00:00
Improved FRIDA mode scripting support (#994)
Co-authored-by: Your Name <you@example.com>
This commit is contained in:
committed by
GitHub
parent
c88b98d1c9
commit
6a3877dcd3
2
frida_mode/.gitignore
vendored
2
frida_mode/.gitignore
vendored
@ -3,3 +3,5 @@ frida_test.dat
|
||||
qemu_test.dat
|
||||
frida_out/**
|
||||
qemu_out/**
|
||||
ts/dist/
|
||||
ts/node_modules/
|
||||
|
@ -94,11 +94,15 @@ FRIDA_GUM_DEVKIT_COMPRESSED_TARBALL:=$(FRIDA_DIR)build/$(GUM_DEVKIT_FILENAME)
|
||||
AFL_COMPILER_RT_SRC:=$(ROOT)instrumentation/afl-compiler-rt.o.c
|
||||
AFL_COMPILER_RT_OBJ:=$(OBJ_DIR)afl-compiler-rt.o
|
||||
|
||||
.PHONY: all 32 clean format $(FRIDA_GUM) quickjs
|
||||
HOOK_DIR:=$(PWD)hook/
|
||||
AFLPP_DRIVER_HOOK_SRC=$(HOOK_DIR)hook.c
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)hook.so
|
||||
|
||||
.PHONY: all 32 clean format hook $(FRIDA_GUM)
|
||||
|
||||
############################## ALL #############################################
|
||||
|
||||
all: $(FRIDA_TRACE)
|
||||
all: $(FRIDA_TRACE) $(AFLPP_DRIVER_HOOK_OBJ)
|
||||
|
||||
32:
|
||||
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
|
||||
@ -197,13 +201,20 @@ $(FRIDA_TRACE): $(GUM_DEVIT_LIBRARY) $(GUM_DEVIT_HEADER) $(OBJS) $(JS_OBJ) $(AFL
|
||||
|
||||
cp -v $(FRIDA_TRACE) $(ROOT)
|
||||
|
||||
############################# HOOK #############################################
|
||||
|
||||
$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -I $(FRIDA_BUILD_DIR) $< -o $@
|
||||
|
||||
hook: $(AFLPP_DRIVER_HOOK_OBJ)
|
||||
|
||||
############################# CLEAN ############################################
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
############################# FORMAT ###########################################
|
||||
format:
|
||||
cd $(ROOT) && echo $(SOURCES) | xargs -L1 ./.custom-format.py -i
|
||||
cd $(ROOT) && echo $(SOURCES) $(AFLPP_DRIVER_HOOK_SRC) | xargs -L1 ./.custom-format.py -i
|
||||
cd $(ROOT) && echo $(INCLUDES) | xargs -L1 ./.custom-format.py -i
|
||||
|
||||
############################# RUN #############################################
|
||||
|
@ -11,3 +11,6 @@ clean:
|
||||
|
||||
format:
|
||||
@gmake format
|
||||
|
||||
hook:
|
||||
@gmake hook
|
||||
|
@ -99,142 +99,752 @@ const address = module.base.add(0xdeadface);
|
||||
Afl.setPersistentAddress(address);
|
||||
```
|
||||
|
||||
# Persisent Hook
|
||||
A persistent hook can be implemented using a conventional shared object, sample
|
||||
source code for a hook suitable for the prototype of `LLVMFuzzerTestOneInput`
|
||||
can be found [here](hook/hook.c). This can be configured using code similar to
|
||||
the following.
|
||||
|
||||
```js
|
||||
const path = Afl.module.path;
|
||||
const dir = path.substring(0, path.lastIndexOf("/"));
|
||||
const mod = Module.load(`${dir}/frida_mode/build/hook.so`);
|
||||
const hook = mod.getExportByName('afl_persistent_hook');
|
||||
Afl.setPersistentHook(hook);
|
||||
```
|
||||
|
||||
Alternatively, the hook can be provided by using FRIDAs built in support for `CModule`, powered by TinyCC.
|
||||
|
||||
```js
|
||||
const cm = new CModule(`
|
||||
|
||||
#include <string.h>
|
||||
#include <gum/gumdefs.h>
|
||||
|
||||
void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
|
||||
uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
`,
|
||||
{
|
||||
memcpy: Module.getExportByName(null, 'memcpy')
|
||||
});
|
||||
Afl.setPersistentHook(cm.afl_persistent_hook);
|
||||
```
|
||||
|
||||
# Advanced Persistence
|
||||
Consider the following target code...
|
||||
```c
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
if (len < 1) return;
|
||||
buf[len] = 0;
|
||||
|
||||
// we support three input cases
|
||||
if (buf[0] == '0')
|
||||
printf("Looks like a zero to me!\n");
|
||||
else if (buf[0] == '1')
|
||||
printf("Pretty sure that is a one!\n");
|
||||
else
|
||||
printf("Neither one or zero? How quaint!\n");
|
||||
|
||||
}
|
||||
|
||||
int run(char *file) {
|
||||
|
||||
int fd = -1;
|
||||
off_t len;
|
||||
char * buf = NULL;
|
||||
size_t n_read;
|
||||
int result = -1;
|
||||
|
||||
do {
|
||||
|
||||
dprintf(STDERR_FILENO, "Running: %s\n", file);
|
||||
|
||||
fd = open(file, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
|
||||
perror("open");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
len = lseek(fd, 0, SEEK_END);
|
||||
if (len < 0) {
|
||||
|
||||
perror("lseek (SEEK_END)");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) != 0) {
|
||||
|
||||
perror("lseek (SEEK_SET)");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
buf = malloc(len);
|
||||
if (buf == NULL) {
|
||||
|
||||
perror("malloc");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
n_read = read(fd, buf, len);
|
||||
if (n_read != len) {
|
||||
|
||||
perror("read");
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
dprintf(STDERR_FILENO, "Running: %s: (%zd bytes)\n", file, n_read);
|
||||
|
||||
LLVMFuzzerTestOneInput(buf, len);
|
||||
dprintf(STDERR_FILENO, "Done: %s: (%zd bytes)\n", file, n_read);
|
||||
|
||||
result = 0;
|
||||
|
||||
} while (false);
|
||||
|
||||
if (buf != NULL) { free(buf); }
|
||||
|
||||
if (fd != -1) { close(fd); }
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
void slow() {
|
||||
|
||||
usleep(100000);
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
if (argc != 2) { return 1; }
|
||||
slow();
|
||||
return run(argv[1]);
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
const slow = DebugSymbol.fromName('slow').address;
|
||||
Afl.print(`slow: ${slow}`);
|
||||
|
||||
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 slow(void) {
|
||||
|
||||
LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
|
||||
}
|
||||
`,
|
||||
{
|
||||
LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
|
||||
__afl_fuzz_ptr: Afl.getAflFuzzPtr(),
|
||||
__afl_fuzz_len: Afl.getAflFuzzLen()
|
||||
});
|
||||
|
||||
Afl.setEntryPoint(cm.slow);
|
||||
Afl.setPersistentAddress(cm.slow);
|
||||
Afl.setInMemoryFuzzing();
|
||||
Interceptor.replace(slow, cm.slow);
|
||||
Afl.print("done");
|
||||
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.
|
||||
|
||||
# Patching
|
||||
Consider the [following](test/js/test2.c) test code...
|
||||
|
||||
```c
|
||||
/*
|
||||
american fuzzy lop++ - a trivial program to test the build
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
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
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
const uint32_t crc32_tab[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
|
||||
...
|
||||
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
uint32_t
|
||||
crc32(const void *buf, size_t size)
|
||||
{
|
||||
const uint8_t *p = buf;
|
||||
uint32_t crc;
|
||||
crc = ~0U;
|
||||
while (size--)
|
||||
crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
|
||||
return crc ^ ~0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't you hate those contrived examples which CRC their data. We can use
|
||||
* FRIDA to patch this function out and always return success. Otherwise, we
|
||||
* could change it to actually correct the checksum.
|
||||
*/
|
||||
int crc32_check (char * buf, int len) {
|
||||
if (len < sizeof(uint32_t)) { return 0; }
|
||||
uint32_t expected = *(uint32_t *)&buf[len - sizeof(uint32_t)];
|
||||
uint32_t calculated = crc32(buf, len - sizeof(uint32_t));
|
||||
return expected == calculated;
|
||||
}
|
||||
|
||||
/*
|
||||
* So you've found a really boring bug in an earlier campaign which results in
|
||||
* a NULL dereference or something like that. That bug can get in the way,
|
||||
* causing the persistent loop to exit whenever it is triggered, and can also
|
||||
* cloud your output unnecessarily. Again, we can use FRIDA to patch it out.
|
||||
*/
|
||||
void some_boring_bug(char c) {
|
||||
switch (c) {
|
||||
case 'A'...'Z':
|
||||
case 'a'...'z':
|
||||
__builtin_trap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
if (!crc32_check(buf, len)) return;
|
||||
|
||||
some_boring_bug(buf[0]);
|
||||
|
||||
if (buf[0] == '0') {
|
||||
printf("Looks like a zero to me!\n");
|
||||
}
|
||||
else if (buf[0] == '1') {
|
||||
printf("Pretty sure that is a one!\n");
|
||||
}
|
||||
else if (buf[0] == '2') {
|
||||
if (buf[1] == '3') {
|
||||
if (buf[2] == '4') {
|
||||
printf("Oh we, weren't expecting that!");
|
||||
__builtin_trap();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("Neither one or zero? How quaint!\n");
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
int fd = -1;
|
||||
off_t len;
|
||||
char * buf = NULL;
|
||||
size_t n_read;
|
||||
int result = -1;
|
||||
|
||||
if (argc != 2) { return 1; }
|
||||
|
||||
printf("Running: %s\n", argv[1]);
|
||||
|
||||
fd = open(argv[1], O_RDONLY);
|
||||
if (fd < 0) { return 1; }
|
||||
|
||||
len = lseek(fd, 0, SEEK_END);
|
||||
if (len < 0) { return 1; }
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) != 0) { return 1; }
|
||||
|
||||
buf = malloc(len);
|
||||
if (buf == NULL) { return 1; }
|
||||
|
||||
n_read = read(fd, buf, len);
|
||||
if (n_read != len) { return 1; }
|
||||
|
||||
printf("Running: %s: (%zd bytes)\n", argv[1], n_read);
|
||||
|
||||
LLVMFuzzerTestOneInput(buf, len);
|
||||
printf("Done: %s: (%zd bytes)\n", argv[1], n_read);
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
There are a couple of obstacles with our target application. Unlike when fuzzing
|
||||
source code, though, we can't simply edit it and recompile it. The following
|
||||
script shows how we can use the normal functionality of FRIDA to modify any
|
||||
troublesome behaviour.
|
||||
|
||||
```js
|
||||
Afl.print('******************');
|
||||
Afl.print('* AFL FRIDA MODE *');
|
||||
Afl.print('******************');
|
||||
Afl.print('');
|
||||
|
||||
const main = DebugSymbol.fromName('main').address;
|
||||
Afl.print(`main: ${main}`);
|
||||
Afl.setEntryPoint(main);
|
||||
Afl.setPersistentAddress(main);
|
||||
Afl.setPersistentCount(10000000);
|
||||
|
||||
const crc32_check = DebugSymbol.fromName('crc32_check').address;
|
||||
const crc32_replacement = new NativeCallback(
|
||||
(buf, len) => {
|
||||
Afl.print(`len: ${len}`);
|
||||
if (len < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
},
|
||||
'int',
|
||||
['pointer', 'int']);
|
||||
Interceptor.replace(crc32_check, crc32_replacement);
|
||||
|
||||
const some_boring_bug = DebugSymbol.fromName('some_boring_bug').address
|
||||
const boring_replacement = new NativeCallback(
|
||||
(c) => { },
|
||||
'void',
|
||||
['char']);
|
||||
Interceptor.replace(some_boring_bug, boring_replacement);
|
||||
|
||||
Afl.done();
|
||||
Afl.print("done");
|
||||
```
|
||||
|
||||
# Advanced Patching
|
||||
Consider the following code fragment...
|
||||
```c
|
||||
extern void some_boring_bug2(char c);
|
||||
|
||||
__asm__ (
|
||||
".text \n"
|
||||
"some_boring_bug2: \n"
|
||||
".global some_boring_bug2 \n"
|
||||
".type some_boring_bug2, @function \n"
|
||||
"mov %edi, %eax \n"
|
||||
"cmp $0xb4, %al \n"
|
||||
"jne ok \n"
|
||||
"ud2 \n"
|
||||
"ok: \n"
|
||||
"ret \n");
|
||||
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
...
|
||||
|
||||
some_boring_bug2(buf[0]);
|
||||
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Rather than using FRIDAs `Interceptor.replace` or `Interceptor.attach` APIs, it
|
||||
is possible to apply much more fine grained modification to the target
|
||||
application by means of using the Stalker APIs.
|
||||
|
||||
The following code locates the function of interest and patches out the UD2
|
||||
instruction signifying a crash.
|
||||
|
||||
```js
|
||||
/* Modify the instructions */
|
||||
const some_boring_bug2 = DebugSymbol.fromName('some_boring_bug2').address
|
||||
const pid = Memory.alloc(4);
|
||||
pid.writeInt(Process.id);
|
||||
|
||||
const cm = new CModule(`
|
||||
#include <stdio.h>
|
||||
#include <gum/gumstalker.h>
|
||||
|
||||
typedef int pid_t;
|
||||
|
||||
#define STDERR_FILENO 2
|
||||
#define BORING2_LEN 10
|
||||
|
||||
extern int dprintf(int fd, const char *format, ...);
|
||||
extern void some_boring_bug2(char c);
|
||||
extern pid_t getpid(void);
|
||||
extern pid_t pid;
|
||||
|
||||
gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
|
||||
gboolean excluded, GumStalkerOutput *output)
|
||||
{
|
||||
pid_t my_pid = getpid();
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (GUM_ADDRESS(insn->address) < GUM_ADDRESS(some_boring_bug2)) {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
if (GUM_ADDRESS(insn->address) >=
|
||||
GUM_ADDRESS(some_boring_bug2) + BORING2_LEN) {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
if (my_pid == pid) {
|
||||
|
||||
if (begin) {
|
||||
|
||||
dprintf(STDERR_FILENO, "\n> 0x%016lX: %s %s\n", insn->address,
|
||||
insn->mnemonic, insn->op_str);
|
||||
|
||||
} else {
|
||||
|
||||
dprintf(STDERR_FILENO, " 0x%016lX: %s %s\n", insn->address,
|
||||
insn->mnemonic, insn->op_str);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (insn->id == X86_INS_UD2) {
|
||||
|
||||
gum_x86_writer_put_nop(cw);
|
||||
return FALSE;
|
||||
|
||||
} else {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
dprintf: Module.getExportByName(null, 'dprintf'),
|
||||
getpid: Module.getExportByName(null, 'getpid'),
|
||||
some_boring_bug2: some_boring_bug2,
|
||||
pid: pid
|
||||
});
|
||||
Afl.setStalkerCallback(cm.js_stalker_callback)
|
||||
Afl.setStdErr("/tmp/stderr.txt");
|
||||
```
|
||||
|
||||
Note that you will more likely want to find the
|
||||
patch address by using:
|
||||
|
||||
```js
|
||||
const module = Process.getModuleByName('target.exe');
|
||||
/* Hardcoded offset within the target image */
|
||||
const address = module.base.add(0xdeadface);
|
||||
```
|
||||
OR
|
||||
```
|
||||
const address = DebugSymbol.fromName("my_function").address.add(0xdeadface);
|
||||
```
|
||||
OR
|
||||
```
|
||||
const address = Module.getExportByName(null, "my_function").add(0xdeadface);
|
||||
```
|
||||
|
||||
The function `js_stalker_callback` should return `TRUE` if the original
|
||||
instruction should be emitted in the instrumented code, or `FALSE` otherwise.
|
||||
In the example above, we can see it is replaced with a `NOP`.
|
||||
|
||||
Lastly, note that the same callback will be called when compiling instrumented
|
||||
code both in the child of the forkserver (as it is executed) and also in the
|
||||
parent of the forserver (when prefetching is enabled) so that it can be
|
||||
inherited by the next forked child. It is **VERY** important that the same
|
||||
instructions be generated in both the parent and the child, or if prefetching is
|
||||
disabled that the same instructions are generated every time the block is
|
||||
compiled. Failure to do so will likely lead to bugs which are incredibly
|
||||
difficult to diagnose. The code above only prints the instructions when running
|
||||
in the parent process (the one provided by `Process.id` when the JS script is
|
||||
executed).
|
||||
|
||||
# API
|
||||
```js
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
Afl.print(msg);
|
||||
class Afl {
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
Afl.done();
|
||||
/**
|
||||
* 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 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.
|
||||
*/
|
||||
Afl.error();
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
Afl.setEntryPoint(address);
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_ADDR`, again a
|
||||
* `NativePointer` should be provided as it's argument.
|
||||
*/
|
||||
Afl.setPersistentAddress(address);
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_RET`, again a
|
||||
* `NativePointer` should be provided as it's argument.
|
||||
*/
|
||||
Afl.setPersistentReturn(address);
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static error(msg: string): void {
|
||||
const buf = Memory.allocUtf8String(msg);
|
||||
Afl.jsApiError(buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is equivalent to setting `AFL_FRIDA_PERSISTENT_CNT`, a
|
||||
* `number` should be provided as it's argument.
|
||||
*/
|
||||
Afl.setPersistentCount(count);
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_PERSISTENT_DEBUG`.
|
||||
*/
|
||||
Afl.setPersistentDebug();
|
||||
return Afl.jsApiGetSymbol("__afl_fuzz_len");
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_DEBUG_MAPS`.
|
||||
*/
|
||||
Afl.setDebugMaps();
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
Afl.addIncludedRange(address, size);
|
||||
return Afl.jsApiGetSymbol("__afl_fuzz_ptr");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
Afl.addExcludedRange(address, size);
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_INST_LIBS`.
|
||||
*/
|
||||
Afl.setInstrumentLibraries();
|
||||
/**
|
||||
* See `AFL_FRIDA_DEBUG_MAPS`.
|
||||
*/
|
||||
public static setDebugMaps(): void {
|
||||
Afl.jsApiSetDebugMaps();
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_INST_DEBUG_FILE`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
Afl.setInstrumentDebugFile(file);
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static setEntryPoint(address: NativePointer): void {
|
||||
Afl.jsApiSetEntryPoint(address);
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_INST_NO_PREFETCH`.
|
||||
*/
|
||||
Afl.setPrefetchDisable();
|
||||
/**
|
||||
* Function used to enable in-memory test cases for fuzzing.
|
||||
*/
|
||||
public static setInMemoryFuzzing(): void {
|
||||
Afl.jsApiAflSharedMemFuzzing.writeInt(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
Afl.setInstrumentNoOptimize();
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_INST_TRACE`.
|
||||
*/
|
||||
Afl.setInstrumentEnableTracing();
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE`.
|
||||
*/
|
||||
public static setInstrumentEnableTracing(): void {
|
||||
Afl.jsApiSetInstrumentTrace();
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_INST_TRACE_UNIQUE`.
|
||||
*/
|
||||
Afl.setInstrumentTracingUnique()
|
||||
/**
|
||||
* See `AFL_INST_LIBS`.
|
||||
*/
|
||||
public static setInstrumentLibraries(): void {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_OUTPUT_STDOUT`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
Afl.setStdOut(file);
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
public static setInstrumentNoOptimize(): void {
|
||||
Afl.jsApiSetInstrumentNoOptimize();
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_OUTPUT_STDERR`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
Afl.setStdErr(file);
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE_UNIQUE`.
|
||||
*/
|
||||
public static setInstrumentTracingUnique(): void {
|
||||
Afl.jsApiSetInstrumentTraceUnique();
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_STATS_FILE`. This function takes a single `string` as
|
||||
* an argument.
|
||||
*/
|
||||
Afl.setStatsFile(file);
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
|
||||
* argument
|
||||
*/
|
||||
Afl.setStatsInterval(interval);
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_PERSISTENT_DEBUG`.
|
||||
*/
|
||||
public static setPersistentDebug(): void {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_PREFETCH`.
|
||||
*/
|
||||
public static setPrefetchDisable(): void {
|
||||
Afl.jsApiSetPrefetchDisable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a function to be called for each instruction which is instrumented
|
||||
* by AFL FRIDA mode.
|
||||
*/
|
||||
public static setStalkerCallback(callback: NativePointer): void {
|
||||
Afl.jsApiSetStalkerCallback(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
|
||||
* argument
|
||||
*/
|
||||
public static setStatsInterval(interval: number): void {
|
||||
Afl.jsApiSetStatsInterval(interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_TRANSITIONS`
|
||||
*/
|
||||
public static setStatsTransitions(): void {
|
||||
Afl.jsApiSetStatsTransitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* See `AFL_FRIDA_STATS_TRANSITIONS`
|
||||
*/
|
||||
Afl.setStatsTransitions()
|
||||
```
|
||||
|
50
frida_mode/hook/hook.c
Normal file
50
frida_mode/hook/hook.c
Normal file
@ -0,0 +1,50 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
|
||||
uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
|
||||
uint32_t input_buf_len) {
|
||||
|
||||
void **esp = (void **)regs->esp;
|
||||
void * arg1 = esp[0];
|
||||
void **arg2 = &esp[1];
|
||||
memcpy(arg1, input_buf, input_buf_len);
|
||||
*arg2 = (void *)input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
|
||||
uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->x[0], input_buf, input_buf_len);
|
||||
regs->x[1] = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
int afl_persistent_hook_init(void) {
|
||||
|
||||
// 1 for shared memory input (faster), 0 for normal input (you have to use
|
||||
// read(), input_buf will be NULL)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
@ -3,10 +3,15 @@
|
||||
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
typedef gboolean (*js_api_stalker_callback_t)(const cs_insn *insn,
|
||||
gboolean begin, gboolean excluded,
|
||||
GumStalkerOutput *output);
|
||||
|
||||
extern unsigned char api_js[];
|
||||
extern unsigned int api_js_len;
|
||||
|
||||
extern gboolean js_done;
|
||||
extern gboolean js_done;
|
||||
extern js_api_stalker_callback_t js_user_callback;
|
||||
|
||||
/* Frida Mode */
|
||||
|
||||
@ -14,5 +19,8 @@ void js_config(void);
|
||||
|
||||
void js_start(void);
|
||||
|
||||
gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
|
||||
gboolean excluded, GumStalkerOutput *output);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -13,6 +13,7 @@ guint64 entry_point = 0;
|
||||
|
||||
static void entry_launch(void) {
|
||||
|
||||
OKF("Entry point reached");
|
||||
__afl_manual_init();
|
||||
|
||||
/* Child here */
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "entry.h"
|
||||
#include "frida_cmplog.h"
|
||||
#include "instrument.h"
|
||||
#include "js.h"
|
||||
#include "persistent.h"
|
||||
#include "prefetch.h"
|
||||
#include "ranges.h"
|
||||
@ -165,8 +166,6 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
}
|
||||
|
||||
begin = FALSE;
|
||||
|
||||
}
|
||||
|
||||
instrument_debug_instruction(instr->address, instr->size);
|
||||
@ -178,7 +177,13 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
|
||||
|
||||
}
|
||||
|
||||
gum_stalker_iterator_keep(iterator);
|
||||
if (js_stalker_callback(instr, begin, excluded, output)) {
|
||||
|
||||
gum_stalker_iterator_keep(iterator);
|
||||
|
||||
}
|
||||
|
||||
begin = FALSE;
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,201 +1,243 @@
|
||||
const write = new NativeFunction(
|
||||
Module.getExportByName(null, 'write'),
|
||||
'int',
|
||||
['int', 'pointer', 'int']
|
||||
);
|
||||
|
||||
const afl_frida_trace = Process.findModuleByName('afl-frida-trace.so');
|
||||
|
||||
function get_api(name, ret, args) {
|
||||
const addr = afl_frida_trace.findExportByName(name);
|
||||
return new NativeFunction(addr, ret, args);
|
||||
}
|
||||
|
||||
const js_api_done = get_api(
|
||||
'js_api_done',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const js_api_error = get_api(
|
||||
'js_api_error',
|
||||
'void',
|
||||
['pointer']);
|
||||
|
||||
const js_api_set_entrypoint = get_api(
|
||||
'js_api_set_entrypoint',
|
||||
'void',
|
||||
['pointer']);
|
||||
|
||||
const js_api_set_persistent_address = get_api(
|
||||
'js_api_set_persistent_address',
|
||||
'void',
|
||||
['pointer']);
|
||||
|
||||
const js_api_set_persistent_return = get_api(
|
||||
'js_api_set_persistent_return',
|
||||
'void',
|
||||
['pointer']);
|
||||
|
||||
const js_api_set_persistent_count = get_api(
|
||||
'js_api_set_persistent_count',
|
||||
'void',
|
||||
['uint64']);
|
||||
|
||||
const js_api_set_persistent_debug = get_api(
|
||||
'js_api_set_persistent_debug',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const js_api_set_debug_maps = get_api(
|
||||
'js_api_set_debug_maps',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const js_api_add_include_range = get_api(
|
||||
'js_api_add_include_range',
|
||||
'void',
|
||||
['pointer', 'size_t']);
|
||||
|
||||
const js_api_add_exclude_range = get_api(
|
||||
'js_api_add_exclude_range',
|
||||
'void',
|
||||
['pointer', 'size_t']);
|
||||
|
||||
const js_api_set_instrument_libraries = get_api(
|
||||
'js_api_set_instrument_libraries',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const js_api_set_instrument_debug_file = get_api(
|
||||
'js_api_set_instrument_debug_file',
|
||||
'void',
|
||||
['pointer']);
|
||||
|
||||
const js_api_set_prefetch_disable = get_api(
|
||||
'js_api_set_prefetch_disable',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const js_api_set_instrument_no_optimize = get_api(
|
||||
'js_api_set_instrument_no_optimize',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const js_api_set_instrument_trace = get_api(
|
||||
'js_api_set_instrument_trace',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const js_api_set_instrument_trace_unique = get_api(
|
||||
'js_api_set_instrument_trace_unique',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const js_api_set_stdout = get_api(
|
||||
'js_api_set_stdout',
|
||||
'void',
|
||||
['pointer']);
|
||||
|
||||
const js_api_set_stderr = get_api(
|
||||
'js_api_set_stderr',
|
||||
'void',
|
||||
['pointer']);
|
||||
|
||||
const js_api_set_stats_file = get_api(
|
||||
'js_api_set_stats_file',
|
||||
'void',
|
||||
['pointer']);
|
||||
|
||||
const js_api_set_stats_interval = get_api(
|
||||
'js_api_set_stats_interval',
|
||||
'void',
|
||||
['uint64']);
|
||||
|
||||
const js_api_set_stats_transitions = get_api(
|
||||
'js_api_set_stats_transitions',
|
||||
'void',
|
||||
[]);
|
||||
|
||||
const afl = {
|
||||
print: function (msg) {
|
||||
"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);
|
||||
write(STDOUT_FILENO, buf, log.length);
|
||||
},
|
||||
done: function() {
|
||||
js_api_done();
|
||||
},
|
||||
error: function(msg) {
|
||||
const buf = Memory.allocUtf8String(msg);
|
||||
js_api_error(buf);
|
||||
},
|
||||
setEntryPoint: function(addr) {
|
||||
js_api_set_entrypoint(addr);
|
||||
},
|
||||
setPersistentAddress: function(addr) {
|
||||
js_api_set_persistent_address(addr);
|
||||
},
|
||||
setPersistentReturn: function(addr) {
|
||||
js_api_set_persistent_return(addr);
|
||||
},
|
||||
setPersistentCount: function(addr) {
|
||||
js_api_set_persistent_count(addr);
|
||||
},
|
||||
setPersistentDebug: function() {
|
||||
js_api_set_persistent_debug();
|
||||
},
|
||||
setDebugMaps: function() {
|
||||
js_api_set_debug_maps();
|
||||
},
|
||||
addIncludedRange: function(address, size) {
|
||||
js_api_add_include_range(address, size);
|
||||
},
|
||||
addExcludedRange: function(address, size) {
|
||||
js_api_add_exclude_range(address, size);
|
||||
},
|
||||
setInstrumentLibraries: function() {
|
||||
js_api_set_instrument_libraries();
|
||||
},
|
||||
setInstrumentDebugFile: function(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
js_api_set_instrument_debug_file(buf)
|
||||
},
|
||||
setPrefetchDisable: function() {
|
||||
js_api_set_prefetch_disable();
|
||||
},
|
||||
setInstrumentNoOptimize: function() {
|
||||
js_api_set_instrument_no_optimize();
|
||||
},
|
||||
setInstrumentEnableTracing: function() {
|
||||
js_api_set_instrument_trace();
|
||||
},
|
||||
setInstrumentTracingUnique: function() {
|
||||
js_api_set_instrument_trace_unique();
|
||||
},
|
||||
setStdOut: function(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
js_api_set_stdout(buf)
|
||||
},
|
||||
setStdErr: function(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
js_api_set_stderr(buf)
|
||||
},
|
||||
setStatsFile: function(file) {
|
||||
const buf = Memory.allocUtf8String(file);
|
||||
js_api_set_stats_file(buf)
|
||||
},
|
||||
setStatsInterval: function(interval) {
|
||||
js_api_set_stats_interval(interval);
|
||||
},
|
||||
setStatsTransitions: function() {
|
||||
js_api_set_stats_transitions();
|
||||
Afl.jsApiWrite(STDOUT_FILENO, buf, log.length);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
Object.defineProperty(global, 'Afl', {value: afl, writeable: false});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// END OF API //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* 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"]);
|
||||
|
@ -5,8 +5,9 @@
|
||||
#include "js.h"
|
||||
#include "util.h"
|
||||
|
||||
static char *js_script = NULL;
|
||||
gboolean js_done = FALSE;
|
||||
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;
|
||||
@ -111,3 +112,11 @@ void js_start(void) {
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
|
@ -138,5 +138,15 @@ void js_api_set_stats_transitions() {
|
||||
|
||||
}
|
||||
|
||||
// "AFL_FRIDA_PERSISTENT_HOOK",
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
|
@ -47,19 +47,6 @@ void persistent_config(void) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void persistent_init(void) {
|
||||
|
||||
OKF("Instrumentation - persistent mode [%c] (0x%016" G_GINT64_MODIFIER "X)",
|
||||
persistent_start == 0 ? ' ' : 'X', persistent_start);
|
||||
OKF("Instrumentation - persistent count [%c] (%" G_GINT64_MODIFIER "d)",
|
||||
persistent_start == 0 ? ' ' : 'X', persistent_count);
|
||||
OKF("Instrumentation - hook [%s]", hook_name);
|
||||
|
||||
OKF("Instrumentation - persistent ret [%c] (0x%016" G_GINT64_MODIFIER "X)",
|
||||
persistent_ret == 0 ? ' ' : 'X', persistent_ret);
|
||||
|
||||
if (hook_name == NULL) { return; }
|
||||
|
||||
void *hook_obj = dlopen(hook_name, RTLD_NOW);
|
||||
@ -79,7 +66,20 @@ void persistent_init(void) {
|
||||
if (persistent_hook == NULL)
|
||||
FATAL("Failed to find afl_persistent_hook in %s", hook_name);
|
||||
|
||||
__afl_sharedmem_fuzzing = 1;
|
||||
}
|
||||
|
||||
void persistent_init(void) {
|
||||
|
||||
OKF("Instrumentation - persistent mode [%c] (0x%016" G_GINT64_MODIFIER "X)",
|
||||
persistent_start == 0 ? ' ' : 'X', persistent_start);
|
||||
OKF("Instrumentation - persistent count [%c] (%" G_GINT64_MODIFIER "d)",
|
||||
persistent_start == 0 ? ' ' : 'X', persistent_count);
|
||||
OKF("Instrumentation - hook [%s]", hook_name);
|
||||
|
||||
OKF("Instrumentation - persistent ret [%c] (0x%016" G_GINT64_MODIFIER "X)",
|
||||
persistent_ret == 0 ? ' ' : 'X', persistent_ret);
|
||||
|
||||
if (persistent_hook != NULL) { __afl_sharedmem_fuzzing = 1; }
|
||||
|
||||
}
|
||||
|
||||
|
@ -9,99 +9,15 @@
|
||||
#include "util.h"
|
||||
|
||||
#if defined(__aarch64__)
|
||||
typedef struct {
|
||||
|
||||
struct arm64_regs {
|
||||
GumCpuContext ctx;
|
||||
uint64_t rflags;
|
||||
|
||||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10;
|
||||
} persistent_ctx_t;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x11;
|
||||
uint32_t fp_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x12;
|
||||
uint32_t ip_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x13;
|
||||
uint32_t sp_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x14;
|
||||
uint32_t lr_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x15;
|
||||
uint32_t pc_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x16;
|
||||
uint64_t ip0;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x17;
|
||||
uint64_t ip1;
|
||||
|
||||
};
|
||||
|
||||
uint64_t x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x29;
|
||||
uint64_t fp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x30;
|
||||
uint64_t lr;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x31;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
// the zero register is not saved here ofc
|
||||
|
||||
uint64_t pc;
|
||||
|
||||
uint32_t cpsr;
|
||||
|
||||
uint8_t vfp_zregs[32][16 * 16];
|
||||
uint8_t vfp_pregs[17][32];
|
||||
uint32_t vfp_xregs[16];
|
||||
|
||||
};
|
||||
|
||||
typedef struct arm64_regs arch_api_regs;
|
||||
|
||||
static arch_api_regs saved_regs = {0};
|
||||
static gpointer saved_lr = NULL;
|
||||
static persistent_ctx_t saved_regs = {0};
|
||||
static gpointer saved_lr = NULL;
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
@ -109,8 +25,8 @@ gboolean persistent_is_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_save_regs(GumArm64Writer * cw,
|
||||
struct arm64_regs *regs) {
|
||||
static void instrument_persitent_save_regs(GumArm64Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
const guint32 mrs_x1_nzcv = 0xd53b4201;
|
||||
@ -129,83 +45,87 @@ static void instrument_persitent_save_regs(GumArm64Writer * cw,
|
||||
|
||||
/* Skip x0 & x1 we'll do that later */
|
||||
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X3,
|
||||
ARM64_REG_X0, (16 * 1),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X4, ARM64_REG_X5,
|
||||
ARM64_REG_X0, (16 * 2),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X6, ARM64_REG_X7,
|
||||
ARM64_REG_X0, (16 * 3),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X8, ARM64_REG_X9,
|
||||
ARM64_REG_X0, (16 * 4),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X10, ARM64_REG_X11,
|
||||
ARM64_REG_X0, (16 * 5),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X12, ARM64_REG_X13,
|
||||
ARM64_REG_X0, (16 * 6),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X14, ARM64_REG_X15,
|
||||
ARM64_REG_X0, (16 * 7),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X16, ARM64_REG_X17,
|
||||
ARM64_REG_X0, (16 * 8),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X18, ARM64_REG_X19,
|
||||
ARM64_REG_X0, (16 * 9),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X20, ARM64_REG_X21,
|
||||
ARM64_REG_X0, (16 * 10),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X22, ARM64_REG_X23,
|
||||
ARM64_REG_X0, (16 * 11),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X24, ARM64_REG_X25,
|
||||
ARM64_REG_X0, (16 * 12),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X26, ARM64_REG_X27,
|
||||
ARM64_REG_X0, (16 * 13),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X28, ARM64_REG_X29,
|
||||
ARM64_REG_X0, (16 * 14),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[2]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X4, ARM64_REG_X5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[4]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X6, ARM64_REG_X7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[6]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X8, ARM64_REG_X9, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[8]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X10, ARM64_REG_X11, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[10]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X12, ARM64_REG_X13, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[12]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X14, ARM64_REG_X15, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[14]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X16, ARM64_REG_X17, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X18, ARM64_REG_X19, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[18]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X20, ARM64_REG_X21, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[20]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X22, ARM64_REG_X23, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[22]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X24, ARM64_REG_X25, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[24]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X26, ARM64_REG_X27, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[26]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X28, ARM64_REG_X29, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[28]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* LR & Adjusted SP */
|
||||
gum_arm64_writer_put_add_reg_reg_imm(cw, ARM64_REG_X2, ARM64_REG_SP,
|
||||
(GUM_RED_ZONE_SIZE + 32));
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X30, ARM64_REG_X2,
|
||||
ARM64_REG_X0, (16 * 15),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
/* LR (x30) */
|
||||
gum_arm64_writer_put_str_reg_reg_offset(cw, ARM64_REG_X30, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[30]));
|
||||
|
||||
/* PC & CPSR */
|
||||
/* PC & Adjusted SP (31) */
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X2,
|
||||
GUM_ADDRESS(persistent_start));
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X1,
|
||||
ARM64_REG_X0, (16 * 16),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_add_reg_reg_imm(cw, ARM64_REG_X3, ARM64_REG_SP,
|
||||
(GUM_RED_ZONE_SIZE + 32));
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_X0, offsetof(GumCpuContext, pc),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_Q0, ARM64_REG_Q1,
|
||||
ARM64_REG_X0, (16 * 17),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_Q2, ARM64_REG_Q3,
|
||||
ARM64_REG_X0, (16 * 18),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_Q4, ARM64_REG_Q5,
|
||||
ARM64_REG_X0, (16 * 19),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_Q6, ARM64_REG_Q7,
|
||||
ARM64_REG_X0, (16 * 20),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
/* CPSR */
|
||||
gum_arm64_writer_put_str_reg_reg_offset(cw, ARM64_REG_X1, ARM64_REG_X0,
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
|
||||
/* Q */
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q0, ARM64_REG_Q1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q2, ARM64_REG_Q3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q4, ARM64_REG_Q5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[32]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q6, ARM64_REG_Q7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[48]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* x0 & x1 */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X3,
|
||||
ARM64_REG_SP, 16,
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X3,
|
||||
ARM64_REG_X0, (16 * 0),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_stp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
@ -217,8 +137,8 @@ static void instrument_persitent_save_regs(GumArm64Writer * cw,
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_restore_regs(GumArm64Writer * cw,
|
||||
struct arm64_regs *regs) {
|
||||
static void instrument_persitent_restore_regs(GumArm64Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
const guint32 msr_nzcv_x1 = 0xd51b4201;
|
||||
@ -228,82 +148,81 @@ static void instrument_persitent_restore_regs(GumArm64Writer * cw,
|
||||
|
||||
/* Skip x0 - x3 we'll do that last */
|
||||
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X4, ARM64_REG_X5,
|
||||
ARM64_REG_X0, (16 * 2),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X6, ARM64_REG_X7,
|
||||
ARM64_REG_X0, (16 * 3),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X8, ARM64_REG_X9,
|
||||
ARM64_REG_X0, (16 * 4),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X10, ARM64_REG_X11,
|
||||
ARM64_REG_X0, (16 * 5),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X12, ARM64_REG_X13,
|
||||
ARM64_REG_X0, (16 * 6),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X14, ARM64_REG_X15,
|
||||
ARM64_REG_X0, (16 * 7),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X16, ARM64_REG_X17,
|
||||
ARM64_REG_X0, (16 * 8),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X18, ARM64_REG_X19,
|
||||
ARM64_REG_X0, (16 * 9),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X20, ARM64_REG_X21,
|
||||
ARM64_REG_X0, (16 * 10),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X22, ARM64_REG_X23,
|
||||
ARM64_REG_X0, (16 * 11),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X24, ARM64_REG_X25,
|
||||
ARM64_REG_X0, (16 * 12),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X26, ARM64_REG_X27,
|
||||
ARM64_REG_X0, (16 * 13),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X28, ARM64_REG_X29,
|
||||
ARM64_REG_X0, (16 * 14),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X4, ARM64_REG_X5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[4]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X6, ARM64_REG_X7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[6]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X8, ARM64_REG_X9, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[8]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X10, ARM64_REG_X11, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[10]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X12, ARM64_REG_X13, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[12]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X14, ARM64_REG_X15, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[14]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X16, ARM64_REG_X17, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X18, ARM64_REG_X19, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[18]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X20, ARM64_REG_X21, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[20]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X22, ARM64_REG_X23, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[22]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X24, ARM64_REG_X25, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[24]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X26, ARM64_REG_X27, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[26]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X28, ARM64_REG_X29, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[28]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* LR & Adjusted SP (use x1 as clobber) */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X30, ARM64_REG_X1,
|
||||
ARM64_REG_X0, (16 * 15),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
/* LR (x30) */
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X30, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[30]));
|
||||
|
||||
/* Adjusted SP (31) (use x1 as clobber)*/
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, sp));
|
||||
gum_arm64_writer_put_mov_reg_reg(cw, ARM64_REG_SP, ARM64_REG_X1);
|
||||
|
||||
/* Don't restore RIP use x1-x3 as clobber */
|
||||
|
||||
/* PC (x2) & CPSR (x1) */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X1,
|
||||
ARM64_REG_X0, (16 * 16),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
/* CPSR */
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X1, ARM64_REG_X0,
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
gum_arm64_writer_put_instruction(cw, msr_nzcv_x1);
|
||||
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_Q0, ARM64_REG_Q1,
|
||||
ARM64_REG_X0, (16 * 17),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_Q2, ARM64_REG_Q3,
|
||||
ARM64_REG_X0, (16 * 18),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_Q4, ARM64_REG_Q5,
|
||||
ARM64_REG_X0, (16 * 19),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_Q6, ARM64_REG_Q7,
|
||||
ARM64_REG_X0, (16 * 20),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q0, ARM64_REG_Q1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q2, ARM64_REG_Q3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[16]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q4, ARM64_REG_Q5, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[32]), GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_Q6, ARM64_REG_Q7, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, q[48]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
/* x2 & x3 */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X3,
|
||||
ARM64_REG_X0, (16 * 1),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X2, ARM64_REG_X3, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[2]), GUM_INDEX_SIGNED_OFFSET);
|
||||
/* x0 & x1 */
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(cw, ARM64_REG_X0, ARM64_REG_X1,
|
||||
ARM64_REG_X0, (16 * 0),
|
||||
GUM_INDEX_SIGNED_OFFSET);
|
||||
gum_arm64_writer_put_ldp_reg_reg_reg_offset(
|
||||
cw, ARM64_REG_X0, ARM64_REG_X1, ARM64_REG_X0,
|
||||
offsetof(GumCpuContext, x[0]), GUM_INDEX_SIGNED_OFFSET);
|
||||
|
||||
}
|
||||
|
||||
@ -334,29 +253,29 @@ static void instrument_afl_persistent_loop(GumArm64Writer *cw) {
|
||||
|
||||
}
|
||||
|
||||
static void persistent_prologue_hook(GumArm64Writer * cw,
|
||||
struct arm64_regs *regs) {
|
||||
static void persistent_prologue_hook(GumArm64Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
if (persistent_hook == NULL) return;
|
||||
|
||||
gum_arm64_writer_put_sub_reg_reg_imm(cw, ARM64_REG_SP, ARM64_REG_SP,
|
||||
GUM_RED_ZONE_SIZE);
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X3,
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X3, ARM64_REG_X3, 0);
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X3, ARM64_REG_X3, 0);
|
||||
|
||||
gum_arm64_writer_put_and_reg_reg_imm(cw, ARM64_REG_X3, ARM64_REG_X3,
|
||||
G_MAXULONG);
|
||||
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X2,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X2, 0);
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X2, ARM64_REG_X2, 0);
|
||||
|
||||
gum_arm64_writer_put_and_reg_reg_imm(cw, ARM64_REG_X2, ARM64_REG_X2,
|
||||
G_MAXULONG);
|
||||
|
||||
gum_arm64_writer_put_ldr_reg_address(cw, ARM64_REG_X1,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
gum_arm64_writer_put_ldr_reg_reg_offset(cw, ARM64_REG_X1, ARM64_REG_X1, 0);
|
||||
|
||||
gum_arm64_writer_put_call_address_with_arguments(
|
||||
cw, GUM_ADDRESS(persistent_hook), 4, GUM_ARG_ADDRESS, GUM_ADDRESS(regs),
|
||||
GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_REGISTER, ARM64_REG_X2,
|
||||
GUM_ARG_REGISTER, ARM64_REG_X3);
|
||||
cw, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, ARM64_REG_X1, GUM_ARG_REGISTER,
|
||||
ARM64_REG_X2);
|
||||
|
||||
gum_arm64_writer_put_add_reg_reg_imm(cw, ARM64_REG_SP, ARM64_REG_SP,
|
||||
GUM_RED_ZONE_SIZE);
|
||||
@ -406,6 +325,8 @@ void persistent_prologue(GumStalkerOutput *output) {
|
||||
|
||||
gconstpointer loop = cw->code + 1;
|
||||
|
||||
OKF("Persistent loop reached");
|
||||
|
||||
instrument_persitent_save_regs(cw, &saved_regs);
|
||||
|
||||
/* loop: */
|
||||
|
@ -10,40 +10,15 @@
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct x86_64_regs {
|
||||
typedef struct {
|
||||
|
||||
uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
|
||||
r15;
|
||||
GumCpuContext ctx;
|
||||
uint64_t rflags;
|
||||
|
||||
union {
|
||||
} persistent_ctx_t;
|
||||
|
||||
uint64_t rip;
|
||||
uint64_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rsp;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rflags;
|
||||
uint64_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t zmm_regs[32][64];
|
||||
|
||||
};
|
||||
|
||||
typedef struct x86_64_regs arch_api_regs;
|
||||
|
||||
static arch_api_regs saved_regs = {0};
|
||||
static gpointer saved_ret = NULL;
|
||||
static persistent_ctx_t saved_regs = {0};
|
||||
static gpointer saved_ret = NULL;
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
@ -51,8 +26,8 @@ gboolean persistent_is_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
struct x86_64_regs *regs) {
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
@ -64,41 +39,41 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 1),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 2),
|
||||
GUM_REG_RCX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 3),
|
||||
GUM_REG_RDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 4),
|
||||
GUM_REG_RDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 5),
|
||||
GUM_REG_RSI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 6),
|
||||
GUM_REG_RBP);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 7),
|
||||
GUM_REG_R8);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 8),
|
||||
GUM_REG_R9);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 9),
|
||||
GUM_REG_R10);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 10),
|
||||
GUM_REG_R11);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 11),
|
||||
GUM_REG_R12);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 12),
|
||||
GUM_REG_R13);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 13),
|
||||
GUM_REG_R14);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 14),
|
||||
GUM_REG_R15);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rbx), GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rcx), GUM_REG_RCX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rdx), GUM_REG_RDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rdi), GUM_REG_RDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rsi), GUM_REG_RSI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rbp), GUM_REG_RBP);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r8), GUM_REG_R8);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r9), GUM_REG_R9);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r10), GUM_REG_R10);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r11), GUM_REG_R11);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r12), GUM_REG_R12);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r13), GUM_REG_R13);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r14), GUM_REG_R14);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, r15), GUM_REG_R15);
|
||||
|
||||
/* Store RIP */
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RBX,
|
||||
GUM_ADDRESS(persistent_start));
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 15),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rip), GUM_REG_RBX);
|
||||
|
||||
/* Store adjusted RSP */
|
||||
gum_x86_writer_put_mov_reg_reg(cw, GUM_REG_RBX, GUM_REG_RSP);
|
||||
@ -106,18 +81,18 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
/* RED_ZONE + Saved flags, RAX, alignment */
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_REG_RBX,
|
||||
GUM_RED_ZONE_SIZE + (0x8 * 2));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 16),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rsp), GUM_REG_RBX);
|
||||
|
||||
/* Save the flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP, 0x8);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 17),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(persistent_ctx_t, rflags), GUM_REG_RBX);
|
||||
|
||||
/* Save the RAX */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP, 0x0);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 0),
|
||||
GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_RAX, offsetof(GumCpuContext, rax), GUM_REG_RBX);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 0x10);
|
||||
@ -127,56 +102,56 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
struct x86_64_regs *regs) {
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RCX, GUM_REG_RAX,
|
||||
(0x8 * 2));
|
||||
offsetof(GumCpuContext, rcx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RAX,
|
||||
(0x8 * 3));
|
||||
offsetof(GumCpuContext, rdx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDI, GUM_REG_RAX,
|
||||
(0x8 * 4));
|
||||
offsetof(GumCpuContext, rdi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSI, GUM_REG_RAX,
|
||||
(0x8 * 5));
|
||||
offsetof(GumCpuContext, rsi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBP, GUM_REG_RAX,
|
||||
(0x8 * 6));
|
||||
offsetof(GumCpuContext, rbp));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R8, GUM_REG_RAX,
|
||||
(0x8 * 7));
|
||||
offsetof(GumCpuContext, r8));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R9, GUM_REG_RAX,
|
||||
(0x8 * 8));
|
||||
offsetof(GumCpuContext, r9));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R10, GUM_REG_RAX,
|
||||
(0x8 * 9));
|
||||
offsetof(GumCpuContext, r10));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R11, GUM_REG_RAX,
|
||||
(0x8 * 10));
|
||||
offsetof(GumCpuContext, r11));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R12, GUM_REG_RAX,
|
||||
(0x8 * 11));
|
||||
offsetof(GumCpuContext, r12));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R13, GUM_REG_RAX,
|
||||
(0x8 * 12));
|
||||
offsetof(GumCpuContext, r13));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R14, GUM_REG_RAX,
|
||||
(0x8 * 13));
|
||||
offsetof(GumCpuContext, r14));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R15, GUM_REG_RAX,
|
||||
(0x8 * 14));
|
||||
offsetof(GumCpuContext, r15));
|
||||
|
||||
/* Don't restore RIP */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSP, GUM_REG_RAX,
|
||||
(0x8 * 16));
|
||||
offsetof(GumCpuContext, rsp));
|
||||
|
||||
/* Restore RBX, RAX & Flags */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
(0x8 * 1));
|
||||
offsetof(GumCpuContext, rbx));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
(0x8 * 0));
|
||||
offsetof(GumCpuContext, rax));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RAX,
|
||||
(0x8 * 17));
|
||||
offsetof(persistent_ctx_t, rflags));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_RBX);
|
||||
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
@ -217,28 +192,27 @@ static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
|
||||
}
|
||||
|
||||
static void persistent_prologue_hook(GumX86Writer * cw,
|
||||
struct x86_64_regs *regs) {
|
||||
static void persistent_prologue_hook(GumX86Writer *cw, persistent_ctx_t *regs) {
|
||||
|
||||
if (persistent_hook == NULL) return;
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
-(GUM_RED_ZONE_SIZE));
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RCX,
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RCX, GUM_REG_RCX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RCX, GUM_REG_RCX, 0);
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_RDI, 0xffffffff);
|
||||
gum_x86_writer_put_and_reg_reg(cw, GUM_REG_RCX, GUM_REG_RDI);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDX,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
GUM_ADDRESS(&__afl_fuzz_len));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 0);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 0);
|
||||
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_RDI, 0xffffffff);
|
||||
gum_x86_writer_put_and_reg_reg(cw, GUM_REG_RDX, GUM_REG_RDI);
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RSI,
|
||||
GUM_ADDRESS(&__afl_fuzz_ptr));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSI, GUM_REG_RSI, 0);
|
||||
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 4, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(regs), GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_REGISTER,
|
||||
GUM_REG_RDX, GUM_ARG_REGISTER, GUM_REG_RCX);
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_REG_RSI, GUM_ARG_REGISTER,
|
||||
GUM_REG_RDX);
|
||||
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP,
|
||||
(GUM_RED_ZONE_SIZE));
|
||||
@ -296,6 +270,8 @@ void persistent_prologue(GumStalkerOutput *output) {
|
||||
|
||||
gconstpointer loop = cw->code + 1;
|
||||
|
||||
OKF("Persistent loop reached");
|
||||
|
||||
/* Pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 8);
|
||||
|
||||
|
@ -1,45 +1,23 @@
|
||||
#include "frida-gumjs.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include "instrument.h"
|
||||
#include "persistent.h"
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
struct x86_regs {
|
||||
typedef struct {
|
||||
|
||||
uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
|
||||
GumCpuContext ctx;
|
||||
uint32_t eflags;
|
||||
|
||||
union {
|
||||
} persistent_ctx_t;
|
||||
|
||||
uint32_t eip;
|
||||
uint32_t pc;
|
||||
static persistent_ctx_t saved_regs = {0};
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t esp;
|
||||
uint32_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eflags;
|
||||
uint32_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t xmm_regs[8][16];
|
||||
|
||||
};
|
||||
|
||||
typedef struct x86_regs arch_api_regs;
|
||||
|
||||
static arch_api_regs saved_regs = {0};
|
||||
static gpointer saved_ret = NULL;
|
||||
static gpointer saved_ret = NULL;
|
||||
|
||||
gboolean persistent_is_supported(void) {
|
||||
|
||||
@ -47,8 +25,8 @@ gboolean persistent_is_supported(void) {
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
struct x86_regs *regs) {
|
||||
static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
|
||||
@ -58,80 +36,80 @@ static void instrument_persitent_save_regs(GumX86Writer * cw,
|
||||
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 1),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 2),
|
||||
GUM_REG_ECX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 3),
|
||||
GUM_REG_EDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 4),
|
||||
GUM_REG_EDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 5),
|
||||
GUM_REG_ESI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 6),
|
||||
GUM_REG_EBP);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ebx), GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ecx), GUM_REG_ECX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, edx), GUM_REG_EDX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, edi), GUM_REG_EDI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, esi), GUM_REG_ESI);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, ebp), GUM_REG_EBP);
|
||||
|
||||
/* Store RIP */
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EBX,
|
||||
GUM_ADDRESS(persistent_start));
|
||||
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 7),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, eip), GUM_REG_EBX);
|
||||
|
||||
/* Store adjusted RSP */
|
||||
gum_x86_writer_put_mov_reg_reg(cw, GUM_REG_EBX, GUM_REG_ESP);
|
||||
|
||||
/* RED_ZONE + Saved flags, RAX */
|
||||
gum_x86_writer_put_add_reg_imm(cw, GUM_REG_EBX, (0x4 * 2));
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 8),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, esp), GUM_REG_EBX);
|
||||
|
||||
/* Save the flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x4);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 9),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(persistent_ctx_t, eflags), GUM_REG_EBX);
|
||||
|
||||
/* Save the RAX */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_ESP, 0x0);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_EAX, (0x4 * 0),
|
||||
GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_offset_ptr_reg(
|
||||
cw, GUM_REG_EAX, offsetof(GumCpuContext, eax), GUM_REG_EBX);
|
||||
|
||||
/* Pop the saved values */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 0x8);
|
||||
|
||||
}
|
||||
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
struct x86_regs *regs) {
|
||||
static void instrument_persitent_restore_regs(GumX86Writer * cw,
|
||||
persistent_ctx_t *regs) {
|
||||
|
||||
GumAddress regs_address = GUM_ADDRESS(regs);
|
||||
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_EAX, regs_address);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ECX, GUM_REG_EAX,
|
||||
(0x4 * 2));
|
||||
offsetof(GumCpuContext, ecx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDX, GUM_REG_EAX,
|
||||
(0x4 * 3));
|
||||
offsetof(GumCpuContext, edx));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EDI, GUM_REG_EAX,
|
||||
(0x4 * 4));
|
||||
offsetof(GumCpuContext, edi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESI, GUM_REG_EAX,
|
||||
(0x4 * 5));
|
||||
offsetof(GumCpuContext, esi));
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBP, GUM_REG_EAX,
|
||||
(0x4 * 6));
|
||||
offsetof(GumCpuContext, ebp));
|
||||
|
||||
/* Don't restore RIP */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_ESP, GUM_REG_EAX,
|
||||
(0x4 * 8));
|
||||
offsetof(GumCpuContext, esp));
|
||||
|
||||
/* Restore RBX, RAX & Flags */
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
(0x4 * 1));
|
||||
offsetof(GumCpuContext, ebx));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
(0x4 * 0));
|
||||
offsetof(GumCpuContext, eax));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_EBX, GUM_REG_EAX,
|
||||
(0x4 * 9));
|
||||
offsetof(persistent_ctx_t, eflags));
|
||||
gum_x86_writer_put_push_reg(cw, GUM_REG_EBX);
|
||||
|
||||
gum_x86_writer_put_popfx(cw);
|
||||
@ -165,7 +143,7 @@ static void instrument_afl_persistent_loop(GumX86Writer *cw) {
|
||||
|
||||
}
|
||||
|
||||
static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) {
|
||||
static void persistent_prologue_hook(GumX86Writer *cw, persistent_ctx_t *regs) {
|
||||
|
||||
if (persistent_hook == NULL) return;
|
||||
|
||||
@ -180,9 +158,8 @@ static void persistent_prologue_hook(GumX86Writer *cw, struct x86_regs *regs) {
|
||||
|
||||
/* Base address is 64-bits (hence two zero arguments) */
|
||||
gum_x86_writer_put_call_address_with_arguments(
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 5, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(regs), GUM_ARG_ADDRESS, GUM_ADDRESS(0), GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(0), GUM_ARG_REGISTER, GUM_REG_EDX, GUM_ARG_REGISTER,
|
||||
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
|
||||
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_REG_EDX, GUM_ARG_REGISTER,
|
||||
GUM_REG_ECX);
|
||||
|
||||
}
|
||||
@ -233,6 +210,8 @@ void persistent_prologue(GumStalkerOutput *output) {
|
||||
|
||||
gconstpointer loop = cw->code + 1;
|
||||
|
||||
OKF("Persistent loop reached");
|
||||
|
||||
/* Pop the return value */
|
||||
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_ESP, GUM_REG_ESP, 4);
|
||||
|
||||
|
@ -2,8 +2,7 @@ PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../..)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
|
||||
AFLPP_DRIVER_HOOK_SRC=$(PWD)aflpp_qemu_driver_hook.c
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)aflpp_qemu_driver_hook.so
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
|
||||
|
||||
LIBJPEG_BUILD_DIR:=$(BUILD_DIR)libjpeg/
|
||||
HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
|
||||
@ -118,11 +117,6 @@ $(TEST_BIN): $(HARNESS_OBJ) $(JPEGTEST_OBJ) $(LIBJPEG_LIB)
|
||||
$(LDFLAGS) \
|
||||
$(TEST_BIN_LDFLAGS) \
|
||||
|
||||
########## HOOK ########
|
||||
|
||||
$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
|
||||
$(CC) -shared $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
########## DUMMY #######
|
||||
|
||||
$(TEST_DATA_DIR): | $(BUILD_DIR)
|
||||
@ -133,8 +127,6 @@ $(TEST_DATA_FILE): | $(TEST_DATA_DIR)
|
||||
|
||||
###### TEST DATA #######
|
||||
|
||||
hook: $(AFLPP_DRIVER_HOOK_OBJ)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
|
@ -14,6 +14,3 @@ frida:
|
||||
|
||||
debug:
|
||||
@gmake debug
|
||||
|
||||
hook:
|
||||
@gmake hook
|
||||
|
@ -1,97 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct x86_64_regs {
|
||||
|
||||
uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
|
||||
r15;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rip;
|
||||
uint64_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rsp;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rflags;
|
||||
uint64_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t zmm_regs[32][64];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
struct x86_regs {
|
||||
|
||||
uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eip;
|
||||
uint32_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t esp;
|
||||
uint32_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eflags;
|
||||
uint32_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t xmm_regs[8][16];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
void **esp = (void **)regs->esp;
|
||||
void * arg1 = esp[1];
|
||||
void **arg2 = &esp[2];
|
||||
memcpy(arg1, input_buf, input_buf_len);
|
||||
*arg2 = (void *)input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
int afl_persistent_hook_init(void) {
|
||||
|
||||
// 1 for shared memory input (faster), 0 for normal input (you have to use
|
||||
// read(), input_buf will be NULL)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
@ -1,18 +1,21 @@
|
||||
PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../..)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
TESTINSTR_DATA_DIR:=$(BUILD_DIR)in/
|
||||
TESTINSTR_DATA_FILE:=$(TESTINSTR_DATA_DIR)in
|
||||
TEST_DATA_DIR:=$(BUILD_DIR)in/
|
||||
TEST_DATA_FILE:=$(TEST_DATA_DIR)in
|
||||
|
||||
TESTINSTBIN:=$(BUILD_DIR)testinstr
|
||||
TESTINSTSRC:=$(PWD)testinstr.c
|
||||
TESTINSTBIN:=$(BUILD_DIR)test
|
||||
TESTINSTSRC:=$(PWD)test.c
|
||||
|
||||
TESTINSTBIN2:=$(BUILD_DIR)test2
|
||||
TESTINSTSRC2:=$(PWD)test2.c
|
||||
|
||||
QEMU_OUT:=$(BUILD_DIR)qemu-out
|
||||
FRIDA_OUT:=$(BUILD_DIR)frida-out
|
||||
|
||||
.PHONY: all 32 clean qemu frida
|
||||
|
||||
all: $(TESTINSTBIN)
|
||||
all: $(TESTINSTBIN) $(TESTINSTBIN2)
|
||||
make -C $(ROOT)frida_mode/
|
||||
|
||||
32:
|
||||
@ -21,24 +24,57 @@ all: $(TESTINSTBIN)
|
||||
$(BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(TESTINSTR_DATA_DIR): | $(BUILD_DIR)
|
||||
$(TEST_DATA_DIR): | $(BUILD_DIR)
|
||||
mkdir -p $@
|
||||
|
||||
$(TESTINSTR_DATA_FILE): | $(TESTINSTR_DATA_DIR)
|
||||
$(TEST_DATA_FILE): | $(TEST_DATA_DIR)
|
||||
echo -n "000" > $@
|
||||
|
||||
$(TESTINSTBIN): $(TESTINSTSRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
$(TESTINSTBIN2): $(TESTINSTSRC2) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
frida: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
AFL_FRIDA_JS_SCRIPT=test.js \
|
||||
frida_js_entry: $(TESTINSTBIN) $(TEST_DATA_FILE)
|
||||
AFL_FRIDA_JS_SCRIPT=entry.js \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-O \
|
||||
-i $(TESTINSTR_DATA_DIR) \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN) @@
|
||||
|
||||
frida_js_replace: $(TESTINSTBIN) $(TEST_DATA_FILE)
|
||||
AFL_FRIDA_JS_SCRIPT=replace.js \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN) @@
|
||||
|
||||
frida_js_patch: $(TESTINSTBIN2) $(TEST_DATA_FILE)
|
||||
AFL_FRIDA_JS_SCRIPT=patch.js \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN2) @@
|
||||
|
||||
frida_js_stalker: $(TESTINSTBIN2) $(TEST_DATA_FILE)
|
||||
AFL_FRIDA_JS_SCRIPT=stalker.js \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN2) @@
|
||||
|
@ -9,8 +9,17 @@ all:
|
||||
clean:
|
||||
@gmake clean
|
||||
|
||||
frida:
|
||||
@gmake frida
|
||||
frida_js_entry:
|
||||
@gmake frida_js_entry
|
||||
|
||||
frida_js_replace:
|
||||
@gmake frida_js_replace
|
||||
|
||||
frida_js_patch:
|
||||
@gmake frida_js_patch
|
||||
|
||||
frida_js_stalker:
|
||||
@gmake frida_js_stalker
|
||||
|
||||
debug:
|
||||
@gmake debug
|
||||
|
34
frida_mode/test/js/patch.js
Normal file
34
frida_mode/test/js/patch.js
Normal file
@ -0,0 +1,34 @@
|
||||
Afl.print('******************');
|
||||
Afl.print('* AFL FRIDA MODE *');
|
||||
Afl.print('******************');
|
||||
Afl.print('');
|
||||
|
||||
const main = DebugSymbol.fromName('main').address;
|
||||
Afl.print(`main: ${main}`);
|
||||
Afl.setEntryPoint(main);
|
||||
Afl.setPersistentAddress(main);
|
||||
Afl.setPersistentCount(10000000);
|
||||
|
||||
const crc32_check = DebugSymbol.fromName('crc32_check').address;
|
||||
const crc32_replacement = new NativeCallback(
|
||||
(buf, len) => {
|
||||
Afl.print(`len: ${len}`);
|
||||
if (len < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
},
|
||||
'int',
|
||||
['pointer', 'int']);
|
||||
Interceptor.replace(crc32_check, crc32_replacement);
|
||||
|
||||
const some_boring_bug = DebugSymbol.fromName('some_boring_bug').address
|
||||
const boring_replacement = new NativeCallback(
|
||||
(c) => { },
|
||||
'void',
|
||||
['char']);
|
||||
Interceptor.replace(some_boring_bug, boring_replacement);
|
||||
|
||||
Afl.done();
|
||||
Afl.print("done");
|
43
frida_mode/test/js/replace.js
Normal file
43
frida_mode/test/js/replace.js
Normal file
@ -0,0 +1,43 @@
|
||||
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 slow = DebugSymbol.fromName('slow').address;
|
||||
Afl.print(`slow: ${slow}`);
|
||||
|
||||
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 slow(void) {
|
||||
|
||||
LLVMFuzzerTestOneInput(__afl_fuzz_ptr, *__afl_fuzz_len);
|
||||
}
|
||||
`,
|
||||
{
|
||||
LLVMFuzzerTestOneInput: LLVMFuzzerTestOneInput,
|
||||
__afl_fuzz_ptr: Afl.getAflFuzzPtr(),
|
||||
__afl_fuzz_len: Afl.getAflFuzzLen()
|
||||
});
|
||||
|
||||
Afl.setEntryPoint(cm.slow);
|
||||
Afl.setPersistentAddress(cm.slow);
|
||||
Afl.setInMemoryFuzzing();
|
||||
Interceptor.replace(slow, cm.slow);
|
||||
Afl.print("done");
|
||||
Afl.done();
|
109
frida_mode/test/js/stalker.js
Normal file
109
frida_mode/test/js/stalker.js
Normal file
@ -0,0 +1,109 @@
|
||||
Afl.print('******************');
|
||||
Afl.print('* AFL FRIDA MODE *');
|
||||
Afl.print('******************');
|
||||
Afl.print('');
|
||||
|
||||
const main = DebugSymbol.fromName('main').address;
|
||||
Afl.print(`main: ${main}`);
|
||||
Afl.setEntryPoint(main);
|
||||
Afl.setPersistentAddress(main);
|
||||
Afl.setPersistentCount(10000000);
|
||||
|
||||
/* Replace CRC-32 check */
|
||||
const crc32_check = DebugSymbol.fromName('crc32_check').address;
|
||||
const crc32_replacement = new NativeCallback(
|
||||
(buf, len) => {
|
||||
if (len < 4) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
},
|
||||
'int',
|
||||
['pointer', 'int']);
|
||||
Interceptor.replace(crc32_check, crc32_replacement);
|
||||
|
||||
/* Patch out the first boring bug */
|
||||
const some_boring_bug = DebugSymbol.fromName('some_boring_bug').address
|
||||
const boring_replacement = new NativeCallback(
|
||||
(c) => { },
|
||||
'void',
|
||||
['char']);
|
||||
Interceptor.replace(some_boring_bug, boring_replacement);
|
||||
|
||||
/* Modify the instructions */
|
||||
const some_boring_bug2 = DebugSymbol.fromName('some_boring_bug2').address
|
||||
const pid = Memory.alloc(4);
|
||||
pid.writeInt(Process.id);
|
||||
|
||||
const cm = new CModule(`
|
||||
#include <stdio.h>
|
||||
#include <gum/gumstalker.h>
|
||||
|
||||
typedef int pid_t;
|
||||
|
||||
#define STDERR_FILENO 2
|
||||
#define BORING2_LEN 10
|
||||
|
||||
extern int dprintf(int fd, const char *format, ...);
|
||||
extern void some_boring_bug2(char c);
|
||||
extern pid_t getpid(void);
|
||||
extern pid_t pid;
|
||||
|
||||
gboolean js_stalker_callback(const cs_insn *insn, gboolean begin,
|
||||
gboolean excluded, GumStalkerOutput *output)
|
||||
{
|
||||
pid_t my_pid = getpid();
|
||||
GumX86Writer *cw = output->writer.x86;
|
||||
|
||||
if (GUM_ADDRESS(insn->address) < GUM_ADDRESS(some_boring_bug2)) {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
if (GUM_ADDRESS(insn->address) >=
|
||||
GUM_ADDRESS(some_boring_bug2) + BORING2_LEN) {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
|
||||
if (my_pid == pid) {
|
||||
|
||||
if (begin) {
|
||||
|
||||
dprintf(STDERR_FILENO, "\n> 0x%016lX: %s %s\n", insn->address,
|
||||
insn->mnemonic, insn->op_str);
|
||||
|
||||
} else {
|
||||
|
||||
dprintf(STDERR_FILENO, " 0x%016lX: %s %s\n", insn->address,
|
||||
insn->mnemonic, insn->op_str);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (insn->id == X86_INS_UD2) {
|
||||
|
||||
gum_x86_writer_put_nop(cw);
|
||||
return FALSE;
|
||||
|
||||
} else {
|
||||
|
||||
return TRUE;
|
||||
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
dprintf: Module.getExportByName(null, 'dprintf'),
|
||||
getpid: Module.getExportByName(null, 'getpid'),
|
||||
some_boring_bug2: some_boring_bug2,
|
||||
pid: pid
|
||||
});
|
||||
Afl.setStalkerCallback(cm.js_stalker_callback)
|
||||
Afl.setStdErr("/tmp/stderr.txt");
|
||||
Afl.done();
|
||||
Afl.print("done");
|
@ -16,13 +16,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define TESTINSTR_SECTION
|
||||
#else
|
||||
#define TESTINSTR_SECTION __attribute__((section(".testinstr")))
|
||||
#endif
|
||||
|
||||
void testinstr(char *buf, int len) {
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
if (len < 1) return;
|
||||
buf[len] = 0;
|
||||
@ -90,7 +84,7 @@ int run(char *file) {
|
||||
|
||||
dprintf(STDERR_FILENO, "Running: %s: (%zd bytes)\n", file, n_read);
|
||||
|
||||
testinstr(buf, len);
|
||||
LLVMFuzzerTestOneInput(buf, len);
|
||||
dprintf(STDERR_FILENO, "Done: %s: (%zd bytes)\n", file, n_read);
|
||||
|
||||
result = 0;
|
177
frida_mode/test/js/test2.c
Normal file
177
frida_mode/test/js/test2.c
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
american fuzzy lop++ - a trivial program to test the build
|
||||
--------------------------------------------------------
|
||||
Originally written by Michal Zalewski
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
Copyright 2019-2020 AFLplusplus Project. All rights reserved.
|
||||
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
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define IGNORED_RETURN(x) (void)!(x)
|
||||
|
||||
const uint32_t crc32_tab[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
uint32_t
|
||||
crc32(const void *buf, size_t size)
|
||||
{
|
||||
const uint8_t *p = buf;
|
||||
uint32_t crc;
|
||||
crc = ~0U;
|
||||
while (size--)
|
||||
crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);
|
||||
return crc ^ ~0U;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't you hate those contrived examples which CRC their data. We can use
|
||||
* FRIDA to patch this function out and always return success. Otherwise, we
|
||||
* could change it to actually correct the checksum.
|
||||
*/
|
||||
int crc32_check (char * buf, int len) {
|
||||
if (len < sizeof(uint32_t)) { return 0; }
|
||||
uint32_t expected = *(uint32_t *)&buf[len - sizeof(uint32_t)];
|
||||
uint32_t calculated = crc32(buf, len - sizeof(uint32_t));
|
||||
return expected == calculated;
|
||||
}
|
||||
|
||||
/*
|
||||
* So you've found a really boring bug in an earlier campaign which results in
|
||||
* a NULL dereference or something like that. That bug can get in the way,
|
||||
* causing the persistent loop to exit whenever it is triggered, and can also
|
||||
* cloud your output unnecessarily. Again, we can use FRIDA to patch it out.
|
||||
*/
|
||||
void some_boring_bug(char c) {
|
||||
switch (c) {
|
||||
case 'A'...'Z':
|
||||
case 'a'...'z':
|
||||
__builtin_trap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern void some_boring_bug2(char c);
|
||||
|
||||
__asm__ (
|
||||
".text \n"
|
||||
"some_boring_bug2: \n"
|
||||
".global some_boring_bug2 \n"
|
||||
".type some_boring_bug2, @function \n"
|
||||
"mov %edi, %eax \n"
|
||||
"cmp $0xb4, %al \n"
|
||||
"jne ok \n"
|
||||
"ud2 \n"
|
||||
"ok: \n"
|
||||
"ret \n");
|
||||
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
if (!crc32_check(buf, len)) return;
|
||||
|
||||
some_boring_bug(buf[0]);
|
||||
some_boring_bug2(buf[0]);
|
||||
|
||||
if (buf[0] == '0') {
|
||||
printf("Looks like a zero to me!\n");
|
||||
}
|
||||
else if (buf[0] == '1') {
|
||||
printf("Pretty sure that is a one!\n");
|
||||
}
|
||||
else if (buf[0] == '2') {
|
||||
printf("Oh we, weren't expecting that!");
|
||||
__builtin_trap();
|
||||
}
|
||||
else
|
||||
printf("Neither one or zero? How quaint!\n");
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
int fd = -1;
|
||||
off_t len;
|
||||
char * buf = NULL;
|
||||
size_t n_read;
|
||||
int result = -1;
|
||||
|
||||
if (argc != 2) { return 1; }
|
||||
|
||||
printf("Running: %s\n", argv[1]);
|
||||
|
||||
fd = open(argv[1], O_RDONLY);
|
||||
if (fd < 0) { return 1; }
|
||||
|
||||
len = lseek(fd, 0, SEEK_END);
|
||||
if (len < 0) { return 1; }
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) != 0) { return 1; }
|
||||
|
||||
buf = malloc(len);
|
||||
if (buf == NULL) { return 1; }
|
||||
|
||||
n_read = read(fd, buf, len);
|
||||
if (n_read != len) { return 1; }
|
||||
|
||||
printf("Running: %s: (%zd bytes)\n", argv[1], n_read);
|
||||
|
||||
LLVMFuzzerTestOneInput(buf, len);
|
||||
printf("Done: %s: (%zd bytes)\n", argv[1], n_read);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2,8 +2,7 @@ PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../..)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
|
||||
AFLPP_DRIVER_HOOK_SRC=$(PWD)aflpp_qemu_driver_hook.c
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)aflpp_qemu_driver_hook.so
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
|
||||
|
||||
LIBPCAP_BUILD_DIR:=$(BUILD_DIR)libpcap/
|
||||
HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
|
||||
@ -137,11 +136,6 @@ $(TEST_BIN): $(HARNESS_OBJ) $(PCAPTEST_OBJ) $(LIBPCAP_LIB)
|
||||
$(LDFLAGS) \
|
||||
$(TEST_BIN_LDFLAGS) \
|
||||
|
||||
########## HOOK ########
|
||||
|
||||
$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
|
||||
$(CC) -shared $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
########## DUMMY #######
|
||||
|
||||
$(AFLPP_DRIVER_DUMMY_INPUT): | $(TCPDUMP_TESTS_DIR)
|
||||
@ -149,8 +143,6 @@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(TCPDUMP_TESTS_DIR)
|
||||
|
||||
###### TEST DATA #######
|
||||
|
||||
hook: $(AFLPP_DRIVER_HOOK_OBJ)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
|
@ -1,97 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct x86_64_regs {
|
||||
|
||||
uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
|
||||
r15;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rip;
|
||||
uint64_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rsp;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rflags;
|
||||
uint64_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t zmm_regs[32][64];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
struct x86_regs {
|
||||
|
||||
uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eip;
|
||||
uint32_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t esp;
|
||||
uint32_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eflags;
|
||||
uint32_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t xmm_regs[8][16];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
void **esp = (void **)regs->esp;
|
||||
void * arg1 = esp[1];
|
||||
void **arg2 = &esp[2];
|
||||
memcpy(arg1, input_buf, input_buf_len);
|
||||
*arg2 = (void *)input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
int afl_persistent_hook_init(void) {
|
||||
|
||||
// 1 for shared memory input (faster), 0 for normal input (you have to use
|
||||
// read(), input_buf will be NULL)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ frida_js: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
-i $(TESTINSTR_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TESTINSTBIN) @@
|
||||
$(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
|
||||
debug: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
gdb \
|
||||
@ -102,6 +102,15 @@ debug: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
--ex 'set disassembly-flavor intel' \
|
||||
--args $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
|
||||
debug_js: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
gdb \
|
||||
--ex 'set environment AFL_FRIDA_JS_SCRIPT=test.js' \
|
||||
--ex 'set environment AFL_FRIDA_PERSISTENT_DEBUG=1' \
|
||||
--ex 'set environment AFL_DEBUG_CHILD=1' \
|
||||
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
|
||||
--ex 'set disassembly-flavor intel' \
|
||||
--args $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
|
||||
run: $(TESTINSTBIN) $(TESTINSTR_DATA_FILE)
|
||||
AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
|
||||
AFL_FRIDA_PERSISTENT_RET=$(AFL_FRIDA_PERSISTENT_RET) \
|
||||
|
@ -5,34 +5,44 @@ 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 persistent_addr = DebugSymbol.fromName('main');
|
||||
Afl.print(`persistent_addr: ${persistent_addr.address}`);
|
||||
if (name === 'testinstr') {
|
||||
const persistent_addr = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
|
||||
Afl.print(`persistent_addr: ${persistent_addr}`);
|
||||
Afl.setEntryPoint(persistent_addr);
|
||||
Afl.setPersistentAddress(persistent_addr);
|
||||
Afl.setInstrumentDebugFile("/dev/stdout");
|
||||
Afl.setPersistentDebug();
|
||||
Afl.setInstrumentNoOptimize();
|
||||
Afl.setInstrumentEnableTracing();
|
||||
|
||||
const persistent_ret = DebugSymbol.fromName('slow');
|
||||
Afl.print(`persistent_ret: ${persistent_ret.address}`);
|
||||
const LLVMFuzzerTestOneInput = new NativeFunction(
|
||||
persistent_addr,
|
||||
'void',
|
||||
['pointer', 'uint64'],
|
||||
{traps: "all"});
|
||||
|
||||
Afl.setPersistentAddress(persistent_addr.address);
|
||||
Afl.setPersistentReturn(persistent_ret.address);
|
||||
Afl.setPersistentCount(1000000);
|
||||
const persistentHook = new NativeCallback(
|
||||
(data, size) => {
|
||||
const input = Afl.aflFuzzPtr.readPointer();
|
||||
const len = Afl.aflFuzzLen.readPointer().readU32();
|
||||
const hd = hexdump(input, {length: len, header: false, ansi: true});
|
||||
Afl.print(`input: ${hd}`);
|
||||
LLVMFuzzerTestOneInput(input, len);
|
||||
},
|
||||
'void',
|
||||
['pointer', 'uint64']);
|
||||
|
||||
Afl.setDebugMaps();
|
||||
Afl.aflSharedMemFuzzing.writeInt(1);
|
||||
Interceptor.replace(persistent_addr, persistentHook);
|
||||
Interceptor.flush();
|
||||
}
|
||||
|
||||
const mod = Process.findModuleByName("libc-2.31.so")
|
||||
Afl.addExcludedRange(mod.base, mod.size);
|
||||
Afl.setInstrumentLibraries();
|
||||
Afl.setInstrumentDebugFile("/tmp/instr.log");
|
||||
Afl.setPrefetchDisable();
|
||||
Afl.setInstrumentNoOptimize();
|
||||
Afl.setInstrumentEnableTracing();
|
||||
Afl.setInstrumentTracingUnique();
|
||||
Afl.setStdOut("/tmp/stdout.txt");
|
||||
Afl.setStdErr("/tmp/stderr.txt");
|
||||
Afl.setStatsFile("/tmp/stats.txt");
|
||||
Afl.setStatsInterval(1);
|
||||
Afl.setStatsTransitions();
|
||||
Afl.done();
|
||||
Afl.print("done");
|
||||
Afl.done();
|
||||
|
@ -17,13 +17,14 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define TESTINSTR_SECTION
|
||||
#define MAIN_SECTION
|
||||
#else
|
||||
#define TESTINSTR_SECTION __attribute__((section(".testinstr")))
|
||||
#define MAIN_SECTION __attribute__((section(".main")))
|
||||
#endif
|
||||
|
||||
void testinstr(char *buf, int len) {
|
||||
void LLVMFuzzerTestOneInput(char *buf, int len) {
|
||||
|
||||
printf (">>> LLVMFuzzerTestOneInput >>>\n");
|
||||
if (len < 1) return;
|
||||
buf[len] = 0;
|
||||
|
||||
@ -43,7 +44,7 @@ void slow() {
|
||||
|
||||
}
|
||||
|
||||
TESTINSTR_SECTION int main(int argc, char **argv) {
|
||||
MAIN_SECTION int main(int argc, char **argv) {
|
||||
|
||||
char * file;
|
||||
int fd = -1;
|
||||
@ -101,7 +102,7 @@ TESTINSTR_SECTION int main(int argc, char **argv) {
|
||||
|
||||
dprintf(STDERR_FILENO, "Running: %s: (%zd bytes)\n", file, n_read);
|
||||
|
||||
testinstr(buf, len);
|
||||
LLVMFuzzerTestOneInput(buf, len);
|
||||
dprintf(STDERR_FILENO, "Done: %s: (%zd bytes)\n", file, n_read);
|
||||
|
||||
slow();
|
||||
|
@ -2,8 +2,7 @@ PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../../../..)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
|
||||
AFLPP_DRIVER_HOOK_SRC=$(PWD)aflpp_qemu_driver_hook.c
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)aflpp_qemu_driver_hook.so
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
|
||||
|
||||
CFLAGS+=-O3 \
|
||||
-funroll-loops \
|
||||
@ -48,7 +47,7 @@ endif
|
||||
|
||||
.PHONY: all 32 clean format qemu qemu_entry frida frida_entry debug
|
||||
|
||||
all: $(AFLPP_DRIVER_HOOK_OBJ)
|
||||
all:
|
||||
make -C $(ROOT)frida_mode/test/png/persistent/
|
||||
|
||||
32:
|
||||
@ -68,9 +67,6 @@ $(TEST_DATA_DIR): | $(BUILD_DIR)
|
||||
$(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR)
|
||||
truncate -s 1M $@
|
||||
|
||||
$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
qemu: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
|
||||
AFL_QEMU_PERSISTENT_HOOK=$(AFLPP_DRIVER_HOOK_OBJ) \
|
||||
AFL_QEMU_PERSISTENT_ADDR=$(AFL_QEMU_PERSISTENT_ADDR) \
|
||||
@ -124,6 +120,28 @@ frida_entry: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
|
||||
-- \
|
||||
$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
|
||||
|
||||
frida_js_load: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
|
||||
AFL_FRIDA_JS_SCRIPT=load.js \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-V 30 \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
|
||||
|
||||
frida_js_cmodule: $(AFLPP_DRIVER_DUMMY_INPUT) $(AFLPP_DRIVER_HOOK_OBJ) | $(BUILD_DIR)
|
||||
AFL_FRIDA_JS_SCRIPT=cmodule.js \
|
||||
$(ROOT)afl-fuzz \
|
||||
-D \
|
||||
-V 30 \
|
||||
-O \
|
||||
-i $(TEST_DATA_DIR) \
|
||||
-o $(FRIDA_OUT) \
|
||||
-- \
|
||||
$(TEST_BIN) $(AFLPP_DRIVER_DUMMY_INPUT)
|
||||
|
||||
debug: $(AFLPP_DRIVER_DUMMY_INPUT)
|
||||
echo $(AFL_FRIDA_PERSISTENT_ADDR)
|
||||
gdb \
|
||||
|
@ -24,5 +24,8 @@ frida:
|
||||
frida_entry:
|
||||
@gmake frida_entry
|
||||
|
||||
frida_js:
|
||||
@gmake frida_js
|
||||
|
||||
debug:
|
||||
@gmake debug
|
||||
|
@ -1,193 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct x86_64_regs {
|
||||
|
||||
uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
|
||||
r15;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rip;
|
||||
uint64_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rsp;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rflags;
|
||||
uint64_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t zmm_regs[32][64];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
struct x86_regs {
|
||||
|
||||
uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eip;
|
||||
uint32_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t esp;
|
||||
uint32_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eflags;
|
||||
uint32_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t xmm_regs[8][16];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
void **esp = (void **)regs->esp;
|
||||
void * arg1 = esp[1];
|
||||
void **arg2 = &esp[2];
|
||||
memcpy(arg1, input_buf, input_buf_len);
|
||||
*arg2 = (void *)input_buf_len;
|
||||
|
||||
}
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
struct arm64_regs {
|
||||
|
||||
uint64_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x11;
|
||||
uint32_t fp_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x12;
|
||||
uint32_t ip_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x13;
|
||||
uint32_t sp_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x14;
|
||||
uint32_t lr_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x15;
|
||||
uint32_t pc_32;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x16;
|
||||
uint64_t ip0;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x17;
|
||||
uint64_t ip1;
|
||||
|
||||
};
|
||||
|
||||
uint64_t x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x29;
|
||||
uint64_t fp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x30;
|
||||
uint64_t lr;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t x31;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
// the zero register is not saved here ofc
|
||||
|
||||
uint64_t pc;
|
||||
|
||||
uint32_t cpsr;
|
||||
|
||||
uint8_t vfp_zregs[32][16 * 16];
|
||||
uint8_t vfp_pregs[17][32];
|
||||
uint32_t vfp_xregs[16];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct arm64_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->x0, input_buf, input_buf_len);
|
||||
regs->x1 = input_buf_len;
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
int afl_persistent_hook_init(void) {
|
||||
|
||||
// 1 for shared memory input (faster), 0 for normal input (you have to use
|
||||
// read(), input_buf will be NULL)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
39
frida_mode/test/png/persistent/hook/cmodule.js
Normal file
39
frida_mode/test/png/persistent/hook/cmodule.js
Normal file
@ -0,0 +1,39 @@
|
||||
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 persistent_addr = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
|
||||
Afl.print(`persistent_addr: ${persistent_addr}`);
|
||||
Afl.setEntryPoint(persistent_addr);
|
||||
Afl.setPersistentAddress(persistent_addr);
|
||||
|
||||
const cm = new CModule(`
|
||||
|
||||
#include <string.h>
|
||||
#include <gum/gumdefs.h>
|
||||
|
||||
void afl_persistent_hook(GumCpuContext *regs, uint8_t *input_buf,
|
||||
uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
`,
|
||||
{
|
||||
memcpy: Module.getExportByName(null, 'memcpy')
|
||||
});
|
||||
Afl.setPersistentHook(cm.afl_persistent_hook);
|
||||
|
||||
Afl.print("done");
|
||||
Afl.done();
|
27
frida_mode/test/png/persistent/hook/load.js
Normal file
27
frida_mode/test/png/persistent/hook/load.js
Normal file
@ -0,0 +1,27 @@
|
||||
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 persistent_addr = DebugSymbol.fromName('LLVMFuzzerTestOneInput').address;
|
||||
Afl.print(`persistent_addr: ${persistent_addr}`);
|
||||
Afl.setEntryPoint(persistent_addr);
|
||||
Afl.setPersistentAddress(persistent_addr);
|
||||
|
||||
const path = Afl.module.path;
|
||||
const dir = path.substring(0, path.lastIndexOf("/"));
|
||||
const mod = Module.load(`${dir}/frida_mode/build/hook.so`);
|
||||
const hook = mod.getExportByName('afl_persistent_hook');
|
||||
Afl.setPersistentHook(hook);
|
||||
|
||||
Afl.print("done");
|
||||
Afl.done();
|
@ -2,8 +2,7 @@ PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../..)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
|
||||
AFLPP_DRIVER_HOOK_SRC=$(PWD)aflpp_qemu_driver_hook.c
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)aflpp_qemu_driver_hook.so
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
|
||||
|
||||
LIBPROJ4_BUILD_DIR:=$(BUILD_DIR)libproj4/
|
||||
HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
|
||||
@ -118,11 +117,6 @@ $(TEST_BIN): $(HARNESS_OBJ) $(PROJ4TEST_OBJ) $(LIBPROJ4_LIB)
|
||||
$(LDFLAGS) \
|
||||
$(TEST_BIN_LDFLAGS) \
|
||||
|
||||
########## HOOK ########
|
||||
|
||||
$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
|
||||
$(CC) -shared $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
########## DUMMY #######
|
||||
|
||||
$(TEST_DATA_DIR): | $(BUILD_DIR)
|
||||
@ -133,8 +127,6 @@ $(TEST_DATA_FILE): | $(TEST_DATA_DIR)
|
||||
|
||||
###### TEST DATA #######
|
||||
|
||||
hook: $(AFLPP_DRIVER_HOOK_OBJ)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
|
@ -15,5 +15,3 @@ frida:
|
||||
debug:
|
||||
@gmake debug
|
||||
|
||||
hook:
|
||||
@gmake hook
|
||||
|
@ -1,97 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct x86_64_regs {
|
||||
|
||||
uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
|
||||
r15;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rip;
|
||||
uint64_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rsp;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rflags;
|
||||
uint64_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t zmm_regs[32][64];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
struct x86_regs {
|
||||
|
||||
uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eip;
|
||||
uint32_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t esp;
|
||||
uint32_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eflags;
|
||||
uint32_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t xmm_regs[8][16];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
void **esp = (void **)regs->esp;
|
||||
void * arg1 = esp[1];
|
||||
void **arg2 = &esp[2];
|
||||
memcpy(arg1, input_buf, input_buf_len);
|
||||
*arg2 = (void *)input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
int afl_persistent_hook_init(void) {
|
||||
|
||||
// 1 for shared memory input (faster), 0 for normal input (you have to use
|
||||
// read(), input_buf will be NULL)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
@ -2,8 +2,7 @@ PWD:=$(shell pwd)/
|
||||
ROOT:=$(shell realpath $(PWD)../../..)/
|
||||
BUILD_DIR:=$(PWD)build/
|
||||
|
||||
AFLPP_DRIVER_HOOK_SRC=$(PWD)aflpp_qemu_driver_hook.c
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(BUILD_DIR)aflpp_qemu_driver_hook.so
|
||||
AFLPP_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/hook.so
|
||||
|
||||
LIBRE2_BUILD_DIR:=$(BUILD_DIR)libre2/
|
||||
HARNESS_BUILD_DIR:=$(BUILD_DIR)harness/
|
||||
@ -116,11 +115,6 @@ $(TEST_BIN): $(HARNESS_OBJ) $(RE2TEST_OBJ) $(LIBRE2_LIB)
|
||||
$(LDFLAGS) \
|
||||
$(TEST_BIN_LDFLAGS) \
|
||||
|
||||
########## HOOK ########
|
||||
|
||||
$(AFLPP_DRIVER_HOOK_OBJ): $(AFLPP_DRIVER_HOOK_SRC) | $(BUILD_DIR)
|
||||
$(CC) -shared $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||
|
||||
########## DUMMY #######
|
||||
|
||||
$(TEST_DATA_DIR): | $(BUILD_DIR)
|
||||
@ -131,8 +125,6 @@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(TEST_DATA_DIR)
|
||||
|
||||
###### TEST DATA #######
|
||||
|
||||
hook: $(AFLPP_DRIVER_HOOK_OBJ)
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILD_DIR)
|
||||
|
||||
|
@ -18,5 +18,3 @@ frida:
|
||||
debug:
|
||||
@gmake debug
|
||||
|
||||
hook:
|
||||
@gmake hook
|
||||
|
@ -1,97 +0,0 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct x86_64_regs {
|
||||
|
||||
uint64_t rax, rbx, rcx, rdx, rdi, rsi, rbp, r8, r9, r10, r11, r12, r13, r14,
|
||||
r15;
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rip;
|
||||
uint64_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rsp;
|
||||
uint64_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint64_t rflags;
|
||||
uint64_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t zmm_regs[32][64];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_64_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
memcpy((void *)regs->rdi, input_buf, input_buf_len);
|
||||
regs->rsi = input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#elif defined(__i386__)
|
||||
|
||||
struct x86_regs {
|
||||
|
||||
uint32_t eax, ebx, ecx, edx, edi, esi, ebp;
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eip;
|
||||
uint32_t pc;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t esp;
|
||||
uint32_t sp;
|
||||
|
||||
};
|
||||
|
||||
union {
|
||||
|
||||
uint32_t eflags;
|
||||
uint32_t flags;
|
||||
|
||||
};
|
||||
|
||||
uint8_t xmm_regs[8][16];
|
||||
|
||||
};
|
||||
|
||||
void afl_persistent_hook(struct x86_regs *regs, uint64_t guest_base,
|
||||
uint8_t *input_buf, uint32_t input_buf_len) {
|
||||
|
||||
void **esp = (void **)regs->esp;
|
||||
void * arg1 = esp[1];
|
||||
void **arg2 = &esp[2];
|
||||
memcpy(arg1, input_buf, input_buf_len);
|
||||
*arg2 = (void *)input_buf_len;
|
||||
|
||||
}
|
||||
|
||||
#else
|
||||
#pragma error "Unsupported architecture"
|
||||
#endif
|
||||
|
||||
int afl_persistent_hook_init(void) {
|
||||
|
||||
// 1 for shared memory input (faster), 0 for normal input (you have to use
|
||||
// read(), input_buf will be NULL)
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
373
frida_mode/ts/lib/afl.ts
Normal file
373
frida_mode/ts/lib/afl.ts
Normal file
@ -0,0 +1,373 @@
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static error(msg: string): void {
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_DEBUG_MAPS`.
|
||||
*/
|
||||
public static setDebugMaps(): void {
|
||||
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.
|
||||
*/
|
||||
public static setEntryPoint(address: NativePointer): void {
|
||||
Afl.jsApiSetEntryPoint(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to enable in-memory test cases for fuzzing.
|
||||
*/
|
||||
public static setInMemoryFuzzing(): void {
|
||||
Afl.jsApiAflSharedMemFuzzing.writeInt(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE`.
|
||||
*/
|
||||
public static setInstrumentEnableTracing(): void {
|
||||
Afl.jsApiSetInstrumentTrace();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_INST_LIBS`.
|
||||
*/
|
||||
public static setInstrumentLibraries(): void {
|
||||
Afl.jsApiSetInstrumentLibraries();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_OPTIMIZE`
|
||||
*/
|
||||
public static setInstrumentNoOptimize(): void {
|
||||
Afl.jsApiSetInstrumentNoOptimize();
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_TRACE_UNIQUE`.
|
||||
*/
|
||||
public static setInstrumentTracingUnique(): void {
|
||||
Afl.jsApiSetInstrumentTraceUnique();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_PERSISTENT_DEBUG`.
|
||||
*/
|
||||
public static setPersistentDebug(): void {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_INST_NO_PREFETCH`.
|
||||
*/
|
||||
public static setPrefetchDisable(): void {
|
||||
Afl.jsApiSetPrefetchDisable();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a function to be called for each instruction which is instrumented
|
||||
* by AFL FRIDA mode.
|
||||
*/
|
||||
public static setStalkerCallback(callback: NativePointer): void {
|
||||
Afl.jsApiSetStalkerCallback(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_INTERVAL`. This function takes a `number` as an
|
||||
* argument
|
||||
*/
|
||||
public static setStatsInterval(interval: number): void {
|
||||
Afl.jsApiSetStatsInterval(interval);
|
||||
}
|
||||
|
||||
/**
|
||||
* See `AFL_FRIDA_STATS_TRANSITIONS`
|
||||
*/
|
||||
public static setStatsTransitions(): void {
|
||||
Afl.jsApiSetStatsTransitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
private static readonly jsApiAddExcludeRange = Afl.jsApiGetFunction(
|
||||
"js_api_add_exclude_range",
|
||||
"void",
|
||||
["pointer", "size_t"]);
|
||||
|
||||
private static readonly jsApiAddIncludeRange = Afl.jsApiGetFunction(
|
||||
"js_api_add_include_range",
|
||||
"void",
|
||||
["pointer", "size_t"]);
|
||||
|
||||
private static readonly jsApiAflSharedMemFuzzing = Afl.jsApiGetSymbol("__afl_sharedmem_fuzzing");
|
||||
|
||||
private static readonly jsApiDone = Afl.jsApiGetFunction(
|
||||
"js_api_done",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiError = Afl.jsApiGetFunction(
|
||||
"js_api_error",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetDebugMaps = Afl.jsApiGetFunction(
|
||||
"js_api_set_debug_maps",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetEntryPoint = Afl.jsApiGetFunction(
|
||||
"js_api_set_entrypoint",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction(
|
||||
"js_api_set_instrument_debug_file",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetInstrumentLibraries = Afl.jsApiGetFunction(
|
||||
"js_api_set_instrument_libraries",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetInstrumentNoOptimize = Afl.jsApiGetFunction(
|
||||
"js_api_set_instrument_no_optimize",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetInstrumentTrace = Afl.jsApiGetFunction(
|
||||
"js_api_set_instrument_trace",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetInstrumentTraceUnique = Afl.jsApiGetFunction(
|
||||
"js_api_set_instrument_trace_unique",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetPersistentAddress = Afl.jsApiGetFunction(
|
||||
"js_api_set_persistent_address",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetPersistentCount = Afl.jsApiGetFunction(
|
||||
"js_api_set_persistent_count",
|
||||
"void",
|
||||
["uint64"]);
|
||||
|
||||
private static readonly jsApiSetPersistentDebug = Afl.jsApiGetFunction(
|
||||
"js_api_set_persistent_debug",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetPersistentHook = Afl.jsApiGetFunction(
|
||||
"js_api_set_persistent_hook",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetPersistentReturn = Afl.jsApiGetFunction(
|
||||
"js_api_set_persistent_return",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetPrefetchDisable = Afl.jsApiGetFunction(
|
||||
"js_api_set_prefetch_disable",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetStalkerCallback = Afl.jsApiGetFunction(
|
||||
"js_api_set_stalker_callback",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetStatsFile = Afl.jsApiGetFunction(
|
||||
"js_api_set_stats_file",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetStatsInterval = Afl.jsApiGetFunction(
|
||||
"js_api_set_stats_interval",
|
||||
"void",
|
||||
["uint64"]);
|
||||
|
||||
private static readonly jsApiSetStatsTransitions = Afl.jsApiGetFunction(
|
||||
"js_api_set_stats_transitions",
|
||||
"void",
|
||||
[]);
|
||||
|
||||
private static readonly jsApiSetStdErr = Afl.jsApiGetFunction(
|
||||
"js_api_set_stderr",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiSetStdOut = Afl.jsApiGetFunction(
|
||||
"js_api_set_stdout",
|
||||
"void",
|
||||
["pointer"]);
|
||||
|
||||
private static readonly jsApiWrite = new NativeFunction(
|
||||
/* tslint:disable-next-line:no-null-keyword */
|
||||
Module.getExportByName(null, "write"),
|
||||
"int",
|
||||
["int", "pointer", "int"]);
|
||||
|
||||
private static jsApiGetFunction(name: string, retType: NativeType, argTypes: NativeType[]): NativeFunction {
|
||||
const addr: NativePointer = Afl.module.getExportByName(name);
|
||||
|
||||
return new NativeFunction(addr, retType, argTypes);
|
||||
}
|
||||
|
||||
private static jsApiGetSymbol(name: string): NativePointer {
|
||||
|
||||
return Afl.module.getExportByName(name);
|
||||
}
|
||||
|
||||
}
|
12
frida_mode/ts/package-lock.json
generated
Normal file
12
frida_mode/ts/package-lock.json
generated
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"tsc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.3.tgz",
|
||||
"integrity": "sha512-SN+9zBUtrpUcOpaUO7GjkEHgWtf22c7FKbKCA4e858eEM7Qz86rRDpgOU2lBIDf0fLCsEg65ms899UMUIB2+Ow==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
32
frida_mode/ts/package.json
Normal file
32
frida_mode/ts/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@worksbutnottested/aflplusplus-frida",
|
||||
"version": "1.0.0",
|
||||
"description": "AFLplusplus Frida Mode",
|
||||
"main": "./dist/frida.js",
|
||||
"types": "./dist/frida.d.ts",
|
||||
"files": [
|
||||
"/dist/"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:worksbutnottested/AFLplusplus.git"
|
||||
},
|
||||
"publishConfig": {
|
||||
"cache": "~/.npm",
|
||||
"registry": "https://npm.pkg.github.com/@worksbutnottested"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "npm run build",
|
||||
"build": "tsc",
|
||||
"lint": "tslint -p tslint.json"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.14.2",
|
||||
"typescript": "^4.0.3",
|
||||
"typescript-tslint-plugin": "^0.5.5",
|
||||
"tslint": "^6.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/frida-gum": "^16.2.0"
|
||||
}
|
||||
}
|
14
frida_mode/ts/tsconfig.json
Normal file
14
frida_mode/ts/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"lib": ["es2020"],
|
||||
"strict": true,
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"lib/**/*"
|
||||
]
|
||||
}
|
256
frida_mode/ts/tslint.json
Normal file
256
frida_mode/ts/tslint.json
Normal file
@ -0,0 +1,256 @@
|
||||
{
|
||||
"rules": {
|
||||
"adjacent-overload-signatures": true,
|
||||
"ban-types": {
|
||||
"options": [
|
||||
["Object", "Avoid using the `Object` type. Did you mean `object`?"],
|
||||
[
|
||||
"Function",
|
||||
"Avoid using the `Function` type. Prefer a specific function type, like `() => void`."
|
||||
],
|
||||
["Boolean", "Avoid using the `Boolean` type. Did you mean `boolean`?"],
|
||||
["Number", "Avoid using the `Number` type. Did you mean `number`?"],
|
||||
["String", "Avoid using the `String` type. Did you mean `string`?"],
|
||||
["Symbol", "Avoid using the `Symbol` type. Did you mean `symbol`?"]
|
||||
]
|
||||
},
|
||||
"ban-ts-ignore": true,
|
||||
"member-access": {
|
||||
"options": ["check-accessor", "check-constructor", "check-parameter-property"]
|
||||
},
|
||||
"member-ordering": {
|
||||
"options": {
|
||||
"order": "statics-first",
|
||||
"alphabetize": true
|
||||
}
|
||||
},
|
||||
"no-any": true,
|
||||
"no-empty-interface": true,
|
||||
"no-for-in": true,
|
||||
"no-import-side-effect": true,
|
||||
"no-inferrable-types": { "options": ["ignore-params"] },
|
||||
"no-internal-module": true,
|
||||
"no-magic-numbers": true,
|
||||
"no-namespace": true,
|
||||
"no-non-null-assertion": true,
|
||||
"no-reference": true,
|
||||
"no-restricted-globals": true,
|
||||
"no-this-assignment": true,
|
||||
"no-var-requires": true,
|
||||
"only-arrow-functions": true,
|
||||
"prefer-for-of": true,
|
||||
"prefer-readonly": true,
|
||||
"promise-function-async": true,
|
||||
"typedef": {
|
||||
"options": [
|
||||
"call-signature",
|
||||
"parameter",
|
||||
"property-declaration"
|
||||
]
|
||||
},
|
||||
"typedef-whitespace": {
|
||||
"options": [
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
]
|
||||
},
|
||||
"unified-signatures": true,
|
||||
"await-promise": true,
|
||||
"ban-comma-operator": true,
|
||||
"curly": true,
|
||||
"forin": true,
|
||||
"function-constructor": true,
|
||||
"label-position": true,
|
||||
"no-arg": true,
|
||||
"no-async-without-await": true,
|
||||
"no-bitwise": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-console": true,
|
||||
"no-construct": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-super": true,
|
||||
"no-duplicate-switch-case": true,
|
||||
"no-duplicate-variable": { "options": ["check-parameters"] },
|
||||
"no-dynamic-delete": true,
|
||||
"no-empty": true,
|
||||
"no-eval": true,
|
||||
"no-floating-promises": true,
|
||||
"no-for-in-array": true,
|
||||
"no-implicit-dependencies": true,
|
||||
"no-inferred-empty-object-type": true,
|
||||
"no-invalid-template-strings": true,
|
||||
"no-misused-new": true,
|
||||
"no-null-keyword": true,
|
||||
"no-null-undefined-union": true,
|
||||
"no-object-literal-type-assertion": true,
|
||||
"no-promise-as-boolean": true,
|
||||
"no-return-await": true,
|
||||
"no-shadowed-variable": true,
|
||||
"no-string-literal": true,
|
||||
"no-string-throw": true,
|
||||
"no-sparse-arrays": true,
|
||||
"no-submodule-imports": true,
|
||||
"no-tautology-expression": true,
|
||||
"no-unbound-method": true,
|
||||
"no-unnecessary-class": { "options": ["allow-empty-class", "allow-static-only"] },
|
||||
"no-unsafe-any": false,
|
||||
"no-unsafe-finally": true,
|
||||
"no-unused-expression": true,
|
||||
"no-var-keyword": true,
|
||||
"no-void-expression": true,
|
||||
"prefer-conditional-expression": true,
|
||||
"radix": true,
|
||||
"restrict-plus-operands": true,
|
||||
"static-this": true,
|
||||
"strict-boolean-expressions": true,
|
||||
"strict-string-expressions": true,
|
||||
"strict-comparisons": true,
|
||||
"strict-type-predicates": true,
|
||||
"switch-default": true,
|
||||
"triple-equals": true,
|
||||
"unnecessary-constructor": true,
|
||||
"use-default-type-parameter": true,
|
||||
"use-isnan": true,
|
||||
"cyclomatic-complexity": true,
|
||||
"eofline": true,
|
||||
"indent": { "options": ["spaces"] },
|
||||
"invalid-void": true,
|
||||
"linebreak-style": { "options": "LF" },
|
||||
"max-classes-per-file": { "options": 1 },
|
||||
"max-file-line-count": { "options": 1000 },
|
||||
"max-line-length": {
|
||||
"options": { "limit": 120 }
|
||||
},
|
||||
"no-default-export": true,
|
||||
"no-default-import": true,
|
||||
"no-duplicate-imports": true,
|
||||
"no-irregular-whitespace": true,
|
||||
"no-mergeable-namespace": true,
|
||||
"no-parameter-reassignment": true,
|
||||
"no-require-imports": true,
|
||||
"no-trailing-whitespace": true,
|
||||
"object-literal-sort-keys": true,
|
||||
"prefer-const": true,
|
||||
"trailing-comma": {
|
||||
"options": {
|
||||
"esSpecCompliant": true,
|
||||
"multiline": "always",
|
||||
"singleline": "never"
|
||||
}
|
||||
},
|
||||
"align": {
|
||||
"options": ["parameters", "arguments", "statements", "elements", "members"]
|
||||
},
|
||||
"array-type": { "options": "array-simple" },
|
||||
"arrow-parens": true,
|
||||
"arrow-return-shorthand": { "options": "multiline" },
|
||||
"binary-expression-operand-order": true,
|
||||
"callable-types": true,
|
||||
"class-name": true,
|
||||
"comment-format": { "options": ["check-space", "check-uppercase"] },
|
||||
"comment-type": { "options": ["singleline", "multiline", "doc", "directive"] },
|
||||
"completed-docs": [
|
||||
true,
|
||||
{
|
||||
"enums": true,
|
||||
"methods": {"locations": "all", "privacies": ["public", "protected"]},
|
||||
"properties": {"locations": "all", "privacies": ["public", "protected"]}
|
||||
}
|
||||
],
|
||||
"deprecation": true,
|
||||
"encoding": true,
|
||||
"file-name-casing": { "options": "camel-case" },
|
||||
"import-spacing": true,
|
||||
"increment-decrement": true,
|
||||
"interface-name": true,
|
||||
"interface-over-type-literal": true,
|
||||
"jsdoc-format": { "options": "check-multiline-start" },
|
||||
"match-default-export-name": true,
|
||||
"new-parens": true,
|
||||
"newline-before-return": true,
|
||||
"newline-per-chained-call": true,
|
||||
"no-angle-bracket-type-assertion": true,
|
||||
"no-boolean-literal-compare": true,
|
||||
"no-consecutive-blank-lines": true,
|
||||
"no-parameter-properties": true,
|
||||
"no-redundant-jsdoc": true,
|
||||
"no-reference-import": true,
|
||||
"no-unnecessary-callback-wrapper": true,
|
||||
"no-unnecessary-initializer": true,
|
||||
"no-unnecessary-qualifier": true,
|
||||
"no-unnecessary-type-assertion": true,
|
||||
"number-literal-format": true,
|
||||
"object-literal-key-quotes": { "options": "consistent-as-needed" },
|
||||
"object-literal-shorthand": true,
|
||||
"one-line": {
|
||||
"options": [
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-finally",
|
||||
"check-open-brace",
|
||||
"check-whitespace"
|
||||
]
|
||||
},
|
||||
"one-variable-per-declaration": true,
|
||||
"ordered-imports": {
|
||||
"options": {
|
||||
"grouped-imports": true,
|
||||
"import-sources-order": "case-insensitive",
|
||||
"named-imports-order": "case-insensitive",
|
||||
"module-source-path": "full"
|
||||
}
|
||||
},
|
||||
"prefer-function-over-method": true,
|
||||
"prefer-method-signature": true,
|
||||
"prefer-object-spread": true,
|
||||
"prefer-switch": true,
|
||||
"prefer-template": true,
|
||||
"prefer-while": true,
|
||||
"quotemark": {
|
||||
"options": ["double", "avoid-escape", "avoid-template"]
|
||||
},
|
||||
"return-undefined": true,
|
||||
"semicolon": { "options": ["always"] },
|
||||
"space-before-function-paren": {
|
||||
"options": {
|
||||
"anonymous": "never",
|
||||
"asyncArrow": "always",
|
||||
"constructor": "never",
|
||||
"method": "never",
|
||||
"named": "never"
|
||||
}
|
||||
},
|
||||
"space-within-parens": { "options": 0 },
|
||||
"switch-final-break": true,
|
||||
"type-literal-delimiter": true,
|
||||
"unnecessary-bind": true,
|
||||
"unnecessary-else": true,
|
||||
"variable-name": { "options": ["ban-keywords", "check-format", "require-const-for-all-caps"] },
|
||||
"whitespace": {
|
||||
"options": [
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-module",
|
||||
"check-separator",
|
||||
"check-type",
|
||||
"check-typecast",
|
||||
"check-preblock",
|
||||
"check-type-operator",
|
||||
"check-rest-spread"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user