Skip to content
Anti-Analysisintermediate

NtSetInformationThread (ThreadHideFromDebugger)

Malware calls NtSetInformationThread with the ThreadHideFromDebugger class to detach a thread from the debugger so breakpoint and exception events stop reaching the analyst.

NtSetInformationThread exposes a thread information class called ThreadHideFromDebugger (value 0x11). When a thread sets this flag on itself, the kernel stops delivering debug events for that thread to an attached debugger. Subsequent exceptions — most importantly the INT3 breakpoint exception — are no longer routed to the debugger, so a breakpoint inside the hidden thread crashes the process instead of stopping it.

This is a cheap, single-call technique that is very effective against naive debugging: the analyst sets a breakpoint, the program runs into it, and instead of breaking the process simply terminates with an unhandled exception.

How it works

The malware resolves the syscall stub from ntdll.dll and calls it with the hide class. No buffer is required — length and pointer are both zero.

c
#include <windows.h>

#define ThreadHideFromDebugger 0x11

typedef NTSTATUS (NTAPI *pfnNtSIT)(HANDLE, ULONG, PVOID, ULONG);

void HideCurrentThread(void)
{
    HMODULE hNtdll = GetModuleHandleW(L"ntdll.dll");
    pfnNtSIT NtSIT = (pfnNtSIT)GetProcAddress(hNtdll, "NtSetInformationThread");
    // hThread = current thread (-2), class 0x11, buffer NULL, length 0
    NtSIT((HANDLE)-2, ThreadHideFromDebugger, NULL, 0);
}

On Windows 10 and later the operation is reversible-detectable: calling NtQueryInformationThread with the same ThreadHideFromDebugger class returns the current state of the flag, so malware can also use it as a tamper check — if the flag does not read back as set, an anti-anti-debug hook is suspected.

Detection & bypass

  • Static — Look for GetProcAddress/import references to NtSetInformationThread (or ZwSetInformationThread). In IDA/Ghidra, search for the constant 0x11 pushed as the second argument with a NULL buffer and zero length; the (HANDLE)-2 (0xFFFFFFFE) pseudo-handle for the current thread is a strong tell. Cross-reference NtQueryInformationThread nearby for the Win10+ tamper check.
  • Dynamic — In x64dbg, set a breakpoint on ntdll!NtSetInformationThread and inspect the second argument (rdx/stack) for 0x11. When you see the call, step over it without letting the flag take effect, or change the class value to a harmless one before it executes.
  • Patch / bypass — Swallow the call: hook NtSetInformationThread and return STATUS_SUCCESS for class 0x11 without performing the operation. Alternatively NOP the call site, or patch the pushed class constant 0x11 to an unused value so the kernel ignores it.
  • Tools — ScyllaHide's "NtSetInformationThread (ThreadHideFromDebugger)" option intercepts the call automatically and also spoofs the NtQueryInformationThread read-back. TitanHide does the same at the kernel level. Frida: Interceptor.attach on the export and overwrite args[1] when it equals 0x11.
Votes

Comments(0)