(转)调试程序时设置断点的原理

简单总结:有软件断点和硬件断点

软件断点:软件断点在X86系统中为中断指令INT 3,其二进制代码opcode是0xCC。当程序执行到INT 3指令时,会引发软件中断。操作系统的INT 3中断处理器会寻找注册在该进程上的调试处理程序。从而像Windbg和VS等等调试器就有了上下其手的机会。程序出错时常看到的”烫烫烫“、”锟斤拷“、”屯屯屯“等与这个终端指令有关

硬件断点:X86系统提供8个调试寄存器(DR0~DR7)和2个MSR用于硬件调试。

转自:https://zhuanlan.zhihu.com/p/34003929

以下为原文:

对于程序员来说,debug的时间往往比写程序的时间还要长。尤其对我这种专写bug为主的程序员来说,一个好的调试器意味着早点下班和休息。现在方便的调试器很多,有著名的Visual Studio(VS)等IDE,也有免费的Windbg和GDB等等。加个断点也很简单,就是按一下键而已。但你有没有想过,调试器Debugger并不能控制程序的执行顺序,为什么它可以让CPU在需要的地方停住呢?

今天我们就来揭开调试断点的神秘面纱,并通过一个实例来看看调试器实际都做了些什么。调试器能够随心所欲的停止程序的执行,主要通过软件断点和硬件断点两种方式。

软件断点

软件断点在X86系统中就是指令INT 3,它的二进制代码opcode是0xCC。当程序执行到INT 3指令时,会引发软件中断。操作系统的INT 3中断处理器会寻找注册在该进程上的调试处理程序。从而像Windbg和VS等等调试器就有了上下其手的机会。

我们先通过一个例子来看看调试器都倒了什么鬼:

#include
int main ()
{
// This loop takes some time so that we
// get a chance to examine the address of
// the breakpoint at the second printf
for (int i = 1; i < 100000000; i++)
   printf("Hello World!");
for (int i = 1; i < 10000000; i++)
   printf("Hello World!");
return 0;
} 

这是一个比较傻的Hello World程序。我们用Windbg打开它,并设置一个断点:

这时Windbg会将自己Attach到该程序的进程,通过程序PE文件的debug节找到调试信息。在调试信息里面找到加断点行所在的机器代码,并把头一个字节用WriteProcessMemory()函数换成0xCC(INT 3)。

让我们来验证一下:

推荐点开全屏看,可能更清楚

注意左边是Windbg窗口,右边是用Process view打开的进程空间,左右的红框是对应的。在我们设置断点之前,左右的内容是完全一样的,这里要特别注意printf编译出来的第一个二进制代码0x68。接下来我们设置断点,并开始运行,那100万个printf让我们有充分的时间,看看发生了什么:

我们会发现push操作代码0x68600e2900的第一个字节被windbg换成了0xCC也就是INT 3。这样windbg就可以在执行到这里时被调度。

不一会,windbg的断点到了:

到达断点后,操作符又被还原为0x68,似乎什么都没有发生,用户被蒙在鼓里,是不是很有意思?

实际上,一般情况下,调试器维护了一大组调试断点,在并把他们都换成了INT 3。在被调度回来后,会都填回去,并通过现在的地址判断是到了那个断点。软件断点没有数目限制。

硬件断点

X86系统提供8个调试寄存器(DR0~DR7)和2个MSR用于硬件调试。其中前四个DR0~DR3是硬件断点寄存器,可以放入内存地址或者IO地址,还可以设置为执行、修改等条件。CPU在执行的到这里并满足条件会自动停下来。

硬件断点十分强大,但缺点是只有四个,这也是为什么所有调试器的硬件断点只能设置4个原因。我们在调试不能修改的ROM时,只能选择这个,所以要省着点用,在一般情况下还是尽量选择软件断点。

还有个INT 1是单步调试命令,这里略过。

其他

Visual Studio有个有趣的特性是debug编译后,会把0xcc(INT 3)填入代码的空隙,这样一旦程序越界就会被VS捕捉而容易发现错误。而0xCCCC在中国的GBK编码是“烫”。有中国程序员翻看内存到代码段会发现很多"烫烫烫",不明所以,以为发生了什么神奇的事情。

有些程序越界也会打出"烫烫烫":

有的用户被吓得够呛,以为计算机过热了,喊烫了,赶紧关机,十分搞笑。

原文地址:https://www.cnblogs.com/z-sm/p/11956004.html

时间: 2024-10-14 18:02:30

(转)调试程序时设置断点的原理的相关文章

Linux高级编程--04.GDB调试程序(设置断点)

调试已运行的程序 在UNIX下用ps查看正在运行的程序的PID(进程ID),然后用gdb PID格式挂接正在运行的程序. 先用gdb 关联上源代码,并进行gdb,在gdb中用attach命令来挂接进程的PID.并用detach来取消挂接的进程. 暂停 / 恢复程序运行 调试程序中,暂停程序运行是必须的,GDB可以方便地暂停程序的运行.你可以设置程序的在哪行停住,在什么条件下停住,在收到什么信号时停往等等.以便于你查看运行时的变量,以及运行时的流程. 当进程被gdb停住时,你可以使用info pr

调试时设置条件断点

一个好的调试器,能够帮助程序员处理很多自动化的工作.试想下列的情形: 错误是发生在一个循环当中,只在循环遍历了若干次以后,才会出现. 错误只在程序中某个变量为一个特定的值,才会出现,而这个变量的值是在程序运行的过程中随机设置的. 多个线程都要调用同一个函数,而你只想在某几个线程执行这个函数的时候,中断程序的执行. 在上面列出来几种情况当中,如果调试器不能提供一个有效的方法帮助我们设置断点的话,调试这种程序将会是很痛苦的一件事.在第一种情况当中,用户不得不在循环中设置断点,并且要记住自己按下F5的

iOS调试程序时,启动应用失败的解决办法

最近在iOS项目中调试程序,项目中用到第三方应用来启动我的应用程序,调试阶段在实体机上用第三方应用启动我的应用时,出现如下错误,程序停止运行: 同时,在AppDelegate对象的如下方法中设置断点: - (BOOL)application:(UIApplication *)application openURL:(nonnull NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(no

硬件断点的原理与实现

硬件断点的原理 Intel 80306以上的CPU给我们提供了调试寄存器用于软件调试,硬件断点是通过设置调试寄存器实现的. 上图为Intel手册提供的32位操作系统下8个调试寄存器的图示(Intel手册卷3 17章第二节 Debug Registers,有兴趣的朋友可以查阅),根据介绍,DR0-DR3为设置断点的地址,DR4和DR5为保留,DR6为调试异常产生后显示的一些信息,DR7保存了断点是否启用.断点类型和长度等信息. 我们在使用硬件断点的时候,就是要设置调试寄存器,将断点的位置设置到DR

Xcode中如何在即将启动的APP中设置断点调试

在实际的App应用中,我们可能会碰到这么一种情况:我们设计一个APP,它可能会在外界发送某种消息或满足某种条件时,被动的启动加载,并根据外界传入的一些参数进行初始化工作. 这里由于程序是由其他程序启动的,所以我们不能够直接在XCode中编译运行程序并设置断点调试.但幸运的是,XCode提供了附加在进程上的调试功能,并且可以在进程启动前,通过进程名称等待进程运行,并在进程真正加载起来时,进入断点调试.具体方法如下: 1.在待调试的XCode中选择Debug->Attach to Process->

0823--静默安装、fiddler设置断点、f12清除数据记录

刚写了半天,然后没保存,哎,墙角抹泪. Anyway,记一下最近工作中遇到的问题吧. 1. 静默安装 cmd命令:拖入exe 文件,空格后输入--silent-install 若要在安装时修改主页,则在后面再加上空格-homepage=www.baidu.com 2. fiddler设置断点 不得不说这是 个蛮神奇的功能,使用fiddler,Autoresponder中添加规则,设置某URL响应为*bpafter,则在抓取到该请求的返回数据时会先暂停请求,然后根据自己的需要可以进行数据的修改(R

android NDK开发在本地C/C++源码中设置断点单步调试具体教程

近期在学android NDK开发,折腾了一天,最终可以成功在ADT中设置断点单步调试本地C/C++源码了.网上关于这方面的资料太少了,并且大都不全,并且调试过程中会出现各种各样的问题,真是非常磨人.程序员就得有耐心. 把自己的调试过程记录下来.希望对须要的朋友有帮助. 在看本文之前,请先确保你已经成功编译了一个android NDKproject,而且可以在模拟器或者真机上执行.至于怎么编译NDKproject,包含配置.生成.so文件等等.可以參考我的前一篇博客:http://blog.cs

android NDK开发在本地C/C++源代码中设置断点单步调试详细教程

最近在学android NDK开发,折腾了一天,终于能够成功在ADT中设置断点单步调试本地C/C++源代码了.网上关于这方面的资料太少了,而且大都不全,并且调试过程中会出现各种各样的问题,真是很磨人,程序猿就得有耐心.把自己的调试过程记录下来,希望对需要的朋友有帮助. 在看本文之前,请先确保你已经成功编译了一个android NDK工程,并且能够在模拟器或者真机上运行.至于怎么编译NDK工程,包括配置.生成.so文件等等,可以参考我的前一篇博客:http://blog.csdn.net/u013

Linux编程基础——GDB(设置断点)(转:TianFang,cnblog: http://www.cnblogs.com/TianFang/archive/2013/01/20/2868889.html)

启动GDB后,首先就是要设置断点,程序中断后才能调试.在gdb中,断点通常有三种形式: 断点(BreakPoint): 在代码的指定位置中断,这个是我们用得最多的一种.设置断点的命令是break,它通常有如下方式: break <function>    在进入指定函数时停住 break <linenum>    在指定行号停住. break +/-offset    在当前行号的前面或后面的offset行停住.offiset为自然数. break filename:linenum