diff options
Diffstat (limited to 'frida_mode/src')
25 files changed, 2326 insertions, 458 deletions
diff --git a/frida_mode/src/cmplog/cmplog.c b/frida_mode/src/cmplog/cmplog.c new file mode 100644 index 00000000..3fab1951 --- /dev/null +++ b/frida_mode/src/cmplog/cmplog.c @@ -0,0 +1,87 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "util.h" + +#define DEFAULT_MMAP_MIN_ADDR (32UL << 10) + +extern struct cmp_map *__afl_cmp_map; + +static GArray *cmplog_ranges = NULL; + +static gboolean cmplog_range(const GumRangeDetails *details, + gpointer user_data) { + + UNUSED_PARAMETER(user_data); + GumMemoryRange range = *details->range; + g_array_append_val(cmplog_ranges, range); + return TRUE; + +} + +static gint cmplog_sort(gconstpointer a, gconstpointer b) { + + return ((GumMemoryRange *)b)->base_address - + ((GumMemoryRange *)a)->base_address; + +} + +void cmplog_init(void) { + + if (__afl_cmp_map != NULL) { OKF("CMPLOG mode enabled"); } + + cmplog_ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), 100); + gum_process_enumerate_ranges(GUM_PAGE_READ, cmplog_range, NULL); + g_array_sort(cmplog_ranges, cmplog_sort); + + for (guint i = 0; i < cmplog_ranges->len; i++) { + + GumMemoryRange *range = &g_array_index(cmplog_ranges, GumMemoryRange, i); + OKF("CMPLOG Range - 0x%016" G_GINT64_MODIFIER "X - 0x%016" G_GINT64_MODIFIER + "X", + range->base_address, range->base_address + range->size); + + } + +} + +static gboolean cmplog_contains(GumAddress inner_base, GumAddress inner_limit, + GumAddress outer_base, GumAddress outer_limit) { + + return (inner_base >= outer_base && inner_limit <= outer_limit); + +} + +gboolean cmplog_is_readable(void *addr, size_t size) { + + if (cmplog_ranges == NULL) FATAL("CMPLOG not initialized"); + + /* + * The Linux kernel prevents mmap from allocating from the very bottom of the + * address space to mitigate NULL pointer dereference attacks. The exact size + * is set by sysctl by setting mmap_min_addr and 64k is suggested on most + * platforms with 32k on ARM systems. We therefore fail fast if the address + * is lower than this. This should avoid some overhead when functions are + * called where one of the parameters is a size, or a some other small value. + */ + if (GPOINTER_TO_SIZE(addr) < DEFAULT_MMAP_MIN_ADDR) { return false; } + + GumAddress inner_base = GUM_ADDRESS(addr); + GumAddress inner_limit = inner_base + size; + + for (guint i = 0; i < cmplog_ranges->len; i++) { + + GumMemoryRange *range = &g_array_index(cmplog_ranges, GumMemoryRange, i); + GumAddress outer_base = range->base_address; + GumAddress outer_limit = outer_base + range->size; + + if (cmplog_contains(inner_base, inner_limit, outer_base, outer_limit)) + return true; + + } + + return false; + +} + diff --git a/frida_mode/src/cmplog/cmplog_arm.c b/frida_mode/src/cmplog/cmplog_arm.c new file mode 100644 index 00000000..5af28f3f --- /dev/null +++ b/frida_mode/src/cmplog/cmplog_arm.c @@ -0,0 +1,19 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "frida_cmplog.h" +#include "util.h" + +#if defined(__arm__) +void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) { + + UNUSED_PARAMETER(instr); + UNUSED_PARAMETER(iterator); + if (__afl_cmp_map == NULL) { return; } + FATAL("CMPLOG mode not supported on this architecture"); + +} + +#endif + diff --git a/frida_mode/src/cmplog/cmplog_arm64.c b/frida_mode/src/cmplog/cmplog_arm64.c new file mode 100644 index 00000000..187d0162 --- /dev/null +++ b/frida_mode/src/cmplog/cmplog_arm64.c @@ -0,0 +1,19 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "frida_cmplog.h" +#include "util.h" + +#if defined(__aarch64__) +void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) { + + UNUSED_PARAMETER(instr); + UNUSED_PARAMETER(iterator); + if (__afl_cmp_map == NULL) { return; } + FATAL("CMPLOG mode not supported on this architecture"); + +} + +#endif + diff --git a/frida_mode/src/cmplog/cmplog_x64.c b/frida_mode/src/cmplog/cmplog_x64.c new file mode 100644 index 00000000..9bf09ad5 --- /dev/null +++ b/frida_mode/src/cmplog/cmplog_x64.c @@ -0,0 +1,346 @@ +#include "frida-gum.h" + +#include "debug.h" +#include "cmplog.h" + +#include "frida_cmplog.h" +#include "util.h" + +#if defined(__x86_64__) + + #define X86_REG_8L(LABEL, REG) \ + case LABEL: { \ + \ + return REG & GUM_INT8_MASK; \ + \ + } + + #define X86_REG_8H(LABEL, REG) \ + case LABEL: { \ + \ + return (REG & GUM_INT16_MASK) >> 8; \ + \ + } + + #define X86_REG_16(LABEL, REG) \ + case LABEL: { \ + \ + return (REG & GUM_INT16_MASK); \ + \ + } + + #define X86_REG_32(LABEL, REG) \ + case LABEL: { \ + \ + return (REG & GUM_INT32_MASK); \ + \ + } + + #define X86_REG_64(LABEL, REG) \ + case LABEL: { \ + \ + return (REG); \ + \ + } + +typedef struct { + + x86_op_type type; + uint8_t size; + + union { + + x86_op_mem mem; + x86_reg reg; + int64_t imm; + + }; + +} cmplog_ctx_t; + +typedef struct { + + cmplog_ctx_t operand1; + cmplog_ctx_t operand2; + +} cmplog_pair_ctx_t; + +static guint64 cmplog_read_reg(GumX64CpuContext *ctx, x86_reg reg) { + + switch (reg) { + + X86_REG_8L(X86_REG_AL, ctx->rax) + X86_REG_8L(X86_REG_BL, ctx->rbx) + X86_REG_8L(X86_REG_CL, ctx->rcx) + X86_REG_8L(X86_REG_DL, ctx->rdx) + X86_REG_8L(X86_REG_BPL, ctx->rbp) + X86_REG_8L(X86_REG_SIL, ctx->rsi) + X86_REG_8L(X86_REG_DIL, ctx->rdi) + + X86_REG_8H(X86_REG_AH, ctx->rax) + X86_REG_8H(X86_REG_BH, ctx->rbx) + X86_REG_8H(X86_REG_CH, ctx->rcx) + X86_REG_8H(X86_REG_DH, ctx->rdx) + + X86_REG_16(X86_REG_AX, ctx->rax) + X86_REG_16(X86_REG_BX, ctx->rbx) + X86_REG_16(X86_REG_CX, ctx->rcx) + X86_REG_16(X86_REG_DX, ctx->rdx) + X86_REG_16(X86_REG_DI, ctx->rdi) + X86_REG_16(X86_REG_SI, ctx->rsi) + X86_REG_16(X86_REG_BP, ctx->rbp) + + X86_REG_32(X86_REG_EAX, ctx->rax) + X86_REG_32(X86_REG_ECX, ctx->rcx) + X86_REG_32(X86_REG_EDX, ctx->rdx) + X86_REG_32(X86_REG_EBX, ctx->rbx) + X86_REG_32(X86_REG_ESP, ctx->rsp) + X86_REG_32(X86_REG_EBP, ctx->rbp) + X86_REG_32(X86_REG_ESI, ctx->rsi) + X86_REG_32(X86_REG_EDI, ctx->rdi) + X86_REG_32(X86_REG_R8D, ctx->r8) + X86_REG_32(X86_REG_R9D, ctx->r9) + X86_REG_32(X86_REG_R10D, ctx->r10) + X86_REG_32(X86_REG_R11D, ctx->r11) + X86_REG_32(X86_REG_R12D, ctx->r12) + X86_REG_32(X86_REG_R13D, ctx->r13) + X86_REG_32(X86_REG_R14D, ctx->r14) + X86_REG_32(X86_REG_R15D, ctx->r15) + X86_REG_32(X86_REG_EIP, ctx->rip) + + X86_REG_64(X86_REG_RAX, ctx->rax) + X86_REG_64(X86_REG_RCX, ctx->rcx) + X86_REG_64(X86_REG_RDX, ctx->rdx) + X86_REG_64(X86_REG_RBX, ctx->rbx) + X86_REG_64(X86_REG_RSP, ctx->rsp) + X86_REG_64(X86_REG_RBP, ctx->rbp) + X86_REG_64(X86_REG_RSI, ctx->rsi) + X86_REG_64(X86_REG_RDI, ctx->rdi) + X86_REG_64(X86_REG_R8, ctx->r8) + X86_REG_64(X86_REG_R9, ctx->r9) + X86_REG_64(X86_REG_R10, ctx->r10) + X86_REG_64(X86_REG_R11, ctx->r11) + X86_REG_64(X86_REG_R12, ctx->r12) + X86_REG_64(X86_REG_R13, ctx->r13) + X86_REG_64(X86_REG_R14, ctx->r14) + X86_REG_64(X86_REG_R15, ctx->r15) + X86_REG_64(X86_REG_RIP, ctx->rip) + + default: + FATAL("Failed to read register: %d", reg); + return 0; + + } + +} + +static guint64 cmplog_read_mem(GumX64CpuContext *ctx, x86_op_mem *mem) { + + guint64 base = 0; + guint64 index = 0; + guint64 address; + + if (mem->base != X86_REG_INVALID) base = cmplog_read_reg(ctx, mem->base); + + if (mem->index != X86_REG_INVALID) index = cmplog_read_reg(ctx, mem->index); + + address = base + (index * mem->scale) + mem->disp; + return address; + +} + +static guint64 cmplog_get_operand_value(GumCpuContext *context, + cmplog_ctx_t * ctx) { + + switch (ctx->type) { + + case X86_OP_REG: + return cmplog_read_reg(context, ctx->reg); + case X86_OP_IMM: + return ctx->imm; + case X86_OP_MEM: + return cmplog_read_mem(context, &ctx->mem); + default: + FATAL("Invalid operand type: %d\n", ctx->type); + + } + +} + +static void cmplog_call_callout(GumCpuContext *context, gpointer user_data) { + + UNUSED_PARAMETER(user_data); + + guint64 address = cmplog_read_reg(context, X86_REG_RIP); + guint64 rdi = cmplog_read_reg(context, X86_REG_RDI); + guint64 rsi = cmplog_read_reg(context, X86_REG_RSI); + + if (((G_MAXULONG - rdi) < 32) || ((G_MAXULONG - rsi) < 32)) return; + + void *ptr1 = GSIZE_TO_POINTER(rdi); + void *ptr2 = GSIZE_TO_POINTER(rsi); + + if (!cmplog_is_readable(ptr1, 32) || !cmplog_is_readable(ptr2, 32)) return; + + uintptr_t k = address; + + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_RTN; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = 31; + + hits &= CMP_MAP_RTN_H - 1; + gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v0, ptr1, + 32); + gum_memcpy(((struct cmpfn_operands *)__afl_cmp_map->log[k])[hits].v1, ptr2, + 32); + +} + +static void cmplog_instrument_put_operand(cmplog_ctx_t *ctx, + cs_x86_op * operand) { + + ctx->type = operand->type; + ctx->size = operand->size; + switch (operand->type) { + + case X86_OP_REG: + gum_memcpy(&ctx->reg, &operand->reg, sizeof(x86_reg)); + break; + case X86_OP_IMM: + gum_memcpy(&ctx->imm, &operand->imm, sizeof(int64_t)); + break; + case X86_OP_MEM: + gum_memcpy(&ctx->mem, &operand->mem, sizeof(x86_op_mem)); + break; + default: + FATAL("Invalid operand type: %d\n", operand->type); + + } + +} + +static void cmplog_instrument_call(const cs_insn * instr, + GumStalkerIterator *iterator) { + + cs_x86 x86 = instr->detail->x86; + cs_x86_op *operand; + + if (instr->id != X86_INS_CALL) return; + + if (x86.op_count != 1) return; + + operand = &x86.operands[0]; + + if (operand->type == X86_OP_INVALID) return; + if (operand->type == X86_OP_MEM && operand->mem.segment != X86_REG_INVALID) + return; + + gum_stalker_iterator_put_callout(iterator, cmplog_call_callout, NULL, NULL); + +} + +static void cmplog_handle_cmp_sub(GumCpuContext *context, guint64 operand1, + guint64 operand2, uint8_t size) { + + guint64 address = cmplog_read_reg(context, X86_REG_RIP); + + register uintptr_t k = (uintptr_t)address; + + k = (k >> 4) ^ (k << 8); + k &= CMP_MAP_W - 1; + + __afl_cmp_map->headers[k].type = CMP_TYPE_INS; + + u32 hits = __afl_cmp_map->headers[k].hits; + __afl_cmp_map->headers[k].hits = hits + 1; + + __afl_cmp_map->headers[k].shape = (size - 1); + + hits &= CMP_MAP_H - 1; + __afl_cmp_map->log[k][hits].v0 = operand1; + __afl_cmp_map->log[k][hits].v1 = operand2; + +} + +static void cmplog_cmp_sub_callout(GumCpuContext *context, gpointer user_data) { + + cmplog_pair_ctx_t *ctx = (cmplog_pair_ctx_t *)user_data; + + if (ctx->operand1.size != ctx->operand2.size) FATAL("Operand size mismatch"); + + guint64 operand1 = cmplog_get_operand_value(context, &ctx->operand1); + guint64 operand2 = cmplog_get_operand_value(context, &ctx->operand2); + + cmplog_handle_cmp_sub(context, operand1, operand2, ctx->operand1.size); + +} + +static void cmplog_instrument_cmp_sub_put_callout(GumStalkerIterator *iterator, + cs_x86_op * operand1, + cs_x86_op *operand2) { + + cmplog_pair_ctx_t *ctx = g_malloc(sizeof(cmplog_pair_ctx_t)); + if (ctx == NULL) return; + + cmplog_instrument_put_operand(&ctx->operand1, operand1); + cmplog_instrument_put_operand(&ctx->operand2, operand2); + + gum_stalker_iterator_put_callout(iterator, cmplog_cmp_sub_callout, ctx, + g_free); + +} + +static void cmplog_instrument_cmp_sub(const cs_insn * instr, + GumStalkerIterator *iterator) { + + cs_x86 x86 = instr->detail->x86; + cs_x86_op *operand1; + cs_x86_op *operand2; + + switch (instr->id) { + + case X86_INS_CMP: + case X86_INS_SUB: + break; + default: + return; + + } + + if (x86.op_count != 2) return; + + operand1 = &x86.operands[0]; + operand2 = &x86.operands[1]; + + if (operand1->type == X86_OP_INVALID) return; + if (operand2->type == X86_OP_INVALID) return; + + if ((operand1->type == X86_OP_MEM) && + (operand1->mem.segment != X86_REG_INVALID)) + return; + + if ((operand2->type == X86_OP_MEM) && + (operand2->mem.segment != X86_REG_INVALID)) + return; + + cmplog_instrument_cmp_sub_put_callout(iterator, operand1, operand2); + +} + +void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) { + + if (__afl_cmp_map == NULL) return; + + cmplog_instrument_call(instr, iterator); + cmplog_instrument_cmp_sub(instr, iterator); + +} + +#endif + diff --git a/frida_mode/src/cmplog/cmplog_x86.c b/frida_mode/src/cmplog/cmplog_x86.c new file mode 100644 index 00000000..2401180c --- /dev/null +++ b/frida_mode/src/cmplog/cmplog_x86.c @@ -0,0 +1,19 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "frida_cmplog.h" +#include "util.h" + +#if defined(__i386__) +void cmplog_instrument(const cs_insn *instr, GumStalkerIterator *iterator) { + + UNUSED_PARAMETER(instr); + UNUSED_PARAMETER(iterator); + if (__afl_cmp_map == NULL) { return; } + FATAL("CMPLOG mode not supported on this architecture"); + +} + +#endif + diff --git a/frida_mode/src/entry.c b/frida_mode/src/entry.c new file mode 100644 index 00000000..e71386a0 --- /dev/null +++ b/frida_mode/src/entry.c @@ -0,0 +1,50 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "entry.h" +#include "instrument.h" +#include "stalker.h" +#include "util.h" + +extern void __afl_manual_init(); + +guint64 entry_start = 0; + +static void entry_launch(void) { + + __afl_manual_init(); + + /* Child here */ + previous_pc = 0; + +} + +void entry_init(void) { + + entry_start = util_read_address("AFL_ENTRYPOINT"); + OKF("entry_point: 0x%016" G_GINT64_MODIFIER "X", entry_start); + +} + +void entry_run(void) { + + if (entry_start == 0) { entry_launch(); } + +} + +static void entry_callout(GumCpuContext *cpu_context, gpointer user_data) { + + UNUSED_PARAMETER(cpu_context); + UNUSED_PARAMETER(user_data); + entry_launch(); + +} + +void entry_prologue(GumStalkerIterator *iterator, GumStalkerOutput *output) { + + UNUSED_PARAMETER(output); + gum_stalker_iterator_put_callout(iterator, entry_callout, NULL, NULL); + +} + diff --git a/frida_mode/src/instrument.c b/frida_mode/src/instrument.c deleted file mode 100644 index 22910062..00000000 --- a/frida_mode/src/instrument.c +++ /dev/null @@ -1,271 +0,0 @@ -#include "frida-gum.h" -#include "config.h" -#include "debug.h" -#include "prefetch.h" -#include "ranges.h" -#include "unistd.h" - -extern uint8_t *__afl_area_ptr; -extern u32 __afl_map_size; - -uint64_t __thread previous_pc = 0; -GumAddress current_log_impl = GUM_ADDRESS(0); - -static gboolean tracing = false; -static gboolean optimize = false; -static gboolean strict = false; - -#if defined(__x86_64__) -static const guint8 afl_log_code[] = { - - 0x9c, /* pushfq */ - 0x50, /* push rax */ - 0x51, /* push rcx */ - 0x52, /* push rdx */ - - 0x48, 0x8d, 0x05, 0x27, - 0x00, 0x00, 0x00, /* lea rax, sym._afl_area_ptr_ptr */ - 0x48, 0x8b, 0x00, /* mov rax, qword [rax] */ - 0x48, 0x8b, 0x00, /* mov rax, qword [rax] */ - 0x48, 0x8d, 0x0d, 0x22, - 0x00, 0x00, 0x00, /* lea rcx, sym.previous_pc */ - 0x48, 0x8b, 0x11, /* mov rdx, qword [rcx] */ - 0x48, 0x8b, 0x12, /* mov rdx, qword [rdx] */ - 0x48, 0x31, 0xfa, /* xor rdx, rdi */ - 0xfe, 0x04, 0x10, /* inc byte [rax + rdx] */ - 0x48, 0xd1, 0xef, /* shr rdi, 1 */ - 0x48, 0x8b, 0x01, /* mov rax, qword [rcx] */ - 0x48, 0x89, 0x38, /* mov qword [rax], rdi */ - - 0x5a, /* pop rdx */ - 0x59, /* pop rcx */ - 0x58, /* pop rax */ - 0x9d, /* popfq */ - - 0xc3, /* ret */ - - /* Read-only data goes here: */ - /* uint8_t** afl_area_ptr_ptr */ - /* uint64_t* afl_prev_loc_ptr */ - -}; - -void instrument_coverage_optimize(const cs_insn * instr, - GumStalkerOutput *output) { - - guint64 current_pc = instr->address; - guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8); - area_offset &= MAP_SIZE - 1; - GumX86Writer *cw = output->writer.x86; - - if (current_log_impl == 0 || - !gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) || - !gum_x86_writer_can_branch_directly_between(cw->pc + 128, - current_log_impl)) { - - gconstpointer after_log_impl = cw->code + 1; - - gum_x86_writer_put_jmp_near_label(cw, after_log_impl); - - current_log_impl = cw->pc; - gum_x86_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code)); - - uint8_t **afl_area_ptr_ptr = &__afl_area_ptr; - uint64_t *afl_prev_loc_ptr = &previous_pc; - gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr, - sizeof(afl_area_ptr_ptr)); - gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr, - sizeof(afl_prev_loc_ptr)); - - gum_x86_writer_put_label(cw, after_log_impl); - - } - - gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, - -GUM_RED_ZONE_SIZE); - gum_x86_writer_put_push_reg(cw, GUM_REG_RDI); - gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDI, area_offset); - gum_x86_writer_put_call_address(cw, current_log_impl); - gum_x86_writer_put_pop_reg(cw, GUM_REG_RDI); - gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, - GUM_RED_ZONE_SIZE); - -} - -#elif defined(__aarch64__) -static const guint8 afl_log_code[] = { - - // __afl_area_ptr[current_pc ^ previous_pc]++; - // previous_pc = current_pc >> 1; - 0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]! - 0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]! - - // x0 = current_pc - 0xc1, 0x01, 0x00, 0x58, // ldr x1, #0x38, =&__afl_area_ptr - 0x21, 0x00, 0x40, 0xf9, // ldr x1, [x1] (=__afl_area_ptr) - - 0xc2, 0x01, 0x00, 0x58, // ldr x2, #0x38, =&previous_pc - 0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2] (=previous_pc) - - // __afl_area_ptr[current_pc ^ previous_pc]++; - 0x42, 0x00, 0x00, 0xca, // eor x2, x2, x0 - 0x23, 0x68, 0x62, 0xf8, // ldr x3, [x1, x2] - 0x63, 0x04, 0x00, 0x91, // add x3, x3, #1 - 0x23, 0x68, 0x22, 0xf8, // str x3, [x1, x2] - - // previous_pc = current_pc >> 1; - 0xe0, 0x07, 0x40, 0x8b, // add x0, xzr, x0, LSR #1 - 0xe2, 0x00, 0x00, 0x58, // ldr x2, #0x1c, =&previous_pc - 0x40, 0x00, 0x00, 0xf9, // str x0, [x2] - - 0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10 - 0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10 - 0xC0, 0x03, 0x5F, 0xD6, // ret - - // &afl_area_ptr_ptr - // &afl_prev_loc_ptr - -}; - -void instrument_coverage_optimize(const cs_insn * instr, - GumStalkerOutput *output) { - - guint64 current_pc = instr->address; - guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8); - area_offset &= MAP_SIZE - 1; - GumArm64Writer *cw = output->writer.arm64; - - if (current_log_impl == 0 || - !gum_arm64_writer_can_branch_directly_between(cw, cw->pc, - current_log_impl) || - !gum_arm64_writer_can_branch_directly_between(cw, cw->pc + 128, - current_log_impl)) { - - gconstpointer after_log_impl = cw->code + 1; - - gum_arm64_writer_put_b_label(cw, after_log_impl); - - current_log_impl = cw->pc; - gum_arm64_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code)); - - uint8_t **afl_area_ptr_ptr = &__afl_area_ptr; - uint64_t *afl_prev_loc_ptr = &previous_pc; - gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr, - sizeof(afl_area_ptr_ptr)); - gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr, - sizeof(afl_prev_loc_ptr)); - - gum_arm64_writer_put_label(cw, after_log_impl); - - } - - gum_arm64_writer_put_stp_reg_reg_reg_offset( - cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, -(16 + GUM_RED_ZONE_SIZE), - GUM_INDEX_PRE_ADJUST); - gum_arm64_writer_put_ldr_reg_u64(cw, ARM64_REG_X0, area_offset); - gum_arm64_writer_put_bl_imm(cw, current_log_impl); - gum_arm64_writer_put_ldp_reg_reg_reg_offset( - cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE, - GUM_INDEX_POST_ADJUST); - -} - -#endif - -static void on_basic_block(GumCpuContext *context, gpointer user_data) { - - /* - * This function is performance critical as it is called to instrument every - * basic block. By moving our print buffer to a global, we avoid it affecting - * the critical path with additional stack adjustments if tracing is not - * enabled. If tracing is enabled, then we're printing a load of diagnostic - * information so this overhead is unlikely to be noticeable. - */ - static char buffer[200]; - int len; - guint64 current_pc = (guint64)user_data; - if (tracing) { - - /* Avoid any functions which may cause an allocation since the target app - * may already be running inside malloc and it isn't designed to be - * re-entrant on a single thread */ - len = snprintf(buffer, sizeof(buffer), - "current_pc: 0x%016" G_GINT64_MODIFIER - "x, previous_pc: 0x%016" G_GINT64_MODIFIER "x\n", - current_pc, previous_pc); - - write(STDOUT_FILENO, buffer, len + 1); - - } - - current_pc = (current_pc >> 4) ^ (current_pc << 8); - current_pc &= MAP_SIZE - 1; - - __afl_area_ptr[current_pc ^ previous_pc]++; - previous_pc = current_pc >> 1; - -} - -void instr_basic_block(GumStalkerIterator *iterator, GumStalkerOutput *output, - gpointer user_data) { - - const cs_insn *instr; - gboolean begin = TRUE; - while (gum_stalker_iterator_next(iterator, &instr)) { - - if (begin) { - - prefetch_write((void *)instr->address); - if (!strict || !range_is_excluded((void *)instr->address)) { - - if (optimize) { - - instrument_coverage_optimize(instr, output); - - } else { - - gum_stalker_iterator_put_callout(iterator, on_basic_block, - (gpointer)instr->address, NULL); - - } - - } - - begin = FALSE; - - } - - gum_stalker_iterator_keep(iterator); - - } - -} - -void instrument_init() { - - optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL); - tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL); - strict = (getenv("AFL_FRIDA_INST_STRICT") != NULL); - -#if !defined(__x86_64__) && !defined(__aarch64__) - optimize = false; -#endif - - OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' '); - OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' '); - OKF("Instrumentation - strict [%c]", strict ? 'X' : ' '); - - if (tracing && optimize) { - - FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible"); - - } - - if (__afl_map_size != 0x10000) { - - FATAL("Bad map size: 0x%08x", __afl_map_size); - - } - -} - diff --git a/frida_mode/src/instrument/instrument.c b/frida_mode/src/instrument/instrument.c new file mode 100644 index 00000000..971f80c0 --- /dev/null +++ b/frida_mode/src/instrument/instrument.c @@ -0,0 +1,155 @@ +#include <unistd.h> + +#include "frida-gum.h" + +#include "config.h" +#include "debug.h" + +#include "entry.h" +#include "frida_cmplog.h" +#include "instrument.h" +#include "persistent.h" +#include "prefetch.h" +#include "ranges.h" +#include "stalker.h" +#include "util.h" + +static gboolean tracing = false; +static gboolean optimize = false; +static GumStalkerTransformer *transformer = NULL; + +__thread uint64_t previous_pc = 0; + +__attribute__((hot)) static void on_basic_block(GumCpuContext *context, + gpointer user_data) { + + UNUSED_PARAMETER(context); + /* + * This function is performance critical as it is called to instrument every + * basic block. By moving our print buffer to a global, we avoid it affecting + * the critical path with additional stack adjustments if tracing is not + * enabled. If tracing is enabled, then we're printing a load of diagnostic + * information so this overhead is unlikely to be noticeable. + */ + static char buffer[200]; + int len; + guint64 current_pc = (guint64)user_data; + uint8_t * cursor; + uint64_t value; + if (unlikely(tracing)) { + + /* Avoid any functions which may cause an allocation since the target app + * may already be running inside malloc and it isn't designed to be + * re-entrant on a single thread */ + len = snprintf(buffer, sizeof(buffer), + "current_pc: 0x%016" G_GINT64_MODIFIER + "x, previous_pc: 0x%016" G_GINT64_MODIFIER "x\n", + current_pc, previous_pc); + + IGNORED_RERURN(write(STDOUT_FILENO, buffer, len + 1)); + + } + + current_pc = (current_pc >> 4) ^ (current_pc << 8); + current_pc &= MAP_SIZE - 1; + + cursor = &__afl_area_ptr[current_pc ^ previous_pc]; + value = *cursor; + + if (value == 0xff) { + + value = 1; + + } else { + + value++; + + } + + *cursor = value; + previous_pc = current_pc >> 1; + +} + +static void instr_basic_block(GumStalkerIterator *iterator, + GumStalkerOutput *output, gpointer user_data) { + + UNUSED_PARAMETER(user_data); + + const cs_insn *instr; + gboolean begin = TRUE; + while (gum_stalker_iterator_next(iterator, &instr)) { + + if (instr->address == entry_start) { entry_prologue(iterator, output); } + if (instr->address == persistent_start) { persistent_prologue(output); } + + if (begin) { + + prefetch_write((void *)instr->address); + if (!range_is_excluded((void *)instr->address)) { + + if (optimize) { + + instrument_coverage_optimize(instr, output); + + } else { + + gum_stalker_iterator_put_callout(iterator, on_basic_block, + (gpointer)instr->address, NULL); + + } + + } + + begin = FALSE; + + } + + if (!range_is_excluded((void *)instr->address)) { + + cmplog_instrument(instr, iterator); + + } + + gum_stalker_iterator_keep(iterator); + + } + +} + +void instrument_init(void) { + + optimize = (getenv("AFL_FRIDA_INST_NO_OPTIMIZE") == NULL); + tracing = (getenv("AFL_FRIDA_INST_TRACE") != NULL); + + if (!instrument_is_coverage_optimize_supported()) optimize = false; + + OKF("Instrumentation - optimize [%c]", optimize ? 'X' : ' '); + OKF("Instrumentation - tracing [%c]", tracing ? 'X' : ' '); + + if (tracing && optimize) { + + FATAL("AFL_FRIDA_INST_OPTIMIZE and AFL_FRIDA_INST_TRACE are incompatible"); + + } + + if (__afl_map_size != 0x10000) { + + FATAL("Bad map size: 0x%08x", __afl_map_size); + + } + + transformer = + gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL); + + cmplog_init(); + +} + +GumStalkerTransformer *instrument_get_transformer(void) { + + if (transformer == NULL) { FATAL("Instrumentation not initialized"); } + return transformer; + +} + diff --git a/frida_mode/src/instrument/instrument_arm32.c b/frida_mode/src/instrument/instrument_arm32.c new file mode 100644 index 00000000..c2d720a7 --- /dev/null +++ b/frida_mode/src/instrument/instrument_arm32.c @@ -0,0 +1,23 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "instrument.h" + +#if defined(__arm__) + +gboolean instrument_is_coverage_optimize_supported(void) { + + return false; + +} + +void instrument_coverage_optimize(const cs_insn * instr, + GumStalkerOutput *output) { + + FATAL("Optimized coverage not supported on this architecture"); + +} + +#endif + diff --git a/frida_mode/src/instrument/instrument_arm64.c b/frida_mode/src/instrument/instrument_arm64.c new file mode 100644 index 00000000..fa3afb48 --- /dev/null +++ b/frida_mode/src/instrument/instrument_arm64.c @@ -0,0 +1,97 @@ +#include "frida-gum.h" + +#include "config.h" +#include "debug.h" + +#include "instrument.h" + +#if defined(__aarch64__) + +static GumAddress current_log_impl = GUM_ADDRESS(0); + +static const guint8 afl_log_code[] = { + + // __afl_area_ptr[current_pc ^ previous_pc]++; + // previous_pc = current_pc >> 1; + 0xE1, 0x0B, 0xBF, 0xA9, // stp x1, x2, [sp, -0x10]! + 0xE3, 0x13, 0xBF, 0xA9, // stp x3, x4, [sp, -0x10]! + + // x0 = current_pc + 0xe1, 0x01, 0x00, 0x58, // ldr x1, #0x3c, =&__afl_area_ptr + 0x21, 0x00, 0x40, 0xf9, // ldr x1, [x1] (=__afl_area_ptr) + + 0xe2, 0x01, 0x00, 0x58, // ldr x2, #0x3c, =&previous_pc + 0x42, 0x00, 0x40, 0xf9, // ldr x2, [x2] (=previous_pc) + + // __afl_area_ptr[current_pc ^ previous_pc]++; + 0x42, 0x00, 0x00, 0xca, // eor x2, x2, x0 + 0x23, 0x68, 0x62, 0xf8, // ldr x3, [x1, x2] + 0x63, 0x04, 0x00, 0x91, // add x3, x3, #1 + 0x63, 0x00, 0x1f, 0x9a, // adc x3, x3, xzr + 0x23, 0x68, 0x22, 0xf8, // str x3, [x1, x2] + + // previous_pc = current_pc >> 1; + 0xe0, 0x07, 0x40, 0x8b, // add x0, xzr, x0, LSR #1 + 0xe2, 0x00, 0x00, 0x58, // ldr x2, #0x1c, =&previous_pc + 0x40, 0x00, 0x00, 0xf9, // str x0, [x2] + + 0xE3, 0x13, 0xc1, 0xA8, // ldp x3, x4, [sp], #0x10 + 0xE1, 0x0B, 0xc1, 0xA8, // ldp x1, x2, [sp], #0x10 + 0xC0, 0x03, 0x5F, 0xD6, // ret + + // &afl_area_ptr_ptr + // &afl_prev_loc_ptr + +}; + +gboolean instrument_is_coverage_optimize_supported(void) { + + return true; + +} + +void instrument_coverage_optimize(const cs_insn * instr, + GumStalkerOutput *output) { + + guint64 current_pc = instr->address; + guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8); + area_offset &= MAP_SIZE - 1; + GumArm64Writer *cw = output->writer.arm64; + + if (current_log_impl == 0 || + !gum_arm64_writer_can_branch_directly_between(cw, cw->pc, + current_log_impl) || + !gum_arm64_writer_can_branch_directly_between(cw, cw->pc + 128, + current_log_impl)) { + + gconstpointer after_log_impl = cw->code + 1; + + gum_arm64_writer_put_b_label(cw, after_log_impl); + + current_log_impl = cw->pc; + gum_arm64_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code)); + + uint8_t **afl_area_ptr_ptr = &__afl_area_ptr; + uint64_t *afl_prev_loc_ptr = &previous_pc; + gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_area_ptr_ptr, + sizeof(afl_area_ptr_ptr)); + gum_arm64_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr, + sizeof(afl_prev_loc_ptr)); + + gum_arm64_writer_put_label(cw, after_log_impl); + + } + + gum_arm64_writer_put_stp_reg_reg_reg_offset( + cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, -(16 + GUM_RED_ZONE_SIZE), + GUM_INDEX_PRE_ADJUST); + gum_arm64_writer_put_ldr_reg_u64(cw, ARM64_REG_X0, area_offset); + gum_arm64_writer_put_bl_imm(cw, current_log_impl); + gum_arm64_writer_put_ldp_reg_reg_reg_offset( + cw, ARM64_REG_LR, ARM64_REG_X0, ARM64_REG_SP, 16 + GUM_RED_ZONE_SIZE, + GUM_INDEX_POST_ADJUST); + +} + +#endif + diff --git a/frida_mode/src/instrument/instrument_x64.c b/frida_mode/src/instrument/instrument_x64.c new file mode 100644 index 00000000..901f3bd0 --- /dev/null +++ b/frida_mode/src/instrument/instrument_x64.c @@ -0,0 +1,93 @@ +#include "frida-gum.h" + +#include "config.h" + +#include "instrument.h" + +#if defined(__x86_64__) + +static GumAddress current_log_impl = GUM_ADDRESS(0); + +static const guint8 afl_log_code[] = { + + // 0xcc, + + 0x9c, /* pushfq */ + 0x51, /* push rcx */ + 0x52, /* push rdx */ + + 0x48, 0x8b, 0x0d, 0x28, + 0x00, 0x00, 0x00, /* mov rcx, sym.&previous_pc */ + 0x48, 0x8b, 0x11, /* mov rdx, qword [rcx] */ + 0x48, 0x31, 0xfa, /* xor rdx, rdi */ + + 0x48, 0x03, 0x15, 0x13, + 0x00, 0x00, 0x00, /* add rdx, sym._afl_area_ptr_ptr */ + + 0x80, 0x02, 0x01, /* add byte ptr [rdx], 1 */ + 0x80, 0x12, 0x00, /* adc byte ptr [rdx], 0 */ + 0x48, 0xd1, 0xef, /* shr rdi, 1 */ + 0x48, 0x89, 0x39, /* mov qword [rcx], rdi */ + + 0x5a, /* pop rdx */ + 0x59, /* pop rcx */ + 0x9d, /* popfq */ + + 0xc3, /* ret */ + 0x90, 0x90, 0x90 /* nop pad */ + + /* Read-only data goes here: */ + /* uint8_t* __afl_area_ptr */ + /* uint64_t* &previous_pc */ + +}; + +gboolean instrument_is_coverage_optimize_supported(void) { + + return true; + +} + +void instrument_coverage_optimize(const cs_insn * instr, + GumStalkerOutput *output) { + + guint64 current_pc = instr->address; + guint64 area_offset = (current_pc >> 4) ^ (current_pc << 8); + area_offset &= MAP_SIZE - 1; + GumX86Writer *cw = output->writer.x86; + + if (current_log_impl == 0 || + !gum_x86_writer_can_branch_directly_between(cw->pc, current_log_impl) || + !gum_x86_writer_can_branch_directly_between(cw->pc + 128, + current_log_impl)) { + + gconstpointer after_log_impl = cw->code + 1; + + gum_x86_writer_put_jmp_near_label(cw, after_log_impl); + + current_log_impl = cw->pc; + gum_x86_writer_put_bytes(cw, afl_log_code, sizeof(afl_log_code)); + + uint64_t *afl_prev_loc_ptr = &previous_pc; + gum_x86_writer_put_bytes(cw, (const guint8 *)&__afl_area_ptr, + sizeof(__afl_area_ptr)); + gum_x86_writer_put_bytes(cw, (const guint8 *)&afl_prev_loc_ptr, + sizeof(afl_prev_loc_ptr)); + + gum_x86_writer_put_label(cw, after_log_impl); + + } + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + -GUM_RED_ZONE_SIZE); + gum_x86_writer_put_push_reg(cw, GUM_REG_RDI); + gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RDI, area_offset); + gum_x86_writer_put_call_address(cw, current_log_impl); + gum_x86_writer_put_pop_reg(cw, GUM_REG_RDI); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + GUM_RED_ZONE_SIZE); + +} + +#endif + diff --git a/frida_mode/src/instrument/instrument_x86.c b/frida_mode/src/instrument/instrument_x86.c new file mode 100644 index 00000000..5b8cbbba --- /dev/null +++ b/frida_mode/src/instrument/instrument_x86.c @@ -0,0 +1,23 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "instrument.h" + +#if defined(__i386__) + +gboolean instrument_is_coverage_optimize_supported(void) { + + return false; + +} + +void instrument_coverage_optimize(const cs_insn * instr, + GumStalkerOutput *output) { + + FATAL("Optimized coverage not supported on this architecture"); + +} + +#endif + diff --git a/frida_mode/src/interceptor.c b/frida_mode/src/interceptor.c index ba05a80a..d2802752 100644 --- a/frida_mode/src/interceptor.c +++ b/frida_mode/src/interceptor.c @@ -1,4 +1,5 @@ #include "frida-gum.h" + #include "debug.h" #include "interceptor.h" @@ -9,8 +10,26 @@ void intercept(void *address, gpointer replacement, gpointer user_data) { gum_interceptor_begin_transaction(interceptor); GumReplaceReturn ret = gum_interceptor_replace(interceptor, address, replacement, user_data); - if (ret != GUM_ATTACH_OK) { FATAL("gum_interceptor_attach: %d", ret); } + if (ret != GUM_REPLACE_OK) { FATAL("gum_interceptor_attach: %d", ret); } gum_interceptor_end_transaction(interceptor); } +void unintercept(void *address) { + + GumInterceptor *interceptor = gum_interceptor_obtain(); + + gum_interceptor_begin_transaction(interceptor); + gum_interceptor_revert(interceptor, address); + gum_interceptor_end_transaction(interceptor); + gum_interceptor_flush(interceptor); + +} + +void unintercept_self(void) { + + GumInvocationContext *ctx = gum_interceptor_get_current_invocation(); + unintercept(ctx->function); + +} + diff --git a/frida_mode/src/lib/lib.c b/frida_mode/src/lib/lib.c new file mode 100644 index 00000000..c5045533 --- /dev/null +++ b/frida_mode/src/lib/lib.c @@ -0,0 +1,176 @@ +#ifndef __APPLE__ + #include <elf.h> + #include <fcntl.h> + #include <limits.h> + #include <stdio.h> + #include <sys/mman.h> + #include <unistd.h> + + #include "frida-gum.h" + + #include "debug.h" + + #include "lib.h" + + #if defined(__arm__) || defined(__i386__) + #define ELFCLASS ELFCLASS32 +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; +typedef Elf32_Shdr Elf_Shdr; +typedef Elf32_Addr Elf_Addr; + #elif defined(__aarch64__) || defined(__x86_64__) + #define ELFCLASS ELFCLASS64 +typedef Elf64_Ehdr Elf_Ehdr; +typedef Elf64_Phdr Elf_Phdr; +typedef Elf64_Shdr Elf_Shdr; +typedef Elf64_Addr Elf_Addr; + #else + #error "Unsupported platform" + #endif + +typedef struct { + + gchar name[PATH_MAX + 1]; + gchar path[PATH_MAX + 1]; + GumAddress base_address; + gsize size; + +} lib_details_t; + +static guint64 text_base = 0; +static guint64 text_limit = 0; + +static gboolean lib_find_exe(const GumModuleDetails *details, + gpointer user_data) { + + lib_details_t *lib_details = (lib_details_t *)user_data; + + memcpy(lib_details->name, details->name, PATH_MAX); + memcpy(lib_details->path, details->path, PATH_MAX); + lib_details->base_address = details->range->base_address; + lib_details->size = details->range->size; + return FALSE; + +} + +static void lib_validate_hdr(Elf_Ehdr *hdr) { + + if (hdr->e_ident[0] != ELFMAG0) FATAL("Invalid e_ident[0]"); + if (hdr->e_ident[1] != ELFMAG1) FATAL("Invalid e_ident[1]"); + if (hdr->e_ident[2] != ELFMAG2) FATAL("Invalid e_ident[2]"); + if (hdr->e_ident[3] != ELFMAG3) FATAL("Invalid e_ident[3]"); + if (hdr->e_ident[4] != ELFCLASS) FATAL("Invalid class"); + +} + +static void lib_read_text_section(lib_details_t *lib_details, Elf_Ehdr *hdr) { + + Elf_Phdr *phdr; + gboolean found_preferred_base = FALSE; + Elf_Addr preferred_base; + Elf_Shdr *shdr; + Elf_Shdr *shstrtab; + char * shstr; + char * section_name; + Elf_Shdr *curr; + char text_name[] = ".text"; + + phdr = (Elf_Phdr *)((char *)hdr + hdr->e_phoff); + for (size_t i = 0; i < hdr->e_phnum; i++) { + + if (phdr[i].p_type == PT_LOAD) { + + preferred_base = phdr[i].p_vaddr; + found_preferred_base = TRUE; + break; + + } + + } + + if (!found_preferred_base) { FATAL("Failed to find preferred load address"); } + + OKF("Image preferred load address 0x%016lx", preferred_base); + + shdr = (Elf_Shdr *)((char *)hdr + hdr->e_shoff); + shstrtab = &shdr[hdr->e_shstrndx]; + shstr = (char *)hdr + shstrtab->sh_offset; + + OKF("shdr: %p", shdr); + OKF("shstrtab: %p", shstrtab); + OKF("shstr: %p", shstr); + + for (size_t i = 0; i < hdr->e_shnum; i++) { + + curr = &shdr[i]; + + if (curr->sh_name == 0) continue; + + section_name = &shstr[curr->sh_name]; + OKF("Section: %2lu - base: 0x%016lX size: 0x%016lX %s", i, curr->sh_addr, + curr->sh_size, section_name); + if (memcmp(section_name, text_name, sizeof(text_name)) == 0 && + text_base == 0) { + + text_base = lib_details->base_address + curr->sh_addr - preferred_base; + text_limit = text_base + curr->sh_size; + OKF("> text_addr: 0x%016lX", text_base); + OKF("> text_limit: 0x%016lX", text_limit); + + } + + } + +} + +static void lib_get_text_section(lib_details_t *details) { + + int fd = -1; + off_t len; + Elf_Ehdr *hdr; + + fd = open(details->path, O_RDONLY); + if (fd < 0) { FATAL("Failed to open %s", details->path); } + + len = lseek(fd, 0, SEEK_END); + + if (len == (off_t)-1) { FATAL("Failed to lseek %s", details->path); } + + OKF("len: %ld", len); + + hdr = (Elf_Ehdr *)mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + if (hdr == MAP_FAILED) { FATAL("Failed to map %s", details->path); } + + lib_validate_hdr(hdr); + lib_read_text_section(details, hdr); + + munmap(hdr, len); + close(fd); + +} + +void lib_init(void) { + + lib_details_t lib_details; + gum_process_enumerate_modules(lib_find_exe, &lib_details); + OKF("Executable: 0x%016lx - %s", lib_details.base_address, lib_details.path); + lib_get_text_section(&lib_details); + +} + +guint64 lib_get_text_base(void) { + + if (text_base == 0) FATAL("Lib not initialized"); + return text_base; + +} + +guint64 lib_get_text_limit(void) { + + if (text_limit == 0) FATAL("Lib not initialized"); + return text_limit; + +} + +#endif + diff --git a/frida_mode/src/lib/lib_apple.c b/frida_mode/src/lib/lib_apple.c new file mode 100644 index 00000000..8f863861 --- /dev/null +++ b/frida_mode/src/lib/lib_apple.c @@ -0,0 +1,82 @@ +#ifdef __APPLE__ + #include "frida-gum.h" + + #include "debug.h" + + #include "lib.h" + #include "util.h" + +extern mach_port_t mach_task_self(); +extern void gum_darwin_enumerate_modules(mach_port_t task, + GumFoundModuleFunc func, + gpointer user_data); + +static guint64 text_base = 0; +static guint64 text_limit = 0; + +static gboolean lib_get_main_module(const GumModuleDetails *details, + gpointer user_data) { + + GumDarwinModule **ret = (GumDarwinModule **)user_data; + GumDarwinModule * module = gum_darwin_module_new_from_memory( + details->path, mach_task_self(), details->range->base_address, + GUM_DARWIN_MODULE_FLAGS_NONE, NULL); + + OKF("Found main module: %s", module->name); + + *ret = module; + + return FALSE; + +} + +gboolean lib_get_text_section(const GumDarwinSectionDetails *details, + gpointer user_data) { + + UNUSED_PARAMETER(user_data); + static size_t idx = 0; + char text_name[] = "__text"; + + OKF("Section: %2lu - base: 0x%016" G_GINT64_MODIFIER + "X size: 0x%016" G_GINT64_MODIFIER "X %s", + idx++, details->vm_address, details->vm_address + details->size, + details->section_name); + + if (memcmp(details->section_name, text_name, sizeof(text_name)) == 0 && + text_base == 0) { + + text_base = details->vm_address; + text_limit = details->vm_address + details->size; + OKF("> text_addr: 0x%016" G_GINT64_MODIFIER "X", text_base); + OKF("> text_limit: 0x%016" G_GINT64_MODIFIER "X", text_limit); + + } + + return TRUE; + +} + +void lib_init(void) { + + GumDarwinModule *module = NULL; + gum_darwin_enumerate_modules(mach_task_self(), lib_get_main_module, &module); + gum_darwin_module_enumerate_sections(module, lib_get_text_section, NULL); + +} + +guint64 lib_get_text_base(void) { + + if (text_base == 0) FATAL("Lib not initialized"); + return text_base; + +} + +guint64 lib_get_text_limit(void) { + + if (text_limit == 0) FATAL("Lib not initialized"); + return text_limit; + +} + +#endif + diff --git a/frida_mode/src/main.c b/frida_mode/src/main.c index 7505c2f9..e031dbed 100644 --- a/frida_mode/src/main.c +++ b/frida_mode/src/main.c @@ -10,13 +10,19 @@ #endif #include "frida-gum.h" + #include "config.h" #include "debug.h" -#include "interceptor.h" +#include "entry.h" #include "instrument.h" +#include "interceptor.h" +#include "lib.h" +#include "persistent.h" #include "prefetch.h" #include "ranges.h" +#include "stalker.h" +#include "util.h" #ifdef __APPLE__ extern mach_port_t mach_task_self(); @@ -30,16 +36,11 @@ extern int __libc_start_main(int *(main)(int, char **, char **), int argc, typedef int *(*main_fn_t)(int argc, char **argv, char **envp); -static main_fn_t main_fn = NULL; -static GumStalker * stalker = NULL; -static GumMemoryRange code_range = {0}; - -extern void __afl_manual_init(); -extern __thread uint64_t previous_pc; +static main_fn_t main_fn = NULL; -static int on_fork() { +static int on_fork(void) { - prefetch_read(stalker); + prefetch_read(); return fork(); } @@ -47,11 +48,17 @@ static int on_fork() { #ifdef __APPLE__ static void on_main_os(int argc, char **argv, char **envp) { + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); + UNUSED_PARAMETER(envp); + } #else static void on_main_os(int argc, char **argv, char **envp) { + UNUSED_PARAMETER(argc); + /* Personality doesn't affect the current process, it only takes effect on * evec */ int persona = personality(ADDR_NO_RANDOMIZE); @@ -70,37 +77,43 @@ static void on_main_os(int argc, char **argv, char **envp) { static int *on_main(int argc, char **argv, char **envp) { - on_main_os(argc, argv, envp); + void *fork_addr; - stalker = gum_stalker_new(); - if (stalker == NULL) { FATAL("Failed to initialize stalker"); } + on_main_os(argc, argv, envp); - gum_stalker_set_trust_threshold(stalker, 0); + unintercept_self(); - GumStalkerTransformer *transformer = - gum_stalker_transformer_make_from_callback(instr_basic_block, NULL, NULL); + stalker_init(); + lib_init(); + entry_init(); instrument_init(); + persistent_init(); prefetch_init(); - ranges_init(stalker); + ranges_init(); - intercept(fork, on_fork, stalker); + fork_addr = GSIZE_TO_POINTER(gum_module_find_export_by_name(NULL, "fork")); + intercept(fork_addr, on_fork, NULL); - gum_stalker_follow_me(stalker, transformer, NULL); - gum_stalker_deactivate(stalker); + stalker_start(); + entry_run(); - __afl_manual_init(); + return main_fn(argc, argv, envp); - /* Child here */ - previous_pc = 0; - prefetch_start(stalker); - main_fn(argc, argv, envp); - _exit(0); +} + +#if defined(EMBEDDED) +extern int *main(int argc, char **argv, char **envp); + +static void intercept_main(void) { + + main_fn = main; + intercept(main, on_main, NULL); } -#ifdef __APPLE__ -static void intercept_main() { +#elif defined(__APPLE__) +static void intercept_main(void) { mach_port_t task = mach_task_self(); OKF("Task Id: %u", task); @@ -119,13 +132,14 @@ static int on_libc_start_main(int *(main)(int, char **, char **), int argc, void(*stack_end)) { main_fn = main; + unintercept_self(); intercept(main, on_main, NULL); return __libc_start_main(main, argc, ubp_av, init, fini, rtld_fini, stack_end); } -static void intercept_main() { +static void intercept_main(void) { intercept(__libc_start_main, on_libc_start_main, NULL); @@ -133,7 +147,7 @@ static void intercept_main() { #endif -__attribute__((constructor)) static void init() { +__attribute__((constructor)) static void init(void) { gum_init_embedded(); if (!gum_stalker_is_supported()) { diff --git a/frida_mode/src/persistent/persistent.c b/frida_mode/src/persistent/persistent.c new file mode 100644 index 00000000..918ff153 --- /dev/null +++ b/frida_mode/src/persistent/persistent.c @@ -0,0 +1,65 @@ +#include <dlfcn.h> + +#include "frida-gum.h" + +#include "config.h" +#include "debug.h" + +#include "persistent.h" +#include "util.h" + +int __afl_sharedmem_fuzzing = 0; +afl_persistent_hook_fn hook = NULL; +guint64 persistent_start = 0; +guint64 persistent_count = 0; + +void persistent_init(void) { + + char *hook_name = getenv("AFL_FRIDA_PERSISTENT_HOOK"); + + persistent_start = util_read_address("AFL_FRIDA_PERSISTENT_ADDR"); + persistent_count = util_read_num("AFL_FRIDA_PERSISTENT_CNT"); + + if (persistent_count != 0 && persistent_start == 0) + FATAL( + "AFL_FRIDA_PERSISTENT_ADDR must be specified if " + "AFL_FRIDA_PERSISTENT_CNT is"); + + if (persistent_start != 0 && persistent_count == 0) persistent_count = 1000; + + if (persistent_count != 0 && persistent_count < 100) + WARNF("Persistent count out of recommended range (<100)"); + + if (persistent_start != 0 && !persistent_is_supported()) + FATAL("Persistent mode not supported on this architecture"); + + 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); + + if (hook_name != NULL) { + + void *hook_obj = dlopen(hook_name, RTLD_NOW); + if (hook_obj == NULL) + FATAL("Failed to load AFL_FRIDA_PERSISTENT_HOOK (%s)", hook_name); + + int (*afl_persistent_hook_init_ptr)(void) = + dlsym(hook_obj, "afl_persistent_hook_init"); + if (afl_persistent_hook_init_ptr == NULL) + FATAL("Failed to find afl_persistent_hook_init in %s", hook_name); + + if (afl_persistent_hook_init_ptr() == 0) + FATAL("afl_persistent_hook_init returned a failure"); + + hook = (afl_persistent_hook_fn)dlsym(hook_obj, "afl_persistent_hook"); + if (hook == NULL) + FATAL("Failed to find afl_persistent_hook in %s", hook_name); + + __afl_sharedmem_fuzzing = 1; + + } + +} + diff --git a/frida_mode/src/persistent/persistent_arm32.c b/frida_mode/src/persistent/persistent_arm32.c new file mode 100644 index 00000000..bc021ff3 --- /dev/null +++ b/frida_mode/src/persistent/persistent_arm32.c @@ -0,0 +1,72 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "persistent.h" +#include "util.h" + +#if defined(__arm__) + +struct arm_regs { + + uint32_t r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10; + + union { + + uint32_t r11; + uint32_t fp; + + }; + + union { + + uint32_t r12; + uint32_t ip; + + }; + + union { + + uint32_t r13; + uint32_t sp; + + }; + + union { + + uint32_t r14; + uint32_t lr; + + }; + + union { + + uint32_t r15; + uint32_t pc; + + }; + + uint32_t cpsr; + + uint8_t vfp_zregs[32][16]; + uint32_t vfp_xregs[16]; + +}; + +typedef struct arm_regs arch_api_regs; + +gboolean persistent_is_supported(void) { + + return false; + +} + +void persistent_prologue(GumStalkerOutput *output) { + + UNUSED_PARAMETER(output); + FATAL("Persistent mode not supported on this architecture"); + +} + +#endif + diff --git a/frida_mode/src/persistent/persistent_arm64.c b/frida_mode/src/persistent/persistent_arm64.c new file mode 100644 index 00000000..c198da69 --- /dev/null +++ b/frida_mode/src/persistent/persistent_arm64.c @@ -0,0 +1,115 @@ +#include "frida-gum.h" + +#include "config.h" +#include "debug.h" + +#include "instrument.h" +#include "util.h" + +#if 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]; + +}; + +typedef struct arm64_regs arch_api_regs; + +gboolean persistent_is_supported(void) { + + return false; + +} + +void persistent_prologue(GumStalkerOutput *output) { + + UNUSED_PARAMETER(output); + FATAL("Persistent mode not supported on this architecture"); + +} + +#endif + diff --git a/frida_mode/src/persistent/persistent_x64.c b/frida_mode/src/persistent/persistent_x64.c new file mode 100644 index 00000000..49f1988c --- /dev/null +++ b/frida_mode/src/persistent/persistent_x64.c @@ -0,0 +1,342 @@ +#include "frida-gum.h" + +#include "config.h" + +#include "instrument.h" +#include "persistent.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]; + +}; + +typedef struct x86_64_regs arch_api_regs; + +static arch_api_regs saved_regs = {0}; +static void * saved_return = NULL; + +gboolean persistent_is_supported(void) { + + return true; + +} + +static void instrument_persitent_save_regs(GumX86Writer * cw, + struct x86_64_regs *regs) { + + GumAddress regs_address = GUM_ADDRESS(regs); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + -(GUM_RED_ZONE_SIZE)); + + /* Should be pushing FPU here, but meh */ + gum_x86_writer_put_pushfx(cw); + gum_x86_writer_put_push_reg(cw, GUM_REG_RAX); + + 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); + + /* 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); + + /* Store adjusted RSP */ + gum_x86_writer_put_mov_reg_reg(cw, GUM_REG_RBX, GUM_REG_RSP); + + /* RED_ZONE + Saved flags, RAX, alignment */ + gum_x86_writer_put_add_reg_imm(cw, GUM_REG_RBX, + GUM_RED_ZONE_SIZE + (0x8 * 3)); + gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, (0x8 * 16), + 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); + + /* 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); + + /* Pop the saved values */ + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, 0x10); + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + (GUM_RED_ZONE_SIZE)); + +} + +static void instrument_persitent_restore_regs(GumX86Writer * cw, + struct x86_64_regs *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)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RAX, + (0x8 * 3)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDI, GUM_REG_RAX, + (0x8 * 4)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RSI, GUM_REG_RAX, + (0x8 * 5)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBP, GUM_REG_RAX, + (0x8 * 6)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R8, GUM_REG_RAX, + (0x8 * 7)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R9, GUM_REG_RAX, + (0x8 * 8)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R10, GUM_REG_RAX, + (0x8 * 9)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R11, GUM_REG_RAX, + (0x8 * 10)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R12, GUM_REG_RAX, + (0x8 * 11)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R13, GUM_REG_RAX, + (0x8 * 12)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R14, GUM_REG_RAX, + (0x8 * 13)); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_R15, GUM_REG_RAX, + (0x8 * 14)); + + /* Don't restore RIP or 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)); + 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)); + 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)); + gum_x86_writer_put_push_reg(cw, GUM_REG_RBX); + + gum_x86_writer_put_popfx(cw); + gum_x86_writer_put_pop_reg(cw, GUM_REG_RAX); + gum_x86_writer_put_pop_reg(cw, GUM_REG_RBX); + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + (GUM_RED_ZONE_SIZE)); + +} + +static void instrument_save_ret(GumX86Writer *cw, void **saved_return_ptr) { + + GumAddress saved_return_address = GUM_ADDRESS(saved_return_ptr); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + -(GUM_RED_ZONE_SIZE)); + gum_x86_writer_put_push_reg(cw, GUM_REG_RAX); + gum_x86_writer_put_push_reg(cw, GUM_REG_RBX); + + gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, saved_return_address); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RBX, GUM_REG_RSP, + GUM_RED_ZONE_SIZE + 0x10); + gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RAX, 0, GUM_REG_RBX); + + gum_x86_writer_put_pop_reg(cw, GUM_REG_RBX); + gum_x86_writer_put_pop_reg(cw, GUM_REG_RAX); + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + (GUM_RED_ZONE_SIZE)); + +} + +static void instrument_jump_ret(GumX86Writer *cw, void **saved_return_ptr) { + + GumAddress saved_return_address = GUM_ADDRESS(saved_return_ptr); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + -(GUM_RED_ZONE_SIZE)); + + /* Place holder for ret */ + gum_x86_writer_put_push_reg(cw, GUM_REG_RAX); + gum_x86_writer_put_push_reg(cw, GUM_REG_RAX); + + gum_x86_writer_put_mov_reg_address(cw, GUM_REG_RAX, saved_return_address); + gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RAX, GUM_REG_RAX, 0); + + gum_x86_writer_put_mov_reg_offset_ptr_reg(cw, GUM_REG_RSP, 0x8, GUM_REG_RAX); + gum_x86_writer_put_pop_reg(cw, GUM_REG_RAX); + gum_x86_writer_put_ret_imm(cw, GUM_RED_ZONE_SIZE); + +} + +static int instrument_afl_persistent_loop_func(void) { + + int ret = __afl_persistent_loop(persistent_count); + previous_pc = 0; + return ret; + +} + +static void instrument_afl_persistent_loop(GumX86Writer *cw) { + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + -(GUM_RED_ZONE_SIZE)); + gum_x86_writer_put_call_address_with_arguments( + cw, GUM_CALL_CAPI, GUM_ADDRESS(instrument_afl_persistent_loop_func), 0); + gum_x86_writer_put_test_reg_reg(cw, GUM_REG_RAX, GUM_REG_RAX); + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + (GUM_RED_ZONE_SIZE)); + +} + +static void persistent_prologue_hook(GumX86Writer * cw, + struct x86_64_regs *regs) { + + if (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_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_REG_RDX, GUM_REG_RDX, 0); + + gum_x86_writer_put_call_address_with_arguments( + cw, GUM_CALL_CAPI, GUM_ADDRESS(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); + + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, + (GUM_RED_ZONE_SIZE)); + +} + +void persistent_prologue(GumStalkerOutput *output) { + + /* + * SAVE REGS + * SAVE RET + * POP RET + * loop: + * CALL instrument_afl_persistent_loop + * TEST EAX, EAX + * JZ end: + * call hook (optionally) + * RESTORE REGS + * call original + * jmp loop: + * + * end: + * JMP SAVED RET + * + * original: + * INSTRUMENTED PERSISTENT FUNC + */ + + GumX86Writer *cw = output->writer.x86; + + gconstpointer loop = cw->code + 1; + // gum_x86_writer_put_breakpoint(cw); + + /* Stack must be 16-byte aligned per ABI */ + instrument_persitent_save_regs(cw, &saved_regs); + + /* Stash and pop the return value */ + instrument_save_ret(cw, &saved_return); + gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_REG_RSP, GUM_REG_RSP, (8)); + + /* loop: */ + gum_x86_writer_put_label(cw, loop); + + /* call instrument_prologue_func */ + instrument_afl_persistent_loop(cw); + + /* jz done */ + gconstpointer done = cw->code + 1; + gum_x86_writer_put_jcc_near_label(cw, X86_INS_JE, done, GUM_UNLIKELY); + + /* Optionally call the persistent hook */ + persistent_prologue_hook(cw, &saved_regs); + + instrument_persitent_restore_regs(cw, &saved_regs); + gconstpointer original = cw->code + 1; + /* call original */ + gum_x86_writer_put_call_near_label(cw, original); + /* jmp loop */ + gum_x86_writer_put_jmp_near_label(cw, loop); + + /* done: */ + gum_x86_writer_put_label(cw, done); + + instrument_jump_ret(cw, &saved_return); + + /* original: */ + gum_x86_writer_put_label(cw, original); + + gum_x86_writer_flush(cw); + +} + +#endif + diff --git a/frida_mode/src/persistent/persistent_x86.c b/frida_mode/src/persistent/persistent_x86.c new file mode 100644 index 00000000..9d39c4e9 --- /dev/null +++ b/frida_mode/src/persistent/persistent_x86.c @@ -0,0 +1,55 @@ +#include "frida-gum.h" + +#include "debug.h" + +#include "persistent.h" +#include "util.h" + +#if 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]; + +}; + +typedef struct x86_regs arch_api_regs; + +gboolean persistent_is_supported(void) { + + return false; + +} + +void persistent_prologue(GumStalkerOutput *output) { + + UNUSED_PARAMETER(output); + FATAL("Persistent mode not supported on this architecture"); + +} + +#endif + diff --git a/frida_mode/src/prefetch.c b/frida_mode/src/prefetch.c index 64633c1c..65c09fba 100644 --- a/frida_mode/src/prefetch.c +++ b/frida_mode/src/prefetch.c @@ -3,9 +3,12 @@ #include <sys/mman.h> #include "frida-gum.h" -#include "prefetch.h" + #include "debug.h" +#include "prefetch.h" +#include "stalker.h" + #define TRUST 0 #define PREFETCH_SIZE 65536 #define PREFETCH_ENTRIES ((PREFETCH_SIZE - sizeof(size_t)) / sizeof(void *)) @@ -49,8 +52,9 @@ void prefetch_write(void *addr) { /* * Read the IPC region one block at the time and prefetch it */ -void prefetch_read(GumStalker *stalker) { +void prefetch_read(void) { + GumStalker *stalker = stalker_get(); if (prefetch_data == NULL) return; for (size_t i = 0; i < prefetch_data->count; i++) { @@ -68,7 +72,7 @@ void prefetch_read(GumStalker *stalker) { } -void prefetch_init() { +void prefetch_init(void) { g_assert_cmpint(sizeof(prefetch_data_t), ==, PREFETCH_SIZE); gboolean prefetch = (getenv("AFL_FRIDA_INST_NO_PREFETCH") == NULL); @@ -106,16 +110,3 @@ void prefetch_init() { } -__attribute__((noinline)) static void prefetch_activation() { - - asm volatile(""); - -} - -void prefetch_start(GumStalker *stalker) { - - gum_stalker_activate(stalker, prefetch_activation); - prefetch_activation(); - -} - diff --git a/frida_mode/src/ranges.c b/frida_mode/src/ranges.c index 49ef5a62..e3f09f9e 100644 --- a/frida_mode/src/ranges.c +++ b/frida_mode/src/ranges.c @@ -1,9 +1,12 @@ -// 0x123-0x321 -// module.so +#include "frida-gum.h" -#include "ranges.h" #include "debug.h" +#include "lib.h" +#include "ranges.h" +#include "stalker.h" +#include "util.h" + #define MAX_RANGES 20 typedef struct { @@ -14,15 +17,11 @@ typedef struct { } convert_name_ctx_t; -typedef struct { - - GumStalker *stalker; - GArray * array; - -} include_range_ctx_t; - -GArray * ranges = NULL; -gboolean exclude_ranges = false; +GArray *module_ranges = NULL; +GArray *libs_ranges = NULL; +GArray *include_ranges = NULL; +GArray *exclude_ranges = NULL; +GArray *ranges = NULL; static void convert_address_token(gchar *token, GumMemoryRange *range) { @@ -159,236 +158,417 @@ static void convert_token(gchar *token, GumMemoryRange *range) { } -static gboolean include_ranges(const GumRangeDetails *details, - gpointer user_data) { +gint range_sort(gconstpointer a, gconstpointer b) { - include_range_ctx_t *ctx = (include_range_ctx_t *)user_data; - GArray * array = (GArray *)ctx->array; - GumAddress base = details->range->base_address; - GumAddress limit = details->range->base_address + details->range->size; + return ((GumMemoryRange *)a)->base_address - + ((GumMemoryRange *)b)->base_address; - OKF("Range for inclusion 0x%016" G_GINT64_MODIFIER - "x-0x%016" G_GINT64_MODIFIER "x", - base, limit); +} - for (int i = 0; i < array->len; i++) { +static gboolean print_ranges_callback(const GumRangeDetails *details, + gpointer user_data) { - GumMemoryRange *range = &g_array_index(array, GumMemoryRange, i); - GumAddress range_base = range->base_address; - GumAddress range_limit = range->base_address + range->size; + UNUSED_PARAMETER(user_data); + if (details->file == NULL) { - /* Before the region */ - if (range_limit < base) { continue; } + OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER "X", + details->range->base_address, + details->range->base_address + details->range->size); - /* After the region */ - if (range_base > limit) { + } else { - GumMemoryRange exclude = {.base_address = base, .size = limit - base}; - OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER - "x", - base, limit); - gum_stalker_exclude(ctx->stalker, &exclude); - return true; + OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER + "X %s(0x%016" G_GINT64_MODIFIER "x)", + details->range->base_address, + details->range->base_address + details->range->size, + details->file->path, details->file->offset); - } + } - /* Overlap the start of the region */ - if (range_base < base) { + return true; - /* Range contains the region */ - if (range_limit > limit) { +} - return true; +static void print_ranges(char *key, GArray *ranges) { - } else { + OKF("Range: %s Length: %d", key, ranges->len); + for (guint i = 0; i < ranges->len; i++) { - base = range_limit; - continue; + GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i); + GumAddress curr_limit = curr->base_address + curr->size; + OKF("Range: %s Idx: %3d - 0x%016" G_GINT64_MODIFIER + "x-0x%016" G_GINT64_MODIFIER "x", + key, i, curr->base_address, curr_limit); - } + } - /* Overlap the end of the region */ +} - } else { +static gboolean collect_module_ranges_callback(const GumRangeDetails *details, + gpointer user_data) { - GumMemoryRange exclude = {.base_address = base, - .size = range_base - base}; - OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER - "x", - base, range_base); - gum_stalker_exclude(ctx->stalker, &exclude); - /* Extend past the end of the region */ - if (range_limit >= limit) { + GArray * ranges = (GArray *)user_data; + GumMemoryRange range = *details->range; + g_array_append_val(ranges, range); + return TRUE; - return true; +} - /* Contained within the region */ +static GArray *collect_module_ranges(void) { - } else { + GArray *result; + result = g_array_new(false, false, sizeof(GumMemoryRange)); + gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, + collect_module_ranges_callback, result); + print_ranges("Modules", result); + return result; - base = range_limit; - continue; +} - } +static GArray *collect_ranges(char *env_key) { - } + char * env_val; + gchar ** tokens; + int token_count; + GumMemoryRange range; + int i; + GArray * result; + + result = g_array_new(false, false, sizeof(GumMemoryRange)); + + env_val = getenv(env_key); + if (env_val == NULL) return result; + + tokens = g_strsplit(env_val, ",", MAX_RANGES); + + for (token_count = 0; tokens[token_count] != NULL; token_count++) + ; + + for (i = 0; i < token_count; i++) { + + convert_token(tokens[i], &range); + g_array_append_val(result, range); } - GumMemoryRange exclude = {.base_address = base, .size = limit - base}; - OKF("\t Excluding 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x", - base, limit); - gum_stalker_exclude(ctx->stalker, &exclude); - return true; + g_array_sort(result, range_sort); -} + /* Check for overlaps */ + for (i = 1; i < token_count; i++) { -gint range_sort(gconstpointer a, gconstpointer b) { + GumMemoryRange *prev = &g_array_index(result, GumMemoryRange, i - 1); + GumMemoryRange *curr = &g_array_index(result, GumMemoryRange, i); + GumAddress prev_limit = prev->base_address + prev->size; + GumAddress curr_limit = curr->base_address + curr->size; + if (prev_limit > curr->base_address) { - return ((GumMemoryRange *)a)->base_address - - ((GumMemoryRange *)b)->base_address; + FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER + "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER + "x-0x%016" G_GINT64_MODIFIER "x", + prev->base_address, prev_limit, curr->base_address, curr_limit); + + } + + } + + print_ranges(env_key, result); + + g_strfreev(tokens); + + return result; } -static gboolean print_ranges(const GumRangeDetails *details, - gpointer user_data) { +static GArray *collect_libs_ranges(void) { - if (details->file == NULL) { + GArray * result; + GumMemoryRange range; + result = g_array_new(false, false, sizeof(GumMemoryRange)); - OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER "X", - details->range->base_address, - details->range->base_address + details->range->size); + if (getenv("AFL_INST_LIBS") == NULL) { + + range.base_address = lib_get_text_base(); + range.size = lib_get_text_limit() - lib_get_text_base(); } else { - OKF("MAP - 0x%016" G_GINT64_MODIFIER "x - 0x%016" G_GINT64_MODIFIER - "X %s(0x%016" G_GINT64_MODIFIER "x)", - details->range->base_address, - details->range->base_address + details->range->size, - details->file->path, details->file->offset); + range.base_address = 0; + range.size = G_MAXULONG; } + g_array_append_val(result, range); + + print_ranges("AFL_INST_LIBS", result); + + return result; + +} + +static gboolean intersect_range(GumMemoryRange *rr, GumMemoryRange *ra, + GumMemoryRange *rb) { + + GumAddress rab = ra->base_address; + GumAddress ral = rab + ra->size; + + GumAddress rbb = rb->base_address; + GumAddress rbl = rbb + rb->size; + + GumAddress rrb = 0; + GumAddress rrl = 0; + + rr->base_address = 0; + rr->size = 0; + + /* ra is before rb */ + if (ral < rbb) { return false; } + + /* ra is after rb */ + if (rab > rbl) { return true; } + + /* The largest of the two base addresses */ + rrb = rab > rbb ? rab : rbb; + + /* The smallest of the two limits */ + rrl = ral < rbl ? ral : rbl; + + rr->base_address = rrb; + rr->size = rrl - rrb; return true; } -void ranges_init(GumStalker *stalker) { +static GArray *intersect_ranges(GArray *a, GArray *b) { - char * showmaps; - char * include; - char * exclude; - char * list; - gchar ** tokens; - int token_count; - GumMemoryRange range; + GArray * result; + GumMemoryRange *ra; + GumMemoryRange *rb; + GumMemoryRange ri; - int i; + result = g_array_new(false, false, sizeof(GumMemoryRange)); - showmaps = getenv("AFL_FRIDA_DEBUG_MAPS"); - include = getenv("AFL_FRIDA_INST_RANGES"); - exclude = getenv("AFL_FRIDA_EXCLUDE_RANGES"); + for (guint i = 0; i < a->len; i++) { - if (showmaps) { + ra = &g_array_index(a, GumMemoryRange, i); + for (guint j = 0; j < b->len; j++) { - gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges, NULL); + rb = &g_array_index(b, GumMemoryRange, j); - } + if (!intersect_range(&ri, ra, rb)) { break; } + + if (ri.size == 0) { continue; } - if (include != NULL && exclude != NULL) { + g_array_append_val(result, ri); - FATAL( - "Cannot specifify both AFL_FRIDA_INST_RANGES and " - "AFL_FRIDA_EXCLUDE_RANGES"); + } } - if (include == NULL && exclude == NULL) { return; } + return result; - list = include == NULL ? exclude : include; - exclude_ranges = include == NULL ? true : false; +} - tokens = g_strsplit(list, ",", MAX_RANGES); +static GArray *subtract_ranges(GArray *a, GArray *b) { - for (token_count = 0; tokens[token_count] != NULL; token_count++) - ; + GArray * result; + GumMemoryRange *ra; + GumAddress ral; + GumMemoryRange *rb; + GumMemoryRange ri; + GumMemoryRange rs; - ranges = g_array_sized_new(false, false, sizeof(GumMemoryRange), token_count); + result = g_array_new(false, false, sizeof(GumMemoryRange)); - for (i = 0; i < token_count; i++) { + for (guint i = 0; i < a->len; i++) { - convert_token(tokens[i], &range); - g_array_append_val(ranges, range); + ra = &g_array_index(a, GumMemoryRange, i); + ral = ra->base_address + ra->size; + for (guint j = 0; j < b->len; j++) { + + rb = &g_array_index(b, GumMemoryRange, j); + + /* + * If rb is after ra, we have no more possible intersections and we can + * simply keep the remaining range + */ + if (!intersect_range(&ri, ra, rb)) { break; } + + /* + * If there is no intersection, then rb must be before ra, so we must + * continue + */ + if (ri.size == 0) { continue; } + + /* + * If the intersection is part way through the range, then we keep the + * start of the range + */ + if (ra->base_address < ri.base_address) { + + rs.base_address = ra->base_address; + rs.size = ri.base_address - ra->base_address; + g_array_append_val(result, rs); + + } + + /* + * If the intersection extends past the limit of the range, then we should + * continue with the next range + */ + if ((ri.base_address + ri.size) > ral) { + + ra->base_address = ral; + ra->size = 0; + break; + + } + + /* + * Otherwise we advance the base of the range to the end of the + * intersection and continue with the remainder of the range + */ + ra->base_address = ri.base_address + ri.size; + ra->size = ral - ra->base_address; + + } + + /* + * When we have processed all the possible intersections, we add what is + * left + */ + if (ra->size != 0) g_array_append_val(result, *ra); } - g_array_sort(ranges, range_sort); + return result; - /* Check for overlaps */ - for (i = 1; i < token_count; i++) { +} - GumMemoryRange *prev = &g_array_index(ranges, GumMemoryRange, i - 1); - GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i); - GumAddress prev_limit = prev->base_address + prev->size; - GumAddress curr_limit = curr->base_address + curr->size; - if (prev_limit > curr->base_address) { +static GArray *merge_ranges(GArray *a) { - FATAL("OVerlapping ranges 0x%016" G_GINT64_MODIFIER - "x-0x%016" G_GINT64_MODIFIER "x 0x%016" G_GINT64_MODIFIER - "x-0x%016" G_GINT64_MODIFIER "x", - prev->base_address, prev_limit, curr->base_address, curr_limit); + GArray * result; + GumMemoryRange rp; + GumMemoryRange *r; + + result = g_array_new(false, false, sizeof(GumMemoryRange)); + if (a->len == 0) return result; + + rp = g_array_index(a, GumMemoryRange, 0); + + for (guint i = 1; i < a->len; i++) { + + r = &g_array_index(a, GumMemoryRange, i); + + if (rp.base_address + rp.size == r->base_address) { + + rp.size += r->size; + + } else { + + g_array_append_val(result, rp); + rp.base_address = r->base_address; + rp.size = r->size; + continue; } } - for (i = 0; i < token_count; i++) { + g_array_append_val(result, rp); - GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i); - GumAddress curr_limit = curr->base_address + curr->size; - OKF("Range %3d - 0x%016" G_GINT64_MODIFIER "x-0x%016" G_GINT64_MODIFIER "x", - i, curr->base_address, curr_limit); + return result; + +} + +void ranges_init(void) { + + GumMemoryRange ri; + GArray * step1; + GArray * step2; + GArray * step3; + GArray * step4; + GumMemoryRange *r; + GumStalker * stalker; + + if (getenv("AFL_FRIDA_DEBUG_MAPS") != NULL) { + + gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, print_ranges_callback, + NULL); } - if (include == NULL) { + module_ranges = collect_module_ranges(); + libs_ranges = collect_libs_ranges(); + include_ranges = collect_ranges("AFL_FRIDA_INST_RANGES"); - for (i = 0; i < token_count; i++) { + /* If include ranges is empty, then assume everything is included */ + if (include_ranges->len == 0) { - gum_stalker_exclude(stalker, &g_array_index(ranges, GumMemoryRange, i)); + ri.base_address = 0; + ri.size = G_MAXULONG; + g_array_append_val(include_ranges, ri); - } + } - } else { + exclude_ranges = collect_ranges("AFL_FRIDA_EXCLUDE_RANGES"); - include_range_ctx_t ctx = {.stalker = stalker, .array = ranges}; - gum_process_enumerate_ranges(GUM_PAGE_NO_ACCESS, include_ranges, &ctx); + /* Intersect with .text section of main executable unless AFL_INST_LIBS */ + step1 = intersect_ranges(module_ranges, libs_ranges); + print_ranges("step1", step1); + + /* Intersect with AFL_FRIDA_INST_RANGES */ + step2 = intersect_ranges(step1, include_ranges); + print_ranges("step2", step2); + + /* Subtract AFL_FRIDA_EXCLUDE_RANGES */ + step3 = subtract_ranges(step2, exclude_ranges); + print_ranges("step3", step3); + + /* + * After step3, we have the total ranges to be instrumented, we now subtract + * that from the original ranges of the modules to configure stalker. + */ + + step4 = subtract_ranges(module_ranges, step3); + print_ranges("step4", step4); + + ranges = merge_ranges(step4); + print_ranges("final", ranges); + + stalker = stalker_get(); + + for (guint i = 0; i < ranges->len; i++) { + + r = &g_array_index(ranges, GumMemoryRange, i); + gum_stalker_exclude(stalker, r); } - g_strfreev(tokens); + g_array_free(step4, TRUE); + g_array_free(step3, TRUE); + g_array_free(step2, TRUE); + g_array_free(step1, TRUE); } gboolean range_is_excluded(gpointer address) { - int i; GumAddress test = GUM_ADDRESS(address); if (ranges == NULL) { return false; } - for (i = 0; i < ranges->len; i++) { + for (guint i = 0; i < ranges->len; i++) { GumMemoryRange *curr = &g_array_index(ranges, GumMemoryRange, i); GumAddress curr_limit = curr->base_address + curr->size; - if (test < curr->base_address) { return !exclude_ranges; } + if (test < curr->base_address) { return false; } - if (test < curr_limit) { return exclude_ranges; } + if (test < curr_limit) { return true; } } - return !exclude_ranges; + return false; } diff --git a/frida_mode/src/stalker.c b/frida_mode/src/stalker.c new file mode 100644 index 00000000..81973e9c --- /dev/null +++ b/frida_mode/src/stalker.c @@ -0,0 +1,30 @@ +#include "debug.h" + +#include "instrument.h" +#include "stalker.h" + +static GumStalker *stalker = NULL; + +void stalker_init(void) { + + stalker = gum_stalker_new(); + if (stalker == NULL) { FATAL("Failed to initialize stalker"); } + + gum_stalker_set_trust_threshold(stalker, 0); + +} + +GumStalker *stalker_get(void) { + + if (stalker == NULL) { FATAL("Stalker uninitialized"); } + return stalker; + +} + +void stalker_start(void) { + + GumStalkerTransformer *transformer = instrument_get_transformer(); + gum_stalker_follow_me(stalker, transformer, NULL); + +} + diff --git a/frida_mode/src/util.c b/frida_mode/src/util.c new file mode 100644 index 00000000..86b94970 --- /dev/null +++ b/frida_mode/src/util.c @@ -0,0 +1,67 @@ +#include "util.h" + +#include "debug.h" + +guint64 util_read_address(char *key) { + + char *value_str = getenv(key); + + if (value_str == NULL) { return 0; } + + if (!g_str_has_prefix(value_str, "0x")) { + + FATAL("Invalid address should have 0x prefix: %s\n", value_str); + + } + + char *value_str2 = &value_str[2]; + + for (char *c = value_str2; *c != '\0'; c++) { + + if (!g_ascii_isxdigit(*c)) { + + FATAL("Invalid address not formed of hex digits: %s ('%c')\n", value_str, + *c); + + } + + } + + guint64 value = g_ascii_strtoull(value_str2, NULL, 16); + if (value == 0) { + + FATAL("Invalid address failed hex conversion: %s\n", value_str2); + + } + + return value; + +} + +guint64 util_read_num(char *key) { + + char *value_str = getenv(key); + + if (value_str == NULL) { return 0; } + + for (char *c = value_str; *c != '\0'; c++) { + + if (!g_ascii_isdigit(*c)) { + + FATAL("Invalid address not formed of decimal digits: %s\n", value_str); + + } + + } + + guint64 value = g_ascii_strtoull(value_str, NULL, 10); + if (value == 0) { + + FATAL("Invalid address failed numeric conversion: %s\n", value_str); + + } + + return value; + +} + |