NtWaitForDebugEvent
Wartet auf das nächste Debug-Event eines Debug-Objects und liefert einen DBGUI_WAIT_STATE_CHANGE zurück.
Prototyp
NTSTATUS NtWaitForDebugEvent( HANDLE DebugObjectHandle, BOOLEAN Alertable, PLARGE_INTEGER Timeout, PDBGUI_WAIT_STATE_CHANGE StateChange );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| DebugObjectHandle | HANDLE | in | Handle auf ein Debug-Object aus NtCreateDebugObject (oder DebugActiveProcess). |
| Alertable | BOOLEAN | in | Bei TRUE kann der Wait durch User-Mode-APCs unterbrochen werden (liefert STATUS_USER_APC). |
| Timeout | PLARGE_INTEGER | in | Optionaler Timeout (100-ns-Einheiten, negativ = relativ). NULL wartet unbegrenzt. |
| StateChange | PDBGUI_WAIT_STATE_CHANGE | out | Empfängt das Event: NewState (CREATE_PROCESS/EXCEPTION/etc.), AppClientId und ereignisspezifische Payload-Union. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x1B4 | win10-1507 |
| Win10 1607 | 0x1BD | win10-1607 |
| Win10 1703 | 0x1C3 | win10-1703 |
| Win10 1709 | 0x1C7 | win10-1709 |
| Win10 1803 | 0x1C9 | win10-1803 |
| Win10 1809 | 0x1CA | win10-1809 |
| Win10 1903 | 0x1CB | win10-1903 |
| Win10 1909 | 0x1CB | win10-1909 |
| Win10 2004 | 0x1D1 | win10-2004 |
| Win10 20H2 | 0x1D1 | win10-20h2 |
| Win10 21H1 | 0x1D1 | win10-21h1 |
| Win10 21H2 | 0x1D3 | win10-21h2 |
| Win10 22H2 | 0x1D3 | win10-22h2 |
| Win11 21H2 | 0x1DD | win11-21h2 |
| Win11 22H2 | 0x1E1 | win11-22h2 |
| Win11 23H2 | 0x1E1 | win11-23h2 |
| Win11 24H2 | 0x1E4 | win11-24h2 |
| Server 2016 | 0x1BD | winserver-2016 |
| Server 2019 | 0x1CA | winserver-2019 |
| Server 2022 | 0x1D9 | winserver-2022 |
| Server 2025 | 0x1E4 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 E4 01 00 00 mov eax, 0x1E4 ; Win11 24H2 SSN F6 04 25 08 03 FE 7F 01 test byte ptr [0x7FFE0308], 1 75 03 jne short +3 0F 05 syscall C3 ret CD 2E int 2Eh C3 ret
Undokumentierte Hinweise
Die kernelseitige Primitive unter `WaitForDebugEventEx`. Ein Debugger hängt sich an, indem er ein Debug-Object mit `NtCreateDebugObject` erstellt (oder öffnet) und es per `NtDebugActiveProcess` an einen Zielprozess bindet, dann in `NtWaitForDebugEvent` schleift, um die kanonischen State-Changes zu erhalten: `DbgCreateProcessStateChange`, `DbgCreateThreadStateChange`, `DbgLoadDllStateChange`, `DbgExceptionStateChange` (das wichtigste — Breakpoint, Single-Step, Access Violation), `DbgExitThreadStateChange`, `DbgExitProcessStateChange`, `DbgUnloadDllStateChange`. Jedes Event muss mit `NtDebugContinue` quittiert werden. Die `DBGUI_WAIT_STATE_CHANGE`-Union trägt ereignisspezifische Daten — ein CONTEXT bei Exceptions, Image-Base/Name bei Modul-Loads, Exit-Codes usw.
Häufige Malware-Nutzung
Zwei klare offensive Muster. (1) **Self-Debug-Anti-Attach**: Der Prozess erstellt ein Debug-Object, hängt es an sich selbst (oder an ein Kind, das er spawnt), und schleift `NtWaitForDebugEvent` in einem dedizierten Worker-Thread. Da Windows nur einen Debugger pro Prozess erlaubt, kann sich ein externer `windbg`/`x64dbg` nicht mehr anhängen — sein `DebugActiveProcess` schlägt mit `STATUS_PORT_ALREADY_SET` fehl. Packer wie Themida und einige Crypter liefern Varianten davon. (2) **Headless-Debugger**, um ein Kind ohne sichtbare UI zu instrumentieren — nützlich für Malware, die entpackten Code steppen oder auf spezifische Exceptions in einem anderen Binary reagieren muss. Umgekehrt nutzen Anti-Anti-Debug-Tools (TitanHide, ScyllaHide) dieselben Primitive, um die Debug-Loop am Leben zu halten und gleichzeitig die für den Analysten sichtbaren Events zu filtern.
Erkennungsmöglichkeiten
`NtCreateDebugObject` kurz gefolgt von `NtDebugActiveProcess(self_oder_child)` und ein in `NtWaitForDebugEvent` geparkter Thread ist der kanonische Fingerabdruck. ETW Microsoft-Windows-Kernel-Process emittiert Process-Debug-Port-Set-Events. Wird das `EPROCESS.DebugPort`-Feld bei einem Nicht-Entwickler-Tool-Prozess Non-NULL, ist das sehr signalstark — Kernel-Callbacks via `PsSetCreateProcessNotifyRoutineEx2` und Protected-Process-Schutz des EDR erfassen das. Aus User-Mode enthüllt `NtQueryInformationProcess` mit `ProcessDebugPort`/`ProcessDebugObjectHandle` den Anschluss, weshalb Packer genau diesen Pfad per Hook-Detour einfrieren.
Direkte Syscall-Beispiele
cSelf-debug anti-attach worker thread
// Run in a dedicated thread spawned at startup. After this returns,
// no external debugger can attach until the debug object handle is closed.
DWORD WINAPI SelfDebugLoop(LPVOID arg) {
HANDLE hDbg = NULL;
OBJECT_ATTRIBUTES oa = { sizeof(oa) };
NTSTATUS st = NtCreateDebugObject(&hDbg, DEBUG_ALL_ACCESS, &oa, 0);
if (!NT_SUCCESS(st)) return st;
// Attach to ourselves — blocks future DebugActiveProcess() from any other tool.
st = NtDebugActiveProcess(NtCurrentProcess(), hDbg);
if (!NT_SUCCESS(st)) return st;
DBGUI_WAIT_STATE_CHANGE sc;
for (;;) {
st = NtWaitForDebugEvent(hDbg, FALSE, NULL, &sc);
if (!NT_SUCCESS(st)) break;
// Pass everything through so the debuggee keeps running.
NtDebugContinue(hDbg, &sc.AppClientId, DBG_CONTINUE);
}
return 0;
}asmx64 direct stub
; NtWaitForDebugEvent direct syscall (Win11 24H2 SSN 0x1E4)
NtWaitForDebugEvent PROC
mov r10, rcx
mov eax, 1E4h
syscall
ret
NtWaitForDebugEvent ENDPrustMinimal headless debug loop
// Cargo: windows-sys = "0.59"
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::LibraryLoader::*;
type WaitFn = unsafe extern "system" fn(HANDLE, u8, *const i64, *mut [u8; 0xD8]) -> i32;
type ContFn = unsafe extern "system" fn(HANDLE, *const [usize; 2], i32) -> i32;
unsafe fn drain(dbg: HANDLE) {
let nt = GetModuleHandleA(b"ntdll.dll\0".as_ptr());
let wait: WaitFn = std::mem::transmute(
GetProcAddress(nt, b"NtWaitForDebugEvent\0".as_ptr()).unwrap());
let cont: ContFn = std::mem::transmute(
GetProcAddress(nt, b"NtDebugContinue\0".as_ptr()).unwrap());
let mut sc = [0u8; 0xD8];
loop {
if wait(dbg, 0, std::ptr::null(), &mut sc) < 0 { break; }
let cid_ptr = sc.as_ptr().add(8) as *const [usize; 2]; // AppClientId
// 0x00010002 = DBG_CONTINUE
cont(dbg, cid_ptr, 0x00010002);
}
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20