#pragma once #include #include #include "utils.h" namespace hooks { class base_untyped_hook { std::string m_name; public: base_untyped_hook(std::string name) : m_name(name) {} virtual ~base_untyped_hook() = default; virtual bool check_consistencies() const { return true; } virtual void assert_dominance() const { } const std::string& name() const { return m_name; } }; template class base_hook; template class base_hook : public base_untyped_hook { using TFn = TReturn(TArgs...); private: TFn* const m_pfnOriginal; utils::thunk m_thunk; public: base_hook(std::string name, TFn* pfnOriginal) : base_untyped_hook(name) , m_pfnOriginal(pfnOriginal) , m_thunk(std::move(name), m_pfnOriginal) { } virtual void set_detour(std::function fn) { if (!fn) m_thunk.set_target(m_pfnOriginal); else m_thunk.set_target(std::move(fn)); } virtual TReturn call_original(TArgs... args) { return m_pfnOriginal(std::forward(args)...); } protected: TFn* get_original() const { return m_pfnOriginal; } TFn* get_thunk() const { return m_thunk.get_thunk(); } }; template class import_hook : public base_hook { using Base = base_hook; TFn** const m_ppfnImportTableItem; public: import_hook(std::string name, TFn** ppfnImportTableItem) : Base(std::move(name), *ppfnImportTableItem) , m_ppfnImportTableItem(ppfnImportTableItem) { const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE); *ppfnImportTableItem = Base::get_thunk(); } import_hook(std::string name, const char* pcszDllName, const char* pcszFunctionName, int hintOrOrdinal) : import_hook(std::move(name), utils::loaded_module::current_process().get_imported_function_pointer(pcszDllName, pcszFunctionName, hintOrOrdinal)) { } ~import_hook() override { const utils::memory_tenderizer tenderizer(m_ppfnImportTableItem, sizeof * m_ppfnImportTableItem, PAGE_READWRITE); *m_ppfnImportTableItem = Base::get_original(); } bool check_consistencies() const override { return *m_ppfnImportTableItem == Base::get_thunk(); } void assert_dominance() const override { if (check_consistencies()) return; const utils::memory_tenderizer tenderizer(m_ppfnImportTableItem, sizeof * m_ppfnImportTableItem, PAGE_READWRITE); *m_ppfnImportTableItem = Base::get_thunk(); } }; template class direct_hook; template class direct_hook : public base_hook { using TFn = TReturn(TArgs...); using Base = base_hook; TFn* m_pfnMinHookBridge; public: direct_hook(std::string name, TFn* pfnFunction) : Base(std::move(name), pfnFunction) { if (const auto mhStatus = MH_CreateHook(pfnFunction, Base::get_thunk(), reinterpret_cast(&m_pfnMinHookBridge)); mhStatus != MH_OK) throw std::runtime_error(std::format("MH_CreateHook(0x{:X}, ...) failure: {}", reinterpret_cast(pfnFunction), static_cast(mhStatus))); MH_EnableHook(Base::get_original()); } ~direct_hook() override { MH_DisableHook(Base::get_original()); } TReturn call_original(TArgs... args) override { return m_pfnMinHookBridge(std::forward(args)...); } }; class wndproc_hook : public base_hook> { using Base = base_hook>; const HWND m_hwnd; public: wndproc_hook(std::string name, HWND hwnd) : Base(std::move(name), reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_WNDPROC))) , m_hwnd(hwnd) { SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast(Base::get_thunk())); } ~wndproc_hook() override { SetWindowLongPtrW(m_hwnd, GWLP_WNDPROC, reinterpret_cast(Base::get_original())); } LRESULT call_original(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) override { return CallWindowProcW(Base::get_original(), hwnd, msg, wParam, lParam); } bool check_consistencies() const override { return GetWindowLongPtrW(m_hwnd, GWLP_WNDPROC) == reinterpret_cast(Base::get_thunk()); } void assert_dominance() const override { if (check_consistencies()) return; SetWindowLongPtrW(m_hwnd, GWLP_WNDPROC, reinterpret_cast(Base::get_thunk())); } }; class untyped_import_hook : public base_untyped_hook { void** const m_ppfnImportTableItem; void* const m_pfnOriginalImport; public: untyped_import_hook(std::string name, void** ppfnImportTableItem, void* pThunk) : base_untyped_hook(std::move(name)) , m_pfnOriginalImport(*ppfnImportTableItem) , m_ppfnImportTableItem(ppfnImportTableItem) { const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE); *ppfnImportTableItem = pThunk; } ~untyped_import_hook() override { MEMORY_BASIC_INFORMATION mbi{}; VirtualQuery(m_ppfnImportTableItem, &mbi, sizeof mbi); if (mbi.State != MEM_COMMIT) return; const utils::memory_tenderizer tenderizer(m_ppfnImportTableItem, sizeof * m_ppfnImportTableItem, PAGE_READWRITE); *m_ppfnImportTableItem = m_pfnOriginalImport; } }; class getprocaddress_singleton_import_hook : public std::enable_shared_from_this { static inline const char* LogTag = "[global_import_hook]"; decltype(GetProcAddress)* const m_pfnGetProcAddress; utils::thunk m_thunk; std::shared_ptr m_getProcAddressHandler; void* m_ldrDllNotificationCookie{}; std::map m_dllNameMap; std::map> m_targetFns; std::map>>> m_hooks; public: getprocaddress_singleton_import_hook(); ~getprocaddress_singleton_import_hook(); std::shared_ptr set_handler(std::wstring dllName, std::string functionName, void* pfnDetour, std::function fnOnOriginalAddressAvailable); static std::shared_ptr get_instance(); private: void initialize(); FARPROC get_proc_address_handler(HMODULE hModule, LPCSTR lpProcName); void hook_module(const utils::loaded_module& mod); }; template class global_import_hook; template class global_import_hook : public base_untyped_hook { using TFn = TReturn(TArgs...); utils::thunk m_thunk; std::shared_ptr m_singleImportHook; public: global_import_hook(std::string name, std::wstring dllName, std::string functionName) : base_untyped_hook(name) , m_thunk(std::move(name), nullptr) { m_singleImportHook = getprocaddress_singleton_import_hook::get_instance()->set_handler( dllName, functionName, m_thunk.get_thunk(), [this](void* p) { m_thunk.set_target(reinterpret_cast(p)); } ); } virtual void set_detour(std::function fn) { if (!fn) m_thunk.set_target(reinterpret_cast(m_singleImportHook.get())); else m_thunk.set_target(std::move(fn)); } virtual TReturn call_original(TArgs... args) { return reinterpret_cast(m_singleImportHook.get())(std::forward(args)...); } }; }