added test

This commit is contained in:
Lysann Tranvouez 2025-09-30 00:09:10 +02:00
parent ade0920e54
commit aaad260259
4 changed files with 94 additions and 42 deletions

View file

@ -14,7 +14,15 @@ add_library(mach_detours SHARED
src/arm64/detours_arm64.h src/arm64/detours_arm64.h
src/arm64/detours_arm64_disasm.cpp src/arm64/detours_arm64_disasm.cpp
) )
target_include_directories(mach_detours target_include_directories(mach_detours
PUBLIC "include" PUBLIC include
PRIVATE "src") PRIVATE src
)
add_executable(mach_detours_sample
sample/main.cpp
)
target_link_libraries(mach_detours_sample
PRIVATE mach_detours
)

View file

@ -5,6 +5,10 @@
#ifndef MACH_DETOURS_H #ifndef MACH_DETOURS_H
#define MACH_DETOURS_H #define MACH_DETOURS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <mach/error.h> #include <mach/error.h>
#include <mach/mach_types.h> #include <mach/mach_types.h>
@ -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_lower_bound(void* value);
void* detour_set_system_region_upper_bound(void* value); void* detour_set_system_region_upper_bound(void* value);
#ifdef __cplusplus
}
#endif
#endif // MACH_DETOURS_H #endif // MACH_DETOURS_H

34
sample/main.cpp Normal file
View file

@ -0,0 +1,34 @@
// Copyright (c) Lysann Tranvouez. All rights reserved.
#include <cassert>
#include "mach_detours.h"
#include <cstring>
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<detour_func_t*>(&real_strerror), reinterpret_cast<detour_func_t>(my_strerror));
detour_transaction_commit();
assert(counter == 0);
strerror(0);
assert(counter == 1);
return 0;
}

View file

@ -3,7 +3,7 @@
#include "mach_detours.h" #include "mach_detours.h"
#define DETOUR_DEBUG 1 #define DETOUR_DEBUG 0
#ifdef __arm64__ #ifdef __arm64__
#define DETOURS_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, static void* internal_detour_alloc_trampoline_allocate_new(const uint8_t* target,
detour_trampoline* lo, detour_trampoline* lo,
detour_trampoline* hi) detour_trampoline* hi)
{ {
void* try_addr = nullptr; 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. // First check the default region for a valid free block.
if (s_default_region && s_default_region->free_list_head && if (s_default_region && s_default_region->free_list_head &&
s_default_region->free_list_head >= lo && s_default_region->free_list_head <= hi) { 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; trampoline = s_default_region->free_list_head;
// do a last sanity check on region. // do a last sanity check on region.
if (trampoline < lo || trampoline > hi) { if (trampoline < lo || trampoline > hi) {
raise(SIGTRAP); DETOUR_BREAK();
return nullptr; return nullptr;
} }
s_default_region->free_list_head = (detour_trampoline*)trampoline->ptr_remain; 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_default_region->next = s_regions_head;
s_regions_head = s_default_region; s_regions_head = s_default_region;
DETOUR_TRACE((" Allocated region %p..%p\n\n", 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. // Put everything but the first trampoline on the free list.
uint8_t* free = nullptr; 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) { if (pc >= targetAddr && pc < targetAddr + op->trampoline->restore_code_size) {
uintptr_t new_pc = (uintptr_t)op->trampoline; uintptr_t new_pc = (uintptr_t)op->trampoline;
new_pc += internal_detour_align_from_target(op->trampoline, pc - targetAddr); 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, DETOUR_TRACE(("detours: thread %u was at 0x%" PRIXPTR ", moved to 0x%" PRIXPTR "\n",
pc, thread->thread, pc, new_pc));
new_pc);
arm_thread_state64_set_pc_fptr(threadState, new_pc); arm_thread_state64_set_pc_fptr(threadState, new_pc);
thread_set_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState, thread_set_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState,
ARM_THREAD_STATE64_COUNT); 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)) { if (pc >= trampAddr && pc < trampAddr + sizeof(*op->trampoline)) {
uintptr_t new_pc = (uintptr_t)op->target; uintptr_t new_pc = (uintptr_t)op->target;
new_pc += internal_detour_align_from_trampoline(op->trampoline, pc - trampAddr); 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, DETOUR_TRACE(("detours: thread %u was at 0x%" PRIXPTR ", moved to 0x%" PRIXPTR "\n",
pc, thread->thread, pc, new_pc));
new_pc);
arm_thread_state64_set_pc_fptr(threadState, new_pc); arm_thread_state64_set_pc_fptr(threadState, new_pc);
thread_set_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState, thread_set_state(thread->thread, ARM_THREAD_STATE64, (thread_state_t)&threadState,
ARM_THREAD_STATE64_COUNT); 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); 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) { if (out_real_trampoline) {
*out_real_trampoline = nullptr; *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)); op = calloc(1, sizeof(detour_operation));
if (!op) { if (!op) {
error = KERN_RESOURCE_SHORTAGE; error = KERN_RESOURCE_SHORTAGE;
fail: fail:
s_pending_error = error; s_pending_error = error;
DETOUR_BREAK(); DETOUR_BREAK();
stop: stop:
if (trampoline) { if (trampoline) {
internal_detour_free_trampoline(trampoline); internal_detour_free_trampoline(trampoline);
// ReSharper disable once CppDFAUnusedValue // 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 = 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); trampoline_code = internal_detour_gen_brk(trampoline_code, trampoline_code_limit);
UNUSED_VARIABLE(trampoline_code); 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; mach_vm_size_t region_size = 0;
natural_t nesting_depth = 99999; natural_t nesting_depth = 99999;
mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
error = mach_vm_region_recurse(port, &region_addr, &region_size, &nesting_depth, (vm_region_recurse_info_t)&region_info, &count); error = mach_vm_region_recurse(port, &region_addr, &region_size, &nesting_depth,
(vm_region_recurse_info_t)&region_info, &count);
if (error != err_none) { if (error != err_none) {
DETOUR_BREAK(); DETOUR_BREAK();
goto fail; 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: " DETOUR_TRACE(("detours: target=%p: "
"%02x %02x %02x %02x " "%02x %02x %02x %02x "
"%02x %02x %02x %02x " "%02x %02x %02x %02x "
"%02x %02x %02x %02x\n", "%02x %02x %02x %02x\n",
target, target,
target[0], target[1], target[2], target[3], target[0], target[1], target[2], target[3],
target[4], target[5], target[6], target[7], target[4], target[5], target[6], target[7],
target[8], target[9], target[10], target[11])); target[8], target[9], target[10], target[11]));
DETOUR_TRACE(("detours: trampoline =%p: " DETOUR_TRACE(("detours: trampoline =%p: "
"%02x %02x %02x %02x " "%02x %02x %02x %02x "
"%02x %02x %02x %02x " "%02x %02x %02x %02x "
"%02x %02x %02x %02x\n", "%02x %02x %02x %02x\n",
trampoline, trampoline,
trampoline->code[0], trampoline->code[1], trampoline->code[0], trampoline->code[1],
trampoline->code[2], trampoline->code[3], trampoline->code[2], trampoline->code[3],
trampoline->code[4], trampoline->code[5], trampoline->code[4], trampoline->code[5],
trampoline->code[6], trampoline->code[7], trampoline->code[6], trampoline->code[7],
trampoline->code[8], trampoline->code[9], trampoline->code[8], trampoline->code[9],
trampoline->code[10], trampoline->code[11])); trampoline->code[10], trampoline->code[11]));
op->kind = detour_operation_kind_attach; op->kind = detour_operation_kind_attach;
op->pointer = (uint8_t**)inout_pointer; 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)); detour_operation* op = calloc(1, sizeof(detour_operation));
if (!op) { if (!op) {
error = KERN_RESOURCE_SHORTAGE; error = KERN_RESOURCE_SHORTAGE;
fail: fail:
s_pending_error = error; s_pending_error = error;
DETOUR_BREAK(); DETOUR_BREAK();
stop: stop:
free(op); free(op);
// ReSharper disable once CppDFAUnusedValue // ReSharper disable once CppDFAUnusedValue
op = nullptr; 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; mach_vm_size_t region_size = 0;
natural_t nesting_depth = 99999; natural_t nesting_depth = 99999;
mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
error = mach_vm_region_recurse(port, &region_addr, &region_size, &nesting_depth, (vm_region_recurse_info_t)&region_info, &count); error = mach_vm_region_recurse(port, &region_addr, &region_size, &nesting_depth,
(vm_region_recurse_info_t)&region_info, &count);
if (error != err_none) { if (error != err_none) {
DETOUR_BREAK(); DETOUR_BREAK();
goto fail; 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; 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) { if (error != err_none) {
DETOUR_BREAK(); DETOUR_BREAK();
goto fail; goto fail;