CommonLib
Loading...
Searching...
No Matches
Hooking.h
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include <../thirdparty/Detours/src/detours.h>
5
6#define __CMNLIB_INTERNAL_STATIC_LIB_ENROLMENT "Hooking macros on Windows use Detours."
7
8#define __CMNLIB_INTERNAL_STATIC_HOOK_IMPL(NAME, ADDRESS, INSTALLER)
9 static bool result_##NAME{};
10 static bool install_##NAME()
11 {
12 if (!ADDRESS)
13 return result_##NAME = false;
14
15 return result_##NAME = INSTALLER(NAME);
16 }
17 static bool runner_##NAME = install_##NAME();
18
19///
20/// Declares a pointer to a function in memory.
21///
22/// \param RETURN_TYPE The return type of the function.
23/// \param CALLING_CONVENTION The calling convention of the function (e.g. `__cdecl`, `__stdcall`, `__fastcall`, etc).
24/// This does not support optimised calling conventions, such as `__usercall` or `__userpurge`.
25/// \param FUNCTION_NAME The name of the function.
26/// \param ADDRESS The address of the function.
27/// \param __VA_ARGS__ The parameters of the function.
28///
29#define FUNCTION_PTR(RETURN_TYPE, CALLING_CONVENTION, FUNCTION_NAME, ADDRESS, ...)
30 RETURN_TYPE (CALLING_CONVENTION* FUNCTION_NAME)(__VA_ARGS__) = (RETURN_TYPE (CALLING_CONVENTION*)(__VA_ARGS__))(ADDRESS)
31
32///
33/// Loads a dynamic link library into memory and declares a pointer to an exported function from it.
34///
35/// \param RETURN_TYPE The return type of the function.
36/// \param LIBRARY_NAME The name of the dynamic link library.
37/// \param FUNCTION_NAME The name of the exported function.
38/// \param __VA_ARGS__ The parameters of the exported function.
39///
40#define IMPORT_FUNCTION_PTR(RETURN_TYPE, LIBRARY_NAME, FUNCTION_NAME, ...)
41 typedef RETURN_TYPE _##FUNCTION_NAME(__VA_ARGS__);
42 _##FUNCTION_NAME* FUNCTION_NAME = (_##FUNCTION_NAME*)IMPORT_FUNC(LIBRARY_NAME, #FUNCTION_NAME);
43
44///
45/// Defines the body of a hook for a function in memory.
46///
47/// \param RETURN_TYPE The return type of the function.
48/// \param CALLING_CONVENTION The calling convention of the function (e.g. `__cdecl`, `__stdcall`, `__fastcall`, etc).
49/// This does not support optimised calling conventions, such as `__usercall` or `__userpurge`.
50/// \param FUNCTION_NAME The name of the function.
51/// \param ADDRESS The address of the function.
52/// \param __VA_ARGS__ The parameters of the function.
53///
54#define HOOK(RETURN_TYPE, CALLING_CONVENTION, FUNCTION_NAME, ADDRESS, ...)
55 typedef RETURN_TYPE CALLING_CONVENTION FUNCTION_NAME(__VA_ARGS__);
56 FUNCTION_NAME* x_##FUNCTION_NAME = (FUNCTION_NAME*)(ADDRESS);
57 FUNCTION_NAME* original_##FUNCTION_NAME = x_##FUNCTION_NAME;
58 FUNCTION_NAME* post_##FUNCTION_NAME{};
59 RETURN_TYPE CALLING_CONVENTION impl_##FUNCTION_NAME(__VA_ARGS__)
60
61///
62/// Defines the body of a hook for a function in memory, and installs it upon initialisation.
63///
64/// \param RETURN_TYPE The return type of the function.
65/// \param CALLING_CONVENTION The calling convention of the function (e.g. `__cdecl`, `__stdcall`, `__fastcall`, etc).
66/// This does not support optimised calling conventions, such as `__usercall` or `__userpurge`.
67/// \param FUNCTION_NAME The name of the function.
68/// \param ADDRESS The address of the function.
69/// \param __VA_ARGS__ The parameters of the function.
70///
71/// \returns Use \ref GET_STATIC_HOOK_RESULT.
72///
73#define STATIC_HOOK(RETURN_TYPE, CALLING_CONVENTION, FUNCTION_NAME, ADDRESS, ...)
74 HOOK(RETURN_TYPE, CALLING_CONVENTION, FUNCTION_NAME, ADDRESS, __VA_ARGS__);
76 RETURN_TYPE CALLING_CONVENTION impl_##FUNCTION_NAME(__VA_ARGS__)
77
78///
79/// Gets the installation result of a hook defined with \ref STATIC_HOOK, \ref STATIC_USER_HOOK or \ref STATIC_ASM_HOOK.
80///
81/// \param FUNCTION_NAME The name of the function that was hooked.
82///
83/// \returns `true` if the installation succeeeded. Otherwise, `false`.
84///
85#define GET_STATIC_HOOK_RESULT(FUNCTION_NAME)
86 result_##FUNCTION_NAME
87
88#if defined(CMNLIB_X64) || defined(CMNLIB_X86)
89
90#define THISCALL __fastcall
91
92#if defined(CMNLIB_X64)
93#define THISCALL_PARAMS(CLASS_NAME) CLASS_NAME* self
94#define THISCALL_RETURN_PARAMS self
95#elif defined(CMNLIB_X86)
96#define THISCALL_PARAMS(CLASS_NAME) CLASS_NAME* self, void* _
97#define THISCALL_RETURN_PARAMS self, _
98#endif
99
100#else
101static_assert(false, "THISCALL is not implemented for this architecture.");
102#endif
103
104///
105/// Defines the body of a hook for a function in a virtual function table in memory.
106///
107/// \param RETURN_TYPE The return type of the function.
108/// \param CALLING_CONVENTION The calling convention of the function (e.g. `__cdecl`, `__stdcall`, `__fastcall`, etc).
109/// This does not support optimised calling conventions, such as `__usercall` or `__userpurge`.
110/// \param CLASS_NAME The name of the class that contains the function being hooked.
111/// \param FUNCTION_NAME The name of the function.
112/// \param __VA_ARGS__ The parameters of the function.
113///
114#define VFTABLE_HOOK(RETURN_TYPE, CALLING_CONVENTION, CLASS_NAME, FUNCTION_NAME, ...)
115 HOOK(RETURN_TYPE, CALLING_CONVENTION, CLASS_NAME##_##FUNCTION_NAME, nullptr, __VA_ARGS__)
116
117///
118/// Defines the body of a hook for a function in a virtual function table in memory, and installs it upon initialisation.
119///
120/// \param RETURN_TYPE The return type of the function.
121/// \param CALLING_CONVENTION The calling convention of the function (e.g. `__cdecl`, `__stdcall`, `__fastcall`, etc).
122/// This does not support optimised calling conventions, such as `__usercall` or `__userpurge`.
123/// \param CLASS_NAME The name of the class that contains the function being hooked.
124/// \param FUNCTION_NAME The name of the function.
125/// \param __VA_ARGS__ The parameters of the function.
126///
127/// \returns Use \ref GET_STATIC_VFTABLE_HOOK_RESULT.
128///
129#define STATIC_VFTABLE_HOOK(RETURN_TYPE, CALLING_CONVENTION, CLASS_NAME, FUNCTION_NAME, ...)
130 STATIC_HOOK(RETURN_TYPE, CALLING_CONVENTION, CLASS_NAME##_##FUNCTION_NAME, nullptr, __VA_ARGS__)
131
132///
133/// Gets the installation result of a hook defined with \ref STATIC_VFTABLE_HOOK.
134///
135/// \param CLASS_NAME The name of the class that contains the function that was hooked.
136/// \param FUNCTION_NAME The name of the function that was hooked.
137///
138/// \returns `true` if the installation succeeeded. Otherwise, `false`.
139///
140#define GET_STATIC_VFTABLE_HOOK_RESULT(CLASS_NAME, FUNCTION_NAME)
141 result_##CLASS_NAME##_##FUNCTION_NAME
142
143#if defined(CMNLIB_X64)
144
145///
146/// Declares an x64 assembly hook. The body must be defined in an `*.asm` file using MASM.
147///
148/// This macro creates a scope for members of the hook to be declared in.
149/// If no members are needed, close the scope immediately using empty braces.
150///
151/// \param NAME The name of the hook.
152/// \param ADDRESS The address to hook.
153///
154#define ASM_HOOK(NAME, ADDRESS)
155 extern "C" uint64_t x_##NAME = (uint64_t)(ADDRESS);
156 extern "C" uint64_t original_##NAME = x_##NAME;
157 extern "C" uint64_t post_##NAME{};
158 extern "C" void* impl_##NAME;
159 extern "C"
160
161///
162/// Declares an x64 assembly hook, and installs it upon initialisation. The body must be defined in an `*.asm` file using MASM.
163///
164/// This macro creates a scope for members of the hook to be declared in.
165/// If no members are needed, close the scope immediately using empty braces.
166///
167/// \param NAME The name of the hook.
168/// \param ADDRESS The address to hook.
169///
170#define STATIC_ASM_HOOK(NAME, ADDRESS)
171 ASM_HOOK(NAME, ADDRESS) {}
172 __CMNLIB_INTERNAL_STATIC_HOOK_IMPL(NAME, ADDRESS, INSTALL_HOOK);
173 extern "C"
174
175#elif defined(CMNLIB_X86)
176
177///
178/// Returns from an assembly hook and executes the original code.
179///
180/// \param NAME The name of the hook.
181///
182#define ASM_HOOK_RETURN(NAME) __asm jmp original_##NAME
183
184///
185/// Returns from an assembly hook and skips the original code.
186///
187/// \param NAME The name of the hook.
188///
189#define ASM_HOOK_BRANCH(NAME) __asm jmp post_##NAME
190
191///
192/// Defines the body of an x86 assembly hook.
193///
194/// \param NAME The name of the hook.
195/// \param ADDRESS The address to hook.
196///
197#define ASM_HOOK(NAME, ADDRESS)
198 void* x_##NAME = (void*)(ADDRESS);
199 void* original_##NAME = x_##NAME;
200 void* post_##NAME{};
201 void NAKED_FUNC impl_##NAME()
202
203///
204/// Defines the body of an x86 assembly hook, and installs it upon initialisation.
205///
206/// \param NAME The name of the hook.
207/// \param ADDRESS The address to hook.
208///
209#define STATIC_ASM_HOOK(NAME, ADDRESS)
210 void* x_##NAME = (void*)(ADDRESS);
211 void* original_##NAME = x_##NAME;
212 void* post_##NAME{};
213 void impl_##NAME();
214 __CMNLIB_INTERNAL_STATIC_HOOK_IMPL(NAME, ADDRESS, INSTALL_HOOK);
215 void NAKED_FUNC impl_##NAME()
216
217#else
218static_assert(false, "Assembly hooks are not implemented for this architecture.");
219#endif
220
221///
222/// Installs a hook defined with \ref HOOK or \ref ASM_HOOK.
223///
224/// \param FUNCTION_NAME The name of the function to call before the original.
225///
226/// \returns `true` if the installation succeeeded, or if the hook was already installed. Otherwise, `false`.
227///
228#define INSTALL_HOOK(FUNCTION_NAME)
229 INSTALL_HOOK_EXPLICIT(FUNCTION_NAME, original_##FUNCTION_NAME)
230
231///
232/// Installs a hook defined with \ref HOOK or \ref ASM_HOOK at an explicit address.
233///
234/// \param FUNCTION_NAME The name of the function to call before the original.
235/// \param ADDRESS The address of the function to hook.
236///
237/// \returns `true` if the installation succeeeded, or if the hook was already installed. Otherwise, `false`.
238///
239#define INSTALL_HOOK_EXPLICIT(FUNCTION_NAME, ADDRESS)
240 std::invoke([&]()
241 {
242 if (!original_##FUNCTION_NAME && !(ADDRESS))
243 return false;
244
245 *(void**)&original_##FUNCTION_NAME = (void*)(ADDRESS);
246
247 DetourTransactionBegin();
248 DetourUpdateThread(GetCurrentThread());
249 DetourAttach((void**)&original_##FUNCTION_NAME, &impl_##FUNCTION_NAME);
250
251 const auto result = DetourTransactionCommit() == NO_ERROR;
252
253 if (result)
254 post_##FUNCTION_NAME = hedgedev::csl::hook::GetPostHookAddress((void*)(x_##FUNCTION_NAME));
255
256 return result;
257 })
258
259///
260/// Uninstalls a hook installed with \ref INSTALL_HOOK.
261///
262/// \param FUNCTION_NAME The name of the function to unhook.
263///
264/// \returns `true` if the uninstallation succeeeded, or if the hook was already uninstalled. Otherwise, `false`.
265///
266#define UNINSTALL_HOOK(FUNCTION_NAME)
267 std::invoke([&]()
268 {
269 if (x_##FUNCTION_NAME == original_##FUNCTION_NAME)
270 return true;
271
272 DetourTransactionBegin();
273 DetourUpdateThread(GetCurrentThread());
274 DetourDetach((void**)&original_##FUNCTION_NAME, &impl_##FUNCTION_NAME);
275
276 return DetourTransactionCommit() == NO_ERROR;
277 })
278
279///
280/// Installs a hook defined with \ref VFTABLE_HOOK.
281///
282/// \param CLASS_NAME The name of the class that contains the function being hooked.
283/// \param INSTANCE A pointer to an instance of the class to extract the virtual function table pointer from.
284/// \param FUNCTION_NAME The name of the function to call before the original.
285/// \param FUNCTION_INDEX The index of the function to hook.
286///
287/// \returns `true` if the installation succeeeded, or if the hook was already installed. Otherwise, `false`.
288///
289#define INSTALL_VFTABLE_HOOK(CLASS_NAME, INSTANCE, FUNCTION_NAME, FUNCTION_INDEX)
290 std::invoke([&]()
291 {
292 if (original_##CLASS_NAME##_##FUNCTION_NAME)
293 return true;
294
295 original_##CLASS_NAME##FUNCTION_NAME = (*(CLASS_NAME##_##FUNCTION_NAME***)INSTANCE)[FUNCTION_INDEX];
296
297 DetourTransactionBegin();
298 DetourUpdateThread(GetCurrentThread());
299 DetourAttach((void**)&original##CLASS_NAME##_##FUNCTION_NAME, impl_##CLASS_NAME##_##FUNCTION_NAME);
300
301 const auto result = DetourTransactionCommit() == NO_ERROR;
302
303 if (result)
304 post_##FUNCTION_NAME = hedgedev::csl::hook::GetPostHookAddress((void*)(x_##FUNCTION_NAME));
305
306 return result;
307 })
308
309///
310/// Uninstalls a hook installed with \ref INSTALL_VFTABLE_HOOK.
311///
312/// \param CLASS_NAME The name of the class that contains the function that was hooked.
313/// \param FUNCTION_NAME The name of the function to unhook.
314///
315/// \returns `true` if the uninstallation succeeeded, or if the hook was already uninstalled. Otherwise, `false`.
316///
317#define UNINSTALL_VFTABLE_HOOK(CLASS_NAME, FUNCTION_NAME)
318 UNINSTALL_HOOK(CLASS_NAME##_##FUNCTION_NAME)
319
320namespace hedgedev::csl::hook
321{
322 inline void* GetPostHookAddress(void* in_pHookStart)
323 {
324 if (!in_pHookStart)
325 return nullptr;
326
327 const auto branchInfo = hedgedev::csl::mem::GetBranchInfo(in_pHookStart);
328
329 auto pPostHook = (uint8_t*)(size_t(in_pHookStart) + branchInfo.InstrLength);
330
331 while (*pPostHook == 0xCC)
332 pPostHook++;
333
334 return pPostHook;
335 }
336}
337
338#ifdef CMNLIB_X86
339#include "HookingUserCall.h"
340#endif
#define __CMNLIB_INTERNAL_STATIC_HOOK_IMPL(NAME, ADDRESS, INSTALLER)
Definition Hooking.h:8
#define INSTALL_HOOK(FUNCTION_NAME)
Definition Hooking.h:228
#define HOOK(RETURN_TYPE, CALLING_CONVENTION, FUNCTION_NAME, ADDRESS,...)
Definition Hooking.h:54
#define UNINSTALL_HOOK(FUNCTION_NAME)
Definition Hooking.h:266
#define STATIC_HOOK(RETURN_TYPE, CALLING_CONVENTION, FUNCTION_NAME, ADDRESS,...)
Definition Hooking.h:73
#define INSTALL_HOOK_EXPLICIT(FUNCTION_NAME, ADDRESS)
Definition Hooking.h:239
Definition Hooking.h:321
void * GetPostHookAddress(void *in_pHookStart)
Definition Hooking.h:322