more tests and tweaks/fixes as a result

This commit is contained in:
Lysann Tranvouez 2025-10-02 22:18:10 +02:00
parent 327dfb0cb7
commit 058069d1f3
6 changed files with 194 additions and 101 deletions

View file

@ -7,6 +7,7 @@
#include "detours_internal.h"
#include <inttypes.h>
#include <stdint.h>
#include <mach/mach.h>

View file

@ -44,16 +44,20 @@ static detour_region* s_default_region = nullptr;
static mach_error_t internal_detour_writable_trampoline_regions()
{
mach_error_t result = err_none;
// Mark all the regions as writable.
const mach_port_t port = mach_task_self();
for (detour_region* pRegion = s_regions_head; pRegion != NULL; pRegion = pRegion->next) {
const mach_error_t error = mach_vm_protect(port, (mach_vm_address_t)pRegion, DETOUR_REGION_SIZE, false,
VM_PROT_READ | VM_PROT_WRITE);
if (error != err_none) {
return error;
DETOUR_BREAK();
result = error;
}
DETOUR_TRACE(("mach_vm_protect(%p, %" PRIu32 ", %d)\n", (void*)pRegion, DETOUR_REGION_SIZE, VM_PROT_READ | VM_PROT_WRITE));
}
return err_none;
return result;
}
static void internal_detour_runnable_trampoline_regions()
@ -66,6 +70,7 @@ static void internal_detour_runnable_trampoline_regions()
if (error != err_none) {
DETOUR_BREAK();
}
DETOUR_TRACE(("mach_vm_protect(%p, %" PRIu32 ", %d)\n", (void*)pRegion, DETOUR_REGION_SIZE, VM_PROT_READ | VM_PROT_EXECUTE));
}
}
@ -96,7 +101,7 @@ static void* internal_detour_alloc_region_from_lo(const uint8_t* lo, const uint8
const vm_map_t task_self = mach_task_self();
for (vm_address_t page = (vm_address_t)try_addr; page < (vm_address_t)hi; page += PAGE_SIZE) {
DETOUR_TRACE((" Try %p\n", (void*)page));
//DETOUR_TRACE((" Try %p\n", (void*)page));
const mach_error_t err = vm_allocate(task_self, &page, DETOUR_REGION_SIZE, 0);
if (err == err_none) {
@ -121,7 +126,7 @@ static void* internal_detour_alloc_region_from_hi(const uint8_t* lo, const uint8
const vm_map_t task_self = mach_task_self();
for (vm_address_t page = try_addr; page > (vm_address_t)lo; page -= PAGE_SIZE) {
DETOUR_TRACE((" Try %p\n", (void*)page));
//DETOUR_TRACE((" Try %p\n", (void*)page));
if ((void*)page >= s_system_region_lower_bound && (void*)page <= s_system_region_upper_bound) {
// Skip region reserved for system DLLs, but preserve address space entropy.
try_addr -= 0x08000000;
@ -339,6 +344,9 @@ mach_error_t detour_transaction_begin()
mach_error_t detour_transaction_abort()
{
if (s_transaction_thread == THREAD_NULL) {
return err_none;
}
if (s_transaction_thread != mach_thread_self()) {
return detour_err_wrong_thread;
}
@ -349,6 +357,7 @@ mach_error_t detour_transaction_abort()
DETOUR_CHECK(
mach_vm_protect(port, (mach_vm_address_t)operation->target, operation->trampoline->restore_code_size, false,
operation->perm));
DETOUR_TRACE(("mach_vm_protect(%p, %" PRIu8 ", %d)\n", (void*)operation->target, operation->trampoline->restore_code_size, operation->perm));
if (operation->kind == detour_operation_kind_attach) {
if (operation->trampoline) {
@ -422,7 +431,7 @@ mach_error_t detour_transaction_commit_ex(detour_func_t** out_failed_target)
operation->target[8], operation->target[9], operation->target[10], operation->target[11]));
detour_platform_operation_commit_detour(operation);
*operation->pointer = detour_platform_operation_get_trampoline_ptr(operation)
*operation->pointer = detour_platform_operation_get_trampoline_ptr(operation);
DETOUR_TRACE(("detours: target=%p: "
"%02x %02x %02x %02x "
@ -467,6 +476,7 @@ mach_error_t detour_transaction_commit_ex(detour_func_t** out_failed_target)
DETOUR_CHECK(
mach_vm_protect(port, (mach_vm_address_t)operation->target, operation->trampoline->restore_code_size, false,
operation->perm));
DETOUR_TRACE(("mach_vm_protect(%p, %" PRIu8 ", %d)\n", (void*)operation->target, operation->trampoline->restore_code_size, operation->perm));
if (operation->kind == detour_operation_kind_detach && operation->trampoline) {
internal_detour_free_trampoline(operation->trampoline);
@ -675,10 +685,10 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
const uint8_t* src = target;
uint8_t* trampoline_code = trampoline->code;
uint8_t* trampoline_code_limit = trampoline_code + sizeof(trampoline->code);
uint32_t offset_target = 0;
uint32_t target_override_len = 0;
uint32_t align_idx = 0;
while (offset_target < DETOUR_PLATFORM_SIZE_OF_JMP) {
while (target_override_len < DETOUR_PLATFORM_SIZE_OF_JMP) {
const uint8_t* curr_op = src;
uint32_t extra_len = 0;
@ -686,8 +696,8 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
src = internal_detour_copy_instruction(trampoline_code, src, &extra_len);
DETOUR_TRACE((" after: src=%p (copied %d bytes)\n", src, (int)(src - curr_op)));
trampoline_code += (src - curr_op) + extra_len;
offset_target = (int32_t)(src - target);
trampoline->align[align_idx].offset_target = offset_target;
target_override_len = (int32_t)(src - target);
trampoline->align[align_idx].offset_target = target_override_len;
trampoline->align[align_idx].offset_trampoline = trampoline_code - trampoline->code;
align_idx++;
@ -701,14 +711,14 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
}
// Consume, but don't duplicate padding if it is needed and available.
while (offset_target < DETOUR_PLATFORM_SIZE_OF_JMP) {
while (target_override_len < DETOUR_PLATFORM_SIZE_OF_JMP) {
const uint32_t len_filler = detour_platform_is_code_filler(src);
if (len_filler == 0) {
break;
}
src += len_filler;
offset_target = (int32_t)(src - target);
target_override_len = (int32_t)(src - target);
}
#if DETOUR_DEBUG
@ -725,7 +735,7 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
}
#endif
if (offset_target < DETOUR_PLATFORM_SIZE_OF_JMP || align_idx > ARRAYSIZE(trampoline->align)) {
if (target_override_len < DETOUR_PLATFORM_SIZE_OF_JMP || align_idx > ARRAYSIZE(trampoline->align)) {
// Too few instructions.
error = detour_err_too_small;
if (s_ignore_too_small) {
@ -741,17 +751,17 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
}
trampoline->code_size = (uint8_t)(trampoline_code - trampoline->code);
trampoline->restore_code_size = (uint8_t)offset_target;
memcpy(trampoline->restore_code, target, offset_target);
trampoline->restore_code_size = (uint8_t)target_override_len;
memcpy(trampoline->restore_code, target, target_override_len);
if (offset_target > sizeof(trampoline->code) - DETOUR_PLATFORM_SIZE_OF_JMP) {
if (target_override_len > sizeof(trampoline->code) - DETOUR_PLATFORM_SIZE_OF_JMP) {
// Too many instructions.
error = detour_err_too_large;
DETOUR_BREAK();
goto fail;
}
trampoline->ptr_remain = target + offset_target;
trampoline->ptr_remain = target + target_override_len;
trampoline->ptr_detour = (uint8_t*)detour;
trampoline_code = trampoline->code + trampoline->code_size;
@ -762,7 +772,7 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
UNUSED_VARIABLE(trampoline_code);
const mach_port_t port = mach_task_self();
const mach_vm_address_t page_addr = internal_detour_round_down_to_page((uintptr_t)target);
//const mach_vm_address_t page_addr = internal_detour_round_down_to_page((uintptr_t)target);
vm_region_submap_short_info_data_64_t region_info;
{
@ -779,11 +789,12 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
}
const vm_prot_t old_perm = region_info.protection;
error = mach_vm_protect(port, page_addr, PAGE_SIZE, false, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
error = mach_vm_protect(port, (mach_vm_address_t)target, target_override_len, false, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
if (error != err_none) {
DETOUR_BREAK();
goto fail;
}
DETOUR_TRACE(("mach_vm_protect(%p, %" PRIu32 ", %d)\n", (void*)target, target_override_len, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY));
DETOUR_TRACE(("detours: target=%p: "
"%02x %02x %02x %02x "
@ -793,7 +804,7 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
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: "
DETOUR_TRACE(("detours: trampoline=%p: "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x "
"%02x %02x %02x %02x\n",
@ -860,7 +871,7 @@ mach_error_t detour_detach(detour_func_t* inout_pointer, detour_func_t detour)
detour = detour_platform_skip_jmp(detour);
// Verify that Trampoline is in place.
const int32_t restore_code_size = trampoline->restore_code_size;
const uint32_t restore_code_size = trampoline->restore_code_size;
uint8_t* target = trampoline->ptr_remain - restore_code_size;
if (restore_code_size == 0 || restore_code_size > sizeof(trampoline->code)) {
error = KERN_FAILURE;
@ -899,12 +910,13 @@ 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,
error = mach_vm_protect(port, (mach_vm_address_t)target, restore_code_size, false,
VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY);
if (error != err_none) {
DETOUR_BREAK();
goto fail;
}
DETOUR_TRACE(("mach_vm_protect(%p, %" PRIu32 ", %d)\n", (void*)target, restore_code_size, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY));
op->kind = detour_operation_kind_detach;
op->pointer = (uint8_t**)inout_pointer;
@ -926,6 +938,7 @@ mach_error_t detour_attach_and_commit_ex(detour_func_t* inout_pointer, detour_fu
{
const mach_error_t error = detour_attach_ex(inout_pointer, detour, out_real_trampoline, out_real_target, out_real_detour);
if (error != err_none) {
detour_transaction_abort();
return error;
}
return detour_transaction_commit();
@ -935,6 +948,7 @@ mach_error_t detour_detach_and_commit(detour_func_t* inout_pointer, detour_func_
{
const mach_error_t error = detour_detach(inout_pointer, detour);
if (error != err_none) {
detour_transaction_abort();
return error;
}
return detour_transaction_commit();