Part 4 β Evaluation
You are reading Part 4: Evaluation and Results.
- π Part 1: Theory
- π Part 2: Detection
- π Part 3: Implementation
π Explore the complete project here: GhostDebug on GitHub
Evaluation and Results
In this chapter the detectability of the newly developed debugger is examined. First the test environment is described. Then the detectability is measured and compared to classic debuggers like x64dbg, including results with plugins such as ScyllaHide.
In addition, potential attack points of the presented approach are discussed. Although the debugger avoids the classical Debugging APIs and therefore bypasses common detection, strategies exist that could still reveal its presence. These aspects are discussed for further hardening and future improvements.
Test Environment
The test target is a program developed for this purpose. At runtime it executes several debugger detection routines. Only after pressing Enter are the checks triggered, so a debugger must already be attached to test effectiveness. The test target uses the following methods:
IsDebuggerPresent
CheckRemoteDebuggerPresent
- PEB (Process Environment Block)
BeingDebugged
flag NtQueryInformationProcess
(ProcessDebugObjectHandle
,ProcessDebugPort
)- CloseHandle trick with invalid handles
After execution the program prints which checks detected a debugger. Three scenarios were tested. In each case the debugger was attached first, then Enter was pressed to trigger the checks:
- Vanilla x64dbg (without plugins) β standard configuration.
- x64dbg with ScyllaHide β plugin configured to bypass as many checks as possible.
- The new VEH debugger β using the developed debugger to test invisibility.
This setup allows comparison of effectiveness of different debugging approaches.
Detectability of the Debugger
Test Results
Vanilla x64dbg
In standard configuration all checks succeed in detecting the debugger. IsDebuggerPresent
, CheckRemoteDebuggerPresent
, the BeingDebugged
flag, the CloseHandle trick, and NtQueryInformationProcess
all report a debugger. This is expected because x64dbg uses the standard Debug APIs.
x64dbg with ScyllaHide
With ScyllaHide most anti-debugging methods are bypassed. API calls like IsDebuggerPresent
and CheckRemoteDebuggerPresent
are hooked and return false. The BeingDebugged
flag and CloseHandle trick are also bypassed. However, NtQueryInformationProcess
still detects a debugger, because the test target disables the instrumentation callback used by ScyllaHide. This makes NtQueryInformationProcess
a reliable detection method in this test [Check Point 2023a].
VEH-based debugger
All checks report that no debugger is present. Neither IsDebuggerPresent
, CheckRemoteDebuggerPresent
, nor NtQueryInformationProcess
detect anything. Because the debugger avoids the Win32 Debugging Interface entirely, no debug flags are set and no hooks are required. This shows the strength of the Vectored Exception Handling approach: it stays invisible to common detection.
Discussion and Possible Detection Methods
Even with these positive results, detection is still possible:
-
Timing checks β using the CPU instruction RDTSC (Read Time-Stamp Counter). If an application measures delays between instructions, it could detect stepping or inserted breakpoints. This requires the program to explicitly place timing checks [Ferrie 2011].
-
Scanning vectored handler list β an application could inspect
LdrpVectorHandlerList
inntdll.dll
where vectored exception handlers are registered. This could reveal the debuggerβs VEH. However, many legitimate applications (e.g., overlays, streaming tools) also use VEH, so identification is uncertain. A countermeasure is moving the exception hook, e.g., by inserting a trampoline hook inKiUserDispatchException
. -
Kernel-mode anti-debugging β advanced systems (e.g., EasyAntiCheat) move exception handling into kernel mode, overwriting pointers in the Hardware Abstraction Layer (HAL). This allows interception of exceptions and protection of registers such as CR3 against inspection [AVX Blog n.d.]. This goes beyond the scope of usermode debugging.
Summary: The VEH-based debugger is not detected by typical checks and avoids hooks unlike ScyllaHide. Advanced detection is possible but requires much more effort.
Conclusion and Outlook
In this work a debugger was developed that avoids the classical Win32 Debugging APIs and instead uses Vectored Exception Handling. The implementation shows that both breakpoints (INT 3
) and single-stepping can be implemented effectively without triggering common detection.
Summary of Results
Evaluation showed that the VEH-based debugger is almost invisible in typical test environments. By avoiding Win32 interfaces no debug flags are set in the process. Compared to x64dbg in standard configuration, where all detection methods succeeded, the VEH debugger was not detected. Even with ScyllaHide, NtQueryInformationProcess
remained a weak point, while the VEH debugger required no hooks and still stayed hidden.
This demonstrates the strength of the debugger: it operates on the exception layer, outside the usual detection mechanisms.
Possible Improvements
Future extensions include:
-
Manual-mapping injection Instead of loading the DLL with
LoadLibrary
, manual mapping could be used to inject without visible module references in the process [Elastic 2017]. -
Hooking
KiUserDispatchException
To evade scans ofLdrpVectorHandlerList
, a trampoline hook inKiUserDispatchException
could hide the VEH handler registration. -
Extended stepping functions Currently only single-stepping is supported. Step-over and step-out functionality could be added to improve usability.
-
Hardware and memory-access breakpoints In addition to software breakpoints, hardware breakpoints and memory watchpoints could be implemented to monitor specific addresses.
Final Remark
The VEH-based debugger shows that even without the official debug port and APIs, a powerful analysis tool can be built. It offers an alternative to classical debuggers that are easily detected. Despite its unusual basis in exception handling, the core operation is similar to an API-based debugger. The key difference is when and why the operating system forwards an exception to the debugger.