首页
社区
课程
招聘
[原创]软件调试基础--06断点调试
发表于: 2016-1-27 15:05 5234

[原创]软件调试基础--06断点调试

2016-1-27 15:05
5234

本节讲述Windbg中常用的断点调试方法,并用一个最简单的例子,来演示断点的使用方法。

测试程序代码比较简单,如下:

void change(char *psz)

{

strcpy(psz, "world");

return;

}

int _tmain(int argc, _TCHAR* argv[])

{

char szTest[] = "hello";

change(szTest);

printf("%s\n", szTest);

system("pause");

return 0;

}

编译后生成CodeTest.exe和对应的符号文件CodeTest.pdb

打开Windbg,启动调试CodeTest.exe,设置好符号文件路径(CodeTest.pdb所在路径和微软符号服务器路径),如下图:



Windbg启动调试够中断在ntdll!LdrpDoDebugBreak函数处,此时main函数还没有执行,我们可以在此时下一个函数断点。(之前我们说过,exe中保存pdb的绝对路径,pdb中保存的也是cpp的绝对路径,所以此时windbg应该同时也打开了源码窗口)

命令:bp CodeTest!change 在CodeTest模块的change函数上下一个断点,当调用了这个函数时,中断下来,以便于调试。

此时,我们用bl命令,可以列出当前的所有断点,如下图:



只有一个断点,就是刚才我们下过的,然后F5(或使用g命令),继续执行。中断到change函数,如下图:

  

断点命中,接下来可以进行单步调试,看这个函数内部执行的情况,期间也可以用dv命令来观察这个函数内的局部变量的值。

以上这些操作,在VS中要比在Windbg中容易完成的多,有些读者可能会问,就这么简单,我们为什么要使用Windbg呢?老实说,到这里,我们所讲述的东西,都是调试最基本的知识,调试方法,也都是最最简单的,并未脱离下断点调试这个节奏。但从现在开始,以后我们接触的调试手段,大多在VS中是很难操作的了。

有时候,我们有这样一种需求,我们调试程序的时候,中断到当前函数时,某个变量的值,我们认为它应该是a,但我们看到的却是b,但这个变量初始化的时候,明明是a,为何却变成b了呢? 下面,我们来说说数据断点。

还是上面的程序,我们szTest中初始化时是“hello”,我们对这个变量所在地址下一个断点,当有代码修改szText时,我们中断下来,进行调试。先清除一下刚刚的断点,命令为:bc 0

  

此时,已经没有断点了,我们重新调试,并输入如下命令:

  

先在main函数中下断点,然后F5执行到main函数入口,然后输入命令:

ba w 1 szTest 此断点为数据写入断点,当代码执行写入szTest地址1字节的时候,就会触发该断点。程序中断下来,我们就找到了,修改了szTest内容的代码在何处,接下来,我们F5,如图:

  

此处对栈内存进行初始化,初始化的内容为CC,此汇编指令含义参见《软件调试基础01--汇编基础》。然后继续F5,如下图:

  

此时,将szTest所在地址的内容,初始化为字符串“hello”,下图为查看常量区内容:

  

至此,szTest初始化已经完成,接下来,我们再执行,change函数里也修改了这个地址的内容,所以会中断下来,F5走起。如图:

  

此处由于change中调用了strcpy,而strcpy中调用了strcat函数,strcat函数内部修改了szTest的第一个字节内容,所以中断下来。此时打印调用堆栈如下:

  

可以在调用堆栈中发现是change函数中改变了szTest的内容,但是为什么中间没有strcpy函数调用这一栈帧呢?我们调试的debug版本,关闭了所有优化,调用堆栈中为什么没有strcpy?此问题,我不做回答,其实用前面的调用堆栈形成一节的知识,是可以解决这个问题的。快快思考吧。。。^_^

本文讲述了两个比较简单的断点使用方法,还有一个比较复杂的断点叫条件断点,以后我们再讲,调试基础系列教程里不做讲解,有兴趣的童鞋可以自行百度。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 284
活跃值: (250)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
2
挺不错的,嘿嘿
2016-1-29 22:27
0
游客
登录 | 注册 方可回帖
返回
//