The Hidden Iceberg – Exception Dispatching (Part 3)

In part 2 of this post series, we left with the question concerning how we reach to the ‘first chance exception’ manifested in the debugger. We further see how exceptions and interrupts have associated handlers in kernel mode, called trap-handlers. The CPU stores the address of interrupt dispatch table (IDT) in IDTR register and every entry in IDT corresponds to an exception or interrupt handler routine also called trap handler. We use Windows 7 x86 system for debugging and note that the trap handler symbol for ‘divide by zero’ exception is ‘KiTrap00’.  The processor transfers control to this routine when a ‘divide by zero’ exception occurs. We can set a breakpoint on this handler and get the stack as shown below.

0: kd> kn # ChildEBP RetAddr 
00 93d0dd98 01236a9e nt!_KiTrap00 >>>>trap handler called on exception 
01 93d0ddc8 00000000 Ex1!SafeDiv+0x3e

In this post, we explicate the flow of exception dispatching from user mode to kernel mode which remains unseen by the developers. It is expected that the reader has some knowledge of C and WinDbg or any other related debugging tool.

Reverse Engineering the trap-handler

The ‘KiDispatchException‘ is the key routine to handle the exception and a breakpoint can be set here:

nt!CommonDispatchException+0x45:
82646011 e8b0910700 call nt!KiDispatchException (826bf1c6)

Or use the flow below to reach the call:

0: kd> bp nt!kitrap00

0: kd> g

Breakpoint 0 hit
nt!KiTrap00:
82646200 6a00 push 0

1: kd> pc
nt!KiTrap00+0x94:
82646294 e86fea2400 call nt!Ki386CheckDivideByZeroTrap (82894d08)

1: kd> pc
nt!KiExceptionExit+0x175:
82645fad e81a000000 call nt!CommonDispatchException (82645fcc)

1: kd> t
nt!CommonDispatchException:
82645fcc 83ec50 sub esp,50h

1: kd> pc
nt!CommonDispatchException+0x45:
82646011 e8b0910700 call nt!KiDispatchException (826bf1c6)
VOID
NTAPI 
KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
                    IN PKEXCEPTION_FRAME ExceptionFrame, 
                    IN PKTRAP_FRAME TrapFrame, 
                    IN KPROCESSOR_MODE PreviousMode,
                    IN BOOLEAN FirstChance)

Let’s look at the parameters passed to ‘KiDispatchException’ routine.

1: kd> ub 82646011 
nt!CommonDispatchException+0x36:
82646002 eb03 jmp nt!CommonDispatchException+0x3b (82646007)
82646004 8b456c mov eax,dword ptr [ebp+6Ch]
82646007 83e001 and eax,1
8264600a 6a01 push 1 >>>>>>>FirstChance
8264600c 50 push eax >>>>>>>PreviousMode
8264600d 55 push ebp >>>>>TrapFrame
8264600e 6a00 push 0 >>>>>>ExceptionFrame
82646010 51 push ecx >>>>>ExceptionRecord

Notice the last parameter ‘FirstChance’ is set to true.

First Chance Exception: When the application is being debugged and an exception occurs, the debugger gets notified. At this point, the application is suspended and debugger (WinDbg) decides how to handle the exception.

At this point, we are preparing to dispatch the first chance exception for the notification to the debugger. You can enable ‘Stop on Exception‘ gflag option to verify this. It makes sure that we break into kernel debugger on exception.  Here is the summary of the steps:

  1. Set the breakpoint on the trap handler and continue. Run the executable to generate an exception. You break into debugger on the breakpoint set.
    0: kd> bp nt!KiTrap00
    
    0: kd> g
    Breakpoint 0 hit
    nt!KiTrap00:
    8264b200 6a00 push 0
  2. Make sure ‘Stop on Exception’ is enabled. If not, enable it by running this command.
    !gflag +soe
  3. Set breakpoint on ‘KiDispatchException’.
  4. Continue (g command). You hit the first chance exception.
    0: kd> g
    Integer divide-by-zero - code c0000094 (first chance)
    First chance exceptions are reported before any exception handling.
    This exception may be expected and handled.
    Ex1!SafeDiv+0x3e:
    001b:011e6a9e f77d0c idiv eax,dword ptr [ebp+0Ch]

    The TRAP_FRAME

At this time debugger has to decide if it will handle the exception or pass. Since we have to figure out the flow of the exception dispatching further, we continue without handling the exception by ‘g’ command. Remember that we are still in kernel mode with debugger having the control. In part 2, we saw the creation of the _KTRAP_FRAME structure which has the partial context of the executing thread.

A trap frame structure has space to save all processor register and exception detail. The operating system has to transfer the control back to the application and the way to do that is to restore the trap frame however we can’t go back to the failure point i.e Eip = 0x1076a9e [idiv eax,dword ptr [ebp+0Ch]] (This is from the trap frame in part 2). OS changes the Eip to ntdll routine ‘KiUserExceptionDispatcher’ to dispatch the exception further. To see this in run-time, set ‘break on access’ breakpoint on _KTRAP_FRAME structure’s Eip member.

Breakpoint 0 hit
nt!KiTrap00:
8264b200 6a00 push 0

1: kd> pc
nt!KiTrap00+0x94:
8264b294 e86fea2400 call nt!Ki386CheckDivideByZeroTrap (82899d08)

1: kd> pc
nt!KiExceptionExit+0x175:
8264afad e81a000000 call nt!CommonDispatchException (8264afcc)

1: kd> t
nt!CommonDispatchException:
8264afcc 83ec50 sub esp,50h

1: kd> pc
nt!CommonDispatchException+0x45:
8264b011 e8b0910700 call nt!KiDispatchException (826c41c6)

1: kd> r
eax=00000001 ebx=00cc6a9e ecx=90c6fce0 edx=00000000 esi=00d1dd3c edi=00cc6a9e
eip=8264b011 esp=90c6fccc ebp=90c6fd34 iopl=0 nv up ei pl nz na po nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000202
nt!CommonDispatchException+0x45:
8264b011 e8b0910700 call nt!KiDispatchException (826c41c6)

1: kd> dt nt!_KTRAP_FRAME 90c6fd34 
+0x000 DbgEbp : 0x1df72c
+0x004 DbgEip : 0xcc6a9e
+0x008 DbgArgMark : 0xbadb0d00
+0x00c DbgArgPointer : 0
+0x010 TempSegCs : 0xdd40
+0x012 Logging : 0xd1 ''
+0x013 Reserved : 0 ''
+0x014 TempEsp : 0xd1dd3c
+0x018 Dr0 : 0xffffffff
+0x01c Dr1 : 0x1df72c
+0x020 Dr2 : 0
+0x024 Dr3 : 0x8264b22d
+0x028 Dr6 : 8
+0x02c Dr7 : 0
+0x030 SegGs : 0
+0x034 SegEs : 0x23
+0x038 SegDs : 0x23
+0x03c Edx : 0
+0x040 Ecx : 0x47bd50eb
+0x044 Eax : 0x14
+0x048 PreviousPreviousMode : 0x102
+0x04c ExceptionList : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
+0x050 SegFs : 0x30
+0x054 Edi : 0xd1dd40
+0x058 Esi : 0xd1dd3c
+0x05c Ebx : 0x7ffdd000
+0x060 Ebp : 0x1df72c
+0x064 ErrCode : 0
+0x068 Eip : 0xcc6a9e >>set breakpoint at member Eip 
+0x06c SegCs : 0x1b
+0x070 EFlags : 0x10202
+0x074 HardwareEsp : 0x1df700
+0x078 HardwareSegSs : 0x23
+0x07c V86Es : 0
+0x080 V86Ds : 0
+0x084 V86Fs : 0
+0x088 V86Gs : 0

1: kd> .reload

1: kd> u 0xcc6a9e l1
Ex1!SafeDiv+0x3e :
00cc6a9e f77d0c idiv eax,dword ptr [ebp+0Ch]

1: kd> ba w 4 90c6fd34+68 >>>>>>>address of Eip in _KTRAP_FRAME

1: kd> g
Integer divide-by-zero - code c0000094 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
Ex1!SafeDiv+0x3e:
001b:00cc6a9e f77d0c idiv eax,dword ptr [ebp+0Ch]

1: kd> g
Breakpoint 2 hit
nt!KiDispatchException+0x3d6:
826c459e c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh

1: kd> r
eax=77476448 ebx=90c6fce0 ecx=00000000 edx=00000000 esi=90c6fd34 edi=001df400
eip=826c459e esp=90c6f8c0 ebp=90c6fcc4 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000246
nt!KiDispatchException+0x3d6:
826c459e c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh ss:0010:90c6fcc0=00000000


1: kd> ub 826c459e 
nt!KiDispatchException+0x3bd:
826c4585 0f95c0 setne al
826c4588 48 dec eax
826c4589 83e0fd and eax,0FFFFFFFDh
826c458c 83c03b add eax,3Bh
826c458f 894650 mov dword ptr [esi+50h],eax
826c4592 83663000 and dword ptr [esi+30h],0
826c4596 a1a0f97682 mov eax,dword ptr [nt!KeUserExceptionDispatcher (8276f9a0)]
826c459b 894668 mov dword ptr [esi+68h],eax >>> esi has the _KTRAP_FRAME address

Following the call chain further, we note that ‘KeUserExceptionDispatcher’ calls the ntdll routine ‘KiUserExceptionDispatcher’. ‘KiUserExceptionDispatcher’ calls ‘RtlDispatchException ‘ of ntdll module in user mode.

1: kd> x nt!KeUserExceptionDispatcher
8276f9a0 nt!KeUserExceptionDispatcher = <no type information>
1: kd> dps 8276f9a0 
8276f9a0 77476448 ntdll!KiUserExceptionDispatcher

1: kd> u KiUserExceptionDispatcher
ntdll!KiUserExceptionDispatcher:
77476448 fc cld
77476449 8b4c2404 mov ecx,dword ptr [esp+4]
7747644d 8b1c24 mov ebx,dword ptr [esp]
77476450 51 push ecx
77476451 53 push ebx
77476452 e81328feff call ntdll!RtlDispatchException (77458c6a)
77476457 0ac0 or al,al
77476459 740c je ntdll!KiUserExceptionDispatcher+0x1f (77476467)

1: kd> kn
# ChildEBP RetAddr 
00 001df3f0 77476457 ntdll!RtlDispatchException
01 001df3f0 00cc6a9e ntdll!KiUserExceptionDispatcher+0xf
*** WARNING: Unable to verify checksum for Ex1.exe
02 001df72c 00cc6b28 Ex1!SafeDiv+0x3e
03 001df744 00cc6e24 Ex1!main+0x18
04 (Inline) -------- Ex1!invoke_main+0x1d
05 001df790 773a1174 Ex1!__scrt_common_main_seh+0xff
06 001df79c 7748b3f5 kernel32!BaseThreadInitThunk+0xe
07 001df7dc 7748b3c8 ntdll!__RtlUserThreadStart+0x70
08 001df7f4 00000000 ntdll!_RtlUserThreadStart+0x1b

In the next post, we discuss more in-depth abut the frame-based handlers and how they are called next in the dispatch process.

One Reply to “The Hidden Iceberg – Exception Dispatching (Part 3)”

Leave a Reply

Your email address will not be published. Required fields are marked *