#pragma once // used: [ext] minhook // @credits: https://github.com/TsudaKageyu/minhook #include "../../dependencies/minhook/minhook.h" // used: l_print #include "log.h" template class CBaseHookObject { public: /// setup hook and replace function /// @returns: true if hook has been successfully created, false otherwise bool Create(void* pFunction, void* pDetour) { if (pFunction == nullptr || pDetour == nullptr) return false; pBaseFn = pFunction; pReplaceFn = pDetour; if (const MH_STATUS status = MH_CreateHook(pBaseFn, pReplaceFn, &pOriginalFn); status != MH_OK) { #ifdef _DEBUG L_PRINT(LOG_ERROR) << CS_XOR("failed to create hook, status: \"") << MH_StatusToString(status) << CS_XOR("\" with base address: ") << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast(pBaseFn); #else L_PRINT(LOG_ERROR) << CS_XOR("failed to create hook"); #endif CS_ASSERT(false); return false; } if (!Replace()) return false; return true; } /// patch memory to jump to our function instead of original /// @returns: true if hook has been successfully applied, false otherwise bool Replace() { // check is hook has been created if (pBaseFn == nullptr) return false; // check that function isn't already hooked if (bIsHooked) return false; if (const MH_STATUS status = MH_EnableHook(pBaseFn); status != MH_OK) { #ifdef _DEBUG L_PRINT(LOG_ERROR) << CS_XOR("failed to enable hook, status: \"") << MH_StatusToString(status) << CS_XOR("\" with base address: ") << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast(pBaseFn); #else L_PRINT(LOG_ERROR) << CS_XOR("failed to enable hook"); #endif CS_ASSERT(false); return false; } // switch hook state bIsHooked = true; return true; } /// restore original function call and cleanup hook data /// @returns: true if hook has been successfully removed, false otherwise bool Remove() { // restore it at first if (!Restore()) return false; if (const MH_STATUS status = MH_RemoveHook(pBaseFn); status != MH_OK) { #ifdef _DEBUG L_PRINT(LOG_ERROR) << CS_XOR("failed to remove hook, status: \"") << MH_StatusToString(status) << CS_XOR("\" with base address: ") << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast(pBaseFn); #else L_PRINT(LOG_ERROR) << CS_XOR("failed to remove hook"); #endif CS_ASSERT(false); return false; } return true; } /// restore patched memory to original function call /// @returns: true if hook has been successfully restored, false otherwise bool Restore() { // check that function is hooked if (!bIsHooked) return false; if (const MH_STATUS status = MH_DisableHook(pBaseFn); status != MH_OK) { #ifdef _DEBUG L_PRINT(LOG_ERROR) << CS_XOR("failed to restore hook, status: \"") << MH_StatusToString(status) << CS_XOR("\" with base address: ") << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast(pBaseFn); #else L_PRINT(LOG_ERROR) << CS_XOR("failed to restore hook"); #endif CS_ASSERT(false); return false; } // switch hook state bIsHooked = false; return true; } /// @returns: original, unwrapped function that would be called without the hook CS_INLINE T GetOriginal() { return reinterpret_cast(pOriginalFn); } /// @returns: true if hook is applied at the time, false otherwise CS_INLINE bool IsHooked() const { return bIsHooked; } private: // current hook state bool bIsHooked = false; // function base handle void* pBaseFn = nullptr; // function that being replace the original call void* pReplaceFn = nullptr; // original function void* pOriginalFn = nullptr; };