NtQueryValueKey
Lee un valor de una clave del registro — primitiva de cosecha dirigida de credenciales y configuraciones.
Prototipo
NTSTATUS NtQueryValueKey( HANDLE KeyHandle, PUNICODE_STRING ValueName, KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass, PVOID KeyValueInformation, ULONG Length, PULONG ResultLength );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| KeyHandle | HANDLE | in | Handle abierto con KEY_QUERY_VALUE (parte de KEY_READ). |
| ValueName | PUNICODE_STRING | in | Nombre del valor a leer. Cadena vacía apunta al valor por defecto. |
| KeyValueInformationClass | KEY_VALUE_INFORMATION_CLASS | in | Formato: KeyValueBasicInformation=0, KeyValueFullInformation=1, KeyValuePartialInformation=2, KeyValuePartialInformationAlign64=4. |
| KeyValueInformation | PVOID | out | Búfer de salida que recibe la estructura KEY_VALUE_*_INFORMATION solicitada. |
| Length | ULONG | in | Tamaño de KeyValueInformation en bytes. |
| ResultLength | PULONG | out | Recibe el tamaño requerido; permite el patrón clásico consulta-vacía y luego nueva consulta. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | 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 |
Módulo del kernel
APIs relacionadas
Stub del syscall
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
Notas no documentadas
El SSN `0x17` está congelado desde Windows 7. Las dos clases de información que importan ofensivamente son `KeyValuePartialInformation` (Type + DataLength + Data, sin nombre) y `KeyValueFullInformation` (lo mismo + nombre) — cubren ≈ 99% de las rutas de credential scraping. Idioma clásico: llamar con `Length=0` para conocer `ResultLength`, asignar, llamar de nuevo; ese baile en dos pasos es por sí mismo una señal de caza cuando apunta a un nombre de valor sensible.
Uso común por malware
**Cosecha dirigida de credenciales y configs (T1552.002, T1555)**. Los stealers no enumeran todo el registro — atacan nombres conocidos bajo claves conocidas. Objetivos de alto valor: (a) **sesiones PuTTY guardadas** en `\Registry\User\<SID>\Software\SimonTatham\PuTTY\Sessions\*` (HostName, UserName, PortNumber, PublicKeyFile, ProxyHost/User) — y donde apunte `PublicKeyFile`, la clave privada se vuelve el siguiente objetivo de NtReadFile; (b) **sesiones WinSCP guardadas** en `\...\Software\Martin Prikryl\WinSCP 2\Sessions\*` con `Password` débilmente cifrado por host+user+nombre; (c) **credenciales RDP en caché** en `\...\Software\Microsoft\Terminal Server Client\Servers\*\UsernameHint`; (d) **credenciales de correo Outlook** bajo `\...\Software\Microsoft\Office\<ver>\Outlook\Profiles\*\<profileGUID>\...`; (e) **valores de password VNC** (TightVNC, UltraVNC); (f) **clientes de correo almacenados** (Thunderbird, eM Client) con entradas cuenta/servidor que apuntan a blobs de credenciales en disco. RedLine, LummaC2, Vidar, AgentTesla, Atomic, Raccoon usan exactamente esta primitiva en sus bucles de harvest guiados por config.
Oportunidades de detección
ETW Microsoft-Windows-Kernel-Registry emite evento de query por llamada — extremadamente ruidoso, cientos por segundo por proceso, así que el volumen por sí mismo es inútil. La señal vive en la **correlación ruta/valor**: cualquier proceso distinto de la app propietaria (mstsc.exe para RDP, putty.exe para PuTTY, outlook.exe para Outlook) leyendo esas claves es un indicador de alta fidelidad. Sysmon `RegistryEvent` no cubre lecturas, por lo que la telemetría de callbacks de kernel EDR (`CmRegisterCallbackEx` → `RegNtPreQueryValueKey`/`RegNtPostQueryValueKey`) es la superficie práctica. El motor de comportamiento anti-stealer de Defender (`Behavior:Win32/StealerCredAccess.*`) marca procesos que consultan varias de estas rutas en rápida sucesión. Regla sintética útil: alertar cuando un mismo PID emite `NtQueryValueKey` contra ≥ 3 subárboles de credenciales distintos en ventana de 5 s.
Ejemplos de syscalls directos
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 }
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20