Debugger for Win32 (v2)

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>

typedef unsigned int uintptr_t;

class Tracer2 {
  typedef struct {
    uintptr_t addr;
    DWORD tid;
    void (\*call_me)(void\*\*);
    char old_byte[1];	
  } rec_info_t;

  static rec_info_t breaks[10];
  static char int3[1];

  static void 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);
  } 

  static bool 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 };

void my_exit2(void\*\* args) {
  printf("custom exit: code %d\\n", (int)args[0]);
  getchar();
}


int main(int argc, char\* argv[]){
  Tracer2\* tracer = new Tracer2();

  tracer->add(exit, &my_exit2);

  return 239;
}

Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

nike

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today