mirror of
https://github.com/Jous99/F4MP.git
synced 2026-01-12 10:40:53 +01:00
258 lines
7.8 KiB
C
258 lines
7.8 KiB
C
|
|
#ifndef COMMON_HOOK_H
|
||
|
|
#define COMMON_HOOK_H
|
||
|
|
|
||
|
|
#include <Windows.h>
|
||
|
|
#include <cstdint>
|
||
|
|
#include "detours.h"
|
||
|
|
#include "Types.h"
|
||
|
|
|
||
|
|
#include "Exceptions.h"
|
||
|
|
|
||
|
|
namespace Hooks {
|
||
|
|
|
||
|
|
enum class CallConvention
|
||
|
|
{
|
||
|
|
stdcall_t,
|
||
|
|
cdecl_t
|
||
|
|
};
|
||
|
|
|
||
|
|
template <CallConvention cc, typename retn, typename convention, typename ...args>
|
||
|
|
struct convention;
|
||
|
|
|
||
|
|
template <typename retn, typename ...args>
|
||
|
|
struct convention<CallConvention::stdcall_t, retn, args...>
|
||
|
|
{
|
||
|
|
typedef retn (__stdcall *type)(args ...);
|
||
|
|
};
|
||
|
|
|
||
|
|
template <typename retn, typename ...args>
|
||
|
|
struct convention<CallConvention::cdecl_t, retn, args...>
|
||
|
|
{
|
||
|
|
typedef retn (__cdecl *type)(args ...);
|
||
|
|
};
|
||
|
|
|
||
|
|
template <CallConvention cc, typename retn, typename ...args>
|
||
|
|
class Hook
|
||
|
|
{
|
||
|
|
typedef typename convention<cc, retn, args...>::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 <typename T>
|
||
|
|
void apply(T pFunc, type detour)
|
||
|
|
{
|
||
|
|
detour_ = detour;
|
||
|
|
orig_ = static_cast<size_t>(pFunc);
|
||
|
|
|
||
|
|
transaction_begin();
|
||
|
|
update_thread(GetCurrentThread());
|
||
|
|
const auto result = DetourAttach(reinterpret_cast<void **>(&orig_), reinterpret_cast<void *>(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<void **>(&orig_), reinterpret_cast<void *>(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
|