From db603acded9835c85a8f96df52bb202c38867fe8 Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Wed, 1 Oct 2025 22:43:05 +0200 Subject: [PATCH] move platform specific into arm file --- src/arm64/detours_arm64.h | 111 ++++++++++++++++++++---- src/detours_internal.h | 62 ++++++++++++-- src/mach_detours.c | 176 ++++++++++---------------------------- 3 files changed, 193 insertions(+), 156 deletions(-) diff --git a/src/arm64/detours_arm64.h b/src/arm64/detours_arm64.h index f031fe7..aca654a 100644 --- a/src/arm64/detours_arm64.h +++ b/src/arm64/detours_arm64.h @@ -5,8 +5,12 @@ #ifndef MACH_DETOURS_ARM64_H #define MACH_DETOURS_ARM64_H +#include "detours_internal.h" + #include +#include + typedef struct detour_trampoline { // An ARM64 instruction is 4 bytes long. @@ -45,9 +49,10 @@ typedef struct detour_trampoline uint8_t* ptr_detour; // first instruction of detour function. } detour_trampoline; -static_assert(sizeof(detour_trampoline) == 192); +static_assert(sizeof(detour_trampoline)== 192); -enum { +enum +{ DETOUR_SIZE_OF_JMP = 12 }; @@ -65,8 +70,10 @@ static inline void write_opcode(uint8_t** int_out_code, const detour_arm64_opcod *int_out_code += sizeof(detour_arm64_opcode_t); } -struct detour_arm64_indirect_jmp { - struct { +struct detour_arm64_indirect_jmp +{ + struct + { uint32_t Rd : 5; uint32_t immhi : 19; uint32_t iop : 5; @@ -74,7 +81,8 @@ struct detour_arm64_indirect_jmp { uint32_t op : 1; } ardp; - struct { + struct + { uint32_t Rt : 5; uint32_t Rn : 5; uint32_t imm : 12; @@ -88,8 +96,10 @@ struct detour_arm64_indirect_jmp { uint32_t br; }; -union detour_arm64_indirect_imm { - struct { +union detour_arm64_indirect_imm +{ + struct + { uint64_t pad : 12; uint64_t adrp_immlo : 2; uint64_t adrp_immhi : 19; @@ -107,7 +117,7 @@ static inline uint8_t* internal_detour_gen_jmp_indirect(uint8_t* code, const uin union detour_arm64_indirect_imm jmp_ind_addr; jmp_ind_addr.value = (((uint64_t)jump_val) & 0xFFFFFFFFFFFFF000) - - (((uint64_t)code) & 0xFFFFFFFFFFFFF000); + (((uint64_t)code) & 0xFFFFFFFFFFFFF000); struct detour_arm64_indirect_jmp* ind_jmp = (struct detour_arm64_indirect_jmp*)code; code = (uint8_t*)(ind_jmp + 1); @@ -140,8 +150,8 @@ static inline uint8_t* internal_detour_gen_jmp_immediate(uint8_t* code, uint8_t* *(uint8_t**)literal = jmp_val; const int32_t delta = (int32_t)(literal - code); - write_opcode(&code, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n] - write_opcode(&code, 0xd61f0000 | (17 << 5)); // BR X17 + write_opcode(&code, 0x58000011 | ((delta / 4) << 5)); // LDR X17,[PC+n] + write_opcode(&code, 0xd61f0000 | (17 << 5)); // BR X17 return code; } @@ -154,7 +164,8 @@ static inline uint8_t* internal_detour_gen_brk(uint8_t* code, const uint8_t* lim return code; } -static inline void internal_detour_find_jmp_bounds(uint8_t* code, detour_trampoline** out_lower, detour_trampoline** out_upper) +static inline void internal_detour_find_jmp_bounds(uint8_t* code, detour_trampoline** out_lower, + detour_trampoline** out_upper) { // The encoding used by detour_gen_jmp_indirect actually enables a // displacement of +/- 4GiB. In the future, this could be changed to @@ -190,10 +201,10 @@ static inline bool internal_detour_is_code_os_patched(const uint8_t* code) const uint32_t hpat_opcode1 = fetch_opcode(branch_target); const uint32_t hpat_opcode2 = fetch_opcode(branch_target + 4); - if (hpat_opcode1 != 0x58008010) { // ldr [PC+PAGE_SIZE] + if (hpat_opcode1 != 0x58008010) { // ldr [PC+PAGE_SIZE] return false; } - if (hpat_opcode2 != 0xd61f0200) { // br + if (hpat_opcode2 != 0xd61f0200) { // br return false; } return true; @@ -207,19 +218,19 @@ static inline bool internal_detour_does_code_end_function(const uint8_t* code) if (internal_detour_is_code_os_patched(code)) { return false; } - if ((opcode & 0xffbffc1f) == 0xd61f0000 || // ret/br - (opcode & 0xfc000000) == 0x14000000) { // b + if ((opcode & 0xffbffc1f) == 0xd61f0000 || // ret/br + (opcode & 0xfc000000) == 0x14000000) { // b return true; - } + } return false; } static inline uint32_t internal_detour_is_code_filler(const uint8_t* code) { - if (*(uint32_t *)code == 0xd503201f) { // nop. + if (*(uint32_t*)code == 0xd503201f) { // nop. return 4; } - if (*(uint32_t *)code == 0x00000000) { // zero-filled padding. + if (*(uint32_t*)code == 0x00000000) { // zero-filled padding. return 4; } return 0; @@ -231,4 +242,66 @@ static inline uint8_t* internal_detour_skip_jmp(uint8_t* code) return code; } -#endif //MACH_DETOURS_ARM64_H \ No newline at end of file +static inline uint8_t* internal_detour_operation_get_target_ptr(detour_operation* operation) +{ + return operation->target; +} + +static inline uint8_t* internal_detour_operation_get_trampoline_ptr(detour_operation* operation) +{ + return operation->trampoline->code; +} + +static inline uint8_t* internal_detour_operation_commit_detour(detour_operation* operation) +{ + uint8_t* code = internal_detour_gen_jmp_indirect( + operation->target, (uint64_t*)&(operation->trampoline->ptr_detour)); + code = internal_detour_gen_brk(code, operation->trampoline->ptr_remain); + return code; +} + +static inline void internal_detour_update_thread_on_commit(detour_pending_thread* thread, detour_operation* operations_head) +{ + arm_thread_state64_t threadState; + mach_msg_type_number_t threadStateCnt = ARM_THREAD_STATE64_COUNT; + const kern_return_t error = thread_get_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState, + &threadStateCnt); + if (error != err_none) { + DETOUR_BREAK(); + return; + } + const uintptr_t pc = arm_thread_state64_get_pc(threadState); + + for (detour_operation* op = operations_head; op != nullptr; op = op->next) { + switch (op->kind) { + case detour_operation_kind_attach: { + const uintptr_t targetAddr = (uintptr_t)op->target; + if (pc >= targetAddr && pc < targetAddr + op->trampoline->restore_code_size) { + uintptr_t new_pc = (uintptr_t)op->trampoline; + new_pc += internal_detour_align_from_target(op->trampoline->align, ARRAYSIZE(op->trampoline->align), pc - targetAddr); + DETOUR_TRACE(("detours: thread %u was at 0x%" PRIXPTR ", moved to 0x%" PRIXPTR "\n", + thread->thread, pc, new_pc)); + arm_thread_state64_set_pc_fptr(threadState, new_pc); + thread_set_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState, + ARM_THREAD_STATE64_COUNT); + } + break; + } + case detour_operation_kind_detach: { + const uintptr_t trampAddr = (uintptr_t)op->trampoline; + if (pc >= trampAddr && pc < trampAddr + sizeof(*op->trampoline)) { + uintptr_t new_pc = (uintptr_t)op->target; + new_pc += internal_detour_align_from_trampoline(op->trampoline->align, ARRAYSIZE(op->trampoline->align), pc - trampAddr); + DETOUR_TRACE(("detours: thread %u was at 0x%" PRIXPTR ", moved to 0x%" PRIXPTR "\n", + thread->thread, pc, new_pc)); + arm_thread_state64_set_pc_fptr(threadState, new_pc); + thread_set_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState, + ARM_THREAD_STATE64_COUNT); + } + break; + } + } + } +} + +#endif //MACH_DETOURS_ARM64_H diff --git a/src/detours_internal.h b/src/detours_internal.h index cfbb971..ddec6f1 100644 --- a/src/detours_internal.h +++ b/src/detours_internal.h @@ -38,12 +38,6 @@ #endif -typedef struct detour_align -{ - uint8_t offset_target; - uint8_t offset_trampoline; -} detour_align; - // Region reserved for system DLLs, which cannot be used for trampolines. static void* s_system_region_lower_bound = (void*)(uintptr_t)0x70000000; static void* s_system_region_upper_bound = (void*)(uintptr_t)0x80000000; @@ -62,4 +56,60 @@ static inline uintptr_t internal_detour_2gb_above(uintptr_t address) #endif } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Structs + +typedef struct detour_trampoline detour_trampoline; + +typedef enum detour_operation_kind +{ + detour_operation_kind_attach, + detour_operation_kind_detach, +} detour_operation_kind; + +typedef struct detour_operation +{ + struct detour_operation* next; + detour_operation_kind kind; + uint8_t** pointer; + uint8_t* target; + detour_trampoline* trampoline; + vm_prot_t perm; +} detour_operation; + +typedef struct detour_pending_thread +{ + struct detour_pending_thread* next; + thread_t thread; +} detour_pending_thread; + +typedef struct detour_align +{ + uint8_t offset_target; + uint8_t offset_trampoline; +} detour_align; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Trampoline Helpers + +static uint8_t internal_detour_align_from_trampoline(const detour_align align[], const size_t align_size, const uint8_t offset_trampoline) +{ + for (size_t n = 0; n < align_size; n++) { + if (align[n].offset_trampoline == offset_trampoline) { + return align[n].offset_target; + } + } + return 0; +} + +static uint8_t internal_detour_align_from_target(const detour_align align[], const size_t align_size, const uint8_t offset_target) +{ + for (size_t n = 0; n < align_size; n++) { + if (align[n].offset_target == offset_target) { + return align[n].offset_trampoline; + } + } + return 0; +} + #endif //MACH_DETOURS_INTERNAL_H \ No newline at end of file diff --git a/src/mach_detours.c b/src/mach_detours.c index 6c2ba17..58e1602 100644 --- a/src/mach_detours.c +++ b/src/mach_detours.c @@ -305,55 +305,9 @@ static void internal_detour_free_unused_trampoline_regions() } } -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// Trampoline Helpers - -static uint8_t internal_detour_align_from_trampoline(const detour_trampoline* trampoline, - const uint8_t offset_trampoline) -{ - for (int32_t n = 0; n < ARRAYSIZE(trampoline->align); n++) { - if (trampoline->align[n].offset_trampoline == offset_trampoline) { - return trampoline->align[n].offset_target; - } - } - return 0; -} - -static uint8_t internal_detour_align_from_target(const detour_trampoline* trampoline, const uint8_t offset_target) -{ - for (int32_t n = 0; n < ARRAYSIZE(trampoline->align); n++) { - if (trampoline->align[n].offset_target == offset_target) { - return trampoline->align[n].offset_trampoline; - } - } - return 0; -} - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Transactions -typedef enum detour_operation_kind -{ - detour_operation_kind_attach, - detour_operation_kind_detach, -} detour_operation_kind; - -typedef struct detour_operation -{ - struct detour_operation* next; - detour_operation_kind kind; - uint8_t** pointer; - uint8_t* target; - detour_trampoline* trampoline; - vm_prot_t perm; -} detour_operation; - -typedef struct detour_pending_thread -{ - struct detour_pending_thread* next; - thread_t thread; -} detour_pending_thread; - static bool s_ignore_too_small = false; static bool s_retain_regions = false; @@ -450,100 +404,60 @@ mach_error_t detour_transaction_commit_ex(detour_func_t** out_failed_target) // Insert or remove each of the detours. for (detour_operation* operation = s_pending_operations_head; operation != nullptr; operation = operation->next) { - if (operation->kind == detour_operation_kind_detach) { - memcpy(operation->target, operation->trampoline->restore_code, operation->trampoline->restore_code_size); -#ifdef DETOURS_ARM64 - *operation->pointer = operation->target; -#endif - } else { - DETOUR_TRACE(("detours: trampoline=%p, ptr_remain=%p, ptr_detour=%p, restore_code_size=%u\n", - operation->trampoline, - operation->trampoline->ptr_remain, - operation->trampoline->ptr_detour, - operation->trampoline->restore_code_size)); + switch (operation->kind) { + case detour_operation_kind_attach: { + DETOUR_TRACE(("detours: trampoline=%p, ptr_remain=%p, ptr_detour=%p, restore_code_size=%u\n", + operation->trampoline, + operation->trampoline->ptr_remain, + operation->trampoline->ptr_detour, + operation->trampoline->restore_code_size)); - DETOUR_TRACE(("detours: target=%p: " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x [before]\n", - operation->target, - operation->target[0], operation->target[1], operation->target[2], operation->target[3], - operation->target[4], operation->target[5], operation->target[6], operation->target[7], - operation->target[8], operation->target[9], operation->target[10], operation->target[11])); + DETOUR_TRACE(("detours: target=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x [before]\n", + operation->target, + operation->target[0], operation->target[1], operation->target[2], operation->target[3], + operation->target[4], operation->target[5], operation->target[6], operation->target[7], + operation->target[8], operation->target[9], operation->target[10], operation->target[11])); -#ifdef DETOURS_ARM64 - uint8_t* code = internal_detour_gen_jmp_indirect(operation->target, - (uint64_t*)&(operation->trampoline->ptr_detour)); - code = internal_detour_gen_brk(code, operation->trampoline->ptr_remain); - UNUSED_VARIABLE(code); - *operation->pointer = operation->trampoline->code; -#endif // DETOURS_ARM64 + internal_detour_operation_commit_detour(operation); + *operation->pointer = internal_detour_operation_get_trampoline_ptr(operation) - DETOUR_TRACE(("detours: target=%p: " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x [after]\n", - operation->target, - operation->target[0], operation->target[1], operation->target[2], operation->target[3], - operation->target[4], operation->target[5], operation->target[6], operation->target[7], - operation->target[8], operation->target[9], operation->target[10], operation->target[11])); + DETOUR_TRACE(("detours: target=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x [after]\n", + operation->target, + operation->target[0], operation->target[1], operation->target[2], operation->target[3], + operation->target[4], operation->target[5], operation->target[6], operation->target[7], + operation->target[8], operation->target[9], operation->target[10], operation->target[11])); - DETOUR_TRACE(("detours: trampoline=%p: " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x\n", - operation->trampoline, - operation->trampoline->code[0], operation->trampoline->code[1], - operation->trampoline->code[2], operation->trampoline->code[3], - operation->trampoline->code[4], operation->trampoline->code[5], - operation->trampoline->code[6], operation->trampoline->code[7], - operation->trampoline->code[8], operation->trampoline->code[9], - operation->trampoline->code[10], operation->trampoline->code[11])); + DETOUR_TRACE(("detours: trampoline=%p: " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + operation->trampoline, + operation->trampoline->code[0], operation->trampoline->code[1], + operation->trampoline->code[2], operation->trampoline->code[3], + operation->trampoline->code[4], operation->trampoline->code[5], + operation->trampoline->code[6], operation->trampoline->code[7], + operation->trampoline->code[8], operation->trampoline->code[9], + operation->trampoline->code[10], operation->trampoline->code[11])); + break; + } + case detour_operation_kind_detach: { + memcpy(operation->target, operation->trampoline->restore_code, + operation->trampoline->restore_code_size); + *operation->pointer = internal_detour_operation_get_target_ptr(operation); + break; + } } } // Update any suspended threads. for (detour_pending_thread* thread = s_pending_threads_head; thread != nullptr; thread = thread->next) { - arm_thread_state64_t threadState; - mach_msg_type_number_t threadStateCnt = ARM_THREAD_STATE64_COUNT; - const kern_return_t error = thread_get_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState, - &threadStateCnt); - if (error != err_none) { - DETOUR_BREAK(); - continue; - } - const uintptr_t pc = arm_thread_state64_get_pc(threadState); - - for (detour_operation* op = s_pending_operations_head; op != nullptr; op = op->next) { - switch (op->kind) { - case detour_operation_kind_attach: { - const uintptr_t targetAddr = (uintptr_t)op->target; - if (pc >= targetAddr && pc < targetAddr + op->trampoline->restore_code_size) { - uintptr_t new_pc = (uintptr_t)op->trampoline; - new_pc += internal_detour_align_from_target(op->trampoline, pc - targetAddr); - DETOUR_TRACE(("detours: thread %u was at 0x%" PRIXPTR ", moved to 0x%" PRIXPTR "\n", - thread->thread, pc, new_pc)); - arm_thread_state64_set_pc_fptr(threadState, new_pc); - thread_set_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState, - ARM_THREAD_STATE64_COUNT); - } - break; - } - case detour_operation_kind_detach: { - const uintptr_t trampAddr = (uintptr_t)op->trampoline; - if (pc >= trampAddr && pc < trampAddr + sizeof(*op->trampoline)) { - uintptr_t new_pc = (uintptr_t)op->target; - new_pc += internal_detour_align_from_trampoline(op->trampoline, pc - trampAddr); - DETOUR_TRACE(("detours: thread %u was at 0x%" PRIXPTR ", moved to 0x%" PRIXPTR "\n", - thread->thread, pc, new_pc)); - arm_thread_state64_set_pc_fptr(threadState, new_pc); - thread_set_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState, - ARM_THREAD_STATE64_COUNT); - } - break; - } - } - } + internal_detour_update_thread_on_commit(thread, s_pending_operations_head); } // Restore all the page permissions