Skip to content
Anti-Analysisintermediate

NtQueryInformationProcess Debug Flags

Querying ProcessDebugPort, ProcessDebugFlags, and ProcessDebugObjectHandle through NtQueryInformationProcess to detect an attached debugger.

NtQueryInformationProcess is the native gateway to per-process information. Three of its information classes leak the presence of a debugger directly from the kernel, bypassing the easily-spoofed PEB flag that IsDebuggerPresent reads. Because these classes consult the kernel's debug-object state, they are a favorite among packers and loaders.

How it works

Three classes matter for anti-debugging:

  • ProcessDebugPort (0x07) — returns a non-zero port handle when a debugger is attached.
  • ProcessDebugObjectHandle (0x1E) — returns a handle to the debug object; non-NULL means a debugger.
  • ProcessDebugFlags (0x1F) — returns the inverse of NoDebugInherit, i.e. 0 when the process is being debugged.
c
typedef NTSTATUS (NTAPI *pNtQIP)(HANDLE, UINT, PVOID, ULONG, PULONG);
pNtQIP NtQueryInformationProcess =
    (pNtQIP)GetProcAddress(GetModuleHandleW(L"ntdll"), "NtQueryInformationProcess");

DWORD_PTR debugPort = 0;
NtQueryInformationProcess(GetCurrentProcess(), 0x07 /*ProcessDebugPort*/,
                          &debugPort, sizeof(debugPort), NULL);
if (debugPort != 0) {
    // Debugger detected.
}

DWORD debugFlags = 0;
NtQueryInformationProcess(GetCurrentProcess(), 0x1F /*ProcessDebugFlags*/,
                          &debugFlags, sizeof(debugFlags), NULL);
if (debugFlags == 0) {
    // Debugger detected (NoDebugInherit cleared).
}

Samples frequently resolve the export dynamically to keep ntdll calls out of the import table.

Detection & analysis

  • Static analysis: look for GetProcAddress strings "NtQueryInformationProcess" alongside the magic class constants 0x07, 0x1E, or 0x1F, and a branch that tests the returned value for zero/non-zero. Direct ordinal calls into ntdll near unpacking routines are a strong signal.
  • Dynamic analysis: hook NtQueryInformationProcess and normalize the output — zero out ProcessDebugPort and ProcessDebugObjectHandle, and force ProcessDebugFlags to 1. ScyllaHide and TitanHide intercept exactly these classes.
  • Detection rule hint: YARA-match the API name string with any of the three class constants; in sandbox telemetry, alert when a process queries ProcessDebugObjectHandle and then exits or branches into a different code path within a short window.
Votes

Comments(0)