Lightweight C++11 library with a tidy API, built using Capstone, to facilitate creating and tracking function-level and instruction-level hooks, or other arbitrary memory patches.
Patcher is a C++11 memory patching and code hooking library that aims to be lightweight yet powerful. It is built around Capstone, a LLVM-based disassembler that itself is lightweight enough to run on embedded systems. Patcher can be compared to Microsoft's Detours library, but Patcher possesses some functionality that Detours lacks.
Patcher's features include:
Hook
)HookCall
)LowLevelHook
)EditExports
)ReplaceReferencesToGlobal
)Write
, WriteBytes
, Construct
)The interface has a number of conveniences, including:
dev
branch includes experimental support for capturing lambdas and functors.Currently, only x86-32, MSVC, and Windows are supported; there is experimental support for Clang/GCC/ICC in MS ABI mode. LowLevelHook
currently only supports standard registers.
dev
branch includes experimental x86-64 support.Future updates may include Python bindings, full support for other common compilers and ABIs, patching imports, patching *nix binaries, extended registers in LowLevelHook
, x86-64, and possibly ARM.
PmfCast
(dev
branch removes this requirement)Patcher's interface is based around PatchContext
RAII objects.
PatchContexts
as function-level statics or globals, so that cleanup automatically happens on exit.PatchContexts
can be used to group together related patches and toggle them separate from each other.PatchContext
is associated with one module, but a module can have as many PatchContexts
as desired.PatchContext
instance.
Examples of commonly-used interfaces:
using namespace Patcher;
// Constructing a PatchContext with no args targets the base module. To target other modules, we could do e.g.
// PatchContext("user32.dll"), or PatchContext("someDLL.dll", true) to hold a reference to the module.
// We declare this as a function-level static so that the patches get reverted when this module gets unloaded.
static PatchContext patcher;
// Lock all other process threads to prevent race conditions between patching and executing.
patcher.LockThreads();
// Insert a code hook replacing a function (and return a function pointer used to call the original function).
static bool(*pfnOldFunction)(int) = nullptr;
patcher.Hook(&SomeFunction, [](int x) -> bool { return pfnOldFunction(x + 1); }, &pfnOldFunction);
// Hook a class virtual function using the MFN_PTR() macro to get its address.
//
// In this example, we assume the target function uses the thiscall calling convention, so we use the ThiscallLambdaPtr
// util to convert the lambda to use thiscall. There is also StdcallLambdaPtr, FastcallLambdaPtr, VectorcallLambdaPtr.
patcher.Hook(
MFN_PTR(SomeClass::SomeVirtualFunction), ThiscallLambdaPtr([](SomeClass* pThis, int x) { pThis->someField_ -= x; }));
// Redirect a function call instruction.
patcher.HookCall(0x4047A8, [](void* p, size_t l) -> void { memset(p, 0, l); });
// Insert an instruction-level hook which read/writes specified registers and maybe changes control flow via return
// value. Note that a return value of 0 means return to origin.
patcher.LowLevelHook(0x518A00, [](Eax<int> readableRegister, Esi<bool>& writableRegister)
{ writableRegister = !writableRegister; return (readableRegister >= 1) ? 0 : 0x518B20; });
// Write some arbitrary bytes.
patcher.WriteBytes(0x481AA4, { 0xC3, 0x90, 0x90 });
// Write some POD data.
patcher.Write<int[3]>(0x5F29AC, { -1, 0, 1 });
// Write some non-POD data.
patcher.Construct<std::vector<int>>(0x5FF740, 10);
// Replace fixed references within the module to a global array with a larger-sized one.
static int newExtendedGlobal[10] = { };
patcher.ReplaceReferencesToGlobal(0x608220, sizeof(int[3]), &newExtendedGlobal);
// Revert a previous patch.
patcher.Revert(0x5FF740);
// Revert all patches. Note that this also gets called automatically when the PatchContext is destroyed.
patcher.RevertAll();
// Unlock threads previously locked by LockThreads().
patcher.UnlockThreads();
bool success = (patcher.GetStatus() == Status::Ok);
For more information, see the Doxygen comments in Patcher.h
and PatcherUtil.h
.
Outpost 2: Divided Destiny
community patchPatcher is licensed under the BSD 3-Clause license. See LICENSE
for more information.