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.dll
和ntsdexts.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连接。如下图所示:
具体做法如下:
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。