#pragma once // Verified in-place patches of game code. Every function checks that the // bytes it expects are present before writing, logs and refuses otherwise. // // Locations are module addresses (re::Address, as in Ghidra). Absolute // addresses inside the code (operands, call targets) were relocated with // their module, so they are given as re::Address too; plain immediates and // struct offsets stay uint32_t. #include #include #include "re/module.h" namespace re { // Writes to a running address (e.g. one already resolved). bool WriteCode(uintptr_t address, const void* bytes, size_t size); // The instruction at `instruction` has the absolute memory operand `from` // (e.g. `fmul st, dword ptr [from]`); make it read `to` instead. bool RedirectOperand(Address instruction, Address from, const void* to); // Replace a 32-bit displacement/immediate `from` inside the instruction at // `instruction` by `to` (e.g. a struct field offset in `[ebp+ecx*8+3FF2Ch]`). bool PatchInstructionDword(Address instruction, uint32_t from, uint32_t to); // Replace the byte `from` at `instruction + offset` (an imm8 or disp8) by `to`. bool PatchInstructionByte(Address instruction, size_t offset, uint8_t from, uint8_t to); // Replace the code bytes `from` at `address` by `to` (same length), e.g. a // whole short instruction. The bytes must not contain relocated addresses. bool ReplaceCode(Address address, const void* from, const void* to, size_t size); // The `call rel32` at `instruction` (E8) calls `from`; make it call `to`. bool RedirectCall(Address instruction, Address from, const void* to); // The near conditional jump at `instruction` (0F 8x rel32) goes to `from`; // make it go to `to`. bool RedirectJcc(Address instruction, Address from, const void* to); // A code replacement, for tables. struct CodeReplacement { Address address; uint8_t from[8]; uint8_t to[8]; uint8_t size; }; int ReplaceAll(const CodeReplacement* replacements, size_t count); template int ReplaceAll(const CodeReplacement (&replacements)[N]) { return ReplaceAll(replacements, N); } // One byte operand of an instruction, for tables of byte patches. struct BytePatch { Address instruction; uint8_t offset; // of the byte within the instruction uint8_t from; }; // Apply the same change to a table of instructions; return the number that // failed (each failure is logged). int PatchAll(const BytePatch* patches, size_t count, uint8_t to); int PatchAll(const Address* instructions, size_t count, uint32_t from, uint32_t to); template int PatchAll(const BytePatch (&patches)[N], uint8_t to) { return PatchAll(patches, N, to); } template int PatchAll(const Address (&instructions)[N], uint32_t from, uint32_t to) { return PatchAll(instructions, N, from, to); } // `push imm32` (68 xx xx xx xx) at `instruction`: replace `expected` by `value`. bool PatchPushImm32(Address instruction, uint32_t expected, uint32_t value); // Code that must run once at startup, after the hooks are installed. class StartupRegistration { public: StartupRegistration(const char* name, void (*function)()); }; void RunStartupFunctions(); } // namespace re #define RE_STARTUP(name, function) \ static ::re::StartupRegistration name##_startup(#name, &function)