调试器编写中遇到的问题

WIN32下R3的调试器其实主要就是两个API:
CreateProcess:
设置调试标志,启动
WaitForDebugEvent:
在其中进行循环,对被调试进程触发的各种消息进行相应,当然这步还是封装下的好,否则很容易对代码造成混乱。
这部分的代码可以在MSDN上找到现成的例子,就不提了。

下面谈谈如何实现三种断点:
1、软断点
在VC中,如果在当前语句下了个断点,且得到了当前语句的地址,那么当调用ReadProcess读取自身内存空间、当前语句时,会发现读到的结果和反汇编的结果不一致,第一个字节会变成0xcc。
0xcc在INTEL手册中代表的汇编语句是int 3,软中断。程序执行到这儿就听来了,使用SEH机制向创建他的进程传递0x80000003消息,问他处理不处理。如果不处理,则他再看自己能不能处理。如果是在调试器中,则将这个消息传递给调试器进程。其中的判断代码为:

代码
    switch (DebugEv.dwDebugEventCode) 
    { 
        
case EXCEPTION_DEBUG_EVENT: 
        
// Process the exception code. When handling 
        
// exceptions, remember to set the continuation 
        
// status parameter (dwContinueStatus). This value 
        
// is used by the ContinueDebugEvent function. 
 
            
switch(DebugEv.u.Exception.ExceptionRecord.ExceptionCode)
            { 
                
case EXCEPTION_ACCESS_VIOLATION: 
                
// First chance: Pass this on to the system. 
                
// Last chance: Display an appropriate error. 
                    break;
 
                
case EXCEPTION_BREAKPOINT: 

 

当收到这个消息的时候进行处理。
简要的说,软中断就是对要调试进程中的汇编语句写0xcc,使其继续执行,执行到目标地址的时候,会断下来,这时可由调试器进行处理(单步、继续执行等)。调试器编写者需准备个容器,将用户下断点时的地址内容保存下来,然后写入0xcc,当用户选择继续执行的时候,将保存的内容写回去。
这时还不行,因为执行线程的EIP已经执行到0xcc之后了,所以得先用GetThreadContext得到当前EIP(第二个标志是传入、传出参数,需注意。我一般使用FULL,把所有的值都得到,调用方便,但容易出错,需注意。),然后将EIP得减去1,再调用SetThreadContext写入到被调试进程在执行的线程,然后继续执行就不会出错了。


2、硬断点
硬断点是最简单实现的,X86架构的CPU具有功能强大的调试寄存器。可以实现读写/执行的硬件断点。
处理器硬件上支持最多4个断点,当然也可以来模拟多个硬件断点。我的想法是因为同时只能到达一个断点,可以弄个链表来保存用户所下的断点。做成类似队列的结构,触发一个,干掉一个,再设置一个。不过应该还是对高低地址进行下排序,来确定谁先上,谁后上的顺序。

3、内存断点
内存断点逻辑上比较复杂,我只实现了单页面只允许单断点。内存断点主要用来判读啊某内存地址是否被读写。内存是分页来存储的,最小操作单位只能是一个分页(1000h),而内存分页又具有权限属性,比如要判断何时读取0x00401005这个地址,可以使0x00401000-0x00402000这一个内存分页都不可读,当读去到这个页面的时候,就会触发0XC0000005异常,告诉调试器无法访问。然后调试器就知道了在读取,就达到了调试效果。说着简单,做起来难,当设置完内存断点之后,读取整个页面任何一个地址,都会触发不允许访问异常。这就麻烦了,那要是读取0x004010010也会不允许访问啊,这不乱套了?下面介绍内存断点实现步骤:
1、调用VirtualQueryEx函数,便利目标进程的所有内存分页(具体代码可以参考WINDOWS核心编程中代码,有需要也可以短信我),每次调用前都需要更新,因为内存总是在创建和释放的。
2、获取到保存断点的内存分页长度问题,这里还涉及个跨内存分页问题,不过逻辑不复杂,就是实现麻烦。保存要下断点的内存分页属性
3、设置内存分页为不可访问,执行
4、访问这块内存分页的时候,会触发
EXCEPTION_ACCESS_VIOLATION异常。判断触发地址是否是用户所下的用户下的地址,如果是,则万事OK,继续执行,写回保存的属性就好。如果不是……
5、如果不是,就将属性写回这个分页,之后就能正常往下跑了,但因为硬件地址还是没到,所以得在下一条语句中将这个内存分页再设成不可访问。这个用软中断就可以了。所以需要设置个标识,用来判断是否是为了这个才断的,再次设置内存分页为不可访问,循环这部,直到到达用户所下的内存断点。

三种断点,大体实现就是这样。有些问题只看别人写,是不能透彻理解的,自己写一遍就会体会到更多的郁闷和乐趣,呵呵
以后想起什么再更新吧,先就这样了

posted @ 2010-09-09 00:36  飘啊飘  阅读(992)  评论(0编辑  收藏  举报