#include "sierra.h"
ULONG MyHook(SIERRA_HOOK_CTX* ctx, ...) {
// logic to execute when the hook triggers
return 0x0;
}
int main() {
SRSetHook(L"ntdll.dll", "NtQueryInformationProcess", MyHook, SR_FLAG_NONE);
}typedef struct _SIERRA_HOOK_CTX {
void* HookedFunc;
void* CleanProxy;
const void* ModuleBase;
} SIERRA_HOOK_CTX;HookedFuncpoints to the original (possibly patched) target.CleanProxyis a trampoline pointing to the clean version.ModuleBaseis the image base of the loaded module containing the function.
If you need to resolve and call APIs manually — without importing GetProcAddress or LoadLibrary — SIERRA exposes internal resolution routines:
void* SRGetModuleBase(const wchar_t* moduleName);
void* SRGetProcedureAddrForCaller(const void* base, const char* funcName, DWORD flags);void* ntdll = SRGetModuleBase(L"ntdll.dll");
void* func = SRGetProcedureAddrForCaller(ntdll, "NtClose", SR_FLAG_NONE);SRGetProcedureAddrForCaller will step over forwarders, avoid .edata, verify .text bounds, and optionally return a clean trampoline proxy if the original is hooked.
| Macro | Behavior |
|---|---|
| SR_FLAG_NONE | Default behavior |
| SR_FLAG_ENABLE_SEH | Raises SEH exception if hook is detected |
| SR_FLAG_DISABLE_TRAMPOLINE | Skips proxy generation entirely |
To restore the original state of a hooked function:
SRRestore(void* target, const BYTE* originalBytes, SIZE_T length);Ensure originalBytes contains a valid copy of the original prologue. This is returned during SRIntercept() internally.
Trampoline memory pages are reused. For long-running processes, clean up stale slots periodically:
SRProxyLRU(__rdtsc() - N);Where N is the number of clock ticks since last use. This removes trampolines older than that threshold.