- Implement complete cross-process injection engine via snd_inj_ctx_t - Track explicit remote_entry_point for safe thread hijacking and PE execution - Refactor standalone syscall resolvers into a unified pipeline (scan/sort) - Perfect status code system by purging generic fallbacks in favor of highly-specific, domain-aware error codes - Fix minor pointer validation and parsing bugs in the reflective loader
3.4 KiB
Module Techniques
The modules subdomain defines how SindriKit loads DLLs and resolves exported functions in the local process. Reflective loaders and PE import fixups call ctx->mod_api rather than invoking LoadLibraryA or GetProcAddress directly.
Paradigm 1: Win32 Resolution (snd_mod_win)
The snd_mod_win implementation uses standard Win32 APIs:
- Load:
LoadLibraryExAwithLOAD_LIBRARY_SEARCH_DEFAULT_DIRS(andLOAD_LIBRARY_SEARCH_DLL_LOAD_DIRwhen available), falling back toLoadLibraryA - Export lookup:
GetProcAddress - Module base:
GetModuleHandleW
Only the three string-based callbacks are populated. Hash-based fields (get_proc_address_hash, get_module_base_hash) are NULL — callers must not invoke them on this backend.
OpSec Implications
Every operation routes through monitored userland APIs and may leave plaintext DLL or function name strings in the binary. Suitable for diagnostics and standard profiles only.
Paradigm 2: NT / PEB Resolution (snd_mod_nt)
The snd_mod_nt implementation avoids kernel32.dll import resolution APIs:
- Load: Checks the PEB module list first via
snd_peb_get_module_base; if not loaded, resolves and callsLdrLoadDllfromntdll.dllvia hash-based EAT parsing - Export lookup (string):
snd_pe_get_export_addresswith a PEB-walking module resolver callback - Export lookup (hash):
snd_pe_get_export_address_hash - Module base (string):
snd_peb_get_module_base(fromsindri/parsers/env/peb.h) - Module base (hash):
snd_peb_get_module_base_hash
All five function pointer slots in snd_module_api_t are populated on this backend.
OpSec Implications
Bypasses GetProcAddress, GetModuleHandle, and LoadLibrary telemetry. Hash-based resolution eliminates plaintext API and module name strings from the binary. Calls to LdrLoadDll and resolved NT exports still pass through in-process ntdll.dll stubs where inline hooks may exist.
This is the standard choice for stealth loader profiles (snd_mod_nt paired with snd_mem_sys).
PEB Walking (parsers/env)
Low-level PEB traversal lives in the parsers domain, not in the modules primitive itself:
| Function | Header | Purpose |
|---|---|---|
snd_peb_get_module_base |
sindri/parsers/env/peb.h |
Locate a loaded module by wide name |
snd_peb_get_module_base_hash |
sindri/parsers/env/peb.h |
Locate a loaded module by compile-time hash |
snd_pe_get_export_address |
sindri/parsers/pe/exports.h |
Resolve an export by name (forwarders use snd_module_resolver_cb) |
snd_pe_get_export_address_hash |
sindri/parsers/pe/exports.h |
Resolve an export by compile-time hash (same forwarder resolver) |
The snd_mod_nt backend wires these parsers into the snd_module_api_t contract so higher-level domains (loaders, import fixups) remain agnostic to the resolution mechanism.
No Syscall Backend
Module resolution does not have a _sys variant. Loading and EAT parsing are inherently userland operations; the evasion benefit comes from PEB walking and hash-based parsing rather than direct syscalls.
Mixing Backends
A common pattern injects snd_mod_nt for import resolution while using snd_mem_sys for memory operations:
snd_ldr_pe_ctx_t ctx = {0};
ctx.mem_api = &snd_mem_sys;
ctx.mod_api = &snd_mod_nt;
Import fixup (snd_pe_resolve_imports) requires a fully populated module API with working load_library and get_proc_address (or hash equivalents).