软件调试基础--05Windbg调试器基础

原文链接在:https://bbs.pediy.com/thread-207316.htm

关于这个作者的这个系列的文章在这里可以看到:https://bbs.pediy.com/user-687689-1.htm

非常感谢原作者。

下载地址:http://www.windbg.org/

Windbg分x86和x64两个版本,请根据需要自行下载,我用win7 x64系统,但是这里下载的始终都是无法正常安装,我安装的是WDK里带的windbg安装包。如果你也遇到此情况,请下载WDK,然后从其Debuggers目录中找到安装包进行安装,WDK下载地址如下:

https://msdn.microsoft.com/en-us/windows/hardware/hh852365

点击Download WDK 7.1.0下载

前面我们已经讲过pdb(符号文件)对调试的重要性。如果我们想在调试过程中看到完整调用堆栈,那么诸如kernel32.dll、ntdll.dll这种系统模块的符号也需要加载。幸运的是,微软已经为我们准备好了一个符号服务器,供我们调试的时候,随时下载这些系统模块对应的pdb文件。

这个符号服务器的地址如下:

srv*d:\mss*http://msdl.microsoft.com/download/symbols

其含义为:从http://msdl.microsoft.com/download/symbols地址处下载符号文件,保存在本地d盘下的mss目录中。设置好后,windbg将自动从这个目录里加载系统模块的符号文件。

打开Windbg,File--Symbol File Path菜单项,填写如下图:

软件调试基础--05Windbg调试器基础

一般情况下,我们调试过程中,如果忘了加载系统模块的符号,设置好这个符号路径后,要选择Reload选项,然后再点击OK

本文不讲述windbg中所支持的全部命令,对于这种东西,windbg有帮助文档可查,如果因为不是很好,还可以百度一下“Windbg命令大全”。

我们只讲述一个比较简单的例子,用windbg进行调试,讲解一下调试过程中,所用到的几个常用的命令。

软件调试基础--05Windbg调试器基础

我们写这样一个例子。编译好后,用windbg调试它。

File--Open Executable菜单项,然后找到我们生成好的exe,此时windbg会默认进入一个断点,这个时候,其实我们的exe还没有执行到main函数,我们在add5处下一个断点,使用如下命令:

bp CodeTest!add5

CodeTest为模块名,根据实际情况更改。然后F5,继续运行,则会进入断点。

我们发现一个比较有意思的事,就是windbg自动找到了我们的cpp文件,并且在windbg的窗口显示出来了,当我们下了断点后,add5函数的左大括号处变红,并且断点命中之后,add5函数的做大括号处颜色发生了改变。想想,其实我们前面讲过了,exe中保存着当前模块对应的pdb所在的全路径,只要exe的pdb还在生成时所在的位置,无论exe在哪,都是可以找到它的pdb的,而pdb中保存的是源文件的全路径,只要这些cpp还在原来生成时所在的目录里,那么就可以根据pdb找到每个源文件在哪里。如此也就解释了为什么windbg调试这个exe的时候,会自动打开cpp文件给我们调试。这个理论,我们要理解清楚,因为我们在开发和调试的过程中,经常会用到这个原理,尤其是在多人团队开发中。

好了,继续我们原来的话题。

此时我们是进行源码级调试,我们也可以像VS中一样,鼠标放在变量上,看每个变量的值。

如果我们想看一下所有局部变量或者参数的值,我们可以在windbg命令窗口中输入dv来查看,如下图:

软件调试基础--05Windbg调试器基础

可以看到每个参数的值,此时nRet还没有初始化,是一个随机的值。

大部分时候,我们是没有源码的,但是我们有pdb文件,此时我们调试是不会显示pdb文件的,而且我们调试出问题的大多数是release版本,局部变量和函数参数都可能被优化成寄存器变量,即便是dv,也不能看出所有局部变量和参数的值了。此时,我们第一节讲到的汇编基础便派上了用场,下面我们假设是没有源码的调试,我想看看现在的调用堆栈情况,在windbg命令行中输入:kb。如下图:

软件调试基础--05Windbg调试器基础

我们可以看到上面两行,为wmain函数调用了add5函数,ChildEBP为栈帧指针,后面我们会讲其存在的意义。RetAddr为当前函数的返回地址,如果你到现在还不知道这是什么东西,那赶紧回家种田吧,哈哈~~ Args to Child为传递给该函数的参数值,可以看到这里显示了3个参数,分别是1,2,3。那我想看看第四个参数怎么办呢?使用dd esp,如下图

软件调试基础--05Windbg调试器基础

0029143d明显是一个地址,这个地址,便是add5的返回地址,和RetAddr列中的是一样的,想想我们调用一个函数的时候的过程,先push好各个参数,然后call 目标函数地址,其实这个call指令同时就把eip的值压栈了,也就是进入一个被调用函数开始位置时,当前栈定就是被调用函数的返回地址。后面跟着的就是参数了,我们可以看到,1,2,3 在前面的调用堆栈里已经显示了,后面的4,5 便是第4个和第5个参数了。

细心一点,我们还看到了此时调用堆栈上,还给我们显示了源代码文件及行号,这就是因为我们加载了pdb,如果没有加载,呵呵~!后果我就不说了,有兴趣的同学试试哈

本节就将这么多吧,其实就是很基本的东西,我们在VS中调试,也不过就使用了这么多功能,你仔细想想,我说这句话属实否?而这么一点点东西,完全不足以我们调试种种软件问题,后续我们将讲解更多的调试方法。也就是:调试不仅仅是单步执行+观察变量值!