mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-16 11:58:08 +00:00
Add global caching of block translation to instrumentation
This commit is contained in:
@ -151,15 +151,12 @@ 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:
|
||||
Creating block for 0x7ffff7953313:
|
||||
0x7ffff7953313 mov qword ptr [rax], 0
|
||||
0x7ffff795331a add rsp, 8
|
||||
0x7ffff795331e ret
|
||||
|
||||
Generated block 0x7ffff75e98e2
|
||||
Generated block 0x7ffff75e98e2
|
||||
0x7ffff75e98e2 mov qword ptr [rax], 0
|
||||
0x7ffff75e98e9 add rsp, 8
|
||||
0x7ffff75e98ed lea rsp, [rsp - 0x80]
|
||||
@ -167,9 +164,12 @@ instances run CMPLOG mode and instrumentation of the binary is less frequent
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
435
frida_mode/src/instrument/instrument_x64_cache.c
Normal file
435
frida_mode/src/instrument/instrument_x64_cache.c
Normal 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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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", []);
|
||||
|
@ -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
97
frida_mode/test/cache/GNUmakefile
vendored
Normal 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
22
frida_mode/test/cache/Makefile
vendored
Normal 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
115
frida_mode/test/cache/cache.c
vendored
Normal 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;
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
Reference in New Issue
Block a user