Statistics

Members: 1925
News: 293
Web Links: 1
Visitors: 3821717

Who's Online

We have 1 guest online
Damn Vulnerable LinuxDamn Vulnerable Linux (DVL) is a Linux-based (modified Damn Small Linux) tool for IT-Security & IT-Anti- Security and Attack & Defense. [CLICK HERE FOR MORE INFOS! ]

Featured Conference Video

T16-Recon2006-Joe_Stewart-OllyBonE.gif OllyBone - Semi-Automatic Unpacking on IA-32. View the conference video here!
Home arrow Articles - Programming arrow Assembly arrow Structured Exception Handling under Win32
Structured Exception Handling under Win32
User Rating: / 0
PoorBest 
Written by Chris Dragan   


Structured Exception Handling is a powerful feature of all Win32 platforms that allows a program to recover from any critical errors like BOUND, divide overflow, page missing or general protection fault. It is documented only for C-level usage (try-except/finally syntax), and no documentation for low level languages exists. Therefore I will try to show how to use it.

The starting point for Structured Exception Handling, SEH, is the Thread Info Block. TIB, as almost all the other structures, is described in winnt.h file that comes with PlatformSDK.

struc NT_TIB

        ExceptionList           dd ?    ; Used by SEH
StackBase               dd ?    ; Used by functions to check for
StackLimit              dd ?    ;  stack overflow
SubSystemTib            dd ?    ; ?
FiberDataOrVersion      dd ?    ; ?
ArbitraryUserPointer    dd ?    ; ?
Self                    dd ?    ; Linear address of the TIB

ends

TIB is accessible at address fs:0. NT_TIB.Self contains linear address of TIB, base of FS segment.

When an exception occurs, the system uses (dword)fs:0, NT_TIB.ExceptionList to find an exception handler and execute it. The exception list entry is very simple:

struc ELENTRY

        Next                    dd ?    ; Points to next entry in the list
ExceptionHandler        dd ?    ; User callback - exception hook
Optional                db X dup (?) ; Exception Handler data
EntryTerminator         dd -1   ; Optional

ends

C compilers usually keep some additional information in ELENTRY.Optional field of varying size and usually terminated with (dword)-1. Both .Optional and .EntryTerminator fields are not required.

Before calling an exception handler, the exception manager pushes ExceptionRecord and ContextRecord onto the stack. These structures identify an exception and processor state before it. The exception manager adds also its own entry to the exception list.

Exception handler is in fact a typical callback. It is not however installed by any API function, but appended in ELENTRY into the exception list.

EXCEPTION_DISPOSITION __cdecl excepthandler (

struct EXCEPTIONRECORD *ExceptionRecord, void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);

The exception handler uses C-style calling convention, it does not release arguments while returning. The most important parameters are ExceptionRecord and ContextRecord, described at the end of this text, that point to the pushed corresponding structures. I do not have yet any idea what is the purpose of EstablisherFrame and DispatcherContext.

struc EXCEPTION_RECORD

        ExceptionCode           dd ?    ; See at the end of this text
ExceptionFlags          dd ?
ExceptionRecord         dd ?    ; ?
ExceptionAddress        dd ?    ; Linear address of faulty instruction
NumberParameters        dd ?    ; Corresponds to the field below
ExceptionInformation    dd 15 dup (?) ; ?

ends

Exception flags are:

EXCEPTION_NONCONTINUABLE        = 1
EXCEPTION_UNWINDING             = 2

EXCEPTION_UNWINDING_FOR_EXIT = 4

The exception handler has two possible ways of proceeding. It can return to the exception manager, or it can unwind the stack and continue the program. In the first case it has to return one of the following values:

enum EXCEPTION_DISPOSITION \

        ExceptionContinueExecution  = 0,\
ExceptionContinueSearch     = 1,\
ExceptionNestedException    = 2,\
ExceptionCollidedUnwind     = 3

The value of zero forces the exception manager to continue the program at saved in context cs:eip, which may be altered by the exception handler. The value of 1 causes the exception manager to call another exception handler in the exception list. Values 2 and 3 inform the exception manager that an error occured - an exception-in-exception happened, or the handler wanted to unwind the stack during another handler of higher instance was doing this already. The other case can be determined if one of .ExceptionFlags is EXCEPTION_UNWINDING or EXCEPTION_UNWINDING_FOR_EXIT.

While appending a new exception handler to the exception list, a common practice is to push new ELENTRY onto the stack. This way unwinding the stack can be done simply by skipping the exception manager's entry and restoring the stack pointer.

Here is an example of exception handling.

----Start-of-file-------------------------------------------------------------

ideal
p686n
model flat, stdcall

O equ <offset>

struc EXCEPTION_RECORD

        ExceptionCode           dd ?
ExceptionFlags          dd ?
ExceptionRecord         dd ?
ExceptionAddress        dd ?
NumberParameters        dd ?
ExceptionInformation    dd 15 dup (?)

ends

procdesc wsprintfA c :dword, :dword, :dword:? procdesc MessageBoxA :dword, :dword, :dword, :dword procdesc ExitProcess :dword

udataseg

  ExCode        dd ?
szCode        db 12 dup (?)

dataseg

szWindowTitle db 'Exception code', 0
szFormat db '%0X', 0

codeseg

proc main

                ; Install exception handler
push    O ExceptionHandler
push    [dword fs:0]    ; ELENTRY.Next
mov     [fs:0], esp     ; Append new ELENTRY
; Cause Invalid Opcode exception
ud2
; Display exception code and quit
_Continue:              call    wsprintfA, O szCode, O szFormat, [ExCode]
call    MessageBoxA, 0, O szCode, O szWindowTitle, 0
call    ExitProcess, 0

endp

proc ExceptionHandler c ExceptionRecord, EF, ContextRecord, DC

                ; Save exception code
mov     eax, [ExceptionRecord]
mov     ecx, [(EXCEPTION_RECORD eax).ExceptionCode]
mov     [ExCode], ecx
; Unwind the stack
mov     eax, [fs:0]     ; Exception Manager's entry
mov     esp, [eax]      ; Our entry
pop     [dword fs:0]    ; Restore fs:0
add     esp, 4          ; Skip ExHandler address
jmp     _Continue

endp

end main

----End-of-file---------------------------------------------------------------

The above source should be compiled with TASM 5.0r or later like this: tasm32 /ml except.asm
tlink32 /x /Tpe /aa /c /V4.0 except.obj,,, LIBPATH\import32.lib

And here are other important constants and structures, all defined in winnt.h PlatformSDK file.

Exception codes:

STATUS_SEGMENT_NOTIFICATION     = 040000005h
STATUS_GUARD_PAGE_VIOLATION     = 080000001h
STATUS_DATATYPE_MISALIGNMENT    = 080000002h
STATUS_BREAKPOINT               = 080000003h
STATUS_SINGLE_STEP              = 080000004h
STATUS_ACCESS_VIOLATION         = 0C0000005h
STATUS_IN_PAGE_ERROR            = 0C0000006h
STATUS_INVALID_HANDLE           = 0C0000008h
STATUS_NO_MEMORY                = 0C0000017h
STATUS_ILLEGAL_INSTRUCTION      = 0C000001Dh
STATUS_NONCONTINUABLE_EXCEPTION = 0C0000025h
STATUS_INVALID_DISPOSITION      = 0C0000026h

STATUS_ARRAY_BOUNDS_EXCEEDED = 0C000008Ch STATUS_FLOAT_DENORMAL_OPERAND = 0C000008Dh

STATUS_FLOAT_DIVIDE_BY_ZERO     = 0C000008Eh
STATUS_FLOAT_INEXACT_RESULT     = 0C000008Fh
STATUS_FLOAT_INVALID_OPERATION  = 0C0000090h
STATUS_FLOAT_OVERFLOW           = 0C0000091h
STATUS_FLOAT_STACK_CHECK        = 0C0000092h
STATUS_FLOAT_UNDERFLOW          = 0C0000093h
STATUS_INTEGER_DIVIDE_BY_ZERO   = 0C0000094h
STATUS_INTEGER_OVERFLOW         = 0C0000095h
STATUS_PRIVILEGED_INSTRUCTION   = 0C0000096h
STATUS_STACK_OVERFLOW           = 0C00000FDh
STATUS_CONTROLCEXIT           = 0C000013Ah
STATUS_FLOAT_MULTIPLE_FAULTS    = 0C00002B4h
STATUS_FLOAT_MULTIPLE_TRAPS     = 0C00002B5h

STATUS_ILLEGAL_VLM_REFERENCE = 0C00002C0h

Context flags:

CONTEXT_i386                    = 000010000h
CONTEXT_i486                    = 000010000h
CONTEXT_CONTROL            = (CONTEXT_i386 or 1) ; SS:ESP, CS:EIP, EFLAGS, EBP
CONTEXT_INTEGER            = (CONTEXT_i386 or 2) ; EAX, EBX,..., ESI, EDI
CONTEXT_SEGMENTS           = (CONTEXT_i386 or 4) ; DS, ES, FS, GS
CONTEXT_FLOATING_POINT     = (CONTEXT_i386 or 8) ; 387 state

CONTEXT_DEBUG_REGISTERS = (CONTEXT_i386 or 16); DB 0-3,6,7 CONTEXT_EXTENDED_REGISTERS = (CONTEXT_i386 or 32); cpu specific extensions

CONTEXT_FULL               = (CONTEXT_CONTROL or CONTEXT_INTEGER or\
CONTEXT_SEGMENTS)

Context structure:

struc CONTEXT

ContextFlags dd ? ; CONTEXT_??? flags

        Dr0                     dd ?            ; Debug registers
Dr1                     dd ?
Dr2                     dd ?
Dr3                     dd ?
Dr6                     dd ?
Dr7                     dd ?
ControlWord             dd ?            ; FPU context
StatusWord              dd ?
TagWord                 dd ?
ErrorOffset             dd ?
ErrorSelector           dd ?
DataOffset              dd ?
DataSelector            dd ?
RegisterArea            dt 8 dup (?)
Cr0NpxState             dd ?
SegGs                   dd ?            ; Segment registers
SegFs                   dd ?
SegEs                   dd ?
SegDs                   dd ?
Edi                     dd ?            ; Integer registers
Esi                     dd ?
Ebx                     dd ?
Edx                     dd ?
Ecx                     dd ?
Eax                     dd ?
Ebp                     dd ?            ; Control registers
Eip                     dd ?
SegCs                   dd ?
EFlags                  dd ?
Esp                     dd ?
SegSs                   dd ?
ExtendedRegisters       db 512 dup (?)

ends

Additional word

This article was posted on comp.lang.asm.x86. Especially thanks to Michael Tippach for pointing out some exception flags. My web page is at {http://ams.ampr.org/cdragan/}