Back to the Start – Exception Handling (Part 4)

The C programming language does not offer any support to handle exceptions in the program and this is one of the major differences between C and C++. The Structured Exception Handling (SEH) is an extension to Microsoft C++ and C language support. Although, it is recommended to use the in-built exception handling mechanism if you are using C++  because it helps with the portability of the code across platforms. But, Microsoft compiler allows us to use SEH with C++ and C. The SEH provides a way to handle resources (memory buffers, synchronization primitives, etc) in case the program flow is interrupted due to software or hardware exceptions.

The SEH mechanism which was mentioned in the first post is :

  • Exception handler – __except block: called in response to an exception.
  • Termination handler – __finally block: always called.

When the developer defines the SEH block (aka exception frame), it gets installed on the stack and with the unwinding of the stack, it gets removed. The exception can be caught in the same function or the caller function. In terms of unwinding the stack, if the exception is unhandled in the callee, it goes higher up the stack in the previous or caller’s frame and appropriate action is taken. So the above two SEH mechanisms may be different in terms of functionality but as far as unwinding the stack is concerned, they are similar.

In this post, we dig into the flow of the exception in user-mode (supported by OS) and in the developer’s code (exception or termination handler written by the developer). It is expected that the reader has some knowledge of C and WinDbg or any other related debugging tool.

The Thread Environment

Let’s take a look at the stack once we return to the user-mode after the ‘First Chance’ exception (see previous post).

1: kd> kn
# ChildEBP RetAddr 
00 0016f6c4 77596457 ntdll!RtlDispatchException+0x8
01 0016f6c4 00f76a9e ntdll!KiUserExceptionDispatcher+0xf
02 0016fa00 00f76b28 Ex1!SafeDiv+0x3e
03 0016fa18 00f76e24 Ex1!main+0x18
04 (Inline) -------- Ex1!invoke_main+0x1d
05 0016fa64 774c1174 Ex1!__scrt_common_main_seh+0xff
06 0016fa70 775ab3f5 kernel32!BaseThreadInitThunk+0xe
07 0016fab0 775ab3c8 ntdll!__RtlUserThreadStart+0x70
08 0016fac8 00000000 ntdll!_RtlUserThreadStart+0x1b

1: kd> r
eax=00000014 ebx=0016f6dc ecx=0016f6f0 edx=00000000 esi=00fcdd3c edi=00fcdd40
eip=77578c72 esp=0016f660 ebp=0016f6c4 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
ntdll!RtlDispatchException+0x8:
001b:77578c72 56 push esi

The control transfer happened to the user-mode function ‘ntdll!KiUserExceptionDispatcher’ from kernel mode, and both ‘RtlDispatchException’ and ‘KiUserExceptionDispatcher’ share the same frame (ChildEBP). Check the previous post on how the control is transferred from the kernel mode to user mode during exception dispatch.

Digging further in ‘RtlDispatchException’, there are a couple of function calls but those of interest in the context of this post are highlighted in bold below.

1: kd> uf /c ntdll!RtlDispatchException
ntdll!RtlDispatchException (77578c6a)
  ntdll!RtlDispatchException+0x14 (77578c7e):
    call to ntdll!RtlCallVectoredExceptionHandlers (77578e9d)
  ntdll!RtlDispatchException+0x30 (77578c95):
    call to ntdll!RtlpGetStackLimits (77578c29)
  ntdll!RtlDispatchException+0x35 (77578c9a):
    call to ntdll!RtlpGetRegistrationHead (7759672f)
  ntdll!RtlDispatchException+0x52 (77578cb7):
    call to ntdll!ZwQueryInformationProcess (77595490)
  ntdll!RtlDispatchException+0xc0 (77578cd2):
    call to ntdll!RtlpGetRegistrationHead (7759672f)
  ntdll!RtlDispatchException+0x109 (77578d1f):
    call to ntdll!RtlIsValidHandler (77578d6c)
  ntdll!RtlDispatchException+0x122 (77578d38):
    call to ntdll!RtlpExecuteHandlerForException (77596598)

The function ‘RtlpGetRegistrationHead’ is to get the exception registration list head stored in Thread Environment Block (TEB) structure. Now you know why the exceptions are thread-specific.

The pointer fetched from ‘RtlpGetRegistrationHead’ is passed to ‘RtlpExecuteHandlerForException’ as one of the parameters which eventually calls the exception handler. Let’s dump the content to understand this better.

1: kd> uf ntdll!RtlpGetRegistrationHead
ntdll!RtlpGetRegistrationHead:
7759672f 64a100000000 mov eax,dword ptr fs:[00000000h]
77596735 c3 ret

<In x86 system FS segment register points to the TEB>
1: kd> !teb
TEB at 7ffdf000
    ExceptionList:        0016f9f0
    StackBase:            00170000
    StackLimit:           0016d000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7ffdf000
    EnvironmentPointer:   00000000
    ClientId:             00000274 . 000006d4
    RpcHandle:            00000000
    Tls Storage:          7ffdf02c
    PEB Address:          7ffd5000
    LastErrorValue:       0
    LastStatusValue:      c0000135
    Count Owned Locks:    0
    HardErrorMode:        0

1: kd> dd fs:0
003b:00000000 0016f9f0 00170000 0016d000 00000000
003b:00000010 00001e00 00000000 <snip>

The ‘ExceptionList’ in TEB is of type ‘_EXCEPTION_REGISTRATION_RECORD’. Here is the chain of exception handler starting from the list head.

1: kd> dt ntdll!_EXCEPTION_REGISTRATION_RECORD 0016f9f0
+0x000 Next : 0x0016fa54 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x00f77f60 _EXCEPTION_DISPOSITION Ex1!_except_handler4+0

1: kd> dt 0x0016fa54 _EXCEPTION_REGISTRATION_RECORD
Ex1!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0x0016faa0 _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x00f77f60 _EXCEPTION_DISPOSITION Ex1!_except_handler4+0

1: kd> dt 0x0016faa0 _EXCEPTION_REGISTRATION_RECORD
Ex1!_EXCEPTION_REGISTRATION_RECORD
+0x000 Next : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x7756d74d _EXCEPTION_DISPOSITION ntdll!_except_handler4+0

You can also dump this list via the following WinDbg Command.

1: kd> !exchain
0016f9f0: Ex1!_except_handler4+0 (00f77f60)
  CRT scope  0, filter: Ex1!SafeDiv+4f (00f76aaf)
                func:   Ex1!SafeDiv+55 (00f76ab5)
0016fa54: Ex1!_except_handler4+0 (00f77f60)
  CRT scope  0, filter: Ex1!__scrt_common_main_seh+139 (00f76e5e)
                func:   Ex1!__scrt_common_main_seh+14d (00f76e72)
0016faa0: ntdll!_except_handler4+0 (7756d74d)

User-mode Exception Handling

Let’s review the code with exception handling. The filter function just returns the exception code ‘EXCEPTION_EXECUTE_HANDLER’ and results in the execution of the exception handling block with print routine. We look at the C code and corresponding assembly code to get a better insight into the internals of the exception handling mechanism.

BOOL SafeDiv(int dividend, int divisor, int *pResult)
{
/*
parameter validation
*/

__try 
{ 
*pResult = dividend / divisor; 
} 
__except( FilterFunction() )
{ 
printf("Exception Handled!\n");
return FALSE;
}
return TRUE;
}

DWORD FilterFunction() 
{ 
return EXCEPTION_EXECUTE_HANDLER; 
}

Disassembly of the filter function from the registration head of the output of ‘!exchain’ WinDbg command:

1: kd> u 00f76aaf
Ex1!SafeDiv+0x4f:
00f76aaf e8f3d0ffff call Ex1!ILT+11170(_FilterFunction) (00f73ba7)
00f76ab4 c3 ret

1: kd> u 00f76ab5
Ex1!SafeDiv+0x55:
00f76ab5 8b65e8 mov esp,dword ptr [ebp-18h]
00f76ab8 684cc6fc00 push offset Ex1!_mbcasemap+0x124 (00fcc64c)
00f76abd e860ceffff call Ex1!ILT+10525(_printf) (00f73922)
00f76ac2 83c404 add esp,4
00f76ac5 c745e400000000 mov dword ptr [ebp-1Ch],0
00f76acc c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh
00f76ad3 8b45e4 mov eax,dword ptr [ebp-1Ch]
00f76ad6 eb0c jmp Ex1!SafeDiv+0x84 (00f76ae4)

1: kd> dc 00fcc64c l5
00fcc64c 65637845 6f697470 6148206e 656c646e Exception Handle
00fcc65c 000a2164 d!..

Here is the complete flow to get the stack for the Filter Function in WinDbg:

1: kd> !process 0 0 ex1.exe
PROCESS 84e7ac30 SessionId: 1 Cid: 0380 Peb: 7ffd5000 ParentCid: 0428
DirBase: 3ec163e0 ObjectTable: 9d9a5188 HandleCount: 11.
Image: Ex1.exe

1: kd> bp /p 84e7ac30 ntdll!RtlpExecuteHandlerForException

1: kd> kn
# ChildEBP RetAddr 
00 002bf4f0 77596457 ntdll!RtlpExecuteHandlerForException
01 002bf4f0 002d6a9e ntdll!KiUserExceptionDispatcher+0xf
02 002bf82c 002d6b28 Ex1!SafeDiv+0x3e
03 002bf844 002d6e24 Ex1!main+0x18
04 (Inline) -------- Ex1!invoke_main+0x1d
05 002bf890 774c1174 Ex1!__scrt_common_main_seh+0xff
06 002bf89c 775ab3f5 kernel32!BaseThreadInitThunk+0xe
07 002bf8dc 775ab3c8 ntdll!__RtlUserThreadStart+0x70
08 002bf8f4 00000000 ntdll!_RtlUserThreadStart+0x1b
1: kd> pc
ntdll!ExecuteHandler+0x1f:
001b:775965c6 e808000000 call ntdll!ExecuteHandler2 (775965d3)
1: kd> t
ntdll!ExecuteHandler2:
001b:775965d3 55 push ebp
1: kd> pc
ntdll!ExecuteHandler2+0x24:
001b:775965f7 ffd1 call ecx
0: kd> t
Ex1!_except_handler4:
001b:002d7f60 55 push ebp
0: kd> pc
Ex1!_except_handler4+0x2b:
001b:002d7f8b e890ffffff call Ex1!ValidateLocalCookies (002d7f20)
1: kd> pc
Ex1!_except_handler4+0x34:
001b:002d7f94 e87aa0ffff call Ex1!ILT+4110(___except_validate_context_record) (002d2013)
0: kd> pc
Ex1!_except_handler4+0x7c:
001b:002d7fdc e86fb3ffff call Ex1!ILT+9035(_EH4_CallFilterFunc (002d3350)

1: kd> kn
# ChildEBP RetAddr 
00 002bf41c 775965f9 Ex1!ILT+9035(_EH4_CallFilterFunc
01 002bf440 775965cb ntdll!ExecuteHandler2+0x26
02 002bf4f0 77596457 ntdll!ExecuteHandler+0x24
03 002bf4f0 002d6a9e ntdll!KiUserExceptionDispatcher+0xf
04 002bf82c 002d6b28 Ex1!SafeDiv+0x3e
05 002bf844 002d6e24 Ex1!main+0x18
06 (Inline) -------- Ex1!invoke_main+0x1d
07 002bf890 774c1174 Ex1!__scrt_common_main_seh+0xff
08 002bf89c 775ab3f5 kernel32!BaseThreadInitThunk+0xe
09 002bf8dc 775ab3c8 ntdll!__RtlUserThreadStart+0x70
0a 002bf8f4 00000000 ntdll!_RtlUserThreadStart+0x1b

1: kd> kn
# ChildEBP RetAddr 
00 002bf3d8 002d8980 Ex1!SafeDiv+0x4f
01 002bf3ec 002d7fe1 Ex1!_EH4_CallFilterFunc+0x12
02 002bf41c 775965f9 Ex1!_except_handler4+0x81
03 002bf440 775965cb ntdll!ExecuteHandler2+0x26
04 002bf4f0 77596457 ntdll!ExecuteHandler+0x24
05 002bf4f0 002d6a9e ntdll!KiUserExceptionDispatcher+0xf
06 002bf82c 002d6b28 Ex1!SafeDiv+0x3e
07 002bf844 002d6e24 Ex1!main+0x18
08 (Inline) -------- Ex1!invoke_main+0x1d
09 002bf890 774c1174 Ex1!__scrt_common_main_seh+0xff
0a 002bf89c 775ab3f5 kernel32!BaseThreadInitThunk+0xe
0b 002bf8dc 775ab3c8 ntdll!__RtlUserThreadStart+0x70
0c 002bf8f4 00000000 ntdll!_RtlUserThreadStart+0x1b

Since the exception is handled we don’t get into the second chance exception. But we have one item left for discussion which is  ‘How did our handler get into the exception handler list head in TEB?’. In the next part of the post, we dig into the exception handler registration (installation on the stack) and conclude by summarizing the flow.

Leave a Reply

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