Metadata
Abstract
An indepth discussion of how VC++ implements exception handling. Source code includes exception handling library for VC++.
Tags and Collections
- Keywords: 05 Finished; Binary Exploitation; C++; MSVC; Structured Exception Handling
Comments
Annotations
Notes
related: Structured Exception Handling
- This guide was written in 2002, which seemed very old to me, but knowing how hard Microsoft tries to maintain backwards-compatibility, I’m just going to pray that not much has changed since for now.
- This guide is Structured Exception Handling (SEH)- / VC++-specific. Normally, only the MSVC exception handling extension (
__try
and__except
) use SEH, and SEH may not always clean up resources properly (i.e., it may not be aware of language features such as destructors), unless /Eha switch is present during compilation (source). “Structured Exceptions may occur for many operations, e.g. due to undefined behavior, passing invalid pointers to APIs, unmounting the backing store of a memory mapped file, and many more” (source). - This guide comes up with names for internal structures (e.g.,
funcinfo
, “tryblock table”,unwindtable
) that may not identify with the actual internal MSVC names.
Relevant structure / member variable definitions:
- Each function call stores a
EXCEPTION_REGISTRATION
struct in its stack frame, which is a linked list node that overlaps with the saved ebp (order on stack: parameters, return address, saved ebp, id, handler, prev). Apart from the ebp, each node contains anid
,handler
function pointer, and aprev
pointer pointing to the same structure in the previous/outer stack frame. During compilation, the MSVC compiler also generates (a) the exception callback function and (b) a block of data that stores information about try-catch blocks within the function, which the author callsfuncinfo
. A pointer to thisfuncinfo
is saved in theEXCEPTION_REGISTRATION
struct, which thehandler
passes to__CxxFrameHandler
. - The
funcinfo
struct also stores a pointer to a “tryblock table,” which is a list of tryblocks. Each tryblock struct contains anid
range (e.g., for identifying whether or not the control is currently in this try block, and also for stack unwinding) and also links to a catchblock table that contains things like the exception type the catch block is interested in and the address of the catch block code. - When an exception
e
is thrown under SEH:- The exception is thrown using
_CxxThrowException(&e, E_EXCPT_INFO_ADDR)
. The method and call is generated by the compiler, andE_EXCPT_INFO_ADDR
points to a struct storing information about the exception thrown (type info, copy ctor, dtor, etc). _CxxThrowException
transfers control to the OS via RaiseException and passes both parameters to it.- OS calls the
handler
, which acts as a wrapper for__CxxFrameHandler
,jmp
ing to the latter with a pointer to the relevantfuncinfo
in eax and also passing the exception pointer & theEXCEPTION_REGISTRATION
node pointer. Refer to the code at the top of notes for all the parameters thathandler
accepts. _CxxFrameHandler
checks if the tryblock table (referenced infuncinfo
) contains a catch block for the current exception and unwinds the stack. If there is a corresponding catch block, the handler transfers control to it; otherwise, control is transferred to the OS, which calls the nexthandler
in the linked list.- The
id
field is dynamically updated upon entering a new try-block and when objects are initialized/destroyed, which informs the stack unwinding process about which objects are still alive and need to be destructed.
- The
- During stack unwinding:
- Destructors for all alive objects in the try-block are called based on the
id
. - The
funcinfo
contains a pointer to the unwind table, which is a linked list (prev
) whose nodes contain a cleanup function pointer (e.g., destructor) for eachid
. The currentid
is used to index this table, and each cleanup function at the index and onwards is called. To account for the case where an object ctor throws an exception when some member objects are already initialized, the compiler also generates similar structures for each ctor. If exception is thrown in a dtor during stack unwinding, however, the program will simply terminate.
- Destructors for all alive objects in the try-block are called based on the
- The exception is thrown using