> Windows Syscalls
ntoskrnl.exeT1055.003T1057T1106

NtOpenThread

Öffnet ein Handle auf einen vorhandenen Thread, identifiziert per CLIENT_ID, mit den gewünschten Zugriffsrechten.

Prototyp

NTSTATUS NtOpenThread(
  PHANDLE            ThreadHandle,
  ACCESS_MASK        DesiredAccess,
  POBJECT_ATTRIBUTES ObjectAttributes,
  PCLIENT_ID         ClientId
);

Argumente

NameTypeDirDescription
ThreadHandlePHANDLEoutEmpfängt bei Erfolg das geöffnete Thread-Handle.
DesiredAccessACCESS_MASKinKombination von THREAD_*-Rechten (z. B. THREAD_SUSPEND_RESUME, THREAD_GET_CONTEXT, THREAD_SET_CONTEXT, THREAD_ALL_ACCESS).
ObjectAttributesPOBJECT_ATTRIBUTESinObjektattribute. ObjectName ist üblicherweise NULL; OBJ_CASE_INSENSITIVE in Attributes setzen.
ClientIdPCLIENT_IDinZeiger auf CLIENT_ID { UniqueProcess, UniqueThread }. UniqueProcess kann NULL sein, wenn UniqueThread systemweit eindeutig ist.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x119win10-1507
Win10 16070x11Fwin10-1607
Win10 17030x123win10-1703
Win10 17090x125win10-1709
Win10 18030x127win10-1803
Win10 18090x128win10-1809
Win10 19030x129win10-1903
Win10 19090x129win10-1909
Win10 20040x12Ewin10-2004
Win10 20H20x12Ewin10-20h2
Win10 21H10x12Ewin10-21h1
Win10 21H20x12Fwin10-21h2
Win10 22H20x12Fwin10-22h2
Win11 21H20x135win11-21h2
Win11 22H20x137win11-22h2
Win11 23H20x137win11-23h2
Win11 24H20x139win11-24h2
Server 20160x11Fwinserver-2016
Server 20190x128winserver-2019
Server 20220x134winserver-2022
Server 20250x139winserver-2025

Kernel-Modul

ntoskrnl.exeNtOpenThread

Verwandte APIs

OpenThreadNtGetNextThreadNtOpenProcessNtSuspendThreadNtGetContextThreadNtSetContextThread

Syscall-Stub

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

NtOpenThread ist der obligatorische erste Schritt für jede Cross-Thread-Manipulation ohne vorhandenes Handle. Die CLIENT_ID-Struktur macht es mächtig: ein Thread lässt sich allein über TID angeben (UniqueProcess = NULL), und der Kernel löst ihn systemweit auf. Die gewährte Access-Mask wird durch PsThreadType->ValidAccessMask und etwaige ObRegisterCallbacks gefiltert. Die meisten EDR-Produkte registrieren genau einen solchen Callback, um gefährliche Rechte (THREAD_SUSPEND_RESUME, THREAD_SET_CONTEXT) zu entfernen, wenn der Quellprozess nicht vertrauenswürdig ist.

Häufige Malware-Nutzung

Pflichtvorstufe nahezu jeder Thread-Level-Injection. Die interessante Frage ist *welche* Access-Mask angefordert wird — `THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME` ist die kanonische „ich werde dich hijacken“-Kombination. OPSEC-bewusste Loader fordern nur das Nötige: zuerst THREAD_QUERY_LIMITED_INFORMATION zum Enumerieren, dann erneutes Öffnen mit erhöhter Maske nur am gewählten Opfer-Thread. Early-Bird-APC-Varianten öffnen ALERTABLE-Threads, die per NtQuerySystemInformation(SystemProcessInformation) gefunden wurden.

Erkennungs­möglichkeiten

Sysmon Event ID 10 (ProcessAccess) ist das Pendant auf *Prozess*-Seite; für Threads ist das Äquivalent Microsoft-Windows-Kernel-Audit-API-Calls (Event ID 5), das beim zugrunde liegenden ObOpenObjectByPointer feuert. Der trennschärfste Filter ist die Access-Mask: legitime Opens fordern Cross-Process praktisch nie THREAD_SET_CONTEXT (0x10) an. EDRs hooken NtOpenThread üblicherweise per ObRegisterCallbacks (Kernel) und per Inline-Hook (User); Kernel-Callbacks sind aus User-Mode nicht umgehbar und bleiben die maßgebliche Quelle.

Direkte Syscall-Beispiele

cOpen by TID for hijack chain

CLIENT_ID cid = { .UniqueProcess = (HANDLE)(ULONG_PTR)pid,
                  .UniqueThread  = (HANDLE)(ULONG_PTR)tid };
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);

HANDLE hThread = NULL;
NTSTATUS st = NtOpenThread(
    &hThread,
    THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME,
    &oa,
    &cid);
if (!NT_SUCCESS(st)) return st;

asmx64 stub (Win11 24H2)

NtOpenThread PROC
    mov  r10, rcx
    mov  eax, 139h          ; Win11 24H2 SSN
    syscall
    ret
NtOpenThread ENDP

rustIndirect syscall (HellsHall)

// Resolve gate address from ntdll!NtOpenThread, then jump rather than execute syscall inline.
// Defeats inline-hook detection that whitelists ntdll-originating syscalls.
unsafe fn nt_open_thread(handle: *mut isize, access: u32,
                         oa: *mut ObjectAttributes, cid: *mut ClientId) -> i32 {
    let ssn: u32 = 0x139;            // Win11 24H2
    let gate: usize = find_syscall_insn("NtOpenThread");
    let r: i32;
    core::arch::asm!(
        "mov r10, rcx",
        "mov eax, {ssn:e}",
        "jmp {gate}",
        ssn = in(reg) ssn, gate = in(reg) gate,
        in("rcx") handle, in("rdx") access, in("r8") oa, in("r9") cid,
        lateout("rax") r,
    );
    r
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20