> Windows Syscalls
ntoskrnl.exeT1622T1057T1106

NtQueryInformationThread

Liest eine Thread-Eigenschaft über die THREADINFOCLASS-Enumeration — TEB-Pointer, Hide-from-Debugger-Flag, Zeiten, Exit-Status.

Prototyp

NTSTATUS NtQueryInformationThread(
  HANDLE          ThreadHandle,
  THREADINFOCLASS ThreadInformationClass,
  PVOID           ThreadInformation,
  ULONG           ThreadInformationLength,
  PULONG          ReturnLength
);

Argumente

NameTypeDirDescription
ThreadHandleHANDLEinHandle auf den Ziel-Thread. NtCurrentThread() ((HANDLE)-2) liest vom Aufrufer.
ThreadInformationClassTHREADINFOCLASSinEnum, das die Eigenschaft auswählt. Üblich: ThreadBasicInformation (0) → TEB, ThreadTimes (1), ThreadIsTerminated (14), ThreadHideFromDebugger (17/0x11), ThreadBreakOnTermination (18).
ThreadInformationPVOIDoutVom Aufrufer bereitgestellter Puffer für den Wert. Struktur richtet sich nach der Klasse.
ThreadInformationLengthULONGinGröße von ThreadInformation in Bytes. STATUS_INFO_LENGTH_MISMATCH bei zu klein.
ReturnLengthPULONGoutOptional. Empfängt die Anzahl tatsächlich geschriebener Bytes.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x25win10-1507
Win10 16070x25win10-1607
Win10 17030x25win10-1703
Win10 17090x25win10-1709
Win10 18030x25win10-1803
Win10 18090x25win10-1809
Win10 19030x25win10-1903
Win10 19090x25win10-1909
Win10 20040x25win10-2004
Win10 20H20x25win10-20h2
Win10 21H10x25win10-21h1
Win10 21H20x25win10-21h2
Win10 22H20x25win10-22h2
Win11 21H20x25win11-21h2
Win11 22H20x25win11-22h2
Win11 23H20x25win11-23h2
Win11 24H20x25win11-24h2
Server 20160x25winserver-2016
Server 20190x25winserver-2019
Server 20220x25winserver-2022
Server 20250x25winserver-2025

Kernel-Modul

ntoskrnl.exeNtQueryInformationThread

Verwandte APIs

GetThreadInformationGetThreadTimesNtSetInformationThreadNtQueryInformationProcessNtGetContextThread

Syscall-Stub

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

SSN `0x25` seit Windows 10 1507 — ein bemerkenswert stabiler Anker. Zwei Klassen dominieren den offensiven Einsatz. ThreadBasicInformation (0) liefert ein `THREAD_BASIC_INFORMATION`, dessen `TebBaseAddress` den TEB des Ziel-Threads offenlegt — der kanonische Weg, PEB / Modulliste abzuleiten, ohne GS-relative Reads zu nutzen (z. B. wenn EAF — Export Address Filter — im Ziel aktiv ist). ThreadHideFromDebugger (17) liefert ein BYTE, das anzeigt, ob das Hide-Bit gesetzt ist; diese Rückseite ist das Herz des *Anti-Anti-Debug-Checks* — ist das Hide-Flag plötzlich 0, hat ein EDR oder ein Analystentool den Set stillschweigend entfernt. ThreadIsTerminated (14) wird in Sleep-Mask-Schleifen genutzt, um auf einen Shutdown-Befehl früh aufzuwachen.

Häufige Malware-Nutzung

Fünf praktische Rezepte. (1) EAF-Bypass: TEB via ThreadBasicInformation lesen statt `gs:[0x30]`, um den Hardware-Breakpoint von Microsofts EAF-Mitigation auf den PEB-Zugriff nicht auszulösen. (2) Anti-Anti-Debug: ThreadHideFromDebugger setzen, dann sofort abfragen — kommt 0 zurück, schluckt ein instrumentiertes ntdll das Set still. (3) Sleep-Mask-Wake: ThreadIsTerminated auf einem Geschwister-Thread pollen, um Shutdown ohne Event-Objekt zu koordinieren. (4) ThreadStartAddress-Probe (Klasse 9), um andere Threads zu durchlaufen und Injection-Ziele zu wählen, die wie legitime Worker (RPC, RuntimeBroker) aussehen. (5) EPROCESS via TEB → PEB → LDR durchlaufen, um Module zu enumerieren, ohne kernel32!GetModuleHandle anzufassen.

Erkennungs­möglichkeiten

Es gibt kein Per-Call-ETW-Signal für Queries (im Unterschied zu NtSetInformationThread mit ThreadHideFromDebugger, das geloggt wird). Detection muss auf Seiteneffekte zielen: Enumerationen, die jeden Thread in Remote-Prozessen anfassen, oder Sequenzen aus NtSetInformationThread(17) gefolgt *unmittelbar* von NtQueryInformationThread(17) am selben Handle innerhalb von Mikrosekunden — typisches Anti-Anti-Debug-Muster. Defenders ASR / Network Protection decken das nicht ab; sauberstes Signal: EDR-Hook auf ntdll!NtQueryInformationThread, gefiltert auf ThreadInformationClass == 0 oder == 0x11 von Aufrufern außerhalb der System-DLLs.

Direkte Syscall-Beispiele

cAnti-anti-debug round-trip on ThreadHideFromDebugger

// Set the hide flag, then query it back. If the readback returns 0,
// an EDR or analysis tool is silently neutralizing NtSetInformationThread —
// strong signal we're being analyzed.
typedef NTSTATUS(NTAPI* fnSet)(HANDLE, ULONG, PVOID, ULONG);
typedef NTSTATUS(NTAPI* fnQuery)(HANDLE, ULONG, PVOID, ULONG, PULONG);

BOOL HideFlagSurvived(void) {
    HMODULE n = GetModuleHandleA("ntdll.dll");
    fnSet pSet = (fnSet)GetProcAddress(n, "NtSetInformationThread");
    fnQuery pQuery = (fnQuery)GetProcAddress(n, "NtQueryInformationThread");
    pSet((HANDLE)-2, 0x11 /* ThreadHideFromDebugger */, NULL, 0);
    BOOLEAN hidden = FALSE;
    ULONG  rl = 0;
    pQuery((HANDLE)-2, 0x11, &hidden, sizeof(hidden), &rl);
    return hidden != 0;
}

asmx64 direct stub

; SSN 0x25 across all modern builds.
NtQueryInformationThread PROC
    mov  r10, rcx
    mov  eax, 25h
    syscall
    ret
NtQueryInformationThread ENDP

rustTEB pointer extraction for EAF-safe walking

// Cargo: windows-sys = "0.59"
use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};

#[repr(C)]
struct ThreadBasicInformation {
    exit_status: i32,
    teb_base_address: *mut u8,
    client_id: [usize; 2],
    affinity_mask: usize,
    priority: i32,
    base_priority: i32,
}

type NtQueryInformationThread = unsafe extern "system" fn(
    thread: isize, class: u32, info: *mut u8, len: u32, ret_len: *mut u32,
) -> i32;

pub unsafe fn get_self_teb() -> Option<*mut u8> {
    let n = GetModuleHandleA(b"ntdll.dll\0".as_ptr());
    let addr = GetProcAddress(n, b"NtQueryInformationThread\0".as_ptr())?;
    let f: NtQueryInformationThread = std::mem::transmute(addr);
    let mut tbi = ThreadBasicInformation {
        exit_status: 0, teb_base_address: core::ptr::null_mut(),
        client_id: [0; 2], affinity_mask: 0, priority: 0, base_priority: 0,
    };
    let mut rl = 0u32;
    if f(-2, 0 /* ThreadBasicInformation */, &mut tbi as *mut _ as *mut u8,
         core::mem::size_of::<ThreadBasicInformation>() as u32, &mut rl) == 0 {
        Some(tbi.teb_base_address)
    } else { None }
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20