From 50fb354983432abd6a7ef9a6c509d6a0045a3bbe Mon Sep 17 00:00:00 2001 From: slayercio Date: Tue, 18 Nov 2025 20:01:46 +0100 Subject: [PATCH] feat: added inline hook manager --- src/detail/hooking.hpp | 31 ++++++++++++ src/impl/hooking.cpp | 105 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 src/detail/hooking.hpp create mode 100644 src/impl/hooking.cpp diff --git a/src/detail/hooking.hpp b/src/detail/hooking.hpp new file mode 100644 index 0000000..3f240db --- /dev/null +++ b/src/detail/hooking.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include + +namespace fxn +{ + struct HookInfo + { + void* targetFunction; + void* hookFunction; + std::vector originalBytes; + }; + + class HookingManager + { + private: + std::vector m_Hooks; + + protected: + HookingManager(); + ~HookingManager(); + + public: + static HookingManager& GetInstance(); + static void Destroy(); + + public: + bool InstallHook(void* targetFunction, void* hookFunction); + bool RemoveHook(void* targetFunction); + }; +} \ No newline at end of file diff --git a/src/impl/hooking.cpp b/src/impl/hooking.cpp new file mode 100644 index 0000000..c844540 --- /dev/null +++ b/src/impl/hooking.cpp @@ -0,0 +1,105 @@ +#include +#include + +#include + +namespace fxn +{ + namespace + { + HookingManager* g_HookingManager = nullptr; + } + + HookingManager::HookingManager() + { + } + + HookingManager::~HookingManager() + { + for (const auto& hook : m_Hooks) + { + RemoveHook(hook.targetFunction); + } + + m_Hooks.clear(); + } + + HookingManager& HookingManager::GetInstance() + { + if (!g_HookingManager) + { + g_HookingManager = new HookingManager(); + } + + return *g_HookingManager; + } + + void HookingManager::Destroy() + { + if (g_HookingManager) + { + delete g_HookingManager; + } + + g_HookingManager = nullptr; + } + + bool HookingManager::InstallHook(void* targetFunction, void* hookFunction) + { + /* + mov rax, + jmp rax + */ + constexpr auto HOOK_SIZE = 12; + constexpr auto HOOK_INSTRUCTION = std::array + { + 0x48, 0xB8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xE0 + }; + + auto hookBytes = HOOK_INSTRUCTION; + *reinterpret_cast(&hookBytes[2]) = reinterpret_cast(hookFunction); + + DWORD oldProtect; + if (!VirtualProtect(targetFunction, HOOK_SIZE, PAGE_EXECUTE_READWRITE, &oldProtect)) + { + return false; + } + + HookInfo hookInfo; + hookInfo.targetFunction = targetFunction; + hookInfo.hookFunction = hookFunction; + hookInfo.originalBytes.resize(HOOK_SIZE); + std::memcpy(hookInfo.originalBytes.data(), targetFunction, HOOK_SIZE); + + std::memcpy(targetFunction, hookBytes.data(), HOOK_SIZE); + VirtualProtect(targetFunction, HOOK_SIZE, oldProtect, &oldProtect); + + m_Hooks.push_back(std::move(hookInfo)); + return true; + } + + bool HookingManager::RemoveHook(void* targetFunction) + { + auto it = std::find_if(m_Hooks.begin(), m_Hooks.end(), + [targetFunction](const HookInfo& hook) { return hook.targetFunction == targetFunction; }); + + if (it == m_Hooks.end()) + { + return false; + } + + DWORD oldProtect; + if (!VirtualProtect(targetFunction, it->originalBytes.size(), PAGE_EXECUTE_READWRITE, &oldProtect)) + { + return false; + } + + std::memcpy(targetFunction, it->originalBytes.data(), it->originalBytes.size()); + VirtualProtect(targetFunction, it->originalBytes.size(), oldProtect, &oldProtect); + + m_Hooks.erase(it); + return true; + } +} \ No newline at end of file