> Windows Syscalls
ntoskrnl.exeT1622T1057T1106

NtQueryInformationThread

Lit une propriété d'un thread via l'énumération THREADINFOCLASS — pointeur TEB, flag hide-from-debugger, temps, statut de sortie.

Prototype

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

Arguments

NameTypeDirDescription
ThreadHandleHANDLEinHandle vers le thread cible. NtCurrentThread() ((HANDLE)-2) lit depuis l'appelant.
ThreadInformationClassTHREADINFOCLASSinÉnum sélectionnant la propriété. Courants : ThreadBasicInformation (0) → TEB, ThreadTimes (1), ThreadIsTerminated (14), ThreadHideFromDebugger (17/0x11), ThreadBreakOnTermination (18).
ThreadInformationPVOIDoutTampon alloué par l'appelant qui reçoit la valeur. Sa structure dépend de la classe.
ThreadInformationLengthULONGinTaille de ThreadInformation en octets. STATUS_INFO_LENGTH_MISMATCH si trop petit.
ReturnLengthPULONGoutOptionnel. Reçoit le nombre d'octets effectivement écrits.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
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

Module noyau

ntoskrnl.exeNtQueryInformationThread

APIs liées

GetThreadInformationGetThreadTimesNtSetInformationThreadNtQueryInformationProcessNtGetContextThread

Stub du syscall

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

Notes non documentées

SSN `0x25` depuis Windows 10 1507 — un ancrage remarquablement stable. Deux classes dominent l'usage offensif. ThreadBasicInformation (0) renvoie une `THREAD_BASIC_INFORMATION` dont `TebBaseAddress` expose le TEB du thread cible, voie canonique pour dériver PEB / liste de modules quand on évite les lectures GS-relatives (par ex. avec EAF — Export Address Filter — activé). ThreadHideFromDebugger (17) renvoie un BYTE indiquant si le bit hide est positionné ; cette relecture est au cœur du *check anti-anti-debug* — si votre flag hide est mystérieusement à 0, un EDR ou un outil d'analyste l'a retiré. ThreadIsTerminated (14) sert dans les boucles sleep-mask pour se réveiller dès qu'un arrêt est demandé.

Usage courant par les malwares

Cinq recettes pratiques. (1) Contournement d'EAF : lire le TEB via ThreadBasicInformation au lieu de `gs:[0x30]` pour éviter le breakpoint matériel posé par la mitigation EAF sur l'accès PEB. (2) Anti-anti-debug : poser ThreadHideFromDebugger, puis le relire — si la relecture est 0, un ntdll instrumenté avale silencieusement le set. (3) Réveil sleep-mask : sonder ThreadIsTerminated sur un thread frère pour coordonner l'arrêt sans event. (4) Sonde ThreadStartAddress (classe 9) pour parcourir d'autres threads et choisir des cibles d'injection qui ressemblent à des workers légitimes (RPC, RuntimeBroker). (5) Parcourir EPROCESS via TEB → PEB → LDR pour énumérer les modules sans toucher à kernel32!GetModuleHandle.

Opportunités de détection

Pas de signal ETW par appel pour les requêtes (contrairement à NtSetInformationThread posant ThreadHideFromDebugger, qui est journalisé). La détection doit cibler les effets de bord : énumérations touchant chaque thread d'un processus distant, ou séquences NtSetInformationThread(17) suivies *immédiatement* de NtQueryInformationThread(17) sur le même handle en quelques microsecondes — pattern anti-anti-debug typique. Les règles ASR / Network Protection de Defender ne le couvrent pas ; le signal le plus propre est un hook EDR sur ntdll!NtQueryInformationThread filtré par ThreadInformationClass == 0 ou == 0x11 provenant d'appelants hors DLL système.

Exemples de syscalls directs

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 }
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20