test overriding a local function

This commit is contained in:
Lysann Tranvouez 2025-10-01 23:55:17 +02:00
parent 4947fb5553
commit 5e76138b53
7 changed files with 81 additions and 1 deletions

View file

@ -30,6 +30,10 @@ mach_error_t detour_attach(detour_func_t* inout_pointer, detour_func_t 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);
mach_error_t detour_detach(detour_func_t* inout_pointer, detour_func_t detour);
mach_error_t detour_attach_and_commit(detour_func_t* inout_pointer, detour_func_t detour);
mach_error_t detour_attach_and_commit_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_detach_and_commit(detour_func_t* inout_pointer, detour_func_t detour);
bool detour_set_ignore_too_small(bool value);
bool detour_set_retain_regions(bool value);
void* detour_set_system_region_lower_bound(void* value);

View file

@ -1,3 +1,5 @@
# Copyright (c) Lysann Tranvouez. All rights reserved.
add_executable(mach_detours_sample
main.c
)

View file

@ -917,6 +917,29 @@ mach_error_t detour_detach(detour_func_t* inout_pointer, detour_func_t detour)
return ERR_SUCCESS;
}
mach_error_t detour_attach_and_commit(detour_func_t* inout_pointer, detour_func_t detour)
{
return detour_attach_and_commit_ex(inout_pointer, detour, nullptr, nullptr, nullptr);
}
mach_error_t detour_attach_and_commit_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)
{
const mach_error_t error = detour_attach_ex(inout_pointer, detour, out_real_trampoline, out_real_target, out_real_detour);
if (error != err_none) {
return error;
}
return detour_transaction_commit();
}
mach_error_t detour_detach_and_commit(detour_func_t* inout_pointer, detour_func_t detour)
{
const mach_error_t error = detour_detach(inout_pointer, detour);
if (error != err_none) {
return error;
}
return detour_transaction_commit();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// API Setters

View file

@ -1,6 +1,11 @@
# Copyright (c) Lysann Tranvouez. All rights reserved.
add_executable(mach_detours_tests
test.cpp)
# The target function must be in a shared library because otherwise it might be in the same code page as the test.cpp functions.
# Between attach and commit the target function's code page is not executable, which can mean our test driver code would not
# be executable.
add_library(mach_detours_test_func SHARED
test_func.c
test_func.h

View file

@ -4,6 +4,8 @@
#include <mach_detours.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "test_func.h"
static int testFunctionDetourCounter = 0;
@ -13,7 +15,7 @@ int testFunctionDetour()
return 94;
}
TEST_CASE( "Overriding custom function" )
TEST_CASE( "Overriding custom function in dylib" )
{
int (*realTestFunction)() = testFunction;
testFunctionCounter = 0;
@ -35,3 +37,43 @@ TEST_CASE( "Overriding custom function" )
REQUIRE( testFunctionCounter == 1 );
REQUIRE( testFunctionDetourCounter == 1 );
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static int localFunctionCounter = 0;
int localFunction()
{
localFunctionCounter++;
return 67;
}
static int localFunctionDetourCounter = 0;
int localFunctionDetour()
{
localFunctionDetourCounter++;
return 12;
}
TEST_CASE( "Overriding local function" )
{
int (*realFunction)() = localFunction;
localFunctionCounter = 0;
localFunctionDetourCounter = 0;
REQUIRE( localFunction() == 67 );
REQUIRE( localFunctionCounter == 1 );
REQUIRE( localFunctionDetourCounter == 0 );
CHECK( detour_transaction_begin() == err_none );
// Overriding a local function requires using detour_attach_and_commit instead of calling attach and commit individually.
// Otherwise when we return from attach (and before commit), the code page with the local function is marked as read+write (but _not_ executable).
// There is a good chance the code we return to (in this case the test function) is on the same memory page and can therefore not be executed (until we call commit).
CHECK( detour_attach_and_commit(reinterpret_cast<detour_func_t*>(&realFunction), reinterpret_cast<detour_func_t>(localFunctionDetour)) == err_none );
REQUIRE( realFunction != localFunction );
REQUIRE( localFunctionCounter == 1 );
REQUIRE( localFunctionDetourCounter == 0 );
REQUIRE( localFunction() == 12 );
REQUIRE( localFunctionCounter == 1 );
REQUIRE( localFunctionDetourCounter == 1 );
}

View file

@ -1,3 +1,5 @@
// Copyright (c) Lysann Tranvouez. All rights reserved.
#include "test_func.h"
int testFunctionCounter = 0;

View file

@ -1,3 +1,5 @@
// Copyright (c) Lysann Tranvouez. All rights reserved.
#pragma once
#ifdef __cplusplus