- Implemented entropy-based pseudo-randomizer in snd_syscall_find_spoof_scan to prevent deterministic telemetry. - Randomized Fat Frame selection using the SSN hash, a static counter, and ASLR-dependent module/stack addresses. - Hardened spoof scanner to reject frames using Frame Registers (UWOP_SET_FPREG) to prevent RtlVirtualUnwind crashes. - Added strict bounds checking to cap Fat Frame sizes at 240 bytes, preventing MASM local stack corruption.Release v1.3.0: Stack Spoofing & Dynamic Fat Frame.
Syscall Primitives
Direct and indirect syscall invocation bypasses hooked ntdll.dll stubs by resolving System Service Numbers (SSNs) and executing the syscall instruction from custom ASM stubs — either inline (direct) or by jumping to a legitimate NTDLL gadget (indirect).
Header: include/sindri/primitives/syscalls.h
Implementation: src/primitives/execution/syscalls/
Warning
Bootstrap required: Call
snd_syscall_set_ntdll(), configure at least one strategy viasnd_syscall_set_resolver(), and configure the invoker viasnd_syscall_set_invoker()beforesnd_syscall_resolve(). For indirect invocation, also set a gadget finder viasnd_syscall_set_gadget_finder(). WithSND_USE_DEFAULTS=ON, onlysnd_syscall_set_ntdll()is required — the resolver, invoker, and gadget finder are pre-configured at compile time._sysmemory/process/mapping backends depend on this pipeline.
How _sys primitives use syscalls
snd_mem_sys / snd_proc_sys / snd_map_sys
│
▼
snd_syscall_resolve(func_hash) <- cascading strategy chain
│
▼
(gadget finder, if indirect) <- locates syscall/sysenter gadget in NTDLL
│
▼
g_syscall_invoker(&args) <- dispatches to configured ASM stub
│
┌───┴───────────────────┐
▼ ▼
snd_syscall_direct_ snd_syscall_indirect_
invoke_asm invoke_asm
Function hashes come from sindri_hashes.h (build-generated, e.g. SND_HASH_NTALLOCATEVIRTUALMEMORY).
Resolution strategies
| Function | Source | Approach |
|---|---|---|
snd_syscall_resolve_ssn_scan |
syscalls_scan.c |
Read SSN from stub bytes; neighbor scan if hooked |
snd_syscall_resolve_ssn_sort |
syscalls_sort.c |
Build sorted Zw* export table; index = SSN |
Typical registration:
snd_syscall_set_resolver(snd_syscall_resolve_ssn_scan);
snd_syscall_add_resolver(snd_syscall_resolve_ssn_sort);
snd_syscall_set_invoker(snd_syscall_direct_invoke_asm);
// or for indirect:
// snd_syscall_set_invoker(snd_syscall_indirect_invoke_asm);
// snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan);
Custom resolvers matching snd_syscall_resolver_t can be added to the chain (max 4 strategies).
Invocation modes
| Function | File | Description |
|---|---|---|
snd_syscall_direct_invoke_asm |
invoke_direct_x64.asm / invoke_direct_x86.asm |
Executes syscall/sysenter inline |
snd_syscall_indirect_invoke_asm |
invoke_indirect_x64.asm / invoke_indirect_x86.asm |
Jumps to a legitimate NTDLL gadget |
snd_syscall_spoofed_invoke_asm |
invoke_spoofed_x64.asm / invoke_spoofed_x86.asm |
Spoofs call stack with dynamic Fat Frame discovery |
Note
Build with
SND_USE_DEFAULTS=ONto pre-configure the invoker (snd_syscall_indirect_invoke_asm), gadget finder (snd_syscall_find_gadget_scan), and primary resolver (snd_syscall_resolve_ssn_scan) at compile time. Onlysnd_syscall_set_ntdll()is then needed at runtime.
Table of Contents
- pipeline.md — lifecycle from bootstrap to kernel transition
- engines.md — scan vs sort resolver internals
- api_reference.md — full public API
Related documentation
- Execution domain — shared
src/primitives/execution/layout - Mapping — KnownDlls bootstrap for clean
ntdll - Primitives domain