# 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 `RadHookCreate` for 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 ```sh 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: ```powershell ./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 ```cpp #include 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(handle); original(); RadHookDisable(handle); RadHookEnable(handle); RadHookDestroy(handle); return 0; } ``` ### Vtable hooking ```cpp struct IFoo { virtual int Value() { return 7; } virtual ~IFoo() = default; }; int Detour(IFoo* self) { return 777; } IFoo obj; void** vtable = *reinterpret_cast(&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(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: ```cpp 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` | 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. ```sh 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](LICENSE) for details.