#pragma once // Verified in-place patches of sacred.exe code. Every function checks that the // bytes it expects are present before writing, logs and refuses otherwise. #include #include namespace re { bool WriteCode(uint32_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(uint32_t instruction, uint32_t 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(uint32_t instruction, uint32_t from, uint32_t to); // Replace the byte `from` at `instruction + offset` (an imm8 or disp8) by `to`. bool PatchInstructionByte(uint32_t 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. bool ReplaceCode(uint32_t address, const void* from, const void* to, size_t size); // The `call rel32` at `instruction` (E8) calls `from`; make it call `to`. bool RedirectCall(uint32_t instruction, uint32_t from, const void* to); // The near conditional jump at `instruction` (0F 8x rel32) goes to `from`; // make it go to `to`. bool RedirectJcc(uint32_t instruction, uint32_t from, const void* to); // A code replacement, for tables. struct CodeReplacement { uint32_t 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 { uint32_t 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 uint32_t* 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 uint32_t (&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(uint32_t 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)