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

NtCreateProcessEx

Creates a new process from a section handle without running ntdll process initialization — building block of 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
ProcessHandlePHANDLEoutReceives the handle to the new process on success.
DesiredAccessACCESS_MASKinAccess rights for the returned process handle, typically PROCESS_ALL_ACCESS.
ObjectAttributesPOBJECT_ATTRIBUTESinOptional object attributes (name, root directory). Usually NULL.
ParentProcessHANDLEinHandle to the parent process (NtCurrentProcess() for self-parent). Enables parent PID spoofing when set to another process.
FlagsULONGinCreation flags including PROCESS_CREATE_FLAGS_INHERIT_HANDLES, PROCESS_CREATE_FLAGS_BREAKAWAY, PROCESS_CREATE_FLAGS_SUSPENDED.
SectionHandleHANDLEinHandle to a SEC_IMAGE section that backs the new process image. NULL inherits the parent's image (used in PPID-spoof + hollow tricks).
DebugPortHANDLEinOptional debug port handle. Almost always NULL.
ExceptionPortHANDLEinOptional exception port handle (legacy LPC mechanism). Almost always NULL.
JobMemberLevelULONGinJob-object nesting level when the parent is part of a job hierarchy; 0 if not relevant.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
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

Kernel module

ntoskrnl.exeNtCreateProcessEx

Related APIs

CreateProcessWCreateProcessAsUserWNtCreateUserProcessNtCreateSectionNtCreateThreadExRtlCreateProcessParametersEx

Syscall stub

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

Undocumented notes

NtCreateProcessEx is the bare-metal process-creation entry point. Unlike NtCreateUserProcess (which CreateProcessW now uses), NtCreateProcessEx requires the caller to pre-create a SEC_IMAGE section from the target executable via NtCreateSection(SEC_IMAGE) and then attach it. Crucially, the resulting process has no initial thread — the caller must subsequently call NtCreateThreadEx (or NtCreateThread). This split makes NtCreateProcessEx the canonical building block of *process hollowing* and the *Early Bird APC* family of techniques: create a process, queue work in it before its first instruction runs. The SSN `0x4D` is stable across all supported builds.

Common malware usage

Used by process-hollowing chains that want to skip CreateProcessW telemetry, by PPID-spoofing tooling (set ParentProcess to explorer.exe or another trusted PID), and by Early Bird injection variants combining NtCreateProcessEx + NtCreateThreadEx(suspended) + NtQueueApcThreadEx. Cobalt Strike's spawnto/spoof-parent features ultimately reach this code path. Less common as a top-level offensive primitive today because NtCreateUserProcess offers richer attribute control with a single syscall.

Detection opportunities

ETW Microsoft-Windows-Kernel-Process emits ProcessStart events from PspInsertProcess regardless of the userland entry point; the kernel does not distinguish between NtCreateProcessEx and NtCreateUserProcess at that callback. Sysmon Event ID 1 fires either way and exposes the real parent PID even when CreateProcessW would have lied. Userland-only EDR hooks on kernelbase!CreateProcessW will miss direct-syscall callers; hooks should sit on ntdll!NtCreateUserProcess *and* ntdll!NtCreateProcessEx. Suspicious indicator: a SEC_IMAGE section over an unsigned PE followed within milliseconds by NtCreateProcessEx and NtCreateThreadEx in suspended state.

Direct syscall examples

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),
    );
}

MITRE ATT&CK mappings

Last verified: 2026-05-20