The exception hierarchy of the operating system allows the developers to handle both hardware and software exceptions in a unified way and also improves the modularity of the code being developed. In the previous post we observed how the disassembly of a guarded code (__try, __except) block is different. In this post and explore how the system takes control when the exception happens. It is expected that the reader has some knowledge of C and WinDbg or any other related debugging tool.
Catching Exception In Debugger
Using WinDbg, load the application symbols during loader break and set a breakpoint on ‘main’ routine
.reload /f ex1.exe bp ex1!main
Let’s put a breakpoint on ntdll exception dispatch routine ‘RtlDispatchException’ as well so that we catch the exception flow when OS gives the control back to the application and continue (g command in WinDbg). Since we are intentionally dividing by zero, debugger breaks in. Notice what it says on top – “First Chance”.
First Chance Exception: When the application is being debugged and an exception occurs, debugger get notified. At this point application is suspended and debugger (WinDbg) decides how to handle the exception.
0:000> bp ntdll!RtlDispatchException 0:000> g (1b0c.2258): Integer divide-by-zero - code c0000094 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=00000014 ebx=7f3e5000 ecx=99f63d15 edx=00000000 esi=002bdd3c edi=002bdd40 eip=00266a9e esp=00bdf850 ebp=00bdf87c iopl=0 nv up ei ng nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010286 Ex1!SafeDiv+0x3e: 00266a9e f77d0c idiv eax,dword ptr [ebp+0Ch] ss:002b:00bdf888=00000000 BOOL SafeDiv(int dividend, int divisor, int *pResult) <<snip>> __try { *pResult = dividend / divisor; } <<snip>> Ex1!SafeDiv+0x26: 00266a86 50 push eax 00266a87 8d45f0 lea eax,[ebp-10h] 00266a8a 64a300000000 mov dword ptr fs:[00000000h],eax 00266a90 8965e8 mov dword ptr [ebp-18h],esp 00266a93 c745fc00000000 mov dword ptr [ebp-4],0 00266a9a 8b4508 mov eax,dword ptr [ebp+8] >>>>first parameter (dividend) goes in eax (hex 14) 00266a9d 99 cdq 00266a9e f77d0c idiv eax,dword ptr [ebp+0Ch] >>>>second parameter (divisor) in 'ebp+0c' location (NULL or Zero) 0:000> ?@ebp+0c Evaluate expression: 12449928 = 00bdf888 0:000> dd 00bdf888 l1 00bdf888 00000000 0:000> r eax eax=00000014
0:000> kn
# ChildEBP RetAddr
00 00bdf384 77b005bf ntdll!RtlDispatchException
01 00bdf384 00266a9e ntdll!KiUserExceptionDispatcher+0xf
02 00bdf87c 00266b28 Ex1!SafeDiv+0x3e >>>>>>>>>>>>>>>>>>
03 00bdf894 00266e24 Ex1!main+0x18
04 (Inline) -------- Ex1!invoke_main+0x1d
05 00bdf8e0 75777c04 Ex1!__scrt_common_main_seh+0xff
06 00bdf8f4 77b1ab8f KERNEL32!BaseThreadInitThunk+0x24
07 00bdf93c 77b1ab5a ntdll!__RtlUserThreadStart+0x2f
08 00bdf94c 00000000 ntdll!_RtlUserThreadStart+0x1b
-
- What happened in between?
- What happened before first chance notification?
0:000> u Ex1!SafeDiv+0x3e l2 Ex1!SafeDiv+0x3e: 00266a9e f77d0c idiv eax,dword ptr [ebp+0Ch] >>>>>Exception occurred here 00266aa1 8b4d10 mov ecx,dword ptr [ebp+10h]>>>>>next instruction in sequence
Exception Handling By Processor

0: kd> !idt -a Dumping IDT: 80b95400 d850b41900000000: 82649200 nt!_KiTrap00 >>>divide by zero trap handler d850b41900000001: 82649390 nt!_KiTrap01 d850b41900000002: Task Selector = 0x0058 d850b41900000003: 82649800 nt!_KiTrap03 d850b41900000004: 82649988 nt!_KiTrap04 d850b41900000005: 82649ae8 nt!_KiTrap05 d850b41900000006: 82649c5c nt!_KiTrap06 d850b41900000007: 8264a258 nt!_KiTrap07 d850b41900000008: Task Selector = 0x0050 d850b41900000009: 8264a6b8 nt!_KiTrap09 d850b4190000000a: 8264a7dc nt!_KiTrap0A <snip>

The OS TRAP
ba e1 nt!kitrap00
0: kd> kn
# ChildEBP RetAddr
00 93d0dd98 01236a9e nt!_KiTrap00 >>>>we hit the trap handler on exception
01 93d0ddc8 00000000 Ex1!SafeDiv+0x3e
In order to get the trap frame we can either look for ‘KiDispatchException’ or just run the command ‘pc’ from our breakpoint on ‘KiTrap00’. You will find the trap frame in ‘ebp’ register as shown below.
1: kd> r eax=00000023 ebx=0028fde0 ecx=851f0128 edx=00000000 esi=010cdd3c edi=01076a9e eip=8264d294 esp=9322fd30 ebp=9322fd34 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!KiTrap00+0x94: 8264d294 e86fea2400 call nt!Ki386CheckDivideByZeroTrap (8289bd08) 1: kd> ub 8264d294 nt!KiTrap00+0x80: 8264d280 6a00 push 0 8264d282 6a00 push 0 8264d284 6a7f push 7Fh 8264d286 e848900900 call nt!KeBugCheck2 (826e62d3) 8264d28b 66837d6c1b cmp word ptr [ebp+6Ch],1Bh 8264d290 751d jne nt!KiTrap00+0xaf (8264d2af) 8264d292 fb sti 8264d293 55 push ebp >>>>parameter to Ki386CheckDivideByZeroTrap 1: kd> dt 9322fd34 nt!_ktrap_frame +0x000 DbgEbp : 0x28fde0 +0x004 DbgEip : 0x1076a9e +0x008 DbgArgMark : 0xbadb0d00 +0x00c DbgArgPointer : 0 +0x010 TempSegCs : 0xdd40 +0x012 Logging : 0xc '' +0x013 Reserved : 0x1 '' +0x014 TempEsp : 0x10cdd3c +0x018 Dr0 : 0xffffffff +0x01c Dr1 : 0x28fde0 +0x020 Dr2 : 0 +0x024 Dr3 : 0x8264d22d +0x028 Dr6 : 8 +0x02c Dr7 : 0 +0x030 SegGs : 0 +0x034 SegEs : 0x23 +0x038 SegDs : 0x23 +0x03c Edx : 0 +0x040 Ecx : 0x278b5988 +0x044 Eax : 0x14 +0x048 PreviousPreviousMode : 0x102 +0x04c ExceptionList : 0xffffffff _EXCEPTION_REGISTRATION_RECORD +0x050 SegFs : 0x30 +0x054 Edi : 0x10cdd40 +0x058 Esi : 0x10cdd3c +0x05c Ebx : 0x7ffdf000 +0x060 Ebp : 0x28fde0 +0x064 ErrCode : 0 +0x068 Eip : 0x1076a9e>>>>>>>>>>>>>address of exception +0x06c SegCs : 0x1b +0x070 EFlags : 0x10202 +0x074 HardwareEsp : 0x28fdb4 +0x078 HardwareSegSs : 0x23 +0x07c V86Es : 0 +0x080 V86Ds : 0 +0x084 V86Fs : 0 +0x088 V86Gs : 0 1: kd> u 0x1076a9e l1 Ex1_1070000!SafeDiv+0x3e: 01076a9e f77d0c idiv eax,dword ptr [ebp+0Ch]
In the next part of the series will explore creation of the exception record, exception context and how we travel back to user mode.