> Windows Syscalls
ntoskrnl.exeT1055T1055.002T1055.012

NtCreateThreadEx

Crea un nuevo hilo en un proceso objetivo, opcionalmente suspendido, con soporte de listas de atributos.

Prototipo

NTSTATUS NtCreateThreadEx(
  PHANDLE              ThreadHandle,
  ACCESS_MASK          DesiredAccess,
  POBJECT_ATTRIBUTES   ObjectAttributes,
  HANDLE               ProcessHandle,
  PVOID                StartRoutine,
  PVOID                Argument,
  ULONG                CreateFlags,
  SIZE_T               ZeroBits,
  SIZE_T               StackSize,
  SIZE_T               MaximumStackSize,
  PPS_ATTRIBUTE_LIST   AttributeList
);

Argumentos

NameTypeDirDescription
ThreadHandlePHANDLEoutRecibe el handle del hilo recién creado.
DesiredAccessACCESS_MASKinDerechos del handle devuelto, normalmente THREAD_ALL_ACCESS para control total.
ObjectAttributesPOBJECT_ATTRIBUTESinAtributos de objeto para el nuevo hilo (normalmente NULL).
ProcessHandleHANDLEinHandle al proceso objetivo que alojará el hilo.
StartRoutinePVOIDinDirección del punto de entrada del hilo en el proceso objetivo.
ArgumentPVOIDinArgumento único del tamaño de un puntero pasado a StartRoutine.
CreateFlagsULONGinBanderas. THREAD_CREATE_FLAGS_CREATE_SUSPENDED (0x1) es con mucho la más común.
ZeroBitsSIZE_TinNúmero de bits altos en cero en la base de la pila (típicamente 0).
StackSizeSIZE_TinTamaño inicial de pila confirmado; 0 deja que el kernel elija el predeterminado.
MaximumStackSizeSIZE_TinTamaño máximo reservado de la pila; 0 deja que el kernel elija el predeterminado.
AttributeListPPS_ATTRIBUTE_LISTinLista opcional de entradas PS_ATTRIBUTE (CLIENT_ID, dirección TEB, etc.). NULL para valores por defecto.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070xB3win10-1507
Win10 16070xB6win10-1607
Win10 17030xB9win10-1703
Win10 17090xBAwin10-1709
Win10 18030xBBwin10-1803
Win10 18090xBCwin10-1809
Win10 19030xBDwin10-1903
Win10 19090xBDwin10-1909
Win10 20040xC1win10-2004
Win10 20H20xC1win10-20h2
Win10 21H10xC1win10-21h1
Win10 21H20xC2win10-21h2
Win10 22H20xC2win10-22h2
Win11 21H20xC6win11-21h2
Win11 22H20xC7win11-22h2
Win11 23H20xC7win11-23h2
Win11 24H20xC9win11-24h2
Server 20160xB6winserver-2016
Server 20190xBCwinserver-2019
Server 20220xC5winserver-2022
Server 20250xC9winserver-2025

Módulo del kernel

ntoskrnl.exeNtCreateThreadEx

APIs relacionadas

CreateRemoteThreadCreateRemoteThreadExCreateThreadNtCreateThreadRtlCreateUserThreadNtResumeThread

Stub del syscall

4C 8B D1            mov r10, rcx
B8 C2 00 00 00      mov eax, 0xC2     ; win10-22h2 / win11-21h2 differ
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

A diferencia de muchos vecinos, la SSN de `NtCreateThreadEx` *cambia en cada actualización de funcionalidades*: `0xC2` en Win10 21H2/22H2, `0xC6` en Win11 21H2, `0xC7` en 22H2/23H2, `0xC9` en 24H2 y Server 2025. Codificar este número es frágil — se recomienda encarecidamente la resolución dinámica vía Hell's Gate o Halo's Gate. Es la bestia de carga del kernel detrás de `CreateRemoteThread`, `CreateRemoteThreadEx` y `CreateThread`; el antiguo `NtCreateThread` está esencialmente obsoleto. Pasar una `PS_ATTRIBUTE_LIST` con `PsAttributeIdealProcessor` o `PsAttributeClientId` permite fijar el hilo o asignarle un CID falsificado.

Uso común por malware

El paso de ejecución en la inyección remota clásica: tras `NtAllocateVirtualMemory` + `NtWriteVirtualMemory` + `NtProtectVirtualMemory`, este syscall es lo que realmente ejecuta el payload en el proceso remoto. A menudo se invoca con `CREATE_SUSPENDED` para que el implante instale un `CONTEXT` personalizado (PEB walking, stack spoof) vía `NtSetContextThread` antes de `NtResumeThread`. Variantes: usar `LoadLibraryA` como `StartRoutine` (inyección DLL de la vieja escuela), señuelos `RtlExitUserThread` o una cadena JOP/ROP que pivota dentro del objetivo.

Oportunidades de detección

Sysmon Event ID 8 (`CreateRemoteThread`) es el evento estrella — se dispara siempre que los procesos origen y destino difieren, con `SourceImage`, `TargetImage`, `StartAddress` y un hash de pila. ETW Threat Intelligence emite `EtwTiLogCreateThread` para la creación de hilo remoto y lo consumen los principales EDR. Los callbacks del kernel `PsSetCreateThreadNotifyRoutine`/`PsSetCreateThreadNotifyRoutineEx` se disparan de forma síncrona y los syscalls directos de userland no pueden eludirlos. Una `StartAddress` que no resuelve a una imagen cargada (es decir, vive en memoria privada) es la señal individual más fuerte.

Ejemplos de syscalls directos

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtCreateThreadEx (SSN 0xC9 on Win11 24H2 / Server 2025).
; WARNING: this SSN moves between feature updates — prefer dynamic resolution.
NtCreateThreadEx PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 0C9h         ; SSN for win11-24h2
    syscall
    ret
NtCreateThreadEx ENDP

cSuspended remote thread + context patch

// Spawn the thread suspended so we can rewrite RIP / RSP before resuming.
HANDLE  hThread = NULL;
NTSTATUS st = NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS, NULL,
                               hRemote, payload_rva, NULL,
                               THREAD_CREATE_FLAGS_CREATE_SUSPENDED,
                               0, 0, 0, NULL);
if (!NT_SUCCESS(st)) return st;

CONTEXT ctx = { .ContextFlags = CONTEXT_FULL };
NtGetContextThread(hThread, &ctx);
ctx.Rip = (DWORD64)real_entry;        // pivot away from the decoy StartRoutine
NtSetContextThread(hThread, &ctx);
NtResumeThread(hThread, NULL);

cHell's Gate dynamic lookup

// Hardcoding 0xC2/0xC6/0xC7/0xC9 across builds is fragile — resolve at runtime.
typedef NTSTATUS (NTAPI *pNtCreateThreadEx)(
    PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, HANDLE, PVOID, PVOID,
    ULONG, SIZE_T, SIZE_T, SIZE_T, PVOID);

DWORD ssn = GetSyscallNumber(GetProcAddress(GetModuleHandleA("ntdll.dll"),
                                            "NtCreateThreadEx"));
set_ssn(ssn);
indirect_syscall_invoke(/* ... */);

Mapeos MITRE ATT&CK

Last verified: 2026-05-20