CommonLib
Toggle main menu visibility
Loading...
Searching...
No Matches
hook_win32.h
Go to the documentation of this file.
1
#
pragma
once
2
3
#
include
<
functional
>
4
5
#
include
"../thirdparty/Detours/src/detours.h"
6
#
include
"host/common.h"
7
#
include
"mem/mem.h"
8
#
include
"ut/os/preprocessor_win32.h"
9
10
#
define
__CMNLIB_INTERNAL_STATIC_LIB_ENROLMENT
11
12
#
define
__CMNLIB_INTERNAL_STATIC_HOOK_IMPL
(
NAME
,
ADDRESS
,
INSTALLER
)
13
static
bool
g_result_
##
NAME
{
}
;
14
static
bool
install_
##
NAME
(
)
15
{
16
if
(
!
ADDRESS
)
17
return
g_result_
##
NAME
=
false
;
18
19
return
g_result_
##
NAME
=
INSTALLER
(
NAME
)
;
20
}
21
static
bool
g_runner_
##
NAME
=
install_
##
NAME
(
)
;
22
23
///
24
/// \class __CMNLIB_INTERNAL_HOOK_COMMON_PARAMS
25
///
26
/// \param RETURN_TYPE The return type of the function.
27
/// \param CALLING_CONVENTION The calling convention of the function (e.g.
28
/// `__cdecl`, `__stdcall`, `__fastcall`, etc).
29
/// For custom calling conventions on x86, such as
30
/// `__usercall` or `__userpurge`, see
31
/// \ref hook_usercall_win32.h.
32
/// \param FUNCTION_NAME The name of the function.
33
/// \param ADDRESS The address of the function.
34
/// \param __VA_ARGS__ The parameters of the function.
35
///
36
37
///
38
/// Declares a pointer to a function in memory.
39
///
40
/// \copydoc __CMNLIB_INTERNAL_HOOK_COMMON_PARAMS
41
///
42
#
define
FUNCTION_PTR
(
RETURN_TYPE
,
CALLING_CONVENTION
,
FUNCTION_NAME
,
ADDRESS
,
...
)
43
RETURN_TYPE
(
CALLING_CONVENTION
*
FUNCTION_NAME
)
(
__VA_ARGS__
)
=
(
RETURN_TYPE
(
CALLING_CONVENTION
*
)
(
__VA_ARGS__
)
)
(
ADDRESS
)
44
45
///
46
/// Loads a dynamic link library into memory and declares a pointer to an exported
47
/// function from it.
48
///
49
/// \param RETURN_TYPE The return type of the function.
50
/// \param LIBRARY_NAME The name of the dynamic link library.
51
/// \param FUNCTION_NAME The name of the exported function.
52
/// \param __VA_ARGS__ The parameters of the exported function.
53
///
54
#
define
DLL_IMPORT_FUNCTION_PTR
(
RETURN_TYPE
,
LIBRARY_NAME
,
FUNCTION_NAME
,
...
)
55
typedef
RETURN_TYPE
FUNCTION_NAME
##
_t
(
__VA_ARGS__
)
;
56
FUNCTION_NAME
##
_t
*
FUNCTION_NAME
=
reinterpret_cast
<
FUNCTION_NAME
##
_t
*
>
(
DLL_IMPORT_EXPLICIT
(
LIBRARY_NAME
,
#
FUNCTION_NAME
)
)
;
57
58
///
59
/// Defines the body of a hook for a function in memory.
60
///
61
/// \copydoc __CMNLIB_INTERNAL_HOOK_COMMON_PARAMS
62
///
63
#
define
HOOK
(
RETURN_TYPE
,
CALLING_CONVENTION
,
FUNCTION_NAME
,
ADDRESS
,
...
)
64
void
*
g_address_
##
FUNCTION_NAME
=
reinterpret_cast
<
void
*
>
(
ADDRESS
)
;
65
void
*
g_address_post_hook_
##
FUNCTION_NAME
{
}
;
66
typedef
RETURN_TYPE
CALLING_CONVENTION
FUNCTION_NAME
(
__VA_ARGS__
)
;
67
FUNCTION_NAME
*
original_
##
FUNCTION_NAME
=
reinterpret_cast
<
FUNCTION_NAME
*
>
(
g_address_
##
FUNCTION_NAME
)
;
68
RETURN_TYPE
CALLING_CONVENTION
impl_
##
FUNCTION_NAME
(
__VA_ARGS__
)
69
70
///
71
/// Defines the body of a hook for a function in memory, and installs it
72
/// upon initialisation.
73
///
74
/// \copydoc __CMNLIB_INTERNAL_HOOK_COMMON_PARAMS
75
///
76
/// \returns Use \ref GET_STATIC_HOOK_RESULT.
77
///
78
#
define
STATIC_HOOK
(
RETURN_TYPE
,
CALLING_CONVENTION
,
FUNCTION_NAME
,
ADDRESS
,
...
)
79
HOOK
(
RETURN_TYPE
,
CALLING_CONVENTION
,
FUNCTION_NAME
,
ADDRESS
,
__VA_ARGS__
)
;
80
__CMNLIB_INTERNAL_STATIC_HOOK_IMPL
(
FUNCTION_NAME
,
ADDRESS
,
INSTALL_HOOK
)
81
RETURN_TYPE
CALLING_CONVENTION
impl_
##
FUNCTION_NAME
(
__VA_ARGS__
)
82
83
///
84
/// Gets the installation result of a hook defined with \ref STATIC_HOOK,
85
/// \ref STATIC_USER_HOOK or \ref STATIC_ASM_HOOK.
86
///
87
/// \param FUNCTION_NAME The name of the function that was hooked.
88
///
89
/// \returns `true` if the installation succeeeded. Otherwise, `false`.
90
///
91
#
define
GET_STATIC_HOOK_RESULT
(
FUNCTION_NAME
)
92
g_result_
##
FUNCTION_NAME
93
94
///
95
/// An alias for `__fastcall`.
96
///
97
/// This is intended for use with the other `THISCALL` macros to make setting up
98
/// function pointers and hooks with this calling convention easier.
99
///
100
/// `__thiscall` is a calling convention that stores a pointer to `this` in `ECX`,
101
/// before falling back to using the stack, similar to `__fastcall`.
102
///
103
/// > [!NOTE]
104
/// > **For x64:** This calling convention is always `__fastcall` regardless, with
105
/// > the class pointer stored in `RCX`.
106
///
107
#
define
THISCALL
__fastcall
108
109
///
110
/// \class __CMNLIB_INTERNAL_THISCALL_PARAMS_COMMON
111
///
112
/// > [!NOTE]
113
/// > **For x86:** This macro creates a discard parameter to fill in for `EDX`,
114
/// > allowing the rest of the arguments to be pushed onto the stack.
115
/// > **For x64:** This macro uses standard `__fastcall` conventions.
116
///
117
118
///
119
/// \def THISCALL_PARAMS
120
/// \brief Expands to the necessary function parameters required by `__thiscall`
121
/// calling convention.
122
/// \hideinitializer
123
///
124
/// \param CLASS_NAME The name of the class this function is from.
125
///
126
/// \copydoc __CMNLIB_INTERNAL_THISCALL_PARAMS_COMMON
127
///
128
/// ## Examples
129
/// \code{.cpp}
130
/// FUNCTION_PTR(int, THISCALL, my_class_function, 0xDEADBEEF, THISCALL_PARAMS(my_class_name), int in_second_param);
131
/// \endcode
132
///
133
134
///
135
/// \def THISCALL_RETURN_PARAMS
136
/// \brief Expands to the necessary function arguments required by `__thiscall`
137
/// calling convention for calling the original function.
138
/// \hideinitializer
139
///
140
/// \copydoc __CMNLIB_INTERNAL_THISCALL_PARAMS_COMMON
141
///
142
/// ## Examples
143
/// \code{.cpp}
144
/// HOOK(int, THISCALL, my_class_function_hook, 0xDEADBEEF, THISCALL_PARAMS(my_class_name), int in_second_param)
145
/// {
146
/// return original_my_class_function_hook(THISCALL_RETURN_PARAMS, in_secondParam);
147
/// }
148
/// \endcode
149
///
150
151
#
if
defined
(
CMNLIB_X64
)
152
#
define
THISCALL_PARAMS
(
CLASS_NAME
)
CLASS_NAME
*
self
153
#
define
THISCALL_RETURN_PARAMS
self
154
#
elif
defined
(
CMNLIB_X86
)
155
#
define
THISCALL_PARAMS
(
CLASS_NAME
)
CLASS_NAME
*
self
,
void
*
_
156
#
define
THISCALL_RETURN_PARAMS
self
,
_
157
#
endif
158
159
///
160
/// Defines the body of a hook for a function in a virtual function table in memory.
161
///
162
/// \param RETURN_TYPE The return type of the function.
163
/// \param CALLING_CONVENTION The calling convention of the function (e.g.
164
/// `__cdecl`, `__stdcall`, `__fastcall`, etc).
165
/// This does not support optimised calling conventions,
166
/// such as `__usercall` or `__userpurge`.
167
/// \param CLASS_NAME The name of the class that contains the function
168
/// being hooked.
169
/// \param FUNCTION_NAME The name of the function.
170
/// \param FUNCTION_INDEX The index of the function in the virtual
171
/// function table.
172
/// \param __VA_ARGS__ The parameters of the function.
173
///
174
#
define
VFTABLE_HOOK
(
RETURN_TYPE
,
CALLING_CONVENTION
,
CLASS_NAME
,
FUNCTION_NAME
,
FUNCTION_INDEX
,
...
)
175
const
size_t
k_index_of_
##
CLASS_NAME
##
_
##
FUNCTION_NAME
=
size_t
(
FUNCTION_INDEX
)
;
176
HOOK
(
RETURN_TYPE
,
CALLING_CONVENTION
,
CLASS_NAME
##
_
##
FUNCTION_NAME
,
nullptr
,
__VA_ARGS__
)
177
178
///
179
/// \class __CMNLIB_INTERNAL_ASM_HOOK_COMMON
180
///
181
/// > [!NOTE]
182
/// > **For x64:** The body must be defined in an `*.asm` file using MASM. This
183
/// > macro creates a scope for members of the hook to be declared in. If no
184
/// > members are needed, close the scope immediately using empty braces.
185
///
186
/// ## Examples
187
/// ### x86
188
/// `mod.cpp`
189
/// \code{.cpp}
190
/// ASM_HOOK(my_asm_hook, 0xDEADBEEF)
191
/// {
192
/// static uint32_t g_my_hook_member = 0x900DF00D;
193
///
194
/// __asm
195
/// {
196
/// mov eax, [g_my_hook_member]
197
/// }
198
///
199
/// ASM_HOOK_BRANCH(my_asm_hook)
200
/// }
201
/// \endcode
202
///
203
/// ### x64
204
/// `mod.cpp`
205
/// \code{.cpp}
206
/// ASM_HOOK(my_asm_hook, 0xDEADBEEF)
207
/// {
208
/// uint64_t g_my_hook_member = 0x900DF00D;
209
/// }
210
/// \endcode
211
///
212
/// `my_asm_hook.asm`
213
/// \code{.asm}
214
/// include CommonLib.masm.inc
215
///
216
/// EXTERN g_my_hook_member : QWORD
217
///
218
/// ASM_HOOK_START my_asm_hook
219
/// mov rax, [g_my_hook_member]
220
/// ASM_HOOK_BRANCH my_asm_hook
221
/// ASM_HOOK_END my_asm_hook
222
///
223
/// END
224
/// \endcode
225
///
226
227
///
228
/// \class __CMNLIB_INTERNAL_ASM_HOOK_RETURN_BRANCH_COMMON
229
///
230
/// > [!NOTE]
231
/// > **For x64:** This macro can be accessed in an `*.asm` file by
232
/// > including `CommonLib.masm.inc`.
233
///
234
235
///
236
/// \def ASM_HOOK
237
/// \brief Creates an assembly hook.
238
/// \hideinitializer
239
///
240
/// \param NAME The name of the hook.
241
/// \param ADDRESS The address to hook.
242
///
243
/// \copydoc __CMNLIB_INTERNAL_ASM_HOOK_COMMON
244
///
245
246
///
247
/// \def STATIC_ASM_HOOK
248
/// \brief Creates an assembly hook, and installs it upon initialisation.
249
/// \hideinitializer
250
///
251
/// \param NAME The name of the hook.
252
/// \param ADDRESS The address to hook.
253
///
254
/// \copydoc __CMNLIB_INTERNAL_ASM_HOOK_COMMON
255
///
256
257
///
258
/// \def ASM_HOOK_RETURN
259
/// \brief Returns from an assembly hook and executes the original code.
260
/// \hideinitializer
261
///
262
/// \param NAME The name of the hook.
263
///
264
/// \copydoc __CMNLIB_INTERNAL_ASM_HOOK_RETURN_BRANCH_COMMON
265
///
266
267
///
268
/// \def ASM_HOOK_BRANCH
269
/// \brief Returns from an assembly hook and skips the original code.
270
/// \hideinitializer
271
///
272
/// \param NAME The name of the hook.
273
///
274
/// \copydoc __CMNLIB_INTERNAL_ASM_HOOK_RETURN_BRANCH_COMMON
275
///
276
277
#
ifdef
CMNLIB_X64
278
279
#
define
ASM_HOOK
(
NAME
,
ADDRESS
)
280
extern
"C"
uintptr_t
g_address_
##
NAME
=
uintptr_t
(
ADDRESS
)
;
281
extern
"C"
uintptr_t
g_address_post_hook_
##
NAME
{
}
;
282
extern
"C"
uintptr_t
original_
##
NAME
=
g_address_
##
NAME
;
283
extern
"C"
void
*
impl_
##
NAME
{
}
;
284
extern
"C"
285
286
#
define
STATIC_ASM_HOOK
(
NAME
,
ADDRESS
)
287
ASM_HOOK
(
NAME
,
ADDRESS
)
{
}
288
__CMNLIB_INTERNAL_STATIC_HOOK_IMPL
(
NAME
,
ADDRESS
,
INSTALL_HOOK
)
;
289
extern
"C"
290
291
#
endif
// CMNLIB_X64
292
293
#
ifdef
CMNLIB_X86
294
295
#
define
ASM_HOOK
(
NAME
,
ADDRESS
)
296
void
*
g_address_
##
NAME
=
reinterpret_cast
<
void
*
>
(
ADDRESS
)
;
297
void
*
g_address_post_hook_
##
NAME
{
}
;
298
void
*
original_
##
NAME
=
g_address_
##
NAME
;
299
void
NAKED
impl_
##
NAME
(
)
300
301
#
define
STATIC_ASM_HOOK
(
NAME
,
ADDRESS
)
302
void
*
g_address_
##
NAME
=
reinterpret_cast
<
void
*
>
(
ADDRESS
)
;
303
void
*
g_address_post_hook_
##
NAME
{
}
;
304
void
*
original_
##
NAME
=
g_address_
##
NAME
;
305
void
impl_
##
NAME
(
)
;
306
__CMNLIB_INTERNAL_STATIC_HOOK_IMPL
(
NAME
,
ADDRESS
,
INSTALL_HOOK
)
;
307
void
NAKED
impl_
##
NAME
(
)
308
309
#
define
ASM_HOOK_RETURN
(
NAME
)
__asm
jmp
original_
##
NAME
310
311
#
define
ASM_HOOK_BRANCH
(
NAME
)
__asm
jmp
g_address_post_hook_
##
NAME
312
313
#
endif
// CMNLIB_X86
314
315
///
316
/// Installs a hook defined with \ref HOOK or \ref ASM_HOOK.
317
///
318
/// \param NAME The name of the hook to install.
319
///
320
/// \returns `true` if the installation succeeeded, or if the hook was already
321
/// installed. Otherwise, `false`.
322
///
323
#
define
INSTALL_HOOK
(
NAME
)
324
INSTALL_HOOK_EXPLICIT
(
NAME
,
original_
##
NAME
)
325
326
///
327
/// Installs a hook defined with \ref HOOK or \ref ASM_HOOK at an explicit address.
328
///
329
/// \param NAME The name of the hook to install.
330
/// \param ADDRESS The address to install the hook to.
331
///
332
/// \returns `true` if the installation succeeeded, or if the hook was already
333
/// installed. Otherwise, `false`.
334
///
335
#
define
INSTALL_HOOK_EXPLICIT
(
NAME
,
ADDRESS
)
336
std
::
invoke
(
[
&
]
(
)
->
bool
337
{
338
if
(
!
original_
##
NAME
&&
!
(
ADDRESS
)
)
339
return
false
;
340
341
*
reinterpret_cast
<
void
*
*
>
(
&
original_
##
NAME
)
=
reinterpret_cast
<
void
*
>
(
ADDRESS
)
;
342
343
DetourTransactionBegin
(
)
;
344
DetourUpdateThread
(
GetCurrentThread
(
)
)
;
345
DetourAttach
(
reinterpret_cast
<
void
*
*
>
(
&
original_
##
NAME
)
,
&
impl_
##
NAME
)
;
346
347
const
auto
result
=
DetourTransactionCommit
(
)
==
NO_ERROR
;
348
349
if
(
result
)
350
{
351
g_address_post_hook_
##
NAME
=
352
hedgedev
::
csl
::
hook
::
_get_post_hook_address
(
reinterpret_cast
<
void
*
>
(
g_address_
##
NAME
)
)
;
353
}
354
355
return
result
;
356
}
)
357
358
///
359
/// Uninstalls a hook installed with \ref INSTALL_HOOK.
360
///
361
/// \param NAME The name of the hook to uninstall.
362
///
363
/// \returns `true` if the uninstallation succeeeded, or if the hook was already
364
/// uninstalled. Otherwise, `false`.
365
///
366
#
define
UNINSTALL_HOOK
(
NAME
)
367
std
::
invoke
(
[
&
]
(
)
->
bool
368
{
369
if
(
g_address_
##
NAME
==
original_
##
NAME
)
370
return
true
;
371
372
DetourTransactionBegin
(
)
;
373
DetourUpdateThread
(
GetCurrentThread
(
)
)
;
374
DetourDetach
(
reinterpret_cast
<
void
*
*
>
(
&
original_
##
NAME
)
,
&
impl_
##
NAME
)
;
375
376
return
DetourTransactionCommit
(
)
==
NO_ERROR
;
377
}
)
378
379
///
380
/// Installs a hook defined with \ref VFTABLE_HOOK.
381
///
382
/// \param INSTANCE A pointer to an instance of the class to extract the
383
/// virtual function table pointer from.
384
/// \param CLASS_NAME The name of the class that owns the function being hooked.
385
/// \param FUNCTION_NAME The name of the function being hooked.
386
///
387
/// \returns `true` if the installation succeeeded, or if the hook was already
388
/// installed. Otherwise, `false`.
389
///
390
#
define
INSTALL_VFTABLE_HOOK
(
INSTANCE
,
CLASS_NAME
,
FUNCTION_NAME
)
391
INSTALL_VFTABLE_HOOK_EXPLICIT
(
INSTANCE
,
CLASS_NAME
,
FUNCTION_NAME
,
k_index_of_
##
CLASS_NAME
##
_
##
FUNCTION_NAME
)
392
393
///
394
/// Installs a hook defined with \ref VFTABLE_HOOK at an explicit index.
395
///
396
/// \param INSTANCE A pointer to an instance of the class to extract the
397
/// virtual function table pointer from.
398
/// \param CLASS_NAME The name of the class that owns the function being hooked.
399
/// \param FUNCTION_NAME The name of the function being hooked.
400
/// \param FUNCTION_INDEX The index of the function in the virtual function table.
401
///
402
/// \returns `true` if the installation succeeeded, or if the hook was already
403
/// installed. Otherwise, `false`.
404
///
405
#
define
INSTALL_VFTABLE_HOOK_EXPLICIT
(
INSTANCE
,
CLASS_NAME
,
FUNCTION_NAME
,
FUNCTION_INDEX
)
406
std
::
invoke
(
[
&
]
(
)
->
bool
407
{
408
if
(
original_
##
CLASS_NAME
##
_
##
FUNCTION_NAME
)
409
return
true
;
410
411
original_
##
CLASS_NAME
##
_
##
FUNCTION_NAME
=
*
reinterpret_cast
<
CLASS_NAME
##
_
##
FUNCTION_NAME
*
*
*
>
(
INSTANCE
)
[
FUNCTION_INDEX
]
;
412
413
DetourTransactionBegin
(
)
;
414
DetourUpdateThread
(
GetCurrentThread
(
)
)
;
415
DetourAttach
(
reinterpret_cast
<
void
*
*
>
(
&
original_
##
CLASS_NAME
##
_
##
FUNCTION_NAME
)
,
impl_
##
CLASS_NAME
##
_
##
FUNCTION_NAME
)
;
416
417
const
auto
result
=
DetourTransactionCommit
(
)
==
NO_ERROR
;
418
419
if
(
result
)
420
{
421
g_address_post_hook_
##
CLASS_NAME
##
_
##
FUNCTION_NAME
=
422
hedgedev
::
csl
::
hook
::
_get_post_hook_address
(
reinterpret_cast
<
void
*
>
(
g_address_
##
CLASS_NAME
##
_
##
FUNCTION_NAME
)
)
;
423
}
424
425
return
result
;
426
}
)
427
428
///
429
/// Uninstalls a hook installed with \ref INSTALL_VFTABLE_HOOK.
430
///
431
/// \param CLASS_NAME The name of the class that owns the function that
432
/// was hooked.
433
/// \param FUNCTION_NAME The name of the function to unhook.
434
///
435
/// \returns `true` if the uninstallation succeeeded, or if the hook was already
436
/// uninstalled. Otherwise, `false`.
437
///
438
#
define
UNINSTALL_VFTABLE_HOOK
(
CLASS_NAME
,
FUNCTION_NAME
)
439
UNINSTALL_HOOK
(
CLASS_NAME
##
_
##
FUNCTION_NAME
)
440
441
namespace
hedgedev::csl::
hook
442
{
443
inline
void
*
_get_post_hook_address
(
void
* in_hook)
444
{
445
uint8_t* result{};
446
447
if
(!in_hook)
448
return
result;
449
450
const
auto
branch_info =
mem
::
get_branch_info
(
in_hook
)
;
451
452
result =
reinterpret_cast
<uint8_t*>(uintptr_t(in_hook) + branch_info.instr_length);
453
454
while
(*result == 0xCC)
455
result++;
456
457
return
result;
458
}
459
}
460
461
#
ifdef
CMNLIB_X86
462
#
include
"hook_usercall_win32.h"
463
#
endif
__CMNLIB_INTERNAL_STATIC_HOOK_IMPL
#define __CMNLIB_INTERNAL_STATIC_HOOK_IMPL(NAME, ADDRESS, INSTALLER)
Definition
hook_win32.h:12
INSTALL_VFTABLE_HOOK_EXPLICIT
#define INSTALL_VFTABLE_HOOK_EXPLICIT(INSTANCE, CLASS_NAME, FUNCTION_NAME, FUNCTION_INDEX)
Definition
hook_win32.h:405
INSTALL_HOOK
#define INSTALL_HOOK(NAME)
Definition
hook_win32.h:323
HOOK
#define HOOK(RETURN_TYPE, CALLING_CONVENTION, FUNCTION_NAME, ADDRESS,...)
Definition
hook_win32.h:63
INSTALL_HOOK_EXPLICIT
#define INSTALL_HOOK_EXPLICIT(NAME, ADDRESS)
Definition
hook_win32.h:335
UNINSTALL_HOOK
#define UNINSTALL_HOOK(NAME)
Definition
hook_win32.h:366
hedgedev::csl::hook
Definition
hook_usercall_win32.h:223
hedgedev::csl::hook::_get_post_hook_address
void * _get_post_hook_address(void *in_hook)
Definition
hook_win32.h:443
hedgedev::csl::mem
Definition
mem.h:169
hedgedev::csl::mem::get_branch_info
branch_info get_branch_info(void *in_address)
Definition
mem_x86.inl:44
DLL_IMPORT_EXPLICIT
#define DLL_IMPORT_EXPLICIT(LIBRARY_NAME, MEMBER_NAME)
Definition
preprocessor_win32.h:19
include
hook
os
hook_win32.h
Generated by
1.18.0