#include "frida-gumjs.h"
#include "config.h"
#include "instrument.h"
#include "persistent.h"
#include "util.h"
#if defined(__i386__)
typedef struct {
GumCpuContext ctx;
uint32_t eflags;
} persistent_ctx_t;
static persistent_ctx_t saved_regs = {0};
static gpointer saved_ret = NULL;
gboolean persistent_is_supported(void) {
return true;
}
static void instrument_persitent_save_regs(GumX86Writer *cw,
persistent_ctx_t *regs) {
GumAddress regs_address = GUM_ADDRESS(regs);
/* Should be pushing FPU here, but meh */
gum_x86_writer_put_pushfx(cw);
gum_x86_writer_put_push_reg(cw, GUM_X86_EAX);
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, regs_address);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, ebx), GUM_X86_EBX);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, ecx), GUM_X86_ECX);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, edx), GUM_X86_EDX);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, edi), GUM_X86_EDI);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, esi), GUM_X86_ESI);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, ebp), GUM_X86_EBP);
/* Store RIP */
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EBX,
GUM_ADDRESS(persistent_start));
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, eip), GUM_X86_EBX);
/* Store adjusted RSP */
gum_x86_writer_put_mov_reg_reg(cw, GUM_X86_EBX, GUM_X86_ESP);
/* RED_ZONE + Saved flags, RAX */
gum_x86_writer_put_add_reg_imm(cw, GUM_X86_EBX, (0x4 * 2));
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, esp), GUM_X86_EBX);
/* Save the flags */
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_ESP, 0x4);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(persistent_ctx_t, eflags), GUM_X86_EBX);
/* Save the RAX */
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_ESP, 0x0);
gum_x86_writer_put_mov_reg_offset_ptr_reg(
cw, GUM_X86_EAX, offsetof(GumCpuContext, eax), GUM_X86_EBX);
/* Pop the saved values */
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_ESP, GUM_X86_ESP, 0x8);
}
static void instrument_persitent_restore_regs(GumX86Writer *cw,
persistent_ctx_t *regs) {
GumAddress regs_address = GUM_ADDRESS(regs);
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, regs_address);
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ECX, GUM_X86_EAX,
offsetof(GumCpuContext, ecx));
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EDX, GUM_X86_EAX,
offsetof(GumCpuContext, edx));
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EDI, GUM_X86_EAX,
offsetof(GumCpuContext, edi));
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ESI, GUM_X86_EAX,
offsetof(GumCpuContext, esi));
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBP, GUM_X86_EAX,
offsetof(GumCpuContext, ebp));
/* Don't restore RIP */
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ESP, GUM_X86_EAX,
offsetof(GumCpuContext, esp));
/* Restore RBX, RAX & Flags */
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_EAX,
offsetof(GumCpuContext, ebx));
gum_x86_writer_put_push_reg(cw, GUM_X86_EBX);
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_EAX,
offsetof(GumCpuContext, eax));
gum_x86_writer_put_push_reg(cw, GUM_X86_EBX);
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_EAX,
offsetof(persistent_ctx_t, eflags));
gum_x86_writer_put_push_reg(cw, GUM_X86_EBX);
gum_x86_writer_put_popfx(cw);
gum_x86_writer_put_pop_reg(cw, GUM_X86_EAX);
gum_x86_writer_put_pop_reg(cw, GUM_X86_EBX);
}
static void instrument_exit(GumX86Writer *cw) {
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, GUM_ADDRESS(_exit));
gum_x86_writer_put_mov_reg_u32(cw, GUM_X86_EDI, 0);
gum_x86_writer_put_push_reg(cw, GUM_X86_EDI);
gum_x86_writer_put_call_reg(cw, GUM_X86_EAX);
}
static int instrument_afl_persistent_loop_func(void) {
int ret = __afl_persistent_loop(persistent_count);
if (instrument_previous_pc_addr == NULL) {
FATAL("instrument_previous_pc_addr uninitialized");
}
*instrument_previous_pc_addr = instrument_hash_zero;
return ret;
}
static void instrument_afl_persistent_loop(GumX86Writer *cw) {
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_X86_EAX, GUM_X86_EAX);
}
static void persistent_prologue_hook(GumX86Writer *cw, persistent_ctx_t *regs) {
if (persistent_hook == NULL) return;
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_ECX,
GUM_ADDRESS(&__afl_fuzz_len));
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ECX, GUM_X86_ECX, 0);
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_ECX, GUM_X86_ECX, 0);
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EDX,
GUM_ADDRESS(&__afl_fuzz_ptr));
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EDX, GUM_X86_EDX, 0);
/* Base address is 64-bits (hence two zero arguments) */
gum_x86_writer_put_call_address_with_arguments(
cw, GUM_CALL_CAPI, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS,
GUM_ADDRESS(®s->ctx), GUM_ARG_REGISTER, GUM_X86_EDX, GUM_ARG_REGISTER,
GUM_X86_ECX);
}
static void instrument_persitent_save_ret(GumX86Writer *cw) {
/* Stack usage by this function */
gssize offset = (3 * 4);
gum_x86_writer_put_pushfx(cw);
gum_x86_writer_put_push_reg(cw, GUM_X86_EAX);
gum_x86_writer_put_push_reg(cw, GUM_X86_EBX);
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, GUM_ADDRESS(&saved_ret));
gum_x86_writer_put_mov_reg_reg_offset_ptr(cw, GUM_X86_EBX, GUM_X86_ESP,
offset);
gum_x86_writer_put_mov_reg_ptr_reg(cw, GUM_X86_EAX, GUM_X86_EBX);
gum_x86_writer_put_pop_reg(cw, GUM_X86_EBX);
gum_x86_writer_put_pop_reg(cw, GUM_X86_EAX);
gum_x86_writer_put_popfx(cw);
}
void persistent_prologue_arch(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;
FVERBOSE("Persistent loop reached");
/* Pop the return value */
gum_x86_writer_put_lea_reg_reg_offset(cw, GUM_X86_ESP, GUM_X86_ESP, 4);
instrument_persitent_save_regs(cw, &saved_regs);
/* 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_exit(cw);
/* original: */
gum_x86_writer_put_label(cw, original);
instrument_persitent_save_ret(cw);
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
}
void persistent_epilogue_arch(GumStalkerOutput *output) {
GumX86Writer *cw = output->writer.x86;
if (persistent_debug) { gum_x86_writer_put_breakpoint(cw); }
gum_x86_writer_put_mov_reg_address(cw, GUM_X86_EAX, GUM_ADDRESS(&saved_ret));
gum_x86_writer_put_jmp_reg_ptr(cw, GUM_X86_EAX);
}
#endif