PDA

View Full Version : Kernel debugger vs user mode exceptions


OpenRCE_omega_red
July 14th, 2010, 00:24
Kernel debugger is a nice and nifty tool allowing us to do things not otherwise possible. Total control over debugged OS and all processes is the main reason to use it. However, there are some hiccups and obstacles that may disrupt our work. One of the most common is the case of intercepting user-mode exceptions with kernel-mode debugger.

Let's assume we have windbg connected to the debuggee OS as a kernel mode debugger. What can we do to catch user-mode exceptions that interest us? First, there is the 'Debug | Event Filters' menu (or sx* commands) that controls debugger's behavior when it does encounter an exception in debugged code. In short, 'Execution - Enabled' option tells the debugger to break on the specific exception. There is a catch though - it only works for kernel mode code 'out of the box'. That is, if we enable breaks on 'Illegal instruction' and run some user-mode program on the debugged OS that generates it, windbg won't break. Why? Well, we're in the kernel debug mode after all.

How to make it work then? It's pretty simple. All NT-based Windows systems support 'Global Flags' debugging mechanism in the kernel, which is a collection of system-wide debugging flags. From within windbg we can access it using '!gflag' extension command. And one of the flags is 'Break on exceptions' - which means kernel debugger will be notified not only of kernel-mode exceptions, but also user-mode ones. Neat. To activate it, use '!gflag +soe' windbg command.

Now all is well, we can see that windbg breaks on every exception in user-mode code. Or does it? There is still one special case that evades our cleverly laid traps. If the user-mode program A is being debugged (using user-mode Debug API) by user-mode program B, we (windbg running as a kernel-mode debugger) won't get exceptions coming from program A - program B will get them instead. It's a bit counter-intuitive, as one would think that a kernel-mode debugger should receive every exception before user-mode debuggers. That isn't the case though, and it seems to be the design decision by Microsoft. All is not lost though - we can still force windbg to receive every and all exceptions before they get to any user-mode debugger in the debugged OS.

To learn how to do that, we need to dive deep into the Windows' kernel function responsible for kernel-mode exception dispatching - KiDispatchException. This is the 'main' code responsible for deciding what to do with an exception that was encountered. It services both kernel-mode and user-mode exceptions, first- and second-chance ones, and most importantly - decides whether to notify kernel debugger about the event or not. Not all events are forwarded to kd (kernel debugger), as we've learned before. But because we are in control of the target system, we can modify the KiDispatchException routine to do our bidding - or routing ALL exceptions to kernel debugger first.

The exact details of the patch vary between systems, but structure of KiDispatchException function is pretty much the same. Using IDA to reverse engineer the kernel, studying Windows Research Kernel or ReactOS sources certainly helps. Disassembly of original KiDispatchException function along with the patch point from two Windows systems is provided below - 32-bit Windows XP Pro and 64-bit Windows 7 with all updates as of 20<0-07-<4. Modifying other kernels is left as an exercise to the reader.

XP 32-bit ("http://omeg.pl/code/XP_32_KiDispatchException.txt")
7 64-bit ("http://omeg.pl/code/7_64_KiDispatchException.txt")

https://www.openrce.org/blog/view/<564/Kernel_debugger_vs_user_mode_exceptions

Indy
July 18th, 2010, 14:33
KiDebugRoutine: http://indy-vx.narod.ru/Temp/Kdp.zip ("http://indy-vx.narod.ru/Temp/Kdp.zip")
Code:
typedef
BOOLEAN
(*PKDEBUG_ROUTINE) (
IN PKTRAP_FRAME TrapFrame,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PEXCEPTION_RECORD ExceptionRecord,
IN PCONTEXT ContextRecord,
IN KPROCESSOR_MODE PreviousMode,
IN BOOLEAN SecondChance
);

o IF
o PASSIVE_LEVEL
o KTRAP_FRAME is formed.