cc682/NetRoc
http://netroc682.spaces.live.com/
!chksym
!chksym 扩展测试一个符号文件对某个模块的有效性。
语法
!chksym <Module> [Symbol]
参数
Module
用名字或基地址指定模块。
Symbol
指定符号文件名。
DLL
Windows NT 4.0
|
不可用
|
Windows 2000
|
不可用
|
Windows XP
|
不可用
|
Windows Vista和之后
|
Dbghelp.dll
|
注释
如果没有指定符号,则测试已加载符号。另外,如果指定了一个.pdb 或者.dbg 符号路径,则测试已加载符号和已加载模块。
!chkimg
!chkimg 扩展通过比较可执行文件映像和符号存储或者其他地方的拷贝,来检测其中被改动过的内容。
语法
!chkimg [Options] [-mmw LogFile LogOptions] [Module]
参数
Options
下面这些选项的任意组合:
-p SearchPath
在访问符号服务器之前先在SearchPath中递归搜索文件。
-f
修正映像中的错误。当检测到内存映像和符号存储中的文件有不同时,会将符号存储中的文件内容覆盖掉内存映像中的。如果在进行活动调试,可以在执行!chkimg -f 之前先创建一个dump文件。
-nar
避免符号服务器上文件的映射的映像被移动。默认情况下,当文件的拷贝位于符号服务器上并且被映射到内存中时,!chkimg 会移动符号服务器上的文件的映像。但是,如果使用了-nar 选项,则不会移动服务器上的文件映像。
已经在内存中的可执行映像(即被扫描的那个)会被移动,因为调试器总是会重定位它所加载的映像。这仅在操作系统已经移动了原始映像时有用。如果映像还没有被移动过,则!chkimg 和调试器会移动它。
一般都不需要使用这个开关。
-ss SectionName
将扫描限制在名字包含字符串SectionName 的节中。会扫描名字中包含该字符串的任何不可丢弃的节。SectionName 区分大小写,并且不能超过8个字符。
-as
扫描映像中除了可丢弃节之外的所有节。默认情况下,(如果没有使用-as或者-ss),扫描会跳过可写的节、不可执行的节、名字中有"PAGE"的节、以及可丢弃的节。
-r StartAddress EndAddress
将扫描限制在从StartAddress 开始,EndAddress 结尾的范围内。在该范围中,任何可以被扫描的节都会扫描。如果某个节和该范围只是部分重叠,则只扫描范围中的那部分。即使使用了-as或者-ss 开关,扫描还是被限制在指定范围中。
-nospec
使得扫描包括Hal.dll 和Ntoskrnl.dll 中的保留节。默认情况下, !chkimg 不会检查这些文件的这部分节。
-noplock
显示相应的字节值分别为0x90(nop指令)和0xF0(lock指令)的位置。默认情况下,这些部位不会被显示出来。(详见注释部分,— 译者)
-np
使得被修改过的指令能被识别出来。Causes patched instructions to be recognized. .
-d
扫描时显示不匹配的区域的摘要信息。该摘要文本的详细信息,查看注释部分。
-db
以类似db 调试器命令的格式显示不匹配的区域。因此,每个显示行的开头是该行第一个字节的地址,后面跟最多16个16进制字节值。字节值后面跟相应的ASCII值。所有的不可打印字符,如回车和换行,都显示为点号(.)。不匹配的字节以星号(*)标识。
-lo lines
将-d 或-db 显示的最大行数限制为lines 。
-v
显示详细信息。
-mmw
创建一个日志文件,并记录!chkimg 的活动情况。日志文件的每一行都对应单个不匹配的项。
LogFile
指定日志文件的完整路径。如果指定相对路径,则是相对于当前路径的。
LogOptions
指定日志文件的内容。LogOptions 是由各个字母组合起来的字符串。日志文件的每一行都包含由逗号分隔的几个列。这些列包含由选项字母指定的项,按照这些字母在LogOptions 中出现的顺序。可以包含下面这些选项多次,但是必须至少包含一个选项。
日志选项
|
日志文件中包含的信息
|
v
|
不匹配的内容的虚拟地址
|
r
|
不匹配内容在模块中的偏移(相对地址)
|
s
|
和不匹配内容地址对应的符号
|
S
|
包含不匹配内容的节的名字
|
e
|
不匹配位置的原始值
|
w
|
不匹配位置的当前值
|
LogOptions 也可以包含下面这些附加选项的一个、多个或者0个。
日志选项
|
作用
|
o
|
如果LogFile 名字的文件已经存在,则覆盖该文件。默认情况下,调试器会将新信息添加到已存在的文件末尾。
|
tString
|
在日志文件中添加一个额外的列。该列中每一项都包含String。tString 选项在将新信息添加到已存在的日志文件时,可以用来和旧的纪录区分开。在t和String之间不能有空格。如果使用tIString选项,它必须是LogOptions 的最后一个选项,因为String包括下一个空格之前的所有字符。
|
例如,如果 LogOptions 是rSewo,则日志中每一行包括不匹配项的相对地址、节名、该地址的原始值和实际值,并且任何先前的文件都会被覆盖。如果想创建多个具有不同选项的日志文件,可以使用-mmw多次。一次最多创建10个日志文件。
Module
指定要检查的模块。Module 可以是模块名、模块的开始地址,或者包含在模块中的任何地址。如果省略Module,调试器使用包含当前指令指针的模块。
DLL
Windows NT 4.0
|
Ext.dll
|
Windows 2000
|
Ext.dll
|
Windows XP和之后
|
Ext.dll
|
只能在基于x86的目标机上使用!chkimg 扩展命令。
注释
使用!chkimg时,它会比较内存中的可执行映像和符号存储中的文件拷贝。
文件中除了可抛弃的、可写得、不可执行的、名字中有"PAGE"的或者INITKDBG 中的节之外,所有节都会被比较。可以使用-ss、-as、或-r 开关来改变这个行为。
!chkimg 会显示文件和内存映像中所有不匹配的地方,除了下面的例外:
- 导入表(IAT)占用的地址不会检查。
- Hal.dll 和Ntoskrnl.exe 中的特定地址不会检查,因为这些节加载时会进行特定的改变。要检查这些地址,使用-nospec 选项。
- 如果文件中的字节值为0x90,内存中的响应字节为0xF0(或者反过来),这种情况被认为是匹配的。一般来说,符号服务器保存单处理器版本和多处理器版本二进制文件中的一个。在x86处理器上,lock指令是0xF0,而在单处理器版本中响应的指令会是nop (0x90)。如果希望!chkimg 显示这种不匹配的项,使用-noplock 选项。
注意 如果使用了-f选项来修正不匹配的内容,!chkimg 仅修正被认为是不匹配的地方。例如,没有包含-noplock时,!chkimg不会将0x90改为0xF0。
包含-d选项时,!chkimg 显示扫描中遇到的不匹配内容的摘要。每个不匹配位置都显示为两行。第一行包含该位置的起始地址、大小、和起始地址对应的符号名和偏移、以及和上一个错误位置之间有多少个字节(圆括号中)。第二行用中括号括起来,包含原始的16进制值、一个冒号、以及当前映像中实际的16进制值。如果该区域大于8个字节,在冒号前后都只会显示8个字节。下面是一个例子。
be000015-be000016 2 bytes - win32k!VeryUsefulFunction+15 (0x8)
[ 85 dd:95 23 ]
有时候,驱动程序会通过hook、重定向或其它方法改变Microsoft Windows内核的一部分。即使是不在堆栈上的驱动也可能改变内核。可以使用!chkimg 扩展作为文件比较工具来查看Windows内核(或任何其它映像)被某些驱动改变了,以及具体是怎么改变的。这种比较在完整dump文件上非常有效。
也可以将!chkimg 和!for_each_module 扩展一起使用来检查所有已加载模块的映像。下面是这种情况的示例。
!for_each_module !chkimg @#ModuleName
例如,假设遇到了bug check,以使用!analyze开始。
kd> !analyze
....
BugCheck 1000008E, {c0000005, bf920e48, baf75b38, 0}
Probably caused by : memory_corruption
CHKIMG_EXTENSION: !chkimg !win32k
....
例中 !analyze 的输出提示可能遇到了内存错误,并且包含了一个CHKIMG_EXTENSION 行提示被破坏的模块可能是Win32k.sys。(即使不存在这一行,也应该考虑可能调用堆栈顶部的模块被破坏了。)不带任何参数使用!chkimg ,如下。
kd> !chkimg win32k
Number of different bytes for win32k: 31
下面的例子说明确实有内存错误。使用!chkimg -d 来显示模块Win32k 中的所有错误。
kd> !chkimg win32k -d
bf920e40-bf920e46 7 bytes - win32k!HFDBASIS32::vSteadyState+1f
[ 78 08 d3 78 0c c2 04:00 00 00 00 00 01 00 ]
bf920e48-bf920e5f 24 bytes - win32k!HFDBASIS32::vHalveStepSize (+0x08)
[ 8b 51 0c 8b 41 08 56 8b:00 00 00 00 00 00 00 00 ]
Number of different bytes for win32k: 31
当尝试反汇编列出来的第二个被破坏的映像位置时,可能会有下面这样的输出。
kd> u win32k!HFDBASIS32::vHalveStepSize
win32k!HFDBASIS32::vHalveStepSize:
bf920e48 0000 add [eax],al
bf920e4a 0000 add [eax],al
bf920e4c 0000 add [eax],al
bf920e4e 0000 add [eax],al
bf920e50 7808 js win32k!HFDBASIS32::vHalveStepSize+0x12 (bf920e5a)
bf920e52 d3780c sar dword ptr [eax+0xc],cl
bf920e55 c20400 ret 0x4
bf920e58 8b510c mov edx,[ecx+0xc]
然后,使用!chkimg –f来修正内存错误。
kd> !chkimg win32k -f
Warning: Any detected errors will be fixed to what we expect!
Number of different bytes for win32k: 31 (fixed)
现在可以反汇编正确的内容并且查看有些什么改变。
kd> u win32k!HFDBASIS32::vHalveStepSize
win32k!HFDBASIS32::vHalveStepSize:
bf920e48 8b510c mov edx,[ecx+0xc]
bf920e4b 8b4108 mov eax,[ecx+0x8]
bf920e4e 56 push esi
bf920e4f 8b7104 mov esi,[ecx+0x4]
bf920e52 03c2 add eax,edx
bf920e54 c1f803 sar eax,0x3
bf920e57 2bf0 sub esi,eax
bf920e59 d1fe sar esi,1
!cppexr
!cppexr 扩展显示一个C++ 异常记录的内容。
语法
!cppexr Address
参数
Address
指定要显示的C++ 异常记录的地址。
DLL
Windows NT 4.0
|
Ext.dll
|
Windows 2000
|
Ext.dll
|
Windows XP和之后
|
Ext.dll
|
注释
!cppexr 扩展显示和目标遇到的C++ 异常相关的信息,包括发生异常处的代码、异常的地址和异常标志。必须是在Msvcrt.dll中定义的标准C++ 异常。
一般可以使用!analyze -v命令来获得address参数。
!cppexr 扩展用来查看C++异常的类型很有用。
附加信息
关于异常的更多信息,查看控制异常和事件、Windows Driver Kit (WDK)文档、 Windows SDK 文档、以及Mark Russinovich和David Solomon编写的Microsoft Windows Internals 。使用.exr 命令来显示其它异常记录。
!cpuid
!cpuid 扩展用来显示系统中的处理器的信息。
语法
!cpuid [Processor]
参数
Processor
指定要显示信息的处理器。如果省略该参数,则显示所有处理器。
DLL
Windows NT 4.0
|
Ext.dll
|
Windows 2000
|
Ext.dll
|
Windows XP和之后
|
Ext.dll
|
注释
!cpuid 扩展可以在用户模式或者内核模式活动调试、本地内核调试和调试dump文件时使用。但是,用户模式minidump文件仅包含活动处理器的信息。
如果在用户模式下调试,!cpuid 扩展用于查看目标程序运行的计算机。在内核模式下调试时,用于查看目标机。
下面是该扩展的示例。
kd> !cpuid
CP F/M/S Manufacturer MHz
0 6,5,1 GenuineIntel 700
1 8,1,5 AuthenticAMD 700
CP 列是处理器号。(这些数字总是连续的,以0开始)。 Manufacturer 列显示处理器厂商。MHz 列显示处理器的速度,如果可用的话。
对于x86处理器或者x64处理器, F 列显示处理器的家族号, M 列显示处理器模型号,S 列显示stepping size(流水线长度?)。
对于Itanium处理器,M列显示处理器模型号, R 列显示处理器修订编号(revision number),F列显示处理器家族号, A列显示架构修订编号。
附加信息
关于如何调试多处理器计算机的信息,查看多处理器语法。
!cs
!cs 扩展显示一个或多个临界区(critical section)或者整个临界区树。
语法
!cs [-s] [-l] [-o]
!cs [-s] [-o] [Address]
!cs [-s] [-l] [-o] StartAddress EndAddress
!cs [-s] [-o] -d InfoAddress
!cs [-s] -t [TreeAddress]
!cs -?
参数
-s
如果可能的话,显示每个临界区的初始堆栈回溯。
-l
仅显示锁定的临界区。
-o
对所有显示出来的已锁定的临界区,显示所有者的堆栈。
Address
指定要显示的临界区地址。如果省略该参数,调试器显示当前进程中所有临界区。
StartAddress
指定要搜索临界区的地址范围的开头。
EndAddress
指定要搜索临界区的范围的结束地址。
-d
显示和DebugInfo 关联的临界区。
InfoAddress
指示DebugInfo 的地址。
-t
显示临界区树。使用-t 选项之前,必须选择为目标进程激活应用程序验证器(Application Verifier)并且选中Check lock usage 选项。
TreeAddress
指定临界区树的根的地址。如果省略该参数或者指定0,调试器现实当前进程的临界区树。
-?
在调试器命令窗口中显示该命令的帮助文本。
DLL
Windows NT 4.0
|
不可用
|
Windows 2000
|
不可用
|
Windows XP和之后
|
Exts.dll
|
注释
!cs 扩展要求被调试进程和Ntdll.dll的完整符号(包含类型信息)。如果没有Ntdll.dll的符号,查看安装Windows符号文件。
下面是如何使用!cs的示例。这条命令显示0x7803B0F8地址处的临界区的信息和初始调用堆栈。
0:001> !cs -s 0x7803B0F8
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
DebugInfo = 0x6A262080
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
Stack trace for DebugInfo = 0x6A262080:
0x6A2137BD: ntdll!RtlInitializeCriticalSectionAndSpinCount+0x9B
0x6A207A4C: ntdll!LdrpCallInitRoutine+0x14
0x6A205569: ntdll!LdrpRunInitializeRoutines+0x1D9
0x6A20DCE1: ntdll!LdrpInitializeProcess+0xAE5
下面的命令显示DebugInfo在0x7803B0F8处的临界区信息。
0:001> !cs -d 0x6A262080
DebugInfo = 0x6A262080
Critical section = 0x7803B0F8 (MSVCRT!__app_type+0x4)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
下面的信息显示当前进程中的所有活动临界区的信息。
0:001> !cs
-----------------------------------------
DebugInfo = 0x6A261D60
Critical section = 0x6A262820 (ntdll!RtlCriticalSectionLock+0x0)
LOCKED
LockCount = 0x0
OwningThread = 0x460
RecursionCount = 0x1
LockSemaphore = 0x0
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x6A261D80
Critical section = 0x6A262580 (ntdll!DeferedCriticalSection+0x0)
NOT LOCKED
LockSemaphore = 0x7FC
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x6A262600
Critical section = 0x6A26074C (ntdll!LoaderLock+0x0)
NOT LOCKED
LockSemaphore = 0x0
SpinCount = 0x0
-----------------------------------------
DebugInfo = 0x77fbde20
Critical section = 0x77c8ba60 (GDI32!semColorSpaceCache+0x0)
LOCKED
LockCount = 0x0
OwningThread = 0x00000dd8
RecursionCount = 0x1
LockSemaphore = 0x0
SpinCount = 0x00000000
-----------------------------------------
...
下面的命令现实临界区树。
0:001> !cs -t
Tree root 00bb08c0
Level Node CS Debug InitThr EnterThr WaitThr TryEnThr LeaveThr EnterCnt WaitCnt
-----------------------------------------------------------------------------------------------
0 00bb08c0 77c7e020 77fbcae0 4c8 4c8 0 0 4c8 c 0
1 00dd6fd0 0148cfe8 01683fe0 4c8 4c8 0 0 4c8 2 0
2 00bb0aa0 008e8b84 77fbcc20 4c8 0 0 0 0 0 0
3 00bb09e0 008e8704 77fbcba0 4c8 0 0 0 0 0 0
4 00bb0a40 008e8944 77fbcbe0 4c8 0 0 0 0 0 0
5 00bb0a10 008e8824 77fbcbc0 4c8 0 0 0 0 0 0
5 00bb0a70 008e8a64 77fbcc00 4c8 0 0 0 0 0 0
3 00bb0b00 008e8dc4 77fbcc60 4c8 0 0 0 0 0 0
4 00bb0ad0 008e8ca4 77fbcc40 4c8 0 0 0 0 0 0
4 00bb0b30 008e8ee4 77fbcc80 4c8 0 0 0 0 0 0
5 00dd4fd0 0148afe4 0167ffe0 4c8 0 0 0 0 0 0
2 00bb0e90 77c2da98 00908fe0 4c8 4c8 0 0 4c8 3a 0
3 00bb0d70 77c2da08 008fcfe0 4c8 0 0 0 0 0 0
!cs -t 的显示中有如下内容:
- InitThr 是初始化临界区的线程的ID。
- EnterThr 是最近一次调用EnterCriticalSection 的线程ID。
- WaitThr 是最近一次因为其他线程拥有临界区而等待的线程的ID。
- TryEnThr 是最近一次调用TryEnterCriticalSection 的线程的ID。
- LeaveThr 是最近一次调用LeaveCriticalSection 的线程ID。
- EnterCnt 是EnterCriticalSection 的次数。
- WaitCnt是出现争用的次数。
附加信息
关于其他可以显示临界区信息的命令和扩展,查看显示临界区。关于临界区的更多信息,查看Microsoft Windows SDK文档、the Windows Driver Kit (WDK)文档、以及Mark Russinovich 和David Solomon 编写的Microsoft Windows Internals。
!cxr
!cxr 扩展命令已经废除。使用.cxr (Display Context Record)命令来替代。