//
// 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");
}
//
// 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);
}
AddVectoredExceptionHandler and SetUnhandledExceptionFilter.