本篇介绍Windows调试工具的基本设置和基本操作方法。这里我们会用一个测试程序一步一步说明如何使用WinDbg开始调试工作。首先用VC建立一个名为TestDebug1的控制台项目,并生成它。
一、符号、源码和可执行映像路径设置
使用WinDbg开始调试工作之前,最重要的就是配置好各种环境了。这使得调试器可以正确识别调试目标中的各种变量、函数等等,使得我们能够进行符号化调试或者源码调试,而不是只能在一堆汇编代码中转圈。
首先来看一下未设置环境之前的样子。使用刚才说的TestDebug1项目,为了对比更清晰,用Release进行编译,链接选项中选中生成map文件和调试信息,如下:
在C/C++选项卡中设置如下:
程序代码如下:
#include "stdafx.h"
#include <stdio.h>
int main(int argc, char* argv[])
{
printf( "TestDebug1.cpp");
return 0;
}
编译之后,将Release目录下的TestDebug1.pdb剪切到其他目录下(如果没有这样做,由于编译出来的程序中包含了符号文件路径,调试器可以直接使用exe中的信息找到pdb文件,而不需要设置路径)。在map文件中可以看到像下面这样的内容:
0001:00000000 _main 00401000 f TestDebug1.obj
说明main函数位于401000地址处。
通过WinDbg的File->Open Executeable菜单打开TestDebug1.exe,可以在调试器命令窗口中看到下面的内容:
可以看到,调试器自动中断下来的位置并不是程序入口点,这是由WinDbg实现造成的,这里先不管它。
调试器命令窗口中可以看到,我们还没有设置符号路径,所以WinDbg目前还找不到TestDebug1.exe的任何符号文件。如果想在main函数下断,这时就不能使用符号,而只能直接使用main的地址。
使用命令bp 00401000在main函数设置断点,然后F5执行就可以中断到main的入口处了。断点设置和基本操作我们将在后面介绍。可以在反汇编窗口中看到这样的内容:
由于没有加载任何符号,所以我们看到的都是一堆反汇编代码和地址。在上一篇中已经介绍过,WinDbg不像OllyDbg这些调试器一样拥有强大的反汇编分析能力,所以仅仅靠这些看起来一团乱麻的反汇编代码,调试工作是很难开展下去的。
l 符号路径的设置
要想在WinDbg中看到程序中的符号,必须通过命令或者WinDbg菜单设置符号路径。如果还设置了Microsoft公共符号存储的话,我们不但能够看到自己程序中的符号,还能够看到Windows平台代码中的符号,这对于调试会提供很好的帮助。
所谓符号路径,就是包含了程序符号信息的符号文件所在的目录路径。通常我们接触到的符号文件都是以pdb作为后缀名的。TestDebug1.exe项目如果在项目设置的Link选项中选中了生成调试信息的话(如上图中的Generate debug info),那么可以在Debug或者Release目录中找到它的符号文件TestDebug1.pdb。
我们通过WinDbg的File->Symbol File Path…菜单,或者命令.sympath设置符号路径为TestDebug1.pdb所在的目录。例如刚才我把生成的pdb文件移动到桌面上了,所以在我的机器上就设置为:
完成之后在命令窗口输入.reload命令,我们可以看到反汇编窗口的内容发生改变:
这里就已经可以看到TestDebug1.exe中的函数、变量名这样的符号了。而我们也可以通过bp main这样的命令直接使用符号来操作调试器。
另外,在Local、Watch等窗口中也可以直接使用符号名查看到变量的值、在Call Stack窗口中可以看到函数名,等等。
l 源码路径的设置
通过上面的设置,我们可以对程序进行符号化调试。如果拥有程序的代码,还可以通过设置源码路径来进行源码级调试。
继续上面的工作,我们通过WinDbg的File->Source File Path…菜单或者.srcpath命令设置源代码保存的路径,比如我的机器上是这样:
确定之后,如果当前指令指针在源文件的代码范围内,就会自动跳出源文件窗口。如果没有跳出,那么可以通过File->Open Source File…菜单手动打开源文件。由于刚才设置的断点还没有删除,所以在源码窗口也能口看到设断的行被高亮了:
之后就基本上可以完全通过源码窗口进行设置断点、查看变量、跟踪代码等操作。比只有符号的时候方便了很多。
l 可执行映像路径的设置
可执行映像路径一般在调试dump文件时才用得上。需要将这个路径设置成要调试的exe、dll、sys等可执行文件的路径。可以通过File->Image File Path…菜单或者.exepath命令设置。
l 使用微软公共符号存储
除了使用自己程序的符号之外,调试时还可以使用微软提供的Windows系统代码的符号。这需要修改一下我们设置的符号路径。最方便的办法是使用.symfix命令。
现在我们来看一下kernel32.dll中的代码,在反汇编窗口的Offset栏中填入kernel32!OpenProcess,在我的机器上代码如下:
注意位于764e8ccf处的那个call,现在只能看到调用了kernel32某个偏移处的地址。
使用命令.symfix+ d:/Symbols命令,注意加号要紧靠前面的文本。d:/Symbols是用来保存下载的符号文件的目录,可以修改成自己需要的路径。再来打开符号路径窗口,我们可以看到调试器自动添加了一些内容:
自己在源码路径中加入这些新的内容也可以实现相同的效果。详细的原理请参考WinDbg帮助文档关于符号服务器设置的部分内容。
接下来再次使用.reload命令重新加载符号,第一次使用到的符号文件会从网上自动下载下来,所以可能有时候会等待一会。完成之后,可以看到反汇编窗口中出现了新的符号内容:
764e8cd8处指令中可以看到这是调用了kernel32导入的函数NtOpenProcess。
微软提供的Windows符号是我们研究Windows实现的必备利器。首先,符号化的名字有助于调试过程中的记忆和对各种信息的识别;其次,通过名字就常常可以猜测出来函数或变量的作用,很大的方便调试。在各种调试应用中,都强烈建议添加微软公共符号的引用。
l 设置环境变量
上面介绍的各种路径都可以通过环境变量来进行设置。将一些常用的路径保存在环境变量中,就可以避免每次在新的工作空间中进行调试时都要重新设置的麻烦。另外,Visual Studio 2008也共享一些环境变量的设置,这样在使用IDE调试的时候也能方便的查看到各种符号了。常用的有下面几个:
环境变量 | 作用 |
_NT_SOURCE_PATH = Path | 指定包含调试目标的源代码的路径。Path可以包含后跟一个冒号(:)的驱动器符。用分号分隔多个目录(;)。 |
_NT_SYMBOL_PATH = Path | 指定包含符号文件的目录树的根目录。Path可以包含后跟一个冒号(:)的驱动器符。用分号分隔多个目录(;)。 |
_NT_EXECUTABLE_IMAGE_PATH = Path | 指定包含二进制可执行文件的路径。Path可以包含后跟一个冒号(:)的驱动器符。用分号分隔多个目录(;)。 |
_NT_DEBUG_LOG_FILE_OPEN = Filename | (仅CDB和KD) 指定调试器用来记录输出的日志文件。 |
_NT_DEBUG_LOG_FILE_APPEND = Filename | (仅CDB和KD) 指定调试器用来添加输出的日志文件。新的内容每次会添加到这个文件末尾,而不是覆盖整个文件。 |
二、配置日志文件
进行调试时,有时候调试器命令窗口会变得很杂乱,所以常常想用.cls命令清空它。但是这样会无法再看到之前调试过程中输出的结果。另外,有时候想保存下整个调试过程的详细记录以备后面“回味”。这时,就需要用到日志文件了。可以将调试器命令窗口中出现过的所有内容都自动记录到日志文件中。
创建日志文件:
- (仅CDB 和KD) 启动调试器之前,设置_NT_DEBUG_LOG_FILE_OPEN环境变量。 启动调试器时,使用-logo 命令行选项。 如-logo d:/logs/mylogfile.txt 使用.logopen命令。如.logopen /t d:/logs/mylogfile.txt (仅WinDbg) 使用Edit->Open/Close Log File菜单命令。
- (仅CDB 和KD) 启动调试器之前,设置_NT_DEBUG_LOG_FILE_APPEND环境变量。 启动调试器时,使用-loga命令行选项。如-loga d:/logs/mylogfile.txt 使用.logappend命令。 如. logappend/t d:/logs/mylogfile.txt (仅WinDbg) 使用Edit->Open/Close Log File菜单命令,然后选择Append。
- 使用.logclose命令
(仅WinDbg) 使用Edit->Open/Close Log File菜单命令,然后选择Close Open Log File。
工作空间(Workspace)是用来保存WinDbg中工作环境的工具。例如习惯的窗口布局方式、符号路径、异常处理的设置等等,都可以通过工作空间保存下来,在下次调试的时候就不用再次设置了。
相关的设置都可以通过WinDbg菜单来完成,有下面几个:
l Open Workspace:这里只能打开自己通过SaveAs保存的工作空间。
l Save Workspace:按默认的方式保存当前的工作空间。下次再打开相同的调试目标时,就会自动打开这个Workspace。
l Save Workspace As:可以自己设置工作空间的名字,这样就能通过Open Workspace来手动打开。
l Clear Workspace:可以选择保存工作空间时要保存哪些设置。
l Delete Workspace:删除当前保存的工作空间。这里可以查看到所有默认保存和另存为的工作空间,用来进行清理是很方便的。
l Save Worlspace in File和Open Workspace in File:将工作空间保存到文件或者从文件打开。可以把自己的工作空间保存下来,这样通过U盘之类的就能在多台机器之间方便的使用相同的设置了。
在没有调试目标的时候调整WinDbg的窗口布局等等设置的话,会保存为默认的工作空间。下一次打开新目标的时候,就会使用这个设置。通常我们可以设定一个默认的工作空间,然后为各个单独的任务保存另外的设置。