程序断点的原理
程序断点的原理
- 调试器 Debugger 并不能控制程序的执行顺序,它之所以可以让 CPU 在需要的地方停住(随心所欲的停止程序的执行),主要通过软件断点和硬件断点两种方式。
软件断点
- 软件断点在
x86/x32/x64系统中就是指令INT 3,它的二进制代码opcode是0xCC。当程序执行到INT 3指令时,会引发软件中断。操作系统的INT 3中断处理器会寻找注册在该进程上的调试处理程序,从而像Windbg和VS等等调试器就有了上下其手的机会。 通过一个例子说明:
1
2
3
4
5
6
int main()
{
printf("Hello World"); // 这里断点
}此时调试器会将目标地址的指令替换为
int 3指令(机器码0xCC),等到达断点的时候会还原目标地址的指令。一般情况下,调试器维护了一大组调试断点,在并把他们都换成了
INT 3。在被调度回来后,会都填回去,并通过现在的地址判断是到了那个断点。软件断点没有数目限制。
硬件断点
x86/x32/x64系统中提供8个调试寄存器(DR0 ~ DR7)和多个MSR用于硬件调试(MSR数量取决于CPU型号)。其中前四个DR0 ~ DR3是硬件断点寄存器,可以放入内存地址或者IO地址,还可以设置为执行、修改等条件。CPU在执行的到这里并满足条件会自动停下来。- 硬件断点十分强大,但缺点是只有四个,这也是为什么所有调试器的硬件断点只能设置
4个的原因。我们在调试不能修改的ROM时,只能选择这个,所以要省着点用,在一般情况下还是尽量选择软件断点。 - 补充:
- 调试控制寄存器 (
DR7):这个寄存器至关重要,它负责定义每个硬件断点的具体行为方式。需要通过设置DR7中的相应位域来为DR0-DR3中的每个地址配置:- 触发条件 (
R/Wx): 指定在什么操作下触发断点,例如执行指令(00)、写入数据(01)、或读写数据(11)等。 - 长度 (
LENx): 指定监控的内存区域大小,例如1字节、2字节或4字节(在64位模式下还支持8字节)。 - 启用开关 (
Lx/Gx): 控制每个断点是仅在当前任务有效(局部,Lx),还是对所有任务都有效(全局,Gx)。
- 触发条件 (
- 调试状态寄存器 (
DR6):当某个硬件断点被触发,CPU陷入调试异常时,这个寄存器会记录哪个断点引起了异常以及其他相关状态信息(例如,是由DR0还是DR1触发的),帮助调试器判断异常原因。 DR4与DR5:这两个寄存器的功能由控制寄存器CR4中的DE(调试扩展)位决定。当CR4.DE = 1时,对DR4和DR5的访问会产生无效操作码异常(#UD),它们被视为保留。当CR4.DE = 0时,它们则被映射为对DR6和DR7的别名访问,此举主要是为了与早期处理器兼容。
- 调试控制寄存器 (
单步执行 (TF标志) 和任务切换断点的机制略。
评论










