> Windows Syscalls
ntoskrnl.exeT1134.004T1106T1055

NtCreateUserProcess

Crea un nuevo proceso en modo usuario y su hilo inicial a partir de una imagen ejecutable.

Prototipo

NTSTATUS NtCreateUserProcess(
  PHANDLE              ProcessHandle,
  PHANDLE              ThreadHandle,
  ACCESS_MASK          ProcessDesiredAccess,
  ACCESS_MASK          ThreadDesiredAccess,
  POBJECT_ATTRIBUTES   ProcessObjectAttributes,
  POBJECT_ATTRIBUTES   ThreadObjectAttributes,
  ULONG                ProcessFlags,
  ULONG                ThreadFlags,
  PRTL_USER_PROCESS_PARAMETERS ProcessParameters,
  PPS_CREATE_INFO      CreateInfo,
  PPS_ATTRIBUTE_LIST   AttributeList
);

Argumentos

NameTypeDirDescription
ProcessHandlePHANDLEoutRecibe un handle al nuevo objeto proceso.
ThreadHandlePHANDLEoutRecibe un handle al hilo principal del nuevo proceso.
ProcessDesiredAccessACCESS_MASKinMáscara de acceso deseada para el handle de proceso retornado (p. ej. PROCESS_ALL_ACCESS).
ThreadDesiredAccessACCESS_MASKinMáscara de acceso deseada para el handle de hilo retornado (p. ej. THREAD_ALL_ACCESS).
ProcessObjectAttributesPOBJECT_ATTRIBUTESinOBJECT_ATTRIBUTES opcionales para el objeto proceso (nombre, descriptor de seguridad, directorio raíz).
ThreadObjectAttributesPOBJECT_ATTRIBUTESinOBJECT_ATTRIBUTES opcionales para el objeto hilo principal.
ProcessFlagsULONGinPROCESS_CREATE_FLAGS_* (BREAKAWAY, INHERIT_HANDLES, NO_DEBUG_INHERIT, SUSPENDED, ...).
ThreadFlagsULONGinTHREAD_CREATE_FLAGS_* (CREATE_SUSPENDED, SKIP_THREAD_ATTACH, HIDE_FROM_DEBUGGER, ...).
ProcessParametersPRTL_USER_PROCESS_PARAMETERSinBloque de parámetros construido por RtlCreateProcessParametersEx (ruta de imagen, línea de comandos, env, handles estándar).
CreateInfoPPS_CREATE_INFOin/outMáquina de estados PS_CREATE_INFO: en entrada PsCreateInitialState, en salida el estado final más los handles de sección / archivo de imagen abiertos.
AttributeListPPS_ATTRIBUTE_LISTinArray de entradas PS_ATTRIBUTE: ImageName, ParentProcess, MitigationOptions, JobList, Token, ChildProcessPolicy, ...

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070xBAwin10-1507
Win10 16070xBDwin10-1607
Win10 17030xC0win10-1703
Win10 17090xC1win10-1709
Win10 18030xC2win10-1803
Win10 18090xC3win10-1809
Win10 19030xC4win10-1903
Win10 19090xC4win10-1909
Win10 20040xC8win10-2004
Win10 20H20xC8win10-20h2
Win10 21H10xC8win10-21h1
Win10 21H20xC9win10-21h2
Win10 22H20xC9win10-22h2
Win11 21H20xCEwin11-21h2
Win11 22H20xCFwin11-22h2
Win11 23H20xCFwin11-23h2
Win11 24H20xD1win11-24h2
Server 20160xBDwinserver-2016
Server 20190xC3winserver-2019
Server 20220xCDwinserver-2022
Server 20250xD1winserver-2025

Módulo del kernel

ntoskrnl.exeNtCreateUserProcess

APIs relacionadas

CreateProcessWCreateProcessAsUserWCreateProcessWithTokenWCreateProcessWithLogonWNtCreateProcessNtCreateProcessExRtlCreateProcessParametersEx

Stub del syscall

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

NtCreateUserProcess es el punto de entrada de kernel moderno bajo toda la familia Win32 CreateProcess* desde Vista. Reemplaza la coreografía legacy NtCreateProcess / NtCreateProcessEx + NtCreateThread + LdrLoadDll por una transacción de kernel única que abre la imagen, construye la sección, prepara el PEB, aplica mitigaciones e inicia el hilo inicial. El SSN deriva en casi cada build (0xBA → 0xD1 en la ventana soportada) por inserciones/eliminaciones de syscalls vecinos; la resolución dinámica es obligatoria. El prototipo de 11 parámetros está dominado por dos estructuras grandes: PS_CREATE_INFO (máquina de estados que el kernel recorre al parsear la imagen) y PS_ATTRIBUTE_LIST (portadora de todo lo que los parámetros legacy lpStartupInfo / lpProcessAttributes / Token / Job de `CreateProcess` transportaban por separado).

Uso común por malware

Dos abusos destacados. **(1) Spoofing de PPID (T1134.004)**: colocar PS_ATTRIBUTE_PARENT_PROCESS en AttributeList sobre un handle duplicado de cualquier proceso al que se pueda OpenProcess(PROCESS_CREATE_PROCESS). El hijo aparece bajo ese padre en Process Explorer, Sysmon Event ID 1 y árboles de procesos de los EDR — clásico para ocultar un Beacon bajo explorer.exe o services.exe. **(2) Downgrade de mitigaciones / abuso de child-process policy**: PS_ATTRIBUTE_MITIGATION_OPTIONS permite *activar* mitigaciones en el hijo (bloquear DLL no Microsoft para impedir inyección EDR en un proceso auxiliar es un truco red-team documentado) o combinar con PS_ATTRIBUTE_CHILD_PROCESS_POLICY para impedir más hijos, complicando el análisis. Uso más sutil: llamar a NtCreateUserProcess directamente salta kernel32!CreateProcessInternalW, donde muchos EDR cuelgan su hook de telemetría en modo usuario.

Oportunidades de detección

La telemetría de kernel es excelente aquí. PsSetCreateProcessNotifyRoutineEx2 se dispara en cada retorno con un PS_CREATE_NOTIFY_INFO que incluye el PID padre *real* (`CreatingThreadId.UniqueProcess`) independientemente de cualquier spoof PS_ATTRIBUTE_PARENT_PROCESS. Sysmon Event ID 1 registra `ParentProcessGuid` (desde ProcessParameters del PEB) y el creador real derivado del kernel — valores divergentes son una señal de spoof de alta fidelidad. Los eventos ETW `Microsoft-Windows-Kernel-Process/ProcessStart` exponen los mismos campos. Los EDR que solo hookean CreateProcessInternalW o NtCreateProcessEx perderán a los llamadores por syscall directo — verificar ambas superficies.

Ejemplos de syscalls directos

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtCreateUserProcess (SSN 0xD1 on 24H2/2025)
; SSN moves nearly every release — resolve dynamically.
NtCreateUserProcess PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 0D1h         ; SSN
    syscall
    ret
NtCreateUserProcess ENDP

cPPID-spoof skeleton

// Minimal PPID-spoof skeleton: launch notepad.exe as a child of explorer.exe.
// Error handling and full PS_ATTRIBUTE / RTL_USER_PROCESS_PARAMETERS setup omitted for brevity.
#include <windows.h>
#include <winternl.h>

#define PROC_THREAD_ATTRIBUTE_PARENT_PROCESS 0x00020000

void SpoofParent(DWORD parent_pid) {
    SIZE_T attr_size = 0;
    InitializeProcThreadAttributeList(NULL, 1, 0, &attr_size);
    LPPROC_THREAD_ATTRIBUTE_LIST attr =
        (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attr_size);
    InitializeProcThreadAttributeList(attr, 1, 0, &attr_size);

    HANDLE parent = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, parent_pid);
    UpdateProcThreadAttribute(attr, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
                              &parent, sizeof(HANDLE), NULL, NULL);

    STARTUPINFOEXW si = { 0 };
    si.StartupInfo.cb = sizeof si;
    si.lpAttributeList = attr;
    PROCESS_INFORMATION pi;

    // CreateProcess wraps NtCreateUserProcess; the AttributeList carries
    // PS_ATTRIBUTE_PARENT_PROCESS through to the kernel.
    CreateProcessW(L"C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL,
                   FALSE, EXTENDED_STARTUPINFO_PRESENT,
                   NULL, NULL, (LPSTARTUPINFOW)&si, &pi);

    DeleteProcThreadAttributeList(attr);
    CloseHandle(parent);
}

rustblock-non-Microsoft-DLLs mitigation

// Cargo: windows-sys = "0.59"
// Launch a helper process with the Microsoft-signed-DLLs-only mitigation —
// blocks EDR userland DLLs from being injected into the child.
use windows_sys::Win32::System::Threading::*;
use windows_sys::core::PCWSTR;

const PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY: usize = 0x00020007;
const PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_ALWAYS_ON: u64
    = 0x0000_0001_0000_0000;

pub unsafe fn launch_locked_down(image: PCWSTR) {
    let mut attr_size = 0usize;
    InitializeProcThreadAttributeList(core::ptr::null_mut(), 1, 0, &mut attr_size);
    let attr = std::alloc::alloc(
        std::alloc::Layout::from_size_align(attr_size, 8).unwrap()) as *mut _;
    InitializeProcThreadAttributeList(attr, 1, 0, &mut attr_size);

    let mut policy: u64 =
        PROCESS_CREATION_MITIGATION_POLICY2_LOADER_INTEGRITY_CONTINUITY_ALWAYS_ON;
    UpdateProcThreadAttribute(attr, 0,
        PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY,
        &mut policy as *mut _ as _, 8,
        core::ptr::null_mut(), core::ptr::null_mut());

    let mut si: STARTUPINFOEXW = core::mem::zeroed();
    si.StartupInfo.cb = core::mem::size_of_val(&si) as u32;
    si.lpAttributeList = attr;
    let mut pi: PROCESS_INFORMATION = core::mem::zeroed();

    CreateProcessW(image, core::ptr::null_mut(), core::ptr::null(),
                   core::ptr::null(), 0, EXTENDED_STARTUPINFO_PRESENT,
                   core::ptr::null(), core::ptr::null(),
                   &si as *const _ as _, &mut pi);
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20