
Shellcode-Injection mithilfe der Windows-Debugging-API
DbgNexum ist ein Proof-of-Concept zum Injizieren von Shellcode mithilfe der Windows-Debugging-API und gemeinsam genutztem Speicher (Dateizuordnung). Es vermeidet das direkte Schreiben und Lesen von Remote-Speicher und nutzt stattdessen Kontextmanipulation, um den Zielprozess zu zwingen, die Payload selbst zu laden und auszuführen.
Der Injizierer hängt an einen Zielprozess an und erstellt einen angehaltenen Thread. Über eine Debug-Schleife setzt er einen Hardware-Breakpoint, um die Ausführung an einer bestimmten Rücksprungadresse abzufangen. Bei jedem Trap verändert der Injizierer die CPU-Register, um Funktionsaufrufe nachzuahmen und so eine Abfolge von Windows-API-Funktionsaufrufen im Zielprozess zu orchestrieren.
Zum Zeitpunkt der Erstellung dieser README habe ich die Technik gegen MDE und Elastic getestet; keiner der beiden hat sie erkannt.
WriteProcessMemory / VirtualAllocEx: Die Payload wird über CreateFileMapping und MapViewOfFile übertragen.ReadProcessMemory: Der Ansatz bezieht alle wichtigen Informationen aus dem Thread-Kontext.Der PoC verwendet einen XOR-verschlüsselten msfvenom-Shellcode, der "calc.exe" startet. Bitte verwende aber deinen eigenen Shellcode!
shellcode.h ein.DbgNexum.exe <PID>
Beispielausgabe:
[i] Section 'MZ' created and shellcode copied
[+] Bait thread created. Setting HWBP on FileTimeToSystemTime
[i] Execution Redirected:
|-> [0] Preparation & anchoring stack
|-> [1] Setting HWBP & buffer alloc
|-> [2] Copying File-Mapping name
|-> [3] Zeroing stack slot
|-> [4] Opening handle to named file mapping
|-> [5] Mapping payload into mem. with exec. perm.
|-> [6] Cleanup & shellcode execution
[+] Successfully detached from process 19256
[i] Orchestration complete.
Der Ausführungsfluss ist ein ständiges Hin und Her zwischen der Debug-Schleife des Injizierers und dem Zielprozess.
Injektionsphasen
Die Funktion DebugLoop enthält die Hauptinjektionslogik und orchestriert die "Zustandsmaschine":
0. Vorbereitung:
ret-Aufruf.1. Speicherzuweisung:
LocalAlloc, um (natürlich) einen kleinen Puffer zu reservieren.2. Datenvorbereitung:
memcpy, um die Zeichenkette MZ in den zuvor reservierten Puffer zu kopieren.3. Stack-Vorbereitung:
memset, um einen Stack-Slot zu nullen. Dies dient der Vorbereitung von Phase 5, die MapViewOfFile aufrufen wird. Da die Funktion mehr als 4 Argumente verwendet, wird das 5. Argument über den Stack übergeben (das wir hier setzen).4. Mapping öffnen:
OpenFileMappingA durch den Thread, wobei der Name MZ verwendet wird, der in den Phasen 2 und 3 "erstellt" wurde.5. Payload mappen:
MapViewOfFile aufzurufen. Dadurch wird der gemeinsame Speicherabschnitt (der den Shellcode enthält) mit EXECUTE-Berechtigungen in den Adressraum des Ziels gemappt.6. Ausführung:
RIP auf die von MapViewOfFile zurückgegebene Adresse um.