Windbg调试命令
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Windbg调试命令详解
1. 概述
用户成功安装微软Windows调试工具集后,能够在安装目录下发现四个调试器程序,分别是:cdb.exe、ntsd.exe、kd.exe和 Windbg.exe。
其中cdb.exe和ntsd.exe 只能调试用户程序,Kd.exe主要用于内核调试,有时候也用于用户态调试,上
述三者的一个共同特点是,都只有控制台界面,以命令行形式工作。
Windbg.exe在用户态、内核态下都能够发挥调试功能,尤其重要的是,它不再
是命令行格式而是采用了可视化的用户界面。
所以绝大部分情况下,我们在谈及Windows调试工具的时候,都直接指向Windbg,而不大谈及前三者。
Windbg在用户态和内核态下,都支持两种调试模式,即“实时调试模式(Living)”和“事后调试模式(Postmortem)”。
所谓实时模式,是被调试的目标对象(Target)当前正在运行当中,调试器可以实时分析、修改被调试目标的状态,如寄存器、内存、变量,调试exe可执行程序或双机实时调试都属于这种模式;所谓事后模式,是被调试的目标对象(Target)已经结束了,现在只是事后对它保留的快照进行分析,这个快照称为转储文件(Dump文件)。
Windbg另一个重大优点,还在于它支持源码级的调试,就像VC自带的调试器一样。
虽然提供了用户界面,但Windbg归根结底还是需要用户一个个地输入命令来指挥其行动。
这就是他的Command窗口。
每个调试命令都各有使用范围,有些命令只能用于内核调试,有些命令只能用于用户调试,有些命令只能用于活动调试。
但用户也不必记得这许多,一旦在某个环境下,使用了不被支持的命令,都会显示“No export XXX found”的字样。
就拿!process命令来说吧,它显示进程信息,但只能用于内核调试中,如果在
用户调试中使用,就是下面的情景:
0:001> !process
No export process found
1.1 寻求帮助
我们首先来看如何在使用过程中获取有用的帮助。
Windbg中的调试命令,分为
三种:基本命令,元命令和扩展命令。
基本命令和元命令是调试器自带的,元命令总是以“.”开头,而扩展命令是外部加入的,总是以感叹号“!”开头。
各种调试命令成千上万,我们首先要想办法把它们都列举出来,并取得使用方法。
基本命令最少了,大概40个左右。
列举所有的基本命令,使用如下命令:
∙?
元命令有一百多个,使用下面命令列举所有元命令:
∙.help [/D]
如使用“/D”参数,命令列表将以DML格式显示。
DML是一种类似于HTML的标识语言,下面会讲到。
下图以DML格式显示所以有字母a开头的元命令:
最后讲扩展命令。
所谓扩展命令,顾名思义是可以“扩展”的。
扩展命令从动态连接库中暴露出来,一般以DLL文件名来代表一类扩展命令集,首先我们要搜索出系统中有多少个这样的DLL文件,使用下面命令:
∙.chain [/D]
此命令能够给出一个扩展命令集的链表。
和.help命令一样,也可以使用/D参数以DML格式显示。
如下所示:
0:001> .chain
Extension DLL search Path:
C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\WINXP; Extension DLL chain:
dbghelp: image 6.2.9200.20512, API 6.2.6, built Fri Sep 07 13:45:49 2012 [path: C:\Program Files (x86)\Windows
Kits\8.0\Debuggers\x64\dbghelp.dll]
ext: image 6.2.9200.16384, API 1.0.0, built Thu Jul 26 10:11:33 2012 [path: C:\Program Files (x86)\Windows
Kits\8.0\Debuggers\x64\winext\ext.dll]
exts: image 6.2.9200.16384, API 1.0.0, built Thu Jul 26 10:15:20 2012 [path: C:\Program Files (x86)\Windows
Kits\8.0\Debuggers\x64\WINXP\exts.dll]
uext: image 6.2.9200.16384, API 1.0.0, built Thu Jul 26 10:15:09 2012 [path: C:\Program Files (x86)\Windows
Kits\8.0\Debuggers\x64\winext\uext.dll]
ntsdexts: image 6.2.9200.16384, API 1.0.0, built Thu Jul 26 10:16:01 2012 [path: C:\Program Files (x86)\Windows
Kits\8.0\Debuggers\x64\WINXP\ntsdexts.dll]
最上面两行显示了扩展模块的搜索路径。
接下来共列出了六个Windbg自带的扩展模块:wdfkd、 dbghellp、ext、exts、uext和ntsdexts。
可以查看到这些扩展模块的版本信息、镜像文件路径。
如何列出某个扩展库中所包含的扩展命令列表呢?绝大部分扩展模块可使用如下命令:
∙!模块名.help
此外,扩展命令模块是可“扩展”的。
如果读者从第三方处获取,或自己编写了一个扩展调试模块,则可通过.load/.unload命令动态加载/卸载。
1.2 DML语言
DML(Debugger Markup Language调试器标记语言)像HTML一样,可从一处链
接到另一处。
不同处在于,DML的链接内容需要用户点击后才会动态生成。
一般用来以精简方式显示大量信息和扩展功能。
DML有很多实用的功能,如果使用者一时不知道从何下手,最好就是输
入.dml_start命令,开始DML之旅。
DML链接以更加可视化的方式,引导用户查看调试信息,使得调试工具的使用相比纯指令格式而言,更为友好。
DML如同是对原指令的一层轻微的包装一样,让生硬的指令更加温和了。
所以建议读者总是把DML默认开启。
∙.prefer_dml 1
开始DML。
∙.prefer_dml 0
关闭DML。
一旦开启DML后,像k等支持DML的调试命令,将默认以DML格式显示输出内容。
DML还能以一种很特殊的方式为函数画流程图。
它主要的原理是使用反汇编,类似于uf,但在逻辑分支处,它会停止反汇编并显示分支让用户选择。
另外,它能显示汇编代码对应的行号,这一点真的非常好。
如果稍加精进,他就能画出非常漂亮的流程图了。
他的一个特点是反汇编的顺序是从后往前推。
只要细想一想,
就会觉得很有道理。
如果正推的话,分支太多;而反推则分支顺序在用户的参与下(即用户进行分支选择),是固定了的。
∙.dml_flow FindAllInfFilesA FindAllInfFilesA+30
这是一个非常简单、实用的例子,对Kernel32库中的FindAllInfFilesA接口函数进行反汇编,效果类似uf命令却更强大。
1.3 基本信息
本节讲解和调试器软件本身相关的命令,比如:查看软件版本、启动参数,以及最基本的软件设置命令。
首先看版本命令:
∙version
此命令显示操作系统的版本信息以及Windbg本身的版本信息,Windbg的配置和操作系统密切相关,所以将操作系统的版本信息一并显示出来是很有必要的。
在内核环境与用户环境下运行此命令,会得到不同的输出。
下图为内核环境下输出结果:
0:001> version
Windows 7 Version 7601 (Service Pack 1) MP (8 procs) Free x64 Product: WinNt, suite: SingleUserTS
kernel32.dll version: 6.1.7601.18015 (win7sp1_gdr.121129-1432) Machine Name:
Debug session time: Thu Aug 22 10:11:04.000 2013 (UTC + 8:00)
System Uptime: 14 days 17:26:44.613
Process Uptime: 14 days 17:14:25.000
Kernel time: 0 days 0:09:02.000
User time: 0 days 0:42:36.000
Full memory user mini dump: C:\Users\mozhang\AppData\Local\Temp\dwm.DMP
Microsoft (R) Windows Debugger Version 6.2.9200.16384 AMD64
Copyright (c) Microsoft Corporation. All rights reserved.
除了Windbg版本信息,上面的输出中还包括目标系统信息。
如果纯粹是为了查看目标系统的版本信息,可使用下面的vertarget命令:
∙vertarget
Windbg支持对多个调试系统中的多个调试目标同时进行调试。
上面我们通过version或vertarget命令列出了当前调试系统的版本信息,还可以查看当前目标系统的状态:
∙||
如Windbg中同时打开多个调试对象,“||”命令将列出对象列表。
笔者为了演示此种情况,先在Windbg中开启Local Debug环境,然后两次调用.opendump 命令打开两个DUMP文件,这样就同时拥有了三个被调试的目标对象。
下图显示了这个情况:
上图中的活动对象是0号对象(可从数字0前面的小数点看出)。
调试器需要在多个调试目标之间进行切换的话,使用“s”参数。
如要切换到1号目标可使用下面的命令:
∙|| 1 s
最后一个命令用来查看系统时间。
这包括系统当前时间,以及系统正常运行持续时间;用户模式下还会显示当前进程的持续时间。
命令格式如下:
∙.time
0:001> .time
Debug session time: Thu Aug 22 10:11:04.000 2013 (UTC + 8:00) System Uptime: 14 days 17:26:44.613 // 系统运行时间
Process Uptime: 14 days 17:14:25.000// 当前进程运行时间
Kernel time: 0 days 0:09:02.000
User time: 0 days 0:42:36.000
1.4 基本设置
首先看一个清屏命令:
∙.cls
当命令窗口中的内容太乱的时候,这个命令帮你快刀斩乱麻。
下面看一个设置默认数字进制的命令:
∙n [8|10|16]
软件默认是16进制,但有时候我们也需要把默认进制改成八进制或十进制的。
下面尝试在八进制下面求数字11的值,如下:
0:001> n 8
base is 8
0:001> ? 11
Evaluate expression: 9 = 00000000`00000009
最后再来说一个处理器模式指令。
关于处理器模式很值得一说,很重要。
处理器模式的设置,反映了Windbg软件的强大。
举例来说,主机为32位的系统,却可以同时调试X86、IA64、X64的目标系统——前提是先将主机的处理器模式设置正确了。
可用处理器模式值有:x86、adm64、ia64、 ebc。
∙.effmach x86
命令.effmach表示Effective Machine Type,即有效的机器类型。
此命令将当前的处理器模式设置为x86模式。
1.5 格式化显示
将一个整数以各种格式显示,包括:16进制、10进制、8进制、二进制、字符串、日期、浮点数等。
是不是很方便?这个命令是:
∙.formats 整数
下面以0x123abc为例:
0:001> .formats 0x123abc
Evaluate expression:
Hex: 00000000`00123abc
Decimal: 1194684
Octal: 0000000000000004435274
Binary: 00000000 00000000 00000000 00000000 00000000 00010010 00111010 10111100
Chars: ......:.
Time: Thu Jan 15 03:51:24 1970
Float: low 1.67411e-039 high 0
Double: 5.90252e-318
1.6 开始调试
现在领大家进入调试阶段。
首先看看如何让调试器附载到一个已运行的进程中去?比如IE软件在运行过程中发生了崩溃,打开Windbg后如何调试呢?第一步就是把Windbg附载到发生崩溃的IE进程上。
使用如下命令格式:
∙.attach PID
或者通过Windbg的启动参数进行挂载:
∙Windbg –p PID
上面两个命令中,PID指定了进程ID。
如果觉得指定PID不方便,也可以通过进程名进行挂载:
∙Windbg -pn 进程名
比如挂载到记事本就可以这样:
windbg –pn notepad.exe
上面的命令是把调试器挂载到已经存在的进程上,另外调试器可以创建新进程并对它进行调试,这二者使用了不同的侵入方法。
使用下面的命令:
∙.create 程序启动命令行
或者Windbg启动参数
∙Windbg 程序启动命令行
比如创建并调试一个记事本子进程,可用.create notepad或者windbg notepad 命令。
也可以打开Windbg后,在File菜单中选择“Open Executable…”启动Notepad子进程,但这个选项只能被执行一次(之后会灰掉)。
使用上述命令可将调试器连续附载到多个进程,也就是说,能够同时调试多个进程,这一点看上去很神奇哦。
下例中,调试器先创建子程序IOCTL.exe,然后又调用.attach命令附加到记事本进程,使用命令“|”列出所有被调试进程。
读者可能会奇怪,多个进程同时调试怎么兼顾呢?只要有一个切换指令就可以了,这样就能够切换到任意的进程(令其为当前进程)并对之进行调试。
比如上图显示1号进程为当前进程(注意1前面的小点),如何将当前进程切换到0号进程呢?可以使用进程列表命令“|”轻松切换,比如:
∙| 0 s
此命令把当前调试环境切换到0号IOCTL.exe进程。
另外需注意的是,多个用户进程调试目标都处于同一个调试会话中,使用“||”命令会看到,它们属于同一个“Live user mode”调试会话。
下面看dump文件调试,使用命令:
∙.opendump 文件名
此命令打开一个dump文件,并建立一个DUMP调试会话。
如何手动创建一个dump 文件呢?比如在调试过程中,遇到了无法解决的问题,希望获得异地帮助,则把当前调试环境保存到Dump文件中发送给能提供帮助的人,不失为一种好办法。
∙.dump 文件名
Dump文件一般以.dmp为后缀,系统生成的Dump文件都默认以.dmp为后缀的,但使用.dump命令时,使用者可以设置任意后缀,甚至无后缀。
下例中,首先为当前进程生成一个dump文件保存到a.txt中(即后缀名为.txt),然后将之打开并分析:
0:001> .dump a.txt
Creating a.txt - mini user dump
Dump successfully written
0:001> .opendump a.txt
Loading Dump File [C:\Program Files (x86)\Windows
Kits\8.0\Debuggers\a.txt]
User Mini Dump File: Only registers, stack and portions of memory are available
Opened 'a.txt' // 打开成功
上文讲到进程挂载命令,当需要解除挂载时,可使用解挂命令,如下:∙.detach
此命令结束当前调试会话, Windbg解除和被调试进程之间的调试关系(不管是通过挂载,还是通过创建方式建立的调试关系),解挂后,被调试进程能够独立运行;如果当前的调试会话是一个Dump文件,此命令直接结束对dump文件的调试,即结束调试会话。
如果需要彻底结束调试,下面的命令更有用:
∙q | qq | qd
q是Quit的缩写。
结束当前调试会话,并返回到最简单的工作空间,甚至把命令行界面也关闭掉。
q和qq两个命令将结束(close)被调试的进程,qd不会关闭调试进程,而是进行解挂操作。
双机调试的时候,如果你感觉调试已经陷入僵局,比如目标机Hang住了动都动不了,此时通过主机让目标机强制宕机或重启,不失为一个好主意。
∙.crash
∙.reboot
crash命令能引发一个系统蓝屏,并生成dump文件;而.reboot使系统重启,不产生dump文件。
2. 符号与源码
符号与源码是调试过程中的重要因素,它们使得枯燥生硬的调试内容更容易地调试人员读懂。
在可能的情况下,应该尽量地为模块加载符号和源码。
大部分情况下源码难以得到,但符号却总能以符号文件的形式易于得到。
什么是符号文件呢?编译器和链接器在创建二进制镜像文件(诸如exe、dll、sys)时,伴生的后缀名为.dbg、.sym或.pdb的包含镜像文件编译、链接过程中生成的符号信息的文件称为符号文件。
具体来说,符号信息包括如下内容:
∙全局变量(类型、名称、地址);
∙局部变量(类型、名称、地址);
∙函数(名称、原型、地址);
∙变量、结构体类型定义;
源文件路径以及每个符号对应于源文件中的行号,这是进行源码级别调试的基础。
有这么多的信息包含在符号文件中,使得符号文件通常要比二进制文件(PE格
式文件)本身要大很多。
调试过程中,符号之重要性不言而喻。
只有正确设置了符号路径,使得调试器能够将调试目标、符号文件以及源码文件一一对应起来,才能够最好地发挥调试器的强大功用。
符号信息隶属于指定的模块,所以只有调试器需要用到某个模块时,他的符号信息才有被加载和分析的必要。
所以我们在讲符号内容之前,先讲和模块相关的命令。
2.1 模块列表
每个可执行程序都是由若干个模块构成,有些模块静态加载,有些模块以动态方式进行加载。
所以对于有些模块,可能在A时刻运行时被加载,而在B时刻运行时,自始至终都未被加载。
调试过程中,调试器根据模块的加载情况加载符号。
有几个命令可以用来列举模块列表,分别是:lm、!dlls、.reload /l、!imgreloc。
下面分别来看。
∙lm [选项] [a Address] [m Pattern | M Pattern]
lm是list loaded modules的缩写,他还有一个DML版本:
∙lmD [选项] [a Address] [m Pattern | M Pattern]
使用/v选项能列出模块的详细信息,包括:模块名、模块地址、模块大小、镜像名、时间戳、以及对应的符号文件信息(包括类型、路径、类型、编译器、符号加载状态)。
如使用参数a,后面跟地址(address),则只有指定地址所在的模块能够被列出;
如使用参数m,后面跟一个表示模块名的字符串通配符,如lm m *o*将显示所有名称中包含字母o的模块,下图所示:
||0:0:001> lm m *o*
start end module name
f3380000 f3512000 dwmcore (private pdb symbols)
f92d0000 f9327000 d3d10_1core (deferred)
fa890000 fa9f1000 WindowsCodecs (deferred)
faa50000 fac44000 comctl32 (deferred)
fbf70000 fbf7c000 version (deferred)
fce20000 fce2f000 profapi (deferred)
fd970000 fdb73000 ole32 (deferred)
fee60000 fee7f000 sechost (deferred)
下面介绍另一个命令:
∙!dlls [选项] [LoaderEntryAddress]
首先看他的可选参数:
-i/-l/-m:排序方式,分别按照初始化顺序、加载顺序、内存起始地址顺序排列。
-a:列出镜像文件PE结构的文件头、Section头等详细信息,是分析PE结构的好帮手(更好的帮手是利用自如PEView或Stud_PE等UI工具)。
-c:指定函数所在的模块。
这个选项非常实用,比如我想知道NtCreateFile函数是哪个模块暴露出来的接口,如下:
0:000> !dlls -c ntcreatefile
Dump dll containing 0x7c92d0ae:
0x00251f48: C:\WINDOWS\system32\ntdll.dll
Base 0x7c920000 EntryPoint 0x7c932c48 Size 0x00096000
Flags 0x00085004 LoadCount 0x0000ffff TlsIndex 0x00000000
LDRP_IMAGE_DLL
LDRP_LOAD_IN_PROGRESS
LDRP_ENTRY_PROCESSED
LDRP_PROCESS_ATTACH_CALLED
除了lm和!dlls外,下文将讲到的.reload命令在加入 /l选项后,也能列举模块,其命令格式如下:
∙.reload /l
最后再来看一个!imgreloc命令,它也能够列出模块列表并显示各模块地址。
但其主要作用尚不在此,它用来判断各个模块是否处于preferred地址范围。
所谓Preferred地址是这么一回事:二进制文件在编译的时候,编译器都会为其设置一个理想地址(Preferred Address),这样二进制文件被加载时,系统会尽可能将他映射到这个理想地址。
当然,所谓“理想”往往是会受到“现实”的挑战的,当存在地址竞争的时候,需要适当调整二进制文件的加载地址,选择另一个合适的地方加载之。
!imgreloc命令就是用来查看这种情况的,命令如下:
∙!imgreloc [模块地址]
命令!imgReloc是Image Relocate的缩写,字面已能够反映其含义:镜像文件重定位信息。
下面是一个例子。
上例中,大部分系统模块(上图下部方框所示)其地址由于事先经过统筹分配,所以一般都能被加载到preferred地址处。
只有少数模块(如最上面的Normaliz模块)由于地址冲突而受到了调整。
2.2 模块信息
上一节我们了解了如何枚举模块列表,这一节我们研究针对单个模块,如何获取详细信息。
有多个命令可以查看指定模块的详细模块信息,这包括:lm、!dh、lmi等,下面来一一介绍。
首先看lm,这个命令上面我们已经介绍过,现在利用它来获取指定模块信息。
其命令格式如下:
∙lm v a模块地址
这里使用了v选项,以显示详细(verbose)信息;并使用a参数以指定模块地址。
通过此命令显示的信息,和我们在explorer资源管理器中通过鼠标右键查看一个文件的属性所看到的信息差不多。
请看下面的清单:
0:000> lm v a 00400000
start end module name
00400000 00752000 UsbKitApp C (private pdb symbols)
C:\Trunk\CY001\UsbKitApp\Debug\UsbKitApp.pdb
Loaded symbol image file: UsbKitApp.exe
Image path: UsbKitApp.exe
Image name: UsbKitApp.exe
Timestamp: Tue Mar 16 22:07:02 2010 (4B9F9086)
CheckSum: 00000000
ImageSize: 00352000
File version: 1.0.0.1
Product version: 1.0.0.1
File flags: 1 (Mask 3F) Debug
File OS: 4 Unknown Win32
File type: 1.0 App
File date: 00000000.00000000
Translations: 0804.03a8
CompanyName: TODO: <公司名>
ProductName: TODO: <产品名>
InternalName: UsbKitApp.exe
OriginalFilename: UsbKitApp.exe
ProductVersion: 1.0.0.1
FileVersion: 1.0.0.1
FileDescription: TODO: <文件说明>
LegalCopyright: TODO: (C) <公司名>。
保留所有权利。
下面看!lmi命令,此命令通过指定模块地址查找模块并获取其信息,其
命令格式如下
∙!lm i 模块地址
此命令侧重获取对调试器有用的信息,请看下面的列表:
0:000> !lmi 0x400000
Loaded Module Info: [0x400000]
Module: UsbKitApp
Base Address: 00400000
Image Name: UsbKitApp.exe
Machine Type: 332 (I386)
Time Stamp: 4b9f9086 Tue Mar 16 22:07:02 2010
Size: 352000
CheckSum: 0
Characteristics: 103
Debug Data Dirs: Type Size VA Pointer
CODEVIEW - GUID: {5DB12DF1-71CA-43F7-AD85-0977FB3629A4}
Age: 3, Pdb:
C:\Trunk\CY001\UsbKitApp\Debug\UsbKitApp.pdb
Image Type: FILE - Image read successfully from debugger.
UsbKitApp.exe
Symbol Type: PDB - Symbols loaded successfully from image header.
C:\Trunk\CY001\UsbKitApp\Debug\UsbKitApp.pdb
Compiler: Resource - front end [0.0 bld 0] - back end [9.0 bld 21022]
Load Report: private symbols & lines, not source indexed
C:\Trunk\CY001\UsbKitApp\Debug\UsbKitApp.pdb 如果还要查看更详细、丰富的模块信息,可以使用!dh命令,命令格式如下: !dh [标志] 模块地址
d h是display header的缩写,直译就是“显示文件头”的意思,它能够显示非常详细的PE头信息。
下图截取了输出信息中的开头部分,其它详细内容,需要读者熟悉微软的PE结构才能看懂:
模块相关的知识点讲完了,下面讲符号有关命令。
和符号相关的知识点包括:符号路径、符号服务器、符号缓存、符号加载以及符号的使用等。
2.3 符号路径
什么是符号路径呢?就是调试器寻找符号文件的方向,它可以是本地文件夹路径、可访问的UNC路径、或者是符号服务器路径。
什么是符号服务器呢?如果调试过程中,需要涉及到成千上万个符号文件,以及同一个符号文件存在不同平台下的不同符号文件版本的时候,那么一一手动设置符号路径肯定是不现实的,于是引入符号服务器的概念。
符号服务器有一套命名规则,使得调试软件能够正确找到需要的符号文件。
一般来说,符号服务器比较大,都是共用的,放在远程主机上。
为了降低网络访问的成本,又引入了符号缓存的概念,即将从服务器上下载到的符号文件,保存在本地缓存中,以后调试器需要符号文件的时候,先从缓存中寻找,找不到的时候再到服务器上下载。
下面分几部分一一来看。
设置符号路径:
设置符号路径的语法如下:
∙.sympath [+] [路径]
如果不加入任何参数执行.sympath命令,将显示当前的路径设置:∙.sympath
如要覆盖原来的路径设置,使用新路径即可:
∙.sympath <新路径>
要在原有路径的基础上添加一个新路径,可使用:
∙.sympath+ <新增路径>
要注意的是,使用.sympath改变或新增符号路径后,符号文件并不会自动更新,应再执行.reload命令以更新之。
这里要谈一谈延迟加载的知识点。
延迟加载使得模块的符号表,只在第一次真正使用的时候才被加载。
这加快了程序启动,不用在一开始耗费大量时间加载全部的符号文件。
使用.symopt +4和.symopt -4来开启或关闭延迟加载设置。
在已经启动了延迟加载的情况下,如想临时改变策略,立刻将指定模块的符号加载到调试器中,可以使用ld或者.reload /f命令。
符号服务器与符号缓存:
设置符号服务器的基本语法是:
∙SRV*[符号缓存]*服务器地址
语法由SRV引导,符号缓存和服务器地址的前面各有一个星号引导。
符号缓存一般也叫做下游符号库。
如某公司有一台专门的符号服务器,地址为
\\symsrv\\symbols,则他们公司的所有开发人员都应该在他们的调试器中使用类似下面的命令:
∙.sympath+ srv*c:\symbols*\\symsrv\symbols
此外,我们总是应该把微软的公用符号库加入到我们的符号路径中:
∙.sympath+ srv*<缓存地
址>*/download/symbols
这是一台微软对外公开的服务器,使用http地址访问,不是所有人都能牢记这个网址,所以最好的办法就是使用.symfix命令,语法如下:
∙.symfix [+] [符号缓存地址]
这个命令等价于上面的.sympath命令,而不用输入长长的http地址。
0:000> .symfix c:\windows\symbols
0:000> .sympath
Symbol search path is:
SRV*c:\windows\symbols*/download/symbols
符号选项:
命令格式如下:
∙显示当前设置:.symopt
∙增加选项:.symopt+ Flags
∙删除选项:.symopt- Flags
第一个命令没有任何参数,显示当前设置。
后面两个,第二个命令含有“+”代表添加一个选项,第三个命令含有“-”代表去除一个选项。
001> .symopt
Symbol options are 0x30337:
0x00000001 - SYMOPT_CASE_INSENSITIVE
0x00000002 - SYMOPT_UNDNAME
0x00000004 - SYMOPT_DEFERRED_LOADS
0x00000010 - SYMOPT_LOAD_LINES
0x00000020 - SYMOPT_OMAP_FIND_NEAREST
0x00000100 - SYMOPT_NO_UNQUALIFIED_LOADS
0x00000200 - SYMOPT_FAIL_CRITICAL_ERRORS 0x00010000 - SYMOPT_AUTO_PUBLICS
0x00020000 - SYMOPT_NO_IMAGE_SEARCH
可用的符号选项请见下表:
表8-1 符号选项
2.4 符号加载
本节分下面几个子题目分别讲解。
立刻加载:
其命令格式如下:
∙ld 模块名[/f 符号文件名]
加载指定模块的符号。
调试器默认采用延迟模式加载符号,也就是直到符号被使用的时候,才将符号文件加载到调试器中并进行解析。
ld使得延迟模式被打破,让指定模块的符号文件立刻加载到调试器中。
此指令可为模块的符号文件设置自定义的匹配名称,比如:
∙ld 123 /f abc
这样一来,abc.pdb将成为123.exe的符号文件。
正常情况下,这是不可能的,只能是abc.pdb对应abc.exe。
重新加载:
如果对自己正在使用的符号文件感到疑惑,比如源代码和行号明显不匹配,最好的做法就是重新加载一下符号文件。
此命令语法如下:
∙.reload /f /v [模块名]
.reload命令的作用是删除指定或所有已加载的符号文件,默认情况下,调试器不会立刻根据符号路径重新搜索并加载新的符号文件,而是推迟到调试器下一次使用到此文件时。
使用/f参数(force),将迫使调试器立刻搜索并重新加载新的符号文件。
其它参数解释如下:
∙/v:将搜索过程中的详细信息都显示出来。
∙/i:不检查pdb文件的版本信息;
∙/l:只显示模块信息,内核模式下,和“lm n t”命令类似,但显示内容比后者更多,因为包含了用户模块信息;
∙/n:仅重载内核符号,不重载用户符号;
∙/o:强制覆盖符号库中的符号文件,即使版本相同;
∙/d:用户层模式下使用Windbg时的默认选项,重载调试器模块列表中的所有模块;
∙/s:内核模式下使用Windbg时的默认选项,重载系统模块列表中的所有模块,另外,如果调试器在用户模式下运行,要加载内核模块,也必须使用/s选项,否则调试器将只会在调试器模块列表中搜索而导致找不到内核模块;
∙/u:卸载指定模块。
如发现当前符号版本不对,使用/u开关先卸载之再重新加载。
符号验证:
上面讲到.reload的时候,我们说过,符号文件会出现不匹配的情况。
这是很有可能的,程序员在后期测试的时候可能会将工程多次编译,为了维护多个版本而使得自己也被搞混。
可以使用下面的命令验证一个模块的符号文件:
∙!chksym <模块名> [符号名]
加载选项:!sym
有两类符号加载选项。
第一类是Noisy/Quiet,Noisy选项将打印符号加载的详细信息,Quiet选项则忽略这些信息。
第二类是Prompts/Prompts off,即是否允许执行提示(Prompts)对话框。
一般都是在调用.reload 命令之前,执行加载选项命令,以见立竿见影之效。
所谓Noisy是吵闹的意思,调试器在搜索、加载符号的时候,会显示更多与搜索有关的信息。
而安静模式下,则不会显示这些信息。
不管吵闹与否,都不会影响到最终的搜索、加载结果。
当从网络上下载符号文件的时候,可能会碰到网络服务器要求客户进行安全认证的情况,如果开启Prompts选项,则弹出认证对话框,让用户输入认证信息;否则,不弹出对话框,并且不会下载符号文件。
不加任何参数的情况下,显示当前加载选项设置,下面的清单表明当前的设置为Quite及Prompts模式:
lkd> !sym
!sym <noisy/quiet - prompts/prompts off> - quiet mode - symbol prompts on
2.5 符号搜索
符号搜索包括全局搜索和就近搜索两种。
全局搜索:
命令“x”被用来进行符号的全局搜索,你可以把它直接就理解为search。
格式如下:
∙x [参数] [模块!符号]
如果什么参数都没有的话,它将列出当前调试环境下的所有局部变量,前提是要在有局部变量存在的情况下,显示局部变量的另一个命令是dv,后文也会讲到。
∙x kernel32!a*,
上面命令搜索并打印出kernel32模块中所有a开头的符号。
x命令支持DML,使用/D选项即以DML格式显示。
如果你不知道ntcreatefile这个函数是在哪个模块中定义的,可以试着使用下
面的命令:
∙x *!*NtCreateFile* (注:亦请参照!dlls –c命令)。