LeviLamina
Loading...
Searching...
No Matches
Hook.h
1#pragma once
2
3#include <atomic> // IWYU pragma: keep
4#include <cstdint>
5#include <memory>
6#include <string>
7#include <string_view>
8#include <type_traits>
9
10#include "ll/api/base/CompilerPredefine.h"
11#include "ll/api/base/Concepts.h" // IWYU pragma: keep
12#include "ll/api/base/Macro.h"
13#include "ll/api/memory/Memory.h"
14#include "ll/api/reflection/TypeName.h"
15#include "ll/api/thread/GlobalThreadPauser.h"
16
17namespace ll::memory {
18
19template <class T>
20struct IsConstMemberFun : std::false_type {};
21
22template <class T, class Ret, class... Args>
23struct IsConstMemberFun<Ret (T::*)(Args...) const> : std::true_type {};
24
25template <class T>
26constexpr bool IsConstMemberFunV = IsConstMemberFun<T>::value;
27
28template <class T>
30 using type = T;
31};
32
33template <class T, class Ret, class... Args>
34struct AddConstAtMemberFun<Ret (T::*)(Args...)> {
35 using type = Ret (T::*)(Args...) const;
36};
37
38template <class T>
39using AddConstAtMemberFunT = typename AddConstAtMemberFun<T>::type;
40
45enum class HookPriority : int {
46 Highest = 0,
47 High = 100,
48 Normal = 200,
49 Low = 300,
50 Lowest = 400,
51};
52
53enum RegisterSaveOptions {
54 SaveNone = 0,
55 SaveR10 = 1 << 0,
56 SaveR11 = 1 << 1,
57 SaveXmm4 = 1 << 2,
58 SaveXmm5 = 1 << 3,
59 SaveAll = SaveR10 | SaveR11 | SaveXmm4 | SaveXmm5
60};
61
62LLNDAPI bool shouldHookSuspendThreads();
63
64LLAPI int
65hook(FuncPtr target, FuncPtr detour, FuncPtr* originalFunc, HookPriority priority, bool suspendThreads = true);
66
67LLAPI int hookEx(
68 FuncPtr target,
69 FuncPtr detour,
70 FuncPtr* originalFunc,
71 HookPriority priority,
72 RegisterSaveOptions options,
73 bool suspendThreads
74);
75
76LLAPI bool unhook(FuncPtr target, FuncPtr detour, bool suspendThreads = true);
77
78LLAPI bool hookOptions(FuncPtr target, RegisterSaveOptions options, bool replace = true, bool suspendThreads = true);
79
80template <class T>
81concept FuncPtrType = std::is_function_v<std::remove_pointer_t<T>> || std::is_member_function_pointer_v<T>;
82
83template <FuncPtrType T>
84constexpr FuncPtr resolveIdentifier(T identifier) {
85 return toFuncPtr(identifier);
86}
87
88template <class T>
89constexpr FuncPtr resolveIdentifier(SignatureView identifier) {
90 return identifier.resolve();
91}
92
93template <class T>
94constexpr FuncPtr resolveIdentifier(SymbolView identifier) {
95 return identifier.resolve();
96}
97
98template <class T>
99constexpr FuncPtr resolveIdentifier(uintptr_t address) {
100 return toFuncPtr(address);
101}
102
103template <class T>
104constexpr uintptr_t unchecked(T identifier) {
105 return reinterpret_cast<uintptr_t>(toFuncPtr(identifier));
106}
107
108template <class T, auto>
109 requires(!std::is_member_function_pointer_v<T>)
110consteval bool virtualDetector() noexcept {
111 return false;
112}
113using ::ll::internal::virtualDetector;
114
115template <class... Ts>
117public:
118 static void hook() {
119 std::optional<thread::GlobalThreadPauser> pauser;
120 if (shouldHookSuspendThreads()) {
121 pauser.emplace();
122 }
123 (((++Ts::_AutoHookCount == 1) ? Ts::hook(false) : 0), ...);
124 }
125 static void unhook() {
126 std::optional<thread::GlobalThreadPauser> pauser;
127 if (shouldHookSuspendThreads()) {
128 pauser.emplace();
129 }
130 (((--Ts::_AutoHookCount == 0) ? Ts::unhook(false) : 0), ...);
131 }
132 HookRegistrar() noexcept { hook(); }
133 ~HookRegistrar() noexcept { unhook(); }
134 HookRegistrar(HookRegistrar const&) noexcept { ((++Ts::_AutoHookCount), ...); }
135 HookRegistrar& operator=(HookRegistrar const& other) noexcept {
136 if (this != std::addressof(other)) {
137 ((++Ts::_AutoHookCount), ...);
138 }
139 return *this;
140 }
141 HookRegistrar(HookRegistrar&&) noexcept = default;
142 HookRegistrar& operator=(HookRegistrar&&) noexcept = default;
143};
144
145struct LL_EBO Hook {};
146
147} // namespace ll::memory
148
149#define LL_HOOK_IMPL(REGISTER, FUNC_PTR, STATIC, CALL, DEF_TYPE, TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, ...) \
150 struct DEF_TYPE : public TYPE { \
151 inline static ::std::atomic_uint _AutoHookCount{}; \
152 \
153 private: \
154 using _FuncPtr = ::ll::memory::FuncPtr; \
155 using HookPriority = ::ll::memory::HookPriority; \
156 using RegisterSaveOptions = ll::memory::RegisterSaveOptions; \
157 using _RawFuncType = RET_TYPE FUNC_PTR(__VA_ARGS__); \
158 using _RawConstFuncType = ::ll::memory::AddConstAtMemberFunT<_RawFuncType>; \
159 \
160 template <class T> \
161 struct _ConstDetector { \
162 [[maybe_unused]] static constexpr bool value = false; \
163 explicit constexpr _ConstDetector(T) {} \
164 }; \
165 template <class T> \
166 [[maybe_unused]] _ConstDetector(T) -> _ConstDetector<T>; \
167 [[maybe_unused]] _ConstDetector(_RawFuncType) -> _ConstDetector<_RawFuncType>; \
168 template <> \
169 struct _ConstDetector<_RawConstFuncType> { \
170 [[maybe_unused]] static constexpr bool value = true; \
171 explicit constexpr _ConstDetector(_RawConstFuncType) {} \
172 }; \
173 template <class T = _RawFuncType, std::enable_if_t<std::is_member_function_pointer_v<T>, int> = 0> \
174 [[maybe_unused]] _ConstDetector(_RawConstFuncType) -> _ConstDetector<_RawConstFuncType>; \
175 \
176 static constexpr bool _IsConstMemberFunction = decltype(_ConstDetector{IDENTIFIER})::value; \
177 \
178 using _OriginFuncType = ::std::conditional_t<_IsConstMemberFunction, _RawConstFuncType, _RawFuncType>; \
179 \
180 inline static _FuncPtr _HookTarget{}; \
181 inline static _OriginFuncType _OriginalFunc{}; \
182 \
183 template <class T> \
184 static consteval void _Detector() { \
185 if constexpr (requires { ::ll::memory::virtualDetector<T, IDENTIFIER>(); }) { \
186 if constexpr (::ll::memory::virtualDetector<T, IDENTIFIER>()) { \
187 static_assert( \
188 ::ll::traits::always_false<T>, \
189 #IDENTIFIER " is a virtual function, you need use prefix $ workaround to hook it." \
190 ); \
191 } \
192 } \
193 } \
194 \
195 public: \
196 template <class T> \
197 requires(::std::is_polymorphic_v<TYPE> && ::std::is_base_of_v<T, TYPE>) \
198 [[nodiscard]] TYPE* thisFor() { \
199 return static_cast<decltype(this)>(reinterpret_cast<T*>(this)); \
200 } \
201 template <class... Args> \
202 STATIC RET_TYPE origin(Args&&... params) { \
203 return CALL(::std::forward<Args>(params)...); \
204 } \
205 \
206 STATIC RET_TYPE detour(__VA_ARGS__); \
207 \
208 static int hook(bool suspendThreads = true) { \
209 _Detector<_OriginFuncType>(); \
210 if (!_HookTarget) _HookTarget = ::ll::memory::resolveIdentifier<_OriginFuncType>(IDENTIFIER); \
211 return ::ll::memory::hookEx( \
212 _HookTarget, \
213 ::ll::memory::toFuncPtr(&DEF_TYPE::detour), \
214 reinterpret_cast<_FuncPtr*>(&_OriginalFunc), \
215 PRIORITY, \
216 OPTIONS, \
217 suspendThreads \
218 ); \
219 } \
220 \
221 static bool unhook(bool suspendThreads = true) { \
222 return ::ll::memory::unhook(_HookTarget, ::ll::memory::toFuncPtr(&DEF_TYPE::detour), suspendThreads); \
223 } \
224 }; \
225 REGISTER; \
226 RET_TYPE DEF_TYPE::detour(__VA_ARGS__)
227
228#define LL_AUTO_REG_HOOK_IMPL(FUNC_PTR, STATIC, CALL, DEF_TYPE, ...) \
229 LL_VA_EXPAND(LL_HOOK_IMPL( \
230 inline ::ll::memory::HookRegistrar<DEF_TYPE> DEF_TYPE##AutoRegister, \
231 FUNC_PTR, \
232 STATIC, \
233 CALL, \
234 DEF_TYPE, \
235 __VA_ARGS__ \
236 ))
237
238#define LL_MANUAL_REG_HOOK_IMPL(...) LL_VA_EXPAND(LL_HOOK_IMPL(, __VA_ARGS__))
239
240#define LL_STATIC_HOOK_IMPL(...) LL_VA_EXPAND(LL_MANUAL_REG_HOOK_IMPL((*), static, _OriginalFunc, __VA_ARGS__))
241
242#define LL_AUTO_STATIC_HOOK_IMPL(...) LL_VA_EXPAND(LL_AUTO_REG_HOOK_IMPL((*), static, _OriginalFunc, __VA_ARGS__))
243
244#define LL_INSTANCE_HOOK_IMPL(DEF_TYPE, TYPE, ...) \
245 LL_VA_EXPAND(LL_MANUAL_REG_HOOK_IMPL((TYPE::*), , (this->*_OriginalFunc), DEF_TYPE, TYPE, __VA_ARGS__))
246
247#define LL_AUTO_INSTANCE_HOOK_IMPL(DEF_TYPE, TYPE, ...) \
248 LL_VA_EXPAND(LL_AUTO_REG_HOOK_IMPL((TYPE::*), , (this->*_OriginalFunc), DEF_TYPE, TYPE, __VA_ARGS__))
249
261#define LL_TYPE_STATIC_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...) \
262 LL_VA_EXPAND(LL_STATIC_HOOK_IMPL( \
263 DEF_TYPE, \
264 TYPE, \
265 PRIORITY, \
266 RegisterSaveOptions::SaveNone, \
267 IDENTIFIER, \
268 RET_TYPE, \
269 __VA_ARGS__ \
270 ))
271
282#define LL_STATIC_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
283 LL_VA_EXPAND(LL_STATIC_HOOK_IMPL( \
284 DEF_TYPE, \
285 ::ll::memory::Hook, \
286 PRIORITY, \
287 RegisterSaveOptions::SaveNone, \
288 IDENTIFIER, \
289 RET_TYPE, \
290 __VA_ARGS__ \
291 ))
292
298#define LL_AUTO_TYPE_STATIC_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...) \
299 LL_VA_EXPAND(LL_AUTO_STATIC_HOOK_IMPL( \
300 DEF_TYPE, \
301 TYPE, \
302 PRIORITY, \
303 RegisterSaveOptions::SaveNone, \
304 IDENTIFIER, \
305 RET_TYPE, \
306 __VA_ARGS__ \
307 ))
308
314#define LL_AUTO_STATIC_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
315 LL_VA_EXPAND(LL_AUTO_STATIC_HOOK_IMPL( \
316 DEF_TYPE, \
317 ::ll::memory::Hook, \
318 PRIORITY, \
319 RegisterSaveOptions::SaveNone, \
320 IDENTIFIER, \
321 RET_TYPE, \
322 __VA_ARGS__ \
323 ))
324
336#define LL_TYPE_INSTANCE_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...) \
337 LL_VA_EXPAND(LL_INSTANCE_HOOK_IMPL( \
338 DEF_TYPE, \
339 TYPE, \
340 PRIORITY, \
341 RegisterSaveOptions::SaveNone, \
342 IDENTIFIER, \
343 RET_TYPE, \
344 __VA_ARGS__ \
345 ))
346
357#define LL_INSTANCE_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
358 LL_VA_EXPAND(LL_INSTANCE_HOOK_IMPL( \
359 DEF_TYPE, \
360 ::ll::memory::Hook, \
361 PRIORITY, \
362 RegisterSaveOptions::SaveNone, \
363 IDENTIFIER, \
364 RET_TYPE, \
365 __VA_ARGS__ \
366 ))
367
373#define LL_AUTO_TYPE_INSTANCE_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...) \
374 LL_VA_EXPAND(LL_AUTO_INSTANCE_HOOK_IMPL( \
375 DEF_TYPE, \
376 TYPE, \
377 PRIORITY, \
378 RegisterSaveOptions::SaveNone, \
379 IDENTIFIER, \
380 RET_TYPE, \
381 __VA_ARGS__ \
382 ))
383
389#define LL_AUTO_INSTANCE_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
390 LL_VA_EXPAND(LL_AUTO_INSTANCE_HOOK_IMPL( \
391 DEF_TYPE, \
392 ::ll::memory::Hook, \
393 PRIORITY, \
394 RegisterSaveOptions::SaveNone, \
395 IDENTIFIER, \
396 RET_TYPE, \
397 __VA_ARGS__ \
398 ))
399
412#define LL_TYPE_STATIC_HOOK_EX(DEF_TYPE, PRIORITY, OPTIONS, TYPE, IDENTIFIER, RET_TYPE, ...) \
413 LL_VA_EXPAND(LL_STATIC_HOOK_IMPL(DEF_TYPE, TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, __VA_ARGS__))
414
426#define LL_STATIC_HOOK_EX(DEF_TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, ...) \
427 LL_VA_EXPAND( \
428 LL_STATIC_HOOK_IMPL(DEF_TYPE, ::ll::memory::Hook, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, __VA_ARGS__) \
429 )
430
436#define LL_AUTO_TYPE_STATIC_HOOK_EX(DEF_TYPE, PRIORITY, OPTIONS, TYPE, IDENTIFIER, RET_TYPE, ...) \
437 LL_VA_EXPAND(LL_AUTO_STATIC_HOOK_IMPL(DEF_TYPE, TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, __VA_ARGS__))
438
444#define LL_AUTO_STATIC_HOOK_EX(DEF_TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, ...) \
445 LL_VA_EXPAND( \
446 LL_AUTO_STATIC_HOOK_IMPL(DEF_TYPE, ::ll::memory::Hook, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, __VA_ARGS__) \
447 )
448
461#define LL_TYPE_INSTANCE_HOOK_EX(DEF_TYPE, PRIORITY, OPTIONS, TYPE, IDENTIFIER, RET_TYPE, ...) \
462 LL_VA_EXPAND(LL_INSTANCE_HOOK_IMPL(DEF_TYPE, TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, __VA_ARGS__))
463
475#define LL_INSTANCE_HOOK_EX(DEF_TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, ...) \
476 LL_VA_EXPAND( \
477 LL_INSTANCE_HOOK_IMPL(DEF_TYPE, ::ll::memory::Hook, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, __VA_ARGS__) \
478 )
479
485#define LL_AUTO_TYPE_INSTANCE_HOOK_EX(DEF_TYPE, PRIORITY, OPTIONS, TYPE, IDENTIFIER, RET_TYPE, ...) \
486 LL_VA_EXPAND(LL_AUTO_INSTANCE_HOOK_IMPL(DEF_TYPE, TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, __VA_ARGS__))
487
493#define LL_AUTO_INSTANCE_HOOK_EX(DEF_TYPE, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, ...) \
494 LL_VA_EXPAND( \
495 LL_AUTO_INSTANCE_HOOK_IMPL(DEF_TYPE, ::ll::memory::Hook, PRIORITY, OPTIONS, IDENTIFIER, RET_TYPE, __VA_ARGS__) \
496 )
Definition Hook.h:116
Definition Signature.h:48
Definition Hook.h:81
Definition Hook.h:145
Definition Hook.h:20