☠️
Uriel Berdeja
  • General
    • Virtual Machines Setup Notes
    • C++17 and C++20 Interesting Features
  • Windows
    • A walkthrough over Themida anti-debug techniques
    • Structured Exception Handlers
    • Win32 Authorization System
    • .NET CLR process internals
    • ClickOnce Technical Details
    • WebDAV Technical Details
    • Monikers
  • Uncategorized
    • Snippets
    • Windows Various Notes
    • Index of ingest resources
    • Tooling Resources
    • TODO List
Powered by GitBook
On this page

Was this helpful?

  1. Windows

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:

Flag
Value
Meaning

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.

import idautils
import idaapi
import idc

# Define the read-only section range
start_addr = 0x400000  # Replace with actual start address
end_addr = 0x401000  # Replace with actual end address

# Iterate through all functions
for func in idautils.Functions():
    flags = idc.GetFunctionFlags(func)
    
    # Skip library functions and thunks
    if flags & (idc.FUNC_LIB | idc.FUNC_THUNK):
        continue
    
    # Iterate through the function's instructions
    for head in idautils.Heads(func, idc.FindFuncEnd(func)):
        if idc.isCode(idc.GetFlags(head)):
            # Check if the instruction references read-only memory
            for i in range(0, idc.RfirstB(head), 1):
                addr = idc.GetOperandValue(head, i)
                
                # Check if the operand is a relative address
                if idc.isOff0(idc.GetFlags(head)) and i == 0 or idc.isOff1(idc.GetFlags(head)) and i == 1:
                    addr += head  # Convert relative address to absolute address
                
                if start_addr <= addr <= end_addr:
                    print(f"Function {idc.GetFunctionName(func)} at address {hex(head)} accesses read-only memory at {hex(addr)}")

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.

//0x258 bytes (sizeof)
struct _HEAP
{
    union
    {
        struct _HEAP_SEGMENT Segment;                                       //0x0
        struct
        {
            struct _HEAP_ENTRY Entry;                                       //0x0
            ULONG SegmentSignature;                                         //0x8
            ULONG SegmentFlags;                                             //0xc
            struct _LIST_ENTRY SegmentListEntry;                            //0x10
            struct _HEAP* Heap;                                             //0x18
            VOID* BaseAddress;                                              //0x1c
            ULONG NumberOfPages;                                            //0x20
            struct _HEAP_ENTRY* FirstEntry;                                 //0x24
            struct _HEAP_ENTRY* LastValidEntry;                             //0x28
            ULONG NumberOfUnCommittedPages;                                 //0x2c
            ULONG NumberOfUnCommittedRanges;                                //0x30
            USHORT SegmentAllocatorBackTraceIndex;                          //0x34
            USHORT Reserved;                                                //0x36
            struct _LIST_ENTRY UCRSegmentList;                              //0x38
        };
    };
    ULONG Flags;                                                            //0x40
    ULONG ForceFlags;                                                       //0x44
    ULONG CompatibilityFlags;                                               //0x48
    ULONG EncodeFlagMask;                                                   //0x4c
    struct _HEAP_ENTRY Encoding;                                            //0x50
    ULONG Interceptor;                                                      //0x58
    ULONG VirtualMemoryThreshold;                                           //0x5c
    ULONG Signature;                                                        //0x60
    ULONG SegmentReserve;                                                   //0x64
    ULONG SegmentCommit;                                                    //0x68
    ULONG DeCommitFreeBlockThreshold;                                       //0x6c
    ULONG DeCommitTotalFreeThreshold;                                       //0x70
    ULONG TotalFreeSize;                                                    //0x74
    ULONG MaximumAllocationSize;                                            //0x78
    USHORT ProcessHeapsListIndex;                                           //0x7c
    USHORT HeaderValidateLength;                                            //0x7e
    VOID* HeaderValidateCopy;                                               //0x80
    USHORT NextAvailableTagIndex;                                           //0x84
    USHORT MaximumTagIndex;                                                 //0x86
    struct _HEAP_TAG_ENTRY* TagEntries;                                     //0x88
    struct _LIST_ENTRY UCRList;                                             //0x8c
    ULONG AlignRound;                                                       //0x94
    ULONG AlignMask;                                                        //0x98
    struct _LIST_ENTRY VirtualAllocdBlocks;                                 //0x9c
    struct _LIST_ENTRY SegmentList;                                         //0xa4
    USHORT AllocatorBackTraceIndex;                                         //0xac
    ULONG NonDedicatedListLength;                                           //0xb0
    VOID* BlocksIndex;                                                      //0xb4
    VOID* UCRIndex;                                                         //0xb8
    struct _HEAP_PSEUDO_TAG_ENTRY* PseudoTagEntries;                        //0xbc
    struct _LIST_ENTRY FreeLists;                                           //0xc0
    struct _HEAP_LOCK* LockVariable;                                        //0xc8
    LONG (*CommitRoutine)(VOID* arg1, VOID** arg2, ULONG* arg3);            //0xcc
    union _RTL_RUN_ONCE StackTraceInitVar;                                  //0xd0
    struct _RTL_HEAP_MEMORY_LIMIT_DATA CommitLimitData;                     //0xd4
    VOID* FrontEndHeap;                                                     //0xe4
    USHORT FrontHeapLockCount;                                              //0xe8
    UCHAR FrontEndHeapType;                                                 //0xea
    UCHAR RequestedFrontEndHeapType;                                        //0xeb
    WCHAR* FrontEndHeapUsageData;                                           //0xec
    USHORT FrontEndHeapMaximumIndex;                                        //0xf0
    volatile UCHAR FrontEndHeapStatusBitmap[257];                           //0xf2
    struct _HEAP_COUNTERS Counters;                                         //0x1f4
    struct _HEAP_TUNING_PARAMETERS TuningParameters;                        //0x250
}; 

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:

RTL_USER_PROCESS_PARAMETERS<DWORD_PTR> rupp;
auto patch_size = (DWORD_PTR)&rupp.WindowFlags - (DWORD_PTR)&rupp.StartingX; // Calculating the original size
ZeroMemory(&rupp.WindowFlags, patch_size); // Patching the size 
rupp.WindowFlags = STARTF_USESHOWWINDOW;
rupp.ShowWindowFlags = SW_SHOWNORMAL;
rupp.Flags |= RTL_USER_PROCESS_PARAMETERS_IMAGE_KEY_MISSING;

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:

NTSTATUS NtSetInformationThread(
  HANDLE                 ThreadHandle,
  THREAD_INFORMATION_CLASS ThreadInformationClass,
  PVOID                  ThreadInformation,
  ULONG                  ThreadInformationLength
);

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:

#define THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001
#define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002
#define THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 0x00000004
#define THREAD_CREATE_FLAGS_HAS_SECURITY_DESCRIPTOR 0x00000010
#define THREAD_CREATE_FLAGS_ACCESS_CHECK_IN_TARGET 0x00000020
#define THREAD_CREATE_FLAGS_INITIAL_THREAD 0x00000080

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:

NTSTATUS
NTAPI
NtUserBuildHwndList(
	_In_ HDESK hDesktop,
	_In_opt_ HWND hwndParent,
	_In_ BOOLEAN bChildren,
	_In_opt_ ULONG dwThreadId,
	_In_opt_ ULONG lParam,
	_Out_ HWND* pWnd,
	_Inout_ PULONG pBufSize
	);
 
// For Windows 8 and up
NTSTATUS
NTAPI
NtUserBuildHwndList(
	_In_ HDESK hDesktop,
	_In_opt_ HWND hwndParent,
	_In_ BOOLEAN bChildren,
	_In_ BOOLEAN bUnknownFlag,
	_In_opt_ ULONG dwThreadId,
	_In_opt_ ULONG lParam,
	_Out_ HWND* pWnd,
	_Inout_ PULONG pBufSize
	)

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.

PreviousC++17 and C++20 Interesting FeaturesNextStructured Exception Handlers

Last updated 7 months ago

Was this helpful?

reference.

The default value is 0x0, but when started by a debugger, it defaults to 0x70. The structure that defines the flag values is SYSTEM_FLAGS_INFORMATION or identified by Microsoft itself as , can be consulted , they're used for advanced internal system diagnostic and troubleshooting features.

The FLG_USER_STACK_TRACE_DB (0x1000) flag has a specific interaction with FLG_HEAP_VALIDATE_PARAMETERS (0x40). If FLG_USER_STACK_TRACE_DB is set in either of the GlobalFlag registry values, Windows will set the FLG_HEAP_VALIDATE_PARAMETERS flag, even if it is marked for clearing in the Load Configuration Table. This occurs during the process load phase. For more details, refer to the

The _HEAP structure is documented unofficially

If no pointer to _HEAP structure is known, it can be retrieved by using kernel32!HeapWalk or ntdll!RtlWalkHeap. HeapWalk will need a call to HeapCreate with 0x0, 0x0, 0x0 parameters and feed a structure with the first member, lpData set to NULL.

Inside the PEB structure lives a member named ProcessParameters, which value is a pointer to the struct.

For a comprehensive understanding of the VersionInfo resource, you can refer to this MSDN

is an enumeration that contains the possible values for the ProcessInformationClass parameter of the function. This function is used to retrieve information about a process. It is exported by ntdll.dll and used by the GetProcessInformation function in kernel32.dll. The GetProcessInformation function is used to retrieve information about a process. It is a wrapper for the GetProcessInformation function, which is a user-mode function that calls NtQueryInformationProcess internally.

PEB structure
GFlags
here
Load Configuration Structure documentation
here
PROCESS_HEAP_ENTRY
RTL_USER_PROCESS_PARAMETERS
article
PROCESSINFOCLASS
NtQueryInformationProcess
Windows 10 Segment Heap Internals by Mark Vincent Yason