NtEnumerateKey
Enumeriert Subkeys eines Registrierungsschlüssels — durchläuft AutoRun, IFEO und Services zur Persistenz-Entdeckung.
Prototyp
NTSTATUS NtEnumerateKey( HANDLE KeyHandle, ULONG Index, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| KeyHandle | HANDLE | in | Handle, geöffnet mit KEY_ENUMERATE_SUB_KEYS (Teil von KEY_READ). |
| Index | ULONG | in | Null-basierter Index des Subkeys. Hochzählen bis STATUS_NO_MORE_ENTRIES. |
| KeyInformationClass | KEY_INFORMATION_CLASS | in | Layout: KeyBasicInformation=0, KeyNodeInformation=1, KeyFullInformation=2, KeyNameInformation=3. |
| KeyInformation | PVOID | out | Ausgabe-Puffer für die angeforderte KEY_*_INFORMATION-Struktur. |
| Length | ULONG | in | Größe von KeyInformation in Bytes. |
| ResultLength | PULONG | out | Erhält die benötigte Größe, falls der Puffer zu klein ist. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x32 | win10-1507 |
| Win10 1607 | 0x32 | win10-1607 |
| Win10 1703 | 0x32 | win10-1703 |
| Win10 1709 | 0x32 | win10-1709 |
| Win10 1803 | 0x32 | win10-1803 |
| Win10 1809 | 0x32 | win10-1809 |
| Win10 1903 | 0x32 | win10-1903 |
| Win10 1909 | 0x32 | win10-1909 |
| Win10 2004 | 0x32 | win10-2004 |
| Win10 20H2 | 0x32 | win10-20h2 |
| Win10 21H1 | 0x32 | win10-21h1 |
| Win10 21H2 | 0x32 | win10-21h2 |
| Win10 22H2 | 0x32 | win10-22h2 |
| Win11 21H2 | 0x32 | win11-21h2 |
| Win11 22H2 | 0x32 | win11-22h2 |
| Win11 23H2 | 0x32 | win11-23h2 |
| Win11 24H2 | 0x32 | win11-24h2 |
| Server 2016 | 0x32 | winserver-2016 |
| Server 2019 | 0x32 | winserver-2019 |
| Server 2022 | 0x32 | winserver-2022 |
| Server 2025 | 0x32 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 32 00 00 00 mov eax, 0x32 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
Index-basierte Enumeration: der Aufrufer iteriert ab `Index=0`, bis er `STATUS_NO_MORE_ENTRIES` erhält. Hinzufügen oder Löschen eines Subkeys während der Enumeration verschiebt Indizes — robuste Caller wiederholen den fehlgeschlagenen Index oder snapshotten Namen vor der Verarbeitung. SSN `0x32` ist seit Windows 7 stabil. `NtEnumerateKey` liefert nur Subkeys — für Werte braucht es `NtEnumerateValueKey` (Schwester-Syscall mit gleicher Form).
Häufige Malware-Nutzung
**Persistenz-Entdeckung (T1518 + T1012 + T1547.x)** — Red und Blue nutzen dieselbe Primitive an gegenüberliegenden Enden der Kill Chain. Der interessante offensive Einsatz ist *Autorun-Enumeration vor dem Plant*: zuerst jeden Standard-Autostart-Key durchgehen, um einen ungenutzten Namen zu finden (Kollisionsvermeidung) oder vorhandene Einträge zu entdecken, die das Implant hijacken kann (Binary-Pfad eines installierten, selten laufenden Scheduled-Task-Launchers ersetzen statt einen neuen Eintrag zu schaffen, der als einziger fehlt in Baselines). Kanonische Pfade: `\Registry\Machine\Software\Microsoft\Windows\CurrentVersion\Run`, `RunOnce`, `Explorer\Shell Folders`, `Image File Execution Options` (dessen Subkeys *Executable-Namen* sind — sie zu enumerieren findet IFEO-Debugger-Hijack-Gelegenheiten), `App Paths`, `\Services` (Type=1 Driver-Services, Type=16 Own-Process-Services), `\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify`. Stealer enumerieren auch `\Software\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles`, um Wi-Fi-Profil-GUIDs zu entdecken, bevor sie PSKs über `wlanapi` extrahieren.
Erkennungsmöglichkeiten
Das `NtEnumerateKey`-Volumen ist auf einem normalen Host gigantisch (GPO-Refresh, Defender-ASR-Engine, jede Explorer-Namespace-Expansion). Kein nützliches Primärsignal. Zwei abgeleitete Signale funktionieren: (1) **Enumeration eines sensiblen Keys durch Nicht-Baseline-Prozess** — Alarm, wenn ein Prozess, dessen Image-Hash zuvor nie `\Microsoft\Windows NT\CurrentVersion\Image File Execution Options` enumeriert hat, dies tut; (2) **Enumeration gefolgt von SetValueKey** am selben Parent — Implants enumerieren typischerweise zur Kollisionsvermeidung und schreiben dann. Microsoft-Windows-Kernel-Registry-ETW trägt beide Events mit ausreichend feinen Zeitstempeln zur Korrelation. EDRs, die `CmRegisterCallbackEx` registrieren, erhalten `RegNtPreEnumerateKey`/`RegNtPostEnumerateKey`-Benachrichtigungen unabhängig vom User-Mode-Hook-Status. Hunt-Query: dieselbe PID emittiert ≥ 20 `NtEnumerateKey` unter `\Microsoft\Windows\CurrentVersion\Run*` plus einen `NtSetValueKey` zu einem Sibling innerhalb 2 s.
Direkte Syscall-Beispiele
cWalk Image File Execution Options subkeys
// hIfeo = handle to \Registry\Machine\Software\Microsoft\
// Windows NT\CurrentVersion\Image File Execution Options
for (ULONG i = 0;; ++i) {
BYTE buf[0x200];
ULONG got = 0;
NTSTATUS s = NtEnumerateKey(
hIfeo, i,
KeyBasicInformation,
buf, sizeof(buf), &got);
if (s == STATUS_NO_MORE_ENTRIES) break;
if (!NT_SUCCESS(s)) continue;
PKEY_BASIC_INFORMATION ki = (PKEY_BASIC_INFORMATION)buf;
// ki->Name is the image name targetable by IFEO Debugger hijack
}asmDirect stub (SSN 0x32)
NtEnumerateKey PROC
mov r10, rcx
mov eax, 32h
syscall
ret
NtEnumerateKey ENDPrustSnapshot subkey names to avoid index drift
use ntapi::ntregapi::{NtEnumerateKey, KEY_BASIC_INFORMATION};
use winapi::shared::ntdef::HANDLE;
pub unsafe fn list_subkeys(h: HANDLE) -> Vec<String> {
let mut names = Vec::new();
let mut buf = vec![0u8; 0x400];
for i in 0u32.. {
let mut got = 0u32;
let s = NtEnumerateKey(
h, i, 0 /* KeyBasicInformation */,
buf.as_mut_ptr() as *mut _, buf.len() as u32, &mut got);
if s == 0x8000001A /* STATUS_NO_MORE_ENTRIES */ { break; }
if s != 0 { continue; }
let ki = &*(buf.as_ptr() as *const KEY_BASIC_INFORMATION);
let name = core::slice::from_raw_parts(
ki.Name.as_ptr(), (ki.NameLength / 2) as usize);
names.push(String::from_utf16_lossy(name));
}
names
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20