diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..0fd9b90
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
index ead1d8a..0844927 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -244,5 +244,15 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b76a736..e50253f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,4 +6,6 @@ set(CMAKE_C_STANDARD 23)
add_library(mach_detours SHARED
mach_detours.c
mach_detours.h
+ arm64/detours_arm64.h
+ detours_internal.h
)
diff --git a/arm64/detours_arm64.h b/arm64/detours_arm64.h
new file mode 100644
index 0000000..9120780
--- /dev/null
+++ b/arm64/detours_arm64.h
@@ -0,0 +1,53 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Lysann Tranvouez. All rights reserved.
+
+#pragma once
+#ifndef MACH_DETOURS_ARM64_H
+#define MACH_DETOURS_ARM64_H
+
+typedef struct detour_align
+{
+ uint8_t obTarget;
+ uint8_t obTrampoline;
+} detour_align;
+
+typedef struct detour_trampoline
+{
+ // An ARM64 instruction is 4 bytes long.
+ //
+ // The overwrite is always composed of 3 instructions (12 bytes) which perform an indirect jump
+ // using _DETOUR_TRAMPOLINE::pbDetour as the address holding the target location.
+ //
+ // Copied instructions can expand.
+ //
+ // The scheme using MovImmediate can cause an instruction
+ // to grow as much as 6 times.
+ // That would be Bcc or Tbz with a large address space:
+ // 4 instructions to form immediate
+ // inverted tbz/bcc
+ // br
+ //
+ // An expansion of 4 is not uncommon -- bl/blr and small address space:
+ // 3 instructions to form immediate
+ // br or brl
+ //
+ // A theoretical maximum for rbCode is thefore 4*4*6 + 16 = 112 (another 16 for jmp to pbRemain).
+ //
+ // With literals, the maximum expansion is 5, including the literals: 4*4*5 + 16 = 96.
+ //
+ // The number is rounded up to 128. m_rbScratchDst should match this.
+ //
+ uint8_t rbCode[128]; // target code + jmp to pbRemain
+ uint8_t cbCode; // size of moved target code.
+ uint8_t cbCodeBreak[3]; // padding to make debugging easier.
+ uint8_t rbRestore[24]; // original target code.
+ uint8_t cbRestore; // size of original target code.
+ uint8_t cbRestoreBreak[3]; // padding to make debugging easier.
+ detour_align rAlign[8]; // instruction alignment array.
+ uint8_t* pbRemain; // first instruction after moved code. [free list]
+ uint8_t* pbDetour; // first instruction of detour function.
+} detour_trampoline;
+
+static_assert(sizeof(detour_trampoline) == 192);
+
+#endif //MACH_DETOURS_ARM64_H
\ No newline at end of file
diff --git a/detours_internal.h b/detours_internal.h
new file mode 100644
index 0000000..cb03395
--- /dev/null
+++ b/detours_internal.h
@@ -0,0 +1,18 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Lysann Tranvouez. All rights reserved.
+
+#pragma once
+#ifndef MACH_DETOURS_INTERNAL_H
+#define MACH_DETOURS_INTERNAL_H
+
+#ifndef DETOUR_TRACE
+#if DETOUR_DEBUG
+#define DETOUR_TRACE(x) printf x
+#define DETOUR_BREAK() raise(SIGTRAP)
+#else
+#define DETOUR_TRACE(x)
+#define DETOUR_BREAK()
+#endif
+#endif
+
+#endif //MACH_DETOURS_INTERNAL_H
\ No newline at end of file
diff --git a/mach_detours.c b/mach_detours.c
index 0e303ab..632d9e2 100644
--- a/mach_detours.c
+++ b/mach_detours.c
@@ -1,8 +1,104 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Lysann Tranvouez. All rights reserved.
+
#include "mach_detours.h"
-#include
+#include "detours_internal.h"
+#include "arm64/detours_arm64.h"
-void hello(void)
+#include
+#include
+
+#include
+#include
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Trampoline Memory Management
+
+typedef struct detour_region
{
- printf("Hello, World!\n");
-}
\ No newline at end of file
+ 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;
+}
diff --git a/mach_detours.h b/mach_detours.h
index 3d117f4..002e880 100644
--- a/mach_detours.h
+++ b/mach_detours.h
@@ -1,3 +1,26 @@
-#pragma once
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Copyright (c) Lysann Tranvouez. All rights reserved.
-void hello();
\ No newline at end of file
+#pragma once
+#ifndef MACH_DETOURS_H
+#define MACH_DETOURS_H
+
+#include
+#include
+
+typedef void* detour_func_t;
+
+#define detour_err_in_progress (err_local | 1)
+
+mach_error_t detour_transaction_begin();
+mach_error_t detour_transaction_abort();
+mach_error_t detour_transaction_commit();
+mach_error_t detour_transaction_commit_ex(detour_func_t** out_failed_target);
+
+mach_error_t detour_manage_thread(thread_t thread);
+
+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);
+
+#endif // MACH_DETOURS_H