CallHook is a simple to include and use C++17 function and function call hooking library, designed for non-invasive hooking by safely redirecting existing calls. Uses Zydis for disassembly.
CallHook aims to make hooking functions less invasive by redirecting existing function calls - meaning it targets incoming call instructions instead of the function prologue. This approach has several advantages over injecting trampolines:
- No instructions are injected - the program code is unchanged besides call targets.
- Resistant to race conditions - CallHook is designed to prevent race conditions while hooking and unhooking, including interactions between threads and modules.
- Hook templates - hook before and after a function call, override the return value, the function call or capture the entire context as if using breakpoints (no actual debugger needed!)
- Choose where you hook - hooking individual calls allows for greater control over the control flow of the program.
- Every call is a goal (almost) - use calls as safe injection points, you might not even need the function you hook, but its location in the surrounding code.
- Hook functions which are shorter than 5 bytes (impossible with a regular trampoline hook)
Include CallHook.h (if not using MSVC, you must statically link the two Zydis libraries found in thirdparty/Zydis to your project)
(all examples are taken from the example dll) Single call hook:
// Initialize CallHook. Make sure initialization succeeds before proceeding.
// You can pass in a different PEParser::ProcessInfo struct if you are looking to hook functions outside of the main module.
if (!CallHook::initialize()) return;
// Hook a specific call (by providing its address/offset from image base)
// This does not hook an entire function, but only a single time this function is called from a specific location.
auto hook2 = new CallHookTemplate<EntryHook>(reinterpret_cast<uint8_t*>(PEParser::getProcessInfo()->mInfo->lpBaseOfDll) + IBO_PLAYER_BLOCK_HOOK_POINT, applyShield);
A function hook:
// Initialize CallHook. Make sure initialization succeeds before proceeding.
// You can pass in a different PEParser::ProcessInfo struct if you are looking to hook functions outside of the main module.
if (!CallHook::initialize()) return;
// Create a call map of the target module.
// This disassembles all of the module's code (.text sections) and maps non-virtual call and function addresses it finds.
CallHook::CallMap callMap{};
// Get all calls to a function at a particular address/offset from image base.
// The address does not have to match the beginning of the function, it can be inside the function too.
// (This is useful for pattern matching instructions inside some function, ignoring the prologue.)
// IBO_GET_SPEFFECTPARAM_FN is defined in static.h and represents the offset of this function from image base.
auto calls = callMap.getCalls(IBO_GET_SPEFFECTPARAM_FN);
// Hook a function (by hooking the calls returned by CallMap::getCalls).
// You can also use a vector of void pointers to function addresses you get yourself.
// The template argument represents the type of hook you want to place (more templates are in HookTemplates.h).
auto hook1 = CallHook::hookFunction<ReturnHook>(calls, spEffectParamHook);