#include "frida-gumjs.h"
#include "instrument.h"
#include "persistent.h"
#include "util.h"
#if defined(__arm__)
// struct _GumArmCpuContext {
// guint32 pc;
// guint32 sp;
// guint32 cpsr;
// guint32 r8;
// guint32 r9;
// guint32 r10;
// guint32 r11;
// guint32 r12;
// GumArmVectorReg v[16];
// guint32 _padding;
// guint32 r[8];
// guint32 lr;
// };
// r11 - fp
// r12 - ip
// r13 - sp
// r14 - lr
// r15 - pc
static GumCpuContext saved_regs = {0};
static gpointer saved_lr = NULL;
gboolean persistent_is_supported(void) {
return true;
}
static void instrument_persitent_save_regs(GumArmWriter *cw,
GumCpuContext *regs) {
/* Save Regs */
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R0, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R1, ARM_REG_SP,
GUM_RED_ZONE_SIZE + sizeof(guint32));
gum_arm_writer_put_ldr_reg_address(cw, ARM_REG_R0, GUM_ADDRESS(regs));
/* Save r1-r7 */
for (size_t i = ARM_REG_R1; i < ARM_REG_R8; i++) {
gum_arm_writer_put_str_reg_reg_offset(
cw, i, ARM_REG_R0, offsetof(GumCpuContext, r[i - ARM_REG_R0]));
}
/* Save r8-r12 */
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R8, ARM_REG_R0,
offsetof(GumCpuContext, r8));
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R9, ARM_REG_R0,
offsetof(GumCpuContext, r9));
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R10, ARM_REG_R0,
offsetof(GumCpuContext, r10));
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R11, ARM_REG_R0,
offsetof(GumCpuContext, r11));
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R12, ARM_REG_R0,
offsetof(GumCpuContext, r12));
/* Save sp & lr */
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_SP, ARM_REG_R0,
offsetof(GumCpuContext, sp));
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_LR, ARM_REG_R0,
offsetof(GumCpuContext, lr));
/* Save r0 (load from stack into r1) */
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R1, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R1, ARM_REG_R0,
offsetof(GumCpuContext, r[0]));
/* Save CPSR */
gum_arm_writer_put_mov_reg_cpsr(cw, ARM_REG_R1);
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R1, ARM_REG_R0,
offsetof(GumCpuContext, cpsr));
/* Save PC */
gum_arm_writer_put_ldr_reg_address(cw, ARM_REG_R1,
GUM_ADDRESS(persistent_start));
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R1, ARM_REG_R0,
offsetof(GumCpuContext, pc));
/* Restore Regs */
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R1, ARM_REG_SP,
GUM_RED_ZONE_SIZE + sizeof(guint32));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R0, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
}
static void instrument_persitent_restore_regs(GumArmWriter *cw,
GumCpuContext *regs) {
gum_arm_writer_put_ldr_reg_address(cw, ARM_REG_R0, GUM_ADDRESS(regs));
/* Restore CPSR */
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R1, ARM_REG_R0,
offsetof(GumCpuContext, cpsr));
gum_arm_writer_put_mov_cpsr_reg(cw, ARM_REG_R1);
/* Restore sp & lr */
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_SP, ARM_REG_R0,
offsetof(GumCpuContext, sp));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_LR, ARM_REG_R0,
offsetof(GumCpuContext, lr));
/* Restore r8-r12 */
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R8, ARM_REG_R0,
offsetof(GumCpuContext, r8));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R9, ARM_REG_R0,
offsetof(GumCpuContext, r9));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R10, ARM_REG_R0,
offsetof(GumCpuContext, r10));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R11, ARM_REG_R0,
offsetof(GumCpuContext, r11));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R12, ARM_REG_R0,
offsetof(GumCpuContext, r12));
/* Restore r7-r0 */
for (size_t i = ARM_REG_R7; i >= ARM_REG_R0; i--) {
gum_arm_writer_put_ldr_reg_reg_offset(
cw, i, ARM_REG_R0, offsetof(GumCpuContext, r[i - ARM_REG_R0]));
}
}
static void instrument_exit(GumArmWriter *cw) {
gum_arm_writer_put_sub_reg_reg_reg(cw, ARM_REG_R0, ARM_REG_R0, ARM_REG_R0);
gum_arm_writer_put_call_address_with_arguments(cw, GUM_ADDRESS(_exit), 1,
GUM_ARG_REGISTER, ARM_REG_R0);
}
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(GumArmWriter *cw) {
gum_arm_writer_put_sub_reg_reg_imm(cw, ARM_REG_SP, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
gum_arm_writer_put_call_address_with_arguments(
cw, GUM_ADDRESS(instrument_afl_persistent_loop_func), 0);
gum_arm_writer_put_add_reg_reg_imm(cw, ARM_REG_SP, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
}
static void persistent_prologue_hook(GumArmWriter *cw, GumCpuContext *regs) {
if (persistent_hook == NULL) return;
gum_arm_writer_put_sub_reg_reg_imm(cw, ARM_REG_SP, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
gum_arm_writer_put_ldr_reg_address(cw, ARM_REG_R2,
GUM_ADDRESS(&__afl_fuzz_len));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R2, ARM_REG_R2, 0);
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R2, ARM_REG_R2, 0);
gum_arm_writer_put_ldr_reg_address(cw, ARM_REG_R1,
GUM_ADDRESS(&__afl_fuzz_ptr));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R1, ARM_REG_R1, 0);
gum_arm_writer_put_call_address_with_arguments(
cw, GUM_ADDRESS(persistent_hook), 3, GUM_ARG_ADDRESS, GUM_ADDRESS(regs),
GUM_ARG_REGISTER, ARM_REG_R1, GUM_ARG_REGISTER, ARM_REG_R2);
gum_arm_writer_put_add_reg_reg_imm(cw, ARM_REG_SP, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
}
static void instrument_persitent_save_lr(GumArmWriter *cw) {
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_R0, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
gum_arm_writer_put_ldr_reg_address(cw, ARM_REG_R0, GUM_ADDRESS(&saved_lr));
gum_arm_writer_put_str_reg_reg_offset(cw, ARM_REG_LR, ARM_REG_R0, 0);
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R0, ARM_REG_SP,
GUM_RED_ZONE_SIZE);
}
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
*/
GumArmWriter *cw = output->writer.arm;
gconstpointer loop = cw->code + 1;
FVERBOSE("Persistent loop reached");
instrument_persitent_save_regs(cw, &saved_regs);
/* loop: */
gum_arm_writer_put_label(cw, loop);
/* call instrument_prologue_func */
instrument_afl_persistent_loop(cw);
/* jz done */
gconstpointer done = cw->code + 1;
gum_arm_writer_put_cmp_reg_imm(cw, ARM_REG_R0, 0);
gum_arm_writer_put_b_cond_label(cw, ARM_CC_EQ, done);
/* 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_arm_writer_put_bl_label(cw, original);
/* jmp loop */
gum_arm_writer_put_b_label(cw, loop);
/* done: */
gum_arm_writer_put_label(cw, done);
instrument_exit(cw);
/* original: */
gum_arm_writer_put_label(cw, original);
instrument_persitent_save_lr(cw);
if (persistent_debug) { gum_arm_writer_put_breakpoint(cw); }
}
void persistent_epilogue_arch(GumStalkerOutput *output) {
GumArmWriter *cw = output->writer.arm;
if (persistent_debug) { gum_arm_writer_put_breakpoint(cw); }
gum_arm_writer_put_ldr_reg_address(cw, ARM_REG_R0, GUM_ADDRESS(&saved_lr));
gum_arm_writer_put_ldr_reg_reg_offset(cw, ARM_REG_R0, ARM_REG_R0, 0);
gum_arm_writer_put_bx_reg(cw, ARM_REG_R0);
}
#endif