NtOpenKeyEx
Variante étendue de NtOpenKey avec OpenOptions — requise pour suivre les liens symboliques et l'ouverture en sémantique backup.
Prototype
NTSTATUS NtOpenKeyEx( PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG OpenOptions );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| KeyHandle | PHANDLE | out | Reçoit le handle vers la clé ouverte en cas de succès. |
| DesiredAccess | ACCESS_MASK | in | Masque d'accès, ex. KEY_READ, KEY_QUERY_VALUE, KEY_WRITE, KEY_ALL_ACCESS. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | OBJECT_ATTRIBUTES contenant le chemin registre NT. |
| OpenOptions | ULONG | in | Drapeaux : REG_OPTION_BACKUP_RESTORE (utilise SeBackupPrivilege), REG_OPTION_OPEN_LINK (ne suit pas le symlink). |
IDs de syscalls par version de Windows
| Version 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 |
Module noyau
APIs liées
Stub du 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
Notes non documentées
NtOpenKeyEx = `NtOpenKey + OpenOptions`. Les deux seules options pertinentes côté offensif sont `REG_OPTION_BACKUP_RESTORE` (`0x4`) — qui pousse le configuration manager à sauter le check DACL et à se reposer sur `SeBackupPrivilege`/`SeRestorePrivilege` de l'appelant — et `REG_OPTION_OPEN_LINK` (`0x8`) — qui ouvre une clé lien symbolique sans suivre la cible, permettant d'auditer les redirections. Le SSN dérive sensiblement d'une build à l'autre car des syscalls voisins ont été ajoutés/retirés : Win10 1909 = 0x11B, Win10 22H2 = 0x121, Win11 24H2 = 0x12B. Coder en dur les SSN est ici très fragile — Hell's Gate / Halo's Gate est obligatoire pour du multi-build.
Usage courant par les malwares
Deux patrons offensifs. (1) **Accès ruche via privilège** : avec `SeBackupPrivilege` actif (accordé à Administrators et Backup Operators), ouvrir avec `REG_OPTION_BACKUP_RESTORE` contourne la DACL de `\Registry\Machine\SAM` et `\Registry\Machine\SECURITY` sans avoir à impersonner SYSTEM — c'est ce que fait `reg.exe save HKLM\SAM` en interne, et ce dont dépendent la plupart des dumpers SAM modernes (module local SAM de CrackMapExec/NetExec, `secretsdump.py -local-auth`). (2) **Persistance consciente des symlinks** : implants APT enchaînent `NtOpenKeyEx` avec `REG_OPTION_OPEN_LINK` pour introspecter les liens symboliques registre posés par eux-mêmes ou par d'anciennes intrusions (IFEO redirigé vers des clés attaquantes, CLSID COM symlinkés vers HKCU). Les outils forensiques font le même audit avec le même flag.
Opportunités de détection
Mêmes fournisseurs que NtOpenKey — l'ETW Microsoft-Windows-Kernel-Registry journalise chaque open avec PID et accès. L'empreinte de `REG_OPTION_BACKUP_RESTORE` est visible : le processus appelant doit détenir `SeBackupPrivilege` *activé*, ce que Sysmon Event ID 4673 (Sensitive Privilege Use) enregistre à l'exercice — coupler l'usage de `SeBackupPrivilege` à un open ultérieur de `\Registry\Machine\SAM` ou `\Registry\Machine\SECURITY` donne un signal de SAM dump à très forte fidélité. Les EDR enregistrant `CmRegisterCallbackEx` voient la valeur OpenOptions dans `RegNtPreOpenKeyEx`. La règle `RegistryEvent` `Sensitive Registry Object` du template SwiftOnSecurity attrape ça. La sous-catégorie d'audit « Sensitive Privilege Use » (4673/4674) filtrée sur `SeBackupPrivilege` est la baseline la moins chère.
Exemples de syscalls directs
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 }
}Mappings MITRE ATT&CK
Last verified: 2026-05-20