Windows Exception Handling

Abstract

Like every big system developer I want to discuss the Windows Exception Handling. I have spent some time on finding out how exception handling internally works in Windows. There are many documents out there ("Under The Hood" - Matt Pietrek) but they do not apply to todays Windows operating systems (Windows XP SP3, Windows Vista). Exception handling by Msvcrt and Msvcr80, the Microsoft C Runtime libraries, was also of my interest. So putting here my research work together, enjoy reading!

- Peter Kleissner, Software Engineer (February 2009)

Exception Handling Overview

In this article I assume you already understand basics of exception handling. I will explain here the Software Exception handling of Windows, the hardware exception handling is another theme. Its necessary to understand that exception handling goes through multiple parts in Windows, and that the implementation of exception handling differs in every Windows version. We have following exception handling implementations in Microsoft Windows:

Structured Exception Handling

So let's start with the most common exception handling - Structured Exception Handling or SEH. Basically SEH defines a list of exception handlers, and every exception handler will be called until one can solve the exception. Exceptions can be solved for example by reloading an unpresent page, or other applications would be to make a dump and send it to the developer.

The correct exception handling defines also how "stable" a computer program is, exception handler usually handle the exception cases, abnormal conditions. As mentioned SEH defines a list of exception handlers. The list is really easy, just containing the address of the structured exception handler and the pointer to the next frame. The address of the first SEH frame is stored in the Thread Information Block at offset 0, which is pointed to by the segment register fs, accessible using assembly language.

The SEH frame is defined as followed:

_EXCEPTION_REGISTRATION struc
     prev    dd      ?
     handler dd      ?
 _EXCEPTION_REGISTRATION ends

Prev points to the next handler that will be executed, if the exception handler at handler can not solve the exception. These frames are usually created on the stack when entering a function and destroyed when leaving the function:

Function:

; register Exception Handler as Structured Exception Handler
push ExceptionHandler
push fs:[0]
mov [fs],esp

...

; remove last Structured Exception Handler
mov eax,[esp]
mov fs:[0],eax
add esp,8

ret

To notice, the segment pointed to by the segment selector fs has its base set to the Thread Information Block of the current thread, and TIB + 0 points to the SEH list. This means also that Windows changes the base of fs (or fs itself) everytime a thread switch occurs. The definition of an exception handler (C++) function looks like following:

EXCEPTION_DISPOSITION
__cdecl
_except_handler(
    struct _EXCEPTION_RECORD *ExceptionRecord,
    void * EstablisherFrame,
    struct _CONTEXT *ContextRecord,
    void * DispatcherContext )
{
...
}

The EXCEPTION_RECORD structure defines the exception and is documented by Microsoft in the MSDN Library "EXCEPTION_RECORD Structure". The CONTEXT structure contains the threads current process context, its registers etc. (also documented less than more by Microsoft). It is also used in thread switching and hibernation file. The reason why the CONTEXT structure does not appear defined at MSDN Library is that Microsoft claims the CONTEXT structure to be platform specific, however only IA32 is of our interest. You can review it in my Hibernation File Attack presentation materials.

typedef struct _EXCEPTION_RECORD {
  DWORD                    ExceptionCode;
  DWORD                    ExceptionFlags;
  struct _EXCEPTION_RECORD *ExceptionRecord;
  PVOID                    ExceptionAddress;
  DWORD                    NumberParameters;
  ULONG_PTR                ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD,  *PEXCEPTION_RECORD;

When returning from an exception handler, the handler returns an EXCEPTION_DISPOSITION value to Windows, which identifies whether the handler actually handled the exception:

typedef enum _EXCEPTION_DISPOSITION
{
    ExceptionContinueExecution = 0,
    ExceptionContinueSearch = 1,
    ExceptionNestedException = 2,
    ExceptionCollidedUnwind = 3
} EXCEPTION_DISPOSITION;

So putting everything of SEH together, following is done inside out:

1. Register Exception Handler
   Create a new SEH frame, and link it to the SEH chain
   First element is received by fs:[0] pointer
2. Exception Occurs
   Windows (ntdll!KiUserExceptionDispatcher) will walk through the SEH list and call your exception handler
3. Exception Handler is callesd by KiUserExceptionDispatcher
   Handle the exception
   return ExceptionContinueExecution -> KiUserExceptionDispatcher will resume execution where exception occured
   return ExceptionContinueSearch   -> next SEH handler will be called if available
4. if no further SEH exception handler is available and the process is not being debugged, the application will be terminated ("Unhandled Exception")

I should also mention here that when control is passed to the applications entry point, there is already an exception handler registered, the Unhandled Exception Handler resisting in the Ntdll. What Ntdll!Unhandled Exception Handler does, will be explained later. Of course you can overwrite the default Unhandled Exception Handler.

Compiler based Structured Exception Handling

Time to discuss compiler based SEH, the exception handling used when using try/throw/catch (Msvcrt) and try/except/finally (C++) kewords. They are handled by the compiler, and do result in a custom exception handling way. Visual C++ uses an extended SEH frame, which points to the only one exception handler Msvcr80!_except_handler4. This special SEH handler exists (of course) per thread, because each thread has its own Thread Information Block. It is important to know that we are leaving here Windows internal exception handling, but this (compiler based SEH) is used with every modern application, although it must not.

__except_handler4 gets all the exceptions and determines where (in which function) the exception occurs, and calls the catch/except function blocks of that function. For doing that determination there exists the scopetable, which address is given in the extended SEH frame:

struct _EXCEPTION_REGISTRATION {
     struct _EXCEPTION_REGISTRATION *prev;
     void (*handler)(PEXCEPTION_RECORD,
                     PEXCEPTION_REGISTRATION,
                     PCONTEXT,
                     PEXCEPTION_RECORD);
     struct scopetable_entry *scopetable;
     int trylevel;
     int _ebp;
     PEXCEPTION_POINTERS xpointers;
};

This extended frame consists of the normal frame (next pointer and handler address) and Msvcrt specific variables. The trylevel is the index in the scopetable array of the current to--use try block. Let's have a look on the main purpose of the extended SEH frame, on scopetable entries:

 typedef struct _SCOPETABLE
 {
     DWORD       previousTryLevel;
     DWORD       lpfnFilter
     DWORD       lpfnHandler
 } SCOPETABLE, *PSCOPETABLE;

The lpfnFilter points to the filter expression of the try key word, a small function block that decides whether to try to handle the exception or not. The lpfnHandler then points to the exception handler. previousTryLevel is the outer try block scopetable index, 0xFFFFFFFF if there is no outer one.

Before Msvcr80, _except_handler3 was used. Do not wonder about the cranky names, this is Microsoft internal and not intended to be public ever. Notice internally it is Msvcr80!_XcptFilter called by __except_handler4 that really dispatches the exception. And to give a word to the keyword throw, it executes Msvcr80!_CxxThrowException which calls Windows API Kernel32!RaiseException.

Vectored Exception Handling

Vectored Exception Handling is an extension to SEH. As Microsoft explains in MSDN Library, "Vectored handlers are called in the order that they were added, after the debugger gets a first chance notification, but before the system begins unwinding the stack". This means that they are called after the debugger, but before the exception handler is called.

Kernel32!AddVectoredExceptionHandler forwarded to Ntdll!AddVectoredExceptionHandler
Kernel32!RemoveVectoredExceptionHandler forwarded to Ntdll!RtlRemoveVectoredExceptionHandler

Newer Windows versions forward these two functions from Kernel32 to Ntdll, Microsoft begun to structure and sort functions to modules where they belong to. For Windows Vista and updated versions (= with Service Pack) there are the new functions AddVectoredContinueHandler and RemoveVectoredContinueHandler available.

Vectored Exception Handlers are also (like Structured Exception Handlers) called by ntdll!KiUserExceptionDispatcher.

Unhandled Exception Filters

Last but not least there are Unhandled Exception Filters. They are the really unnicest part of exception handling, until it contains well documented bugs. Anyway its quite as easy as Vectored Exception Handling, just two functions are used. The one register, the other calls the Unhandled Exception Filter.

SetUnhandledExceptionFilter
UnhandledExceptionFilter

The Unhandled Exception Filter is called via UnhandledExceptionFilter() function, when an exception could not be handled/resolved. This is done before the application is terminated due to an unhandled exception. It is called by Ntdll!Unhandled Exception Handler (an internal not exported function).

The bug is when you use multiple Unhandled Exception Filters, which is really not to recommend. It is undefined behaviour. The real bug is that Unhandled Exception Filters are never executed if there is a debugger attached, so a very very good, currently not solved anti-debugging mechanism. Microsoft has documented this bug under http://support.microsoft.com/kb/173652/en-us. I assume it comes from the fact that UnhandledExceptionFilter() is called only within the Unhandled Exception Handler, but not the Windows Exception API, and the Unhandled Exception Handler will never be called if a debugger is attached.

Conclusion

The most important part on exception handling is to understand that there are basic outlines but implementation remains different in different Windows versions (also Service Packs). When reading old articles such as the ones from Microsoft Systems Journal be aware they do not apply to Windows Vista and newer. They are good to get a basic understanding but nothing more than that. Also, be aware that there is undocumented internal exception handling in Kernel, functions such as NtRaiseException remain unknown (what they really do). Debugging exception handling is a difficult thing until debugging is exceptioning itself (debug events are exception events).

When parsing through the Internet you'll see functions like RtlDispatchException. Understand that the most important function behind exception handling is KiUserExceptionDispatcher, and everything other are just help functions. In Windows Vista KiUserExceptionDisptacher is a short solid function, before Vista a quite extensive function. Do not spend too much time on exception handling, just implement and pass by; it's Windows specific and subjected to change.

References