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 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 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.
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::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 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:
The presence of a debugger can be detected by checking a combination of those flags.
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.
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.