!chkimg

!chkimg 扩展将可执行文件的映像与符号存储区或其他文件存储库上的副本进行比较,以在可执行文件的映像中检测损坏。

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

参数

Options 以下选项的任意组合:

-p **** SearchPath
在访问符号服务器之前,以递归方式在 SearchPath 中搜索文件。

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

-nar
阻止移动符号服务器上的文件的映射映像。 默认情况下,当文件的副本位于符号服务器上并映射到内存中时,!chkimg 会将文件的映像移到符号服务器上。 但是,如果使用 -nar 选项,则不会移动服务器中的文件映像。

将移动内存中已有的可执行映像(即正在扫描的映像),因为调试器始终重新定位其加载的映像。

仅当操作系统已移动原始映像时,此开关才有用。 如果尚未移动映像,!chkimg 和调试器将移动映像。 很少使用此开关。

-ss **** SectionName
将扫描限制为名称包含字符串 SectionName 的节。 扫描将包含名称包含此字符串的任何不可放弃节。 SectionName 区分大小写,并且不能超过 8 个字符。

-as
促使扫描包含映像的所有节,但可放弃节除外。 默认情况下,(如果不使用 -as-ss),扫描将跳过可写入的节、不可执行的节、名称中具有“PAGE”的节以及不可放弃的节。

-r **** StartAddress **** EndAddress
将扫描限制为以 StartAddress 开头且以 EndAddress 结尾的内存范围。 在此范围内,扫描通常要扫描的任何节。 如果节与此范围部分重叠,则仅扫描与此范围重叠的该部分节。 即使还使用 -as-ss 开关,扫描也仅限于此范围。

-nospec
促使扫描包含 Hal.dll 和 Ntoskrnl.exe 的保留节。 默认情况下,!chkimg 不会检查这些文件的某些节。

-noplock
通过采用字节值 0x90(nop 指令)和 0xF0 字节值(lock 指令),显示不匹配的区域。 默认情况下,不会显示这些不匹配项。

-np
促使识别修补的指令。

-d
显示进行扫描时所有不匹配区域的摘要。 有关此摘要文本的详细信息,请参阅“备注”部分。

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

-lo **** lines
-d-db 显示的输出行数限制为行数。

-v
显示详细信息。

-mmw
创建日志文件并在此文件中记录 !chkimg 的活动。 日志文件的每一行都表示单个不匹配项。

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

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

日志选项 日志文件中包含的信息

v

不匹配项的虚拟地址

r

模块中不匹配项的偏移量(相对地址)

s

对应于不匹配项地址的符号

S

包含不匹配项的节的名称

e

不匹配项位置预期的正确值

w

位于不匹配项位置的不正确值

LogOptions 还可以包括以下其他选项中的一些或不包含任何选项。

日志选项 效果

o

如果已存在名为 LogFile 的文件,则会覆盖现有文件。 默认情况下,调试器会将新信息追加到任何现有文件的末尾。

tString

向日志文件添加额外的列。 此列中的每个条目都包含 String。 如果将新信息追加到现有日志文件,并且必须将新记录与旧记录区分开来,则 tString 选项非常有用。 不能在 tString 之间添加空格。 如果使用 tIString 选项,则它必须是 LogOptions 中的最后一个选项,因为 String 将包含下一个空格之前存在的所有字符。

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

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

DLL

Windows XP 及更高版本

Ext.dll

注解

使用 !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 会在扫描发生时显示所有不匹配区域的摘要。 每个不匹配项都显示在两行上。 第一行包括范围的开始、范围的结束、范围的大小、对应于范围开始的符号名称和偏移量,以及自上次错误(括在括号中)以来的字节数。 第二行括在方括号中,包括预期十六进制字节值、冒号,然后是在映像中实际遇到的十六进制字节值。 如果范围超过 8 个字节,则只有前 8 个字节显示在冒号之前和冒号之后。 以下示例显示这种情况。

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

有时,驱动程序会使用挂钩、重定向或其他方法更改 Microsoft Windows 内核的一部分。 即使是不再位于堆栈上的驱动程序,也可能会更改内核的一部分。 可以使用 !chkimg 扩展作为文件比较工具,以确定驱动程序正在更改 Windows 内核(或任何其他映像)的哪些部分,以及这些部分的更改方式。 此比较对完整转储文件最为有效。

检查每个加载的模块

还可以将 !chkimg!for_each_module 扩展结合使用,以检查每个已加载模块的映像。 以下示例显示这种情况。

!for_each_module !chkimg @#ModuleName 

!analyze 示例

假设遇到 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 显示 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

调查存储和内存损坏

随机文件和内存损坏可能难以调查。 在某些情况下需要考虑的一种工具是启用额外的内存检查,例如使用驱动程序验证程序。 有关驱动程序验证程序的信息,请参阅驱动程序验证程序

若要测试物理内存,请使用 Windows 内存诊断工具。 蓝屏数据中介绍了其使用和其他常规技术。

使用扫描磁盘实用工具标识文件系统错误。 选择并按住(或右键单击)要扫描的驱动器,然后选择“属性”。 选择工具。 选择“立即检查”按钮。