SafetyHook
Loading...
Searching...
No Matches
inline_hook.hpp
Go to the documentation of this file.
1
3
4#pragma once
5
6#ifndef SAFETYHOOK_USE_CXXMODULES
7#include <cstdint>
8#include <expected>
9#include <memory>
10#include <mutex>
11#include <utility>
12#include <vector>
13#else
14import std.compat;
15#endif
16
18#include "safetyhook/common.hpp"
19#include "safetyhook/utility.hpp"
20
21namespace safetyhook {
23class SAFETYHOOK_API InlineHook final {
24public:
26 struct Error {
28 enum : uint8_t {
36 } type;
37
39 union {
41 uint8_t* ip;
42 };
43
47 [[nodiscard]] static Error bad_allocation(Allocator::Error err) {
48 Error error{};
49 error.type = BAD_ALLOCATION;
50 error.allocator_error = err;
51 return error;
52 }
53
57 [[nodiscard]] static Error failed_to_decode_instruction(uint8_t* ip) {
58 Error error{};
60 error.ip = ip;
61 return error;
62 }
63
67 [[nodiscard]] static Error short_jump_in_trampoline(uint8_t* ip) {
68 Error error{};
70 error.ip = ip;
71 return error;
72 }
73
77 [[nodiscard]] static Error ip_relative_instruction_out_of_range(uint8_t* ip) {
78 Error error{};
80 error.ip = ip;
81 return error;
82 }
83
87 [[nodiscard]] static Error unsupported_instruction_in_trampoline(uint8_t* ip) {
88 Error error{};
90 error.ip = ip;
91 return error;
92 }
93
97 [[nodiscard]] static Error failed_to_unprotect(uint8_t* ip) {
98 Error error{};
100 error.ip = ip;
101 return error;
102 }
103
107 [[nodiscard]] static Error not_enough_space(uint8_t* ip) {
108 Error error{};
109 error.type = NOT_ENOUGH_SPACE;
110 error.ip = ip;
111 return error;
112 }
113 };
114
116 enum Flags : int {
118 StartDisabled = 1 << 0,
119 };
120
128 [[nodiscard]] static std::expected<InlineHook, Error> create(
129 void* target, void* destination, Flags flags = Default);
130
138 template <typename T, typename U>
139 [[nodiscard]] static std::expected<InlineHook, Error> create(T target, U destination, Flags flags = Default) {
140 return create(reinterpret_cast<void*>(target), reinterpret_cast<void*>(destination), flags);
141 }
142
150 [[nodiscard]] static std::expected<InlineHook, Error> create(
151 const std::shared_ptr<Allocator>& allocator, void* target, void* destination, Flags flags = Default);
152
160 template <typename T, typename U>
161 [[nodiscard]] static std::expected<InlineHook, Error> create(
162 const std::shared_ptr<Allocator>& allocator, T target, U destination, Flags flags = Default) {
163 return create(allocator, reinterpret_cast<void*>(target), reinterpret_cast<void*>(destination), flags);
164 }
165
166 InlineHook() = default;
167 InlineHook(const InlineHook&) = delete;
168 InlineHook(InlineHook&& other) noexcept;
169 InlineHook& operator=(const InlineHook&) = delete;
170 InlineHook& operator=(InlineHook&& other) noexcept;
171 ~InlineHook();
172
176 void reset();
177
180 [[nodiscard]] uint8_t* target() const { return m_target; }
181
184 [[nodiscard]] uintptr_t target_address() const { return reinterpret_cast<uintptr_t>(m_target); }
185
188 [[nodiscard]] uint8_t* destination() const { return m_destination; }
189
192 [[nodiscard]] uintptr_t destination_address() const { return reinterpret_cast<uintptr_t>(m_destination); }
193
196 [[nodiscard]] const Allocation& trampoline() const { return m_trampoline; }
197
200 explicit operator bool() const { return static_cast<bool>(m_trampoline); }
201
205 template <typename T> [[nodiscard]] T original() const { return reinterpret_cast<T>(m_trampoline.address()); }
206
209 [[nodiscard]] const auto& original_bytes() const { return m_original_bytes; }
210
217 template <typename RetT = void, typename... Args> RetT call(Args... args) {
218 std::scoped_lock lock{m_mutex};
219 return m_trampoline ? original<RetT (*)(Args...)>()(args...) : RetT();
220 }
221
228 template <typename RetT = void, typename... Args> RetT ccall(Args... args) {
229 std::scoped_lock lock{m_mutex};
230 return m_trampoline ? original<RetT(SAFETYHOOK_CCALL*)(Args...)>()(args...) : RetT();
231 }
232
239 template <typename RetT = void, typename... Args> RetT thiscall(Args... args) {
240 std::scoped_lock lock{m_mutex};
241 return m_trampoline ? original<RetT(SAFETYHOOK_THISCALL*)(Args...)>()(args...) : RetT();
242 }
243
250 template <typename RetT = void, typename... Args> RetT stdcall(Args... args) {
251 std::scoped_lock lock{m_mutex};
252 return m_trampoline ? original<RetT(SAFETYHOOK_STDCALL*)(Args...)>()(args...) : RetT();
253 }
254
261 template <typename RetT = void, typename... Args> RetT fastcall(Args... args) {
262 std::scoped_lock lock{m_mutex};
263 return m_trampoline ? original<RetT(SAFETYHOOK_FASTCALL*)(Args...)>()(args...) : RetT();
264 }
265
274 template <typename RetT = void, typename... Args> RetT unsafe_call(Args... args) {
275 return original<RetT (*)(Args...)>()(args...);
276 }
277
286 template <typename RetT = void, typename... Args> RetT unsafe_ccall(Args... args) {
287 return original<RetT(SAFETYHOOK_CCALL*)(Args...)>()(args...);
288 }
289
298 template <typename RetT = void, typename... Args> RetT unsafe_thiscall(Args... args) {
299 return original<RetT(SAFETYHOOK_THISCALL*)(Args...)>()(args...);
300 }
301
310 template <typename RetT = void, typename... Args> RetT unsafe_stdcall(Args... args) {
311 return original<RetT(SAFETYHOOK_STDCALL*)(Args...)>()(args...);
312 }
313
322 template <typename RetT = void, typename... Args> RetT unsafe_fastcall(Args... args) {
323 return original<RetT(SAFETYHOOK_FASTCALL*)(Args...)>()(args...);
324 }
325
327 [[nodiscard]] std::expected<void, Error> enable();
328
330 [[nodiscard]] std::expected<void, Error> disable();
331
333 [[nodiscard]] bool enabled() const { return m_enabled; }
334
335private:
336 friend class MidHook;
337
338 enum class Type {
339 Unset,
340 E9,
341 FF,
342 };
343
344 uint8_t* m_target{};
345 uint8_t* m_destination{};
346 Allocation m_trampoline{};
347 std::vector<uint8_t> m_original_bytes{};
348 uintptr_t m_trampoline_size{};
349 std::recursive_mutex m_mutex{};
350 bool m_enabled{};
351 Type m_type{Type::Unset};
352
353 std::expected<void, Error> setup(
354 const std::shared_ptr<Allocator>& allocator, uint8_t* target, uint8_t* destination);
355 std::expected<void, Error> e9_hook(const std::shared_ptr<Allocator>& allocator);
356
357#if SAFETYHOOK_ARCH_X86_64
358 std::expected<void, Error> ff_hook(const std::shared_ptr<Allocator>& allocator);
359#endif
360
361 void destroy();
362};
363} // namespace safetyhook
Allocator for allocating memory near target addresses.
A memory allocation.
Definition allocator.hpp:22
Error
The error type returned by the allocate functions.
Definition allocator.hpp:80
An inline hook.
Definition inline_hook.hpp:23
RetT stdcall(Args... args)
Calls the original function.
Definition inline_hook.hpp:250
RetT unsafe_fastcall(Args... args)
Calls the original function.
Definition inline_hook.hpp:322
std::expected< void, Error > enable()
Enable the hook.
uint8_t * target() const
Get a pointer to the target.
Definition inline_hook.hpp:180
RetT unsafe_thiscall(Args... args)
Calls the original function.
Definition inline_hook.hpp:298
RetT thiscall(Args... args)
Calls the original function.
Definition inline_hook.hpp:239
static std::expected< InlineHook, Error > create(T target, U destination, Flags flags=Default)
Create an inline hook.
Definition inline_hook.hpp:139
void reset()
Reset the hook.
uintptr_t target_address() const
Get the target address.
Definition inline_hook.hpp:184
static std::expected< InlineHook, Error > create(void *target, void *destination, Flags flags=Default)
Create an inline hook.
RetT unsafe_stdcall(Args... args)
Calls the original function.
Definition inline_hook.hpp:310
RetT fastcall(Args... args)
Calls the original function.
Definition inline_hook.hpp:261
static std::expected< InlineHook, Error > create(const std::shared_ptr< Allocator > &allocator, T target, U destination, Flags flags=Default)
Create an inline hook with a given Allocator.
Definition inline_hook.hpp:161
RetT call(Args... args)
Calls the original function.
Definition inline_hook.hpp:217
RetT unsafe_call(Args... args)
Calls the original function.
Definition inline_hook.hpp:274
Flags
Flags for InlineHook.
Definition inline_hook.hpp:116
@ StartDisabled
Start the hook disabled.
Definition inline_hook.hpp:118
@ Default
Default flags.
Definition inline_hook.hpp:117
uint8_t * destination() const
Get a pointer ot the destination.
Definition inline_hook.hpp:188
bool enabled() const
Check if the hook is enabled.
Definition inline_hook.hpp:333
RetT ccall(Args... args)
Calls the original function.
Definition inline_hook.hpp:228
std::expected< void, Error > disable()
Disable the hook.
const Allocation & trampoline() const
Get the trampoline Allocation.
Definition inline_hook.hpp:196
T original() const
Returns the address of the trampoline to call the original function.
Definition inline_hook.hpp:205
RetT unsafe_ccall(Args... args)
Calls the original function.
Definition inline_hook.hpp:286
uintptr_t destination_address() const
Get the destination address.
Definition inline_hook.hpp:192
const auto & original_bytes() const
Returns a vector containing the original bytes of the target function.
Definition inline_hook.hpp:209
static std::expected< InlineHook, Error > create(const std::shared_ptr< Allocator > &allocator, void *target, void *destination, Flags flags=Default)
Create an inline hook with a given Allocator.
A mid function hook.
Definition mid_hook.hpp:25
Error type for InlineHook.
Definition inline_hook.hpp:26
Error type for InlineHook.
Definition inline_hook.hpp:26
enum safetyhook::InlineHook::Error::@032317102165126106167177121203303367040001002232 type
The type of error.
@ UNSUPPORTED_INSTRUCTION_IN_TRAMPOLINE
An unsupported instruction was found in the trampoline.
Definition inline_hook.hpp:33
@ NOT_ENOUGH_SPACE
Not enough space to create the hook.
Definition inline_hook.hpp:35
@ IP_RELATIVE_INSTRUCTION_OUT_OF_RANGE
An IP-relative instruction is out of range.
Definition inline_hook.hpp:32
@ FAILED_TO_UNPROTECT
Failed to unprotect memory.
Definition inline_hook.hpp:34
@ FAILED_TO_DECODE_INSTRUCTION
Failed to decode an instruction.
Definition inline_hook.hpp:30
@ SHORT_JUMP_IN_TRAMPOLINE
The trampoline contains a short jump.
Definition inline_hook.hpp:31
@ BAD_ALLOCATION
An error occurred when allocating memory.
Definition inline_hook.hpp:29
uint8_t * ip
IP of the problematic instruction.
Definition inline_hook.hpp:41
static Error unsupported_instruction_in_trampoline(uint8_t *ip)
Create a UNSUPPORTED_INSTRUCTION_IN_TRAMPOLINE error.
Definition inline_hook.hpp:87
static Error short_jump_in_trampoline(uint8_t *ip)
Create a SHORT_JUMP_IN_TRAMPOLINE error.
Definition inline_hook.hpp:67
static Error not_enough_space(uint8_t *ip)
Create a NOT_ENOUGH_SPACE error.
Definition inline_hook.hpp:107
static Error failed_to_decode_instruction(uint8_t *ip)
Create a FAILED_TO_DECODE_INSTRUCTION error.
Definition inline_hook.hpp:57
static Error ip_relative_instruction_out_of_range(uint8_t *ip)
Create a IP_RELATIVE_INSTRUCTION_OUT_OF_RANGE error.
Definition inline_hook.hpp:77
static Error bad_allocation(Allocator::Error err)
Create a BAD_ALLOCATION error.
Definition inline_hook.hpp:47
Allocator::Error allocator_error
Allocator error information.
Definition inline_hook.hpp:40
static Error failed_to_unprotect(uint8_t *ip)
Create a FAILED_TO_UNPROTECT error.
Definition inline_hook.hpp:97