> Windows Syscalls
ntoskrnl.exeT1552.002T1555T1012

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

NameTypeDirDescription
KeyHandleHANDLEinHandle, geöffnet mit KEY_QUERY_VALUE (Teil von KEY_READ).
ValueNamePUNICODE_STRINGinName des zu lesenden Werts. Leerer String trifft den Default-Wert.
KeyValueInformationClassKEY_VALUE_INFORMATION_CLASSinLayout: KeyValueBasicInformation=0, KeyValueFullInformation=1, KeyValuePartialInformation=2, KeyValuePartialInformationAlign64=4.
KeyValueInformationPVOIDoutAusgabe-Puffer, der die angeforderte KEY_VALUE_*_INFORMATION-Struktur aufnimmt.
LengthULONGinGröße von KeyValueInformation in Bytes.
ResultLengthPULONGoutErhält die benötigte Größe; ermöglicht das klassische Leerabfrage-dann-Wiederholen-Muster.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x17win10-1507
Win10 16070x17win10-1607
Win10 17030x17win10-1703
Win10 17090x17win10-1709
Win10 18030x17win10-1803
Win10 18090x17win10-1809
Win10 19030x17win10-1903
Win10 19090x17win10-1909
Win10 20040x17win10-2004
Win10 20H20x17win10-20h2
Win10 21H10x17win10-21h1
Win10 21H20x17win10-21h2
Win10 22H20x17win10-22h2
Win11 21H20x17win11-21h2
Win11 22H20x17win11-22h2
Win11 23H20x17win11-23h2
Win11 24H20x17win11-24h2
Server 20160x17winserver-2016
Server 20190x17winserver-2019
Server 20220x17winserver-2022
Server 20250x17winserver-2025

Kernel-Modul

ntoskrnl.exeNtQueryValueKey

Verwandte APIs

RegQueryValueExWRegGetValueWNtEnumerateValueKeyNtQueryMultipleValueKeyNtOpenKeyNtClose

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.

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

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