Table of Contents

Hooks and backends

Backend selection

IHookBackend separates hook orchestration from the engine that owns a function-entry patch.

Backend Architectures Notes
NativeDetourBackend X64 Managed relocation, several jump schemes, chaining, safe Windows and Linux X64 patching
X86DetourBackend X86 Managed X86 relocation and safe Windows patching
PolyHook2Backend X64/native dependency One physical hook per target with managed logical layers
MinHookBackend Windows X86/X64 Adapter over MinHook.NET; upstream relocation limitations still apply

Register a process-wide default or set a backend on transaction options:

HookBackends.Default = NativeDetourBackend.Instance;

Transactions

Transactions prepare hooks before installation, share pattern scans, and apply a consistent failure policy.

Targets can be absolute addresses, RVAs, exports, byte patterns, or the destination of a direct relative call identified by a byte pattern.

using ScanRegion region = ScanRegion.FromModule(moduleHandle);
using var transaction = new HookTransaction(
    region,
    options: new HookTransactionOptions
    {
        Backend = NativeDetourBackend.Instance,
        FailureMode = TransactionFailureMode.RollbackAndThrow,
    });

var update = new DetourHandle<UpdateDelegate>();
transaction.AddDetour(
    update,
    HookTarget.FromPattern("48 89 5C 24 ?? 57 48 83 EC ??"),
    OnUpdate);

CommitResult result = transaction.Commit();

The handle exposes Original after commit. Disposal restores installed hooks newest-first.

When a caller does not need a long-lived field, AddDetour and AddContextHook also have overloads that allocate and return a fresh handle. The transaction still owns the hook, so the return value can be ignored when no individual control or status inspection is needed.

For lower-level scans, DataScanner.CurrentAddress is the resolved virtual address and DataScanner.CurrentOffset is its signed byte offset from ScanRegion.BaseAddress.

Relative-call targets

Some functions have no stable entry-point signature, while one of their callers does. Use FromRelativeCall to scan for that call site and detour the function reached by its direct relative CALL:

transaction.AddDetour(
    createTribeHook,
    HookTarget.FromRelativeCall(
        "E8 ?? ?? ?? ?? 48 63 F8 89 7C 24 ?? 85 C0"),
    OnCreateTribe);

If the signature begins before the call instruction, provide its signed byte offset:

HookTarget target = HookTarget.FromRelativeCall(pattern, callOffset: 12);

This mode accepts only decoded direct relative calls. An indirect call fails during the resolve phase with a diagnostic identifying the matched instruction. Ordinary FromPattern targets keep their existing behavior and resolve to the matched address plus their optional offset.

Persistent AOB cache

Set HookTransactionOptions.AobCache to reuse AOB matches across process runs:

using RedBird.Core.Memory.Scanners;

string cacheDirectory = "MyCacheDir";

AobCacheOptions aobCache = new(Path.Combine(cacheDirectory, "game-module.aobcache.json"));

var options = new HookTransactionOptions
{
    Backend = NativeDetourBackend.Instance,
    AobCache = aobCache,
};

DataScanner scanner = DataScanner.Create(
    region,
    logger: null,
    aobCache: aobCache);

RedBird creates the parent directory and writes a compact JSON document containing AOB-to-RVA maps. A complete cache hit performs no pattern scan. When only some entries are present, the transaction scans just those misses and atomically rewrites the cache. Stored RVAs avoid carrying absolute addresses between processes.

Both the x64 and x86 DataScanner support the same cache through their three-argument Create overload. Scan caches its first match. ScanAll caches a complete RVA array only when enumeration finishes before its limit; a truncated set is never mistaken for a complete result. One scanner instance loads the document once for all of its fluent calls. Cached overloads of MultiPatternScanner.FindFirst and FindAll are available to core scanner callers as well.

Use a separate path for each module or scan region. Cached entries are used without validating the region, pattern bytes, or RVA, so delete the cache when the target binary or signatures change. Malformed files and I/O failures fall back to the normal scanner and do not fail the transaction. The default store uses System.Text.Json with a plain AobCacheDocument. To use MessagePack or another format, implement IAobCacheStore and assign it to AobCacheOptions.Store.

Export targets and handles

On Windows, the module argument to HookTarget.FromExport is an HMODULE (the module base address returned by the loader). A transaction uses its scan region's module when the argument is omitted.

private readonly DetourHandle<LoadMapDelegate> loadMapHook = new();

transaction.AddDetour(
    loadMapHook,
    HookTarget.FromExport("DLL_LoadMapToPlay", moduleHandle),
    OnLoadMap);

The explicit-handle overload requires an initialized handle. Use new DetourHandle<TDelegate>(), as above, or use the overload beginning with HookTarget and retain its returned handle.

Chaining

The native engines layer patches and require reverse-order removal by default.