Uninformed: Informative Information for the Uninformed

Vol 5» 2006.Sep

Structured Exception Handling

Structured Exception Handling (SEH) is a uninform system for dispatching and handling exceptions that occur during the normal course of a program's execution. This system is similar in spirit to the way that UNIX derivatives use signals to dispatch and handle exceptions, such as through SIGPIPE and SIGSEGV. SEH, however, is a more generalized and powerful system for accomplishing this task, in the author's opinion. Microsoft's integration of SEH spans both user-mode and kernel-mode and is a licensed implementation of what is described in a patent owned by Borland[1]. In fact, this patent is one of the reasons why open source operating systems have not chosen to integrate this style of exception dispatching[11].

In terms of implementation, structured exception handling works by defining a uniform way of handling all exceptions that occur during the normal course of process execution. In this context, an exception is defined as an event that occurs during execution that necessitates some form of extended handling. There are two primary types of exceptions. The first type, known as a hardware exception, is used to categorize exceptions that originate from hardware. For example, when a program makes reference to an invalid memory address, the processor will raise an exception through an interrupt that gives the operating system an opportunity to handle the error. Other examples of hardware exceptions include illegal instructions, alignment faults, and other architecture-specific issues. The second type of exception is known as a software exception. A software exception, as one might expect, originates from software rather than from the hardware. For example, in the event that a process attempts to close an invalid handle, the operating system may generate an exception.

One of the reasons that the word structured is included in structured exception handling is because of the fact that it is used to dispatch both hardware and software exceptions. This generalization makes it possible for applications to handle all types of exceptions using a common system, thus allowing for greater application flexibility when it comes to error handling.

The most important detail of SEH, insofar as it pertains to this document, is the mechanism through which applications can dynamically register handlers to be called when various types of exceptions occur. The act of registering an exception handler is most easily described as inserting a function pointer into a chain of function pointers that are called whenever an exception occurs. Each exception handler in the chain is given the opportunity to either handle the exception or pass it on to the next exception handler.

At a higher level, the majority of compiler-generated C/C++ functions will register exception handlers in their prologue and remove them in their epilogue. In this way, the exception handler chain mirrors the structure of a thread's stack in that they are both LIFOs (last-in-first-out). The exception handler that was registered last will be the first to be removed from the chain, much the same as last function to be called will be the first to be returned from.

To understand how the process of registering an exception handler actually works in practice, it makes sense to analyze code that makes use of exception handling. For instance, the code below illustrates what would be required to catch all exceptions and then display the type of exception that occurred:

    printf("Exception code: %.8x\n", GetExceptionCode());

In the event that an exception occurs from code inside of the __try / __except block, the printf call will be issued and GetExceptionCode will return the actual exception that occurred. For instance, if code made reference to an invalid memory address, the exception code would be 0xc0000005, or EXCEPTION_ACCESS_VIOLATION. To completely understand how this works, it is necessary to dive deeper and take a look at the assembly that is generated from the C code described above. When disassembled, the code looks something like what is shown below:

00401000 55               push    ebp
00401001 8bec             mov     ebp,esp
00401003 6aff             push    0xff
00401005 6818714000       push    0x407118
0040100a 68a4114000       push    0x4011a4
0040100f 64a100000000     mov     eax,fs:[00000000]
00401015 50               push    eax
00401016 64892500000000   mov     fs:[00000000],esp
0040101d 83c4f4           add     esp,0xfffffff4
00401020 53               push    ebx
00401021 56               push    esi
00401022 57               push    edi
00401023 8965e8           mov     [ebp-0x18],esp
00401026 c745fc00000000   mov     dword ptr [ebp-0x4],0x0
0040102d c6050000000001   mov     byte ptr [00000000],0x1
00401034 c745fcffffffff   mov     dword ptr [ebp-0x4],0xffffffff
0040103b eb2b             jmp     ex!main+0x68 (00401068)
0040103d 8b45ec           mov     eax,[ebp-0x14]
00401040 8b08             mov     ecx,[eax]
00401042 8b11             mov     edx,[ecx]
00401044 8955e4           mov     [ebp-0x1c],edx
00401047 b801000000       mov     eax,0x1
0040104c c3               ret

0040104d 8b65e8           mov     esp,[ebp-0x18]
00401050 8b45e4           mov     eax,[ebp-0x1c]
00401053 50               push    eax
00401054 6830804000       push    0x408030
00401059 e81b000000       call    ex!printf (00401079)
0040105e 83c408           add     esp,0x8
00401061 c745fcffffffff   mov     dword ptr [ebp-0x4],0xffffffff
00401068 8b4df0           mov     ecx,[ebp-0x10]
0040106b 64890d00000000   mov     fs:[00000000],ecx
00401072 5f               pop     edi
00401073 5e               pop     esi
00401074 5b               pop     ebx
00401075 8be5             mov     esp,ebp
00401077 5d               pop     ebp
00401078 c3               ret

The actual registration of the exception handler all occurs behind the scenes in the C code. However, in the assembly code, the registration of the exception handler starts at 0x0040100a and spans four instructions. It is these four instructions that are responsible for registering the exception handler for the calling thread. The way that this actually works is by chaining an EXCEPTION_REGISTRATION_RECORD to the front of the list of exception handlers. The head of the list of already registered exception handlers is found in the ExceptionList attribute of the NT_TIB structure. If no exception handlers are registered, this value will be set to 0xffffffff. The NT_TIB structure makes up the first part of the TEB, or Thread Environment Block, which is an undocumented structure used internally by Windows to keep track of per-thread state in user-mode. A thread's TEB can be accessed in a position-independent fashion by referencing addresses relative to the fs segment register. For example, the head of the exception list chain be be obtained through fs:[0].

To make sense of the four assembly instructions that register the custom exception handler, each of the four instructions will be described individually. For reference purposes, the layout of the EXCEPTION_REGISTRATION_RECORD is described below:

   +0x000 Next             : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : Ptr32

  1. push 0x4011a4

    The first instruction pushes the address of the CRT generated _except_handler3 symbol. This routine is responsible for dispatching general exceptions that are registered through the __except compiler intrinsic. The key thing to note here is that the virtual address of a function is pushed onto the stack that is excepted to be referenced in the event that an exception is thrown. This push operation is the first step in dynamically constructing an EXCEPTION_REGISTRATION_RECORD on the stack by first setting the Handler attribute.

  2. mov eax,fs:[00000000]

    The second instruction takes the current pointer to the first EXCEPTION_REGISTRATION_RECORD and stores it in eax.

  3. push eax

    The third instruction takes the pointer to the first exception registration record in the exception list and pushes it onto the stack. This, in turn, sets the Next attribute of the record that is being dynamically generated on the stack. Once this instruction completes, a populated EXCEPTION_REGISTRATION_RECORD will exist on the stack that takes the following form:

       +0x000 Next             : 0x0012ffb0
       +0x004 Handler          : 0x004011a4     ex!_except_handler3+0

  4. mov fs:[00000000],esp

    Finally, the dynamically generated exception registration record is stored as the first exception registration record in the list for the current thread. This completes the process of inserting a new registration record into the chain of exception handlers.

The important things to take away from this description of exception handler registration are as follows. First, the registration of exception handlers is a runtime operation. This means that whenever a function is entered that makes use of an exception handler, it must dynamically register the exception handler. This has implications as it relates to performance overhead. Second, the list of registered exception handlers is stored on a per-thread basis. This makes sense because threads are considered isolated units of execution and therefore exception handlers are only relative to a particular thread. The final, and perhaps most important, thing to take away from this is that the assembly generated by the compiler to register an exception handler at runtime makes use of the current thread's stack. This fact will be revisited later in this section.

In the event that an exception occurs during the course of normal execution, the operating system will step in and take the necessary steps to dispatch the exception. In the event that the exception occurred in the context of a thread that is running in user-mode, the kernel will take the exception information and generate an EXCEPTION_RECORD that is used to encapsulate all of the exception information. Furthermore, a snapshot of the executing state of the thread is created in the form of a populated CONTEXT structure. The kernel then passes this information off to the user-mode thread by transferring execution from the location that the fault occurred at to the address of ntdll!KiUserExceptionDispatcher. The important thing to understand about this is that execution of the exception dispatcher occurs in the context of the thread that generated the exception.

The job of ntdll!KiUserExceptionDispatcher is, as the name implies, to dispatch user-mode exceptions. As one might guess, the way that it goes about doing this is by walking the chain of registered exception handlers stored relative to the current thread. The diagram in figure [*] provides a basic example of how it walks the chain. As the exception dispatcher walks the chain, it calls the handler associated with each registration record, giving that handler the opportunity to handle, fail, or pass on the exception.

Figure: Walking the chain of exception registration records
Image seh_chain

While there are other things involved in the exception dispatching process, this description will suffice to set the stage for how it might be abused to gain code execution.