#pragma once #include #include "utils.h" namespace hooks { template class base_hook; template class base_hook { using TFn = TReturn(TArgs...); private: TFn* const m_pfnOriginal; utils::thunk m_thunk; public: base_hook(TFn* pfnOriginal) : m_pfnOriginal(pfnOriginal) , m_thunk(m_pfnOriginal) { } virtual ~base_hook() = default; 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(TFn** ppfnImportTableItem) : Base(*ppfnImportTableItem) , m_ppfnImportTableItem(ppfnImportTableItem) { const utils::memory_tenderizer tenderizer(ppfnImportTableItem, sizeof * ppfnImportTableItem, PAGE_READWRITE); *ppfnImportTableItem = Base::get_thunk(); } import_hook(const char* pcszDllName, const char* pcszFunctionName, int hintOrOrdinal) : import_hook(utils::get_imported_function_pointer(GetModuleHandleW(nullptr), pcszDllName, pcszFunctionName, hintOrOrdinal)) { } ~import_hook() override { const utils::memory_tenderizer tenderizer(m_ppfnImportTableItem, sizeof * m_ppfnImportTableItem, PAGE_READWRITE); *m_ppfnImportTableItem = Base::get_original(); } }; template class export_hook : public base_hook { using Base = base_hook; static constexpr uint8_t DetouringThunkTemplate[12]{ 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs rax, 0x0000000000000000 0xFF, 0xE0, // jmp rax }; TFn* const m_pfnExportThunk; uint8_t s_originalThunk[sizeof DetouringThunkTemplate]{}; public: export_hook(TFn* pfnExportThunk) : Base(reinterpret_cast(utils::resolve_unconditional_jump_target(pfnExportThunk))) , m_pfnExportThunk(pfnExportThunk) { auto pExportThunk = reinterpret_cast(pfnExportThunk); // Make it writeable. const utils::memory_tenderizer tenderizer(pfnExportThunk, sizeof DetouringThunkTemplate, PAGE_EXECUTE_READWRITE); // Back up original thunk bytes. memcpy(s_originalThunk, pExportThunk, sizeof s_originalThunk); // Write thunk template. memcpy(pExportThunk, DetouringThunkTemplate, sizeof DetouringThunkTemplate); // Write target address. *reinterpret_cast(&pExportThunk[2]) = Base::get_thunk(); } ~export_hook() override { const utils::memory_tenderizer tenderizer(m_pfnExportThunk, sizeof DetouringThunkTemplate, PAGE_EXECUTE_READWRITE); // Restore original thunk bytes. memcpy(m_pfnExportThunk, s_originalThunk, sizeof s_originalThunk); // Clear state. memset(s_originalThunk, 0, sizeof s_originalThunk); } }; class wndproc_hook : public base_hook> { using Base = base_hook>; const HWND s_hwnd; public: wndproc_hook(HWND hwnd) : Base(reinterpret_cast(GetWindowLongPtrW(hwnd, GWLP_WNDPROC))) , s_hwnd(hwnd) { SetWindowLongPtrW(hwnd, GWLP_WNDPROC, reinterpret_cast(Base::get_thunk())); } ~wndproc_hook() override { SetWindowLongPtrW(s_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); } }; }