diff --git a/CMakeLists.txt b/CMakeLists.txt index 36257a2..49f89bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,15 @@ add_library(mach_detours SHARED src/arm64/detours_arm64.h src/arm64/detours_arm64_disasm.cpp ) - target_include_directories(mach_detours - PUBLIC "include" - PRIVATE "src") + PUBLIC include + PRIVATE src +) + + +add_executable(mach_detours_sample + sample/main.cpp +) +target_link_libraries(mach_detours_sample + PRIVATE mach_detours +) diff --git a/include/mach_detours.h b/include/mach_detours.h index 97ef6b5..fcd7dcf 100644 --- a/include/mach_detours.h +++ b/include/mach_detours.h @@ -5,6 +5,10 @@ #ifndef MACH_DETOURS_H #define MACH_DETOURS_H +#ifdef __cplusplus +extern "C" { +#endif + #include #include @@ -31,4 +35,8 @@ bool detour_set_retain_regions(bool value); void* detour_set_system_region_lower_bound(void* value); void* detour_set_system_region_upper_bound(void* value); +#ifdef __cplusplus +} +#endif + #endif // MACH_DETOURS_H diff --git a/sample/main.cpp b/sample/main.cpp new file mode 100644 index 0000000..feed8d4 --- /dev/null +++ b/sample/main.cpp @@ -0,0 +1,34 @@ +// Copyright (c) Lysann Tranvouez. All rights reserved. + +#include + +#include "mach_detours.h" + +#include + +char* (*real_strerror)(int errno) = strerror; + +static int counter = 0; + +char* my_strerror(const int errno) +{ + counter++; + return real_strerror(errno); +} + +int main(int argc, const char* argv[]) +{ + counter = 0; + strerror(0); + assert(counter == 0); + + detour_transaction_begin(); + detour_attach(reinterpret_cast(&real_strerror), reinterpret_cast(my_strerror)); + detour_transaction_commit(); + + assert(counter == 0); + strerror(0); + assert(counter == 1); + + return 0; +} \ No newline at end of file diff --git a/src/mach_detours.c b/src/mach_detours.c index 0340257..0c3ffdf 100644 --- a/src/mach_detours.c +++ b/src/mach_detours.c @@ -3,7 +3,7 @@ #include "mach_detours.h" -#define DETOUR_DEBUG 1 +#define DETOUR_DEBUG 0 #ifdef __arm64__ #define DETOURS_ARM64 @@ -141,8 +141,8 @@ static void* internal_detour_alloc_region_from_hi(const uint8_t* lo, const uint8 } static void* internal_detour_alloc_trampoline_allocate_new(const uint8_t* target, - detour_trampoline* lo, - detour_trampoline* hi) + detour_trampoline* lo, + detour_trampoline* hi) { void* try_addr = nullptr; @@ -200,12 +200,11 @@ static detour_trampoline* internal_detour_alloc_trampoline(uint8_t* target) // First check the default region for a valid free block. if (s_default_region && s_default_region->free_list_head && s_default_region->free_list_head >= lo && s_default_region->free_list_head <= hi) { - - found_region: + found_region: trampoline = s_default_region->free_list_head; // do a last sanity check on region. if (trampoline < lo || trampoline > hi) { - raise(SIGTRAP); + DETOUR_BREAK(); return nullptr; } s_default_region->free_list_head = (detour_trampoline*)trampoline->ptr_remain; @@ -236,7 +235,7 @@ static detour_trampoline* internal_detour_alloc_trampoline(uint8_t* target) s_default_region->next = s_regions_head; s_regions_head = s_default_region; DETOUR_TRACE((" Allocated region %p..%p\n\n", - s_default_region, ((uint8_t*)s_default_region) + DETOUR_REGION_SIZE - 1)); + s_default_region, ((uint8_t*)s_default_region) + DETOUR_REGION_SIZE - 1)); // Put everything but the first trampoline on the free list. uint8_t* free = nullptr; @@ -522,9 +521,8 @@ mach_error_t detour_transaction_commit_ex(detour_func_t** out_failed_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); - printf("detours: thread %u was at 0x%" PRIXPTR ", moved to 0x%" PRIXPTR "\n", thread->thread, - pc, - new_pc); + 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); @@ -536,9 +534,8 @@ mach_error_t detour_transaction_commit_ex(detour_func_t** out_failed_target) 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); - printf("detours: thread %u was at 0x%" PRIXPTR ", moved to 0x%" PRIXPTR "\n", thread->thread, - pc, - new_pc); + 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); @@ -648,7 +645,8 @@ mach_error_t detour_attach(detour_func_t* inout_pointer, detour_func_t detour) return detour_attach_ex(inout_pointer, detour, nullptr, nullptr, nullptr); } -mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour, detour_func_t* out_real_trampoline, detour_func_t* out_real_target, detour_func_t* out_real_detour) +mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour, detour_func_t* out_real_trampoline, + detour_func_t* out_real_target, detour_func_t* out_real_detour) { if (out_real_trampoline) { *out_real_trampoline = nullptr; @@ -719,10 +717,10 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour op = calloc(1, sizeof(detour_operation)); if (!op) { error = KERN_RESOURCE_SHORTAGE; - fail: + fail: s_pending_error = error; DETOUR_BREAK(); - stop: + stop: if (trampoline) { internal_detour_free_trampoline(trampoline); // ReSharper disable once CppDFAUnusedValue @@ -844,7 +842,8 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour trampoline_code = trampoline->code + trampoline->code_size; - trampoline_code = internal_detour_gen_jmp_immediate(trampoline_code, &trampoline_code_limit, trampoline->ptr_remain); + trampoline_code = + internal_detour_gen_jmp_immediate(trampoline_code, &trampoline_code_limit, trampoline->ptr_remain); trampoline_code = internal_detour_gen_brk(trampoline_code, trampoline_code_limit); UNUSED_VARIABLE(trampoline_code); @@ -857,7 +856,8 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour mach_vm_size_t region_size = 0; natural_t nesting_depth = 99999; mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; - error = mach_vm_region_recurse(port, ®ion_addr, ®ion_size, &nesting_depth, (vm_region_recurse_info_t)®ion_info, &count); + error = mach_vm_region_recurse(port, ®ion_addr, ®ion_size, &nesting_depth, + (vm_region_recurse_info_t)®ion_info, &count); if (error != err_none) { DETOUR_BREAK(); goto fail; @@ -872,24 +872,24 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour } DETOUR_TRACE(("detours: target=%p: " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x\n", - target, - target[0], target[1], target[2], target[3], - target[4], target[5], target[6], target[7], - target[8], target[9], target[10], target[11])); + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + target, + target[0], target[1], target[2], target[3], + target[4], target[5], target[6], target[7], + target[8], target[9], target[10], target[11])); DETOUR_TRACE(("detours: trampoline =%p: " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x " - "%02x %02x %02x %02x\n", - trampoline, - trampoline->code[0], trampoline->code[1], - trampoline->code[2], trampoline->code[3], - trampoline->code[4], trampoline->code[5], - trampoline->code[6], trampoline->code[7], - trampoline->code[8], trampoline->code[9], - trampoline->code[10], trampoline->code[11])); + "%02x %02x %02x %02x " + "%02x %02x %02x %02x " + "%02x %02x %02x %02x\n", + trampoline, + trampoline->code[0], trampoline->code[1], + trampoline->code[2], trampoline->code[3], + trampoline->code[4], trampoline->code[5], + trampoline->code[6], trampoline->code[7], + trampoline->code[8], trampoline->code[9], + trampoline->code[10], trampoline->code[11])); op->kind = detour_operation_kind_attach; op->pointer = (uint8_t**)inout_pointer; @@ -931,10 +931,10 @@ mach_error_t detour_detach(detour_func_t* inout_pointer, detour_func_t detour) detour_operation* op = calloc(1, sizeof(detour_operation)); if (!op) { error = KERN_RESOURCE_SHORTAGE; - fail: + fail: s_pending_error = error; DETOUR_BREAK(); - stop: + stop: free(op); // ReSharper disable once CppDFAUnusedValue op = nullptr; @@ -976,7 +976,8 @@ mach_error_t detour_detach(detour_func_t* inout_pointer, detour_func_t detour) mach_vm_size_t region_size = 0; natural_t nesting_depth = 99999; mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; - error = mach_vm_region_recurse(port, ®ion_addr, ®ion_size, &nesting_depth, (vm_region_recurse_info_t)®ion_info, &count); + error = mach_vm_region_recurse(port, ®ion_addr, ®ion_size, &nesting_depth, + (vm_region_recurse_info_t)®ion_info, &count); if (error != err_none) { DETOUR_BREAK(); goto fail; @@ -984,7 +985,8 @@ mach_error_t detour_detach(detour_func_t* inout_pointer, detour_func_t detour) } const vm_prot_t old_perm = region_info.protection; - error = mach_vm_protect(port, (mach_vm_address_t)target, PAGE_SIZE, false, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); + error = mach_vm_protect(port, (mach_vm_address_t)target, PAGE_SIZE, false, + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); if (error != err_none) { DETOUR_BREAK(); goto fail;