> Windows Syscalls
ntoskrnl.exeT1055.012T1055T1106

NtCreateProcess

Legacy-NT-Prozesserzeugung aus einem vorab gebauten Section-Objekt — Vorgänger von NtCreateProcessEx und NtCreateUserProcess.

Prototyp

NTSTATUS NtCreateProcess(
  PHANDLE            ProcessHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes,
  HANDLE             ParentProcess,
  BOOLEAN            InheritObjectTable,
  HANDLE             SectionHandle,
  HANDLE             DebugPort,
  HANDLE             ExceptionPort
);

Argumente

NameTypeDirDescription
ProcessHandlePHANDLEoutEmpfängt das Handle auf den neu erstellten Prozess.
DesiredAccessACCESS_MASKinAngeforderte Prozess-Zugriffsmaske (PROCESS_ALL_ACCESS für vollen Zugriff).
ObjectAttributesPOBJECT_ATTRIBUTESinOptionaler Object-Manager-Name und Security-Descriptor.
ParentProcessHANDLEinHandle auf den Eltern-Prozess. PEB und vererbbare Handles werden von ihm geklont.
InheritObjectTableBOOLEANinTRUE, um die Handle-Tabelle des Elternprozesses zu erben; FALSE für eine leere Tabelle.
SectionHandleHANDLEinHandle auf eine SEC_IMAGE-Image-Section, die zum primären Image des Prozesses wird.
DebugPortHANDLEinOptionaler Debug-Port (LPC/ALPC), der DBG-Events empfängt; meist NULL.
ExceptionPortHANDLEinOptionaler Exception-Port für Second-Chance-Exceptions; auf modernem Windows meist NULL.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070xADwin10-1507
Win10 16070xAFwin10-1607
Win10 17030xB2win10-1703
Win10 17090xB3win10-1709
Win10 18030xB4win10-1803
Win10 18090xB4win10-1809
Win10 19030xB5win10-1903
Win10 19090xB5win10-1909
Win10 20040xB9win10-2004
Win10 20H20xB9win10-20h2
Win10 21H10xB9win10-21h1
Win10 21H20xBAwin10-21h2
Win10 22H20xBAwin10-22h2
Win11 21H20xBDwin11-21h2
Win11 22H20xBEwin11-22h2
Win11 23H20xBEwin11-23h2
Win11 24H20xC0win11-24h2
Server 20160xAFwinserver-2016
Server 20190xB4winserver-2019
Server 20220xBCwinserver-2022
Server 20250xC0winserver-2025

Kernel-Modul

ntoskrnl.exeNtCreateProcess

Verwandte APIs

CreateProcessWCreateProcessAsUserWNtCreateProcessExNtCreateUserProcessNtCreateSectionNtCreateThreadCsrClientCallServer

Syscall-Stub

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

Der *originale* NT-Prozesserzeugungs-Syscall — seit Windows NT 3.1 vorhanden. Es ist die niedrigststufige Primitive: der Aufrufer muss bereits die Image-Section erstellt haben (`NtCreateSection(SEC_IMAGE, …)`), und der Kernel baut nur das EPROCESS/KPROCESS, kopiert das PEB-Skelett des Elternteils und klont den Adressraum. Er erzeugt **keinen** Initial-Thread (Folge-Aufruf mit `NtCreateThread` erforderlich), lädt **keine** DLL über das Hauptimage hinaus und ruft **kein** CSRSS, um den neuen Prozess zu registrieren — der entstehende Prozess ist also **noch kein Win32-Prozess**. Um einen Konsolen-/Win32-sichtbaren Prozess zu bekommen, muss man `CsrClientCallServer(BasepCreateProcess)` manuell aufrufen. `NtCreateProcessEx` (Windows XP+) ergänzte `JobMemberLevel` und Section-lose Erzeugung; `NtCreateUserProcess` (Vista+) ersetzte beide als One-Shot-CreateProcess-Implementierung, die Section, Thread, PEB, CSRSS, AppContainer und Mitigation-Policies in einem einzigen Syscall erledigt. Das moderne `kernel32!CreateProcessW` ruft ausschließlich `NtCreateUserProcess` — `NtCreateProcess` ist nur durch direkten Aufruf erreichbar.

Häufige Malware-Nutzung

Historisch (vor 2010ern) in **manuellen Process-Hollowing**-Ketten genutzt, die maximale Kontrolle über PEB-Initialisierung, Mitigation-Policy-Bypass oder Evasion gegen Werkzeuge wollten, die `CreateProcessW` hookten, aber nicht die untere NT-Schicht. Das kanonische Muster: `NtCreateSection(SEC_IMAGE)` auf einer harmlosen Zielbinary → `NtCreateProcess` mit dieser Section als Image → `NtCreateThread` am Entry-Point des neuen Images. Weil der entstehende Prozess nie mit CSRSS spricht, fehlt ihm ein Konsolen-Subsystem-Eintrag, er erscheint nicht in `EnumProcesses`, bis die CSRSS-Benachrichtigung folgt, und auf Legacy-Systemen war er für ein kurzes Zeitfenster effektiv unsichtbar für den Task Manager. Moderne `Process Notify Routine`-Callbacks (`PsSetCreateProcessNotifyRoutineEx`) feuern aus `PspInsertProcess` unabhängig vom User-Mode-Wrapper, der Sichtbarkeits-Evasion-Vorteil ist also weg, und `NtCreateUserProcess` wird universell bevorzugt. Heute sind Aufrufe von `NtCreateProcess` aus anderem als einer Handvoll MS-Testbinaries und gewissen Cygwin-/MSYS2-`fork()`-Emulationen im Wesentlichen ein Malware-Fingerprint.

Erkennungs­möglichkeiten

Das stärkste Einzelsignal ist *jede* Invokation außerhalb einer bekannten Allowlist — Cygwins `cygwin1.dll`, MSYS2 `msys-2.0.dll`, die WSL1-LXSS-Schicht und Microsofts eigene NT-Subsystem-Testtools. Kernel-Callbacks `PsSetCreateProcessNotifyRoutineEx` erhalten ein `PS_CREATE_NOTIFY_INFO` mit `FileObject != NULL` nur, wenn das Image vom Kernel geöffnet wurde — Aufrufer von `NtCreateProcess` liefern die Section selbst, daher ist `ImageFileName` die Quelldatei der Section und der *aufrufende* Kontext sagt, wer sie gebaut hat. ETW `Microsoft-Windows-Kernel-Process` Event ID 1 feuert einheitlich; korrelieren Sie das `CreateRoutine`-Feld (im Kernel-Stack der Thread-Ansicht sichtbar), um `NtCreateUserProcess` von `NtCreateProcessEx` / `NtCreateProcess` zu unterscheiden. Jedes Sysmon-Event 1, dessen ParentImage ungewöhnlich ist und dessen `OriginalFileName` nicht mit `ImageFileName` übereinstimmt, ist ein starkes Signal.

Direkte Syscall-Beispiele

cMinimal legacy hollow-skeleton (NtCreateSection -> NtCreateProcess -> NtCreateThread)

// Minimal legacy-style hollow. Modern EDR sees the PspInsertProcess callback
// regardless, so this is documented for reverse-engineers, not as a working
// evasion in 2026.
#include <windows.h>
#include <winternl.h>

typedef NTSTATUS (NTAPI* pNtCreateProcess)(
    PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, HANDLE, BOOLEAN,
    HANDLE, HANDLE, HANDLE);

void legacy_hollow(HANDLE hImageSection) {
    pNtCreateProcess NtCreateProcess = (pNtCreateProcess)GetProcAddress(
        GetModuleHandleA("ntdll.dll"), "NtCreateProcess");

    HANDLE hProc = NULL;
    NTSTATUS st = NtCreateProcess(
        &hProc,
        PROCESS_ALL_ACCESS,
        NULL,
        NtCurrentProcess(),   // parent = self → inherits address-space template
        FALSE,                // do not inherit handles
        hImageSection,        // SEC_IMAGE backing
        NULL,                 // no debug port
        NULL);                // no exception port

    // At this point the process exists but has zero threads and has not been
    // announced to CSRSS. Caller now creates the initial thread at the image
    // entry point with NtCreateThread.
}

asmx64 direct stub (Win11 24H2 / Server 2025, SSN 0xC0)

NtCreateProcess PROC
    mov  r10, rcx
    mov  eax, 0C0h
    syscall
    ret
NtCreateProcess ENDP

rustResolve and call via Halo's Gate

// Hardcoded SSNs drift; resolve at runtime by walking ntdll exports.
use windows_sys::Win32::Foundation::HANDLE;
use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};

unsafe fn nt_create_process_ssn() -> u32 {
    let h = GetModuleHandleA(b"ntdll.dll\0".as_ptr());
    let p = GetProcAddress(h, b"NtCreateProcess\0".as_ptr()) as *const u8;
    // mov r10, rcx ; mov eax, IMM32 ; ...
    if p.read() == 0x4C && p.add(1).read() == 0x8B && p.add(2).read() == 0xD1 && p.add(3).read() == 0xB8 {
        u32::from_le_bytes([p.add(4).read(), p.add(5).read(), p.add(6).read(), p.add(7).read()])
    } else { 0 }
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20