> Windows Syscalls
ntoskrnl.exeT1547.001T1546.012T1112

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

NameTypeDirDescription
KeyHandlePHANDLEoutReçoit le handle vers la clé créée ou ouverte.
DesiredAccessACCESS_MASKinMasque d'accès, ex. KEY_WRITE, KEY_ALL_ACCESS, KEY_SET_VALUE.
ObjectAttributesPOBJECT_ATTRIBUTESinOBJECT_ATTRIBUTES contenant le chemin du registre (ex. \Registry\Machine\Software\...).
TitleIndexULONGinRéservé. Doit être zéro.
ClassPUNICODE_STRINGinNom de classe optionnel pour la clé. Habituellement NULL.
CreateOptionsULONGinDrapeaux, ex. REG_OPTION_NON_VOLATILE, REG_OPTION_VOLATILE, REG_OPTION_CREATE_LINK.
DispositionPULONGoutReçoit REG_CREATED_NEW_KEY ou REG_OPENED_EXISTING_KEY. Peut être NULL.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x1Dwin10-1507
Win10 16070x1Dwin10-1607
Win10 17030x1Dwin10-1703
Win10 17090x1Dwin10-1709
Win10 18030x1Dwin10-1803
Win10 18090x1Dwin10-1809
Win10 19030x1Dwin10-1903
Win10 19090x1Dwin10-1909
Win10 20040x1Dwin10-2004
Win10 20H20x1Dwin10-20h2
Win10 21H10x1Dwin10-21h1
Win10 21H20x1Dwin10-21h2
Win10 22H20x1Dwin10-22h2
Win11 21H20x1Dwin11-21h2
Win11 22H20x1Dwin11-22h2
Win11 23H20x1Dwin11-23h2
Win11 24H20x1Dwin11-24h2
Server 20160x1Dwinserver-2016
Server 20190x1Dwinserver-2019
Server 20220x1Dwinserver-2022
Server 20250x1Dwinserver-2025

Module noyau

ntoskrnl.exeNtCreateKey

APIs liées

RegCreateKeyExWRegOpenKeyExWNtOpenKeyNtOpenKeyExNtSetValueKeyNtDeleteKey

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 ENDP

rustntapi 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