大话调试器(附录)

第6篇中的完整源代码,简单起见写在一个C文件里。
编译时把文件名后缀改成.C。因为是C风格的,C++可能编译不过。
在MSVC++6(SP4)/GCC3.3.1、Windows2000(SP4)上编译运行均没问题。

/* ex2.c by Boco@USTC 2005-1-4 */

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#define NO_TRACE /* 将此句注释,则详细查看调试器的工作 */

#define TF_BIT 0x100
#define INT3 0xCC
#define TASKBAR_CLASS "Shell_TrayWnd"

#define MAX_PROC 256
#define MAX_THRD 256
#define MAX_DLL 256
#define MAX_BP 8

#define HANDLE_EVENT(id,func) \
   case id: func(); break;
#define HANDLE_DEFAULT() \
   default: mark=DBG_CONTINUE;
#define alloc(ar,max) \
   _alloc(ar,sizeof(ar[0]),max)
#define find(ar,max,id) \
   _find(ar,sizeof(ar[0]),max,id)
#define suspend_except(ts,id) \
   thread_except(ts,id,SuspendThread)
#define resume_except(ts,id) \
   thread_except(ts,id,ResumeThread)

typedef struct process process;
typedef struct thread thread;
typedef struct module module;
typedef struct breakpoint breakpoint;

struct breakpoint /* 存储断点信息 */
{
   BOOL f; /* 有效位,下同 */
   DWORD addr; /* 断点物理位置 */
   BYTE c; /* 保存被断点覆盖的代码 */
};

struct thread /* 存储线程信息 */
{
   BOOL f;
   DWORD id; /* 线程ID */
   HANDLE h; /* 线程句柄 */
   LPVOID addr; /* 线程执行的初始地址 */
};

struct module
{
   BOOL f;
   LPVOID base; /* 模块基地址 */
   HANDLE h; /* 模块的文件句柄*/
};

struct process
{
   BOOL f;
   DWORD id; /* PID */
   HANDLE h; /* 进程句柄 */
   LPVOID base; /* 程序(EXE)模块基地址 */
   LPVOID addr; /* 程序的启动地址 */
   BOOL init_bp; /* 标识初始断点是否已经发生 */
   breakpoint* write_back; /* 存放单步后准备写回的断点 */

   thread ts[MAX_THRD]; /* 存放该进程内的线程 */
   module ms[MAX_DLL]; /* 存放该进程内的模块 */
   breakpoint bps[MAX_BP]; /* 存放该进程内的断点 */
};

/* declaration */
void on_create_process();
void on_exit_process();
void on_create_thread();
void on_exit_thread();
void on_load_dll();
void on_unload_dll();
void on_exception();
void on_bp(DWORD pid,DWORD tid,DWORD addr);

void* _alloc(void* ar,size_t cb,size_t max);
void* _find(void* ar,size_t cb,size_t max,DWORD id);
void safe_read(HANDLE p,DWORD addr,BYTE* c);
void safe_write(HANDLE p,DWORD addr,BYTE c);
void thread_except(thread ts[],DWORD id,DWORD (CALLBACK* func)(HANDLE));
void set_bp(DWORD pid,DWORD addr);

void my_init(DWORD pid);

process ps[MAX_PROC];

DEBUG_EVENT d;
DWORD mark;

void debug(DWORD id) /* debug loop */
{
   if (!DebugActiveProcess(id))
     assert(0);

   memset(ps,0,sizeof(ps));
 
   for (;;)
   {
     if (!WaitForDebugEvent(&d,INFINITE))
       assert(0);
     switch (d.dwDebugEventCode) /* 派发调试事件 */
     {
       HANDLE_EVENT(CREATE_PROCESS_DEBUG_EVENT,on_create_process);
       HANDLE_EVENT(EXIT_PROCESS_DEBUG_EVENT,on_exit_process);
       HANDLE_EVENT(CREATE_THREAD_DEBUG_EVENT,on_create_thread);
       HANDLE_EVENT(EXIT_THREAD_DEBUG_EVENT,on_exit_thread);
       HANDLE_EVENT(LOAD_DLL_DEBUG_EVENT,on_load_dll);
       HANDLE_EVENT(UNLOAD_DLL_DEBUG_EVENT,on_unload_dll);
       HANDLE_EVENT(EXCEPTION_DEBUG_EVENT,on_exception);
       HANDLE_DEFAULT();
     }
     /* 继续程序执行 */
     if (!ContinueDebugEvent(d.dwProcessId,d.dwThreadId,mark))
       assert(0);
     if (d.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT)
       break;
   }
}

/* event handlers */

void on_create_process()
{
   process* p;
   thread* t;
   module* m;
   CREATE_PROCESS_DEBUG_INFO* e;

   e=&d.u.CreateProcessInfo;
   p=alloc(ps,MAX_PROC); /* 产生进程对象 */
   p-&gt;id=d.dwProcessId;
   p-&gt;h=e-&gt;hProcess;
   p-&gt;addr=e-&gt;lpStartAddress;
   p-&gt;base=e-&gt;lpBaseOfImage;
   t=alloc(p-&gt;ts,MAX_THRD); /* 产生进程里第一个线程对象 */
   t-&gt;id=d.dwThreadId;
   t-&gt;h=e-&gt;hThread;
   t-&gt;addr=e-&gt;lpStartAddress;
   m=alloc(p-&gt;ms,MAX_DLL); /* 产生进程里的程序模块对象 */
   m-&gt;base=e-&gt;lpBaseOfImage;
   m-&gt;h=e-&gt;hFile;
   mark=DBG_CONTINUE;
#ifndef NO_TRACE
   printf("process(%d) created\n",p-&gt;id);
   printf("thread(%d) created\n",t-&gt;id);
   printf("module loaded at 0x%08x\n",m-&gt;base);
#endif
}

void on_exit_process()
{
   process* p;
   int i;

   p=find(ps,MAX_PROC,d.dwProcessId);
   CloseHandle(p-&gt;h);
   /* 结束调试前,把所有没有关闭的句柄关闭 */
   for (i=0;i<MAX_THRD;i++)
   {
     if (p->ts[i].f)
     {
       CloseHandle(p-&gt;ts[i].h);
       p-&gt;ts[i].f=FALSE;
     }
   }
   for (i=0;i<MAX_DLL;i++)
   {
     if (p->ms[i].f)
     {
       CloseHandle(p-&gt;ms[i].h);
       p-&gt;ms[i].f=FALSE;
     }
   }
   mark=DBG_CONTINUE;
}

void on_create_thread()
{
   process* p;
   thread* t;
   p=find(ps,MAX_PROC,d.dwProcessId);
   t=alloc(ps-&gt;ts,MAX_THRD);
   t-&gt;id=d.dwThreadId;
   t-&gt;h=d.u.CreateThread.hThread;
   t-&gt;addr=d.u.CreateThread.lpStartAddress;
   mark=DBG_CONTINUE;
#ifndef NO_TRACE
   printf("thread(%d) created\n",t-&gt;id);
#endif
}

void on_exit_thread()
{
   process* p;
   thread* t;
   p=find(ps,MAX_PROC,d.dwProcessId);
   t=find(ps-&gt;ts,MAX_THRD,d.dwThreadId);
   CloseHandle(t-&gt;h);
   t-&gt;f=FALSE;
   mark=DBG_CONTINUE;
#ifndef NO_TRACE
   printf("thread(%d) exit with code: %d\n",
     t-&gt;id,d.u.ExitThread.dwExitCode);
#endif
}

void on_load_dll()
{
   process* p;
   module* m;
   p=find(ps,MAX_PROC,d.dwProcessId);
   m=alloc(ps-&gt;ms,MAX_DLL);
   m-&gt;h=d.u.LoadDll.hFile;
   m-&gt;base=d.u.LoadDll.lpBaseOfDll;
   mark=DBG_CONTINUE;
#ifndef NO_TRACE
   printf("module loaded at 0x%08x\n",m-&gt;base);
#endif
}

void on_unload_dll()
{
   process* p;
   module* m;
   p=find(ps,MAX_PROC,d.dwProcessId);
   m=find(ps-&gt;ms,MAX_DLL,(DWORD)d.u.UnloadDll.lpBaseOfDll);
   CloseHandle(m-&gt;h);
   m-&gt;f=FALSE;
   mark=DBG_CONTINUE;
#ifndef NO_TRACE
   printf("module unloaded at 0x%08x\n",m-&gt;base);
#endif
}

void on_exception()
{
   process* p;
   thread* t;
   breakpoint* bp;
   EXCEPTION_RECORD* r;
   CONTEXT ctx;
   BOOL f;
   p=find(ps,MAX_PROC,d.dwProcessId);   /* 找到发生异常的进程 */
   if (!p-&gt;init_bp)   /* 对初始断点特别关注 */
   {
     mark=DBG_CONTINUE;
     p-&gt;init_bp=TRUE;
     my_init(p-&gt;id);
   }
   else
   {
     mark=DBG_EXCEPTION_NOT_HANDLED;

     r=&d.u.Exception.ExceptionRecord;
     if (d.u.Exception.dwFirstChance) /* 异常是第一次触发吗?(详见第2篇) */
     {
       if (r-&gt;ExceptionCode==EXCEPTION_BREAKPOINT) /* 触发了断点中断 */
       {
         /* 根据中断地点查找断点记录 */
         bp=find(ps-&gt;bps,MAX_BP,(DWORD)r-&gt;ExceptionAddress);

         if (bp) /* 是调试器安置的断点 */
         {
           t=find(ps-&gt;ts,MAX_THRD,d.dwThreadId); /* 取线程上下文 */
           memset(&ctx,0,sizeof(ctx));
           ctx.ContextFlags=CONTEXT_FULL;
           f=GetThreadContext(t-&gt;h,&ctx);
           assert(f);
           ctx.Eip--;   /* 将EIP值减1 */
           ctx.EFlags|=TF_BIT; /* 打开CPU单步标记 */
           f=SetThreadContext(t-&gt;h,&ctx); /* 向目标写入修改过的上下文 */
           assert(f);
         
           ps-&gt;write_back=bp; /* 标记该进程在单步结束后准备写回的断点 */
           safe_write(ps-&gt;h,bp-&gt;addr,bp-&gt;c); /* 写入被断点覆盖的代码 */

           on_bp(d.dwProcessId,d.dwThreadId,bp-&gt;addr); /* 用户处理 */

           /* 挂起除异常线程外的所有线程 */         
           suspend_except(ps-&gt;ts,d.dwThreadId);

           mark=DBG_CONTINUE;
         }
       }
       else if (r-&gt;ExceptionCode==EXCEPTION_SINGLE_STEP) /* 发生了单步中断 */
       {
         bp=ps-&gt;write_back; /* 获得准备写回的断点记录 */
         if (bp) /* 如果bp==0则表示此单步中断不是调试器引起的 */
         {
           ps-&gt;write_back=NULL;
           safe_write(ps-&gt;h,bp-&gt;addr,INT3); /* 写回断点 */
           resume_except(ps-&gt;ts,d.dwThreadId); /* 恢复其它线程的运行 */
           mark=DBG_CONTINUE;
         }
       }
     }
     else
     {
       assert(0); /* 如果运行到这里,表示目标程序出错了,咱不考虑…… */
     }
   }
}

/* 从数组中选(分配)一个未用的 */
void* _alloc(void* ar,size_t cb,size_t max)
{
   size_t i;
   for (i=0;i<max;i++,ar=(void*)((DWORD)ar+cb))
   {
     if (!*(BOOL*)ar)
     {
       *(BOOL*)ar=TRUE;
       return ar;
     }
   }
   return 0;
}

/* 从数组中查找id所对应的元素 */
void* _find(void* ar,size_t cb,size_t max,DWORD id)
{
   size_t i;
   for (i=0;i&lt;max;i++,ar=(void*)((DWORD)ar+cb))
   {
     if (*(BOOL*)ar && *(DWORD*)((BOOL*)ar+1)==id)
       return ar;
   }
   return 0;
}

/* 对所有ID不为id的线程调用func */
void thread_except(thread ts[],DWORD id,
            DWORD (CALLBACK* func)(HANDLE))
{
   int i;
   for (i=0;i&lt;MAX_THRD;i++)
   {
     if (ts[i].f && ts[i].id!=id)
       func(ts[i].h);
   }
}

void safe_read(HANDLE p,DWORD addr,BYTE* c)
{
   DWORD r,old;
   VirtualProtectEx(p,(LPVOID)addr,1,PAGE_READWRITE,&old);
   ReadProcessMemory(p,(LPVOID)addr,c,1,&r);
   VirtualProtectEx(p,(LPVOID)addr,1,old,&old);
   assert(r==1);
}

void safe_write(HANDLE p,DWORD addr,BYTE c)
{
   DWORD r,old;
   VirtualProtectEx(p,(LPVOID)addr,1,PAGE_READWRITE,&old);
   WriteProcessMemory(p,(LPVOID)addr,&c,1,&r);
   VirtualProtectEx(p,(LPVOID)addr,1,old,&old);
   assert(r==1);
}

/* 设置断点 */
void set_bp(DWORD pid,DWORD addr)
{
   process* p;
   breakpoint* bp;
   p=find(ps,MAX_PROC,pid);
   bp=alloc(ps->bps,MAX_BP);
   bp-&gt;addr=addr;
   safe_read(p-&gt;h,addr,&bp-&gt;c);
   safe_write(p-&gt;h,addr,INT3);
}

/* main */

int main(int argc,char* argv[])
{
   DWORD id;

   /* 根据任务栏的窗口类名称查找explorer.exe的进程ID */
   GetWindowThreadProcessId(FindWindow(TASKBAR_CLASS,""),&id);

   printf("found explorer PID: %d\n",id);
   debug(id);
   return 0;
}

void my_init(DWORD pid)
{
   /* 在CreateProcessW处设置断点。
      注意:KERNEL32.dll模块的基地址是不变的,
      所以调试器进程和目标进程的CreateProcessW地址一样*/
   set_bp(pid,(DWORD)GetProcAddress(
     GetModuleHandle("kernel32.dll"),"CreateProcessW"));
}

/* 从目标程序的地址空间里读一个Unicode字符串 */
void read_string(HANDLE h,DWORD addr,char* s,int max)
{
   char* p;
   int i;
   p=(char*)malloc(sizeof(char)*max*2);
   assert(p);
   for (i=0;i<max-1;i++)
   {
     safe_read(h,addr++,p+2*i);
     safe_read(h,addr++,p+2*i+1);
   }
   p[2*i]=p[2*i+1]=0;
   WideCharToMultiByte(CP_ACP,0, /* 转换成ANSI字符串 */
     (LPCWSTR)p,-1,s,max,NULL,NULL);
   free(p);
}

/* 代表断点触发时的用户处理 */
void on_bp(DWORD pid,DWORD tid,DWORD addr)
{
   char s[256];
   process* p;
   thread* t;
   CONTEXT ctx;
   DWORD a,r;
   p=find(ps,MAX_PROC,pid); /* 根据进程ID查找进程 */
   t=find(p->ts,MAX_THRD,tid); /* 根据线程ID查找触发断点的线程 */
   memset(&ctx,0,sizeof(ctx));
   ctx.ContextFlags=CONTEXT_FULL;
   GetThreadContext(t-&gt;h,&ctx); /* 获得线程上下文 */
   a=ctx.Esp+8; /* 取CreateProcessW的第二个参数 */
   ReadProcessMemory(ps-&gt;h, /* 间址一次从栈里得到字符串首地址 */
     (LPVOID)a,&a,4,&r);
   assert(r==4);
   read_string(ps-&gt;h,a,s,256); /* 根据参数读出字符串 */
   printf("executing %s\n",s);
}

你可能感兴趣的:(职场,休闲,大话,调试器,附录)