102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
hook_registry.c
Go to the documentation of this file.
1#include <stdint.h>
2#include <string.h>
3
4#include <dttr_runtime.h>
5#include <dttr_test_support.h>
6
7static uint8_t patch_target[4] = {0x10, 0x20, 0x30, 0x40};
8static void *pointer_target = (void *)0x11112222u;
9
12
13 const uint8_t original[] = {0x10, 0x20, 0x30, 0x40};
14 const uint8_t patch[] = {0xAA, 0xBB, 0xCC, 0xDD};
15 memcpy(patch_target, original, sizeof(original));
16
18 (uintptr_t)patch_target,
19 patch,
20 sizeof(patch)
21 );
22 assert_non_null(hook);
23 assert_memory_equal(patch_target, patch, sizeof(patch));
24
26 assert_memory_equal(patch_target, original, sizeof(original));
27}
28
31
32 void *original = (void *)0x11112222u;
33 void *replacement = (void *)0x33334444u;
34 void *out_original = NULL;
35 pointer_target = original;
36
38 (uintptr_t)&pointer_target,
39 replacement,
40 &out_original
41 );
42 assert_non_null(hook);
43 assert_ptr_equal(out_original, original);
44 assert_ptr_equal(pointer_target, replacement);
45
47 assert_ptr_equal(pointer_target, original);
48}
49
52
53 uint8_t first = 0x11;
54 uint8_t second = 0x22;
55 const uint8_t first_patch = 0xA1;
56 const uint8_t second_patch = 0xB2;
57 int owner_a = 0;
58 int owner_b = 0;
59
60 void *previous_owner = DTTR_Core_HookSetOwner(&owner_a);
62 (uintptr_t)&first,
63 &first_patch,
64 1
65 );
66 assert_non_null(first_hook);
67 DTTR_Core_HookSetOwner(&owner_b);
69 (uintptr_t)&second,
70 &second_patch,
71 1
72 );
73 assert_non_null(second_hook);
74 DTTR_Core_HookSetOwner(previous_owner);
75
77 assert_int_equal(first, 0x11);
78 assert_int_equal(second, second_patch);
79
81 assert_int_equal(second, 0x22);
82}
83
86
87 uint8_t target = 0x55;
88 const uint8_t patch = 0x66;
89 DTTR_Core_Hook *hook = DTTR_Core_HookPatchBytes((uintptr_t)&target, &patch, 1);
90 assert_non_null(hook);
91 assert_int_equal(target, patch);
92
94 assert_int_equal(target, 0x55);
95
96 DTTR_Core_Hook *second_hook = DTTR_Core_HookPatchBytes((uintptr_t)&target, &patch, 1);
97 assert_non_null(second_hook);
98 assert_int_equal(target, patch);
100 assert_int_equal(target, 0x55);
101}
102
105
106 uint8_t target[4] = {0x10, 0x20, 0x30, 0x40};
107 const uint8_t first_patch[] = {0xAA, 0xBB};
108 const uint8_t overlapping_patch[] = {0xCC, 0xDD};
109
111 (uintptr_t)&target[1],
112 first_patch,
113 sizeof(first_patch)
114 );
115 assert_non_null(first_hook);
116 assert_memory_equal(&target[1], first_patch, sizeof(first_patch));
117
118 DTTR_Core_Hook *overlapping_hook = DTTR_Core_HookPatchBytes(
119 (uintptr_t)&target[2],
120 overlapping_patch,
121 sizeof(overlapping_patch)
122 );
123 assert_null(overlapping_hook);
124 assert_int_equal(target[2], 0xBB);
125
126 DTTR_Core_HookDetach(first_hook);
127 assert_memory_equal(target, ((uint8_t[]){0x10, 0x20, 0x30, 0x40}), sizeof(target));
128}
129
130typedef int(__cdecl *hook_target_fn)(int value);
133static int chain_call_log[2];
134static size_t chain_call_count = 0;
135
136__attribute__((noinline)) static int __cdecl hook_target(int value) {
137 volatile int extra = 7;
138 return value + extra;
139}
140
141static void chain_log(int id) {
144}
145
146static int __cdecl chain_detour_a(int value) {
147 chain_log(1);
148 assert_non_null(chain_original_a);
149 return chain_original_a(value) + 10;
150}
151
152static int __cdecl chain_detour_b(int value) {
153 chain_log(2);
154 assert_non_null(chain_original_b);
155 return chain_original_b(value) + 100;
156}
157
163
164 assert_int_equal(hook_target(5), 12);
165
167 (uintptr_t)hook_target,
168 0,
170 (void **)&chain_original_a
171 );
172 assert_non_null(hook_a);
173 assert_non_null(chain_original_a);
174
176 (uintptr_t)hook_target,
177 0,
179 (void **)&chain_original_b
180 );
181 assert_non_null(hook_b);
182 assert_non_null(chain_original_b);
183
184 assert_int_equal(hook_target(5), 122);
185 assert_int_equal(chain_call_count, 2);
186 assert_int_equal(chain_call_log[0], 2);
187 assert_int_equal(chain_call_log[1], 1);
188
189 DTTR_Core_HookDetach(hook_b);
192 assert_int_equal(hook_target(5), 22);
193 assert_int_equal(chain_call_count, 1);
194 assert_int_equal(chain_call_log[0], 1);
195
196 DTTR_Core_HookDetach(hook_a);
198 assert_int_equal(hook_target(5), 12);
199}
200
201static const DTTR_TestCase TEST_CASES[] = {
202 {"hook-registry-patch-bytes", test_patch_bytes_detach_restores_original},
203 {"hook-registry-pointer", test_pointer_hook_detach_restores_original},
204 {"hook-registry-owner-detach", test_owner_detach_only_detaches_matching_owner},
205 {"hook-registry-cleanup-all", test_cleanup_all_restores_hooks_and_allows_reuse},
206 {"hook-registry-overlap", test_overlapping_byte_patches_are_rejected},
207 {"hook-registry-function-chain", test_function_hooks_chain_and_detach},
208};
209
const DTTR_BackendState * state
void * id
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
DTTR_Graphics_COM_DirectDrawSurface7 DWORD DWORD void void DWORD flags DTTR_Graphics_COM_DirectDrawSurface7 void void *cb void * target
static const DTTR_TestCase TEST_CASES[]
Definition core.c:17
#define DTTR_TEST_ARRAY_COUNT(TESTS)
#define DTTR_TEST_MAIN(TESTS)
static uint8_t patch_target[4]
Definition core.c:14
DTTR_Core_Hook * DTTR_Core_HookAttachPointer(uintptr_t addr, void *new_value, void **out_original)
DTTR_Core_Hook * DTTR_Core_HookAttachFunction(uintptr_t addr, int prologue_size, void *handler, void **out_original)
DTTR_Core_Hook * DTTR_Core_HookPatchBytes(uintptr_t addr, const uint8_t *bytes, size_t size)
void DTTR_Core_HookCleanupAll()
Detach all remaining hooks and free the sigscan cache.
void DTTR_Core_HookDetach(DTTR_Core_Hook *hook)
void DTTR_Core_HookDetachOwner(void *owner)
void * DTTR_Core_HookSetOwner(void *owner)
static void test_owner_detach_only_detaches_matching_owner(void **state)
static void * pointer_target
int(* hook_target_fn)(int value)
static void test_patch_bytes_detach_restores_original(void **state)
static void chain_log(int id)
static int chain_call_log[2]
static void test_pointer_hook_detach_restores_original(void **state)
static void test_overlapping_byte_patches_are_rejected(void **state)
static void test_function_hooks_chain_and_detach(void **state)
static int chain_detour_b(int value)
static hook_target_fn chain_original_a
static hook_target_fn chain_original_b
static size_t chain_call_count
__attribute__((noinline))
static void test_cleanup_all_restores_hooks_and_allows_reuse(void **state)
static int chain_detour_a(int value)