> Windows Syscalls
ntoskrnl.exeT1055T1106

NtCreateThread

Syscall heredado de creación de hilos que requiere un INITIAL_TEB construido manualmente; sustituido por NtCreateThreadEx.

Prototipo

NTSTATUS NtCreateThread(
  PHANDLE            ThreadHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes,
  HANDLE             ProcessHandle,
  PCLIENT_ID         ClientId,
  PCONTEXT           ThreadContext,
  PINITIAL_TEB       InitialTeb,
  BOOLEAN            CreateSuspended
);

Argumentos

NameTypeDirDescription
ThreadHandlePHANDLEoutRecibe el handle del hilo recién creado si tiene éxito.
DesiredAccessACCESS_MASKinDerechos de acceso para el handle de hilo devuelto, normalmente THREAD_ALL_ACCESS.
ObjectAttributesPOBJECT_ATTRIBUTESinAtributos de objeto opcionales; casi siempre NULL para la creación de hilos.
ProcessHandleHANDLEinHandle al proceso objetivo que poseerá el hilo (PROCESS_CREATE_THREAD requerido).
ClientIdPCLIENT_IDoutRecibe el par (PID, TID) que identifica al hilo recién creado.
ThreadContextPCONTEXTinEstructura CONTEXT totalmente poblada con RIP/RSP para el nuevo hilo — estado inicial de registros provisto por el llamador.
InitialTebPINITIAL_TEBinINITIAL_TEB construido manualmente que describe la pila de usuario (base, límite, base de asignación). Por eso esta API es engorrosa.
CreateSuspendedBOOLEANinSi TRUE el hilo se crea suspendido y debe reanudarse con NtResumeThread.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x4Ewin10-1507
Win10 16070x4Ewin10-1607
Win10 17030x4Ewin10-1703
Win10 17090x4Ewin10-1709
Win10 18030x4Ewin10-1803
Win10 18090x4Ewin10-1809
Win10 19030x4Ewin10-1903
Win10 19090x4Ewin10-1909
Win10 20040x4Ewin10-2004
Win10 20H20x4Ewin10-20h2
Win10 21H10x4Ewin10-21h1
Win10 21H20x4Ewin10-21h2
Win10 22H20x4Ewin10-22h2
Win11 21H20x4Ewin11-21h2
Win11 22H20x4Ewin11-22h2
Win11 23H20x4Ewin11-23h2
Win11 24H20x4Ewin11-24h2
Server 20160x4Ewinserver-2016
Server 20190x4Ewinserver-2019
Server 20220x4Ewinserver-2022
Server 20250x4Ewinserver-2025

Módulo del kernel

ntoskrnl.exeNtCreateThread

APIs relacionadas

CreateThreadCreateRemoteThreadRtlCreateUserThreadNtCreateThreadExNtResumeThread

Stub del syscall

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

NtCreateThread es la primitiva original de creación de hilos, anterior a Vista. A diferencia de su sucesor NtCreateThreadEx, no está documentada en los encabezados públicos y obliga al llamador a construir manualmente un INITIAL_TEB — describiendo base, límite y base de asignación de la pila — más un CONTEXT totalmente poblado con RIP/RSP. El SSN `0x4E` ha permanecido estable en todas las builds soportadas de Windows 10 y Windows 11, lo cual es inusual y refleja que Microsoft ya no toca esta superficie. El kernel sigue implementándola por compatibilidad pero hoy CreateThread, RtlCreateUserThread y NtCreateThreadEx pasan todos por NtCreateThreadEx.

Uso común por malware

Usada por herramientas de inyección antiguas anteriores a las listas de atributos de NtCreateThreadEx — algunas variantes de PassTheHash, las primeras primitivas de robo de token de Mimikatz y alternativas históricas a CreateRemoteThread. Una pequeña parte del herramental ofensivo moderno elige NtCreateThread precisamente porque pocos EDR la hookean: la API se invoca tan rara vez por código legítimo que la cobertura de los proveedores es irregular, lo que la convierte en un bypass de nicho contra pilas de hooks de userland ingenuas. La contrapartida es que el llamador debe asignar y montar una pila en el proceso objetivo antes de emitir la llamada.

Oportunidades de detección

ETW Microsoft-Windows-Threat-Intelligence emite eventos ThreadCreate desde PspInsertThread sin importar si el punto de entrada de userland fue NtCreateThread o NtCreateThreadEx — la telemetría a nivel kernel es idéntica. Sysmon Event ID 8 (CreateRemoteThread) también se dispara para cualquier inyección de hilo entre procesos. El único discriminador útil está en userland: un EDR que solo hookee ntdll!NtCreateThreadEx perderá las rutas que pasan por NtCreateThread. Los defensores deben asegurarse de instrumentar ambos stubs y vigilar la rara combinación de NtCreateThread + ProcessHandle remoto.

Ejemplos de syscalls directos

asmx64 direct stub

; Direct syscall stub for NtCreateThread (SSN 0x4E, Win10 1507+ through Win11 24H2)
NtCreateThread PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 4Eh          ; SSN
    syscall
    ret
NtCreateThread ENDP

cLegacy remote thread with hand-built INITIAL_TEB

// Caller must allocate stack in the target process and populate both CONTEXT and INITIAL_TEB.
typedef struct _INITIAL_TEB {
    PVOID PreviousStackBase;
    PVOID PreviousStackLimit;
    PVOID StackBase;
    PVOID StackLimit;
    PVOID AllocatedStackBase;
} INITIAL_TEB, *PINITIAL_TEB;

SIZE_T stackSize = 0x10000;
PVOID  remoteStack = NULL;
NtAllocateVirtualMemory(hProcess, &remoteStack, 0, &stackSize,
                        MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

INITIAL_TEB teb = { 0 };
teb.StackBase          = (PBYTE)remoteStack + stackSize;
teb.StackLimit         = remoteStack;
teb.AllocatedStackBase = remoteStack;

CONTEXT ctx = { 0 };
ctx.ContextFlags = CONTEXT_FULL;
ctx.Rip = (DWORD64)pShellcode;
ctx.Rsp = (DWORD64)teb.StackBase - 0x28;

HANDLE   hThread = NULL;
CLIENT_ID cid    = { 0 };
NtCreateThread(&hThread, THREAD_ALL_ACCESS, NULL, hProcess,
               &cid, &ctx, &teb, FALSE);

rustwindows-sys + naked syscall stub

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

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

Mapeos MITRE ATT&CK

Last verified: 2026-05-20