> Windows Syscalls
ntoskrnl.exeT1003.002T1003.004T1012

NtOpenKeyEx

Extended variant of NtOpenKey accepting OpenOptions — required for symlink-following and backup-semantics opens.

Prototype

NTSTATUS NtOpenKeyEx(
  PHANDLE            KeyHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes,
  ULONG              OpenOptions
);

Arguments

NameTypeDirDescription
KeyHandlePHANDLEoutReceives the handle to the opened key on success.
DesiredAccessACCESS_MASKinAccess mask, e.g. KEY_READ, KEY_QUERY_VALUE, KEY_WRITE, KEY_ALL_ACCESS.
ObjectAttributesPOBJECT_ATTRIBUTESinOBJECT_ATTRIBUTES with the NT registry path.
OpenOptionsULONGinFlags: REG_OPTION_BACKUP_RESTORE (use SeBackupPrivilege), REG_OPTION_OPEN_LINK (don't follow symlink).

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070x10Cwin10-1507
Win10 16070x111win10-1607
Win10 17030x115win10-1703
Win10 17090x117win10-1709
Win10 18030x119win10-1803
Win10 18090x11Awin10-1809
Win10 19030x11Bwin10-1903
Win10 19090x11Bwin10-1909
Win10 20040x120win10-2004
Win10 20H20x120win10-20h2
Win10 21H10x120win10-21h1
Win10 21H20x121win10-21h2
Win10 22H20x121win10-22h2
Win11 21H20x127win11-21h2
Win11 22H20x129win11-22h2
Win11 23H20x129win11-23h2
Win11 24H20x12Bwin11-24h2
Server 20160x111winserver-2016
Server 20190x11Awinserver-2019
Server 20220x126winserver-2022
Server 20250x12Bwinserver-2025

Kernel module

ntoskrnl.exeNtOpenKeyEx

Related APIs

RegOpenKeyExWNtOpenKeyNtOpenKeyTransactedExNtCreateKeyNtQueryKeyNtQueryValueKey

Syscall stub

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

Undocumented notes

NtOpenKeyEx is `NtOpenKey + OpenOptions`. The only two options that matter offensively are `REG_OPTION_BACKUP_RESTORE` (`0x4`) — which causes the configuration manager to skip the DACL check and use the caller's `SeBackupPrivilege` / `SeRestorePrivilege` instead — and `REG_OPTION_OPEN_LINK` (`0x8`) — which opens a symbolic-link key without following the target, allowing audit of redirections. The SSN drifts significantly across builds because adjacent syscalls were added/removed: Win10 1909 = 0x11B, Win10 22H2 = 0x121, Win11 24H2 = 0x12B. Hardcoding SSNs is brittle here — Hell's Gate / Halo's Gate dynamic lookup is mandatory if you want cross-build support.

Common malware usage

Two distinct offensive patterns. (1) **Privilege-mediated hive access**: with `SeBackupPrivilege` enabled (granted to Administrators, Backup Operators), opening with `REG_OPTION_BACKUP_RESTORE` bypasses the DACL on `\Registry\Machine\SAM` and `\Registry\Machine\SECURITY` without needing SYSTEM impersonation — this is what `reg.exe save HKLM\SAM` does under the hood, and what most modern SAM dumpers (CrackMapExec/NetExec local SAM module, secretsdump.py with `-local-auth`) rely on. (2) **Symlink-aware persistence**: APT-style implants chain `NtOpenKeyEx` with `REG_OPTION_OPEN_LINK` to introspect registry symbolic links planted by themselves or earlier intrusions for hijack persistence (Image File Execution Options redirected to attacker keys, COM CLSID symlinked into HKCU). Forensics tools doing the same audit benefit from the same flag.

Detection opportunities

Same providers as NtOpenKey — Microsoft-Windows-Kernel-Registry ETW logs each open with PID and access. The fingerprint of `REG_OPTION_BACKUP_RESTORE` is visible: the requesting process must hold `SeBackupPrivilege` *enabled*, which Sysmon Event ID 4673 (Sensitive Privilege Use) records when actually exercised — pair `SeBackupPrivilege` use with a subsequent open of `\Registry\Machine\SAM` or `\Registry\Machine\SECURITY` for a very high-fidelity SAM-dump signal. EDRs that register `CmRegisterCallbackEx` see the OpenOptions value in `RegNtPreOpenKeyEx`. Sysmon's `RegistryEvent` `Sensitive Registry Object` rule from SwiftOnSecurity's template catches this. Audit Subcategory "Sensitive Privilege Use" (4673/4674) with privilege filter `SeBackupPrivilege` is the cheapest baseline.

Direct syscall examples

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 ENDP

rustOpen 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 }
}

MITRE ATT&CK mappings

Last verified: 2026-05-20