even more docs
This commit is contained in:
parent
2eeb075739
commit
d126d9abbf
8 changed files with 117 additions and 3 deletions
11
README.md
Normal file
11
README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# mach-detours
|
||||||
|
|
||||||
|
An instrumentation library for modern arm64 macOS.
|
||||||
|
|
||||||
|
[mach_override](https://github.com/rentzsch/mach_override) is no longer maintained and doesn't run on arm64 or modern macOS with tightened security.<br/>
|
||||||
|
[Microsoft-Detours](https://github.com/microsoft/Detours) is Windows only but supports arm64.<br/>
|
||||||
|
**mach-detours** is based on Microsoft-Detours, but adapted to macOS (using `mach/mach.h` system functions), and limited to only arm64.
|
||||||
|
|
||||||
|
Tested on M4 Pro running macOS 15.1.
|
||||||
|
|
||||||
|
For technical documentation, see the [documentation overview](docs/overview.md).
|
||||||
BIN
docs/img/interception-figure-1-control-flow.png
Normal file
BIN
docs/img/interception-figure-1-control-flow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8 KiB |
BIN
docs/img/interception-figure-2-instructions.png
Normal file
BIN
docs/img/interception-figure-2-instructions.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
32
docs/interception.md
Normal file
32
docs/interception.md
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Interception of Binary Functions
|
||||||
|
|
||||||
|
The mach-detours library enables interception of function calls. Interception code is applied dynamically at runtime. mach-detours replaces the first few instructions of the *target function* with an unconditional jump to the user-provided *detour function*. Instructions from the target function are preserved in a *trampoline function*. The trampoline consists of the instructions removed from the target function and an unconditional branch to the remainder of the target function.
|
||||||
|
|
||||||
|
When execution reaches the target function, control jumps directly to the user-supplied detour function. The detour function performs whatever interception *preprocessing* is appropriate. The detour function can return control to the *source* function or it can call the trampoline function, which invokes the target function without interception. When the target function completes, it returns control to the detour function. The detour function performs appropriate *postprocessing* and returns control to the source function. **Figure 1** shows the logical flow of control for function invocation with and without interception.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
<figure>
|
||||||
|
<img src="img/interception-figure-1-control-flow.png">
|
||||||
|
<figcaption>Figure 1. Control flow of invocation without and with a detour in place.</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
The mach-detours library intercepts target functions by rewriting their in-process binary image. For each target function, mach-detours actually rewrites two functions, the target function and the matching trampoline function, and one function pointer, the target pointer. The trampoline function is allocated dynamically by mach-detours. The trampoline contains the initial instructions from the target function and a jump to the remainder of the target function.
|
||||||
|
|
||||||
|
The *target pointer* is initialized by the user to point to the target function. After a detour is attached to the target function, the target pointer is modified to point to the trampoline function. After the detour is detached from the target function, the target pointer is restored to point to the original target function.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
<figure>
|
||||||
|
<img src="img/interception-figure-2-instructions.png">
|
||||||
|
<figcaption>Figure 2. Trampoline and target functions, before (on the left) and after (on the right) insertion of the detour.</figcaption>
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
**Figure 2** shows the insertion of a detour. To detour a target function, mach-detours first allocates memory for the dynamic trampoline function and then enables write access to both the target and the trampoline. (Note: Making the memory containing these functions writable means it also has to be non-executable under modern macOS. This poses some restrictions on the code that can be run while the detour is being inserted. Often the user would want to suspend other threads, mach-detours provides APIs for that.)
|
||||||
|
|
||||||
|
Starting with the first instruction, mach-detours copies instructions from the target to the trampoline until enough bytes have been copied to insert an unconditional jump to the detour. If the target function is too short, mach-detours aborts and returns an error code.<br/>
|
||||||
|
To copy instructions, mach-detours uses a simple table-driven disassembler. mach-detours adds a jump instruction from the end of the trampoline to the first non-copied instruction of the target function. mach-detours writes an unconditional jump instruction to the detour function as the first instruction of the target function. To finish, mach-detours restores the original page permissions on both the target and trampoline functions.
|
||||||
9
docs/overview.md
Normal file
9
docs/overview.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# mach-detours Overview
|
||||||
|
|
||||||
|
**mach-detours** is a library for intercepting binary functions on arm64 machines. Detours is most commonly used to intercept system API calls within an application, such as to add debugging instrumentation. Interception code is applied dynamically at runtime. mach-detours replaces the first few instructions of the _target function_ with an unconditional jump to the user-provided _detour function_. Instructions from the target function are placed in a _trampoline_. The address of the trampoline is placed in a _target pointer_. The detour function can either replace the target function or extend its semantics by invoking the target function as a subroutine through the target pointer to the trampoline.
|
||||||
|
|
||||||
|
Detours are inserted at execution time. The code of the target function is modified in memory, not on disk, thus enabling interception of binary functions at a very fine granularity. For example, the procedures in a shared library can be detoured in one execution of an application, while the original procedures are not detoured in another execution running at the same time. Unlike dyld interposing or symbol rebinding, the interception techniques used in the mach-detours library are guaranteed to work regardless of the method used by application or system code to locate the target function.
|
||||||
|
|
||||||
|
This technical overview of Detours is divided into two sections:
|
||||||
|
* [Interception of Binary Functions](interception.md)
|
||||||
|
* [Using mach-detours](using.md)
|
||||||
64
docs/using.md
Normal file
64
docs/using.md
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
# Using mach-detours
|
||||||
|
|
||||||
|
Two things are necessary in order to detour a target function: a target pointer containing the address of the target function and a detour function. For proper interception the target function, detour function, and the target pointer must have exactly the same call signature.
|
||||||
|
|
||||||
|
The code fragment in Figure 5 illustrates the usage of the mach-detours library. User code must include the `mach_detours.h` header file and link with the `mach_detours` library.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <mach_detours.h>
|
||||||
|
|
||||||
|
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((detour_func_t*)&real_strerror, (detour_func_t)my_strerror);
|
||||||
|
detour_transaction_commit();
|
||||||
|
|
||||||
|
assert(counter == 0);
|
||||||
|
strerror(0);
|
||||||
|
assert(counter == 1);
|
||||||
|
|
||||||
|
detour_transaction_begin();
|
||||||
|
detour_detach((detour_func_t*)&real_strerror, (detour_func_t)my_strerror);
|
||||||
|
detour_transaction_commit();
|
||||||
|
|
||||||
|
assert(counter == 1);
|
||||||
|
strerror(0);
|
||||||
|
assert(counter == 1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
###### Figure 5. [Simple example](../sample/main.c) detour to modify the strerror API.
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Interception of the target function is enabled by invoking the `detour_attach` API within a detour transaction. A detour transaction is marked by calls to the `detour_transaction_begin` API and the `detour_transaction_commit` API. The `detour_attach` API takes two arguments: the address of the target pointer and the pointer to the detour function. The target function is not given as an argument because it must already be stored in the target pointer.
|
||||||
|
|
||||||
|
In a multi-threaded application, `detour_manage_thread` API enlists threads in the transaction so that they are suspended while the transaction is being set up and so that their instruction pointers are appropriately updated when the transaction commits.
|
||||||
|
|
||||||
|
The `detour_attach` API allocates and prepares a trampoline for calling the target function. When the detour transaction commits, the target function and trampoline are rewritten and the target pointer is updated to point to the trampoline function.
|
||||||
|
|
||||||
|
Once a target function has been detoured, any call to the target function will be re-routed through the detour function. It is the responsibility of the detour function to copy arguments when invoking the target function through the trampoline. This is intuitive as the target function becomes simply a subroutine callable by the detour function.
|
||||||
|
|
||||||
|
Interception of a target function is removed by calling the `detour_detach` API within a detour transaction. Like the `detour_attach` API, the `detour_detach` API takes two arguments: the address of the target pointer and the pointer to the detour function. When the detour transaction commits, the target function is rewritten and restored to its original code, the trampoline function is deleted, and the target pointer is restored to point to the original target function.
|
||||||
|
|
||||||
|
In cases where detour functions need to inserted into an existing application without source code access, the detour functions should be packaged in a dylib. The dylib can be inserted into a new process using `DYLD_INSERT_LIBRARIES` or the mach task port API.
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "mach_detours.h"
|
#include <mach_detours.h>
|
||||||
|
|
||||||
char* (*real_strerror)(int errno) = strerror;
|
char* (*real_strerror)(int errno) = strerror;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -804,8 +804,6 @@ mach_error_t detour_attach_ex(detour_func_t* inout_pointer, detour_func_t detour
|
||||||
UNUSED_VARIABLE(trampoline_code);
|
UNUSED_VARIABLE(trampoline_code);
|
||||||
|
|
||||||
const mach_port_t port = mach_task_self();
|
const mach_port_t port = mach_task_self();
|
||||||
//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;
|
vm_region_submap_short_info_data_64_t region_info;
|
||||||
{
|
{
|
||||||
mach_vm_address_t region_addr = (mach_vm_address_t)target;
|
mach_vm_address_t region_addr = (mach_vm_address_t)target;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue