NtCreateKey
Crée ou ouvre une clé de registre — primitive noyau derrière toute persistance écrite dans le registre.
Prototype
NTSTATUS NtCreateKey( PHANDLE KeyHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, ULONG TitleIndex, PUNICODE_STRING Class, ULONG CreateOptions, PULONG Disposition );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| KeyHandle | PHANDLE | out | Reçoit le handle vers la clé créée ou ouverte. |
| DesiredAccess | ACCESS_MASK | in | Masque d'accès, ex. KEY_WRITE, KEY_ALL_ACCESS, KEY_SET_VALUE. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | OBJECT_ATTRIBUTES contenant le chemin du registre (ex. \Registry\Machine\Software\...). |
| TitleIndex | ULONG | in | Réservé. Doit être zéro. |
| Class | PUNICODE_STRING | in | Nom de classe optionnel pour la clé. Habituellement NULL. |
| CreateOptions | ULONG | in | Drapeaux, ex. REG_OPTION_NON_VOLATILE, REG_OPTION_VOLATILE, REG_OPTION_CREATE_LINK. |
| Disposition | PULONG | out | Reçoit REG_CREATED_NEW_KEY ou REG_OPENED_EXISTING_KEY. Peut être NULL. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x1D | win10-1507 |
| Win10 1607 | 0x1D | win10-1607 |
| Win10 1703 | 0x1D | win10-1703 |
| Win10 1709 | 0x1D | win10-1709 |
| Win10 1803 | 0x1D | win10-1803 |
| Win10 1809 | 0x1D | win10-1809 |
| Win10 1903 | 0x1D | win10-1903 |
| Win10 1909 | 0x1D | win10-1909 |
| Win10 2004 | 0x1D | win10-2004 |
| Win10 20H2 | 0x1D | win10-20h2 |
| Win10 21H1 | 0x1D | win10-21h1 |
| Win10 21H2 | 0x1D | win10-21h2 |
| Win10 22H2 | 0x1D | win10-22h2 |
| Win11 21H2 | 0x1D | win11-21h2 |
| Win11 22H2 | 0x1D | win11-22h2 |
| Win11 23H2 | 0x1D | win11-23h2 |
| Win11 24H2 | 0x1D | win11-24h2 |
| Server 2016 | 0x1D | winserver-2016 |
| Server 2019 | 0x1D | winserver-2019 |
| Server 2022 | 0x1D | winserver-2022 |
| Server 2025 | 0x1D | winserver-2025 |
Module noyau
APIs liées
Stub du syscall
4C 8B D1 mov r10, rcx B8 1D 00 00 00 mov eax, 0x1D 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
`0x1D` est le SSN depuis Windows 7 et reste inchangé jusqu'à Win11 24H2 — l'un des numéros codés en dur les plus fiables. L'appel passe par CmpCallCallBacksEx puis CmCreateKey dans le Configuration Manager (allocation CM_KEY_BODY). Comme le chemin ObjectAttributes est analysé dans l'espace d'objets NT (`\Registry\Machine\...`, `\Registry\User\<SID>\...`), les implants l'utilisent directement pour éviter les hooks de traduction de chemin que les EDR posent sur advapi32!RegCreateKeyEx.
Usage courant par les malwares
La persistance est l'usage dominant. Les opérateurs créent des sous-clés sous `HKLM\Software\Microsoft\Windows\CurrentVersion\Run`, `HKCU\...\Run`, `HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon` (Shell/Userinit/AppInit_DLLs), `HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<cible.exe>` (détournement par valeur Debugger), et `HKLM\System\CurrentControlSet\Services\<nom>` pour la persistance par service. Appeler NtCreateKey directement contourne les hooks user-mode d'advapi32 et permet d'écrire dans les clés sans artefacts de type CreateRemoteThread.
Opportunités de détection
Le provider ETW Microsoft-Windows-Kernel-Registry (`{70EB4F03-C1DE-4F73-A051-33D13D5413BD}`) déclenche `EventCreateKey` (Opcode 10) avec le chemin complet et le PID créateur — la meilleure source de détection, insensible aux contournements user-mode. Sysmon Event ID 12 (RegistryEvent: CreateKey / DeleteKey) capture la même chose là où Defender for Endpoint n'est pas déployé. Chasser surtout sur les clés d'autostart, les sous-clés IFEO ciblant des LOLBins courants (`mmc.exe`, `taskmgr.exe`, `osk.exe`), et toute nouvelle entrée `Services\` dont l'ImagePath sort de `\SystemRoot\System32\`. Les hooks EDR sur advapi32!RegCreateKeyEx sont facilement éludés ; l'ETW noyau ne l'est pas.
Exemples de syscalls directs
cCreate or open HKCU Run subkey
// Open / create HKCU\Software\Microsoft\Windows\CurrentVersion\Run via Nt path.
// Pair with NtSetValueKey to drop a persistence entry.
UNICODE_STRING path;
RtlInitUnicodeString(&path,
L"\\Registry\\User\\<SID>\\Software\\Microsoft\\Windows\\CurrentVersion\\Run");
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE, NULL, NULL);
HANDLE hKey = NULL;
ULONG disposition = 0;
NTSTATUS s = NtCreateKey(&hKey, KEY_SET_VALUE, &oa,
0, NULL, REG_OPTION_NON_VOLATILE, &disposition);asmDirect stub (SSN 0x1D)
NtCreateKey PROC
mov r10, rcx
mov eax, 1Dh
syscall
ret
NtCreateKey ENDPrustntapi crate persistence helper
// Cargo: ntapi = "0.4", winapi = { version = "0.3", features = ["winnt"] }
use ntapi::ntregapi::NtCreateKey;
use ntapi::ntrtl::RtlInitUnicodeString;
use winapi::shared::ntdef::{OBJECT_ATTRIBUTES, OBJ_CASE_INSENSITIVE, UNICODE_STRING};
use winapi::um::winnt::{KEY_SET_VALUE, REG_OPTION_NON_VOLATILE};
use std::ptr::null_mut;
pub unsafe fn open_run_key(sid_path: *const u16) -> *mut core::ffi::c_void {
let mut us: UNICODE_STRING = core::mem::zeroed();
RtlInitUnicodeString(&mut us, sid_path);
let mut oa = OBJECT_ATTRIBUTES {
Length: core::mem::size_of::<OBJECT_ATTRIBUTES>() as u32,
RootDirectory: null_mut(),
ObjectName: &mut us,
Attributes: OBJ_CASE_INSENSITIVE,
SecurityDescriptor: null_mut(),
SecurityQualityOfService: null_mut(),
};
let mut h = null_mut();
let mut disp = 0u32;
NtCreateKey(&mut h, KEY_SET_VALUE, &mut oa, 0,
null_mut(), REG_OPTION_NON_VOLATILE, &mut disp);
h
}Mappings MITRE ATT&CK
Last verified: 2026-05-20