DISCLAIMER: The information in this site is for educational purpose only. The authors of this blog are not responsible for any kind of misuse of this information.

Saturday, November 15, 2014

WIN32 SEH

Saluton !

Today I gonna dump some facts about Windows Structured Exception Handling (SEH) mechanism. This article is mostly based on This great article.

OK, some dry facts about exception handling in Windows:
  • per thread
  • FS:[0] (TIB first dword) points to the first EXCEPTION_REGISTRATION struct
_EXCEPTION_REGISTRATION struct
     prev    dd      ? // EXCEPTION_REGISTRATION linked list next record
     handler dd      ? // _except_ handler callback function pointer
_EXCEPTION_REGISTRATION ends
except_handler callback 
  • can agree/decline to handle a given exception
    • if declined, the system will continue looking for other exception handler
 EXCEPTION_DISPOSITION __cdecl _except_handler(
     struct _EXCEPTION_RECORD *ExceptionRecord,
     void * EstablisherFrame,
     struct _CONTEXT *ContextRecord, // registers values when exception occured
     void * DispatcherContext );
 typedef struct _EXCEPTION_RECORD
 {
     DWORD ExceptionCode; // assigned by OS
     DWORD ExceptionFlags;
     struct _EXCEPTION_RECORD *ExceptionRecord;
     PVOID ExceptionAddress; // where the exception occured
     DWORD NumberParameters;
     DWORD ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
 }  EXCEPTION_RECORD;
 Exception handling flow:


 

  • Exception occurred
  • The system walks the EXCEPTION_REGISTRATIONs linked list to find a hanlder
  • when a handler agreed to handle the exception all the handlers before it are called again with flag EH_UNWINDING (to allow cleanups*) 

    * when handler declines handling an exception, control typically doesn't exit from the function normally - calling dtors and finally blocks
  • execution continues wherever the handling callback decides (with ebp, esp set by the handler)
 Unwinding from an exception 
  • all things below the handling frame's stack region are "removed"
  • EXCEPTION_REGISTRATIONs in the list prior to the one that handled the exception are removed from the list
  • see step 11 in the above figure 
 The last EXCEPTION_REGISTRATION element
  • default OS supplied exception handler (terminate / debug dialog)
  • always agrees to handle an exception
  • if no debugger is installed, the default dialog you will see may be similar to this:

Visual C++  Compiler level SEH
  • every EXCEPTION_REGISTRATION handler points to the same VC++ runtime lib function __except_handlerX
  • only one EXCEPTION_REGISTRATION is created per function on the stack

The Extended Exception Handling Frame

 EBP-00 _ebp
 EBP-04 trylevel
 EBP-08 scopetable pointer
 EBP-0C handler function address
 EBP-10 previous EXCEPTION_REGISTRATION
 EBP-14 GetExceptionPointers // pointers to GetExceptionInformation, GetExceptionCode

 EBP-18 Standard ESP in frame

struct _EXCEPTION_REGISTRATION{
      struct _EXCEPTION_REGISTRATION *prev;
      void (*handler)(PEXCEPTION_RECORD,
                      PEXCEPTION_REGISTRATION,
                      PCONTEXT,
                      PEXCEPTION_RECORD);
       struct scopetable_entry *scopetable;
       int trylevel; // index into scopetable
       int _ebp; // ebp value before the EXCEPTION_REGISTRATION was created
       PEXCEPTION_POINTERS xpointers;
};
 typedef struct _SCOPETABLE
 {
     DWORD       previousTryLevel; // the next level try block trylevel (most in -> out)
     DWORD       lpfnFilter
     DWORD       lpfnHandler
 } SCOPETABLE, *PSCOPETABLE; 

There is more to learn about this mechanism but I'll stop here. What we know now is sufficient to reverse a program that uses SEH & Exceptions to obfuscate its execution flow.

The main idea behind this technique is expressed in the following simple example:

void foo() 
{
    printf("code line #1\n");
    printf("code line #2\n");
    printf("code line #3\n");
}

will become:
void foo()
{
    try 
    {
        // exception
    }
    catch(...) 
    {
        printf("code line #1\n");
        try 
        {
            // exception
        }
        catch(...)
        {
            printf("code line #2\n");
            try 
            {
                // exception
            }
            catch(...)
            {
                printf("code line #3\n");
            }
        }
    }
}