Previous version of mini-debugger I described here has a disadvantage,
as it was unable to catch symbols in statically linked binaries.
This version patches target, not assuming that we call via jump table, and it also shows how
single stepping in debuggers works. It may be not absolutely thread-safe, but I tried :).
NB: please note that it uses standard debugging machinery, so running under debuggers, such as MSVC debugger isn't the best idea.
#define_WIN32_WINNT 0x0500 #include<stdio.h> #include<windows.h>typedefunsignedint uintptr_t;class Tracer2 {typedefstruct { uintptr_t addr; DWORD tid;void (*call_me)(void**);char old_byte[1]; } rec_info_t;static rec_info_t breaks[10];staticchar int3[1];staticvoid patch_code(void* where, char* what, int size) { DWORD old = 0; VirtualProtect(where, size, PAGE_READWRITE, &old);for (int i=0; i<size; i++) { *((char*)where+i) = *((char*)what+i); } VirtualProtect(where, size, old, &old); FlushInstructionCache(GetCurrentProcess(), where, size); } staticbool add_break(void* at, void (*who)(void**)) { rec_info_t* br = NULL;for (int i=0; i<sizeof(breaks)/sizeof(breaks[0]); i++) {if (breaks[i].addr == 0) { br = breaks + i;break; } }if (br == NULL) {return false; } br->addr = (uintptr_t)at; memcpy(br->old_byte, at, sizeof(br->old_byte)); br->call_me = who; patch_code(at, int3, sizeof(int3));return true; }static LONG WINAPI trap(PEXCEPTION_POINTERS ExceptionInfo) { uintptr_t pc = ExceptionInfo->ContextRecord->Eip; rec_info_t* br = NULL; DWORD ec = ExceptionInfo->ExceptionRecord->ExceptionCode;if (ec != EXCEPTION_BREAKPOINT && ec != EXCEPTION_SINGLE_STEP) {return EXCEPTION_CONTINUE_SEARCH; }bool traced = (ec == EXCEPTION_SINGLE_STEP); DWORD me = GetCurrentThreadId();for (int i=0; i<sizeof(breaks)/sizeof(breaks[0]); i++) {if (traced) {if (me == breaks[i].tid) { br = breaks + i;break; } } else {if (breaks[i].addr == pc) { br = breaks + i;break; } } }if (br == NULL) {return EXCEPTION_CONTINUE_SEARCH; }if (traced) { patch_code((void*)br->addr, int3, sizeof(int3)); ExceptionInfo->ContextRecord->EFlags &= ~0x100; } else { br->tid = me; patch_code((void*)pc, br->old_byte, sizeof(br->old_byte));void** args = (void**)(ExceptionInfo->ContextRecord->Esp+4); br->call_me(args); ExceptionInfo->ContextRecord->EFlags |= 0x100; }return EXCEPTION_CONTINUE_EXECUTION; }public: Tracer2() { memset(breaks, 0, sizeof(breaks)); AddVectoredExceptionHandler(1, &trap); } ~Tracer2() { RemoveVectoredExceptionHandler(&trap); }bool add(void* where, void (*who)(void**)) {return add_break(where, who); } }; Tracer2::rec_info_t Tracer2::breaks[10]; char Tracer2::int3[] = { (char)0xcc };voidmy_exit2(void** args) { printf("custom exit: code %d\n", (int)args[0]); getchar(); }intmain(int argc, char* argv[]){ Tracer2* tracer = new Tracer2(); tracer->add(exit, &my_exit2);return 239; }