wenmo8 发布的文章

!chkimg扩展命令通过将可执行文件的映像与符号存储库或其他文件存储库上的副本进行比较来检测可执行文件映像中的损坏。

语法

!chkimg[Options][-mmw LogFile LogOptions] [Module]

参数

Options

以下选项的任意组合:


-pSearchPath

在访问符号服务器之前,递归地搜索SearchPath路径。

-f

修复Image中的错误。每当扫描检测到符号存储中的文件与内存中的图像之间存在差异时,符号存储中的文件内容将复制到Image上。如果正在执行实时调试,则可以在执行!chkimg-f扩展之前创建一个转储文件。

-nar
防止移动符号服务器上文件的映射。默认情况下,当文件副本位于符号服务器上并映射到内存时!chkimg移动符号服务器上文件的Image。但是,如果使用-nar选项,则不会移动服务器上的文件映像。已在内存中的可执行映像(即正在扫描的映像)将被移动,因为调试器总是重新定位它加载的映像。仅当操作系统已移动原始映像时,此开关才有用。如果Image没有被移动!chkimg和调试器将移动Image。很少使用这个开关。
-ssSectionName

将扫描限制为名称包含字符串SectionName的节。扫描将包括名称中包含此字符串的任何不可丢弃部分。SectionName区分大小写,不能超过8个字符。

-as

使扫描包括除可丢弃部分以外的Image的所有部分。默认情况下,(如果不使用-as或-ss),扫描将跳过可写的部分、不可执行的部分、名称中有“PAGE”的部分以及可丢弃的部分。

-rStartAddressEndAddress

将扫描限制为以StartAddress开始、以EndAddress结束的内存范围。在此范围内,将扫描通常要扫描的任何部分。如果部分与此范围重叠,则只扫描与此范围重叠的部分。即使您也使用-as或-ss开关,扫描也限制在该范围内。

-nospec

使扫描包括的保留部分Hal.dll 和 Ntoskrnl.exe. 默认情况下!chkimg不检查这些文件的某些部分。

-noplock

显示字节值0x90(nop指令)和字节值0xF0(锁定指令)不匹配的区域。默认情况下,不显示这些不匹配项。

-np

使修补的指令被识别。

-d

扫描时显示所有不匹配区域的摘要。

-db

以类似于db debugger命令的格式显示不匹配的区域。因此,每个显示行显示该行中第一个字节的地址,后跟最多16个十六进制字节值。字节值后面紧跟着相应的ASCII值。所有不可打印的字符,如回车符和换行符,都显示为句点(.)。不匹配的字节用星号(*)标记。

-lolines

将-d或-db显示的输出行数限制为行数。

-v

显示详细信息。

-mmw

创建日志文件并记录!chkimg在这个文件里的活动。日志文件的每一行都表示一个不匹配项。

LogFile

指定日志文件的完整路径。如果指定相对路径,则该路径相对于当前路径。

LogOptions

指定日志文件的内容。LogOptions是一个由多个字母串联而成的字符串。日志文件中的每一行包含多个用逗号分隔的列。这些列包括以下选项字母指定的项,顺序是字母在LogOptions字符串中的出现顺序。可以多次包含以下选项。必须至少包含一个选项。


Log optionInformation included in the log file

v

The virtual address of the mismatch

r

The offset (relative address) of the mismatch within the module

s

The symbol that corresponds to the address of the mismatch

S

The name of the section that contains the mismatch

e

The correct value that was expected at the mismatch location

w

The incorrect value that was at the mismatch location


 

LogOptions还可以包括以下附加选项中的一些,或者不包括。


Log optionEffect

o

If a file that has the name LogFile already exists, the existing file is overwritten. By default, the debugger appends new information to the end of any existing file.

tString

Adds an extra column to the log file. Each entry in this column contains String. The tString option is useful if you are appending new information to an existing log file and you have to distinguish the new records from the old. You cannot add space between t and String. If you use the tIString option, it must be the final option in LogOptions, because String is taken to include all of the characters that are present before the next space.


 

例如,如果LogOptions是rSewo,则日志文件的每一行都包含不匹配位置的相对地址和节名以及该位置的预期值和实际值。此选项还会导致覆盖以前的任何文件。如果要创建多个具有不同选项的日志文件,可以多次使用-mmw开关。最多可以同时创建10个日志文件。

Module

指定要检查的模块。Module可以是模块的名称、模块的起始地址或模块中包含的任何地址。如果省略模块,调试器将使用包含当前指令指针的模块。

DLL

Windows 2000

Ext.dll

Windows XP and later

Ext.dll

备注

当你使用!它将内存中可执行文件的映像与驻留在符号存储中的文件副本进行比较。比较文件的所有部分,除了可丢弃、可写、不可执行、名称中有“PAGE”或来自INITKDBG的节除外。可以通过使用-ss、-as或-r开关来更改此行为。!chkimg将映像和文件之间的任何不匹配显示为映像错误,但以下情况除外:

  • 不检查由导入地址表(IAT)占用的地址。
  • 某些特定地址哈尔.dll以及Ntoskrnl.exe文件未选中,因为加载这些节时会发生某些更改。要检查这些地址,请包含-nospec选项。
  • 如果文件中存在字节值0x90,并且如果值0xF0出现在图像的相应字节中(反之亦然),则将这种情况视为匹配。通常,符号服务器保存单处理器和多处理器版本中都存在的二进制文件的一个版本。在基于x86的处理器上,锁指令是0xF0,而该指令对应于单处理器版本中的nop(0x90)指令。如果你愿意的话!chkimg要将此对显示为不匹配,请设置-noplock选项。

注意,如果您使用-f选项来修复Image不匹配!chkimg只修复那些它认为是错误的不匹配。例如!除非包含-noplock,否则chkimg不会将0x90字节更改为0xF0字节。

当您包含-d选项时!当扫描发生时,chkimg显示所有不匹配区域的摘要。每个不匹配显示在两行上。第一行包括范围的开始、范围的结束、范围的大小、与范围开始相对应的符号名称和偏移量,以及自上一个错误以来的字节数(在括号中)。第二行的十六进制值中包含了一个十六进制值。如果范围大于8个字节,则只有前8个字节显示在冒号之前和冒号之后。下面的示例显示了这种情况。

be000015-be000016  2 bytes - win32k!VeryUsefulFunction+15 (0x8)
     [ 85 dd:95 23 ]

有时,驱动程序会使用钩子、重定向或其他方法来更改Microsoft Windows内核的一部分。即使是不再在堆栈中的驱动程序也可能改变了内核的一部分。你可以用!chkimgextension是一个文件比较工具,用于确定驱动程序正在更改Windows内核(或任何其他映像)的哪些部分,以及这些部分是如何更改的。这种比较对完全转储文件最有效。
你也可以用!chkimg和!for_each_module他在一起用于检查每个加载模块的映像。下面的示例显示了这种情况。

 

!for_each_module !chkimg @#ModuleName 

例如,假设您遇到了一个bug检查,并使用 !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显示Win32 K模块的所有错误。

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 -fto修复内存。

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

!cpuid扩展命令显示有关系统上处理器的信息。

语法

!cpuid[Processor]

参数

Processor

指定将显示其信息的处理器。如果忽略此参数,则显示所有处理器。

DLL

Windows 2000

Ext.dll

Windows XP and later

Ext.dll

备注

!cpuid扩展在实时用户模式或内核模式调试、本地内核调试和转储文件调试期间工作。但是,用户模式的小型转储文件仅包含有关活动处理器的信息。如果在用户模式下调试,则!cpuid扩展描述目标应用程序正在运行的计算机。在内核模式下,它描述目标计算机。
下面的示例显示了这个扩展。

0:000> !cpuid
CP  F/M/S  Manufacturer     MHz
 0  6,158,10 GenuineIntel    3192
 1  6,158,10 GenuineIntel    3192
 2  6,158,10 GenuineIntel    3192
 3  6,158,10 GenuineIntel    3192
 4  6,158,10 GenuineIntel    3192
 5  6,158,10 GenuineIntel    3192
 6  6,158,10 GenuineIntel    3192
 7  6,158,10 GenuineIntel    3192
 8  6,158,10 GenuineIntel    3192
 9  6,158,10 GenuineIntel    3192
10  6,158,10 GenuineIntel    3192
11  6,158,10 GenuineIntel    3192
CP列给出处理器编号。(这些数字总是连续的,从零开始)。制造商列指定处理器制造商。MHz列指定处理器速度(如果可用)。对于基于x86或基于x64的处理器,F列显示处理器系列号,M列显示处理器型号,S列显示步进大小。对于基于安腾的处理器,M列显示处理器型号,R列显示处理器修订号,F列显示处理器系列号,A列显示体系结构修订号。

十一、系统内存信息流(SystemMemoryInfoStream)

SystemMemoryInfoStream包含系统内存管理的一些信息,它紧随在UnloadedModuleListStream流的后面。UnloadedModuleListStream的信息如下:

0x91f8+0n324=0x933c

SystemMemoryInfoStream的相关信息如下:

 

可知SystemMemoryInfoStream的RVA 为0x933C,大小为492字节,数据如下:

 

对应的数据结构目前我还没找到,但我们可以通过Minidump Browser工具大概了解一下:

 

对照上面的图,感觉这些数据对应的是SYSTEM_INFO结构,如下:

typedef struct_SYSTEM_INFO {
union {
DWORD dwOemId;
struct{
WORD wProcessorArchitecture;
WORD wReserved;
} DUMMYSTRUCTNAME;
} DUMMYUNIONNAME;
DWORD dwPageSize;
LPVOID lpMinimumApplicationAddress;
LPVOID lpMaximumApplicationAddress;
DWORD_PTR dwActiveProcessorMask;
DWORD dwNumberOfProcessors;
DWORD dwProcessorType;
DWORD dwAllocationGranularity;
WORD wProcessorLevel;
WORD wProcessorRevision;
} SYSTEM_INFO,
*LPSYSTEM_INFO;

有时在调试时,您希望知道某个特定进程是否使用 /LARGEADDRESSAWARE标志链接,从而能够使用高于2Gb边界的用户模式地址。我的解决方案是:
!address -summary将显示进程的有效用户模式地址空间:

0:022> !address –summary
..
       Tot: 7fff0000 (2097088 KB) Busy: 278fd000 (648180 KB) <<< 2Gb for non-large-address-aware EXE or large address aware EXE on x86 system without /3Gb in boot.ini


或者


Tot: bd7f0000 (3104704 KB) Busy: 23dee000 (587704 KB) <<< 3Gb for large-address-aware EXE on x86 system with /3Gb in boot.ini


或则


Tot: ffff0000 (4194240 KB) Busy: 268b2000 (631496 KB) <<< 4Gb for large-address-aware EXE running with WoW64 on x64 system


但是,由于第一种情况不明确,要实际查看EXE是否与/LargeAddressware链接,请执行以下操作:


    0:000> !dlls -c inetinfo    <<< inetinfo is the module name of the EXE in this case]
    Dump dll containing 0x01000000:

    0x00081eb0: C:WINDOWSsystem32inetsrvinetinfo.exe
          Base   0x01000000  EntryPoint  0x0100326e  Size        0x00006000
          Flags  0x00004000  LoadCount   0x0000ffff  TlsIndex    0x00000000
                 LDRP_ENTRY_PROCESSED
    0:000> .shell -i - -ci "!dlls -f 0x00081eb0" FIND "characteristics"
         12F characteristics
特征字段是:0x12f&0x20==0x20。这是IMAGE_FILE_LARGE_ADDRESS_AWARE的值。–有关此定义和相关定义,请参阅平台SDK中的winnt.h。
所以这个EXE可以识别大地址。
注意,上面使用的.shell命令(用于shell到另一个EXE,在本例中是“FIND”)是我一直用来过滤调试器命令输出的内容。非常方便。

如果您使用了WinDBG源代码调试功能,您可能会发现,每当您在调试中遇到一个与尚未打开的WinDBG文件中的源代码相关的点时,都会打开另一个源代码窗口,这很烦人。这导致了大量的源窗口和一组不断重新排列的窗口。

如果您进入View->Options,并将“打开此多个后重用”选项设置为1,而不是默认值(0):

 

然后你会发现它总是重用同一个源窗口来显示当前相关的源代码。
这意味着您可以将源窗口拖到WinDBG主窗口之外的某个位置(例如,拖到另一个监视器上),每次打开一个新的源文件时,它都会在那里打开,而不会扰乱WinDBG主区域中窗口的排列。