调试笔记之侦查广告插件
朋友的电脑出了一个“怪”毛病,当使用资源管理器的时候,冷不丁会跳出一个非法访问对话框(其实就是应用程序错误对话框,也称GPF对话框),点击确定按钮关闭后,Explorer进程便会退出,然后重启,导致开始菜单和任务栏也消失片刻后再出现。
在把WinDBG设置为JIT调试器后,重现问题,于是WinDBG被自动唤起。崩溃现场的信息如下:
(680.c4c): Access violation - code c0000005 (first/second chance not available)
eax=00000000 ebx=00000000 ecx=00000003 edx=00000000 esi=01f1ad28 edi=00000000
eip=769c6239 esp=01f1a764 ebp=01f1a7cc iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ole32!CServerContextActivator::GetClassObject+0x11c:
769c6239 8b08 mov ecx,dword ptr [eax] ds:0023:00000000=????????
看来是典型的访问空指针,第一现场在ole32模块的CServerContextActivator::GetClassObject方法中。ole32是支撑COM技术的核心模块,像CoInitializeEx,CoCreateInstanceEx等重要的COM函数都是这个模块导出的。
从上面这个发生问题的函数下手来追查问题原因不是不可以,但是效率太低了,因为这个模块本身有问题的概率很小,所以还是先看一下栈回溯:
0:011> kn 100
# ChildEBP RetAddr
00 01f1a7cc 769c60fe ole32!CServerContextActivator::GetClassObject+0x11c
01 01f1a804 769c62e4 ole32!ActivationPropertiesIn::DelegateGetClassObject+0xf3
02 01f1a824 769c6290 ole32!CApartmentActivator::GetClassObject+0x4d
03 01f1a83c 769dddf4 ole32!CProcessActivator::GCOCallback+0x2b
04 01f1a85c 769dddab ole32!CProcessActivator::AttemptActivation+0x2c
05 01f1a894 769c6337 ole32!CProcessActivator::ActivateByContext+0x42
06 01f1a8bc 769c60fe ole32!CProcessActivator::GetClassObject+0x48
07 01f1a8f4 769c6118 ole32!ActivationPropertiesIn::DelegateGetClassObject+0xf3
08 01f1ab3c 769c60fe ole32!CClientContextActivator::GetClassObject+0x88
09 01f1ab74 769c5f92 ole32!ActivationPropertiesIn::DelegateGetClassObject+0xf3
0a 01f1b320 769c5e4b ole32!ICoGetClassObject+0x334
0b 01f1b34c 769c5dcd ole32!CComActivator::DoGetClassObject+0x93
0c 01f1b36c 75c77e99 ole32!CoGetClassObject+0x1b
0d 01f1b3d8 75c65433 urlmon!CProtMgr::FindFirstCF+0x11a
0e 01f1b3fc 75c655bb urlmon!COInetSession::FindFirstCF+0xb2
0f 01f1b488 75c6550d urlmon!COInetSession::CreateProtocolInfo+0x66
10 01f1b4a4 75c645fe urlmon!COInetSession::ParseUrl+0x1e
11 01f1b4ec 75c64ac0 urlmon!CoInternetGetSecurityUrl+0xad
12 01f1b9d4 75c64a36 urlmon!CSecurityManager::_MapUrlToZoneDirect+0x46
13 01f1ba0c 75c630de urlmon!CSecurityManager::MapUrlToZoneInternal+0xcf
14 01f1ba28 75f0c6b1 urlmon!CSecurityManager::MapUrlToZone+0x1a
15 01f1caac 75f141e6 browseui!CShellBrowser2::_GetTempZone+0x126
16 01f1cd24 75f1779c browseui!CShellBrowser2::_UpdateZonesPane+0x108
17 01f1cd84 77f52fb8 browseui!CShellBrowser2::Exec+0x132
18 01f1cda8 7e5693c2 shlwapi!IUnknown_Exec+0x3e
19 01f1cdcc 7e56bd4e shdocvw!CBaseBrowser2::_Exec_psbMixedZone+0x36
1a 01f1cde8 75ef860c shdocvw!CBaseBrowser2::_SwitchActivationNow+0x1c5
1b 01f1cdf4 75f0e58e browseui!CCommonBrowser::_SwitchActivationNow+0x14
1c 01f1ce08 7e56b9db browseui!CShellBrowser2::_SwitchActivationNow+0x10
1d 01f1de8c 75ef856f shdocvw!CBaseBrowser2::ActivatePendingView+0x17a
1e 01f1de98 75f14b30 browseui!CCommonBrowser::ActivatePendingView+0x14
1f 01f1dea8 7e56d4ee browseui!CShellBrowser2::ActivatePendingView+0x16
20 01f1dedc 7e56af14 shdocvw!CBaseBrowser2::_CreateNewShellView+0x27c
21 01f1df04 7e56ae80 shdocvw!CBaseBrowser2::_CreateNewShellViewPidl+0x46
22 01f1ef84 75ef85ef shdocvw!CBaseBrowser2::_NavigateToPidl+0x17c
23 01f1ef9c 75f0b894 browseui!CCommonBrowser::_NavigateToPidl+0x1d
24 01f1efc0 75f1331b browseui!CShellBrowser2::_NavigateToPidl+0x111
25 01f1f034 75f36d9f browseui!CShellBrowser2::OnCreate+0x49f
26 01f1f04c 7e56d337 browseui!CExplorerBrowser::OnCreate+0x13
27 01f1f068 75ef28c9 shdocvw!CBaseBrowser2::WndProcBS+0xf1
28 01f1f084 75f16d33 browseui!CCommonBrowser::WndProcBS+0x20
29 01f1f0c0 75f146a8 browseui!CShellBrowser2::WndProcBS+0x196
2a 01f1f0ec 77d18734 browseui!IEFrameWndProc+0xff
2b 01f1f118 77d1d05b user32!InternalCallWinProc+0x28
2c 01f1f180 77d1b4c0 user32!UserCallWinProcCheckWow+0xea
2d 01f1f1d4 77d1f9fe user32!DispatchClientMessage+0xa3
2e 01f1f204 7c92e473 user32!__fnINLPCREATESTRUCT+0x8b
2f 01f1f290 77d1fe13 ntdll!KiUserCallbackDispatcher+0x13
30 01f1f734 77d1fecc user32!NtUserCreateWindowEx+0xc
31 01f1f7e0 77d1fc58 user32!_CreateWindowEx+0x1ed
32 01f1f81c 77f5b2d1 user32!CreateWindowExW+0x33
33 01f1fc94 75f15073 shlwapi!CreateWindowExWrapW+0xc1
34 01f1ff20 75f15385 browseui!BrowserThreadProc+0x197
35 01f1ffb4 7c80b699 browseui!BrowserProtectedThreadProc+0x50
36 01f1ffec 00000000 kernel32!BaseThreadStart+0x37
这个栈回溯有五十多个栈帧,这样的深度在本地程序中不算浅了。首先应该看一下整个栈序列中有没有可疑的模块。如果想了解某个模块的详细信息,那么可以使用lmvm命令,比如:
0:011> lm vm urlmon
start end module name
75c60000 75cff000 urlmon (pdb symbols) d:\symbols\urlmon.pdb\9AE72F9177B7457E8FD6586279A7FF032\urlmon.pdb
Loaded symbol image file: urlmon.dll
Image path: C:\WINDOWS\system32\urlmon.dll
Image name: urlmon.dll
Timestamp: Wed Apr 29 08:51:19 2009 (49F7DCC7)
CheckSum: 0009A3C4
ImageSize: 0009F000
File version: 6.0.2900.3562
Product version: 6.0.2900.3562
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0804.04b0
CompanyName: Microsoft Corporation
ProductName: Microsoft(R) Windows(R) Operating System
InternalName: UrlMon.dll
OriginalFilename: UrlMon.dll
ProductVersion: 6.00.2900.3562
FileVersion: 6.00.2900.3562 (xpsp_sp2_gdr.090427-1232)
FileDescription: OLE32 Extensions for Win32
LegalCopyright: (C) Microsoft Corporation. All rights reserved.
这样看下来,没有发现异常的模块,所有模块都是Windows自身的。
长话短说,因为是发生在ole32模块中的,所以应该看一下是怎么进入到这个模块的。顺着栈帧向下排查,自然看到了CoGetClassObject函数,熟悉COM的朋友都知道这是一个公开的COM函数,用来创建COM对象。
HRESULT CoGetClassObject(
__in REFCLSID rclsid,
__in DWORD dwClsContext,
__in_opt COSERVERINFO pServerInfo,
__in REFIID riid,
__out LPVOID *ppv
);
因为我们没有私有符号,看不到函数参数的类型;但公开的函数可以查到函数原型,所以从公开的函数下手常常是一个捷径。
使用.frame命令切换到CoGetClassObject函数的栈帧:
0:011> .frame /c c
0c 01f1b36c 75c77e99 ole32!CoGetClassObject+0x1b
eax=00000000 ebx=01f1ad28 ecx=00000003 edx=00000000 esi=01f1ad28 edi=00000000
eip=769c5dcd esp=01f1b354 ebp=01f1b36c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
ole32!CoGetClassObject+0x1b:
769c5dcd 5d pop ebp
再执行kv命令显示包含参数的栈帧信息:
0:011> kv
*** Stack trace for last set context - .thread/.cxr resets it
ChildEBP RetAddr Args to Child
01f1b36c 75c77e99 01f1b434 00000001 00000000 ole32!CoGetClassObject+0x1b (FPO: [5,0,0])
第一个参数(01f1b434 )是REFCLSID类型的,这个类型没有导出,但是查一下头文件,就可以看到它是IID类型的引用:
#define REFCLSID const IID &
而IID类型是GUID的别名:
typedef GUID IID;
所以:
0:011> dt _GUID 01f1b434
msvcr71!_GUID
{bbca9f81-8f4f-11d2-90ff-0080c83d3571}
+0x000 Data1 : 0xbbca9f81
+0x004 Data2 : 0x8f4f
+0x006 Data3 : 0x11d2
+0x008 Data4 : [8] "???"
于是我们得到了出问题时要创建的COM对象的类ID。接下来可以通过注册表来查找这个类ID对应的模块和更多信息。
启动regedit,然后在如下注册表键下查找:
HKEY_CLASSES_ROOT\CLSID
于是便发现了这个CLSID的模块,名为wc98pp.dll,看看这个名字,便不是很善,看一下详细信息:
0:011> lmvm wc98pp
start end module name
02b50000 02b61000 wc98pp C (export symbols) wc98pp.dll
Loaded symbol image file: wc98pp.dll
Image path: C:\WINDOWS\wc98pp.dll
Image name: wc98pp.dll
Timestamp: Sat Jun 20 02:22:17 1992 (2A425E19)
CheckSum: 00000000
ImageSize: 00011000
File version: 0.0.0.0
Product version: 0.0.0.0
File flags: 0 (Mask 0)
File OS: 0 Unknown Base
File type: 0.0 Unknown
File date: 00000000.00000000
Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4
无版本,无厂家,无产品名称...
GOOGLE一下,果不其然!
删除它的注册表项,然后删除文件,问题消失了。