NtOpenKeyEx
Variante extendida de NtOpenKey con OpenOptions — necesaria para seguir enlaces simbólicos y aperturas con semántica backup.
Prototipo
NTSTATUS NtOpenKeyEx( PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG OpenOptions );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| KeyHandle | PHANDLE | out | Recibe el handle a la clave abierta en éxito. |
| DesiredAccess | ACCESS_MASK | in | Máscara de acceso, p. ej. KEY_READ, KEY_QUERY_VALUE, KEY_WRITE, KEY_ALL_ACCESS. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | OBJECT_ATTRIBUTES con la ruta NT del registro. |
| OpenOptions | ULONG | in | Banderas: REG_OPTION_BACKUP_RESTORE (usa SeBackupPrivilege), REG_OPTION_OPEN_LINK (no sigue el symlink). |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x10C | win10-1507 |
| Win10 1607 | 0x111 | win10-1607 |
| Win10 1703 | 0x115 | win10-1703 |
| Win10 1709 | 0x117 | win10-1709 |
| Win10 1803 | 0x119 | win10-1803 |
| Win10 1809 | 0x11A | win10-1809 |
| Win10 1903 | 0x11B | win10-1903 |
| Win10 1909 | 0x11B | win10-1909 |
| Win10 2004 | 0x120 | win10-2004 |
| Win10 20H2 | 0x120 | win10-20h2 |
| Win10 21H1 | 0x120 | win10-21h1 |
| Win10 21H2 | 0x121 | win10-21h2 |
| Win10 22H2 | 0x121 | win10-22h2 |
| Win11 21H2 | 0x127 | win11-21h2 |
| Win11 22H2 | 0x129 | win11-22h2 |
| Win11 23H2 | 0x129 | win11-23h2 |
| Win11 24H2 | 0x12B | win11-24h2 |
| Server 2016 | 0x111 | winserver-2016 |
| Server 2019 | 0x11A | winserver-2019 |
| Server 2022 | 0x126 | winserver-2022 |
| Server 2025 | 0x12B | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 2B 01 00 00 mov eax, 0x12B 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
NtOpenKeyEx = `NtOpenKey + OpenOptions`. Las dos únicas opciones relevantes en términos ofensivos son `REG_OPTION_BACKUP_RESTORE` (`0x4`) — que hace que el configuration manager omita el chequeo de DACL y use `SeBackupPrivilege`/`SeRestorePrivilege` del invocador — y `REG_OPTION_OPEN_LINK` (`0x8`) — que abre una clave symlink sin seguir el destino, permitiendo auditar redirecciones. El SSN deriva mucho entre builds porque se añadieron/quitaron syscalls vecinos: Win10 1909 = 0x11B, Win10 22H2 = 0x121, Win11 24H2 = 0x12B. Codificar SSNs en duro aquí es muy frágil — Hell's Gate / Halo's Gate es obligatorio para multi-build.
Uso común por malware
Dos patrones ofensivos distintos. (1) **Acceso a hive vía privilegio**: con `SeBackupPrivilege` habilitado (concedido a Administrators y Backup Operators), abrir con `REG_OPTION_BACKUP_RESTORE` esquiva la DACL de `\Registry\Machine\SAM` y `\Registry\Machine\SECURITY` sin impersonar SYSTEM — es lo que `reg.exe save HKLM\SAM` hace bajo el capó, y de lo que dependen los dumpers SAM modernos (módulo local SAM de CrackMapExec/NetExec, `secretsdump.py -local-auth`). (2) **Persistencia consciente de symlinks**: implantes APT encadenan `NtOpenKeyEx` con `REG_OPTION_OPEN_LINK` para inspeccionar enlaces simbólicos del registro plantados por sí mismos o por intrusiones previas (IFEO redirigido a claves del atacante, CLSID COM con symlink a HKCU). Las herramientas forenses hacen la misma auditoría con el mismo flag.
Oportunidades de detección
Mismos proveedores que NtOpenKey — ETW Microsoft-Windows-Kernel-Registry registra cada open con PID y acceso. La huella de `REG_OPTION_BACKUP_RESTORE` es visible: el proceso debe tener `SeBackupPrivilege` *habilitado*, lo que Sysmon Event ID 4673 (Sensitive Privilege Use) registra al ejercerlo — combinar uso de `SeBackupPrivilege` con apertura posterior de `\Registry\Machine\SAM` o `\Registry\Machine\SECURITY` da una señal de SAM dump de muy alta fidelidad. Los EDR que registran `CmRegisterCallbackEx` ven el valor OpenOptions en `RegNtPreOpenKeyEx`. La regla `RegistryEvent` `Sensitive Registry Object` del template de SwiftOnSecurity lo atrapa. La subcategoría de auditoría "Sensitive Privilege Use" (4673/4674) filtrada por `SeBackupPrivilege` es la baseline más barata.
Ejemplos de syscalls directos
cOpen SAM hive with backup privilege
// Caller must first enable SeBackupPrivilege in its token.
#define REG_OPTION_BACKUP_RESTORE 0x00000004
UNICODE_STRING name;
RtlInitUnicodeString(&name, L"\\Registry\\Machine\\SAM");
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hKey = NULL;
NTSTATUS s = NtOpenKeyEx(&hKey, KEY_READ, &oa,
REG_OPTION_BACKUP_RESTORE);asmDirect stub (SSN 0x12B, Win11 24H2)
; SSN drifts across builds — resolve dynamically when targeting multiple.
NtOpenKeyEx PROC
mov r10, rcx
mov eax, 12Bh ; Win11 24H2
syscall
ret
NtOpenKeyEx ENDPrustOpen key but don't follow registry symlink
use ntapi::ntregapi::NtOpenKeyEx;
use ntapi::ntrtl::RtlInitUnicodeString;
use winapi::shared::ntdef::{HANDLE, OBJECT_ATTRIBUTES, OBJ_CASE_INSENSITIVE, UNICODE_STRING};
const REG_OPTION_OPEN_LINK: u32 = 0x0000_0008;
pub unsafe fn open_link(path: *const u16) -> Option<HANDLE> {
let mut us: UNICODE_STRING = core::mem::zeroed();
RtlInitUnicodeString(&mut us, path);
let mut oa = OBJECT_ATTRIBUTES {
Length: core::mem::size_of::<OBJECT_ATTRIBUTES>() as u32,
RootDirectory: core::ptr::null_mut(),
ObjectName: &mut us,
Attributes: OBJ_CASE_INSENSITIVE,
SecurityDescriptor: core::ptr::null_mut(),
SecurityQualityOfService: core::ptr::null_mut(),
};
let mut h: HANDLE = core::ptr::null_mut();
if NtOpenKeyEx(&mut h, 0x20019 /* KEY_READ */, &mut oa, REG_OPTION_OPEN_LINK) == 0 {
Some(h)
} else { None }
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20