NtCreateKeyedEvent
Erzeugt ein Keyed-Event-Objekt — leichtgewichtige Kernel-Sync-Primitive, die Waits und Wakes per Virtual-Address-Key paart.
Prototyp
NTSTATUS NtCreateKeyedEvent( PHANDLE KeyedEventHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG Flags );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| KeyedEventHandle | PHANDLE | out | Empfängt ein Handle auf das neue Keyed-Event-Objekt. |
| DesiredAccess | ACCESS_MASK | in | KEYEDEVENT_WAIT (1), KEYEDEVENT_WAKE (2) oder KEYEDEVENT_ALL_ACCESS. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | Optionaler Name (z. B. \KernelObjects\CritSecOutOfMemoryEvent) + Security-Descriptor. NULL für eine anonyme Instanz. |
| Flags | ULONG | in | Reserviert — muss 0 sein. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0xA4 | win10-1507 |
| Win10 1607 | 0xA6 | win10-1607 |
| Win10 1703 | 0xA9 | win10-1703 |
| Win10 1709 | 0xAA | win10-1709 |
| Win10 1803 | 0xAB | win10-1803 |
| Win10 1809 | 0xAB | win10-1809 |
| Win10 1903 | 0xAC | win10-1903 |
| Win10 1909 | 0xAC | win10-1909 |
| Win10 2004 | 0xB0 | win10-2004 |
| Win10 20H2 | 0xB0 | win10-20h2 |
| Win10 21H1 | 0xB0 | win10-21h1 |
| Win10 21H2 | 0xB1 | win10-21h2 |
| Win10 22H2 | 0xB1 | win10-22h2 |
| Win11 21H2 | 0xB4 | win11-21h2 |
| Win11 22H2 | 0xB5 | win11-22h2 |
| Win11 23H2 | 0xB5 | win11-23h2 |
| Win11 24H2 | 0xB7 | win11-24h2 |
| Server 2016 | 0xA6 | winserver-2016 |
| Server 2019 | 0xAB | winserver-2019 |
| Server 2022 | 0xB3 | winserver-2022 |
| Server 2025 | 0xB7 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 B7 00 00 00 mov eax, 0xB7 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
Keyed Events sind eine bewusst minimalistische Windows-Synchronisationsprimitive: Ein einzelnes Keyed Event kann beliebig viele Waiter bedienen, weil das Rendezvous über einen *Key* (jeder Wert in Zeigergröße, üblicherweise eine virtuelle Adresse) statt über das Event-Objekt selbst läuft. Der Kernel hält pro Event eine Hash-Tabelle, indiziert mit `(Process, Key)`; `NtWaitForKeyedEvent` blockiert, bis *irgendein anderer Thread* `NtReleaseKeyedEvent` mit demselben Key aufruft. In der Praxis nutzt fast jeder Prozess die einzige globale Instanz unter `\KernelObjects\CritSecOutOfMemoryEvent`, die `ntdll.dll` beim Start öffnet und in `peb->KeyedEventHandle` ablegt. Ein brandneues Keyed Event mit `NtCreateKeyedEvent` zu erzeugen ist außerhalb von Diagnose-Tools und einer Handvoll historischer XP-Ära-Allokatoren selten (das globale Event handhabt Contention seit Vista).
Häufige Malware-Nutzung
Schwaches Signal. Der Hauptreiz für offensive Nutzung ist *Namenlosigkeit*: Ein Keyed Event mit NULL `ObjectAttributes` erzeugt kein benanntes Kernel-Objekt, kein File-Handle, keine Netzspur, und ein Sleep-Mask oder Stage Gate auf Basis von `NtWaitForKeyedEvent` (gepaart mit `NtReleaseKeyedEvent` aus einem Peer-Thread) ist für die Enumeration benannter Events / Semaphoren / Mutexe, die manche Sandbox-Detektoren ausführen, unsichtbar. Einige Sleep-Obfuscation-Designs (Ekko-/Ekko-ähnliche Familie, manche Foliage- / TimerHandler-artige Designs) nutzen das globale Event `\KernelObjects\CritSecOutOfMemoryEvent` als Sync-Punkt, statt ein eigenes anzulegen — dieser Pfad ruft `NtCreateKeyedEvent` gar nicht auf. Direkte Aufrufe von `NtCreateKeyedEvent` aus Nicht-Microsoft-Code sind ungewöhnlich, aber nicht belastend.
Erkennungsmöglichkeiten
Als Syscall im Grunde nicht detektierbar. Es gibt kein Sysmon-Event für Keyed-Event-Erzeugung und keinen ETW-Provider, der es sauber zeigt. Forensische Enumeration via `!handle` in WinDbg oder SystemInformers Handle-Ansicht zeigt `KeyedEvent`-Objekte pro Prozess, aber das globale Event hält nahezu jeder Prozess — das einzig nützliche Artefakt ist ein *unbenanntes* `KeyedEvent`-Handle in einem Prozess, der nichts mit Low-Level-Critical-Section-Infrastruktur zu tun hat. Selbst dann sind False Positives durch in-process-Allokatoren und Instrumentierungs-Bibliotheken hoch.
Direkte Syscall-Beispiele
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtCreateKeyedEvent (SSN 0xB7 on Win11 24H2)
NtCreateKeyedEvent PROC
mov r10, rcx ; KeyedEventHandle
mov eax, 0B7h ; SSN — drifts per build
syscall
ret
NtCreateKeyedEvent ENDPcPrivate unnamed keyed event
// No public Win32 wrapper. Almost all callers use the global instance instead.
#include <windows.h>
#include <winternl.h>
#define KEYEDEVENT_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED | 0x3)
typedef NTSTATUS (NTAPI *pNtCreateKeyedEvent)(
PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG);
HANDLE MakePrivateKeyedEvent(void) {
HANDLE h = NULL;
pNtCreateKeyedEvent fn = (pNtCreateKeyedEvent)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtCreateKeyedEvent");
fn(&h, KEYEDEVENT_ALL_ACCESS, NULL /* unnamed */, 0);
return h;
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20