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

NtCreateProcessEx

Crea un nuevo proceso a partir de un handle de sección sin ejecutar la inicialización ntdll — pieza clave del process hollowing.

Prototipo

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

Argumentos

NameTypeDirDescription
ProcessHandlePHANDLEoutRecibe el handle del nuevo proceso si tiene éxito.
DesiredAccessACCESS_MASKinDerechos de acceso para el handle de proceso devuelto, normalmente PROCESS_ALL_ACCESS.
ObjectAttributesPOBJECT_ATTRIBUTESinAtributos de objeto opcionales (nombre, directorio raíz). Normalmente NULL.
ParentProcessHANDLEinHandle al proceso padre (NtCurrentProcess() para sí mismo). Habilita el spoofing de PID padre cuando apunta a otro proceso.
FlagsULONGinBanderas de creación incluyendo PROCESS_CREATE_FLAGS_INHERIT_HANDLES, PROCESS_CREATE_FLAGS_BREAKAWAY, PROCESS_CREATE_FLAGS_SUSPENDED.
SectionHandleHANDLEinHandle a una sección SEC_IMAGE que respalda la imagen del nuevo proceso. NULL hereda la imagen del padre (usado en combos PPID-spoof + hollow).
DebugPortHANDLEinHandle de puerto de depuración opcional. Casi siempre NULL.
ExceptionPortHANDLEinHandle de puerto de excepciones opcional (mecanismo LPC heredado). Casi siempre NULL.
JobMemberLevelULONGinNivel de anidamiento de job-object cuando el padre forma parte de una jerarquía de jobs; 0 si no aplica.

IDs de syscalls por versión de Windows

Versión 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

Módulo del kernel

ntoskrnl.exeNtCreateProcessEx

APIs relacionadas

CreateProcessWCreateProcessAsUserWNtCreateUserProcessNtCreateSectionNtCreateThreadExRtlCreateProcessParametersEx

Stub del 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

Notas no documentadas

NtCreateProcessEx es el punto de entrada de bajo nivel para crear procesos. A diferencia de NtCreateUserProcess (que ahora usa CreateProcessW), NtCreateProcessEx exige que el llamador haya creado previamente una sección SEC_IMAGE desde el ejecutable objetivo vía NtCreateSection(SEC_IMAGE) y la adjunte. Crucialmente, el proceso resultante no tiene hilo inicial — el llamador debe invocar después NtCreateThreadEx (o NtCreateThread). Esta separación convierte a NtCreateProcessEx en la pieza canónica del *process hollowing* y de la familia *Early Bird APC*: crear un proceso, encolarle trabajo antes de que ejecute su primera instrucción. La SSN `0x4D` es estable en todas las builds soportadas.

Uso común por malware

Usada por cadenas de process hollowing que quieren evitar la telemetría de CreateProcessW, por herramientas de PPID-spoofing (poner ParentProcess en explorer.exe u otro PID de confianza), y por variantes Early Bird que combinan NtCreateProcessEx + NtCreateThreadEx(suspendido) + NtQueueApcThreadEx. Las funciones spawnto/spoof-parent de Cobalt Strike acaban en este camino de código. Menos común como primitiva ofensiva de alto nivel hoy porque NtCreateUserProcess ofrece un control de atributos más rico en un solo syscall.

Oportunidades de detección

ETW Microsoft-Windows-Kernel-Process emite eventos ProcessStart desde PspInsertProcess sin importar el punto de entrada de userland; el kernel no distingue entre NtCreateProcessEx y NtCreateUserProcess en ese callback. Sysmon Event ID 1 se dispara en cualquier caso y expone el PID padre real incluso cuando CreateProcessW habría mentido. Los hooks de userland sobre kernelbase!CreateProcessW pierden a los llamadores por syscalls directos; los hooks deben sentarse sobre ntdll!NtCreateUserProcess *y* ntdll!NtCreateProcessEx. Indicador sospechoso: una sección SEC_IMAGE sobre un PE sin firmar seguida en milisegundos por NtCreateProcessEx y NtCreateThreadEx en estado suspendido.

Ejemplos de syscalls directos

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

Mapeos MITRE ATT&CK

Last verified: 2026-05-20