> Windows Syscalls
ntoskrnl.exeT1055T1055.002T1055.012

NtCreateThreadEx

Crée un nouveau thread dans un processus cible, éventuellement suspendu, avec support des listes d'attributs.

Prototype

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

Arguments

NameTypeDirDescription
ThreadHandlePHANDLEoutReçoit le handle du thread nouvellement créé.
DesiredAccessACCESS_MASKinDroits du handle retourné, généralement THREAD_ALL_ACCESS pour un contrôle complet.
ObjectAttributesPOBJECT_ATTRIBUTESinAttributs d'objet pour le nouveau thread (généralement NULL).
ProcessHandleHANDLEinHandle vers le processus cible qui hébergera le thread.
StartRoutinePVOIDinAdresse du point d'entrée du thread dans le processus cible.
ArgumentPVOIDinArgument unique de la taille d'un pointeur transmis à StartRoutine.
CreateFlagsULONGinDrapeaux. THREAD_CREATE_FLAGS_CREATE_SUSPENDED (0x1) est de loin le plus courant.
ZeroBitsSIZE_TinNombre de bits de poids fort à zéro dans la base de pile (typiquement 0).
StackSizeSIZE_TinTaille initiale de pile validée ; 0 laisse le noyau choisir la valeur par défaut.
MaximumStackSizeSIZE_TinTaille maximale réservée pour la pile ; 0 laisse le noyau choisir la valeur par défaut.
AttributeListPPS_ATTRIBUTE_LISTinListe optionnelle d'entrées PS_ATTRIBUTE (CLIENT_ID, adresse TEB, etc.). NULL par défaut.

IDs de syscalls par version de Windows

Version 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

Module noyau

ntoskrnl.exeNtCreateThreadEx

APIs liées

CreateRemoteThreadCreateRemoteThreadExCreateThreadNtCreateThreadRtlCreateUserThreadNtResumeThread

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

Notes non documentées

Contrairement à beaucoup de ses voisins, `NtCreateThreadEx` a un SSN qui *change à chaque mise à jour de fonctionnalité* : `0xC2` sur Win10 21H2/22H2, `0xC6` sur Win11 21H2, `0xC7` sur 22H2/23H2, `0xC9` sur 24H2 et Server 2025. Coder en dur ce numéro est fragile — la résolution dynamique via Hell's Gate ou Halo's Gate est fortement recommandée. C'est la bête de somme côté noyau derrière `CreateRemoteThread`, `CreateRemoteThreadEx` et `CreateThread` ; l'ancien `NtCreateThread` est essentiellement obsolète. Passer une `PS_ATTRIBUTE_LIST` avec `PsAttributeIdealProcessor` ou `PsAttributeClientId` permet d'épingler le thread ou de lui assigner un CID forgé.

Usage courant par les malwares

L'étape d'exécution de l'injection distante classique : après `NtAllocateVirtualMemory` + `NtWriteVirtualMemory` + `NtProtectVirtualMemory`, ce syscall est ce qui lance réellement le payload dans le processus distant. Souvent appelé avec `CREATE_SUSPENDED` pour que l'implant installe un `CONTEXT` personnalisé (PEB walking, stack spoof) via `NtSetContextThread` avant `NtResumeThread`. Les variantes incluent `LoadLibraryA` comme `StartRoutine` (injection DLL old-school), des leurres `RtlExitUserThread` ou une chaîne JOP/ROP qui pivote dans la cible.

Opportunités de détection

Sysmon Event ID 8 (`CreateRemoteThread`) est l'événement phare — il se déclenche dès que les processus source et cible diffèrent, avec `SourceImage`, `TargetImage`, `StartAddress` et un hash de pile. ETW Threat Intelligence émet `EtwTiLogCreateThread` pour la création de thread distant et est consommé par les principaux EDR. Les callbacks noyau `PsSetCreateThreadNotifyRoutine`/`PsSetCreateThreadNotifyRoutineEx` se déclenchent synchroniquement et ne peuvent être contournés par des syscalls directs userland. Une `StartAddress` qui ne pointe pas vers une image chargée (donc en mémoire privée) est le signal unique le plus fort.

Exemples de syscalls directs

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(/* ... */);

Mappings MITRE ATT&CK

Last verified: 2026-05-20