> Windows Syscalls
ntoskrnl.exeT1055T1106

NtCreateThread

Legacy-Syscall zur Thread-Erzeugung mit manuell aufgebauter INITIAL_TEB; abgelöst durch NtCreateThreadEx.

Prototyp

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

Argumente

NameTypeDirDescription
ThreadHandlePHANDLEoutErhält das Handle des neu erzeugten Threads bei Erfolg.
DesiredAccessACCESS_MASKinZugriffsrechte für das zurückgegebene Thread-Handle, typischerweise THREAD_ALL_ACCESS.
ObjectAttributesPOBJECT_ATTRIBUTESinOptionale Objektattribute; bei der Thread-Erzeugung fast immer NULL.
ProcessHandleHANDLEinHandle auf den Zielprozess, dem der Thread gehören wird (PROCESS_CREATE_THREAD erforderlich).
ClientIdPCLIENT_IDoutErhält das (PID, TID)-Paar zur Identifikation des neuen Threads.
ThreadContextPCONTEXTinVollständig gefüllte CONTEXT-Struktur mit RIP/RSP für den neuen Thread — vom Aufrufer bereitgestellter Registerzustand.
InitialTebPINITIAL_TEBinManuell aufgebaute INITIAL_TEB, die den User-Stack beschreibt (Base, Limit, Allocation Base). Der Grund, warum diese API mühsam ist.
CreateSuspendedBOOLEANinBei TRUE wird der Thread suspendiert erzeugt und muss mit NtResumeThread fortgesetzt werden.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
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

Kernel-Modul

ntoskrnl.exeNtCreateThread

Verwandte APIs

CreateThreadCreateRemoteThreadRtlCreateUserThreadNtCreateThreadExNtResumeThread

Syscall-Stub

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

Undokumentierte Hinweise

NtCreateThread ist die ursprüngliche, vor Vista eingeführte Thread-Erzeugungsprimitive. Im Gegensatz zu ihrem Nachfolger NtCreateThreadEx ist sie in den öffentlichen Headern undokumentiert und zwingt den Aufrufer, eine INITIAL_TEB von Hand aufzubauen — mit Stack-Base, Stack-Limit und Allocation-Base — sowie einen vollständig gefüllten CONTEXT mit RIP/RSP. Die SSN `0x4E` ist über alle unterstützten Windows 10- und Windows 11-Builds stabil geblieben, was ungewöhnlich ist und widerspiegelt, dass Microsoft an dieser Oberfläche nichts mehr ändert. Der Kernel implementiert sie weiterhin aus Kompatibilitätsgründen, doch Win32 CreateThread, RtlCreateUserThread und NtCreateThreadEx dispatchen heute alle über NtCreateThreadEx.

Häufige Malware-Nutzung

Verwendet von älteren Injection-Tools, die vor den Attribute-Listen von NtCreateThreadEx entstanden — einige PassTheHash-Varianten, frühe Token-Stealing-Primitiven von Mimikatz und historische Alternativen zu CreateRemoteThread. Eine kleine Untermenge moderner Offensiv-Tools wählt NtCreateThread bewusst, weil so wenige EDRs sie hooken: die API wird von legitimer Software so selten genutzt, dass die Herstellerabdeckung lückenhaft ist und sie zu einem Nischen-Bypass gegen naive Userland-Hook-Stacks wird. Der Preis ist, dass der Aufrufer im Zielprozess einen Stack allokieren und zusammenfügen muss, bevor er den Aufruf absetzt.

Erkennungs­möglichkeiten

ETW Microsoft-Windows-Threat-Intelligence emittiert ThreadCreate-Events aus PspInsertThread heraus, unabhängig davon, ob der Userland-Einstiegspunkt NtCreateThread oder NtCreateThreadEx war — die kernelseitige Telemetrie ist also identisch. Sysmon Event ID 8 (CreateRemoteThread) feuert ebenfalls bei jeder prozessübergreifenden Thread-Injektion. Der einzige sinnvolle Diskriminator liegt im Userland: ein EDR, das nur ntdll!NtCreateThreadEx hookt, übersieht Pfade über NtCreateThread. Verteidiger sollten beide Stubs instrumentieren und die seltene Kombination NtCreateThread + Remote-ProcessHandle beobachten.

Direkte Syscall-Beispiele

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

MITRE ATT&CK-Mappings

Last verified: 2026-05-20