> Windows Syscalls
ntoskrnl.exeT1055.012T1134.004T1106

NtCreateProcessEx

Crée un nouveau processus à partir d'un handle de section sans exécuter l'initialisation ntdll — brique du process hollowing.

Prototype

NTSTATUS NtCreateProcessEx(
  PHANDLE            ProcessHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes,
  HANDLE             ParentProcess,
  ULONG              Flags,
  HANDLE             SectionHandle,
  HANDLE             DebugPort,
  HANDLE             ExceptionPort,
  ULONG              JobMemberLevel
);

Arguments

NameTypeDirDescription
ProcessHandlePHANDLEoutReçoit le handle du nouveau processus en cas de succès.
DesiredAccessACCESS_MASKinDroits d'accès pour le handle de processus retourné, typiquement PROCESS_ALL_ACCESS.
ObjectAttributesPOBJECT_ATTRIBUTESinAttributs d'objet optionnels (nom, répertoire racine). Généralement NULL.
ParentProcessHANDLEinHandle vers le processus parent (NtCurrentProcess() pour soi-même). Permet le spoofing de PID parent s'il pointe vers un autre processus.
FlagsULONGinDrapeaux de création incluant PROCESS_CREATE_FLAGS_INHERIT_HANDLES, PROCESS_CREATE_FLAGS_BREAKAWAY, PROCESS_CREATE_FLAGS_SUSPENDED.
SectionHandleHANDLEinHandle vers une section SEC_IMAGE qui porte l'image du nouveau processus. NULL hérite de l'image du parent (utilisé dans les combos PPID-spoof + hollow).
DebugPortHANDLEinHandle de port de debug optionnel. Presque toujours NULL.
ExceptionPortHANDLEinHandle de port d'exception optionnel (mécanisme LPC historique). Presque toujours NULL.
JobMemberLevelULONGinNiveau d'imbrication dans une job-object quand le parent fait partie d'une hiérarchie de jobs ; 0 si non pertinent.

IDs de syscalls par version de Windows

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

Module noyau

ntoskrnl.exeNtCreateProcessEx

APIs liées

CreateProcessWCreateProcessAsUserWNtCreateUserProcessNtCreateSectionNtCreateThreadExRtlCreateProcessParametersEx

Stub du syscall

4C 8B D1            mov r10, rcx
B8 4D 00 00 00      mov eax, 0x4D
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

NtCreateProcessEx est le point d'entrée bas-niveau de création de processus. Contrairement à NtCreateUserProcess (utilisé désormais par CreateProcessW), NtCreateProcessEx exige que l'appelant ait pré-créé une section SEC_IMAGE depuis l'exécutable cible via NtCreateSection(SEC_IMAGE) puis la rattache. Surtout, le processus résultant n'a pas de thread initial — l'appelant doit ensuite appeler NtCreateThreadEx (ou NtCreateThread). Cette séparation fait de NtCreateProcessEx la brique canonique du *process hollowing* et de la famille *Early Bird APC* : créer un processus, y poster du travail avant que sa première instruction ne s'exécute. Le SSN `0x4D` est stable sur toutes les builds supportées.

Usage courant par les malwares

Utilisée par les chaînes de process hollowing qui veulent éviter la télémétrie de CreateProcessW, par les outils de PPID-spoofing (positionner ParentProcess sur explorer.exe ou un autre PID de confiance), et par les variantes Early Bird combinant NtCreateProcessEx + NtCreateThreadEx(suspendu) + NtQueueApcThreadEx. Les fonctions spawnto/spoof-parent de Cobalt Strike atteignent au final ce chemin de code. Moins courante comme primitive offensive de haut niveau aujourd'hui car NtCreateUserProcess offre un contrôle d'attributs plus riche en un seul syscall.

Opportunités de détection

ETW Microsoft-Windows-Kernel-Process émet des événements ProcessStart depuis PspInsertProcess indépendamment du point d'entrée userland ; le noyau ne distingue pas NtCreateProcessEx de NtCreateUserProcess à ce callback. Sysmon Event ID 1 se déclenche dans les deux cas et expose le vrai PID parent même quand CreateProcessW aurait menti. Les hooks userland sur kernelbase!CreateProcessW ratent les appelants en syscalls directs ; les hooks doivent être placés sur ntdll!NtCreateUserProcess *et* ntdll!NtCreateProcessEx. Indicateur suspect : une section SEC_IMAGE sur un PE non signé suivie en quelques millisecondes par NtCreateProcessEx puis NtCreateThreadEx en état suspendu.

Exemples de syscalls directs

asmx64 direct stub

; Direct syscall stub for NtCreateProcessEx (SSN 0x4D, stable on Win10 1507+ through Win11 24H2)
NtCreateProcessEx PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 4Dh          ; SSN
    syscall
    ret
NtCreateProcessEx ENDP

cProcess-hollow style: SEC_IMAGE + NtCreateProcessEx

// Open the target PE and wrap it in a SEC_IMAGE section, then spawn a process backed by it.
HANDLE hFile = NULL;
IO_STATUS_BLOCK iosb = { 0 };
OBJECT_ATTRIBUTES oa = { 0 };
UNICODE_STRING path;
RtlInitUnicodeString(&path, L"\\??\\C:\\Windows\\System32\\notepad.exe");
InitializeObjectAttributes(&oa, &path, OBJ_CASE_INSENSITIVE, NULL, NULL);
NtOpenFile(&hFile, FILE_EXECUTE | SYNCHRONIZE, &oa, &iosb,
           FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT);

HANDLE hSection = NULL;
NtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, NULL,
                PAGE_READONLY, SEC_IMAGE, hFile);

HANDLE hProcess = NULL;
NtCreateProcessEx(&hProcess,
                  PROCESS_ALL_ACCESS,
                  NULL,
                  NtCurrentProcess(),     // ParentProcess — set elsewhere for PPID spoof
                  0,                       // Flags
                  hSection,                // SectionHandle
                  NULL,                    // DebugPort
                  NULL,                    // ExceptionPort
                  0);                      // JobMemberLevel
// New process has NO threads yet — caller must NtCreateThreadEx into it.

rustwindows-sys + naked syscall stub

// Cargo: windows-sys = "0.59"
use std::arch::asm;

#[unsafe(naked)]
unsafe extern "system" fn nt_create_process_ex_stub() {
    asm!(
        "mov r10, rcx",
        "mov eax, 0x4D",
        "syscall",
        "ret",
        options(noreturn),
    );
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20