Everybody likes automation (!exchain) – Exception Handling (Part 5)

This is the last post related to the internals of structured exception handling (SEH). In this post, we look at the summary of the exception handling and dispatching, a WinDbg extension ‘exchain’ to view the chain of registered exception handlers.¬† We start by inspecting the disassembly of the exception handling code. We look at how the exception handler frames are created on the stack and unwinded as the programs execute. Next, we use the WinDbg extension to display the exception registration records and understand the nested structure in case of nested __try, __except block. It is expected that the reader has some knowledge of C and WinDbg or any other related debugging tool.

Revisiting Try-Except

Let’s take a look at the code and compiler-generated disassembled code again to understand how the registration record is created:

#include<stdio.h>
#include <windows.h>

void Div(int dividend, int divisor, int *pResult);
BOOL SafeDiv(int dividend, int divisor, int *pResult);
DWORD FilterFunction() ;

int main()
{
int result = 0;

if(!SafeDiv(20,0,&result))
{
printf("SafeDiv Failed!\n");
return(-1);
}
else
{
printf("Result = %d\n", result);
} 
return(0);
}

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

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

void Div(int dividend, int divisor, int *pResult)
{ 
/*
parameter validation
*/ 
*pResult = dividend / divisor; 
}

DWORD FilterFunction() 
{ 
return EXCEPTION_EXECUTE_HANDLER; 
}

The disassembly of the ‘SafeDiv’ function containing the __try, __except block and the area of interest in the code is highlighted in the diff below. The additional code generated by the compiler to push the ‘_except_handler4’ followed by the record getting added in the handler list in TEB which can be accessed via the FS segment register in x86 architecture. In x86 architecture, the FS segment register points to the TEB of the current executing thread.

Automation using ‘exchain’

To dig deeper into the internals of exception registration and understand the registration record structure, we break into the ‘SafeDiv’ using WinDbg. As we proceed, we match the output of ‘exchain’ with the memory content containing the exception registration information.

0: kd> kn
# ChildEBP RetAddr 
00 0035f808 00056b28 Ex1!SafeDiv+0x19
01 0035f820 00056e24 Ex1!main+0x18
02 (Inline) -------- Ex1!invoke_main+0x1d
03 0035f86c 774c1174 Ex1!__scrt_common_main_seh+0xff
04 0035f878 775ab3f5 kernel32!BaseThreadInitThunk+0xe
05 0035f8b8 775ab3c8 ntdll!__RtlUserThreadStart+0x70
06 0035f8d0 00000000 ntdll!_RtlUserThreadStart+0x1b

0: kd> r
eax=0035f85c ebx=7ffde000 ecx=4908580c edx=00000001 esi=000add3c edi=000add40
eip=00056a79 esp=0035f7ec ebp=0035f808 iopl=0 nv up ei pl nz na po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000203
Ex1!SafeDiv+0x19:
001b:00056a79 53 push ebx

0: kd> ub Ex1!SafeDiv+0x19
Ex1!SafeDiv:
001b:00056a60 55 push ebp
001b:00056a61 8bec mov ebp,esp
001b:00056a63 6afe push 0FFFFFFFEh
001b:00056a65 6850b60a00 push offset Ex1!__rtc_tzz+0x104 (000ab650)
001b:00056a6a 68607f0500 push offset Ex1!_except_handler4 (00057f60)
001b:00056a6f 64a100000000 mov eax,dword ptr fs:[00000000h]
001b:00056a75 50 push eax
001b:00056a76 83c4f4 add esp,0FFFFFFF4h

Highlighted above are the pushes done to create ‘_EH4_EXCEPTION_REGISTRATION_RECORD’. Here is the stack after the pushes.

0: kd> dps 0035f7ec l30
0035f7ec 00000fa0
0035f7f0 00000000
0035f7f4 0007ee1b Ex1!__acrt_InitializeCriticalSectionEx+0x5c
0035f7f8 0035f85c
0035f7fc 00057f60 Ex1!_except_handler4
0035f800 000ab650 Ex1!__rtc_tzz+0x104
0035f804 fffffffe
0035f808 0035f820
0035f80c 00056b28 Ex1!main+0x18
0035f810 00000014
0035f814 00000000
0035f818 0035f81c
0035f81c 00000000
0035f820 0035f86c
0035f824 00056e24 Ex1!__scrt_common_main_seh+0xff
<snip>

0: kd> dt 0035f7f0 _EH4_EXCEPTION_REGISTRATION_RECORD
Ex1!_EH4_EXCEPTION_REGISTRATION_RECORD
+0x000 SavedESP : (null) 
+0x004 ExceptionPointers : 0x0007ee1b _EXCEPTION_POINTERS
+0x008 SubRecord : _EXCEPTION_REGISTRATION_RECORD
+0x010 EncodedScopeTable : 0xab650
+0x014 TryLevel : 0xfffffffe

The ‘TryLevel’ defines the number of nested __try blocks.

The ‘EncodedScopeTable’ corresponds to the ‘_EH4_SCOPETABLE’ structure which contains the user-defined exception filter.

Next, we look into the memory content after executing the push instructions.

001b:00056a65 6850b60a00 push offset Ex1!__rtc_tzz+0x104 (000ab650)
001b:00056a6a 68607f0500 push offset Ex1!_except_handler4 (00057f60)

0: kd> dt 0035f7f0 _EH4_EXCEPTION_REGISTRATION_RECORD SubRecord.
Ex1!_EH4_EXCEPTION_REGISTRATION_RECORD
+0x008 SubRecord : 
+0x000 Next : 0x0035f85c _EXCEPTION_REGISTRATION_RECORD
+0x004 Handler : 0x00057f60 _EXCEPTION_DISPOSITION 
Ex1!_except_handler4+0

Here is the scope Table for the current Registration record. The ‘ScopeRecords’ in the Scope table corresponds to the number of exception handlers registered.

0: kd> dt 0035f7f0 _EH4_EXCEPTION_REGISTRATION_RECORD
Ex1!_EH4_EXCEPTION_REGISTRATION_RECORD
+0x000 SavedESP : (null) 
+0x004 ExceptionPointers : 0x0007ee1b _EXCEPTION_POINTERS
+0x008 SubRecord : _EXCEPTION_REGISTRATION_RECORD
+0x010 EncodedScopeTable : 0xab650
+0x014 TryLevel : 0xfffffffe

0: kd> dt 0xab650 _EH4_SCOPETABLE 
Ex1!_EH4_SCOPETABLE
+0x000 GSCookieOffset : 0xfffffffe
+0x004 GSCookieXOROffset : 0
+0x008 EHCookieOffset : 0xffffffd4
+0x00c EHCookieXOROffset : 0
+0x010 ScopeRecord : [1] _EH4_SCOPETABLE_RECORD

0: kd> dt 0xab650 _EH4_SCOPETABLE ScopeRecord.
Ex1!_EH4_SCOPETABLE
+0x010 ScopeRecord : [1] 
+0x000 EnclosingLevel : 0xfffffffe
+0x004 FilterFunc : 0x00056aaf long Ex1!SafeDiv+0
+0x008 u : <unnamed-tag>
0: kd> ln 0x00056aaf 
(00056a60) Ex1!SafeDiv+0x4f | (00056b00) Ex1!FilterFunction

0: kd> dt 0xab650 _EH4_SCOPETABLE ScopeRecord.u.
Ex1!_EH4_SCOPETABLE
+0x010 ScopeRecord : [1] 
+0x008 u : 
+0x000 HandlerAddress : 0x00056ab5 void Ex1!SafeDiv+0
+0x000 FinallyFunc : 0x00056ab5 void Ex1!SafeDiv+0
0: kd> ln 0x00056ab5
(00056a60) Ex1!SafeDiv+0x55 | (00056b00) Ex1!FilterFunction

At this point, we don’t have the exception handler added to the exception registration record list head in the TEB. We also display the output of the ‘!exchain’ WinDbg command to verify. The currently registered records are for the function¬† ‘Ex1!__scrt_common_main_seh’ which would have called ‘Invoke_main’ in __try __except block.

0: kd> !exchain
0035f85c: Ex1!_except_handler4+0 (00057f60)
CRT scope 0, filter: Ex1!__scrt_common_main_seh+139 (00056e5e)
func: Ex1!__scrt_common_main_seh+14d (00056e72)
0035f8a8: ntdll!_except_handler4+0 (7756d74d)
CRT scope 0,

Continuing further in the code, we notice a write to ‘FS:0’ in the following lines:

00266a87 8d45f0 lea eax,[ebp-10h]
00266a8a 64a300000000 mov dword ptr fs:[00000000h],eax

As mentioned earlier, the FS segment register has the TEB of the currently executing thread and ‘FS:0’ points to the exception registration record list.

0: kd> 
eax=0035f7f8 ebx=7ffde000 ecx=4908580c edx=00000001 esi=000add3c edi=000add40
eip=00056a8a esp=0035f7dc ebp=0035f808 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
Ex1!SafeDiv+0x2a:
001b:00056a8a 64a300000000 mov dword ptr fs:[00000000h],eax fs:003b:00000000=0035f85c

Our ‘_EH4_EXCEPTION_REGISTRATION_RECORD’ is at 0035f7f0¬† and SubRecord which is of type ‘_EXCEPTION_REGISTRATION_RECORD’ is at +8 offset, passed in EAX register highlighted above and currently being added to the list. Now, if we check the output of the ‘!exchain’ WinDbg command, we see our exception registration record is added to the handler list. You have the filter and handler for the Scope mentioned as well.

0: kd> p
Ex1!SafeDiv+0x30:
001b:00056a90 8965e8 mov dword ptr [ebp-18h],esp

0: kd> !exchain
0035f7f8: Ex1!_except_handler4+0 (00057f60)
CRT scope 0, filter: Ex1!SafeDiv+4f (00056aaf)
func: Ex1!SafeDiv+55 (00056ab5)
0035f85c: Ex1!_except_handler4+0 (00057f60)
CRT scope 0, filter: Ex1!__scrt_common_main_seh+139 (00056e5e)
func: Ex1!__scrt_common_main_seh+14d (00056e72)
0035f8a8: ntdll!_except_handler4+0 (7756d74d)
CRT scope 0,

Let’s take a look at the output of the nested try block:

void main()
{
char str[10] = "test";
char buf[12];
__try // try block 0
{
__try // try block 1
{
*(int*)123=456;
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
{
printf("Access violation");
}
strcpy(buf,str);
}
__finally
{
puts("in finally");
}
}

0:000> !exchain
008dfcdc: ex2!_except_handler4+0 (00f57f90)
CRT scope 1, filter: ex2!main+70 (00f56b00)
func: ex2!main+97 (00f56b27)
CRT scope 0, func: ex2!main+cc (00f56b5c)
008dfd28: ex2!_except_handler4+0 (00f57f90)
CRT scope 0, filter: ex2!__scrt_common_main_seh+139 (00f56ea3)
func: ex2!__scrt_common_main_seh+14d (00f56eb7)
008dfd84: ntdll!_except_handler4+0 (77b17380)
CRT scope 0, filter: ntdll!__RtlUserThreadStart+545c3 (77b2f123)
func: ntdll!__RtlUserThreadStart+5460a (77b2f16a)
008dfd9c: ntdll!FinalExceptionHandlerPad58+0 (77ac0009)

There is only one filter function as there is only one __except block and we have two handlers (One for __except and one for __finally). For each nesting level, we will have one ScopeRecord in the ScopeTable.

Let’s just duplicate the code create nested __try, __except block and see the difference in the exception handler registration and Scope table.

#include <windows.h>
#include<stdio.h>
void main()
{
char str[10] = "test";
char buf[12];

__try // try block 0
{
__try // try block 1
{
*(int*)123=456; //Exception 1
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
{
printf("Access violation");
}
strcpy(buf,str);
}
__finally
{
puts("in finally");
}

/********************/

__try // try block 2
{
__try // try block 3
{
*(int*)123=456; //Exception 2
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
{
printf("Access violation");
}
strcpy(buf,str);
}
__except(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
{
puts("in finally");
}
}

On hitting the exception 1, here is the ‘!exchain’ output:

0:000> !exchain
0105f86c: Ex3!_except_handler4+0 (00dc8070)
CRT scope 1, filter: Ex3!main+70 (00dc6b00)
func: Ex3!main+97 (00dc6b27)
CRT scope 0, func: Ex3!main+cc (00dc6b5c)
0105f8b8: Ex3!_except_handler4+0 (00dc8070)
CRT scope 0, filter: Ex3!__scrt_common_main_seh+139 (00dc6f83)
func: Ex3!__scrt_common_main_seh+14d (00dc6f97)
0105f914: ntdll!_except_handler4+0 (77b17380)
CRT scope 0, filter: ntdll!__RtlUserThreadStart+545c3 (77b2f123)
func: ntdll!__RtlUserThreadStart+5460a (77b2f16a)

On hitting exception 2, here is the ‘!exchain’ output:

0:000> !exchain
0105f86c: Ex3!_except_handler4+0 (00dc8070)
CRT scope 3, filter: Ex3!main+fb (00dc6b8b)
func: Ex3!main+122 (00dc6bb2)
CRT scope 2, filter: Ex3!main+152 (00dc6be2)
func: Ex3!main+179 (00dc6c09)
0105f8b8: Ex3!_except_handler4+0 (00dc8070)
CRT scope 0, filter: Ex3!__scrt_common_main_seh+139 (00dc6f83)
func: Ex3!__scrt_common_main_seh+14d (00dc6f97)
0105f914: ntdll!_except_handler4+0 (77b17380)
CRT scope 0, filter: ntdll!__RtlUserThreadStart+545c3 (77b2f123)
func: ntdll!__RtlUserThreadStart+5460a (77b2f16a)
0105f92c: ntdll!FinalExceptionHandlerPad40+0 (77abfff7)

The figure summarizes all the steps very well, also explains the additional code generated by the compiler for __try, __except block and its registration.

We note that the number of scope records displayed at a time for the function is corresponding to the nested levels of __try, __except or __try, __finally blocks. The ‘exchain’ extension can be very useful while debugging the issues related to exception handlers. From reverse engineering perspective, the knowledge of exception handling is very important to troubleshoot issues where application developer is commonly unaware of the exception handling mechanism either from user mode or kernel mode. The understanding of the exception handling mechanism is also very helpful in the analysis of stack-based security attacks.

 

I hope the exception handling series could be helpful for the readers with various interest levels such as understanding exception handling in more depth, the internal mechanism or doing real-life debugging and software security.

Leave a Reply

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