NtSetInformationThread
Définit une propriété d'un thread via l'énumération THREADINFOCLASS — notamment ThreadHideFromDebugger.
Prototype
NTSTATUS NtSetInformationThread( HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| ThreadHandle | HANDLE | in | Handle vers le thread cible. Utilisez NtCurrentThread() ((HANDLE)-2) pour le thread appelant. |
| ThreadInformationClass | THREADINFOCLASS | in | Énum sélectionnant la propriété à définir, ex. ThreadHideFromDebugger (0x11), ThreadBreakOnTermination (0x12). |
| ThreadInformation | PVOID | in | Tampon contenant la nouvelle valeur. Pour ThreadHideFromDebugger, il est ignoré et peut être NULL. |
| ThreadInformationLength | ULONG | in | Taille en octets du tampon ThreadInformation. 0 pour ThreadHideFromDebugger. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0xD | win10-1507 |
| Win10 1607 | 0xD | win10-1607 |
| Win10 1703 | 0xD | win10-1703 |
| Win10 1709 | 0xD | win10-1709 |
| Win10 1803 | 0xD | win10-1803 |
| Win10 1809 | 0xD | win10-1809 |
| Win10 1903 | 0xD | win10-1903 |
| Win10 1909 | 0xD | win10-1909 |
| Win10 2004 | 0xD | win10-2004 |
| Win10 20H2 | 0xD | win10-20h2 |
| Win10 21H1 | 0xD | win10-21h1 |
| Win10 21H2 | 0xD | win10-21h2 |
| Win10 22H2 | 0xD | win10-22h2 |
| Win11 21H2 | 0xD | win11-21h2 |
| Win11 22H2 | 0xD | win11-22h2 |
| Win11 23H2 | 0xD | win11-23h2 |
| Win11 24H2 | 0xD | win11-24h2 |
| Server 2016 | 0xD | winserver-2016 |
| Server 2019 | 0xD | winserver-2019 |
| Server 2022 | 0xD | winserver-2022 |
| Server 2025 | 0xD | winserver-2025 |
Module noyau
APIs liées
Stub du syscall
4C 8B D1 mov r10, rcx B8 0D 00 00 00 mov eax, 0xD 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
Le SSN de NtSetInformationThread est `0xD` sur tous les builds Windows modernes, ce qui en fait un point d'ancrage privilégié pour les stubs syscall faits main. La surface intéressante est l'énumération `THREADINFOCLASS` : `ThreadHideFromDebugger = 0x11` fait que KiDispatchException ne notifie plus le débogueur attaché des exceptions levées par ce thread, rendant invisibles les points d'arrêt et le single-step. `ThreadBreakOnTermination = 0x12` active le bit BreakOnTermination de l'EPROCESS afin que la fin du thread provoque un bug-check — parfois utilisé comme piège anti-altération brutal. La plupart des autres classes (ThreadIdealProcessor, ThreadPriorityBoost) sont totalement bénignes.
Usage courant par les malwares
ThreadHideFromDebugger est *le* primitif anti-debug en mode utilisateur — appelé sur le thread principal (et souvent sur chaque worker) au démarrage pour faire taire x64dbg, WinDbg ou le débogueur local d'IDA. Les protecteurs commerciaux (VMProtect, Themida, Enigma) y recourent ; les crypters du commerce, les droppers de documents et les outils post-ex Cobalt Strike soucieux d'échapper aux bacs à sable d'analystes aussi. Certaines familles abusent également de ThreadBreakOnTermination pour qu'un kill forcé par un EDR provoque un BSOD du système — une évasion destructive vue chez quelques wipers et rootkits agressifs.
Opportunités de détection
Depuis Windows 10 1809, le noyau émet un événement ETW Threat Intelligence (`EtwTiLogSetInformationThread`) dès que ThreadHideFromDebugger est activé ; Defender for Endpoint et la plupart des EDR modernes le consomment — c'est le signal le plus fiable, car même un appelant en syscall direct ne peut pas le supprimer depuis le user-mode. Microsoft-Windows-Threat-Intelligence est un provider ETW protégé, l'inscription nécessite donc PPL/AntiMalware Light. Les hooks usermode sur `ntdll!NtSetInformationThread` sont systématiquement contournés par syscalls directs ; la télémétrie noyau reste l'unique couche fiable. Corréler avec ThreadInformationClass == 0x11 pour limiter le bruit.
Exemples de syscalls directs
asmThreadHideFromDebugger direct stub
; SSN 0xD on all modern builds. ThreadInformationClass = 0x11 (ThreadHideFromDebugger).
HideCurrentThread PROC
sub rsp, 28h
mov rcx, -2 ; NtCurrentThread()
mov edx, 11h ; ThreadHideFromDebugger
xor r8, r8 ; ThreadInformation = NULL
xor r9d, r9d ; ThreadInformationLength = 0
mov r10, rcx
mov eax, 0Dh
syscall
add rsp, 28h
ret
HideCurrentThread ENDPcIndirect syscall via ntdll lookup
// Resolves ntdll!NtSetInformationThread and calls it as-is so EDR
// hooks on the prologue still see the call — useful when only the
// ThreadHideFromDebugger event matters and the loader doesn't want
// to look suspicious otherwise.
typedef NTSTATUS(NTAPI* fnNtSetInformationThread)(
HANDLE, ULONG, PVOID, ULONG);
void HideFromDebugger(void) {
HMODULE h = GetModuleHandleA("ntdll.dll");
fnNtSetInformationThread p = (fnNtSetInformationThread)
GetProcAddress(h, "NtSetInformationThread");
p((HANDLE)-2, 0x11 /* ThreadHideFromDebugger */, NULL, 0);
}rustwindows-sys + naked stub
// Cargo: windows-sys = "0.59"
use std::arch::asm;
#[unsafe(naked)]
unsafe extern "system" fn nt_set_information_thread_stub(
_thread: isize,
_class: u32,
_info: *mut u8,
_len: u32,
) -> i32 {
asm!(
"mov r10, rcx",
"mov eax, 0xD",
"syscall",
"ret",
options(noreturn),
);
}
pub fn hide_from_debugger() -> i32 {
unsafe { nt_set_information_thread_stub(-2, 0x11, core::ptr::null_mut(), 0) }
}Mappings MITRE ATT&CK
Last verified: 2026-05-20