Protection System For Counter-Strike 1.6
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

#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