Search notes:

WinAPI: Structured Exception Handling

Each thread can set up its own variant of Structured Exception Handling (SEH).

Recovering from an access violation

//
//  Compiling:
//     cl recover-from-access-violation.c user32.lib
//

#include <windows.h>

HANDLE stdOut;

void out(char const* text) {
   DWORD charsWritten;
   WriteConsole(stdOut, text, lstrlen(text), &charsWritten, NULL);
}

LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS exPtr) {
    char buf[123];

    out("Exception handler\r\n");

    if (exPtr->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) {
      out("Expected EXCEPTION_ACCESS_VIOLATION!\n");
      return EXCEPTION_CONTINUE_SEARCH;
    }

    wsprintf(buf, "    ExceptionAddress: %x  EIP %x\n", exPtr->ExceptionRecord->ExceptionAddress, exPtr->ContextRecord->Eip);
    out(buf);


 //
 // The instruction that caused the exception is 6 byte long (at least on
 // 32 Bit Windows, compare with recover-from-access-violation.objdump).
 // We increase the instruction pointer (EIP) by these 6 bytes so as
 // to recover from the violation...
 //
   (exPtr->ContextRecord->Eip) += 6;

 //
 // ... and set the changed contect record into the current record
 // which causes the thread to continue after the bad instruction:
 //
    SetThreadContext(GetCurrentThread(), exPtr->ContextRecord);
    out("Never reached\n");

    return EXCEPTION_CONTINUE_SEARCH;
}


int main() {
  int *ptr;

  stdOut = GetStdHandle(STD_OUTPUT_HANDLE);

  AddVectoredExceptionHandler(1, ExceptionHandler  );

  ptr = (int*) 0;

  out("Going to cause access violation\n");
 *ptr = 42;
  out("After access violation\n");

}
Github repository WinAPI, path: /Diagnostics/Structured-Exception-Handling/recover-from-access-violation.c

Trap breakpoint and resume execution

//
//  Compiling
//    cl trap-int-3.c user32.lib
//
#include <windows.h>

//
// TODO: Should FlushInstructionCache() be used?
//

HANDLE stdOut;

#define NOF_BREAKPOINTS 4

char  byte_orig;
char *func_addrs[NOF_BREAKPOINTS];
char  old_instr [NOF_BREAKPOINTS];
int   nrLastFuncBreakpointHit;

void out(char const* text) {
   DWORD charsWritten;   
   WriteConsole(stdOut, text, lstrlen(text), &charsWritten, NULL);
}


LONG WINAPI ExHandler(PEXCEPTION_POINTERS exPtr) {
    int i;
    char buf[123];

    if (exPtr->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) {

//    wsprintf(buf, "breakpoint at address %d\n", exPtr->ContextRecord->Eip);
//    out(buf);

      for (i=0; i<NOF_BREAKPOINTS; i++) {
       //
       // Trying to determine the function that caused the breakpoint
       // exception.
  
          if (exPtr->ContextRecord->Eip == (int) func_addrs[i]) {
           //
           // The EIP register is equal to the address of one of our
           // functions. Reporting it to the console:
           //
              wsprintf(buf, "Breakpoint %d was hit\n", i);
              out(buf);

           //
           // Store the number of the breakpoint so that we can
           // set the breakpoint again after single stepping
           // the »current« instruction:
           //
              nrLastFuncBreakpointHit = i;
  
           //
           // In order to proceed with the execution of the program, we
           // restore the old value of the byte that was replaced by
           // the int-3 instruction:
           //
             *func_addrs[i] = old_instr[i];
  
           //
           // Set TF bit in order to single step the next
           // instruction. This allows to set the break point
           // again after the single step instruction.
           //
              exPtr->ContextRecord->EFlags |= 0x100;
  
           //
           // Resume execution:
           //
              SetThreadContext(GetCurrentThread(), exPtr->ContextRecord);

          }
       }
    }
    else if (exPtr->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
//    wsprintf(buf, "Single step at address %d\n", exPtr->ContextRecord->Eip);
//    out(buf);

    //
    // The processor is one instruction behind the last breakpoint that was
    // hit. We can now set the breakpoint again.
    //
      *func_addrs[nrLastFuncBreakpointHit] = 0xcc;

    //
    // Apparently, the TF flag is reset after the single execution, it
    // needs not be reset.
    //
    // Thus, we can resume execution again.
    //
    // This can apparently be done by SetThreadContext(…) or returning
    // EXCEPTION_CONTINUE_EXECUTION. I am not sure which one is
    // correct.
    //
//     SetThreadContext(GetCurrentThread(), exPtr->ContextRecord);
       return EXCEPTION_CONTINUE_EXECUTION;
    }
    else {

    //
    // Should never be reached.
    //
       out("Either EXCEPTION_CONTINUE_SEARCH or EXCEPTION_SINGLE_STEP was expected.\n");
    }

    return EXCEPTION_CONTINUE_SEARCH;
}

int func_0(int);
int func_1(int);
int func_2(int);
int func_3(int);

int func_0(int i) {
  if (i == 0) return func_1(i+1);
  if (i == 2) return func_1(i+1);
  return i;
}

int func_1(int i) {
  if (i == 1) return func_0(i+1);
  if (i == 3) return func_1(i+1);
  if (i == 4) return func_2(i+1);
  return i;
}

int func_2(int i) {
  if (i == 5) return func_3(i+1);
  return i;
}

int func_3(int i) {
  if (i == 6) return func_2(i+1);
  return i;
}


int main() {
  int  i, res;
  char buf[123];

  stdOut = GetStdHandle(STD_OUTPUT_HANDLE);

  AddVectoredExceptionHandler(1, ExHandler);

  func_addrs[0] = (char*) func_0;
  func_addrs[1] = (char*) func_1;
  func_addrs[2] = (char*) func_2;
  func_addrs[3] = (char*) func_3;

  for (i=0; i<NOF_BREAKPOINTS; i++) {
  //
  // Setting the breakpoints at the functions addresses.
  //
  // First, we need to make the code segment writable to be able to
  // insert the breakpoint instruction. Otherwise, the modification of
  // the (read-only) code segment would cause an exception.
  //

     DWORD oldProtection;
     VirtualProtect(func_addrs[0], 1, PAGE_EXECUTE_READWRITE, &oldProtection);

  //
  // We also want to store the value of the byte before we set the int-3
  // instruction:
  //
     old_instr[i]  = *func_addrs[i];

  //
  // Finally, we can inject the int-3 instruction (0xcc):
  //
    *func_addrs[i] = 0xcc;

  }

  res = func_0(0);

  wsprintf(buf, "res = %d\n", res);

  out(buf);
}
Github repository WinAPI, path: /Diagnostics/Structured-Exception-Handling/trap-int-3.c

TODO

Exception handlers

When an application encounters a user mode (software?) exception, the kernel traps the exception (via the Interrupt Descriptor Table (IDT)) and passes it to the (user mode) exception dispatcher.
The user mode exception handler checks if a debugger is attached. If this is the case, it notifies the debugger of the exception. This is the first chance exception.
If the debugger does not handle the the exception or there is no debugger attached, the user mode dispatcher checks for one (or more) installed vectored exception handlers (VEHs) and calls them until one handles the exception.
If none of the VEHs handled the exception, the (stack based) chain of structured exception handlers (SEH) is traversed until a SEH is found that handles the exception.
If none of the SEHs handles the exception, the exception is raised again. If there is a debugger, the debugger now ges a second chance to handle the exception.
If the debugger didn't handle the 2nd chance exception or if there was no debugger, the process is terminated.
Thus, VEH takes precedence over SEH.
The first item of the SEH chain is stored in the first element of the TIB.

SEH handlers

The exception handlers form a linked list on the stack.
When an exception occurs, Windows picks the first element of that list and walks the list until it finds a handler that is able to properly deal with the exception.
Is the head of this linked pointed at in the first element of the TIB?

See also

WinAPI

Links

Matt Pietrek: A Crash Course on the Depths of Win32™ Structured Exception Handling
Win32Easy: Exception Handling - Inform your users! which explains the difference between AddVectoredExceptionHandler and SetUnhandledExceptionFilter.
Oleg Krivtsov: Exception Handling in Visual C++

Index