PDF加了目录,可以快速的定位.
附件pdf文档,以及用到的部分功能函数
第三章 int3断点与硬件断点
3.1 问题的提出与解决
3.1.1 问题提出
如何实现软硬件断点?如何判断用户断点的正确性?如何提高效率?
3.2 问题的解决
3.2.1 Int3断点的实现
8086/8088提供断点指令Int3和单步标志TF,调试工具利用它们可以设置断点和实现单步。从80386开始,在片上集成了调试寄存器。利用这些 调试寄存器不仅可以设置代码执行断点,而且还可以设置数据访问断点,不仅可以把断点设置在RAM中,也可以把断点设置在ROM中。
Int3是Intel系列CPU专门引入的用于表示中断的指令,对于Int3断点,常见的做法是在用户要下断点的地方,将指令替换成CC,当调试程序运行 到CC处,调试器将会收到EXCEPTION_BREAKPOINT消息,然后在这里将原CC处的代码还原及EIP减一再处理用户的请求。用Int3的好 处很明显,可以设任意多个断点,缺点是改变了程序的原指令,容易被软件检测到,而且这里可能会遇到一个问题,当用户在某个内存分页设了一个不可读,不可写 的断点,这时调试器是无法将CC写进去的,也无法将原来的指令读出来!所以在设之前,我们先将目标内存页的属性设为可读可写的,设完之后再将内存页置为新 的属性(移除了用户断点权限的新属性),这个开销是非常大的。
本程序中,对于用户断点的正确性检测只做了如下判断,首先是判断用户的断点是否是处于有效的内存分页内,然后再判断是否重复,同一个内存地址,只能设一个断点,而对于Int3断点和硬件执行断点并没有判断是否处于指令起始处。
当初的设想是被调试程序跑起来的时候,将被调试程序的指令全部解析一下,然后取得每条指令的正确起始位置,添加到树中,然后用户在下Int3断点的时候, 再判断一下下的断点是否处于指令的首地址处!后来请教了一下钱老师以及查看windbg等同类软件,发现也没有做这类的检测,同时分析觉得此弊大于利,所 以放弃了这种做法,程序中并没有检测。
3.2.2 硬件断点的实现
80386和80486都支持6个调试寄存器,如图:
他们分别是DR0、DR1、DR2、DR3,调试状态寄存器DR6和调试控制寄存器DR7。这些断点寄存器都是32位寄存器。
Dr0~3用于设置硬件断点的线性地址,也就是断点位置,产生的异常是STATUS_SINGLE_STEP 。这些寄存器长32位,与32位线性地址长 度相符,处理器硬件把执行指令所涉及的线性地址和断点地址寄存内的线性地址进行比较,判断执行指令是否触及断点。与Int3不同的是,硬件断点不仅可以设 置在RAM中,也可以设置在ROM中,且速度比Int3更快。
Dr7是一些控制位,用于控制断点的方式。其中有四个GiLi,分别对应四个断点,Gi、Li控制DRi所指示的断点i在断点条件满足是,是否引起异常。
Gi和Li分别称为全局断点允许位和局部断点允许位。在任务切换时,处理器清各Li位,所以Li位只支持一个任务范围内的断点,任务切换并不影响Gi位,所以Gi支持系统内各任务的断点。
R/Wi分别对应四个DRi,R/Wi字段指示DRi断点信息的类型。每一个R/W占两位,所表示的类型为:
RWE字段取值 断点类型 RWE字段取值 断点类型
0 0 只执行 1 0 未定义
0 1 只写入 1 1 只读或写数据
LENi字段指示DRi断点信息的长度,每一个LENi占两位。所表示的长度如下:
LEN字段取值 断点长度(范围) 断点地址寄存器指示的断点地址
0 0 1字节 全部32位指示一个单字节的断点
0 1 2字节 最低1位被忽略,确定字对齐开始的2字节断点
1 0 未定义(不能取该值)
1 1 4字节 最低2位被忽略,确定双字对齐地址开始的4字节断点
注意: 指令执行断点的断点长度必须为1字节,数据访问的断点长度可以是1、2、4字节。
Dr6用于显示是哪些引起断点的原因,如果是Dr0~3或单步(EFLAGS的TF)的话,则相应设置对应的位。
Bi表示由DRi所指示的断点引起的调试陷阱。
对于DR7的设置,原本程序中是使用位移来完成的,但是调试的时候非常之不方便,所以后来改用结构体了。结构体的定义如下:
3.2.3 对于int3断点的优化
当设int3断点的时候,判断一下当前调试寄存器有没有空闲的,有的话则优先使用调试寄存器,因为int3断点涉及内存分页属性,读目标进程内存,写目标进程,而硬件断点只需设调试寄存器,这样可以大大的提高效率。
附件:
第三章附件