-
Notifications
You must be signed in to change notification settings - Fork 394
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Hooking: Replace minhook with safetyhook (thread trapping branch)
It's faster, safer, better.
- Loading branch information
Showing
10 changed files
with
227 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,100 @@ | ||
#include <spdlog/spdlog.h> | ||
#include <MinHook.h> | ||
|
||
#include <safetyhook/inline_hook.hpp> | ||
|
||
#include "FunctionHook.hpp" | ||
|
||
using namespace std; | ||
|
||
|
||
bool g_isMinHookInitialized{ false }; | ||
|
||
FunctionHook::FunctionHook(Address target, Address destination) | ||
: m_target{ 0 }, | ||
m_destination{ 0 }, | ||
m_original{ 0 } | ||
: m_target{ target }, | ||
m_destination{ destination } | ||
{ | ||
spdlog::info("Attempting to hook {:p}->{:p}", target.ptr(), destination.ptr()); | ||
|
||
// Initialize MinHook if it hasn't been already. | ||
if (!g_isMinHookInitialized && MH_Initialize() == MH_OK) { | ||
g_isMinHookInitialized = true; | ||
} | ||
|
||
// Create the hook. Call create afterwards to prevent race conditions accessing FunctionHook before it leaves its constructor. | ||
if (auto status = MH_CreateHook(target.as<LPVOID>(), destination.as<LPVOID>(), (LPVOID*)&m_original); status == MH_OK) { | ||
/*if (auto status = MH_CreateHook(target.as<LPVOID>(), destination.as<LPVOID>(), (LPVOID*)&m_original); status == MH_OK) { | ||
m_target = target; | ||
m_destination = destination; | ||
spdlog::info("Hook init successful {:p}->{:p}", target.ptr(), destination.ptr()); | ||
} | ||
else { | ||
spdlog::error("Failed to hook {:p}: {}", target.ptr(), MH_StatusToString(status)); | ||
} | ||
}*/ | ||
} | ||
|
||
FunctionHook::~FunctionHook() { | ||
remove(); | ||
} | ||
|
||
bool FunctionHook::create() { | ||
if (m_target == 0 || m_destination == 0 || m_original == 0) { | ||
if (m_target == 0 || m_destination == 0 ) { | ||
spdlog::error("FunctionHook not initialized"); | ||
return false; | ||
} | ||
|
||
if (auto status = MH_EnableHook((LPVOID)m_target); status != MH_OK) { | ||
/*if (auto status = MH_EnableHook((LPVOID)m_target); status != MH_OK) { | ||
m_original = 0; | ||
m_destination = 0; | ||
m_target = 0; | ||
spdlog::error("Failed to hook {:x}: {}", m_target, MH_StatusToString(status)); | ||
return false; | ||
}*/ | ||
|
||
try { | ||
auto expect = safetyhook::InlineHook::create(safetyhook::Allocator::global(), m_target, m_destination); | ||
|
||
if (!expect) { | ||
std::string error = ""; | ||
switch (expect.error().type) { | ||
case safetyhook::InlineHook::Error::BAD_ALLOCATION: | ||
error = "bad allocation"; | ||
break; | ||
case safetyhook::InlineHook::Error::FAILED_TO_DECODE_INSTRUCTION: | ||
error = "failed to decode instruction"; | ||
break; | ||
case safetyhook::InlineHook::Error::SHORT_JUMP_IN_TRAMPOLINE: | ||
error = "short jump in trampoline"; | ||
break; | ||
case safetyhook::InlineHook::Error::IP_RELATIVE_INSTRUCTION_OUT_OF_RANGE: | ||
error = "IP relative instruction out of range"; | ||
break; | ||
case safetyhook::InlineHook::Error::UNSUPPORTED_INSTRUCTION_IN_TRAMPOLINE: | ||
error = "unsupported instruction in trampoline"; | ||
break; | ||
case safetyhook::InlineHook::Error::FAILED_TO_UNPROTECT: | ||
error = "failed to unprotect memory"; | ||
break; | ||
case safetyhook::InlineHook::Error::NOT_ENOUGH_SPACE: | ||
error = "not enough space"; | ||
break; | ||
default: | ||
error = std::format("unknown error {}", (int32_t)expect.error().type); | ||
break; | ||
}; | ||
|
||
spdlog::error("Failed to hook {:x}: {}", m_target, error); | ||
return false; | ||
} | ||
|
||
m_inline_hook = std::move(*expect); | ||
} catch (const std::exception& e) { | ||
spdlog::error("Failed to hook {:x}: {}", m_target, e.what()); | ||
return false; | ||
} catch (...) { | ||
spdlog::error("Failed to hook {:x}: unknown exception", m_target); | ||
return false; | ||
} | ||
|
||
spdlog::info("Hooked {:x}->{:x}", m_target, m_destination); | ||
return true; | ||
} | ||
|
||
bool FunctionHook::remove() { | ||
// Don't try to remove invalid hooks. | ||
if (m_original == 0) { | ||
return true; | ||
} | ||
|
||
// Disable then remove the hook. | ||
if (MH_DisableHook((LPVOID)m_target) != MH_OK || | ||
MH_RemoveHook((LPVOID)m_target) != MH_OK) { | ||
if (m_inline_hook) { | ||
spdlog::info("Hooked {:x}->{:x}", m_target, m_destination); | ||
} else { | ||
spdlog::error("Failed to hook {:x}", m_target); | ||
return false; | ||
} | ||
|
||
// Invalidate the members. | ||
m_target = 0; | ||
m_destination = 0; | ||
m_original = 0; | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
#include <spdlog/spdlog.h> | ||
#include <MinHook.h> | ||
|
||
#include "FunctionHookMinHook.hpp" | ||
|
||
using namespace std; | ||
|
||
|
||
bool g_isMinHookInitialized{ false }; | ||
|
||
FunctionHookMinHook::FunctionHookMinHook(Address target, Address destination) | ||
: m_target{ 0 }, | ||
m_destination{ 0 }, | ||
m_original{ 0 } | ||
{ | ||
spdlog::info("Attempting to hook {:p}->{:p}", target.ptr(), destination.ptr()); | ||
|
||
// Initialize MinHook if it hasn't been already. | ||
if (!g_isMinHookInitialized && MH_Initialize() == MH_OK) { | ||
g_isMinHookInitialized = true; | ||
} | ||
|
||
// Create the hook. Call create afterwards to prevent race conditions accessing FunctionHookMinHook before it leaves its constructor. | ||
if (auto status = MH_CreateHook(target.as<LPVOID>(), destination.as<LPVOID>(), (LPVOID*)&m_original); status == MH_OK) { | ||
m_target = target; | ||
m_destination = destination; | ||
|
||
spdlog::info("Hook init successful {:p}->{:p}", target.ptr(), destination.ptr()); | ||
} | ||
else { | ||
spdlog::error("Failed to hook {:p}: {}", target.ptr(), MH_StatusToString(status)); | ||
} | ||
} | ||
|
||
FunctionHookMinHook::~FunctionHookMinHook() { | ||
remove(); | ||
} | ||
|
||
bool FunctionHookMinHook::create() { | ||
if (m_target == 0 || m_destination == 0 || m_original == 0) { | ||
spdlog::error("FunctionHookMinHook not initialized"); | ||
return false; | ||
} | ||
|
||
if (auto status = MH_EnableHook((LPVOID)m_target); status != MH_OK) { | ||
m_original = 0; | ||
m_destination = 0; | ||
m_target = 0; | ||
|
||
spdlog::error("Failed to hook {:x}: {}", m_target, MH_StatusToString(status)); | ||
return false; | ||
} | ||
|
||
spdlog::info("Hooked {:x}->{:x}", m_target, m_destination); | ||
return true; | ||
} | ||
|
||
bool FunctionHookMinHook::remove() { | ||
// Don't try to remove invalid hooks. | ||
if (m_original == 0) { | ||
return true; | ||
} | ||
|
||
// Disable then remove the hook. | ||
if (MH_DisableHook((LPVOID)m_target) != MH_OK || | ||
MH_RemoveHook((LPVOID)m_target) != MH_OK) { | ||
return false; | ||
} | ||
|
||
// Invalidate the members. | ||
m_target = 0; | ||
m_destination = 0; | ||
m_original = 0; | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
#pragma once | ||
|
||
#include <windows.h> | ||
#include <cstdint> | ||
|
||
#include <utility/Address.hpp> | ||
|
||
class FunctionHookMinHook { | ||
public: | ||
FunctionHookMinHook() = delete; | ||
FunctionHookMinHook(const FunctionHookMinHook& other) = delete; | ||
FunctionHookMinHook(FunctionHookMinHook&& other) = delete; | ||
FunctionHookMinHook(Address target, Address destination); | ||
virtual ~FunctionHookMinHook(); | ||
|
||
bool create(); | ||
|
||
// Called automatically by the destructor, but you can call it explicitly | ||
// if you need to remove the hook. | ||
bool remove(); | ||
|
||
auto get_original() const { | ||
return m_original; | ||
} | ||
|
||
template <typename T> | ||
T* get_original() const { | ||
return (T*)m_original; | ||
} | ||
|
||
auto is_valid() const { | ||
return m_original != 0; | ||
} | ||
|
||
FunctionHookMinHook& operator=(const FunctionHookMinHook& other) = delete; | ||
FunctionHookMinHook& operator=(FunctionHookMinHook&& other) = delete; | ||
|
||
private: | ||
uintptr_t m_target{ 0 }; | ||
uintptr_t m_destination{ 0 }; | ||
uintptr_t m_original{ 0 }; | ||
}; |
Oops, something went wrong.