A walkthrough over Themida anti-debug techniques
While exploring solutions for integrating anti-debug functionality similar to ScyllaHide's integration with x64dbg, I found that many plugins are not supported by the latest versions of IDA Pro. Although some workarounds exist, it led me to consider the absence of a comprehensive tool using the same anti-anti-debug techniques as ScyllaHide.
This sparked the idea of deep diving on these techniques, serving as a learning opportunity as well.
ScyllaHide bypasses Themida protections using the following features, as stated in the latest "scylla_hide.ini" file:
Hide from PEB: BeingDebugged, HeapFlags, NtGlobalFGlag, StartupInfo, OsBuildNumber.
NtCloseHook
NtCreateThreadExHook
NtSetInformationThreadHook
NtQueryInformationProcessHook
NtQuerySystemInformationHook
NtUserBuildHwndListHook
NtUserFindWindowExHook
NtUserQueryWindowHook
NtUserGetForegroundWindowHook
BreakOnTLS
SkipEPOutsideCode
Kill Anti-Attach
So lets breakdown these techniques in particular.
PEB based Techniques
NtGlobalFlag
According to unofficial documentation, NtGlobalFlag is a kernel variable retrievable using the kernel-mode RtlGetNtGlobalFlags function. This function is rarely used by driver developers and simplifies RTL. The field is part of the PEB structure and is copied from the kernel during PEB creation. The relative offset is 0x068 in x86 architecture and 0xBC in x86_64 architecture. It is declared as a DWORD and serves as a record of the process's initial state.
When the value of the NtGlobalFlags is 0x70 means that:
FLG_HEAP_ENABLE_TAIL_CHECK
(0x10)
checks for buffer overruns when the heap is freed
FLG_HEAP_ENABLE_FREE_CHECK
(0x20)
validates each heap allocation when it is freed.
FLG_HEAP_VALIDATE_PARAMETERS
(0x40)
verifies selected aspects of the heap whenever a heap function is called.
The value of NtGlobalFlag can be configured through various means during the process creation phase, both at the kernel and user-mode levels.
Among the most important ones are:
The global value can be set in the registry at the following location: "HKLM\System\CurrentControlSet\Control\Session Manager".
Note that changes to this registry key require a system reboot to take effect. To verify the updated value before rebooting, you can run a second process and query the flag.
The registry value specific to the executable can be set under: "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\filename"
Image File Execution Options (IFEO) are commonly used to enable automatic debugging upon process startup. This is achieved by setting the appropriate registry value for the "Tracing Flags" options. During process load time, the "Tracing Flags" registry entry is read. If the value is non-zero, the bits are OR-ed into the corresponding DWORD in the PEB.
Tracing Flags Values
Heap Tracing Enabled: 0x00000001
CritSecTracingEnabled: 0x00000002
Once these "Tracing Flags" are set, heap tracing is activated at the kernel level. In the given example, this is done by enabling heap tracing for a process with PID=0. This process is chosen for two reasons: it always exists, and it does not perform heap allocations. The tracing is then stopped and merged in the usual manner, as demonstrated in the example.
Another way is using the overrides from the Load Configuration Directory.
In the Load Configuration Directory of a process's executable, two fields play a crucial role in flag management:
GlobalFlagsClear: Specifies the flags that should be cleared.
GlobalFlagsSet: Specifies the flags that should be set.
"_NO_DEBUG_HEAP" Environment Variable
When the _NO_DEBUG_HEAP environment variable is present, the three heap-related flags will not be set in the NtGlobalFlag field due to the debugger's influence.
Inspecting the PEB in IDA
The PEB can be inspected in IDA using the _PEB and PEB_LDR_DATA structures, which are defined in the ntapi Type Library. To load this library, navigate to the Type Libraries subview and press Ins.
Reverse Engineering Themida's PEB Reading
The first step in reverse engineering the code used by Themida to read the PEB is to identify the memory range where the PEB structure is loaded. Once this is done, you can use IDAPython to search for cross-references—specifically, instructions that read that part of the memory, whether through absolute or relative addressing.
The code provided above serves as a foundational example for automated analysis techniques, but it's important to note that it doesn't cover all possible scenarios. While this automated approach is useful for broad sweeps, IDA Pro offers more granular control for manual analysis. Specifically, you can set up read-access hardware breakpoints within the Process Environment Block (PEB) segment to closely monitor data access patterns. This allows for real-time debugging and can be particularly useful for in-depth investigations.
BeingDebugged
Indicates whether a process is being debugged or not. The BeingDebugged flag is located at offset 0x2 within the PEB structure. However, the PEB itself is located at different offsets depending on the architecture:
x86: 0x32
x86_64: 0x62
0: The process is not being debugged.
1: The process is being debugged.
When a debugger attaches to a process, the Windows Kernel sets the BeingDebugged flag to 1. This is typically done during the process creation phase if the process is started with debugging, or later if a debugger attaches to an already running process. The kernel ensures that this flag is set appropriately to reflect the debugging state, making it a reliable indicator for debugging status.
Heap Flags
Heap flags are settings that control the behavior of a heap, which is a region of a process's memory space used for dynamic memory allocation. These flags are often used for debugging, performance tuning, and security hardening. They can be set when a heap is created using the HeapCreate function or modified later using HeapSetInformation.
When the process is created, an inital Heap is created along and the base address at 0x18 offset of the PEB structure.
Among the structure properties, two flags, Flags and ForceFlags are affected by the presence of a debugger, for example the value of the Flags field is normally set to HEAP_GROWABLE, and the value for the ForceFlags is normally set to zero.
Again when the environment variable "_NO_DEBUG_HEAP is present", the behavior described above will not take effect.
This heap flags can be controlled on a per-process basis, through the "PageHeapFlags", which can be found in the IFEO registry key.
But in the case that a debugger is present, the Flags value is a combination of the following values:
HEAP_GROWABLE (0x02)
HEAP_TAIL_CHECKING_ENABLED (0x20)
HEAP_FREE_CHECKING_ENABLED (0x40)
HEAP_VALIDATE_PARAMETERS_ENABLED (0x40000000)
Also, for ForceFlag value is:
HEAP_TAIL_CHECKING_ENABLED (0x20)
HEAP_FREE_CHECKING_ENABLED (0x40)
HEAP_VALIDATE_PARAMETERS_ENABLED (0x40000000)
One interesting fact about HEAP_TAIL_CHECKING_ENABLED is that adds a 8 byte footer to each allocation, which are used to check for buffer overruns when the heap is freed, if the footer is overwritten, the heap manager will raise an exception, those 8 bytes are fixed values and can be read after every heap, which is a way to detect a debugger.
Process Startup Info
Some debuggers manipulate StartUpInfo to start the debuggee process. This is completely dependent on the debugger.
The way to patch the modifications that the debugger might do to the process are:
OS Build Version Number
I found limited documentation on this specific anti-debugging technique, even though it's employed by Themida. My primary source was ScyllaHide's documentation, which unfortunately doesn't delve into the technique's internals.
It appears that the method involves searching the resource data entry for VS_VERSIONINFO within the NTDLL module. Themida then compares the OS build number to the one found in this resource data entry. If there's a mismatch, Themida terminates the process.
VS_VERSIONINFO serves as the root structure organizing data in file-version resources, encapsulating all other file-version information structures.
Themida seems to focus on two key values within VS_VERSIONINFO: ProductVersion and FileVersion. Additionally, it checks the VS_FIXEDFILEINFO signature, a DWORD value set to 0xFEEF04BD.
To successfully bypass Themida's checks, these three values must be patched.
Nt* Hooks
NtClose
Multiple APIs interface with the kernel32!CloseHandle function, but the pivotal API in this technique is NtClose, which is tasked with actually closing the handle. When a debugger is active and an invalid handle is provided, NtClose returns either STATUS_INVALID_HANDLE or EXCEPTION_INVALID_HANDLE. These return values can be intercepted using a vectored exception handler.
To put it succinctly, the exception can be captured by a specialized exception handler. If control is transferred to this handler, it serves as an indicator that a debugger is currently active.
In a more specialized context, ScyllaHide utilizes NtQueryObject to verify the handle's validity. A hook can be implemented on NtClose to override the default behavior, making it return STATUS_SUCCESS instead of STATUS_INVALID_HANDLE.
NtSetInformationThread
NtSetInformationThread is a low-level system function responsible for altering various attributes of a thread. Exported by ntdll.dll, it is often invoked indirectly through the SetThreadContext function in kernel32.dll. The function prototype in C can be defined as:
When called with the ThreadHideFromDebugger flag (numerically represented as 0x11) in the ThreadInformationClass parameter, the function instructs the targeted thread to stop sending debugging events to any attached debugger. This can be particularly useful for evading debugging.
Hooking NtSetInformationThread can be an anti-debugging strategy. If the function is invoked with incorrect parameters but still returns STATUS_SUCCESS (0x00000000), it's a strong indicator that either a debugger is attached or the function itself is hooked. Furthermore, it seems that the function is rarely used by legitimate applications.
NtCreateThreadEx
The NtCreateThreadEx function is used to create a thread in a target process. It is a wrapper for the NtCreateThread function, which is a kernel-mode function that creates a thread in the context of the calling process. NtCreateThreadEx is a user-mode function that calls NtCreateThread internally.
The NtCreateThreadEx function is exported by ntdll.dll, and it is used by the CreateRemoteThread function in kernel32.dll. The CreateRemoteThread function is used to create a thread in a target process. It is a wrapper for the CreateRemoteThreadEx function, which is a user-mode function that calls NtCreateThreadEx internally.
This is important, because, some threads can be used to scan memory looking for software breakpoints, altough not always reliable, it can be used to detect a debugger.
It's possible to use NtCreateThreadEx for hiding the thread, this thread in particular will not send events to the debugger by using THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER flag in the CreateFlags parameter, which value translates to 0x4 at compile time.
Among the possible values (just for the sake of curiosity) are:
NtQueryInformationProcess
Some important values are:
ProcessDebugFlags
ProcessDebugPort
ProcessDebugObjectHandle
ProcessBasicInformation
ProcessBreakOnTermination
ProcessHandleTracing
NtQuerySystemInformation
This function is widely used by Task Manager and Proc. Exp.
In kernel mode, ZwQuerySystemInformation is also a stub that transfers execution to the NtQuerySystemInformation implementation but such that the execution is recognised as originating in kernel mode.
The SystemKernelDebuggerInformation can be used to detect kernel debuggers, although might not be quite relevant for themida.
By using the SYSTEM_INFORMATION_CLASS enumeration class SystemProcessInformation we tell the function that we want process information only, this can be used to listing the Windows Processes.
Debugee process should have a parent process like explorer, powershell or cmd, but definetely hide the fact that the parent PID belongs to a debugger like x64dbg or IDA pro.
NtUserBuildHwndList
This function is used to build a list of windows that belong to a process. Living inside win32u.dll as an undocumented api, has the following prototype:
Win32u.dll is just a stub that does a syscall, the actual implementation is in win32kfull.sys.
EnumWindows, EnumDesktopWindows, EnumChildWindows, FindWindow, FindWindowEx use this api internally, so it's a good candidate for hooking.
NtUserFindWindowEx
NtUserFindWindowEx is the internal process that FindWindowExA and FindWindowExW calls to find a window by class name and window name.
Like any other FindWindow function it can be used to search for windows that belong to a debugger, or analysis tool.
These techniques are subject to change
And this article may have some technical inaccuracies, so please, if you find something wrong, or you want to add something, feel free to reach me on twitter.
Thanks to:
Chris Eagle
mrexodia
Check Point Research
Reference that aren't linked in the article but were part of:
THE IDA PRO BOOK - 2ND EDITION by Chris Eagle
ScyllaHide v1.3 Documentation
Check Point Research https://anti-debug.checkpoint.com/
The "Ultimate" Anti-Debugging Reference by Peter Ferrie
https://www.geoffchappell.com/
Disclaimer
The primary objective of this article is to furnish resources and methodologies for the reverse engineering of malicious software for the purposes of security research, threat analysis, and lawful mitigation. This article does not condone, promote, or provide guidance on reverse engineering legitimate or commercial software, which may be illegal and subject to civil and criminal penalties under various jurisdictions.
The author of this article disclaim any liability for any damages, losses, or legal consequences incurred as a result of the application of any content provided herein. Readers assume all risks associated with the use of this information.
Last updated
Was this helpful?