mirror of https://github.com/r4sas/ExtraMirror
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
75 lines
3.7 KiB
75 lines
3.7 KiB
#ifndef EASYHOOK_HPP |
|
#define EASYHOOK_HPP |
|
#include "main.h" |
|
/* This macro creates the type, an instance of the type, and a prologue object specific to that function */ |
|
#define HOOKINIT(functor_type, function, trampoline_name, prologue_name) \ |
|
using functor_type = decltype(&function); \ |
|
functor_type trampoline_name = NULL; \ |
|
EasyHook::prologue prologue_name; |
|
|
|
namespace EasyHook { |
|
/* I googled this... it's supposedly 'equivalent' to __attribute__((packed)) in GCC */ |
|
# define PACKED(s) __pragma(pack(push, 1)) s __pragma(pack(pop)) |
|
# define FUNCTOR(function) decltype(&function) |
|
|
|
union prologue { |
|
PACKED(struct { |
|
CHAR jmp; |
|
ULONG addr; |
|
}) parts; |
|
CHAR bytes[sizeof(ULONGLONG)]; |
|
ULONGLONG full = 0; |
|
}; |
|
|
|
enum opcode { |
|
LONGJUMP_SIZE = 0x05, |
|
RELJUMP = 0xe9, |
|
NOP = 0x90, |
|
}; |
|
|
|
// TODO: Hook64 at some distant future time when I become smart enough to learn about 64-bit functions... |
|
|
|
class Hook32 { |
|
static const SIZE_T jumpSize = 5; |
|
public: |
|
Hook32() {}; |
|
|
|
LPVOID hook(LPVOID target, prologue &original, LPVOID hook) const { |
|
LPVOID trampoline = NULL; |
|
DWORD oldProtection = 0; |
|
if (jumpSize < 5) return 0; // minimum jump size of 5. Necessary for 32-bit programs |
|
if (!VirtualProtect((LPVOID)target, 1, PAGE_EXECUTE_READWRITE, &oldProtection)) |
|
return NULL; |
|
if (!(trampoline = VirtualAlloc(NULL, 1, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE))) |
|
return VirtualProtect((LPVOID)target, 1, oldProtection, &oldProtection), NULL; |
|
|
|
// set up the trampoline first so by the time the atomic function is called, it's already set up. |
|
memcpy(trampoline, target, jumpSize); // save the first 5 bytes of the original function in the trampoline |
|
*((PCHAR)((ULONG)trampoline + jumpSize)) = opcode::RELJUMP; // relative jump on the 6th byte... |
|
*((PULONG)((ULONG)trampoline + jumpSize + 1)) = (ULONG)target - (ULONG)trampoline - opcode::LONGJUMP_SIZE; // return back to the target |
|
|
|
memcpy(&original, target, sizeof original); // save the full original first 8 bytes from the function in a member variable |
|
prologue tmp = original; // copy the original 8 bytes so we don't overwrite anything important. |
|
tmp.parts.jmp = opcode::RELJUMP; // set the first byte to a relative jmp |
|
*(PULONG)(tmp.bytes + 1) = (ULONG)hook - (ULONG)target - opcode::LONGJUMP_SIZE; // jmp to the trampoline |
|
InterlockedExchange64((PLONGLONG)target, tmp.full); // atomically exchange the first 8 bytes of the original instruction |
|
|
|
VirtualProtect((LPVOID)target, 1, oldProtection, &oldProtection); // reset the target permissions |
|
return trampoline; |
|
} |
|
|
|
bool unhook(LPVOID trampoline, prologue original) const { |
|
DWORD oldProtection = 0; |
|
prologue origFunc; // a place to restore the original function to retrieve the address |
|
memcpy(&origFunc, (LPVOID)((ULONG)trampoline + jumpSize), sizeof origFunc); // get the 8 bytes from the trampoline |
|
PULONG target = (PULONG)(origFunc.parts.addr + opcode::LONGJUMP_SIZE + (ULONG)trampoline); // get the address and offset it to get the original |
|
if (!VirtualProtect((LPVOID)target, 1, PAGE_EXECUTE_READWRITE, &oldProtection)) // give RWX permissions to the target function |
|
return false; |
|
InterlockedExchange64((PLONGLONG)target, original.full); // replace the 8 bytes of the original function with the original bytes. |
|
VirtualProtect((LPVOID)target, 1, oldProtection, &oldProtection); // restore the original function permissions |
|
VirtualFree(trampoline, 0, MEM_RELEASE); // release the memory |
|
return true; |
|
} |
|
}; |
|
} |
|
#endif |