不知道你有没有听说过OD,我们可以模仿他的思路
下int 3软件断点,然后获得线程上下文就可以了。
MSDN里面有大量的Debugging Functions,你可以去看下。下面是一段微型调试器的代码
- C/C++ code
-
#include <windows.h> #include <stdio.h> #include <assert.h> int main() { DEBUG_EVENT d; DWORD mark; BOOL initBP,r; DWORD pid; initBP=0; printf("set pid=? "); scanf("%d",&pid); r=DebugActiveProcess(pid); /* 开始调试 */ assert(r); printf("debugging..."); for (;;) { if (!WaitForDebugEvent(&d,INFINITE)) /* 等待调试事件 */ assert(0); switch (d.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: case EXIT_PROCESS_DEBUG_EVENT: case CREATE_THREAD_DEBUG_EVENT: case EXIT_THREAD_DEBUG_EVENT: case LOAD_DLL_DEBUG_EVENT: case UNLOAD_DLL_DEBUG_EVENT: case OUTPUT_DEBUG_STRING_EVENT: case RIP_EVENT: mark=DBG_CONTINUE; /* 这些事件都不需要处理,让目标直接运行 */ break; case EXCEPTION_DEBUG_EVENT: if (initBP) { mark=DBG_EXCEPTION_NOT_HANDLED; /* 没处理就是没处理*/ } else { initBP=1; mark=DBG_CONTINUE; /* 初始断点要特别留意 */ } break; } if (!ContinueDebugEvent( d.dwProcessId, d.dwThreadId,mark)) /* 继续执行目标程序 */ assert(0); if (d.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT) break; } printf("stopped/n"); return 0; }
如上是直接附加到活动进程,如果我们学着OD,加载进程后,在OEP那里断下的话,用CreateProcess创建进程,标志位打上Debug标志即可,类似下面这样
- C/C++ code
-
STARTUPINFO st = {0}; PROCESS_INFORMATION pro = {0}; st.cb = sizeof(st); CreateProcess(NULL, "d://test.exe ", NULL, NULL, TRUE, DEBUG_ONLY_THIS_PROCESS, NULL, NULL, &st, &pro); CloseHandle(pro.hThread); CloseHandle(pro.hProcess); DEBUG_EVENT dbe; BOOL rc; while(WaitForDebugEvent(&dbe, INFINITE)) { if(dbe. dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; ContinueDebugEvent(dbe.dwProcessId, dbe.dwThreadId ,DBG_CONTINUE ); } //有的代码我就略过了,在上课,不方便写很多
软件断点(INT3):对应的异常是 EXCEPTION_BREAKPOINT,处理方式为恢复代码为原来字节,并将EIP减一,并设置单步以便进入单步后重新设置这个一般断点。
那么现在要解决的问题就剩一个了,就是写入断点int3,OEP那边断下之后就要写,然后开始执行。
这个可以用WriteProcess实现,当你初次加载之后,进程虽然断下来了,但是PE已经加载进了内存空间中。
根据偏移,把int 3对应的十六进制0xCC指令写入
断下来之后,用GetThreadContext获得线程上下文就可以得到寄存器的值了
这里有些注意点,看下面的代码
- C/C++ code
-
if (r->ExceptionCode==EXCEPTION_BREAKPOINT) /* 触发了断点中断 */ { /* 根据中断地点查找断点记录 */ bp=find(ps->bps,MAX_BP,(DWORD)r->ExceptionAddress); if (bp) /* 是调试器安置的断点 */ { t=find(ps->ts,MAX_THRD,d.dwThreadId); /* 取线程上下文 */ memset(&ctx,0,sizeof(ctx)); ctx.ContextFlags=CONTEXT_FULL; f=GetThreadContext(t->h,&ctx); assert(f); ctx.Eip--; /* 将指令指针值减1 */ ctx.EFlags|=TF_BIT; /* 打开CPU单步标记 */ f=SetThreadContext(t->h,&ctx); /* 向目标写入修改过的上下文 */ assert(f); ps->write_back=bp; /* 标记该进程在单步结束后准备写回的断点 */ safe_write(ps->h,bp->addr,bp->c); /* 写入被断点覆盖的代码 */ on_bp(d.dwProcessId,d.dwThreadId,bp->addr); /* 用户处理 */ suspend_except(ps->ts,d.dwThreadId); /* 挂起除异常线程外的所有线程 */ mark=DBG_CONTINUE; } }