Add global caching of block translation to instrumentation

This commit is contained in:
Your Name
2022-02-18 17:39:21 +00:00
parent a5943dc782
commit 852c036cae
18 changed files with 846 additions and 19 deletions

View File

@ -151,25 +151,25 @@ instances run CMPLOG mode and instrumentation of the binary is less frequent
* `AFL_FRIDA_INST_DEBUG_FILE` - File to write raw assembly of original blocks
and their instrumented counterparts during block compilation.
```
***
Creating block for 0x7ffff7953313:
0x7ffff7953313 mov qword ptr [rax], 0
0x7ffff795331a add rsp, 8
0x7ffff795331e ret
Creating block for 0x7ffff7953313:
0x7ffff7953313 mov qword ptr [rax], 0
0x7ffff795331a add rsp, 8
0x7ffff795331e ret
Generated block 0x7ffff75e98e2
0x7ffff75e98e2 mov qword ptr [rax], 0
0x7ffff75e98e9 add rsp, 8
0x7ffff75e98ed lea rsp, [rsp - 0x80]
0x7ffff75e98f5 push rcx
0x7ffff75e98f6 movabs rcx, 0x7ffff795331e
0x7ffff75e9900 jmp 0x7ffff75e9384
Generated block 0x7ffff75e98e2
0x7ffff75e98e2 mov qword ptr [rax], 0
0x7ffff75e98e9 add rsp, 8
0x7ffff75e98ed lea rsp, [rsp - 0x80]
0x7ffff75e98f5 push rcx
0x7ffff75e98f6 movabs rcx, 0x7ffff795331e
0x7ffff75e9900 jmp 0x7ffff75e9384
***
```
* `AFL_FRIDA_INST_CACHE_SIZE` - Set the size of the instrumentation cache used
as a look-up table to cache real to instrumented address block translations.
Default is 256Mb.
* `AFL_FRIDA_INST_INSN` - Generate instrumentation for conditional
instructions (e.g. `CMOV` instructions on x64).
* `AFL_FRIDA_INST_JIT` - Enable the instrumentation of Just-In-Time compiled
@ -178,6 +178,8 @@ instances run CMPLOG mode and instrumentation of the binary is less frequent
* `AFL_FRIDA_INST_NO_OPTIMIZE` - Don't use optimized inline assembly coverage
instrumentation (the default where available). Required to use
`AFL_FRIDA_INST_TRACE`.
* `AFL_FRIDA_INST_NO_CACHE` - Don't use a look-up table to cache real to
instrumented address block translations.
* `AFL_FRIDA_INST_NO_PREFETCH` - Disable prefetching. By default, the child will
report instrumented blocks back to the parent so that it can also instrument
them and they be inherited by the next child on fork, implies

View File

@ -9,8 +9,10 @@
js_api_done;
js_api_error;
js_api_set_backpatch_disable;
js_api_set_cache_disable;
js_api_set_debug_maps;
js_api_set_entrypoint;
js_api_set_instrument_cache_size;
js_api_set_instrument_coverage_file;
js_api_set_instrument_debug_file;
js_api_set_instrument_jit;

View File

@ -22,6 +22,9 @@ extern uint32_t __afl_map_size;
extern __thread guint64 *instrument_previous_pc_addr;
extern gboolean instrument_cache_enabled;
extern gsize instrument_cache_size;
void instrument_config(void);
void instrument_init(void);
@ -59,5 +62,10 @@ void instrument_on_fork(void);
guint64 instrument_get_offset_hash(GumAddress current_rip);
void instrument_cache_config(void);
void instrument_cache_init(void);
void instrument_cache_insert(gpointer real_address, gpointer code_address);
void instrument_cache(const cs_insn *instr, GumStalkerOutput *output);
#endif

View File

@ -5,6 +5,14 @@
#include "debug.h"
#ifndef MAP_FIXED_NOREPLACE
#ifdef MAP_EXCL
#define MAP_FIXED_NOREPLACE MAP_EXCL | MAP_FIXED
#else
#define MAP_FIXED_NOREPLACE MAP_FIXED
#endif
#endif
#define UNUSED_PARAMETER(x) (void)(x)
#define IGNORED_RETURN(x) (void)!(x)

View File

@ -249,6 +249,8 @@ static void instrument_basic_block(GumStalkerIterator *iterator,
}
instrument_cache(instr, output);
if (js_stalker_callback(instr, begin, excluded, output)) {
gum_stalker_iterator_keep(iterator);
@ -282,6 +284,7 @@ void instrument_config(void) {
instrument_coverage_config();
asan_config();
cmplog_config();
instrument_cache_config();
}
@ -392,6 +395,7 @@ void instrument_init(void) {
instrument_coverage_init();
instrument_coverage_optimize_init();
instrument_debug_init();
instrument_cache_init();
}

View File

@ -5,6 +5,9 @@
#if defined(__arm__)
gboolean instrument_cache_enabled = FALSE;
gsize instrument_cache_size = 0;
gboolean instrument_is_coverage_optimize_supported(void) {
return false;
@ -55,5 +58,27 @@ gpointer instrument_cur(GumStalkerOutput *output) {
}
void instrument_cache_config(void) {
}
void instrument_cache_init(void) {
}
void instrument_cache_insert(gpointer real_address, gpointer code_address) {
UNUSED_PARAMETER(real_address);
UNUSED_PARAMETER(code_address);
}
void instrument_cache(const cs_insn *instr, GumStalkerOutput *output) {
UNUSED_PARAMETER(instr);
UNUSED_PARAMETER(output);
}
#endif

View File

@ -379,5 +379,27 @@ gpointer instrument_cur(GumStalkerOutput *output) {
}
void instrument_cache_config(void) {
}
void instrument_cache_init(void) {
}
void instrument_cache_insert(gpointer real_address, gpointer code_address) {
UNUSED_PARAMETER(real_address);
UNUSED_PARAMETER(code_address);
}
void instrument_cache(const cs_insn *instr, GumStalkerOutput *output) {
UNUSED_PARAMETER(instr);
UNUSED_PARAMETER(output);
}
#endif

View File

@ -202,10 +202,17 @@ static void instrument_coverage_switch(GumStalkerObserver *self,
}
if (op[0].type != X86_OP_IMM) { return; }
if (op[0].type != X86_OP_IMM) {
instrument_cache_insert(start_address, *target);
return;
}
break;
case X86_INS_RET:
instrument_cache_insert(start_address,
(guint8 *)*target + sizeof(afl_log_code));
break;
default:
return;

View File

@ -0,0 +1,435 @@
#include <sys/mman.h>
#include <sys/resource.h>
#include "instrument.h"
#include "util.h"
#if defined(__x86_64__)
#define INVALID 1
#define DEFAULT_CACHE_SIZE (256ULL << 20)
gboolean instrument_cache_enabled = TRUE;
gsize instrument_cache_size = DEFAULT_CACHE_SIZE;
static gpointer *map_base = MAP_FAILED;
void instrument_cache_config(void) {
instrument_cache_enabled = (getenv("AFL_FRIDA_INST_NO_CACHE") == NULL);
if (getenv("AFL_FRIDA_INST_CACHE_SIZE") != NULL) {
if (!instrument_cache_enabled) {
FFATAL(
"AFL_FRIDA_INST_CACHE_SIZE incomatible with "
"AFL_FRIDA_INST_NO_CACHE");
}
instrument_cache_size =
util_read_address("AFL_FRIDA_INST_CACHE_SIZE", DEFAULT_CACHE_SIZE);
util_log2(instrument_cache_size);
}
}
void instrument_cache_init(void) {
FOKF(cBLU "Instrumentation" cRST " - " cGRN "cache:" cYEL " [%c]",
instrument_cache_enabled ? 'X' : ' ');
if (!instrument_cache_enabled) { return; }
FOKF(cBLU "Instrumentation" cRST " - " cGRN "cache size:" cYEL " [0x%016lX]",
instrument_cache_size);
const struct rlimit data_limit = {.rlim_cur = RLIM_INFINITY,
.rlim_max = RLIM_INFINITY};
if (setrlimit(RLIMIT_AS, &data_limit) != 0) {
FFATAL("Failed to setrlimit: %d", errno);
}
map_base =
gum_memory_allocate(NULL, instrument_cache_size, instrument_cache_size,
GUM_PAGE_READ | GUM_PAGE_WRITE);
if (map_base == MAP_FAILED) { FFATAL("Failed to map segment: %d", errno); }
FOKF(cBLU "Instrumentation" cRST " - " cGRN "cache addr:" cYEL " [0x%016lX]",
GUM_ADDRESS(map_base));
}
static gpointer *instrument_cache_get_addr(gpointer addr) {
gsize mask = (instrument_cache_size / sizeof(gpointer)) - 1;
return &map_base[GPOINTER_TO_SIZE(addr) & mask];
}
void instrument_cache_insert(gpointer real_address, gpointer code_address) {
if (!instrument_cache_enabled) { return; }
gpointer *target = instrument_cache_get_addr(real_address);
if (*target == code_address) {
return;
} else if (*target == NULL) {
*target = code_address;
} else {
*target = GSIZE_TO_POINTER(INVALID);
}
}
static gboolean instrument_cache_relocate(GumAddress old_pc, GumAddress new_pc,
gint32 old_offset,
gint32 *new_offset) {
guint64 old_target = old_pc + old_offset;
gint64 relocated = old_target - new_pc;
if (relocated > G_MAXINT32 || relocated < G_MININT32) { return FALSE; }
*new_offset = relocated;
return TRUE;
}
static void instrument_cache_rewrite_branch_insn(const cs_insn * instr,
GumStalkerOutput *output) {
GumX86Writer *cw = output->writer.x86;
cs_x86 * x86 = &instr->detail->x86;
guint8 modified[sizeof(instr->bytes)] = {0};
guint8 offset = 0;
guint8 skip = 0;
g_assert(sizeof(x86->prefix) == 4);
g_assert(sizeof(x86->opcode) == 4);
/*
* If the target is simply RAX, we can skip writing the code to load the
* RIP
*/
if (x86->operands[0].type == X86_OP_REG ||
x86->operands[0].reg == X86_REG_RAX) {
return;
}
/* Write the prefix */
for (gsize i = 0; i < sizeof(x86->prefix); i++) {
if (x86->prefix[i] != 0) {
if (x86->prefix[i] == 0xf2) {
skip++;
} else {
modified[offset++] = x86->prefix[i];
skip++;
}
}
}
/* Write the REX */
if (x86->rex == 0) {
/*
* CALL (near) and JMP (near) default to 64-bit operands, MOV does not,
* write REX.W
*/
modified[offset++] = 0x48;
} else {
if ((x86->rex & 0xF8) != 0x40) {
FATAL("Unexpected REX byte: 0x%02x", x86->rex);
}
modified[offset++] = x86->rex | 0x08;
skip++;
}
/*
* CALL is FF /2, JMP is FF /4. The remaining op-code fields should thus be
* unused
*/
if (x86->opcode[0] != 0xFF || x86->opcode[1] != 0x00 ||
x86->opcode[2] != 0x00 || x86->opcode[3] != 0x00) {
FFATAL("Unexpected Op-code: 0x%02x 0x%02x 0x%02x 0x%02x", x86->opcode[0],
x86->opcode[1], x86->opcode[2], x86->opcode[3]);
}
/* The reg field of the ModRM should be set to 2 for CALL and 4 for JMP */
guint8 reg = (x86->modrm >> 3) & 7;
if (reg != 0x4 && reg != 0x2) {
FFATAL("Unexpected Reg: 0x%02x, ModRM: 0x%02x", reg, x86->modrm);
}
/* MOV */
modified[offset++] = 0x8b;
skip++;
/* Clear the reg field (RAX) */
modified[offset++] = x86->modrm & 0xc7;
skip++;
/* Operands */
guint8 op_len = instr->size - skip;
/* If our branch was RIP relative, we'll need to fix-up the offset */
if (x86->operands[0].type == X86_OP_MEM &&
x86->operands[0].mem.base == X86_REG_RIP) {
/* RIP relative offsets should be 32-bits */
if (op_len != sizeof(gint32)) {
FFATAL("Unexpected operand length: %d\n", op_len);
}
gint32 old_offset = *(gint32 *)&instr->bytes[skip];
gint32 new_offset = 0;
if (instrument_cache_relocate(instr->address, cw->pc, old_offset,
&new_offset)) {
gint32 *output = (gint32 *)&modified[offset];
*output = new_offset;
offset += sizeof(gint32);
} else {
GumAddress target = instr->address + old_offset;
gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, target);
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_RAX, GUM_REG_RAX);
return;
}
} else {
for (int i = 0; i < op_len; i++) {
guint8 val = instr->bytes[i + skip];
modified[offset++] = val;
}
}
gum_x86_writer_put_bytes(cw, modified, offset);
}
static void instrument_cache_write_push_frame(GumX86Writer *cw) {
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))),
GUM_REG_XAX);
gum_x86_writer_put_lahf(cw);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (2 * sizeof(gpointer))),
GUM_REG_XAX);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (3 * sizeof(gpointer))),
GUM_REG_XBX);
}
static void instrument_cache_write_pop_frame(GumX86Writer *cw) {
gum_x86_writer_put_mov_reg_reg_offset_ptr(
cw, GUM_REG_XBX, GUM_REG_XSP,
-(GUM_RED_ZONE_SIZE + (3 * sizeof(gpointer))));
gum_x86_writer_put_mov_reg_reg_offset_ptr(
cw, GUM_REG_XAX, GUM_REG_XSP,
-(GUM_RED_ZONE_SIZE + (2 * sizeof(gpointer))));
gum_x86_writer_put_sahf(cw);
gum_x86_writer_put_mov_reg_reg_offset_ptr(
cw, GUM_REG_XAX, GUM_REG_XSP,
-(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))));
}
static void instrument_cache_write_lookup(GumX86Writer *cw) {
/* &map_base[GPOINTER_TO_SIZE(addr) & MAP_MASK]; */
gsize mask = (instrument_cache_size / sizeof(gpointer)) - 1;
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_XBX, mask);
gum_x86_writer_put_and_reg_reg(cw, GUM_REG_XAX, GUM_REG_XBX);
gum_x86_writer_put_shl_reg_u8(cw, GUM_REG_XAX, util_log2(sizeof(gpointer)));
gum_x86_writer_put_mov_reg_u64(cw, GUM_REG_XBX, GPOINTER_TO_SIZE(map_base));
gum_x86_writer_put_add_reg_reg(cw, GUM_REG_XAX, GUM_REG_XBX);
/* Read the return address lookup */
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_XAX, GUM_REG_XAX);
}
void instrument_cache_jmp_call(const cs_insn *instr, GumStalkerOutput *output) {
GumX86Writer *cw = output->writer.x86;
cs_x86 * x86 = &instr->detail->x86;
if (x86->op_count != 1) { FFATAL("Unexpected operand count"); }
if (x86->operands[0].type == X86_OP_IMM) { return; }
gconstpointer null = cw->code;
instrument_cache_write_push_frame(cw);
/*
* We are about to re-write the CALL or JMP instruction, but replace the
* op-code with that for a MOV into RAX. Since we are keeping the operand from
* the JMP exactly the same, it is imperative that the target register state
* be exactly the same as how the target left it. Since `LAHF` spoils `RAX` we
* must restore it from the stack. We also must avoid adjusting `RSP`, so we
* use `MOV` instructions to store our context into the stack beyond the
* red-zone.
*/
gum_x86_writer_put_mov_reg_reg_offset_ptr(
cw, GUM_REG_XAX, GUM_REG_XSP,
-(GUM_RED_ZONE_SIZE + (1 * sizeof(gpointer))));
instrument_cache_rewrite_branch_insn(instr, output);
instrument_cache_write_lookup(cw);
/* Test if its set*/
gum_x86_writer_put_cmp_reg_i32(cw, GUM_REG_XAX, INVALID);
gum_x86_writer_put_jcc_short_label(cw, X86_INS_JLE, null, GUM_UNLIKELY);
/* If it's set, then stash the address beyond the red-zone */
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (4 * sizeof(gpointer))),
GUM_REG_XAX);
if (instr->id == X86_INS_JMP) {
instrument_cache_write_pop_frame(cw);
gum_x86_writer_put_jmp_reg_offset_ptr(
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + (4 * sizeof(gpointer))));
} else {
gum_x86_writer_put_mov_reg_address(
cw, GUM_REG_XAX, GUM_ADDRESS(instr->address + instr->size));
gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_XSP,
-sizeof(gpointer), GUM_REG_XAX);
instrument_cache_write_pop_frame(cw);
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_XSP, GUM_REG_XSP,
-sizeof(gpointer));
gum_x86_writer_put_jmp_reg_offset_ptr(
cw, GUM_REG_XSP, -(GUM_RED_ZONE_SIZE + ((4 - 1) * sizeof(gpointer))));
}
/* Tidy up our mess and let FRIDA handle it */
gum_x86_writer_put_label(cw, null);
instrument_cache_write_pop_frame(cw);
}
void instrument_cache_ret(const cs_insn *instr, GumStalkerOutput *output) {
GumX86Writer *cw = output->writer.x86;
cs_x86 * x86 = &instr->detail->x86;
guint16 n = 0;
if (x86->op_count != 0) {
if (x86->operands[0].type != X86_OP_IMM) {
FFATAL("Unexpected operand type");
}
n = x86->operands[0].imm;
}
gconstpointer null = cw->code;
instrument_cache_write_push_frame(cw);
gum_x86_writer_put_mov_reg_reg_ptr(cw, GUM_REG_XAX, GUM_REG_XSP);
instrument_cache_write_lookup(cw);
/* Test if its set*/
gum_x86_writer_put_cmp_reg_i32(cw, GUM_REG_XAX, INVALID);
gum_x86_writer_put_jcc_short_label(cw, X86_INS_JLE, null, GUM_UNLIKELY);
/* If it's set, then overwrite our return address and return */
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_REG_XSP, GUM_REG_XAX);
instrument_cache_write_pop_frame(cw);
if (n == 0) {
gum_x86_writer_put_ret(cw);
} else {
gum_x86_writer_put_ret_imm(cw, n);
}
/* Tidy up our mess and let FRIDA handle it */
gum_x86_writer_put_label(cw, null);
instrument_cache_write_pop_frame(cw);
}
void instrument_cache(const cs_insn *instr, GumStalkerOutput *output) {
if (!instrument_cache_enabled) { return; }
switch (instr->id) {
case X86_INS_RET:
instrument_cache_ret(instr, output);
break;
case X86_INS_CALL:
case X86_INS_JMP:
instrument_cache_jmp_call(instr, output);
break;
default:
return;
}
}
#endif

View File

@ -6,6 +6,9 @@
#if defined(__i386__)
gboolean instrument_cache_enabled = FALSE;
gsize instrument_cache_size = 0;
static GHashTable *coverage_blocks = NULL;
#pragma pack(push, 1)
@ -242,5 +245,27 @@ gpointer instrument_cur(GumStalkerOutput *output) {
}
void instrument_cache_config(void) {
}
void instrument_cache_init(void) {
}
void instrument_cache_insert(gpointer real_address, gpointer code_address) {
UNUSED_PARAMETER(real_address);
UNUSED_PARAMETER(code_address);
}
void instrument_cache(const cs_insn *instr, GumStalkerOutput *output) {
UNUSED_PARAMETER(instr);
UNUSED_PARAMETER(output);
}
#endif

View File

@ -68,6 +68,12 @@ class Afl {
static setBackpatchDisable() {
Afl.jsApiSetBackpatchDisable();
}
/**
* See `AFL_FRIDA_INST_NO_CACHE`.
*/
static setCacheDisable() {
Afl.jsApiSetCacheDisable();
}
/**
* See `AFL_FRIDA_DEBUG_MAPS`.
*/
@ -91,6 +97,13 @@ class Afl {
static setInMemoryFuzzing() {
Afl.jsApiAflSharedMemFuzzing.writeInt(1);
}
/**
* See `AFL_FRIDA_INST_CACHE_SIZE`. This function takes a single `number`
* as an argument.
*/
static setInstrumentCacheSize(size) {
Afl.jsApiSetInstrumentCacheSize(size);
}
/**
* See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string`
* as an argument.
@ -299,8 +312,10 @@ 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.jsApiSetBackpatchDisable = Afl.jsApiGetFunction("js_api_set_backpatch_disable", "void", []);
Afl.jsApiSetCacheDisable = Afl.jsApiGetFunction("js_api_set_cache_disable", "void", []);
Afl.jsApiSetDebugMaps = Afl.jsApiGetFunction("js_api_set_debug_maps", "void", []);
Afl.jsApiSetEntryPoint = Afl.jsApiGetFunction("js_api_set_entrypoint", "void", ["pointer"]);
Afl.jsApiSetInstrumentCacheSize = Afl.jsApiGetFunction("js_api_set_instrument_cache_size", "void", ["size_t"]);
Afl.jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction("js_api_set_instrument_coverage_file", "void", ["pointer"]);
Afl.jsApiSetInstrumentDebugFile = Afl.jsApiGetFunction("js_api_set_instrument_debug_file", "void", ["pointer"]);
Afl.jsApiSetInstrumentInstructions = Afl.jsApiGetFunction("js_api_set_instrument_instructions", "void", []);

View File

@ -262,6 +262,19 @@ __attribute__((visibility("default"))) void js_api_set_stalker_adjacent_blocks(
}
__attribute__((visibility("default"))) void js_api_set_cache_disable(void) {
instrument_cache_enabled = FALSE;
}
__attribute__((visibility("default"))) void js_api_set_instrument_cache_size(
gsize size) {
instrument_cache_size = size;
}
__attribute__((visibility("default"))) void js_api_set_js_main_hook(
const js_main_hook_t hook) {

97
frida_mode/test/cache/GNUmakefile vendored Normal file
View File

@ -0,0 +1,97 @@
PWD:=$(shell pwd)/
ROOT:=$(PWD)../../../
BUILD_DIR:=$(PWD)build/
TEST_CACHE_SRC:=$(PWD)cache.c
TEST_CACHE_OBJ:=$(BUILD_DIR)cache
TEST_DATA_DIR:=$(BUILD_DIR)in/
CACHE_INPUT:=$(TEST_DATA_DIR)in
QEMU_OUT:=$(BUILD_DIR)qemu-out
FRIDA_OUT:=$(BUILD_DIR)frida-out
ADDR_BIN:=$(ROOT)frida_mode/build/addr
GET_SYMBOL_ADDR:=$(ROOT)frida_mode/util/get_symbol_addr.sh
AFLPP_FRIDA_DRIVER_HOOK_OBJ=$(ROOT)frida_mode/build/frida_hook.so
AFL_FRIDA_BASE_ADDR:=$(shell $(ADDR_BIN))
AFL_FRIDA_PERSISTENT_ADDR=$(shell $(GET_SYMBOL_ADDR) $(TEST_CACHE_OBJ) LLVMFuzzerTestOneInput $(AFL_FRIDA_BASE_ADDR))
DUMMY_DATA_FILE:=$(BUILD_DIR)dummy.dat
.PHONY: all 32 clean frida frida_noinst debug format
all: $(TEST_CACHE_OBJ)
make -C $(ROOT)frida_mode/
32:
CFLAGS="-m32" LDFLAGS="-m32" ARCH="x86" make all
$(BUILD_DIR):
mkdir -p $@
$(TEST_DATA_DIR): | $(BUILD_DIR)
mkdir -p $@
$(CACHE_INPUT): | $(TEST_DATA_DIR)
echo -n "ABC" > $@
$(TEST_CACHE_OBJ): $(TEST_CACHE_SRC) | $(BUILD_DIR)
$(CC) -g $(CFLAGS) $(LDFLAGS) $< -o $@
########## DUMMY #######
$(DUMMY_DATA_FILE): | $(BUILD_DIR)
dd if=/dev/zero bs=1048576 count=1 of=$@
frida: $(TEST_CACHE_OBJ) $(CACHE_INPUT) $(DUMMY_DATA_FILE)
AFL_FRIDA_INST_INSN=1 \
AFL_FRIDA_PERSISTENT_CNT=1000000 \
AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \
AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
$(ROOT)afl-fuzz \
-O \
-i $(TEST_DATA_DIR) \
-o $(FRIDA_OUT) \
-Z \
-t 10000+ \
-- \
$(TEST_CACHE_OBJ) $(DUMMY_DATA_FILE)
frida_nocache: $(TEST_CACHE_OBJ) $(CACHE_INPUT) $(DUMMY_DATA_FILE)
AFL_FRIDA_INST_NO_CACHE=1 \
AFL_FRIDA_PERSISTENT_CNT=1000000 \
AFL_FRIDA_PERSISTENT_HOOK=$(AFLPP_FRIDA_DRIVER_HOOK_OBJ) \
AFL_FRIDA_PERSISTENT_ADDR=$(AFL_FRIDA_PERSISTENT_ADDR) \
AFL_ENTRYPOINT=$(AFL_FRIDA_PERSISTENT_ADDR) \
$(ROOT)afl-fuzz \
-O \
-i $(TEST_DATA_DIR) \
-o $(FRIDA_OUT) \
-Z \
-- \
$(TEST_CACHE_OBJ) $(DUMMY_DATA_FILE)
debug: $(TEST_CACHE_OBJ) $(CACHE_INPUT)
gdb \
--ex 'set environment LD_PRELOAD=$(ROOT)afl-frida-trace.so' \
--ex 'set disassembly-flavor intel' \
--ex 'r $(CACHE_INPUT)' \
--args $(TEST_CACHE_OBJ) $(CACHE_INPUT)
show: $(TEST_CACHE_OBJ) $(CACHE_INPUT)
gdb \
--ex "set disassembly-flavor intel" \
--ex "set confirm off" \
--ex "symbol-file $(TEST_CACHE_OBJ)" \
--ex "x/50i LLVMFuzzerTestOneInput" \
--ex "r" \
--args $(TEST_CACHE_OBJ) $(CACHE_INPUT)
clean:
rm -rf $(BUILD_DIR)
format:
cd $(ROOT) && echo $(TEST_CACHE_SRC) | xargs -L1 ./.custom-format.py -i

22
frida_mode/test/cache/Makefile vendored Normal file
View File

@ -0,0 +1,22 @@
all:
@echo trying to use GNU make...
@gmake all || echo please install GNUmake
32:
@echo trying to use GNU make...
@gmake 32 || echo please install GNUmake
frida:
@gmake frida
frida_nocache:
@gmake frida_nocache
debug:
@gmake debug
clean:
@gmake clean
format:
@gmake format

115
frida_mode/test/cache/cache.c vendored Normal file
View File

@ -0,0 +1,115 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void LLVMFuzzerTestOneInput(char *buf, int len);
__asm__ (
"LLVMFuzzerTestOneInput:\n"
".func LLVMFuzzerTestOneInput\n"
".global LLVMFuzzerTestOneInput\n"
" jmpq *jmp_offset(%rip)\n"
" nop\n"
" nop\n"
"call_target:\n"
" ret\n"
" nop\n"
" nop\n"
"jmp_target:\n"
" callq *call_offset(%rip)\n"
" nop\n"
" nop\n"
" leaq rax_offset(%rip), %rax\n"
" jmp (%rax)\n"
" nop\n"
" ud2\n"
" nop\n"
"rax_target:\n"
" ret\n"
"\n"
"\n"
".global jmp_offset\n"
".p2align 3\n"
"jmp_offset:\n"
" .quad jmp_target\n"
"call_offset:\n"
" .quad call_target\n"
"rax_offset:\n"
" .quad rax_target\n"
);
int main(int argc, char **argv) {
char * file;
int fd = -1;
off_t len;
char * buf = NULL;
size_t n_read;
int result = -1;
if (argc != 2) { return 1; }
do {
file = argv[1];
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 = (char *)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;
}

View File

@ -125,7 +125,7 @@ vorbis: $(VORBIS_LIB)
########## HARNESS #######
$(DECODE_SRC):
wget -O $@ $(DECODE_URL)
wget -O $@ $(DECODE_URL) || curl -L -o $@ $(DECODE_URL)
$(DECODE_OBJ): $(DECODE_SRC)
$(CXX) -o $@ -c $< -I$(VORBIS_DIR)include/ -I$(OGG_DIR)include/
@ -135,7 +135,7 @@ decode: $(DECODE_OBJ)
########## HARNESS #######
$(HARNESS_SRC):
wget -O $@ $(HARNESS_URL)
wget -O $@ $(HARNESS_URL) || curl -L -o $@ $(HARNESS_URL)
$(HARNESS_OBJ): $(HARNESS_SRC)
$(CC) -o $@ -c $<
@ -165,8 +165,8 @@ $(AFLPP_DRIVER_DUMMY_INPUT): | $(BUILD_DIR)
###### TEST DATA #######
$(TEST_DATA_FILE): $(TEST_DATA_DIR)
wget -O $@ $(TEST_DATA_SRC)
$(TEST_DATA_FILE): | $(TEST_DATA_DIR)
wget -O $@ $(TEST_DATA_SRC) || curl -L -o $@ $(TEST_DATA_SRC)
clean:
rm -rf $(BUILD_DIR)

View File

@ -84,6 +84,13 @@ class Afl {
Afl.jsApiSetBackpatchDisable();
}
/**
* See `AFL_FRIDA_INST_NO_CACHE`.
*/
public static setCacheDisable(): void {
Afl.jsApiSetCacheDisable();
}
/**
* See `AFL_FRIDA_DEBUG_MAPS`.
*/
@ -110,6 +117,14 @@ class Afl {
Afl.jsApiAflSharedMemFuzzing.writeInt(1);
}
/**
* See `AFL_FRIDA_INST_CACHE_SIZE`. This function takes a single `number`
* as an argument.
*/
public static setInstrumentCacheSize(size: number): void {
Afl.jsApiSetInstrumentCacheSize(size);
}
/**
* See `AFL_FRIDA_INST_COVERAGE_FILE`. This function takes a single `string`
* as an argument.
@ -354,6 +369,11 @@ class Afl {
"void",
[]);
private static readonly jsApiSetCacheDisable = Afl.jsApiGetFunction(
"js_api_set_cache_disable",
"void",
[]);
private static readonly jsApiSetDebugMaps = Afl.jsApiGetFunction(
"js_api_set_debug_maps",
"void",
@ -364,6 +384,11 @@ class Afl {
"void",
["pointer"]);
private static readonly jsApiSetInstrumentCacheSize = Afl.jsApiGetFunction(
"js_api_set_instrument_cache_size",
"void",
["size_t"]);
private static readonly jsApiSetInstrumentCoverageFile = Afl.jsApiGetFunction(
"js_api_set_instrument_coverage_file",
"void",

View File

@ -57,10 +57,12 @@ static char *afl_environment_variables[] = {
"AFL_FRIDA_DEBUG_MAPS",
"AFL_FRIDA_DRIVER_NO_HOOK",
"AFL_FRIDA_EXCLUDE_RANGES",
"AFL_FRIDA_INST_CACHE_SIZE",
"AFL_FRIDA_INST_COVERAGE_FILE",
"AFL_FRIDA_INST_DEBUG_FILE",
"AFL_FRIDA_INST_INSN",
"AFL_FRIDA_INST_JIT",
"AFL_FRIDA_INST_NO_CACHE",
"AFL_FRIDA_INST_NO_OPTIMIZE",
"AFL_FRIDA_INST_NO_PREFETCH",
"AFL_FRIDA_INST_NO_PREFETCH_BACKPATCH",