Windbg指令体系

Windbg,作为Windows御用的调试工具,功能异常强大,其指令亦是相当繁杂。我是从01年开始使用,至今为止所掌握的指令还是最常用的一些,如果做点稍复杂的工作,必须求助于Windbg帮助手册,查手册寻找指令或确认指令语法格式已是经常之事。

倒是被Windbg所替代的一代神器Softice,以其界面和指令的简洁、易用,曾给几代程序人留下深刻印象。我大约是从96年开始使用Softice,至01年完全转移到Windbg上来,但很多Softice的指令还是犹如历历在目,看来“先入者为王”对习惯的养成来说同样是条不二准则。

言归正传,Windbg的指令分为三种:

  • 1)Debugger commands,或 regular commands

    如 u: 反汇编指令;db: 以16进制及字节方式打印数据;t: 单步执行 …

    regular commands 一般是面向于被调试程序或目标系统的,即可以显示被调试程序相关的信息或控制目标程序的的执行等。

  • 2)Meta commands

    如 .reload: 加载符号表;.cls: 清除command窗口所显示的信息;.thread: 挂接并显示指定线线程的上下文(context) …

    meta commands 一般指控制windbg本身的一些指令,所有指令以”.”为开始,如符号表管理相关的指令。

    其实有一些指令功能上与regular command相混淆,如:

    .restart: 重启被调试机器或程序,呵呵,没有比这个对被调试对象更大的控制操作了;
    .writemem: 将指定内存区域写入文件;
    ……

    此外,还有些莫名的指令也归入这一类,如.dbgdbg指令,它会启动调试器来调试当前的调试器,够绕吧!

  • 3)Debugger extension commands

    如 !process:显示当前进程信息;!analyze: 著名的BSOD分析命令…

    这类指令属于windbg的扩展指令,所有指令均以”!”为前缀,指令的实现不在debugger程序体中,而是相关的DLL文件。windbg的扩展组件,有标准的格式,开发者可以根据需要为协助自己程序的调试定制自己的扩展组件。windbg程序包已为不同的应用环境提供了不同的扩展组件:如应用程,内核层,NDIS驱动,GDI图形类驱动……

除了Windbg的帮助手册之外,还有几本书对Windbg的使用很有帮助:

1, Dmitry Vostokov, Memory Dump Analysis Anthology, Volume 1 & 2
    结合实例介绍Windbg的使用技巧,共两本
2, Mario Hewardt & Daniel Pravat, Advanced Windows Debugging
    此书有中译本。主要还是面向Windows内核调试的。
3, 熊力,Windows用户态程序高效排错
    主要面向于用户态及.NET程序的调试。

慎用MmSetAddressRangeModified

MmSetAddressRangeModified用来设置PFN为dirty/modified,并将PTE的dirty位清除。但除此之外,还有个不明显的副作用,看下面的分析:

1: kd> !pte 0xfffff880`0c9e6000
                                 VA fffff8800c9e6000
PXE @ FFFFF6FB7DBEDF88     PPE at FFFFF6FB7DBF1000    PDE at FFFFF6FB7E200320    PTE at FFFFF6FC40064F30
contains 000000003FE84863  contains 000000003FE83863  contains 0000000014516863  contains 000000000FAD7963
pfn 3fe84      ---DA--KWEV  pfn 3fe83      ---DA--KWEV  pfn 14516      ---DA--KWEV  pfn fad7       -G-DA--KWEV

PTE entry 状态为dirty,并且是可写的(writable)。 再看调用MmSetAddressRangeModified后的状态:

1: kd> !pte 0xfffff880`0c9e6000
                                 VA fffff8800c9e6000
PXE @ FFFFF6FB7DBEDF88     PPE at FFFFF6FB7DBF1000    PDE at FFFFF6FB7E200320    PTE at FFFFF6FC40064F30
contains 000000003FE84863  contains 000000003FE83863  contains 0000000014516863  contains 000000000FAD7921
pfn 3fe84      ---DA--KWEV  pfn 3fe83      ---DA--KWEV  pfn 14516      ---DA--KWEV  pfn fad7       -G--A--KREV

PTE entry的dirty位已被清除,但是此pte已被设成了readonly状态了。所以如果再有写操作,必然会导致page fault发生。

这就是我曾遇到的一个Ext2Fsd的bug:Ext2Fsd为了将page cache锁定,创建了MDL并重新映射到系统空间(调用MmMapLockedPagesSpecifyCache)。新映射的va具有dirty及writable属性,故此va在spinlock (DISPATCH_LEVEL)下进行写操作不会导致任何异常。但在提交改动过程中,Ext2Fsd调用了MmSetAddressRangeModified,调用后MmSetAddressRangeModified会将此pte设置为readonly,如果下一次的写操作正好在spinlock下(DISPATCH_LEVEL),将会导致BSOD: DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1),如果在获取spinlock前曾执行过写操作(IRQL < DISPATCH_LEVEL),则会正常触发page fault,然后MmAccessFault会重置pte为writeable,并设置dirty位,此后如果再进入DISPATCH_LEVEL,对此va进行写操作便不会触发page fault了。这就构成了一定的随机性和隐蔽性,给调试带来了很大的麻烦。

明白了问题所在,不妨再做个实验:如果手工将此pte设为writeable的,再进行写操作,cpu应该直接置pte为dirty,而不必调用OS(即page fault)。

对va 0xfffffa60`04ae7000 调用MmSetAddressRangeModified后,

1: kd> !pte 0xfffffa60`04ae7000
                                 VA fffffa6004ae7000
PXE @ FFFFF6FB7DBEDFA0     PPE at FFFFF6FB7DBF4C00    PDE at FFFFF6FB7E980128    PTE at FFFFF6FD30025738
contains 000000007FFC4863  contains 000000007FFC3863  contains 00000000539A2863  contains 00000000149AD921
pfn 7ffc4      ---DA--KWEV  pfn 7ffc3      ---DA--KWEV  pfn 539a2      ---DA--KWEV  pfn 149ad      -G--A—KREV

手工修改 0xfffffa60`04ae7000为writeable,不必置dirty标志:
1: kd> dq FFFFF6FD30025738 l1
fffff6fd`30025738  00000000`149ad921

1: kd> eb FFFFF6FD30025738 23
1: kd> !pte 0xfffffa60`04ae7000
                                 VA fffffa6004ae7000
PXE @ FFFFF6FB7DBEDFA0     PPE at FFFFF6FB7DBF4C00    PDE at FFFFF6FB7E980128    PTE at FFFFF6FD30025738
contains 000000007FFC4863  contains 000000007FFC3863  contains 00000000539A2863  contains 00000000149AD923
pfn 7ffc4      ---DA--KWEV  pfn 7ffc3      ---DA--KWEV  pfn 539a2      ---DA--KWEV  pfn 149ad      -G--A--KWEV

在进行写操作前可以对KiPageFault或MmAccessFault设置断点,然后进行实验。看断点会不会触发,操作后再检查一下pte的dirty标志是不是已经设置了。具体实验结果,就留给读者自己去验证了。

在DISPATCH_LEVEL中操作paged va无论是已将其page锁定还是重新映射过,都有点如履薄冰的感觉,特别是对file cache,Cache Manager的不少内部操作都会更改pte的属性或调用MmSetAddressRangeModified,到处都可能有陷阱。所以最保险的方式还是不用spinlock 微笑

用Windbg替代VSJitDebugger

习惯了Windbg,准备用Windbg替代Vistual Studio的VSJitDebugger.exe来做Postmortem Debugger。但尝试了下面两种办法都没有成功:

  1. 1, Windbg –I (设Windbg为默认的debugger应对Unhandled User Mode Exceptions)
  2. 2, 直接改注册表:[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]

直到在http://msdn.microsoft.com/en-us/library/ff542967.aspx里才看到,64位系统里对64位和32位程序有不同的设置:

1, A failing 64-bit application will be debugged according to the settings stored in the \\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AeDebug key.

2, A failing 32-bit application will be debugged according to the settings stored in the \\HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug key. However, if the Debugger value in this key specifies an application in the %windir%\system32 directory, Windows will look in %windir%\syswow64 instead.

  • 将两处的AeDebug都设成Windbg后,分别对64位和32程序进行测试,Windbg被加载并成功attach到问题进程上。

  • D:\Documents> type AeDebug-Windbg.reg

    Windows Registry Editor Version 5.00

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug]
    "UserDebuggerHotKey"=dword:00000000
    "Debugger"="\"C:\\Program Files\\Debugging Tools for Windows (x64)\\windbg.exe\" -p %ld -e %ld -g"
    "Auto"="1"

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\AutoExclusionList]
    "DWM.exe"=dword:00000001


    [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug]
    "UserDebuggerHotKey"=dword:00000000
    "Debugger"="\"C:\\Program Files\\Debugging Tools for Windows (x64)\\windbg.exe\" -p %ld -e %ld -g"
    "Auto"="1"

    [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\AeDebug\AutoExclusionList]
    "DWM.exe"=dword:00000001

    IE8 下载即崩溃

    这几天Green Browser一直不老实工作,只要我点下载或另存文件就异常退出,升级成最新版本问题也没能解决。然后检查IE8,同样会出异常退出,看来是IE8的问题。

    先禁掉可疑add-on插件,问题照旧,还是不能下载任何文件。启动IE8 w/o add-on模式,问题还是存在,这下快轮到我崩溃了。

    调用Visutal Studio分析,发现问题在Wpc.dll模块中,stack trace如下:

         Wpc.dll!WPCSecurity::CheckSID()  + 0x1d bytes   
         Wpc.dll!CWpcSettings::Initialize()  + 0x11 bytes   
         Wpc.dll!CWebSettings::Initialize()  + 0x3a bytes   
         Wpc.dll!CWindowsParentalControls::GetWebSettings()  + 0x68 bytes   
    >    ieframe.dll!InitWPCSettings()  + 0xeb bytes   
         ieframe.dll!CheckFileDownloadSettings()  + 0x26 bytes   
         ieframe.dll!CDocObjectHost::CDOHBindStatusCallback::_ProcessCONTENTDISPOSITIONBindStatus()  + 0x11d bytes   
         ieframe.dll!CDocObjectHost::CDOHBindStatusCallback::_ProcessSecurityBindStatus()  + 0x7a6f0 bytes   
         ieframe.dll!CDocObjectHost::CDOHBindStatusCallback::OnProgress()  - 0x15 bytes   
         urlmon.dll!CBSCHolder::OnProgress()  + 0x40 bytes   
         urlmon.dll!CBinding::CallOnProgress()  + 0x31 bytes   
         urlmon.dll!CBinding::OnTransNotification()  + 0xfb50 bytes   
         urlmon.dll!CBinding::ReportProgress()  + 0x2c bytes   
         urlmon.dll!COInetProt::ReportProgress()  + 0x58 bytes   
         urlmon.dll!CTransaction::DispatchReport()  - 0xa bytes   
         urlmon.dll!CTransaction::DispatchPacket()  + 0x31 bytes   
         urlmon.dll!CTransaction::OnINetCallback()  + 0x83 bytes   
         urlmon.dll!TransactionWndProc()  + 0x28 bytes   
         user32.dll!_InternalCallWinProc@20()  + 0x23 bytes   
         user32.dll!_UserCallWinProcCheckWow@32()  + 0xd3 bytes   
         user32.dll!_DispatchMessageWorker@8()  + 0xee bytes   
         user32.dll!_DispatchMessageW@4()  + 0xf bytes   
         ieframe.dll!CTabWindow::_TabWindowThreadProc()  - 0x32182 bytes   
         ieframe.dll!LCIETab_ThreadProc()  + 0x4433 bytes   
         iertutil.dll!CIsoScope::RegisterThread()  - 0x3227 bytes   
         kernel32.dll!@BaseThreadInitThunk@12()  + 0xe bytes   
         ntdll.dll!___RtlUserThreadStart@8()  + 0x23 bytes   
         ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes   

    ieframe.dll!InitWPCSettings() 尝试加载Wpc.dll,然后在Wpc中发生异常。Wpc.dll是处理Parental Controls的模块,对此了解不多,网上也没有多少介绍。

    先找到我上次做的系统备份,是一个月前做的,竟然没找到这个文件,于是揣测是近期新装的程序或Windows update安装的。但到底是什么软件还很难去定位。

    c:\Windows\SysWOW64>dir Wpc.dll
    2009/07/14  09:16           308,736 Wpc.dll
    c:\Windows\SysWOW64>dumpbin /headers wpc.dll |grep version
                9.00 linker version
                6.01 operating system version
                6.01 image version
                6.01 subsystem version
                   0 Win32 version

    查看PE文件头,OS竟是6.1,资源中的版本号是1.0.0.1。这里要注意一点:Windows 6.1是Windows 7,Microsoft比较搞笑。我当前的系统是Windows Server 2008 AMD64,是Windows 6.0,就试着用另一台Visata AMD64的Wpc.dll替代这个,结果竟然正常运行。看来IE8 crash是这个Wpc.dll的问题。

    再检查一下Vista系统的Wpc.dll

    C:\Windows\SysWOW64>dir Wpc.dll
    2008/01/21  10:51           296,960 Wpc.dll
    C:\Windows\SysWOW64>dumpbin /headers Wpc.dll | grep -i version
    Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
                8.00 linker version
                6.00 operating system version
                6.00 image version
                6.00 subsystem version
                   0 Win32 version

    Vista系统的Wpc.dll资源中的版本号也是1.0.0.1。看来二者是用同一source code分别针对Vista和Win7做的不同的编译。

    同时将本系统的64位Wpc.dll恢复成Vista系统的,至此Green Browser/IE8下载又正常工作了。

    VirtualKD 2.3

    朋友很早向我推荐VirtualKD,说比windbg的模拟串口快很多。但直到最近我发现VirtualKD 2.3也出来了,便有了试试的想法。安装相当简单,先启动vmmon64.exe或vmmon.exe,然后启动VMware target OS,在target OS中安装“\target” 目录下的程序,系统会自动重启。在vmmon中设置好windbg/kd的路径后,vmmon会自动启动windbg/kd,相当方便。

    虽然windbg的模拟串口对我来说还够用,但试过VirtualKD之后,发现VirtualKD确实很贴心。呵呵!

    顺便测试了VMware 7.0.0-203739,VirutalKD 2.3已加入对VMware 7的支持。

    相关键接: http://sourceforge.net/projects/virtualkd http://virtualkd.sysprogs.org/

    Pages:  1 2