#ifndef COMMON_HOOK_H #define COMMON_HOOK_H #include #include #include "detours.h" #include "Types.h" #include "Exceptions.h" namespace Hooks { enum class CallConvention { stdcall_t, cdecl_t }; template struct convention; template struct convention { typedef retn (__stdcall *type)(args ...); }; template struct convention { typedef retn (__cdecl *type)(args ...); }; template class Hook { typedef typename convention::type type; size_t orig_; type detour_; bool is_applied_; bool has_open_transaction_; void transaction_begin() { const auto result = DetourTransactionBegin(); if (result != NO_ERROR) { if (result == ERROR_INVALID_OPERATION) { throw Exceptions::Core::Exceptions::DetourException( "A pending transaction already exists" ); } throw Exceptions::Core::Exceptions::DetourException("Unknown error"); } has_open_transaction_ = true; } void transaction_commit() { const auto result = DetourTransactionCommit(); if (result != NO_ERROR) { switch (result) { case ERROR_INVALID_DATA: throw Exceptions::Core::Exceptions::DetourException( "Target function was changed by third party between steps of the transaction" ); case ERROR_INVALID_OPERATION: throw Exceptions::Core::Exceptions::DetourException( "No pending transaction exists" ); case ERROR_INVALID_BLOCK: throw Exceptions::Core::Exceptions::DetourException( "The function referenced is too small to be detoured" ); case ERROR_INVALID_HANDLE: throw Exceptions::Core::Exceptions::DetourException( "The ppPointer parameter is null or points to a null pointer" ); case ERROR_NOT_ENOUGH_MEMORY: throw Exceptions::Core::Exceptions::DetourException( "Not enough memory exists to complete the operation" ); default: throw Exceptions::Core::Exceptions::DetourException( "Unknown error" ); } } has_open_transaction_ = false; } static void update_thread(HANDLE hThread) { const auto result = DetourUpdateThread(hThread); if (result != NO_ERROR) { if (result == ERROR_NOT_ENOUGH_MEMORY) { throw Exceptions::Core::Exceptions::DetourException( "Not enough memory to record identity of thread" ); } throw Exceptions::Core::Exceptions::DetourException("Unknown error"); } } public: Hook() : orig_(0), detour_(0), is_applied_(false), has_open_transaction_(false) { } ~Hook() noexcept(false) { if (has_open_transaction_) { const auto result = DetourTransactionAbort(); if (result != NO_ERROR) { if (result == ERROR_INVALID_OPERATION) { throw Exceptions::Core::Exceptions::DetourException( "No pending transaction exists" ); } throw Exceptions::Core::Exceptions::DetourException("Unknown error"); } } remove(); } template void apply(T pFunc, type detour) { detour_ = detour; orig_ = static_cast(pFunc); transaction_begin(); update_thread(GetCurrentThread()); const auto result = DetourAttach(reinterpret_cast(&orig_), reinterpret_cast(detour_)); if (result != NO_ERROR) { switch (result) { case ERROR_INVALID_BLOCK: throw Exceptions::Core::Exceptions::DetourException( "The function referenced is too small to be detoured" ); case ERROR_INVALID_HANDLE: throw Exceptions::Core::Exceptions::DetourException( "The ppPointer parameter is null or points to a null pointer" ); case ERROR_INVALID_OPERATION: throw Exceptions::Core::Exceptions::DetourException( "No pending transaction exists" ); case ERROR_NOT_ENOUGH_MEMORY: throw Exceptions::Core::Exceptions::DetourException( "Not enough memory exists to complete the operation" ); default: throw Exceptions::Core::Exceptions::DetourException("Unknown error"); } } transaction_commit(); is_applied_ = true; } void remove() { if (!is_applied_) return; is_applied_ = false; transaction_begin(); update_thread(GetCurrentThread()); const auto result = DetourDetach(reinterpret_cast(&orig_), reinterpret_cast(detour_)); if (result != NO_ERROR) { switch (result) { case ERROR_INVALID_BLOCK: throw Exceptions::Core::Exceptions::DetourException( "The function to be detached was too small to be detoured" ); case ERROR_INVALID_HANDLE: throw Exceptions::Core::Exceptions::DetourException( "The ppPointer parameter is null or points to a null pointer" ); case ERROR_INVALID_OPERATION: throw Exceptions::Core::Exceptions::DetourException( "No pending transaction exists" ); case ERROR_NOT_ENOUGH_MEMORY: throw Exceptions::Core::Exceptions::DetourException( "Not enough memory exists to complete the operation" ); default: throw Exceptions::Core::Exceptions::DetourException( "Unknown error" ); } } transaction_commit(); } auto get_orig(){ return orig_; } retn call_orig(args ... p) { return type(orig_)(p...); } bool is_applied() const { return is_applied_; } }; } #endif