> Windows Syscalls
ntoskrnl.exeT1027.011T1027T1106

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

NameTypeDirDescription
SignalHandleHANDLEinHandle 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.
WaitHandleHANDLEinHandle auf das zu wartende Objekt (jedes wartbare Dispatcher-Objekt — Event, Mutant, Semaphore, Prozess, Thread, Timer, ...). Muss SYNCHRONIZE gewähren.
AlertableBOOLEANinWenn TRUE, kann das Warten durch User-Mode-APCs (NtQueueApcThread) unterbrochen werden und gibt STATUS_USER_APC zurück.
TimeoutPLARGE_INTEGERinOptionales Timeout: NULL = unendlich, negativ = relative 100-ns-Einheiten, positiv = absolute Zeit. STATUS_TIMEOUT (0x102) zeigt Ablauf an.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x19Awin10-1507
Win10 16070x1A3win10-1607
Win10 17030x1A9win10-1703
Win10 17090x1ACwin10-1709
Win10 18030x1AEwin10-1803
Win10 18090x1AFwin10-1809
Win10 19030x1B0win10-1903
Win10 19090x1B0win10-1909
Win10 20040x1B6win10-2004
Win10 20H20x1B6win10-20h2
Win10 21H10x1B6win10-21h1
Win10 21H20x1B8win10-21h2
Win10 22H20x1B8win10-22h2
Win11 21H20x1C1win11-21h2
Win11 22H20x1C5win11-22h2
Win11 23H20x1C5win11-23h2
Win11 24H20x1C8win11-24h2
Server 20160x1A3winserver-2016
Server 20190x1AFwinserver-2019
Server 20220x1BEwinserver-2022
Server 20250x1C8winserver-2025

Kernel-Modul

ntoskrnl.exeNtSignalAndWaitForSingleObject

Verwandte APIs

SignalObjectAndWaitNtWaitForSingleObjectNtWaitForMultipleObjectsNtSetEventNtCreateEvent

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.

Erkennungs­mö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 ENDP

cEkko-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