Windbg介绍

1. 介绍

1.1. 相关网站

微软网站

社区网站

调试分享

1.2. 下载

对于Win10,推荐使用微软商店下载windbg preview版本。其它的os,可以下载windows sdk,在安装选项中选择windbg。

preview版本皆可调试32,64位进程和内核。传统版本分为32和64两个版本,但优势在小巧便携。

1.3. 符号配置

微软的动态库,exe等,微软一般会公开pdb文件的下载,windbg经过配置后,会自动下载,从而利于解析程序的数据结构。

简易的方法是配置环境变量_NT_SYMBOL_PATH

srv*c:\mss*http://msdl.microsoft.com/download/symbols

这样,windbg会优先在c:\mss里寻找pdb,若没有,再去下载。

但是近来这个国内实际没法通过这个网站下载,可尝试使用镜像站(最近好像也不行了)

http://sym.ax2401.com:9999/symbols

或寻找一个可以科学上网的http代理(不能是sock5代理),然后配置环境变量_NT_SYMBOL_PROXY为代理地址,形如127.0.0.1:10809

以下演示一下:

a. 新建notepad进程。

b. windbg里attach到notepad进程

c. 输入!sym noisy使之展示符号载入细节

0:000> !sym noisy
noisy mode - symbol prompts on

d. 输入.reload /f ntdll.dll让windbg下载ntdll.pdb,如果看到

SYMSRV:  HTTPGET: /download/symbols/ntdll.pdb/54A4ABD98E75ECCB93B912D2747051301/ntdll.pdb
SYMSRV:  HttpSendRequest: 800C2EFD - ERROR_INTERNET_CANNOT_CONNECT

之类的提示,那一般就是网络不通,没法下载

2. 常用命令

windbg的命令分为标准命令,.命令和!命令。!命令实际上windbg的扩展插件里引入的,只不过有时候略写了插件名。

例如!kdexts.locks!ntsdexts.locks是不同的命令。在windbg目录里搜索kdext.dllntsdexts.dll能直接找到。

windbg里按下F1,即可检索各个命令,查看详情与示例

2.1. 常用的标准命令

命令|作用

-|-

d|查看内存数据

k|查看栈回溯

u|将地址反汇编为汇编代码

r|查看寄存器数据

~|切换线程(用户态)

dt|查看结构体的定义

b|下断点

lmf|查看各个模块的文件路径和符号载入

2.2. 常用的.命令有

命令|作用

-|-

.reload|载入pdb

.load|载入扩展插件

.process|切换到进程(内核态)

.thread|切换到(内核态)

.detach|脱离调试目标

.kill|关闭进程

2.3. 常用的!命令有

命令|作用

-|-

!process|罗列进程(内核态)

!running|展示cpu当前运行的线程(内核态)

!analyze|自动分析dmp

!handle|逆向handle

!fileobj|逆向文件对象

!locks|分析死锁

!poolused|展示内存池消耗(内核态)

!memusage|展示物理内存的统计信息(内核态)

3. 调试环境搭建

3.1. 实时调试进程

File->attach to process

如果使用传统版本windbg,注意调试32位进程使用32位windbg,64的则用64的。

3.2. 调试进程dump

进程dump分两种。一种是手工抓的,例如任务管理器里右击进程条目创建dump。另一种是崩溃后,windows帮我们抓的崩溃现场。简易的做法是用管理权限执行

procdump -i -ma c:\windows\temp

那么进程崩溃后,会在此目录下发现2个dmp文件。

windbg的file->Open dump file选择dmp文件打开。

3.3. 调试内核dump

内核dump分为mini,core,full三种。在windows蓝屏后,即生成。minidump通常位于C:\windows\minidump\,后两者则通常是C:\windows\memory.dmp文件。coredmp只包含内核层的数据。

coredump和fulldump需要事先增加一定的windows虚拟内存,否则可能会生成失败,参见《深入解析Windows操作系统》崩溃转储分析章节。在磁盘分区较满时也会在开机时自动删除,可通过注册表配置解决。

对于能预测到windows内核层卡死的情况,可事先配置注册表,卡死按下

右Ctrl+双击scrolllock,手工触发蓝屏。再从dump中来分析卡死当时的内核。

本小节配置参见蓝屏设置.zip

3.4. 配置可调试的内核

对于复杂的内核卡死的现象,还可以配置实时调试环境。以下仅述winxp,win7,win10在idv,vdi的内核调试环境的搭建。

被调试机一般称为server机,windbg所在机称为client机。

通用的方法是通过串口调试。host端给虚机windows提供模拟串口设备,windows配置以支持由串口调试。随后host将串口设备和socket listen端口相连接。调试机通过tcp连接上那个socket端口。调试机里创建模拟2个串口,1个连接socket端口,一个被windbg连接。如下图所示:

tbBVy9.jpg

具体做法如下:

a. 对于idv,编辑

/etc/vmmanger/vm.template.oa.xml

添加

<qemu:commandline>
    ……….
    <qemu:arg value="-serial"/>
    <qemu:arg value="tcp::9090,server,nowait"/>
  </qemu:commandline>

b. 对于vdi,在启动虚机前,先编辑

/etc/libvirt/rco_spice.xml

亦添加这段。tcp端口一般不能再用9090,通常都是早就被别人占了。

启动虚机后,再换回rco_spice.xml。否则会影响其它vdi虚机的启动。

c. 在虚机里用cmd执行

bcdedit /set debug on
bcdedit /set debugtype serial
bcdedit /set debugport 1
bcdedit /set baudrate 115200
bcdedit /set {bootmgr} displaybootmenu yes
bcdedit /timeout 2

这样虚机每次开机都会支持从串口发起调试。

d. 调试client端,安装虚拟串口工具,如Virtual Serial Port Driver。运行之,添加两个串口comX,comY。将二者连在一起。

e. client端,安装combytcp。左边写上host机的ip和端口,点击client。右边选择comX,baudrate选择115200。

f. 打开windbg,执行File->attach to kernel->填写comY和baudrate。点击OK

g. windbg提示开始载入符号,一般就表示连接成功。太久的话,可以试试按下Ctrl+Break是否有反应。

对于裸机装win10,可以使用usb端口调试或者网卡调试。usb端口调试需要物理端口支持。

网卡调试参阅https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/setting-up-a-network-debugging-connection。较简单,client和server之间直接进行网络连接。值得注意的是,此会把client机的Windows的网卡名字给了。

另外,如果server机开启了内核调试,即使用户态代码执行了汇编int 3,那么整个内核也会被暂停。可能是windows刻意如此设计。但是,对于使用者来说十分不便。甚至引发客户误报故障。

3.5. 远程调试进程

windbg旧版小巧便携,可以发送到客户现场。如果遇到用户态程序的疑难杂症,可以进行远程调试。大致是客户的Windows里开启windbg server端,连接目标程序。我们的windows开启windbg客户端,连接客户的windbg。

至于两个windbg的网络连接,可能需要内网穿透技术的支持。

a. 客户的Windows里运行dbgsrv.exe -t -tcp:port=5002

b. 我们的windows里运行windbg,选择File->Connect to Remote Stub Server。输入tcp:port=5002,server=XXX,然后attach to process,可以调试server机的进程。参见https://blog.csdn.net/xiaohua_de/article/details/78779434。

c. 可能需要防火墙放通相关端口,建议事先试验一下。

远程调试进程的方法有不少,之所以用这种,是因为pdb是用client的网络来下载的。毕竟客户现场的环境通常没法配代理下载pdb。

3.6. 调试donet进程或dump

首先许多donet程序文件,使用dotpeek可以很容易反编译得到源码。并且可以生成代码文件和pdb。

分析汇编代码当然较晦涩,如果Windbg能直接结合pdb和源码,那么分析过程就清晰得多。

分析donet部分线程还需要donet的相关dll文件,这样才能直接看到donet代码而不是汇编码。

以下以调试一个dump为例:

a. 将崩溃现场的donet%windir%\microsoft.net\framework\<.NET 版本>的相关dll复制到自机,需要sos.dll,mscordacwks.dll(需要改名mscordacwks_AAA_AAA_x.x.x.xxxx.dll

其中AAA是dump的bit位对应的(x86 or AMD64),x.x.x.x实际上就是mscordacwks.dll的版本号(可以从文件的property dialog中查到),比如2.0.50272.8763, 4.7.2114.0等。
)。或者自己也安装相同版本的donet,需要版本号严格相同。

c. 推荐使用旧版windbg。windbg打开dmp,配置source path,填入源码的所在路径。配置symbol file path,添加b所得的pdb。配置image file path,添加a,b中的exe,dll路径。

d. 输入

.load X:\Y\Z\sos.dll

.load X:\Y\Z\clr.dll

成功后输入!clrstack,可以看到clr的栈

0:000> !clrstack
OS Thread Id: 0x1b54 (0)
Child SP       IP Call Site
01b7dfb8 77802bec [InlinedCallFrame: 01b7dfb8] 
01b7dfb4 687f1c3c *** WARNING: Unable to verify checksum for System.Windows.Forms.ni.dll
DomainBoundILStubClass.IL_STUB_PInvoke(System.Runtime.InteropServices.HandleRef, System.String, System.String, Int32)
01b7dfb8 68903382 [InlinedCallFrame: 01b7dfb8] System.Windows.Forms.SafeNativeMethods.MessageBox(System.Runtime.InteropServices.HandleRef, System.String, System.String, Int32)
01b7dffc 68903382 System.Windows.Forms.MessageBox.ShowCore(System.Windows.Forms.IWin32Window, System.String, System.String, System.Windows.Forms.MessageBoxButtons, System.Windows.Forms.MessageBoxIcon, System.Windows.Forms.MessageBoxDefaultButton, System.Windows.Forms.MessageBoxOptions, Boolean)
01b7e000 68902fd3 [InlinedCallFrame: 01b7e000] 
01b7e088 68902fd3 System.Windows.Forms.MessageBox.Show(System.String, System.String, System.Windows.Forms.MessageBoxButtons, System.Windows.Forms.MessageBoxIcon)
01b7e098 08e2a46c AT.SoaFace.Core.Tools.Utility.MsgBox.ShowError(System.String)
01b7e0ac 08e2a257 AT.SoaFace.AppEngine.Program.ApplicationThreadException(System.Object, System.Threading.ThreadExceptionEventArgs)
01b7e0bc 68848c25 System.Windows.Forms.Application+ThreadContext.OnThreadException(System.Exception)
01b7e0f8 68852da7 System.Windows.Forms.Control.WndProcException(System.Exception)
01b7e104 68aa215f System.Windows.Forms.Control+ControlNativeWindow.OnThreadException(System.Exception)
01b7e108 6824764e System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)
01b7e3ec 01fad0ce [InlinedCallFrame: 01b7e3ec] 
01b7e3e8 682a302c DomainBoundILStubClass.IL_STUB_PInvoke(MSG ByRef)
01b7e3ec 68256ce1 [InlinedCallFrame: 01b7e3ec] System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG ByRef)
01b7e420 68256ce1 

但是kb看汇编就完全不一样了。

0:000> kb
ChildEBP RetAddr  Args to Child              
01b7dc58 7644870d 00070860 00000001 00000008 win32u!NtUserWaitMessage+0xc
01b7dc98 764485fd 00000008 00000001 00000000 user32!DialogBox2+0x103
01b7dcc8 764a2a08 00070860 764a0f30 01b7df08 user32!InternalDialogBox+0xe2
01b7dd8c 77803b3c 01b7def0 764a1897 01b7df08 user32!SoftModalMessageBox+0x728
01b7dd94 764a1897 01b7df08 21ff2084 00000000 win32u!NtUserModifyUserStartupInfoFlags+0xc
01b7df9c 687f1c3c 00070860 21ff2084 10bb7278 user32!MessageBoxWorker+0x2ca
01b7dfe8 68903382 00000010 00000000 00070860 System_Windows_Forms_ni+0x761c3c
01b7e068 68902fd3 00000000 00000000 00000000 System_Windows_Forms_ni+0x873382
01b7e0b4 68848c25 00000000 21ff1f78 03dda280 System_Windows_Forms_ni+0x872fd3
01b7e0f0 68852da7 21e1542c 01b7e218 68aa215f System_Windows_Forms_ni+0x7b8c25
01b7e0fc 68aa215f 6824764e 01b7e234 6b74c240 System_Windows_Forms_ni+0x7c2da7
01b7e24c 7646626b 00010880 00000202 00000000 System_Windows_Forms_ni+0xa1215f
01b7e278 764571bc 0680b23e 00010880 00000202 user32!_InternalCallWinProc+0x2b
01b7e35c 764562eb 0680b23e 00000000 00000202 user32!UserCallWinProcCheckWow+0x3ac
01b7e3d0 764560c0 01b7e458 01b7e418 682a302c user32!DispatchMessageWorker+0x21b
01b7e3dc 682a302c 01b7e458 fa8a32e1 6b66faf0 user32!DispatchMessageW+0x10
01b7e418 68256ce1 fa8a32e1 6b66faf0 01b7e540 System_Windows_Forms_ni+0x21302c
01b7e49c 682568f3 00000000 00000004 00000000 System_Windows_Forms_ni+0x1c6ce1
01b7e4f0 68256760 21e1c314 00000000 00000000 System_Windows_Forms_ni+0x1c68f3
01b7e51c 68847f87 21e1c314 21e695e4 000d059a System_Windows_Forms_ni+0x1c6760
01b7e534 6886dd92 fa8a32e1 6b66faf0 01b7e930 System_Windows_Forms_ni+0x7b7f87
01b7e604 6b66ebb6 21b75258 01d40008 01b7e668 System_Windows_Forms_ni+0x7ddd92
01b7e614 6b671e10 01b7e984 01b7e658 6b749b20 clr!CallDescrWorkerInternal+0x34
01b7e668 6b7fff7b fba4f215 00000002 00000000 clr!CallDescrWorkerWithHandler+0x6b
01b7e6a8 6b800055 21cac6a4 21b853d8 01b7e9c8 clr!CallDescrWorkerReflectionWrapper+0x55
01b7e9bc 6a5afbb1 00000000 1116acdc 6a5b1530 clr!RuntimeMethodHandle::InvokeMethod+0x84e

更多内容请参阅《格蠹汇编》第22,23章,参阅https://www.cnblogs.com/kissdodog/p/3731743.html。

3.7. 时间旅行调试

windbg preview才有的功能。时间行程调试是一种工具,可让你记录正在运行的进程的执行,然后在以后向前和向后重播。 旅行调试(TTD)可让您通过 “倒带” 调试器会话来更轻松地调试问题,而无需在发现 bug 之前重现问题。

参见https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/time-travel-debugging-overview

4. 崩溃卡死等调试示例

4.1. 死锁导致的应用卡死

故障现象

反复关开powerpnt.exe24小时,有机率出现powerpnt窗口已小时,但是进程仍然存留的问题。

分析

此时该进程的cpu并不高,因此不是相关线程陷入简单死循环。耗时良久,还是不能结束,一般来说也不是卡顿。推测可能是死锁,或者陷入内核不返回。

当然,也可以实时调试。本次是调试dump。

输入!locks分析死锁,得到

CritSec ntdll!LdrpLoaderLock+0 at 77a17340
WaiterWoken        No
LockCount          2
RecursionCount     1
OwningThread       b70
EntryCount         0
ContentionCount    8
*** Locked

CritSec GDI32!semLocal+0 at 774f9140
WaiterWoken        No
LockCount          1
RecursionCount     1
OwningThread       fc8
EntryCount         0
ContentionCount    1
*** Locked

可见有两个criticalsection类型正在被锁住。一个是77a17340,持有线程是b70。另一个是774f9140,持有线程是fc8。

输入!threads,得到线程概览

0:000> !thread
No export thread found
0:000> !threads
Index TID       TEB         StackBase	StackLimit  DeAlloc     StackSize ThreadProc
0	00000b70	0x7ffdf000	0x001b0000	0x001a2000	0x000b0000	0x0000e000	0x0
1	00000abc	0x7ffde000	0x02bd0000	0x02bcf000	0x02ad0000	0x00001000	0x0
2	00000784	0x7ffdd000	0x02cf0000	0x02cee000	0x02bf0000	0x00002000	0x0
3	00000fc8	0x7ffdc000	0x02fb0000	0x02fac000	0x02eb0000	0x00004000	0x0
4	00000218	0x7ffda000	0x03120000	0x0311f000	0x03020000	0x00001000	0x0
5	000004d0	0x7ffd9000	0x03790000	0x0378c000	0x03690000	0x00004000	0x0
Total VM consumed by thread stacks 0x0001a000

切换到b70,即0号线程,输入~0s

想看看0号线程正在做什么,为什么不释放cs77a17340。输入kv

0:000> kv
ChildEBP RetAddr  Args to Child              
001a6a40 77985e6c 7796fc72 00000268 00000000 ntdll!KiFastSystemCallRet (FPO: [0,0,0])
001a6a44 7796fc72 00000268 00000000 00000000 ntdll!NtWaitForSingleObject+0xc (FPO: [3,0,0])
001a6aa8 7796fb56 00000000 00000000 00000001 ntdll!RtlpWaitOnCriticalSection+0x13e (FPO: [Non-Fpo])
001a6ad0 774b7acf 774f9140 37010818 01570ae0 ntdll!RtlEnterCriticalSection+0x150 (FPO: [Non-Fpo])
001a6ae4 774b7a3d 37010818 001a6af4 00000010 GDI32!GetFontRealizationInfo+0x4e (FPO: [Non-Fpo])
001a6b0c 77021af7 37010818 001a6b20 37010818 GDI32!GdiRealizationInfo+0x1b (FPO: [Non-Fpo])
001a6b38 77021c26 37010818 000004e4 001a6c8c LPK!FontHasWesternScript+0x21 (FPO: [Non-Fpo])
001a6b48 774bd81c 37010818 72959818 00000001 LPK!LpkUseGDIWidthCache+0x91 (FPO: [Non-Fpo])
001a6c8c 774bd8d0 37010818 72959818 00000001 GDI32!GetTextExtentPointAInternal+0x134 (FPO: [Non-Fpo])
001a6ca8 729433c9 37010818 72959818 00000001 GDI32!GetTextExtentPointA+0x18 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
001a6ccc 7799316f 7799316f 72943218 72940000 MSVBVM60!EbLibraryLoad+0x170
001a6dd4 7799fd0d 00000000 00000000 6e7a981a ntdll!RtlpFreeHeap+0xb7a (FPO: [Non-Fpo])
001a6ef0 7799349f 779934ca 000001de 000001e8 ntdll!LdrpRunInitializeRoutines+0x24b (FPO: [Non-Fpo])
001a6fc4 75bfb8a4 003a7cd4 001a7004 001a6ff0 ntdll!RtlpAllocateHeap+0xe73 (FPO: [Non-Fpo])

猜测0号线程正在wait什么CriticalSection而未返回。先欲查看wait的是谁。ntdll!NtWaitForSingleObject,ntdll!RtlpWaitOnCriticalSection,ntdll!RtlEnterCriticalSection这三个API都不是公开的。windbg解析函数的实际入参也经常不准。因此不易判断。

API虽然不公开,但是ntdll可以使用IDA反汇编,我们可以大致知道他们的形参。

推测正在wait的cs是774f9140,因为它是ntdll!RtlEnterCriticalSection的参数。

这样,就说明0号线程被3号线程卡住。

接下来看看3号线程持有cs774f9140,但为什么不释放。

输入~3s切换到3号线程,查看栈回溯

0:003> kb
ChildEBP RetAddr  Args to Child              
02fae634 77985e6c 7796fc72 00000204 00000000 ntdll!KiFastSystemCallRet
02fae638 7796fc72 00000204 00000000 00000000 ntdll!NtWaitForSingleObject+0xc
02fae69c 7796fb56 00000000 00000000 00000001 ntdll!RtlpWaitOnCriticalSection+0x13e
02fae6c4 7799f6d0 77a17340 75397f2e 779870da ntdll!RtlEnterCriticalSection+0x150
02fae830 7799f5f9 02fae890 02fae85c 00000000 ntdll!LdrpLoadDll+0x287
02fae864 75bfb8a4 00340924 02fae8a4 02fae890 ntdll!LdrLoadDll+0x92
02fae89c 778b28c3 00000000 00000000 00000002 KERNELBASE!LoadLibraryExW+0x15a
02fae8b0 774c2cf5 774c31e0 00000000 774c2cbd kernel32!LoadLibraryW+0x11
02fae8bc 774c2cbd 00000000 015e83b8 00000000 GDI32!bLoadSpooler+0x24

同理,可以看到RtlEnterCriticalSection正在等待cs77a17340而未返回。

即0号,3号线程互相死锁。

更多地,还可以分析3号线程在进程退出的时候为什么还要LoadLibrary,load的是那个dll。可能对分析根因有帮助。

4.2. 陷入内核不返回的应用卡死

参见《格蠹汇编》第8章

4.3. 调试崩溃dump

进程崩溃一般都是由Windows异常触发。c++编译器实现异常实际上就是依赖于windows异常。本节简述Windows异常为异常。

Windows异常明细参见https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55。具体原理参阅《软件调试》第11章。

c语言从虽然从main函数开始编写,但是vc编译器实际上在main函数的上层编写了两层捕获异常的代码。如果异常走到这里,就会调用HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug的调试器接管。procdump自动捕获它程序崩溃的原理就是设置Aebug为procdump。因此procdump也会抓到两个dump。

4.3.1 简单异常

4.3.1.1 demo

试写一个访问null指针的程序崩溃:

void func1(int i) {
	char* p = NULL;
	*p = i;
}
void func2(int i) {	func1(i); }
void func3(int i) {	func2(i); }

int main() {
	func3(5);
	return 0;
}

windbg用!analyze -v分析崩溃原因

PROCESS_NAME:  throwexcept.exe

WRITE_ADDRESS:  00000000 

ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%p            0x%p                    %s

EXCEPTION_CODE_STR:  c0000005

EXCEPTION_PARAMETER1:  00000001

EXCEPTION_PARAMETER2:  00000000

STACK_TEXT:  
00cff7e4 0051102c 00000005 00cff7fc 0051104c throwexcept!func1+0x11
00cff7f0 0051104c 00000005 00cff808 0051106a throwexcept!func2+0xc
00cff7fc 0051106a 00000005 00cff850 00511241 throwexcept!func3+0xc
00cff808 00511241 00000001 00fff678 01000058 throwexcept!main+0xa
00cff850 76166359 00b3f000 76166340 00cff8bc throwexcept!__scrt_common_main_seh+0xfa
WARNING: Stack unwind information not available. Following frames may be wrong.
00cff860 77417c24 00b3f000 56a6e476 00000000 kernel32!BaseThreadInitThunk+0x19
00cff8bc 77417bf4 ffffffff 77438fea 00000000 ntdll!__RtlUserThreadStart+0x2f
00cff8cc 00000000 005112c9 00b3f000 00000000 ntdll!_RtlUserThreadStart+0x1b


SYMBOL_NAME:  throwexcept!func1+11

MODULE_NAME: throwexcept

IMAGE_NAME:  throwexcept.exe

可以看到崩溃原因,是因为触发了异常c0000005,即STATUS_ACCESS_VIOLATION。执行崩溃代码的模块是throwexcpet.exe,在0号线程的throwexcept!func1+11处。

所以要看看此处执行了什么代码导致访问违例。~0s切换到0号线程,用u命令查看这一段代码

0:000> u throwexcept!func1
throwexcept!func1:
00511000 55              push    ebp
00511001 8bec            mov     ebp,esp
00511003 51              push    ecx
00511004 c745fc00000000  mov     dword ptr [ebp-4],0
0051100b 8b45fc          mov     eax,dword ptr [ebp-4]
0051100e 8a4d08          mov     cl,byte ptr [ebp+8]
00511011 8808            mov     byte ptr [eax],cl
00511013 8be5            mov     esp,ebp

throwexcept!func1+11处代码为即00511011 8808 mov byte ptr [eax],cl,输入r rax,查看得知rax为0。

4.3.1.2 explorer无尽崩溃一例

故障现象

旧镜像正常。编辑镜像,升级guestttool重启后黑屏。按Ctrl+alt+del有反应。安全模式下正常。屏蔽Guestttool正常。

分析

按Ctrl+alt+del有反应说明不是卡死,也不是显示问题。猜测是explorer崩溃导致黑屏。一般explorer崩溃后自动被启动,猜测是无尽地崩溃,无尽的重启。

升级Guesttool后导致的问题,故而推测是Guesttool某个dll hook了explorer导致崩溃。

使用procdump收集崩溃dump。

windbg打开dump,输入!analyze -v自动分析

PROCESS_NAME:  explorer.exe
WRITE_ADDRESS:  0000000009910d48
ERROR_CODE: (NTSTATUS) 0xc0000005 - 0x%p            0x%p                    %s
EXCEPTION_CODE_STR:  c0000005
EXCEPTION_PARAMETER1:  0000000000000001
EXCEPTION_PARAMETER2:  0000000009910d48
IP_ON_HEAP:  0074007500420074
The fault address in not in any loaded module, please check your build's rebase
log at <releasedir>\bin\build_logs\timebuild\ntrebase.log for module which may
contain the address if it were loaded.

STACK_TEXT:  
00000000`09910d50 00000000`773c3c3f : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDispatchException+0x1d
00000000`09911430 000007fe`fd05bded : 00000000`00000000 00000000`09912238 00000000`0ba9557e 00000000`00000000 : ntdll!RtlRaiseException+0x22f
00000000`09911de0 000007fe`fd06802d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!RaiseException+0x39
00000000`09911eb0 000007fe`fd08ea06 : 00000000`099121a0 00000000`00000000 00000000`09912238 00000000`0000001d : KERNELBASE!OutputDebugStringA+0x6d
00000000`09912180 000007fe`fce1208e : 00000000`0bbdfdc0 00000000`099123e0 00000000`09912238 00000000`00000208 : KERNELBASE!OutputDebugStringW+0x66
00000000`099121d0 000007fe`fce12193 : 000007fe`fce45da0 00000000`c0000034 00000000`09912228 00000000`099123e0 : RCD_AppInit+0x208e
00000000`09912230 000007fe`fce12358 : 00000000`00000000 00000000`09912730 00000000`00000104 00000000`09912b00 : RCD_AppInit+0x2193
00000000`099122e0 000007fe`fce12744 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`09912e88 : RCD_AppInit+0x2358
00000000`09912970 00000000`772790e9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000030 : RCD_AppInit+0x2744
00000000`09912df0 000007fe`f4db1851 : 00000000`774a7ff0 00000000`00000000 00000000`09077b30 00000000`00000000 : kernel32!SetUnhandledExceptionFilter+0xc9
00000000`099130c0 00000000`773c4918 : 00000000`00000000 00000000`f3ae8b8a 00000000`774a7ff0 00000000`00000000 : baiducn+0x1851
00000000`099130f0 00000000`773c0002 : 00000000`772fe670 00000000`00000000 00000000`00000000 00000000`09077b20 : ntdll!RtlpCallVectoredHandlers+0xa8
00000000`09913160 00000000`773eb61e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDispatchException+0x22
00000000`09913840 00000000`773a186a : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!KiUserExceptionDispatch+0x2e
00000000`09913f40 00000000`773a009a : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlpDosPathNameToRelativeNtPathName_Ustr+0x2a
00000000`09914210 000007fe`fd055ee7 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus+0x6a
00000000`09914260 00000000`772b6c55 : 00000000`00000000 00000000`00000000 00000000`00000003 00000000`00000000 : KERNELBASE!CreateFileW+0xa7
00000000`099143c0 000007fe`fce12240 : 00000000`00000000 00000000`09914540 00000000`00000000 00000000`09914d10 : kernel32!FindFirstVolumeW+0x45
00000000`09914440 000007fe`fce12744 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`09914fe8 : RCD_AppInit+0x2240
00000000`09914ad0 00000000`772790e9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000030 : RCD_AppInit+0x2744
00000000`09914f50 000007fe`f4db1851 : 00000000`774a7ff0 00000000`7748aa00 00000000`09077b30 00000000`00000000 : kernel32!SetUnhandledExceptionFilter+0xc9
00000000`09915220 00000000`773c4918 : 00000000`00000000 00000000`f3ae8b8a 00000000`774a7ff0 00000000`00000000 : baiducn+0x1851
00000000`09915250 00000000`773c0002 : 00000000`0000000e 00000000`00000000 00000000`00000000 00000000`09077b20 : ntdll!RtlpCallVectoredHandlers+0xa8
00000000`099152c0 00000000`773c3c3f : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDispatchException+0x22
00000000`099159a0 000007fe`fd05bded : 00000000`00000000 00000000`099167a8 00000000`0ba9548e 00000000`00000000 : ntdll!RtlRaiseException+0x22f
00000000`09916350 000007fe`fd06802d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!RaiseException+0x39
00000000`09916420 000007fe`fd08ea06 : 00000000`09916710 00000000`00000000 00000000`099167a8 00000000`0000001d : KERNELBASE!OutputDebugStringA+0x6d
00000000`099166f0 000007fe`fce1208e : 00000000`0bbdfd60 00000000`09916950 00000000`099167a8 00000000`00000208 : KERNELBASE!OutputDebugStringW+0x66
00000000`09916740 000007fe`fce12193 : 000007fe`fce45da0 00000000`c0000034 00000000`09916798 00000000`09916950 : RCD_AppInit+0x208e
00000000`099167a0 000007fe`fce12358 : 00000000`00000000 00000000`09916ca0 00000000`00000104 00000000`09917100 : RCD_AppInit+0x2193
00000000`09916850 000007fe`fce12744 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`099173f8 : RCD_AppInit+0x2358
00000000`09916ee0 00000000`772790e9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000030 : RCD_AppInit+0x2744
00000000`09917360 000007fe`f4db1851 : 00000000`774a7ff0 00000000`7748aa00 00000000`09077b30 00000000`00000000 : kernel32!SetUnhandledExceptionFilter+0xc9
00000000`09917630 00000000`773c4918 : 00000000`00000000 00000000`f3ae8b8a 00000000`774a7ff0 00000000`00000000 : baiducn+0x1851
00000000`09917660 00000000`773c0002 : 00000000`0000000e 00000000`00000000 00000000`00000000 00000000`09077b20 : ntdll!RtlpCallVectoredHandlers+0xa8
00000000`099176d0 00000000`773c3c3f : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDispatchException+0x22
00000000`09917db0 000007fe`fd05bded : 00000000`00000000 00000000`09918bb8 00000000`0ba954be 00000000`00000000 : ntdll!RtlRaiseException+0x22f
00000000`09918760 000007fe`fd06802d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!RaiseException+0x39
00000000`09918830 000007fe`fd08ea06 : 00000000`09918b20 00000000`00000000 00000000`09918bb8 00000000`0000001d : KERNELBASE!OutputDebugStringA+0x6d
00000000`09918b00 000007fe`fce1208e : 00000000`0bbdfd00 00000000`09918d60 00000000`09918bb8 00000000`00000208 : KERNELBASE!OutputDebugStringW+0x66
00000000`09918b50 000007fe`fce12193 : 000007fe`fce45da0 00000000`c0000034 00000000`09918ba8 00000000`09918d60 : RCD_AppInit+0x208e
00000000`09918bb0 000007fe`fce12358 : 00000000`00000000 00000000`099190b0 00000000`00000104 00000000`09919500 : RCD_AppInit+0x2193
00000000`09918c60 000007fe`fce12744 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`09919808 : RCD_AppInit+0x2358
00000000`099192f0 00000000`772790e9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000030 : RCD_AppInit+0x2744
00000000`09919770 000007fe`f4db1851 : 00000000`774a7ff0 00000000`7748aa00 00000000`09077b30 00000000`00000000 : kernel32!SetUnhandledExceptionFilter+0xc9
00000000`09919a40 00000000`773c4918 : 00000000`00000000 00000000`f3ae8b8a 00000000`774a7ff0 00000000`00000000 : baiducn+0x1851
00000000`09919a70 00000000`773c0002 : 00000000`0000000e 00000000`00000000 00000000`00000000 00000000`09077b20 : ntdll!RtlpCallVectoredHandlers+0xa8
00000000`09919ae0 00000000`773c3c3f : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDispatchException+0x22
00000000`0991a1c0 000007fe`fd05bded : 00000000`00000000 00000000`0991afc8 00000000`0ba9542e 00000000`00000000 : ntdll!RtlRaiseException+0x22f
00000000`0991ab70 000007fe`fd06802d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!RaiseException+0x39
00000000`0991ac40 000007fe`fd08ea06 : 00000000`0991af30 00000000`00000000 00000000`0991afc8 00000000`0000001d : KERNELBASE!OutputDebugStringA+0x6d
00000000`0991af10 000007fe`fce1208e : 00000000`0bbdfca0 00000000`0991b170 00000000`0991afc8 00000000`00000208 : KERNELBASE!OutputDebugStringW+0x66
00000000`0991af60 000007fe`fce12193 : 000007fe`fce45da0 00000000`c0000034 00000000`0991afb8 00000000`0991b170 : RCD_AppInit+0x208e
00000000`0991afc0 000007fe`fce12358 : 00000000`00000000 00000000`0991b4c0 00000000`00000104 00000000`0991b900 : RCD_AppInit+0x2193
00000000`0991b070 000007fe`fce12744 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`0991bc18 : RCD_AppInit+0x2358
00000000`0991b700 00000000`772790e9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000030 : RCD_AppInit+0x2744
00000000`0991bb80 000007fe`f4db1851 : 00000000`774a7ff0 00000000`7748aa00 00000000`09077b30 00000000`00000000 : kernel32!SetUnhandledExceptionFilter+0xc9
00000000`0991be50 00000000`773c4918 : 00000000`00000000 00000000`f3ae8b8a 00000000`774a7ff0 00000000`00000000 : baiducn+0x1851
00000000`0991be80 00000000`773c0002 : 00000000`0000000e 00000000`00000000 00000000`00000000 00000000`09077b20 : ntdll!RtlpCallVectoredHandlers+0xa8
00000000`0991bef0 00000000`773c3c3f : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDispatchException+0x22
00000000`0991c5d0 000007fe`fd05bded : 00000000`00000000 00000000`0991d3d8 00000000`0ba9545e 00000000`00000000 : ntdll!RtlRaiseException+0x22f
00000000`0991cf80 000007fe`fd06802d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!RaiseException+0x39
00000000`0991d050 000007fe`fd08ea06 : 00000000`0991d340 00000000`00000000 00000000`0991d3d8 00000000`0000001d : KERNELBASE!OutputDebugStringA+0x6d
00000000`0991d320 000007fe`fce1208e : 00000000`0bbdfc40 00000000`0991d580 00000000`0991d3d8 00000000`00000208 : KERNELBASE!OutputDebugStringW+0x66
00000000`0991d370 000007fe`fce12193 : 000007fe`fce45da0 00000000`c0000034 00000000`0991d3c8 00000000`0991d580 : RCD_AppInit+0x208e
00000000`0991d3d0 000007fe`fce12358 : 00000000`00000000 00000000`0991d8d0 00000000`00000104 00000000`0991dd00 : RCD_AppInit+0x2193
00000000`0991d480 000007fe`fce12744 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`0991e028 : RCD_AppInit+0x2358
00000000`0991db10 00000000`772790e9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000030 : RCD_AppInit+0x2744
00000000`0991df90 000007fe`f4db1851 : 00000000`774a7ff0 00000000`7748aa00 00000000`09077b30 00000000`00000000 : kernel32!SetUnhandledExceptionFilter+0xc9
00000000`0991e260 00000000`773c4918 : 00000000`00000000 00000000`f3ae8b8a 00000000`774a7ff0 00000000`00000000 : baiducn+0x1851
00000000`0991e290 00000000`773c0002 : 00000000`0000000e 00000000`00000000 00000000`00000000 00000000`09077b20 : ntdll!RtlpCallVectoredHandlers+0xa8
00000000`0991e300 00000000`773c3c3f : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDispatchException+0x22
00000000`0991e9e0 000007fe`fd05bded : 00000000`00000000 00000000`0991f7e8 00000000`0ba953ce 00000000`00000000 : ntdll!RtlRaiseException+0x22f
00000000`0991f390 000007fe`fd06802d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!RaiseException+0x39
00000000`0991f460 000007fe`fd08ea06 : 00000000`0991f750 00000000`00000000 00000000`0991f7e8 00000000`0000001d : KERNELBASE!OutputDebugStringA+0x6d
00000000`0991f730 000007fe`fce1208e : 00000000`0bad1280 00000000`0991f990 00000000`0991f7e8 00000000`00000208 : KERNELBASE!OutputDebugStringW+0x66
00000000`0991f780 000007fe`fce12193 : 000007fe`fce45da0 00000000`c0000034 00000000`0991f7d8 00000000`0991f990 : RCD_AppInit+0x208e
00000000`0991f7e0 000007fe`fce12358 : 00000000`00000000 00000000`0991fce0 00000000`00000104 00000000`09920100 : RCD_AppInit+0x2193
00000000`0991f890 000007fe`fce12744 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`09920438 : RCD_AppInit+0x2358
00000000`0991ff20 00000000`772790e9 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000030 : RCD_AppInit+0x2744
00000000`099203a0 000007fe`f4db1851 : 00000000`774a7ff0 00000000`7748aa00 00000000`09077b30 00000000`00000000 : kernel32!SetUnhandledExceptionFilter+0xc9
00000000`09920670 00000000`773c4918 : 00000000`00000000 00000000`f3ae8b8a 00000000`774a7ff0 00000000`00000000 : baiducn+0x1851
00000000`099206a0 00000000`773c0002 : 00000000`0000000e 00000000`00000000 00000000`00000000 00000000`09077b20 : ntdll!RtlpCallVectoredHandlers+0xa8
00000000`09920710 00000000`773c3c3f : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlDispatchException+0x22
00000000`09920df0 000007fe`fd05bded : 00000000`00000000 00000000`09921bf8 00000000`0ba9536e 00000000`00000000 : ntdll!RtlRaiseException+0x22f
00000000`099217a0 000007fe`fd06802d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNELBASE!RaiseException+0x39
00000000`09921870 000007fe`fd08ea06 : 00000000`09921b60 00000000`00000000 00000000`09921bf8 00000000`0000001d : KERNELBASE!OutputDebugStringA+0x6d

同样是c0000005异常,在往0000000009910d48写数据的时候访问违例。代码发生在ntdll!RtlDispatchException+0x1d。当然Windows自己的bug还是比较少的,不像是ntdll的bug。看看别的疑点。

从崩溃处的栈回溯来看,似乎若干栈帧似乎是在不停循环,像是栈溢出了,即栈不停的增长,最后试图增长到9910d48时,由于这个地址不可写,或者不属于栈区,因此崩溃了。用!address看看该进程内存分布

+        0`098d0000        0`09910000        0`00040000             MEM_FREE    PAGE_NOACCESS                      Free       
+        0`09910000        0`09911000        0`00001000 MEM_PRIVATE MEM_RESERVE                                    Stack      [~35; 73c.d2c]
         0`09911000        0`09990000        0`0007f000 MEM_PRIVATE MEM_COMMIT  PAGE_READWRITE                     Stack      [~35; 73c.d2c]

9910d48虽然在栈区,但是似乎没有可读写的属性。表明上原因找到了。

继续看为什么栈帧在循环。KERNELBASE!RaiseException表明此处抛出了异常,kernel32!SetUnhandledExceptionFilter表明windows程序框架会去SetUnhandledExceptionFilter里寻找异常接受者。KERNELBASE!OutputDebugStringA内部实现里其实就是抛出异常0x40010006。但是一般这个异常是被忽略的。猜测这个异常被baiducn.dll给接管了,但是它没有处理好。

至于appinit为什么要调用OutputDebugString,这也是一个问题。毕竟发布版本,也没人接受输出日志。

4.3.2 异常

4.3.2.1 demo

写一个无人接受c++异常而崩溃的demo

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <process.h>
void func1(int i) {	throw i; }
void func2(int i) {	func1(i); }
unsigned __stdcall func3(void *) {
	int i = 9;
	func2(i);
	return 0;
}
int main() {
	_beginthreadex(nullptr, 0, func3, nullptr, 0, null);
	Sleep(10000);
	return 0;
}

windbg打开dmp,输入!analyze -v自动分析

CONTEXT:  013df940 -- (.cxr 0x13df940)
eax=013dfe20 ebx=19930520 ecx=00000003 edx=00000000 esi=006510c0 edi=006525d4
eip=75864402 esp=013dfe20 ebp=013dfe7c iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
KERNELBASE!RaiseException+0x62:
75864402 8b4c2454        mov     ecx,dword ptr [esp+54h] ss:002b:013dfe74=bcc88d5b
Resetting default scope

EXCEPTION_RECORD:  013df8f0 -- (.exr 0x13df8f0)
ExceptionAddress: 75864402 (KERNELBASE!RaiseException+0x00000062)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 3
   Parameter[0]: 19930520
   Parameter[1]: 013dfebc
   Parameter[2]: 006525d4
  pExceptionObject: 013dfebc
  _s_ThrowInfo    : 006525d4
  Type            : int

PROCESS_NAME:  throwexcept.exe

ERROR_CODE: (NTSTATUS) 0xc0000409 - <Unable to get error code text>

EXCEPTION_CODE_STR:  c0000409

EXCEPTION_PARAMETER1:  00000007

STACK_TEXT:  
013dfe7c 748d7a46 e06d7363 00000001 00000003 KERNELBASE!RaiseException+0x62
WARNING: Stack unwind information not available. Following frames may be wrong.
013dfeac 00651098 013dfebc 006525d4 00000009 VCRUNTIME140!CxxThrowException+0x66
013dfec0 006510ac 00000009 013dfedc 006510e1 throwexcept!func1+0x18
013dfecc 006510e1 00000009 00000009 013dff14 throwexcept!func2+0xc
013dfedc 7556248f 00000000 8f07697f 75562450 throwexcept!func3+0x21
013dff14 76166359 010e3a80 76166340 013dff80 ucrtbase!thread_start<unsigned int (__stdcall*)(void *),1>+0x3f
013dff24 77417c24 010e3a80 57ab392c 00000000 kernel32!BaseThreadInitThunk+0x19
013dff80 77417bf4 ffffffff 77438fd1 00000000 ntdll!__RtlUserThreadStart+0x2f
013dff90 00000000 75562450 010e3a80 00000000 ntdll!_RtlUserThreadStart+0x1b


SYMBOL_NAME:  vcruntime140!CxxThrowException+66

MODULE_NAME: VCRUNTIME140

IMAGE_NAME:  VCRUNTIME140.dll

STACK_COMMAND:  .cxr 0x13df940 ; kb

FAILURE_BUCKET_ID:  FAIL_FAST_FATAL_APP_EXIT_c0000409_VCRUNTIME140.dll!CxxThrowException

4.4. 死锁导致的windows卡死

故障现象

虚机刚开机展示桌面过程中卡死,Ctrl+alt+del无反应。一般持续30-60min后恢复。非必现,反复重启复现频率大约1/30。

分析

无法呼出任务管理器,当作Windows卡死处理。Ctrl+ScrollLock蓝屏,或直接Windows内核调试观测。

Windows内核调试时,输入running观察每个cpu正在跑的线程,看看是否有某些线程长期占用cpu。结果并无。

输入!locks分析死锁。大约半小时后分析得到:

2: kd> !locks
**** DUMP OF ALL RESOURCE OBJECTS ****
KD: Scanning for held locks...

Resource @ 0xfffff8800128efa8    Exclusively owned
    Contention Count = 2
     Threads: fffffa800c776660-01<*> 
KD: Scanning for held locks.........................

Resource @ 0xfffffa800e52c880    Exclusively owned
    Contention Count = 67
    NumberOfSharedWaiters = 13
     Threads: fffffa800f09e540-02<*> fffffa800c89e340-01    fffffa800d6b2b50-01    fffffa800cc76b50-01    
              fffffa800c8a5060-01    fffffa800cbd0060-01    fffffa800ee2b060-01    fffffa800ec13650-01    
              fffffa800f241930-01    fffffa800efa3640-01    fffffa800f1fd060-01    fffffa800f71e060-01    
              fffffa800f515a20-01    fffffa800c776660-01    
KD: Scanning for held locks...........................................................................................................

Resource @ 0xfffffa800f175050    Exclusively owned
    Contention Count = 8
     Threads: fffffa800f09e540-01<*> 
KD: Scanning for held locks..........

Resource @ 0xfffffa800f22d2a8    Exclusively owned
     Threads: fffffa800f09e540-01<*> 
KD: Scanning for held locks.....................................................................................................................
8342 total locks, 4 locks currently held

这里主要观察Exclusively Resource。其中Resource 0xfffffa800e52c880甚为异常,一个线程持有,13个线程等待。用!locks -v [addr]查看该资源和14个相关线程的明细。

2: kd> !locks -v 0xfffffa800e52c880    

Resource @ 0xfffffa800e52c880    Exclusively owned
    Contention Count = 67
    NumberOfSharedWaiters = 13
     Threads: fffffa800f09e540-02<*> 

     THREAD fffffa800f09e540  Cid 0cd8.0cf0  Teb: 000007fffffd9000 Win32Thread: 0000000000000000 WAIT: (Executive) KernelMode Alertable
         fffffa800f703fd8  NotificationEvent
         fffffa800f703f98  Semaphore Limit 0x7fffffff
     IRP List:
         fffffa800ecf1c60: (0006,03a0) Flags: 00060043  Mdl: fffffa800cc7f440
         fffffa800cbbea20: (0006,03a0) Flags: 00060901  Mdl: 00000000
     Not impersonating
     DeviceMap                 fffff8a000008b30
     Owning Process            fffffa800f5ef060       Image:         vds.exe
     Attached Process          N/A            Image:         N/A
     Wait Start TickCount      167828         Ticks: 3611 (0:00:00:56.421)
     Context Switch Count      234            IdealProcessor: 0             
     UserTime                  00:00:00.000
     KernelTime                00:00:00.015
     Win32 Start Address 0x000000007700f6f0
     Stack Init fffff8800526bfb0 Current fffff8800526b510
     Base fffff8800526c000 Limit fffff88005266000 Call 0000000000000000
     Priority 14 BasePriority 8 PriorityDecrement 80 IoPriority 2 PagePriority 5
     Child-SP          RetAddr           Call Site
     fffff880`0526b550 fffff800`04ec1772 nt!KiSwapContext+0x7a
     fffff880`0526b690 fffff800`04ec0c8a nt!KiCommitThreadWait+0x1d2
     fffff880`0526b720 fffff800`051583ec nt!KeWaitForMultipleObjects+0x272
     fffff880`0526b9e0 fffff880`00c0dde5 nt!FsRtlCancellableWaitForMultipleObjects+0xac
     fffff880`0526ba40 00000000`00000000 0xfffff880`00c0dde5

fffffa800c89e340-01    

     THREAD fffffa800c89e340  Cid 05c8.053c  Teb: 000000007eee9000 Win32Thread: 0000000000000000 WAIT: (WrResource) KernelMode Non-Alertable
         fffffa800e499840  Semaphore Limit 0x7fffffff
     IRP List:
         fffffa800c8ab010: (0006,03a0) Flags: 00060000  Mdl: 00000000
         fffffa800c8aaa30: (0006,03a0) Flags: 00060000  Mdl: 00000000
         fffffa800c8a8010: (0006,03a0) Flags: 00060000  Mdl: 00000000
         fffffa800c8a4340: (0006,03a0) Flags: 00060000  Mdl: 00000000
     Not impersonating
     DeviceMap                 fffff8a000d516e0
     Owning Process            fffffa800f04cb10       Image:         360EntClient.exe
...
...

持有线程fffffa800f09e540属于进程vds.exe。它有两个irp。分析其中一个!irp [addr]

2: kd> !irp fffffa800cbbea20
Irp is active with 9 stacks 9 is current (= 0xfffffa800cbbed30)
 No Mdl: No System Buffer: Thread fffffa800f09e540:  Irp stack trace.  
     cmd  flg cl Device   File     Completion-Context
 [N/A(0), N/A(0)]
            0  0 00000000 00000000 00000000-00000000    

			Args: 00000000 00000000 00000000 00000000
 [N/A(0), N/A(0)]
            0  0 00000000 00000000 00000000-00000000    

			Args: 00000000 00000000 00000000 00000000
 [N/A(0), N/A(0)]
            0  0 00000000 00000000 00000000-00000000    

			Args: 00000000 00000000 00000000 00000000
 [N/A(0), N/A(0)]
            0  0 00000000 00000000 00000000-00000000    

			Args: 00000000 00000000 00000000 00000000
 [N/A(0), N/A(0)]
            0  0 00000000 00000000 00000000-00000000    

			Args: 00000000 00000000 00000000 00000000
 [N/A(0), N/A(0)]
            0  0 00000000 00000000 00000000-00000000    

			Args: 00000000 00000000 00000000 00000000
 [N/A(0), N/A(0)]
            0  0 00000000 00000000 00000000-00000000    

			Args: 00000000 00000000 00000000 00000000
 [N/A(0), N/A(0)]
            0  0 00000000 00000000 00000000-00000000    

			Args: 00000000 00000000 00000000 00000000
>[IRP_MJ_READ(3), N/A(0)]
            0  0 fffffa800e52c030 fffffa800f59c070 00000000-00000000    
	       \FileSystem\Ntfs
			Args: 00000200 00000000 00000000 00000000

未果。

终止vds.exe.kill fffffa800f5ef060即可立刻恢复正常。这里确实不知vds在等待什么,亦或vds自己的bug,亦或别人的程序不释放资源,使得vds卡死。

vds.exe即Windows服务Virtual Disk。它因为Guesttool的某个diskpart调用而启动。

4.5. 疑似内存占用高导致的Windows卡死

Windows管理内存原理待研究……。以下试举一例

故障现象

虚机Windows7不定期卡死。难手工复现,但3-5天总会出现一例。

分析

故障时,host机查看qemu进程cpu消耗并不高。不像是内核层的死循环问题。windbg分析dump文件,!locks也没有Exclusively Resource。查看内存时,发现占用特别高。

先用!memusage查看物理内存的使用和各个进程的消耗。

1: kd> !memusage
 loading PFN database
loading (100% complete)
Compiling memory usage data (99% Complete).
             Zeroed:        9 (      36 kb)
               Free:        2 (       8 kb)
            Standby:     4437 (   17748 kb)
           Modified:      839 (    3356 kb)
    ModifiedNoWrite:      135 (     540 kb)
       Active/Valid:   752253 ( 3009012 kb)
         Transition:    17266 (   69064 kb)
         SLIST/Temp:        0 (       0 kb)
                Bad:        0 (       0 kb)
            Unknown:        0 (       0 kb)
              TOTAL:   774941 ( 3099764 kb)

Dangling Yes Commit:    17246 (   68984 kb)
 Dangling No Commit:   638353 ( 2553412 kb)
  Building kernel map
  Finished building kernel map
Scanning PFN database - (100% complete) 

  Usage Summary (in Kb):
Control       Valid Standby Dirty Shared Locked PageTables  name
ffffffffffffd  1644      0     0     0  1644     0    AWE
fffffa80023d9b80     0     52     0     0     0     0  mapped_file( Can't read file name buffer at fffff8a007375ec0 )
...
   6762c     4      0     0     0     4     0   Page File Section
--------   204    128     0 ----- -----   128  session 0 fffff88005745000
--------  3696      0     0 ----- -----   260  session 1 fffff8800576d000
--------    24      0     0 ----- -----    24  process ( System ) fffffa8002445040
--------    32     36     0 ----- -----    68  process ( csrss.exe ) fffffa800244f060
--------    28     28     0 ----- -----    48  process ( wininit.exe ) fffffa80024545c0
--------     4      0     0 ----- -----     4  process ( iexplore.exe ) fffffa8002625aa0
--------     4      0     0 ----- -----     4  process ( iexplore.exe ) fffffa8002a4f060
--------    20      0     0 ----- -----    20  process ( LogonUI.exe ) fffffa8002c207b0
--------     4      0     0 ----- -----     4  process ( QQExternal.exe ) fffffa8002ff5060
--------     4      0     0 ----- -----     4  process ( QQExternal.exe ) fffffa80030a7b00
--------    32      0     0 ----- -----    32  process ( LogonUI.exe ) fffffa80031691f0
...

这里物理内存仅剩8kb。再用!poolused查看内存池的占用,

1: kd> !poolused 4
....
 Sorting by Paged Pool Consumed

               NonPaged                  Paged
 Tag     Allocs         Used     Allocs         Used

 fpRD         0            0      21339   3233968128	Compressed file large read buffer , Binary: wof.sys
 fprd         0            0      65114   2133655552	Compressed file small read buffer , Binary: wof.sys
 CM31         0            0      20994    119910400	Internal Configuration manager allocations , Binary: nt!cm
 CM25         0            0       3369     15839232	Internal Configuration manager allocations , Binary: nt!cm
...
1: kd> !poolused 2
....
 Sorting by NonPaged Pool Consumed

               NonPaged                  Paged
 Tag     Allocs         Used     Allocs         Used

 FMic     34737     53344128          0            0	IRP_CTRL structure , Binary: fltmgr.sys
 Irp      35838     33702832          0            0	Io, IRP packets 
 Cont      2397     10666272          0            0	Contiguous physical memory allocations for device drivers 
 Mdl      18614      6753040          0            0	Io, Mdls 
 MmIn     17255      6625920          0            0	Mm inpaged io structures , Binary: nt!mm
 EtwB       101      4259840          2       131072	Etw Buffer , Binary: nt!etw
 fpcx     17248      4139520          0            0	Compressed file IO context , Binary: wof.sys
 Pool         5      3315280          0            0	Pool tables, etc. 
 ...

可以看到无论时分页还是非分页的池内存,wof.sys占用量都是非常靠前的。

重做镜像,不再引入wof.sys后不再出现此问题。

4.6. 蓝屏

蓝屏的关键信息一般是一个错误码加上4个参数,我们照Windows文档指南进行排查。

以下举一例

故障现象

编辑镜像原正常,一旦安装radmin软件,重启后则蓝屏。蓝屏频率90%。

分析

与radmin有关。推测是guesttool安装的驱动和radmin冲突有关。windbg打开dmp后,输入!analyze -v分析,亦可直接用’.bugcheck’获知蓝屏的错误码和4个参数。

2: kd> .bugcheck
Bugcheck code 0000001E
Arguments ffffffff`c0000096 fffff804`307c9bbe 00000000`00000000 00000000`00000000

windbg里按F1打开帮助文档,索引里搜索Bug Check 0x1E

Bug Check 0x1E: KMODE_EXCEPTION_NOT_HANDLED

The KMODE_EXCEPTION_NOT_HANDLED bug check has a value of 0x0000001E. This indicates that a kernel-mode program generated an exception which the error handler did not catch.

Important This topic is for programmers. If you are a customer who has received a blue screen error code while using your computer, see Troubleshoot blue screen errors.

KMODE_EXCEPTION_NOT_HANDLED Parameters

The following parameters are displayed on the blue screen.

Parameter	Description
1			The exception code that was not handled
2			The address at which the exception occurred
3			Parameter 0 of the exception
4			Parameter 1 of the exception

主要看第二参数,即异常发生的地址。反汇编该地址

2: kd> u fffff804`307c9bbe
nt!KiSaveDebugRegisterState+0x8e:
fffff804`307c9bbe 0f32	rdmsr
...

rdmsr是个很简单的寄存器内数值搬运的动作。蓝屏的原因是因为虚拟化不支持msr寄存器。

4.7. 逆向api的实际入参

64位程序的函数传参,从左至右分别通过rcx,rdx,r8,r9,rsp+20h,rsp+28h…来传参。32位程序较复杂,有stdcall,cdecl,fastcall等。我们用windbg断点时,能分析出当时所调用的实际参数。以下举一例。

通过process monitor监控注册表等手段,猜测某项功能最终调用的是SetDisplayConfig,但是涉及相关概念复杂,API文档不易读懂。

LONG SetDisplayConfig(
  UINT32                  numPathArrayElements,
  DISPLAYCONFIG_PATH_INFO *pathArray,
  UINT32                  numModeInfoArrayElements,
  DISPLAYCONFIG_MODE_INFO *modeInfoArray,
  UINT32                  flags
);

而且指针数组所指向的多个结构体变量也不易逆向分析。API MONITOR也没有记录此api(api monitor其实可以添加此api)。

windbg菜单处点击file->attach to process,选择目标程序,给SetDisplayConfig下断点。最好带上它所属的动态库名

bp User32!SetDisplayConfig。函数名大小写严格,动态库名无所谓。

bl罗列断点,e表示断点有效。bd命令来disable断点。

0:016> bl
0 e Disable Clear  00007ff8`c7413400     0001 (0001)  0:**** USER32!SetDisplayConfig

而后运行目标程序,直到断点触发。由于这是64位程序,所以直接用r命令看寄存器即可知前4个参数的值。

0:000> r
rax=0000000000000000 rbx=0000005a7ebfd450 rcx=0000000000000001
rdx=000001cd7eec1e60 rsi=000001cd7a16de10 rdi=000001cd7a16de20
rip=00007ff8c7413400 rsp=0000005a7ebfba18 rbp=0000005a7ebfbb20
 r8=0000000000000003  r9=000001cd78f6add0 r10=0000000000000000
r11=0000005a7ebfb7c0 r12=0000005a7ebfcfa4 r13=0000000000000001
r14=0000005a7ebfd450 r15=000001cd7a16dda0
...

参数2和4是结构体数组。以第4个参数为例,结构体的数量为r8即3,三个结构体的地址是分别是1cd78f6add0,1cd78f6add8,1cd78f6ade0。

得知结构体指针后,用dt命令参看结构体成员。由于事先不知道此结构体DISPLAYCONFIG_MODE_INFO的符号属于哪个pdb,事先需要用.reload /f载入所有的pdb。再用dt命令参看。

0:000> dt wintypes!DISPLAYCONFIG_MODE_INFO 000001cd`78f6ade0
   +0x000 infoType         : 0x8d7aaa0 (No matching name)
   +0x004 id               : 0
   +0x008 adapterId        : _LUID
   +0x010 targetMode       : DISPLAYCONFIG_TARGET_MODE
   +0x010 sourceMode       : DISPLAYCONFIG_SOURCE_MODE
   +0x010 desktopImageInfo : DISPLAYCONFIG_DESKTOP_IMAGE_INFO

windbg preview里可以直接点击,查看具体的成员。例如点击adpterId可以直接查看adapterId字段的具体成员

0:000> dx -r1 (*((wintypes!_LUID *)0x1cd78f6ade8))
(*((wintypes!_LUID *)0x1cd78f6ade8))                 [Type: _LUID]
    [+0x000] LowPart          : 0x107ac [Type: unsigned long]
    [+0x004] HighPart         : 1 [Type: long]

参数5在栈上,一般是当前帧的RetAddr位置+28h。使用dps rsp查看栈上数据

0:000> dps rsp
0000005a`7ebfba18  00007ff8`9e66c313 igfxDI!DllUnregisterServer+0x17af3
0000005a`7ebfba20  0000005a`7ebfd450
0000005a`7ebfba28  0000005a`7ebfbb20
0000005a`7ebfba30  000001cd`7a16de10
0000005a`7ebfba38  000001cd`7a16dda0
0000005a`7ebfba40  0000005a`000082a0
0000005a`7ebfba48  0000005a`7ebfba01
...

即参数5存储在0000005a7ebfba40上。由于参数5是UINT32类型,故而使用dd来查看

0:000> dd 0000005a`7ebfba40
0000005a`7ebfba40  000082a0 0000005a 7ebfba01 0000005a
...

参数5为0x82a0。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章