NtSignalAndWaitForSingleObject
Signalisiert atomar ein Dispatcher-Objekt und wartet auf ein anderes in einem einzigen, race-freien Übergang.
Prototyp
NTSTATUS NtSignalAndWaitForSingleObject( HANDLE SignalHandle, HANDLE WaitHandle, BOOLEAN Alertable, PLARGE_INTEGER Timeout );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| SignalHandle | HANDLE | in | Handle auf das zu signalisierende Objekt (Event mit SET-Zugriff, vom Aufrufer gehaltenes Mutant oder Semaphore mit MODIFY_STATE). Muss einer der signalisierbaren Dispatcher-Typen sein. |
| WaitHandle | HANDLE | in | Handle auf das zu wartende Objekt (jedes wartbare Dispatcher-Objekt — Event, Mutant, Semaphore, Prozess, Thread, Timer, ...). Muss SYNCHRONIZE gewähren. |
| Alertable | BOOLEAN | in | Wenn TRUE, kann das Warten durch User-Mode-APCs (NtQueueApcThread) unterbrochen werden und gibt STATUS_USER_APC zurück. |
| Timeout | PLARGE_INTEGER | in | Optionales Timeout: NULL = unendlich, negativ = relative 100-ns-Einheiten, positiv = absolute Zeit. STATUS_TIMEOUT (0x102) zeigt Ablauf an. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x19A | win10-1507 |
| Win10 1607 | 0x1A3 | win10-1607 |
| Win10 1703 | 0x1A9 | win10-1703 |
| Win10 1709 | 0x1AC | win10-1709 |
| Win10 1803 | 0x1AE | win10-1803 |
| Win10 1809 | 0x1AF | win10-1809 |
| Win10 1903 | 0x1B0 | win10-1903 |
| Win10 1909 | 0x1B0 | win10-1909 |
| Win10 2004 | 0x1B6 | win10-2004 |
| Win10 20H2 | 0x1B6 | win10-20h2 |
| Win10 21H1 | 0x1B6 | win10-21h1 |
| Win10 21H2 | 0x1B8 | win10-21h2 |
| Win10 22H2 | 0x1B8 | win10-22h2 |
| Win11 21H2 | 0x1C1 | win11-21h2 |
| Win11 22H2 | 0x1C5 | win11-22h2 |
| Win11 23H2 | 0x1C5 | win11-23h2 |
| Win11 24H2 | 0x1C8 | win11-24h2 |
| Server 2016 | 0x1A3 | winserver-2016 |
| Server 2019 | 0x1AF | winserver-2019 |
| Server 2022 | 0x1BE | winserver-2022 |
| Server 2025 | 0x1C8 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 C8 01 00 00 mov eax, 0x1C8 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 Kernel-Implementierung hinter `SignalObjectAndWait`. Die definierende Eigenschaft ist die *Atomarität*: Signal und Wait erfolgen als ein einziger Thread-Übergang, ohne Instruktionsfenster dazwischen, in dem ein anderer Thread das Signal beobachten und ein Rendezvous machen könnte, bevor dieser Thread auf seinem Wartobjekt geparkt hat. Vergleiche das naive Paar `SetEvent(a); WaitForSingleObject(b, ...)` — zwei getrennte Syscalls mit einer in User-Land sichtbaren Lücke. Die SSN driftet bei jedem Major-Release (`0x1B0` 1903, `0x1B6` 2004, `0x1C8` 24H2 / Server 2025). Das Alertable-Flag macht Ekko-artige Sleep-Masks erst praktikabel: das Wait kann durch einen APC beendet werden, die Signal-dann-Wait-Sequenz bleibt aber atomar.
Häufige Malware-Nutzung
Der Eckpfeiler race-freier **Sleep-Mask**-Designs. Das Ekko-/Foliage-Muster verwendet drei Timer-Queue-Stufen, deren Handoff durch `NtSignalAndWaitForSingleObject` choreografiert wird — an jeder Phasengrenze signalisiert der Beacon-Thread atomar 'Phase N erledigt, ich parke' und wartet auf das nächste Event, das die ROP-Gadget-Kette feuert. Ohne Atomarität kann ein Memory-Scanner, der das 'ich bin verschlüsselt'-Flag beobachtet und um den Speicherlesen rennt, bevor das Wait registriert ist, ein Sub-Mikrosekunden-Race-Fenster gewinnen — Atomarität kollabiert dieses Fenster auf Null. Dieselbe Primitive wird in der klassischen Windows-Synchronisation eingesetzt (das `_endthreadex` der C-Runtime nutzt sie, um das Thread-Ende-Event zu signalisieren und sich selbst zu beenden, dabei atomar den Per-Thread-Runtime-Zustand freizugeben). Manche Red-Team-C2-Kanäle nutzen es auch für atomares Dequeue-Handoff in lock-freien Worker-Pools.
Erkennungsmöglichkeiten
Für sich allein ist NtSignalAndWaitForSingleObject Standard — Win32-Threadpools (`PTP_WORK`), C-Runtime und viele interne .NET-Primitive ketten darüber. Es ist *kein* nützliches Primärsignal. Das interessante Muster ist die Korrelation mit der Ekko-Sleep-Mask-Telemetriekette: Timer-Queue-Erzeugung (`NtCreateTimer`), drei oder mehr direkt hintereinander erzeugte Events, eine via `NtLockVirtualMemory` gesperrte RWX-Region und dann ein `NtSignalAndWaitForSingleObject`-Muster mit Timeouts im Sekunden-bis-Minuten-Bereich. Memory-Scanner, die Working-Set-Bytes zu zufälligen Zeitpunkten snapshotten — statt auf Syscalls zu reagieren — umgehen diese ganze Evasion-Klasse, weil sie nicht auf das Beobachten des atomaren Handoffs angewiesen sind.
Direkte Syscall-Beispiele
asmx64 stub (Win11 24H2 SSN 0x1C8)
; Direct syscall stub for NtSignalAndWaitForSingleObject
NtSignalAndWaitForSingleObject PROC
mov r10, rcx ; syscall convention
mov eax, 1C8h ; SSN (Win11 24H2 / Server 2025)
syscall
ret
NtSignalAndWaitForSingleObject ENDPcEkko-style atomic sleep handoff
// Phase handoff in an Ekko sleep mask: atomically signal "encrypted,
// parked" and wait for the wake event. No userland gap for a scanner
// to race into.
#include <windows.h>
typedef NTSTATUS (NTAPI *pNtSignalAndWaitForSingleObject)(
HANDLE Signal, HANDLE Wait, BOOLEAN Alertable, PLARGE_INTEGER Timeout);
NTSTATUS SleepHandoff(HANDLE h_encrypted_done, HANDLE h_wake) {
pNtSignalAndWaitForSingleObject NtSignalAndWaitForSingleObject =
(pNtSignalAndWaitForSingleObject)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtSignalAndWaitForSingleObject");
// Wait alertable so a timer APC can wake us; infinite timeout.
return NtSignalAndWaitForSingleObject(
h_encrypted_done, h_wake, TRUE, NULL);
}rustRendezvous primitive (race-free)
// SignalObjectAndWait wraps NtSignalAndWaitForSingleObject.
// Use it to atomically release a producer event and park on the consumer
// event in a lock-free queue worker.
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::Threading::SignalObjectAndWait;
pub unsafe fn handoff(signal: HANDLE, wait: HANDLE, ms: u32) -> u32 {
SignalObjectAndWait(signal, wait, ms, /* alertable */ 0)
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20