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
53LLNDAPI bool shouldHookSuspendThreads();
54
55LLAPI int
56hook(FuncPtr target, FuncPtr detour, FuncPtr* originalFunc, HookPriority priority, bool suspendThreads = true);
57
58LLAPI bool unhook(FuncPtr target, FuncPtr detour, bool suspendThreads = true);
59
60template <class T>
61concept FuncPtrType = std::is_function_v<std::remove_pointer_t<T>> || std::is_member_function_pointer_v<T>;
62
63template <FuncPtrType T>
64constexpr FuncPtr resolveIdentifier(T identifier) {
65 return toFuncPtr(identifier);
66}
67
68template <class T>
69constexpr FuncPtr resolveIdentifier(SignatureView identifier) {
70 return identifier.resolve();
71}
72
73template <class T>
74constexpr FuncPtr resolveIdentifier(SymbolView identifier) {
75 return identifier.resolve();
76}
77
78template <class T>
79constexpr FuncPtr resolveIdentifier(uintptr_t address) {
80 return toFuncPtr(address);
81}
82
83template <class T>
84constexpr uintptr_t unchecked(T identifier) {
85 return reinterpret_cast<uintptr_t>(toFuncPtr(identifier));
86}
87
88template <class T, auto>
89 requires(!std::is_member_function_pointer_v<T>)
90consteval bool virtualDetector() noexcept {
91 return false;
92}
93using ::ll::internal::virtualDetector;
94
95template <class... Ts>
97public:
98 static void hook() {
99 std::optional<thread::GlobalThreadPauser> pauser;
100 if (shouldHookSuspendThreads()) {
101 pauser.emplace();
102 }
103 (((++Ts::_AutoHookCount == 1) ? Ts::hook(false) : 0), ...);
104 }
105 static void unhook() {
106 std::optional<thread::GlobalThreadPauser> pauser;
107 if (shouldHookSuspendThreads()) {
108 pauser.emplace();
109 }
110 (((--Ts::_AutoHookCount == 0) ? Ts::unhook(false) : 0), ...);
111 }
112 HookRegistrar() noexcept { hook(); }
113 ~HookRegistrar() noexcept { unhook(); }
114 HookRegistrar(HookRegistrar const&) noexcept { ((++Ts::_AutoHookCount), ...); }
115 HookRegistrar& operator=(HookRegistrar const& other) noexcept {
116 if (this != std::addressof(other)) {
117 ((++Ts::_AutoHookCount), ...);
118 }
119 return *this;
120 }
121 HookRegistrar(HookRegistrar&&) noexcept = default;
122 HookRegistrar& operator=(HookRegistrar&&) noexcept = default;
123};
124
125struct LL_EBO Hook {};
126
127} // namespace ll::memory
128
129#define LL_HOOK_IMPL(REGISTER, FUNC_PTR, STATIC, CALL, DEF_TYPE, TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
130 struct DEF_TYPE : public TYPE { \
131 inline static ::std::atomic_uint _AutoHookCount{}; \
132 \
133 private: \
134 using _FuncPtr = ::ll::memory::FuncPtr; \
135 using HookPriority = ::ll::memory::HookPriority; \
136 using _RawFuncType = RET_TYPE FUNC_PTR(__VA_ARGS__); \
137 using _RawConstFuncType = ::ll::memory::AddConstAtMemberFunT<_RawFuncType>; \
138 \
139 template <class T> \
140 struct _ConstDetector { \
141 [[maybe_unused]] static constexpr bool value = false; \
142 explicit constexpr _ConstDetector(T) {} \
143 }; \
144 template <class T> \
145 [[maybe_unused]] _ConstDetector(T) -> _ConstDetector<T>; \
146 [[maybe_unused]] _ConstDetector(_RawFuncType) -> _ConstDetector<_RawFuncType>; \
147 template <> \
148 struct _ConstDetector<_RawConstFuncType> { \
149 [[maybe_unused]] static constexpr bool value = true; \
150 explicit constexpr _ConstDetector(_RawConstFuncType) {} \
151 }; \
152 template <class T = _RawFuncType, std::enable_if_t<std::is_member_function_pointer_v<T>, int> = 0> \
153 [[maybe_unused]] _ConstDetector(_RawConstFuncType) -> _ConstDetector<_RawConstFuncType>; \
154 \
155 static constexpr bool _IsConstMemberFunction = decltype(_ConstDetector{IDENTIFIER})::value; \
156 \
157 using _OriginFuncType = ::std::conditional_t<_IsConstMemberFunction, _RawConstFuncType, _RawFuncType>; \
158 \
159 inline static _FuncPtr _HookTarget{}; \
160 inline static _OriginFuncType _OriginalFunc{}; \
161 \
162 template <class T> \
163 static consteval void _Detector() { \
164 if constexpr (requires { ::ll::memory::virtualDetector<T, IDENTIFIER>(); }) { \
165 if constexpr (::ll::memory::virtualDetector<T, IDENTIFIER>()) { \
166 static_assert( \
167 ::ll::traits::always_false<T>, \
168 #IDENTIFIER " is a virtual function, you need use prefix $ workaround to hook it." \
169 ); \
170 } \
171 } \
172 } \
173 \
174 public: \
175 template <class T> \
176 requires(::std::is_polymorphic_v<TYPE> && ::std::is_base_of_v<T, TYPE>) \
177 [[nodiscard]] TYPE* thisFor() { \
178 return static_cast<decltype(this)>(reinterpret_cast<T*>(this)); \
179 } \
180 template <class... Args> \
181 STATIC RET_TYPE origin(Args&&... params) { \
182 return CALL(::std::forward<Args>(params)...); \
183 } \
184 \
185 STATIC RET_TYPE detour(__VA_ARGS__); \
186 \
187 static int hook(bool suspendThreads = true) { \
188 _Detector<_OriginFuncType>(); \
189 if (!_HookTarget) _HookTarget = ::ll::memory::resolveIdentifier<_OriginFuncType>(IDENTIFIER); \
190 return ::ll::memory::hook( \
191 _HookTarget, \
192 ::ll::memory::toFuncPtr(&DEF_TYPE::detour), \
193 reinterpret_cast<_FuncPtr*>(&_OriginalFunc), \
194 PRIORITY, \
195 suspendThreads \
196 ); \
197 } \
198 \
199 static bool unhook(bool suspendThreads = true) { \
200 return ::ll::memory::unhook(_HookTarget, ::ll::memory::toFuncPtr(&DEF_TYPE::detour), suspendThreads); \
201 } \
202 }; \
203 REGISTER; \
204 RET_TYPE DEF_TYPE::detour(__VA_ARGS__)
205
206#define LL_AUTO_REG_HOOK_IMPL(FUNC_PTR, STATIC, CALL, DEF_TYPE, ...) \
207 LL_VA_EXPAND(LL_HOOK_IMPL( \
208 inline ::ll::memory::HookRegistrar<DEF_TYPE> DEF_TYPE##AutoRegister, \
209 FUNC_PTR, \
210 STATIC, \
211 CALL, \
212 DEF_TYPE, \
213 __VA_ARGS__ \
214 ))
215
216#define LL_MANUAL_REG_HOOK_IMPL(...) LL_VA_EXPAND(LL_HOOK_IMPL(, __VA_ARGS__))
217
218#define LL_STATIC_HOOK_IMPL(...) LL_VA_EXPAND(LL_MANUAL_REG_HOOK_IMPL((*), static, _OriginalFunc, __VA_ARGS__))
219
220#define LL_AUTO_STATIC_HOOK_IMPL(...) LL_VA_EXPAND(LL_AUTO_REG_HOOK_IMPL((*), static, _OriginalFunc, __VA_ARGS__))
221
222#define LL_INSTANCE_HOOK_IMPL(DEF_TYPE, TYPE, ...) \
223 LL_VA_EXPAND(LL_MANUAL_REG_HOOK_IMPL((TYPE::*), , (this->*_OriginalFunc), DEF_TYPE, TYPE, __VA_ARGS__))
224
225#define LL_AUTO_INSTANCE_HOOK_IMPL(DEF_TYPE, TYPE, ...) \
226 LL_VA_EXPAND(LL_AUTO_REG_HOOK_IMPL((TYPE::*), , (this->*_OriginalFunc), DEF_TYPE, TYPE, __VA_ARGS__))
227
239#define LL_TYPE_STATIC_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...) \
240 LL_VA_EXPAND(LL_STATIC_HOOK_IMPL(DEF_TYPE, TYPE, PRIORITY, IDENTIFIER, RET_TYPE, __VA_ARGS__))
241
252#define LL_STATIC_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
253 LL_VA_EXPAND(LL_STATIC_HOOK_IMPL(DEF_TYPE, ::ll::memory::Hook, PRIORITY, IDENTIFIER, RET_TYPE, __VA_ARGS__))
254
260#define LL_AUTO_TYPE_STATIC_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...) \
261 LL_VA_EXPAND(LL_AUTO_STATIC_HOOK_IMPL(DEF_TYPE, TYPE, PRIORITY, IDENTIFIER, RET_TYPE, __VA_ARGS__))
262
268#define LL_AUTO_STATIC_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
269 LL_VA_EXPAND(LL_AUTO_STATIC_HOOK_IMPL(DEF_TYPE, ::ll::memory::Hook, PRIORITY, IDENTIFIER, RET_TYPE, __VA_ARGS__))
270
282#define LL_TYPE_INSTANCE_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...) \
283 LL_VA_EXPAND(LL_INSTANCE_HOOK_IMPL(DEF_TYPE, TYPE, PRIORITY, IDENTIFIER, RET_TYPE, __VA_ARGS__))
284
295#define LL_INSTANCE_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
296 LL_VA_EXPAND(LL_INSTANCE_HOOK_IMPL(DEF_TYPE, ::ll::memory::Hook, PRIORITY, IDENTIFIER, RET_TYPE, __VA_ARGS__))
297
303#define LL_AUTO_TYPE_INSTANCE_HOOK(DEF_TYPE, PRIORITY, TYPE, IDENTIFIER, RET_TYPE, ...) \
304 LL_VA_EXPAND(LL_AUTO_INSTANCE_HOOK_IMPL(DEF_TYPE, TYPE, PRIORITY, IDENTIFIER, RET_TYPE, __VA_ARGS__))
305
311#define LL_AUTO_INSTANCE_HOOK(DEF_TYPE, PRIORITY, IDENTIFIER, RET_TYPE, ...) \
312 LL_VA_EXPAND(LL_AUTO_INSTANCE_HOOK_IMPL(DEF_TYPE, ::ll::memory::Hook, PRIORITY, IDENTIFIER, RET_TYPE, __VA_ARGS__))
Definition Hook.h:96
Definition Signature.h:43
Definition Hook.h:61
Definition Hook.h:125
Definition Hook.h:20