标 题: 调试器设计(2) 作 者: Tweek 时 间: 2010-2-26 最开始,调试器给我的感觉是:被调试程序是调试器的一个子程序,调试器完全包含和控制 被调试程序。相当于一种包含关系。 而且:调试器的基本循环。我一直觉得不能裁开。只能这样不断循环。 while ( TRUE == bContinue ) { bContinue = WaitForDebugEvent ( &stDE , INFINITE ) ; { …… } ContinueDebugEvent(stDE.dwProcessId, stDE.dwThreadId, DBG_CONTINUE); } 但是,后来。才发现,很多都是微软的猫腻。 CreateProcess和直接运行exe没有本质区别。 WaritForDebugEvent仅仅是等待调试事件的返回。 ContinueDebugEvent仅仅是继续被挂起的被调试程序。 任何调试事件 都是内核捕捉处理。内核在捕捉到事件后,处理保存,然后,发送调试事件信息的副本过去。 细细想想ring3的调试器,不过是在被调试进程被挂起的时候,对其信息的获取和修改。 调试事件和异常,是被调试程序自己的事情。我相信 exe在运行的时候 内核对程序也是以调试方式进行的。而ring3的调试器,不过是内核把自己的东西 分了一份出来而已。 还是 来看一个例子:test是 可以自己产生int3断点。可以附加进程进行调试。可以创建子进程的 测试程序。 我们直接点击int3.程序出错,提示是否需要调试 我们可以用vs调试程序。 关闭程序。重新运行,并创建子进程。 子进程和父进程都是同一exe模块。 输入父进程PID,debug父进程。 然后弹出 CREATE_PROCESS_DEBUG_EVENT的调试事件。接着点击确定后 大量的 LOAD_DLL_DEBUG_EVENT 事件。 我们知道,父进程的创建和加载dll,先于子进程。但是,子进程在附加调试父进程的时候,却可以捕捉到已经过去的调试事件。 那么,调试器捕捉到的异常,都应该是被进程内核提前获得和保留的。我相信,内核态有一个长长的队列,按照顺序保存这进程出现的所有调试事件。一旦有其他进程有了获得的权限后,就按照顺序,通过底层发给相应的进程。 我们继续疯狂,点掉大量的对话框,最后…… 没有对话框了。程序出现未响应状态。这是因为里面程序在等待调试事件。这时,回到父进程,点击int3. 子进程捕捉到 异常。 异常地址有点怪,应该是被重定位了。 然后,父进程退出 系统,但是退出的时候,并没有提示,是否需要调试。那么如何知道,程序已经被其他程序调试。系统应该保留了一个队列,保存调试事件信息。如果遇到系统无法处理的事件,那么将它放入队列,检测是否有其他进程在调试,如果有,发送过去,并清空刚才的那个信息。如果没有,则提示是否需要调试,并调用vs附加相应进程。 虽然,中断和异常都会被内核获得,然后保留。但是,并不是所有的异常都会需要调试器处理。有些系统自己故意设置的int3断点,就会被系统自己处理掉,但是,同样会返回给调试器调试异常。 我用我自己写的一个简单调试引擎加载debuggee 第一个异常断点是在0x770ce60e(这个断点的作用在介绍int3断点的时候介绍) 查看下内存(上次说过,dll是映射的,所以查看哪个进程的内存空间,Ntdll加载的地址都是一样的) 中断在Ntdll上。 再用刚才的调试引擎反汇编 果然是int3.但是,系统并没有崩溃,或者提示错误,只是将这个异常平静的发送给了调试器。 对于程序可不可以在自己进程空间对自己进行调试?我没有测试过。不过可以想到,微软是不会这样设计的,windows的调试是为了分析和处理异常的,不是用来完成破解。如果,程序可以自己处理异常,那么还需要把把自己产生的异常,再发给自己一次吗? 最后,再完整的看看 程序从被加载到被调试的大概过程: 调试器用CreateProcess或者DebugActiveProcess得到获取一个进程所有调试事件的权限(这个权限仅仅是为了后面的调试API的使用)。然后,被调试进程在出现异常的时候,马上被进程的内核捕捉到,进程被系统挂起。如果内核代码有处理这种问题的方法,会在被处理后,发送相关信息到一个队列,调试器利用WaitForDebugEvent在这个队列里面得到调试事件(需要有调试权限才能执行这个函数)。在进行完一些操作后,调用ContinueDebugEvent 继续执行调试事件。等待下一次异常事件的到来。 这个需要提到一个问题ContinueDebugEvent的参数DBG_EXCEPTION_NOT_HANDLED和DBG_CONTINUE如果选择前一个,将直接执行被挂起线程。如果选择后一个参数,将停止所有异常处理然后执行挂起线程。我不太明白msdn上说的停止异常处理是指的调试器还是被调试程序。但是,在我测试的范围里,还没有感觉出这个两个参数在ring3级体现的不同。 (附件是测试程序,在D盘根目录下运行。) 待续……