NtQueryKey
Liefert Metadaten zu einem geöffneten Registry-Schlüssel — Name, Klasse, Anzahl Unterschlüssel/Werte, letzte Änderung.
Prototyp
NTSTATUS NtQueryKey( HANDLE KeyHandle, KEY_INFORMATION_CLASS KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| KeyHandle | HANDLE | in | Handle auf einen geöffneten Schlüssel. Der erforderliche Zugriff hängt von KeyInformationClass ab; KEY_QUERY_VALUE genügt meist. |
| KeyInformationClass | KEY_INFORMATION_CLASS | in | Angeforderte Variante: KeyBasicInformation, KeyNodeInformation, KeyFullInformation, KeyNameInformation, KeyCachedInformation, KeyVirtualizationInformation usw. |
| KeyInformation | PVOID | out | Vom Aufrufer allokierter Puffer, der die angeforderte Struktur empfängt. |
| Length | ULONG | in | Größe von KeyInformation in Bytes. |
| ResultLength | PULONG | out | Empfängt die tatsächlich zurückgegebene (oder bei STATUS_BUFFER_TOO_SMALL benötigte) Größe. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x16 | win10-1507 |
| Win10 1607 | 0x16 | win10-1607 |
| Win10 1703 | 0x16 | win10-1703 |
| Win10 1709 | 0x16 | win10-1709 |
| Win10 1803 | 0x16 | win10-1803 |
| Win10 1809 | 0x16 | win10-1809 |
| Win10 1903 | 0x16 | win10-1903 |
| Win10 1909 | 0x16 | win10-1909 |
| Win10 2004 | 0x16 | win10-2004 |
| Win10 20H2 | 0x16 | win10-20h2 |
| Win10 21H1 | 0x16 | win10-21h1 |
| Win10 21H2 | 0x16 | win10-21h2 |
| Win10 22H2 | 0x16 | win10-22h2 |
| Win11 21H2 | 0x16 | win11-21h2 |
| Win11 22H2 | 0x16 | win11-22h2 |
| Win11 23H2 | 0x16 | win11-23h2 |
| Win11 24H2 | 0x16 | win11-24h2 |
| Server 2016 | 0x16 | winserver-2016 |
| Server 2019 | 0x16 | winserver-2019 |
| Server 2022 | 0x16 | winserver-2022 |
| Server 2025 | 0x16 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 16 00 00 00 mov eax, 0x16 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
Bemerkenswert: die SSN von NtQueryKey ist von Windows 10 1507 bis Windows 11 24H2 auf `0x16` festgenagelt — eine der stabilsten Nummern in der SSDT, was eine hartkodierte SSN über Builds hinweg sicher macht. Die KEY_FULL_INFORMATION-Variante liefert LastWriteTime als FILETIME, die Anzahl der Unterschlüssel / Werte und die Maximallängen — dieselben Zahlen, auf die `reg query` und `regjump` setzen.
Häufige Malware-Nutzung
Drei Spielarten des Missbrauchs. (1) *Timestomp-Detection-Evasion*: ein Operator liest LastWriteTime vor der Manipulation eines Werts und stellt ihn anschließend per NtSetInformationKey (KeyWriteTimeInformation) wieder her. (2) *Enumerations-Tooling*: Registry-laufende Implants kombinieren NtEnumerateKey + NtQueryKey, um einen Teilbaum zu rekursieren und hochwertige Ziele (Autoruns, Services, AppCompat-Shims) zu identifizieren. (3) *Sandbox-Fingerprinting*: Schlüssel wie `HKLM\HARDWARE\DESCRIPTION\System\BIOS` abfragen und LastWriteTime inspizieren, um frisch gebaute Analyse-VMs zu erkennen.
Erkennungsmöglichkeiten
NtQueryKey allein ist Hintergrundrauschen in hoher Frequenz — der Explorer ruft es zigtausendfach pro Stunde auf. Das wertvolle Signal ist die folgende *Schreiboperation*: KeyWriteTimeInformation über NtSetInformationKey ist in legitimen Workloads selten und nahezu diagnostisch für Timestomp. EDRs, die CmRegisterCallbackEx hooken, sehen RegNtPostQueryKey, ignorieren es aber meist aus Kostengründen. ETW Microsoft-Windows-Kernel-Registry liefert RegistryQueryKey, ist aber standardmäßig deaktiviert. Hunt-Tipp: ein Prozess-Handle-Inventar, das einen Nicht-Explorer/Nicht-svchost-Prozess mit hunderten offenen Registry-Handles zeigt, ist eine bessere Spur als jeder einzelne NtQueryKey-Aufruf.
Direkte Syscall-Beispiele
cRead LastWriteTime for timestomp staging
// Capture the original LastWriteTime before tampering with a value,
// so it can be restored via NtSetInformationKey afterward.
KEY_BASIC_INFORMATION* kbi = (KEY_BASIC_INFORMATION*)buf;
ULONG ret = 0;
NTSTATUS st = NtQueryKey(hKey, KeyBasicInformation, kbi, sizeof(buf), &ret);
if (NT_SUCCESS(st)) {
g_savedWriteTime = kbi->LastWriteTime;
}asmx64 direct stub (stable SSN 0x16)
NtQueryKey PROC
mov r10, rcx
mov eax, 16h
syscall
ret
NtQueryKey ENDPrustFull information enumeration
// Cargo: ntapi = "0.4"
let mut needed: u32 = 0;
let status = unsafe {
NtQueryKey(h_key,
KeyFullInformation,
null_mut(),
0,
&mut needed)
};
assert_eq!(status, STATUS_BUFFER_TOO_SMALL as i32);
let mut buf = vec![0u8; needed as usize];
let status = unsafe {
NtQueryKey(h_key,
KeyFullInformation,
buf.as_mut_ptr() as _,
buf.len() as u32,
&mut needed)
};
assert!(status == 0);MITRE ATT&CK-Mappings
Last verified: 2026-05-20