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);