// Copyright (c) Microsoft Corporation. All rights reserved. // Copyright (c) Lysann Tranvouez. All rights reserved. #include "mach_detours.h" #include "detours_internal.h" #include "arm64/detours_arm64.h" #include #include #include #include //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Trampoline Memory Management typedef struct detour_region { uint32_t signature; struct detour_region* next; // Next region in list of regions. detour_trampoline* free_list_head; // List of free trampolines in this region. } detour_region; static const uint32_t DETOUR_REGION_SIGNATURE = 'Rrtd'; static const uint32_t DETOUR_REGION_SIZE = 0x10000; static const uint32_t DETOUR_TRAMPOLINES_PER_REGION = (DETOUR_REGION_SIZE / sizeof(detour_trampoline)) - 1; static detour_region* s_regions_head = nullptr; static detour_region* s_default_region = nullptr; static mach_error_t internal_detour_writable_trampoline_regions() { // 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; } } return err_none; } static void internal_detour_runnable_trampoline_regions() { // Mark all the regions as executable. 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_EXECUTE); if (error != err_none) { DETOUR_BREAK(); } } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Transactions typedef enum detour_operation_kind { attach, detach, } detour_operation_kind; typedef struct detour_operation { struct detour_operation* next; detour_operation_kind kind; uint8_t** pointer; uint8_t* target; struct 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 _Atomic(thread_t) s_transaction_thread = THREAD_NULL; static detour_operation* s_pending_operations_head = nullptr; static detour_pending_thread* s_pending_threads_head = nullptr; static mach_error_t s_pending_error = err_none; static void** s_pending_error_pointer = nullptr; mach_error_t detour_transaction_begin() { // Make sure only one thread can start a transaction. thread_t expected = THREAD_NULL; // ReSharper disable once CppIncompatiblePointerConversion if (!atomic_compare_exchange_strong(&s_transaction_thread, &expected, mach_thread_self())) { return detour_err_in_progress; } s_pending_operations_head = nullptr; s_pending_threads_head = nullptr; s_pending_error_pointer = nullptr; // Make sure the trampoline pages are writable. s_pending_error = internal_detour_writable_trampoline_regions(); return s_pending_error; }