libradhook
A minimal, cross-platform inline function hooking library for x86-64 and AArch64, targeting Windows and Android, built as a lightweight alternative to libraries like MinHook and Dobby with a flat, C-style, handle-based API.
Features
- Inline hooking on x86-64 and AArch64
- Vtable hooking via direct slot swapping, no trampoline required
- Windows and Android support
- Flat C API with opaque handles
- Typed convenience overload of
RadHookCreatefor function pointers - Trampolines to call the original function from within a detour
- Enable / disable / destroy individual hooks, or all hooks at once
- Deferred enable/disable via a queue, applied atomically in one pass
- Hook enumeration and introspection (target, detour, trampoline, enabled state)
- Human-readable status strings
- Runtime version query for ABI sanity checks
- Builds as a static or shared library via CMake
Requirements
- CMake 3.21+
- A C++20 compiler (msvc, gcc, or clang)
- Android NDK (only if targeting android)
Building
CMake
cmake -S . -B build
cmake --build build
Useful options:
| Option | Default | Description |
|---|---|---|
BUILD_SHARED_LIBS |
ON |
Build radhook as a shared library instead of static |
RADHOOK_BUILD_TESTS |
ON when top-level |
Build the test suite |
Build.ps1
A helper PowerShell script is provided for common build targets:
./Build.ps1 -Target msvc -Config Release # msvc, release, shared lib
./Build.ps1 -Target gcc -Config Release # mingw/gcc via ninja
./Build.ps1 -Target android -Config Release # android
./Build.ps1 -Target msvc -Static -RunTests # static build with tests
Usage
Inline hooking
#include <radhook/radhook.h>
int TargetFunction() { return 42; }
int DetourFunction() { return 1337; }
int main() {
RadHookHandle handle = nullptr;
// create and install the hook
RadHookResult result = RadHookCreate(TargetFunction, (void*)DetourFunction, &handle);
if (result != RadHookResult::Success) {
// handle error, RadHookResultToString(result) for a message
}
// call through the trampoline
using Fn = int(*)();
Fn original = RadHookGetOriginalAs<Fn>(handle);
original();
RadHookDisable(handle);
RadHookEnable(handle);
RadHookDestroy(handle);
return 0;
}
Vtable hooking
struct IFoo {
virtual int Value() { return 7; }
virtual ~IFoo() = default;
};
int Detour(IFoo* self) { return 777; }
IFoo obj;
void** vtable = *reinterpret_cast<void***>(&obj);
RadHookHandle handle = nullptr;
RadHookVTableCreate(vtable, /*index=*/0, (void*)Detour, &handle);
// no relocation is needed for vtable hooks
using Fn = int(*)(IFoo*);
Fn original = RadHookGetOriginalAs<Fn>(handle);
original(&obj);
RadHookDestroy(handle);
Deferred apply
Stage several enable/disable transitions and flip them together in one pass, instead of one hook at a time:
RadHookQueueEnable(handleA);
RadHookQueueDisable(handleB);
// nothing has changed yet
Foo();
// then apply
RadHookApplyQueued(); // both transitions land together
If a handle is queued more than once before RadHookApplyQueued runs, the
most recent call wins.
API overview
| Function Name | Description |
|---|---|
RadHookCreate |
Create and install a hook, enabled by default |
RadHookVTableCreate |
Create and install a hook on a vtable slot |
RadHookEnable |
Enable an installed hook |
RadHookDisable |
Disable an installed hook, restoring original bytes |
RadHookDestroy |
Disable, release, and invalidate a hook |
RadHookEnableAll / RadHookDisableAll |
Enable or disable every registered hook |
RadHookQueueEnable / RadHookQueueDisable |
Stage a hook's enable/disable state without applying it |
RadHookApplyQueued |
Apply every staged enable/disable transition at once |
RadHookGetCount |
Number of currently registered hooks |
RadHookGetHandles / RadHookEnumerate |
Enumerate registered hook handles |
RadHookIsValid |
Check whether a handle refers to a registered hook |
RadHookIsEnabled |
Check whether a hook is currently enabled |
RadHookGetTarget |
Get the hooked target function pointer |
RadHookGetDetour |
Get the detour function pointer |
RadHookGetOriginal / RadHookGetOriginalAs<T> |
Get the trampoline to call the original implementation |
RadHookResultToString |
Human-readable name for a RadHookResult |
RadHookGetVersion |
Query the library's major/minor/patch version |
All fallible operations return a RadHookResult, including granular error
codes such as TrampolineTooFar, DisassemblyFailed, and
MemoryProtectFailed.
Testing
Tests are built with CTest and cover creation, enable/disable and lifecycle transitions, trampoline behavior, vtable hooking, queued apply, invalid input handling, failure cases, memory integrity, and stress/churn scenarios.
cmake -S . -B build -DRADHOOK_BUILD_TESTS=ON
cmake --build build
ctest --test-dir build --output-on-failure
License
Licensed under the GNU Lesser General Public License v3 (LGPLv3). See LICENSE for details.