总目录
1. WinDbg概述
2. WinDbg主要功能
3. WinDbg程序调试示例
4. CPU寄存器及指令系统
5. CPU保护模式概述
6. 汇编语言不等于CPU指令
7. 用WinDbg观察托管程序架构
8. Windows PE/COFF文件格式简述
9. 让WinDbg自动打开DotNet Runtime源程序
10. WinDbg综合实战
前言
本篇主要介绍WinDbg的下载、安装、主界面介绍、加载被调试程序、WinDbg命令类型、WinDbg命令列表、WinDbg帮助系统的使用。
下载与安装
WinDbg安装包并非普通exe或rar文件,而是扩展名为.appinstaller的微软应用商店安装包文件,文件名为windbg.appinstaller,下载请点击这里,如果链接失效,可以到这里查找。
备注:.appinstaller应用商店文件本质是xml文件,可以用任何文本阅读器打开。在Windows 10及以上环境双击该文件,Windows会自动连网,并到https://windbg.download.prss.microsoft.com/dbazure/prod/1-2402-24001-0/windbg.msixbundle下载真实的安装包文件并自动安装。
由于我的电脑已经安装好了,所以双击后会显示如下页面。图中左上角是原始安装文件,右上角是WinDbg应用程序Logo图像。
软件安装好以后,在开始菜单就可以找到WinDbg图标。在我的电脑上,该图标实际指向了这个文件:
C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\DbgX.Shell.exe。
WinDbg界面
打开的WinDbg,初始界面如下图所示。其中虚线框称为命令(Command)窗口,是我们调试程序时最常用到的窗口。该窗口底部红色实线框是命令输入栏,由于图示状态下尚未加载任何被调试程序,此时还无法输入任何调试命令,而是显示:
Debuggee not connected (当前未与被调试应用建立连接)。
上图中点击红色虚线圆所示的关闭符号,会关闭命令窗口,可以使用以下三种方式将该窗口再找回:
- View -> Layouts -> Default(见下图右侧):此按钮将恢复系统默认窗口布局
- View -> Command(见下图左侧):此按钮会显示Command窗口,可能需要重新定位
- Alt + 1 快捷键
WinDbg还有其他窗口可用,如下图红色虚线框所示,读者可尝试使用。
加载被调试程序
WinDbg是用来调试程序或进程的,只有和被调试程序或进程进行了连接,才能输入调试命令。
在上图中点击左上角的文件按钮,就会打开如下图所示界面:
界面很简洁,可以打开exe文件,也可以让WinDbg附加到已经运行的进程上,或者使用调试器打开转储文件(俗称Dump文件),甚至可以调试内核等。本系列文章主要使用打开exe文件方式,也就是上图中的第二项(Launch executable)。本文中,我将使用一个自编的C#小程序(Core.exe)作为示例,但你可以调试任何其他应用Windows程序,比如Windows自带的记事本程序(notepad.exe)。
一旦选定了被调试对象,WinDbg就会启动程序加载,期间命令窗口会输出很多信息。启动完成后的界面如下图所示。此界面和前面界面之间的最主要差别是:命令行已经不是灰显了,可以输入调试命令了。除此以外,左上角还列出了被调试的程序、WinDbg版本,命令窗口还会输出更多信息,比如截图显示:当前使用的是64位版本的WinDbg(因为有AMD64)。
图中命令行左侧的**0:000>**是命令行提示符,冒号左侧的0代表的是进程号,冒号后面的000代表的是当前线程。既然调试一个应用程序实际上就是打开了一个操作系统进程,而一个进程可能会包含数个线程,所以输入命令时一定要关注当前处于哪个进程和哪个线程。
在命令行输入波浪号 ~ 并回车,可以显示当前所有线程。
如下图所示,我的调试器当前有两个线程,分别是0号和1号线程。切换线程的命令是 ~[线程ID]s,比如输入 ~1s就会将当前线程切换到1号线程,命令行提示符也会发生相应变化。您可以试试在不同的线程下输入k命令显示当前栈帧,会发现0:000>和0:001>下的栈帧结果是不同的,比如1号线程栈中的KERNEL32模块在0号线程中就不存在:
备注:如果WinDbg是以内核态调试,则命令行提示符会包含kd字样,意思是kernel debug,如果被调试电脑有多个CPU,则KD前还会显示CPU编号。如:
0: kd>
如果调试器暂时正忙于处理未执行完毕的上一条命令,则命令行会显示BUSY字样。系统BUSY时,虽然可以输入新命令,但系统必须等到之前命令执行完毕以后才能执行新命令。下图所示的BUSY是因为WinDbg正在从微软符号服务器下载符号。
如果被调试程序处于运行状态,则不仅有BUSY标记,还会显示Debuggee is running,此时无法输入命令。这种情况下,必须先使用Break按钮将被调试程序中断到调试器,然后才能输入命令,如下图所示:
WinDbg帮助
WinDbg功能实在太过强大了,真想将其写透,至少需要上千页的文章。
不过,我们并不需要学会所有功能以后才开始实际调试。WinDbg是图形界面,简单上手其实非常容易,只要稍加学习即可。当确实需要深入研究某项具体功能时,可以使用WinDbg自带的帮助系统,也就是点击上图中右上方那个Local Help按钮,即可弹出帮助窗口,如下图所示:
帮助窗口中有目录、索引、搜索和收藏夹四个选项卡,如上图红色框所示。如果需要系统学习,可以使用目录选项卡,此时整个帮助就如同一本教科书。建议初学者起码跟随图示Getting Started with WinDbg(User-Mode)操作一遍,算是初步入门。
如果需要深入了解某个具体命令,可以点击搜索选项卡,输入要查找的命令名回车,然后双击列表中合适的行,右侧就会出现该命令的详细解释。下图以查询bp命令为例说明:
上述示例中我们搜索bp,结果列出了bp, bu, bm三个命令在一行的一篇文章。
如果我们又想查找与bp命令相关的其他主题,比如我们想知道bp和bu有什么区别,那么我们可以使用索引选项卡,依旧输入bp,目录窗口会列出和bp相关的很多主题,比如在breakpoints主题下,我们发现有BP versus BU(BP与BU),双击即可直接显示这篇文章。
如果此时又想知道Unresolved Breakpoints(bu Breakpoints)在整个帮助目录中的位置,那么我们可以再切换到目录选项卡,此时左侧的目录结构清晰表明,这篇文章位于Debugging Technique -> Standard Debugging Techniques -> Using Breakpoints目录下,其上一篇文章是Breakpoint Syntax(断点命令语法)。
另外,在命令行窗口直接输入.hh加上欲查询命令可以快速打开WinDbg帮助窗口并直接完成搜索,比如使用下面的命令同样可以达到前文所述效果,但速度更快。
0:000> .hh bp
WinDbg命令
WinDbg之所以非常强大,主要基于两项重大支撑:
- 丰富的命令
- 脚本支持
WinDbg命令非常多,如能熟练驾驭,调试Windows程序几乎可以说所向披靡。
不过正因为它的命令太多,也让熟练掌握该技术充满挑战。
脚本支持是建立在命令基础上的,WinDbg可以通过编写脚本(比如使用JavaScript、lambda表达式等),将诸多命令链接起来,实现复杂的调试和丰富且个性化的显示。微软官网有一系列有关WinDbg使用的视频,感兴趣的可以点击这里学习。
WinDbg有三种命令,分别称为基本命令,元命令和扩展命令,三者各自有不同的语法。
基本命令
可以直接在命令行输入的命令称为基本命令。基本命令是WinDbg自带的命令。以下例子使用 lmnm 命令列出 Core 模块的基本信息(加载起始地址、加载结束地址、模块名、所属文件):
0:000> lmnm core
Browse full module list
start end module name
00000000`02330000 00000000`02338000 Core Core.dll
WinDbg的基本命令是调试过程中使用最多的命令,其命令一般比较短,数量也并不很多。
可以使用?命令可以列出所有基本命令!
0:000> ?
Open debugger.chm for complete debugger documentation
B[C|D|E][<bps>] - clear/disable/enable breakpoint(s)
BL - list breakpoints
BA <access> <size> <addr> - set processor breakpoint
BP <address> - set soft breakpoint
D[type][<range>] - dump memory
DT [-n|y] [[mod!]name] [[-n|y]fields]
[address] [-l list] [-a[]|c|i|o|r[#]|v] - dump using type information
DV [<name>] - dump local variables
DX [-r[#]] <expr> - display C++ expression using extension model (e.g.: NatVis)
E[type] <address> [<values>] - enter memory values
G[H|N] [=<address> [<address>...]] - go
K <count> - stacktrace
KP <count> - stacktrace with source arguments
LM[k|l|u|v] - list modules
LN <expr> - list nearest symbols
P [=<addr>] [<value>] - step over
Q - quit
R [[<reg> [= <expr>]]] - view or set registers
S[<opts>] <range> <values> - search memory
SX [{e|d|i|n} [-c "Cmd1"] [-c2 "Cmd2"] [-h] {Exception|Event|*}] - event filter
T [=<address>] [<expr>] - trace into
U [<range>] - unassemble
version - show debuggee and debugger version
X [<*|module>!]<*|symbol> - view symbols
? <expr> - display expression
?? <expr> - display C++ expression
$< <filename> - take input from a command file
Hit Enter...
<expr> unary ops: + - not by wo dwo qwo poi hi low
binary ops: + - * / mod(%) and(&) xor(^) or(|)
comparisons: == (=) < > !=
operands: number in current radix, public symbol, <reg>
<type> : b (byte), w (word), d[s] (doubleword [with symbols]),
a (ascii), c (dword and Char), u (unicode), l (list)
f (float), D (double), s|S (ascii/unicode string)
q (quadword)
<pattern> : [(nt | <dll-name>)!]<var-name> (<var-name> can include ? and *)
<range> : <address> <address>
: <address> L <count>
User-mode options:
~ - list threads status
~#s - set default thread
| - list processes status
|#s - set default process
x64 options:
DG <selector> - dump selector
<reg> : [r|e]ax, [r|e]bx, [r|e]cx, [r|e]dx, [r|e]si, [r|e]di, [r|e]bp, [r|e]sp, [r|e]ip, [e]fl,
r8-r15 with b/w/d subregisters
al, ah, bl, bh, cl, ch, dl, dh, cs, ds, es, fs, gs, ss
sil, dil, bpl, spl
dr0, dr1, dr2, dr3, dr6, dr7
fpcw, fpsw, fptw, st0-st7, mm0-mm7
xmm0-xmm15
<flag> : iopl, of, df, if, tf, sf, zf, af, pf, cf
<addr> : #<16-bit protect-mode [seg:]address>,
&<V86-mode [seg:]address>
元命令(meta command)
元命令也是WinDbg自带的命令,和基本命令主要用于具体调试不同,元命令一般用于设置、显示或管理调试器或运行环境(比如加载扩展模块 .load,清空命令窗口 .cls,设置符号文件路径 .sympath,重新加载符号文件 .reload,显示已加载的扩展模块链表 .chain等)。
元命令必须以ASCII句点字符开始,后接命令、选项与参数。下面展示如何使用 .sympath 显示当前符号文件的路径设置:
0:000> .sympath
Symbol search path is: srv*;srv*c:\Symbols*http://msdl.microsoft.com/download/symbols;E:\test\a\Core\bin\Debug\net8.0
Expanded Symbol search path is: cache*;SRV*https://msdl.microsoft.com/download/symbols;srv*c:\symbols*http://msdl.microsoft.com/download/symbols;e:\test\a\core\bin\debug\net8.0
************* Path validation summary **************
Response Time (ms) Location
Deferred srv*
Deferred srv*c:\Symbols*http://msdl.microsoft.com/download/symbols
OK E:\test\a\Core\bin\Debug\net8.0
WinDbg有大约200个元命令。
可以使用 .help 列出所有元命令!可以使用 .help后加元命令显示具体元命令使用方法。
扩展命令(extension command)
与基本命令和元命令都是WinDbg自带的命令不同,扩展命令是WinDbg通过加载扩展模块(dll)而暂时拥有的外部命令,类似于很多应用程序的插件。通过加载WinDbg本身安装的扩展模块(如dbghelp.dll, exts.dll等)或手工加载微软或第三方开发的扩展(如sos.dll)等,可以使WinDbg的功能得到进一步加强。
使用扩展命令时,需要使用如下语法:
![扩展模块名.]扩展命令名 [选项] [参数…]
由于使用扩展命令时需要用ASCII感叹号(英文叫bang)开头,所以也称为bang命令。
想使用某扩展命令,首先必须知道这个命令属于哪个dll模块,然后还必须确保WinDbg已经加载了该模块。WinDbg启动时,会根据需要自动加载部分模块。查询当前已经加载的扩展模块,可以使用.chain这个元命令,如下图所示。
0:000> .chain
Extension DLL search Path:
C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\WINXP;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\arcade;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\pri;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64;C:\Users\zhang\AppData\Local\Dbg\EngineExtensions;C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64;C:\Program Files (x86)\VMware\VMware Workstation\bin\;C:\Program Files\Microsoft\jdk-11.0.16.101-hotspot\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\Program Files\Microsoft SQL Server\120\DTS\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\110\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\120\Tools\Binn\;C:\Program Files\Microsoft SQL Server\120\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\120\Tools\Binn\ManagementStudio\;C:\Program Files (x86)\Microsoft SQL Server\120\DTS\Binn\;C:\Program Files (x86)\Windows Kits\10\Debuggers\x64;C:\Program Files\dotnet\;C:\Program Files\nodejs\;C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\;C:\Users\zhang\AppData\Roaming\.dotnet;C:\Users\zhang\AppData\Local\Microsoft\WindowsApps;C:\Users\zhang\AppData\Local\Programs\Microsoft VS Code\bin;C:\Windows\Microsoft.NET\Framework\v4.0.30319;C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools;D:\masm32\bin;C:\Users\zhang\AppData\Local\Programs\Azure Data Studio\bin;C:\Users\zhang\.dotnet\tools;C:\Users\zhang\AppData\Roaming\npm
Extension DLL chain:
JsProvider: image 10.0.27553.1004, API 0.0.0,
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\JsProvider.dll]
DbgModelApiXtn: image 10.0.27553.1004, API 0.0.0,
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\DbgModelApiXtn.dll]
sos: image 8.0.510501, API 2.0.0, built Tue Feb 6 06:03:41 2024
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\sos\sos.dll]
CLRComposition: image 10.0.27553.1004, API 0.0.0,
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\CLRComposition.dll]
DbgEngCoreDMExt: image 10.0.27553.1004, API 0.0.0,
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\DbgEngCoreDMExt.dll]
dbghelp: image 10.0.27553.1004, API 10.0.6,
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\dbghelp.dll]
exts: image 10.0.27553.1004, API 1.0.0,
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\WINXP\exts.dll]
uext: image 10.0.27553.1004, API 1.0.0,
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\uext.dll]
ntsdexts: image 10.0.27553.1004, API 1.0.0,
[path: C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\WINXP\ntsdexts.dll]
以上列表中,我们主要需关注的就是下半部分Extension DLL chain部分,通过该列表我们知道,当前WinDbg已经自动为我们加载了这几个扩展模块:
JsProvider
DbgModelApiXtn
sos
CLRComposition
DbgEngCoreDMExt
dbghelp
exts
uext
ntsdexts
假设我们知道有一个扩展命令 !peb可以用来显示当前进程的进程环境块PEB,那么我们就可以输入:
0:000> !peb
PEB at 0028b000
InheritedAddressSpace: No
ReadImageFileExecOptions: No
BeingDebugged: Yes
ImageBaseAddress: 00450000
NtGlobalFlag: 0
NtGlobalFlag2: 0
Ldr 77b8eb20
Ldr.Initialized: Yes
Ldr.InInitializationOrderModuleList: 02662a50 . 0269c708
Ldr.InLoadOrderModuleList: 02662b38 . 0269c6f8
Ldr.InMemoryOrderModuleList: 02662b40 . 0269c700
略
这个例子中,我们在惊叹号后面直接跟了命令名,并没有指定扩展模块名,这种情况下WinDbg是如何知道到哪个模块中去找peb命令呢?
原来,WinDbg是按照 .chain命令输出的列表顺序去遍历所有扩展模块的,这也是 .chain这个命令的来源。比如针对我们的例子,首先查找的是JsProvider,如果没找到就继续找DbgModelApiXtn,总之一旦找到就直接执行,且执行完毕以后不会继续找。事实上,exts模块中才有peb这个扩展命令,所以只要加载了exts.dll,就可以用 !peb,或者用 !exts.peb,使用后者的优点是无需寻找,也不会出现二义性,即便不同模块中有重复也不会出错,缺点是需要额外多敲几个字符。
如果我们知道某个模块有某个扩展命令,但该模块没有被WinDbg自动加载,如何手动加载呢?答案是使用 .load元命令,举例如下:
0:000> .load C:\Program Files\WindowsApps\Microsoft.WinDbg_1.2402.24001.0_x64__8wekyb3d8bbwe\amd64\winext\sos\sos.dll
查看扩展命令列表
一个扩展模块会包含很多扩展命令。如何知道某模块到底有哪些命令?可以使用元命令.extmatch *列出所有已加载的扩展模块的扩展命令,并且是按当前搜索顺序加载的。
0:000> .extmatch *
!sos.AnalyzeOOM
!sos.BPMD
!sos.CLRMACreateInstance
!sos.CLRMAReleaseInstance
!sos.CLRStack
!sos.COMState
!sos.ClrStack
!sos.DumpALC
!sos.DumpArray
!sos.DumpAssembly
!sos.DumpAsync
!sos.DumpCCW
!sos.DumpClass
!sos.DumpDelegate
!sos.DumpDomain
!sos.DumpGCConfigLog
!sos.DumpGCData
!sos.DumpHeap
!sos.DumpIL
!sos.DumpLog
!sos.DumpMD
!sos.DumpMT
!sos.DumpModule
!sos.DumpObj
!sos.DumpRCW
!sos.DumpRuntimeTypes
!sos.DumpSig
!sos.DumpSigElem
!sos.DumpStack
!sos.DumpStackObjects
!sos.DumpVC
!sos.Dumpccw
!sos.Dumplog
!sos.Dumprcw
!sos.Dumpruntimetypes
!sos.EEHeap
!sos.EEStack
!sos.EEVersion
!sos.EHInfo
!sos.Ehinfo
!sos.ExposeDML
!sos.FinalizeQueue
!sos.FindAppDomain
!sos.FindRoots
!sos.Findappdomain
!sos.GCHandleLeaks
!sos.GCHandleleaks
!sos.GCHandles
!sos.GCHeapStat
!sos.GCInfo
!sos.GCRoot
!sos.GCWhere
!sos.GcHeapStat
!sos.GcWhere
!sos.Gchandleleaks
!sos.GetCodeTypeFlags
!sos.HeapStat
!sos.Help
!sos.HistClear
!sos.HistInit
!sos.HistObj
!sos.HistObjFind
!sos.HistRoot
!sos.HistStats
!sos.IP2MD
!sos.InitializeHostServices
!sos.ListNearObj
!sos.MinidumpMode
!sos.Minidumpmode
!sos.Name2EE
!sos.ObjSize
!sos.PathTo
!sos.PrintException
!sos.Printexception
!sos.ProcInfo
!sos.RCWCleanupList
!sos.Rcwcleanuplist
!sos.SOSFlush
!sos.SOSHandleCLRN
!sos.SOSInitializeByHost
!sos.SOSStatus
!sos.SOSUninitializeByHost
!sos.SaveModule
!sos.SaveState
!sos.SetClrPath
!sos.SetHostRuntime
!sos.SetSymbolServer
!sos.StopOnCatch
!sos.StopOnException
!sos.Stoponexception
!sos.SuppressJitOptimization
!sos.SyncBlk
!sos.ThreadPool
!sos.ThreadState
!sos.Threads
!sos.Token2EE
!sos.TraceToCode
!sos.TraverseHeap
!sos.Traverseheap
!sos.U
!sos.VMMap
!sos.VMStat
!sos.VerifyGMT
!sos.VerifyHeap
!sos.VerifyObj
!sos.VerifyStackTrace
!sos.Verifyheap
!sos.Watch
!sos.WatsonBuckets
!sos.analyzeoom
!sos.ao
!sos.assemblies
!sos.bpmd
!sos.clrmodules
!sos.clrstack
!sos.clrthreads
!sos.clru
!sos.comstate
!sos.crashinfo
!sos.da
!sos.dbgout
!sos.dclog
!sos.dg
!sos.dgc
!sos.do
!sos.dso
!sos.dumpalc
!sos.dumparray
!sos.dumpassembly
!sos.dumpasync
!sos.dumpccw
!sos.dumpclass
!sos.dumpdelegate
!sos.dumpdomain
!sos.dumpexceptions
!sos.dumpgcconfiglog
!sos.dumpgcdata
!sos.dumpgen
!sos.dumpheap
!sos.dumpil
!sos.dumplog
!sos.dumpmd
!sos.dumpmodule
!sos.dumpmt
!sos.dumpobj
!sos.dumprcw
!sos.dumpruntimetypes
!sos.dumpsig
!sos.dumpsigelem
!sos.dumpstack
!sos.dumpstackobjects
!sos.dumpvc
!sos.eeheap
!sos.eestack
!sos.eeversion
!sos.ehinfo
!sos.enummem
!sos.exposeDML
!sos.ext
!sos.finalizequeue
!sos.findappdomain
!sos.findroots
!sos.fq
!sos.gchandleleaks
!sos.gchandles
!sos.gcheapstat
!sos.gcinfo
!sos.gcroot
!sos.gcwhere
!sos.getCodeTypeFlags
!sos.heapstat
!sos.help
!sos.histclear
!sos.histinit
!sos.histobj
!sos.histobjfind
!sos.histroot
!sos.histstats
!sos.hof
!sos.ip2md
!sos.listnearobj
!sos.lno
!sos.logging
!sos.maddress
!sos.minidumpmode
!sos.name2ee
!sos.objsize
!sos.pathto
!sos.pe
!sos.printexception
!sos.procinfo
!sos.rcwcleanuplist
!sos.runtimes
!sos.savemodule
!sos.savestate
!sos.setclrpath
!sos.sethostruntime
!sos.setsymbolserver
!sos.sizestats
!sos.sjo
!sos.soe
!sos.sos
!sos.sosflush
!sos.soshelp
!sos.sosstatus
!sos.stoponcatch
!sos.stoponexception
!sos.suppressjitoptimization
!sos.syncblk
!sos.t
!sos.threadpool
!sos.threads
!sos.threadstate
!sos.token2ee
!sos.tp
!sos.tracetocode
!sos.traverseheap
!sos.u
!sos.verifyheap
!sos.verifyobj
!sos.vh
!sos.vmmap
!sos.vmstat
!sos.vo
!sos.watch
!CLRComposition.clrflush
!CLRComposition.disableclrcomposition
!CLRComposition.enableclrcomposition
!CLRComposition.enableclrplugin
!CLRComposition.forceclrmixedmodeunwind
!CLRComposition.tryunknownmodulesforclrbind
!dbghelp.chksym
!dbghelp.dh
!dbghelp.homedir
!dbghelp.lmi
!dbghelp.stackdbg
!dbghelp.sym
!exts.acl
!exts.atom
!exts.avrf
!exts.bievents
!exts.bipackages
!exts.bitcount
!exts.biworkitems
!exts.cs
!exts.decodeptr32
!exts.decodeptr64
!exts.dllcheck
!exts.dlls
!exts.dlltree
!exts.encodeptr32
!exts.encodeptr64
!exts.envvar
!exts.exrlog
!exts.findthis
!exts.gflag
!exts.globaltokenattr
!exts.hashtable
!exts.heap
!exts.help
!exts.hstring
!exts.hstring2
!exts.kuser
!exts.ldrnode
!exts.mui
!exts.peb
!exts.psmapps
!exts.psmclientapps
!exts.psmtestapps
!exts.psmusageapps
!exts.psr
!exts.rebase
!exts.schema
!exts.sd
!exts.services
!exts.shipassert
!exts.sid
!exts.slist
!exts.stltree
!exts.teb
!exts.time
!exts.tls
!exts.token
!exts.tp
!exts.udeadlock
!exts.urcu
!exts.winrterr
!uext.evlog
!uext.findstack
!uext.handle
!uext.help
!uext.mapped_file
!uext.runaway
!uext.uniqstack
!uext.vadump
!uext.vprot
!ntsdexts.critsec
!ntsdexts.dp
!ntsdexts.dreg
!ntsdexts.dt
!ntsdexts.gatom
!ntsdexts.handle
!ntsdexts.help
!ntsdexts.hleak
!ntsdexts.htrace
!ntsdexts.locks
!ntsdexts.runaway
!ntsdexts.threadtoken
从以上列表可知,很多扩展(如sos, exts,uext,ntsdexts模块)都包含有help扩展命令。下面,让我们分析一下如下命令的结果:
0:000> !help handle
Help for handle not found
0:000> !ntsdexts.help handle
!handle [handle [flags [type]]] - Dump handle information
If no handle specified, all handles are dumped.
Flags are bits indicating greater levels of detail.
If the handle is 0 or -1, all handles are scanned. If the handle is not
zero, that particular handle is examined. The flags are as follows:
1 - Get type information (default)
2 - Get basic information
4 - Get name information
8 - Get object specific info (where available)
If Type is specified, only object of that type are scanned. Type is a
standard NT type name, e.g. Event, Semaphore, etc. Case sensitive, of
course.
Examples:
!handle -- dumps the types of all the handles, and a summary table
!handle 0 0 -- dumps a summary table of all the open handles
!handle 0 f -- dumps everything we can find about a handle.
!handle 0 f Event
-- dumps everything we can find about open events
很显然,第一行命令 0:000> !help handle 没有得到handle扩展命令,原因就在于按查询顺序,第一个找到的help命令出现在sos模块,所以!help相当于!sos.help,用sos的help去查本属于ntsdexts模块的命令,当然不会有结果。第二条命令显式加入了模块名ntsdexts,因此得到了我们想要的结果。
另外观察一下如下命令:
0:000> !ntsdexts.help
ntsdexts help:
!critSec csAddress - Dump a critical section
!dp [v] [pid | pcsr_process] - Dump CSR process
!dreg -[d|w] <keyPath>[![<valueName> | *]] - Dump registry information
!dt [v] pcsr_thread - Dump CSR thread
!error value - Decode error value
!gatom - Dump the global atom table
!handle [handle] - Dump handle information
!help [cmd] - Displays this list or gives details on command
!locks [-v][-o] - Dump all Critical Sections in process
从前面曾经列出的所有扩展命令清单中,我们知道最后一个扩展命令是!ntsdexts.threadtoken,但上面运行!ntsdexts.help命令并未查到threadtoken命令。如果此时运行!threadtoken扩展命令,会得到如下结果:
0:000> !threadtoken
!threadtoken has been replaced by !token (in exts.dll).
系统提示,这个命令被exts模块的扩展命令!token替换了。帮助中查不到,是不是因为这个原因呢?我们继续研究。
sos中有一个很常用的命令u,我们经常使用这个命令来反汇编.NET被JIT编译后的代码(与直接使用u命令相比,!sos.u可以显示源代码行数,注释等,便于对应)。但如果使用 !sos.help u查询,会发现该命令并没有提供help。
0:000> !sos.help u
Help for u not found
还有一种情况,就是扩展模块未提供help命令,比如dbghelp扩展就是这样:
0:000> .extmatch /e dbghelp *
!dbghelp.chksym
!dbghelp.dh
!dbghelp.homedir
!dbghelp.lmi
!dbghelp.stackdbg
!dbghelp.sym
这种情况下,可以尝试一下是否包含 -h 选项。比如:
0:000> !dbghelp.dh -h
Usage: dh [options] address
Dumps headers from an image based at address
Options:
-a Dump everything
-f Dump file headers
-s Dump section headers
-e Dump exports
-i Dump imports
这说明,扩展命令的提供方各不相同,提供帮助的方式各不相同,提供帮助文档的完整性也各不相同。
注意:扩展命令区分大小写!
另外,WinDbg自带的很多扩展是可以在WinDbg的帮助窗口查到的,比如刚提到的 !dh 命令,如下图所示:
除此以外,我们还可以去开发者官网查询。比如比如sos.dll是微软开发的,到这里就可以看到微软关于该扩展的完整帮助,包括前面我们提到的找不到帮助的 !u 命令,这里也给出了详细使用说明,如下图所示:
总结
授之以鱼不如授之以渔,我们通过此文介绍了WinDbg的基本使用。虽然我们并没有讲解具体命令,但我们详细介绍了如何查询所有可用的命令,以及如何使用帮助系统深入学习各个命令。