Special flags in system tables, which dwell in process memory and which an operation system sets, can be used to indicate that the process is being debugged. The states of these flags can be verified either by using specific API functions or examining the system tables in memory.
These techniques are the most commonly used by malware.
The following techniques use existing API functions (WinAPI or NativeAPI) that check system structures in the process memory for particular flags that indicate the process is being debugged right now.
The function kernel32!IsDebuggerPresent() determines whether the current process is being debugged by a user-mode debugger such as OllyDbg or x64dbg. Generally, the function only checks the BeingDebugged flag of the Process Environment Block (PEB).
The following code can be used to terminate process if it is being debugged:
The function kernel32!CheckRemoteDebuggerPresent() checks if a debugger (in a different process on the same machine) is attached to the current process.
The function ntdll!NtQueryInformationProcess() can retrieve a different kind of information from a process. It accepts a ProcessInformationClass parameter which specifies the information you want to get and defines the output type of the ProcessInformation parameter.
It is possible to retrieve the port number of the debugger for the process using the ntdll!NtQueryInformationProcess(). There is a documented class ProcessDebugPort, which retrieves a DWORD value equal to 0xFFFFFFFF (decimal -1) if the process is being debugged.
A kernel structure called EPROCESS, which represents a process object, contains the field NoDebugInherit. The inverse value of this field can be retrieved using an undocumented class ProcessDebugFlags (0x1f). Therefore, if the return value is 0, a debugger is present.
When debugging begins, a kernel object called “debug object” is created. It is possible to query for the value of this handle by using the undocumented ProcessDebugObjectHandle (0x1e) class.
The ntdll!RtlQueryProcessDebugInformation() function can be used to read certain fields from the process memory of the requested process, including the heap flags.
The ntdll!NtQuerySystemInformation() function accepts a parameter which is the class of information to query. Most of the classes are not documented. This includes the SystemKernelDebuggerInformation (0x23) class, which has existed since Windows NT. The SystemKernelDebuggerInformation class returns the value of two flags: KdDebuggerEnabled in al, and KdDebuggerNotPresent in ah. Therefore, the return value in ah is zero if a kernel debugger is present.
For IsDebuggerPresent(): Set the BeingDebugged flag of the Process Environment Block (PEB) to 0. See BeingDebugged Flag Mitigation for further information.
For CheckRemoteDebuggerPresent() and NtQueryInformationProcess(): As CheckRemoteDebuggerPresent() calls NtQueryInformationProcess(), the only way is to hook the NtQueryInformationProcess() and set the following values in return buffers:
0 (or any value except -1) in case of a ProcessDebugPort query.
Non-zero value in case of a ProcessDebugFlags query.
0 in case of a ProcessDebugObjectHandle query.
The only way to mitigate these checks with RtlQueryProcessHeapInformation(), RtlQueryProcessDebugInformation() and NtQuerySystemInformation() functions is to hook them and modify the returned values:
RTL_PROCESS_HEAPS::HeapInformation::Heaps[0]::Flags to HEAP_GROWABLE for RtlQueryProcessHeapInformation() and RtlQueryProcessDebugInformation().
SYSTEM_KERNEL_DEBUGGER_INFORMATION::DebuggerEnabled to 0 and SYSTEM_KERNEL_DEBUGGER_INFORMATION::DebuggerNotPresent to 1 for the NtQuerySystemInformation() function in case of a SystemKernelDebuggerInformation query.
The following approaches are used to validate debugging flags in system structures. They examine the process memory manually without using special debug API functions.
The NtGlobalFlag field of the Process Environment Block (0x68 offset on 32-Bit and 0xBC on 64-bit Windows) is 0 by default. Attaching a debugger doesn’t change the value of NtGlobalFlag. However, if the process was created by a debugger, the following flags will be set:
FLG_HEAP_ENABLE_TAIL_CHECK (0x10)
FLG_HEAP_ENABLE_FREE_CHECK (0x20)
FLG_HEAP_VALIDATE_PARAMETERS (0x40)
The presence of a debugger can be detected by checking a combination of those flags.
The heap contains two fields which are affected by the presence of a debugger. Exactly how they are affected depends on the Windows version. These fields are Flags and ForceFlags.
The values of Flags and ForceFlags are normally set to HEAP_GROWABLE and 0, respectively.
When a debugger is present, the Flags field is set to a combination of these flags on Windows NT, Windows 2000, and 32-bit Windows XP:
HEAP_GROWABLE (2)
HEAP_TAIL_CHECKING_ENABLED (0x20)
HEAP_FREE_CHECKING_ENABLED (0x40)
HEAP_SKIP_VALIDATION_CHECKS (0x10000000)
HEAP_VALIDATE_PARAMETERS_ENABLED (0x40000000)
On 64-bit Windows XP, and Windows Vista and higher, if a debugger is present, the Flags field is set to a combination of these flags:
HEAP_GROWABLE (2)
HEAP_TAIL_CHECKING_ENABLED (0x20)
HEAP_FREE_CHECKING_ENABLED (0x40)
HEAP_VALIDATE_PARAMETERS_ENABLED (0x40000000)
When a debugger is present, the ForceFlags field is set to a combination of these flags:
If the HEAP_TAIL_CHECKING_ENABLED flag is set in NtGlobalFlag, the sequence 0xABABABAB will be appended (twice in 32-Bit and 4 times in 64-Bit Windows) at the end of the allocated heap block.
If the HEAP_FREE_CHECKING_ENABLED flag is set in NtGlobalFlag, the sequence 0xFEEEFEEE will be appended if additional bytes are required to fill in the empty space until the next memory block.
This technique was originally described as an issue for TitanHide, a kernel driver to hide debuggers from detection. The detailed documentation for the structure KUSER_SHARED_DATA and its fields is available here.
Here is what the author of the issue wrote in the post regarding the features of the structure and its appropriate field:
“0x7ffe02d4 is actually 0x7ffe0000 + 0x2d4. 0x7ffe0000 is the fixed user mode address of the KUSER_SHARED_DATA structure that contains data that is shared between user mode and the kernel (though user mode doesn’t have write access to it). The struct has some interesting properties:
its address is fixed and has been in all Windows versions since it was introduced
its user mode address is the same in 32 bit and 64 bit mode
all offsets and sizes are strictly fixed, and new fields are only ever appended or added in place of unused padding space
Hence this program will work in 32 bit Windows 2000 and 64 bit Windows 10 without recompiling”.
Set the BeingDebugged flag to 0. This can be done by DLL injection. If you use OllyDbg or x32/64dbg as a debugger, you can choose various Anti-Debug plugins such as ScyllaHide.
For NtGlobalFlag:
Set the NtGlobalFlag to 0. This can be done by DLL injection. If you use OllyDbg or x32/64dbg as a debugger, you can choose various Anti-Debug plugins such as ScyllaHide.
For Heap Flags:
Set the Flags value to HEAP_GROWABLE, and the ForceFlags value to 0. This can be done by DLL injection. If you use OllyDbg or x32/64dbg as a debugger, you can choose various Anti-Debug plugins such as ScyllaHide.
For Heap Protection:
Manually patch 12 bytes for 32-bit and 20 bytes in a 64-bit environment after the heap. Hook kernel32!HeapAlloc() and patch the heap after its allocation.
For KUSER_SHARED_DATA:
For a possible mitigation, please check the link when the technique is described (with the issue for TitanHide) and also a draft code for patching kdcom.dllhere.