NtQueryValueKey
Liest einen Wert aus einem Registrierungsschlüssel — die zielgerichtete Credential- und Config-Sammel-Primitive.
Prototyp
NTSTATUS NtQueryValueKey( HANDLE KeyHandle, PUNICODE_STRING ValueName, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| KeyHandle | HANDLE | in | Handle, geöffnet mit KEY_QUERY_VALUE (Teil von KEY_READ). |
| ValueName | PUNICODE_STRING | in | Name des zu lesenden Werts. Leerer String trifft den Default-Wert. |
| KeyValueInformationClass | KEY_VALUE_INFORMATION_CLASS | in | Layout: KeyValueBasicInformation=0, KeyValueFullInformation=1, KeyValuePartialInformation=2, KeyValuePartialInformationAlign64=4. |
| KeyValueInformation | PVOID | out | Ausgabe-Puffer, der die angeforderte KEY_VALUE_*_INFORMATION-Struktur aufnimmt. |
| Length | ULONG | in | Größe von KeyValueInformation in Bytes. |
| ResultLength | PULONG | out | Erhält die benötigte Größe; ermöglicht das klassische Leerabfrage-dann-Wiederholen-Muster. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x17 | win10-1507 |
| Win10 1607 | 0x17 | win10-1607 |
| Win10 1703 | 0x17 | win10-1703 |
| Win10 1709 | 0x17 | win10-1709 |
| Win10 1803 | 0x17 | win10-1803 |
| Win10 1809 | 0x17 | win10-1809 |
| Win10 1903 | 0x17 | win10-1903 |
| Win10 1909 | 0x17 | win10-1909 |
| Win10 2004 | 0x17 | win10-2004 |
| Win10 20H2 | 0x17 | win10-20h2 |
| Win10 21H1 | 0x17 | win10-21h1 |
| Win10 21H2 | 0x17 | win10-21h2 |
| Win10 22H2 | 0x17 | win10-22h2 |
| Win11 21H2 | 0x17 | win11-21h2 |
| Win11 22H2 | 0x17 | win11-22h2 |
| Win11 23H2 | 0x17 | win11-23h2 |
| Win11 24H2 | 0x17 | win11-24h2 |
| Server 2016 | 0x17 | winserver-2016 |
| Server 2019 | 0x17 | winserver-2019 |
| Server 2022 | 0x17 | winserver-2022 |
| Server 2025 | 0x17 | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 17 00 00 00 mov eax, 0x17 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
SSN `0x17` ist seit Windows 7 eingefroren. Die offensiv relevanten Information Classes sind `KeyValuePartialInformation` (Type + DataLength + Data, ohne Namen) und `KeyValueFullInformation` (Name + dasselbe) — sie decken ~99% der Credential-Scraping-Pfade ab. Klassisches Caller-Idiom: zunächst mit `Length=0` aufrufen, um `ResultLength` zu erfahren, allokieren, erneut aufrufen; dieser Zweischritt ist selbst ein Hunt-Signal, wenn er auf einen sensitiven Wertnamen zielt.
Häufige Malware-Nutzung
**Zielgerichtetes Credential-/Config-Harvesting (T1552.002, T1555)**. Stealer enumerieren nicht die ganze Registry — sie fragen bekannte Wertnamen unter bekannten Keys ab. Hochwertige Ziele: (a) **Putty Saved Sessions** unter `\Registry\User\<SID>\Software\SimonTatham\PuTTY\Sessions\*` (HostName, UserName, PortNumber, PublicKeyFile, ProxyHost/User) — wohin `PublicKeyFile` zeigt, wird die Private Key Datei das nächste NtReadFile-Ziel; (b) **WinSCP Saved Sessions** unter `\...\Software\Martin Prikryl\WinSCP 2\Sessions\*` mit `Password`, schwach verschlüsselt durch Host+User+Sessionname; (c) **gecachte RDP-Credentials** in `\...\Software\Microsoft\Terminal Server Client\Servers\*\UsernameHint`; (d) **Outlook-Profil-Mail-Credentials** unter `\...\Software\Microsoft\Office\<ver>\Outlook\Profiles\*\<profileGUID>\...`; (e) **VNC-Passwortwerte** (TightVNC, UltraVNC); (f) **Mail-Clients** (Thunderbird, eM Client) mit Konto-/Server-Einträgen, die auf Disk-Credential-Blobs zeigen. RedLine, LummaC2, Vidar, AgentTesla, Atomic, Raccoon nutzen genau diese Primitive in ihren config-gesteuerten Harvest-Loops.
Erkennungsmöglichkeiten
Microsoft-Windows-Kernel-Registry-ETW emittiert pro Aufruf ein Query-Event — extrem geschwätzig, hunderte pro Sekunde pro Prozess, Volumen allein nutzlos. Das Signal lebt in der **Pfad-/Wert-Korrelation**: jeder andere Prozess als die Eigentümer-Anwendung (mstsc.exe für RDP, putty.exe für PuTTY, outlook.exe für Outlook), der diese Keys liest, ist ein hochwertiger Indikator. Sysmons `RegistryEvent` deckt Reads nicht direkt ab, deshalb ist EDR-Kernel-Callback-Telemetrie (`CmRegisterCallbackEx` → `RegNtPreQueryValueKey`/`RegNtPostQueryValueKey`) die praktische Erkennungsfläche. Defenders Stealer-Verhaltens-Engine (`Behavior:Win32/StealerCredAccess.*`) markiert Prozesse, die viele dieser Pfade in schneller Folge abfragen. Nützliche synthetische Regel: alarmieren, wenn dieselbe PID `NtQueryValueKey` gegen ≥ 3 verschiedene Credential-Subtrees in einem 5-Sekunden-Fenster ausführt.
Direkte Syscall-Beispiele
cRead PuTTY HostName for a saved session
// hSess = handle to \Registry\User\<SID>\Software\SimonTatham\PuTTY\Sessions\<name>
UNICODE_STRING vn;
RtlInitUnicodeString(&vn, L"HostName");
BYTE buf[512];
ULONG got = 0;
NTSTATUS s = NtQueryValueKey(
hSess, &vn,
KeyValuePartialInformation,
buf, sizeof(buf), &got);
if (NT_SUCCESS(s)) {
PKEY_VALUE_PARTIAL_INFORMATION pv = (PKEY_VALUE_PARTIAL_INFORMATION)buf;
// pv->Type == REG_SZ
// pv->Data == L"prod-jumpbox.contoso.com"
}asmDirect stub (SSN 0x17)
NtQueryValueKey PROC
mov r10, rcx
mov eax, 17h
syscall
ret
NtQueryValueKey ENDPrustTwo-call resize idiom
use ntapi::ntregapi::{NtQueryValueKey, KEY_VALUE_PARTIAL_INFORMATION};
use ntapi::ntrtl::RtlInitUnicodeString;
use winapi::shared::ntdef::{HANDLE, UNICODE_STRING};
pub unsafe fn read_value(h: HANDLE, name: *const u16) -> Option<Vec<u8>> {
let mut vn: UNICODE_STRING = core::mem::zeroed();
RtlInitUnicodeString(&mut vn, name);
let mut need = 0u32;
// probe length
let _ = NtQueryValueKey(h, &mut vn, 2, core::ptr::null_mut(), 0, &mut need);
let mut buf = vec![0u8; need as usize];
let s = NtQueryValueKey(
h, &mut vn, 2,
buf.as_mut_ptr() as *mut _, buf.len() as u32, &mut need);
if s == 0 {
let pv = &*(buf.as_ptr() as *const KEY_VALUE_PARTIAL_INFORMATION);
let n = pv.DataLength as usize;
Some(buf[std::mem::offset_of!(KEY_VALUE_PARTIAL_INFORMATION, Data)..
std::mem::offset_of!(KEY_VALUE_PARTIAL_INFORMATION, Data) + n].to_vec())
} else { None }
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20