和线程句柄。
为了管理单 步中断处理 我们还必须维护一个基于线程的单步地址的管理 这样就可以允许被调试程序 拥有多线程的功能。
--我们不能保证单步运行时不被该进程的其他线程所打断。
// 我们利用一个 map 进行管理线程 ID 和线程句柄之间的关系 // 同时也用一个 map 管理函数地址和断点的关系 typedef mapltDWORD HANDLE lessltDWORDgt gt THREAD_MAP typedef mapltDWORD void lessltDWORDgt gt THREAD_SINGLESTEP_MAP THREAD_MAP _gthreads FUN_BREAK_MAP _gFunBreaks // 并且假设设置断点时采用了如下方案进行原来代码的管理 BYTE code SetBreakPointpFunAdd 0xCC ifcode 0xCC _gFunBreakspFunAdd code … // 调试处理程序 BOOL WINAPI OnDebugEventDEBUG_EVENT pEvent BOOL rc TRUE switchpEvent-gtdwDebugEventCode case CREATE_PROCESS_DEBUG_EVENT: // 记录线程 ID 和线程句柄的关系 _gthreadspEvent-gtdwThreadId pEvent-gtu.CreateProcessInfo.hThread … break case CREATE_THREAD_DEBUG_EVENT: // 记录线程 ID 和线程句柄的关系 _gthreads pEvent-gtdwThreadId pEvent-gtu.CreateThread.hThread … break case EXIT_THREAD_DEBUG_EVENT: // 线程退出时清除线程 ID _gthreads.erase pEvent-gtdwThreadId … break case EXCEPTION_DEBUG_EVENT: // 中断处理程序 rc OnDebugExceptionpEvent break … return rc 下面进行中断处理程序。
同样 我们只考虑我们关心的中断信息代码。
在发生中断时 们 我 通过 GetThreadContextampcontext得到中断线程的上下文信息。
此时context.esp 就 是函数的返回地址 context.esp4 位置的值就是函数的第一个参数context.esp8 就是第二个参数依次类推可以得到你想要的任何参数。
需要注意的是 因为参数是在被调 试进程中的内容所以你必须通过 ReadProcessMemory 函数才能得到 DWORD buf4 // 取 4 个参数 ReadProcessMemory_ghDebug voidcontext.esp 4 ampbuf sizeofbuf ampdwRead 那么 buf0就是第一个参数buf1就是第二个参数。
。
。
注意在 FunAint a char p OPENFILENAME pof函数调用时buf0 a buf1 p 这里 buf1是 p 的 指针而不是 p 的内容如果你希望访问 p 的内容必须同样通过 ReadProcessMemory 函数再次取得 p 的内容。
对于结构 体指针也必须如此 // 取得 p 的内容 char pBuf256 ReadProcessMemory_ghDebug voidbuf1 amppBuf sizeofpBuf ampdwRead //取得 pof 的内容 OPENFILENAME of ReadProcessMemory_ghDebug voidbuf2 ampof sizeofof ampdwRead 如果结构体中还有指针要取得该指针的内容也必须和取得 p 的内容一样的方式读取被 调试程序的内存。
总的来说 你必须意识到监视目标程序的所有内容都是对目标进程的内存 读取操作这些指针都是目标进程的内存地址而不是调试进程的地址。
很明显 当被调试进程在函数入口产生中断调试信息时 调试程序只能得到函数的输入参数 而不 能得到我们希望的输出参数及返回值为了实现我们的目标我们必须在函数调用结 束时再次产生中断取得函数的输出参数和返回值。
在处理函数入口中断时 就必须设 置好函数的返回地址的断点。
这样 在函数返回时 就可以得到函数的输出参数和返回值了。
关于这里的实现说明请参考附录的源代码。
你完全可以参照附录的源代码写出你自己的简单的调试监视程序。
当然有几个问题因 为 比较复杂我没有在这里进行说明。
一个就是函数返回断点的处理比如 TRY、CATCH 的 处理就必须重新设计好 RETURN_FUN_STACK 的结 构考虑一些除错处理还是可以解 决这个问题的。
另外一个问题就是函数的入口断点和返回断点没有任何关系。
这个问题更好 解决只需重新设计 RETURN_FUNFUN_BREAK_MAP 等结构体就可以将它们关联起 来。
由于我在这里只要是分析如何实现中断调试处理的过程这些完善程序的工 作就由读 者自行跟踪改造了。
关于 Win9X 系统 细心的读者在上面可以发现一个问题那就是在 SetBreakPoint 函数中有一个限制 就 是函数的入口地址不能大于 0x80000000。
确实如此我们知道 0x80000000 以上的空 间是系统共有的空间我们一般不能修改这些空间的程 序否则将影响系统的工作。
在 NT 环境下 所有的 DLL 都被加载在 0x80000000 下 修改 0x80000000 以下空间的代码不 会对其它进程产生影 响。
所以在 NT 下可以用上面的方案监视所有的 DLL 函数。
然而在 Win9X 下 kernel32.dll user32.dll gdi32.dll 等 系统 DLL 都被加载到 0x80000000 以上的空间修改这些空间的代码将破坏系统工作。
那么在 9X 下就不能监视这 些 DLL 模块的函数吗 的确在 Win9X 平台下不能利用在函数入口处设置断点的方法实现监视。
我们必须 采用 另外的方法实现该功能。
在前面讨论中知道通过 API HOOK 修改模块导入表的方法可以 实现将 API 的入口修改为自己监视程序的入口 也可以实现监视功能。
如果采用 API HOOK .
上一篇:
安装并配置vCenter_5_vCenter_Server_Heartbeat
下一篇:
恋沫