360网盾 广告拦截模块崩溃分析
声明:此Bug不是本人发现的,所做的工作是对Bug成因进行分析,分析的过程中自己学到了很多东西,
在此感谢提供此Bug的坛友:
xuecniao ,是你让我有了这次锻炼的机会。
分析过程是逆向分析的,希望能和大家多多交流。其中有错误之处欢迎指正。
1.分析环境:
Windwos XP SP3_CN(虚拟机)
360安全卫士8.7.2002(最新版)
备用木马库版本:2012.11.3.1
工具:
IDA 5.5
Windbg
OD
2.分析过程
样本代码如下:
xp3系统,ie8,360安全卫士-7.6.0.1130
<A href="file:///c:\windows\system32\BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB">按一下</A>
<a> 标签的 href 属性用于指定超链接目标的 URL。
首先这是一个恶意的URL链接,正常情况下IE处理这个请求但不会崩溃,开启360安全卫士以后,IE崩溃,使用Windbg调试,发现以下地址报错
65448f26 8b4c2404 mov ecx,dword ptr [esp+4]
65448f2a 66833900 cmp word ptr [ecx],0 ds:0023:00000000=????
65448f2e 8d4102 lea eax,[ecx+2]
65448f31 740a je Adfilter!OnTrayMsg+0x2119b (65448f3d)
0:008> r
eax=00000000 ebx=00000000 ecx=00000000 edx=000000c0 esi=0211e354 edi=02b24c14
eip=65448f2a esp=0211e170 ebp=0211e1ac iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
Adfilter!OnTrayMsg+0x21188:
65448f2a 66833900 cmp word ptr [ecx],0 ds:0023:00000000=????
ecx为0,自然程序在读取[eax]的内容,触发一个内存不可读的异常,IE发生崩溃。
查看这个Adfilter的相关信息:
0:008> !lmi Adfilter
Loaded Module Info: [adfilter]
Module: Adfilter
Base Address: 65400000
Image Name: C:\Program Files\360\360safe\safemon\Adfilter.dll
Machine Type: 332 (I386)
Time Stamp: 4f558bc0 Tue Mar 06 12:00:00 2012
Size: ab000
CheckSum: b1275
Characteristics: 210e
Debug Data Dirs: Type Size VA Pointer
CODEVIEW 45, 0, a8000 [Debug data not mapped] - Can't validate symbols, if present.
Image Type: FILE - Image read successfully from debugger.
C:\Program Files\360\360safe\safemon\Adfilter.dll
Symbol Type: EXPORT - PDB not found
Load Report: export symbols
确认是360网盾的一个DLL文件,并且DLL文件属性信息可以查看到“360网盾 广告拦截模块”,
使用IDA加载Adfilter.dll进行静态分析,跳转到崩溃代码,发现如下信息:
.text:65448F26 ; =============== S U B R O U T I N E =======================================
.text:65448F26
.text:65448F26 ; Attributes: library function
.text:65448F26
.text:65448F26 ; size_t __cdecl wcslen(const wchar_t *Str)
.text:65448F26 _wcslen proc near ; CODE XREF: sub_654012C5+61p
.text:65448F26 ; sub_65402AB1+D4p ...
.text:65448F26
.text:65448F26 Str = dword ptr 4
.text:65448F26
.text:65448F26 mov ecx, [esp+Str]
.text:65448F2A cmp word ptr [ecx], 0
.text:65448F2E lea eax, [ecx+2]
.text:65448F31 jz short loc_65448F3D
.text:65448F33
.text:65448F33 loc_65448F33: ; CODE XREF: _wcslen+15j
.text:65448F33 mov dx, [eax]
.text:65448F36 inc eax
.text:65448F37 inc eax
.text:65448F38 test dx, dx
.text:65448F3B jnz short loc_65448F33
.text:65448F3D
.text:65448F3D loc_65448F3D: ; CODE XREF: _wcslen+Bj
.text:65448F3D sub eax, ecx
.text:65448F3F sar eax, 1
.text:65448F41 dec eax
.text:65448F42 retn
.text:65448F42 _wcslen endp
显然IDA已经把函数名提示出来:wcslen(const wchar_t *Str),查看参数可以发现,只有一个参数,为wchar_t 型的字符串指针,
反汇编中,ecx是一个字符串指针,指向一串字符,而在程序中,
65448f26 8b4c2404 mov ecx,dword ptr [esp+4]
ecx为空,即空指针,空指针引用出错导致的IE崩溃。
使用VC代码进行演示:
#include <Windows.h>
int _tmain(int argc, _TCHAR* argv[])
{
WCHAR* p = NULL;
wcslen(p);
return 0;
}
上述代码肯定会触发一个异常。
现在为止,知道了是什么地方导致的IE崩溃,但是传递给wcslen()函数空指针的原因何在,之前为什么没有检测到空指针,这些疑问需要解决,
分析的过程中也绕了一个弯路,以下是绕的弯路的分析过程:
从00d615e4 函数处调用Adfilter.dll中代码
Breakpoint 0 hit
eax=65461620 ebx=00175c90 ecx=00000001 edx=000000c0 esi=0211e260 edi=02b24bf8
eip=00d615e4 esp=0211e1b4 ebp=0211e1e0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
IEFRAME!IEIsProtectedModeProcess+0x580b:
00d615e4 ff5018 call dword ptr [eax+18h] ds:0023:65461638=65405c64
继续往上分析发现。。。
下这个断点,然后得到当 eax=0时跳转到错误处理流程。
bp 65405cb9 "r eax;gc"
.text:65405CA3 mov edi, [ebp+8]
.text:65405CA6 mov ecx, edi
.text:65405CA8 call sub_65405AA8
.text:65405CAD test eax, eax
.text:65405CAF jz short loc_65405C9C
.text:65405CB1 mov eax, [ebp+0Ch]
.text:65405CB4 sub eax, 0FAh
.text:65405CB9 jz loc_65405F75 ; eax=0x0,在这里跳转到错误处理流程
....
.text:65405F75 loc_65405F75: ; CODE XREF: .text:65405CB9j
.text:65405F75 mov eax, [ebp+8]
.text:65405F78 push dword ptr [eax+1Ch]
.text:65405F7B lea edi, [eax+1Ch]
.text:65405F7E call sub_654026F7
.text:65405F83 push dword ptr [edi]
.text:65405F85 call sub_65402612
.text:65405F8A mov eax, [esi]
.text:65405F8C pop ecx
.text:65405F8D pop ecx
.text:65405F8E mov eax, [eax+58h]
.text:65405F91 push ebx
.text:65405F92 mov [ebp+1Ch], eax
.text:65405F95 mov al, [ebp+0Bh]
.text:65405F98 lea ecx, [ebp-28h]
.text:65405F9B mov [ebp-28h], al
.text:65405F9E call ?_Tidy@?$basic_string@GU?$char_traits@G@std@@V?$allocator@G@2@@std@@AAEX_N@Z ; std::basic_string<ushort,std::char_traits<ushort>,std::allocator<ushort>>::_Tidy(bool)
.text:65405FA3 mov eax, [ebp+1Ch]
.text:65405FA6 mov [ebp-4], ebx
.text:65405FA9 test byte ptr [eax+1], 40h
.text:65405FAD mov eax, [eax+8] ; 正常情况兄下[eax+8]保存有字符串的指针
.text:65405FB0 jz short loc_65405FC4
.text:65405FB2 mov eax, [eax]
.text:65405FB4 push eax ; Str
.text:65405FB5 mov [ebp+1Ch], eax
.text:65405FB8 call _wcslen ; 这里调用出错函数
当eax=oxfa时,执行.text:65405CB4 sub eax, 0FAh 后eax=0,跳转到错误的执行流程。。。
breakpoint 9 redefined
eax=ffffff6f
eax=ffffff6f
eax=ffffff6f
eax=ffffff6f
eax=ffffff6f
eax=ffffff6f
eax=ffffff6f
eax=ffffff6c
eax=ffffff6f
eax=00000000
Breakpoint 8 hit
eax=00000000 ebx=00000000 ecx=0211e1a0 edx=000000c0 esi=0211e354 edi=02b24d08
eip=65405f75 esp=0211e178 ebp=0211e1ac iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
Adfilter!IsInWhiteList+0x7ef:
65405f75 8b4508 mov eax,dword ptr [ebp+8] ss:0023:0211e1b4=02b24d08
以下为调试时使用的一些命令
bp 65405C64 "r ‘poi(ebp+0x0c)’;'gc'"
bp 65405C64 ".echo begin ######### ;dd poi(ebp+0c);.echo end ##############;gc"
bp 65405CA8 ".echo begin ######### ;dd poi(ebp+0c);.echo end ##############;gc"
bp 65405CA8 ".echo begin ######### ;dd poi(ebp+0c);.echo end ##############;gc"
begin #########
000000fa ???????? ???????? ???????? ????????
0000010a ???????? ???????? ???????? ????????
0000011a ???????? ???????? ???????? ????????
0000012a ???????? ???????? ???????? ????????
0000013a ???????? ???????? ???????? ????????
0000014a ???????? ???????? ???????? ????????
0000015a ???????? ???????? ???????? ????????
0000016a ???????? ???????? ???????? ????????
bp 65405CA6 ".echo begin ######### ;dd poi(ebp+0c);.echo end ##############;gc"
bp 65405C7B ".echo begin ######### ;dd poi(ebp+0c);.echo end ##############;gc"
bp 65405C64 ".echo begin ######### ;dd poi(ebp+0c);.echo end ##############;gc"
begin #########
0211e260 00000003 000000fa 00d61078 00000000
0211e270 e3540001 00000211 00000000 00000000
0211e280 d4af0000 021100d5 0211e430 00000000
0211e290 0211e368 00d5d454 001758d0 000000fa
0211e2a0 0211e354 0211e430 00000000 0000400c
0211e2b0 0000400b 0000400b 00000000 0211e430
0211e2c0 00000000 0000400c 00000000 0211e40c
0211e2d0 00000000 0000400c 00000000 0211e3ec
end ##############
Breakpoint 8 hit
eax=00000000 ebx=00000000 ecx=0211e1a0 edx=000000c0 esi=0211e354 edi=02b24d08
eip=65405f75 esp=0211e178 ebp=0211e1ac iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
Adfilter!IsInWhiteList+0x7ef:
65405f75 8b4508 mov eax,dword ptr [ebp+8] ss:0023:0211e1b4=02b24d08
如下地址写入了0xfa
.text:00D5D46C ; __stdcall IConnectionPoint_InvokeWithCancel(x, x, x, x, x)
.text:00D5D46C _IConnectionPoint_InvokeWithCancel@20 proc near
.text:00D5D46C ; CODE XREF: DoInvokeParamHelper(IUnknown *,IConnectionPoint *,int *,void * *,long,uint,...)+7Ap
.text:00D5D46C
.text:00D5D46C var_30 = dword ptr -30h
.text:00D5D46C var_2C = dword ptr -2Ch
.text:00D5D46C var_1E = dword ptr -1Eh
.text:00D5D46C var_E = dword ptr -0Eh
.text:00D5D46C var_8 = dword ptr -8
.text:00D5D46C var_4 = dword ptr -4
.text:00D5D46C arg_0 = dword ptr 8
.text:00D5D46C arg_4 = dword ptr 0Ch
.text:00D5D46C arg_8 = dword ptr 10h
.text:00D5D46C arg_C = dword ptr 14h
.text:00D5D46C arg_10 = dword ptr 18h
.text:00D5D46C
.text:00D5D46C mov edi, edi
.text:00D5D46E push ebp
.text:00D5D46F mov ebp, esp
.text:00D5D471 sub esp, 30h
.text:00D5D474 mov eax, [ebp+arg_4]
.text:00D5D477 mov [ebp+var_2C], eax ; 写入0xfa
.text:00D5D47A mov eax, [ebp+arg_8]
.text:00D5D47D mov [ebp+var_1E], eax
.text:00D5D480 mov eax, [ebp+arg_C]
.text:00D5D483 mov [ebp+var_8], eax
.text:00D5D486 mov eax, [ebp+arg_10]
.text:00D5D489 mov [ebp+var_4], eax
.text:00D5D48C lea eax, [ebp+var_30]
.text:00D5D48F push eax
.text:00D5D490 push [ebp+arg_0]
.text:00D5D493 mov [ebp+var_30], 3
.text:00D5D49A mov [ebp+var_E], offset ?InvokeWithCancelProc@@YGJPAUIDispatch@@PAUSHINVOKEPARAMS@@@Z ; InvokeWithCancelProc(IDispatch *,SHINVOKEPARAMS *)
.text:00D5D4A1 call _IConnectionPoint_InvokeIndirect@8 ; IConnectionPoint_InvokeIndirect(x,x)
.text:00D5D4A6 leave
.text:00D5D4A7 retn 14h
.text:00D5D4A7 _IConnectionPoint_InvokeWithCancel@20 endp
调用关系:
WARNING: Stack unwind information not available. Following frames may be wrong.
0211e290 00d5d454 IEFRAME!IEIsProtectedModeProcess+0x16a1
0211e368 00d29054 IEFRAME!IEIsProtectedModeProcess+0x167b
0211e45c 00d365b0 IEFRAME!Ordinal218+0x2c1f
0211e4a4 63777bcb IEFRAME!Ordinal303+0x4052
0211f568 63776ac8 mshtml!CWebOCEvents::BeforeNavigate2+0x29f
0211f750 63776610 mshtml!CDoc::DoNavigate+0xab5
0211f870 637788a2 mshtml!CDoc::FollowHyperlink2+0xda7
0211f8f0 636b6de9 mshtml!CDoc::FollowHyperlink+0x9d
找到的相关信息:
http://www.sonic.net/~undoc/comes_v_microsoft/Supp_Rpt_Andrew_Schulman.pdf
Connection Point APIs: A “connection point” is a mechanism related to
receiving callback notifications of given events. Microsoft has a
documented ConnectionPoint class, and in 2004 Microsoft documented
ConnectToConnectionPoint. However, there remain connection-point
related shell APIs, implemented by shlwapi.dll, and used by the IE
component shdocvw.dll, that are not documented:
IConnectionPoint_InvokeWithCancel and
IConnectionPoint_SimpleInvoke. See e.g.:
最终找到如下的一个地址,写入了0xfa这个整数。。
函数声明:
.text:00D28EB9 ; int __stdcall FireEvent_BeforeNavigate(struct IUnknown *punk, int, int, struct _ITEMIDLIST_ABSOLUTE *, int, int, int, void *Src, ULONG Size, int, int)
.text:00D28EB9 _FireEvent_BeforeNavigate@44 proc near ; CODE XREF: CBaseBrowser2::_FireBeforeNavigateEvent(_ITEMIDLIST_ABSOLUTE const *,int *)+172p
.text:00D28EB9 ; CBaseBrowser2::FireBeforeNavigate3(IHTMLWindow2 *,IDispatch *,ushort const *,ulong,ushort const *,uchar *,ulong,ushort const *,int,int *)+88p ...
.text:00D28EB9
.text:00D28FFC xor eax, eax
.text:00D28FFE cmp [ebp+var_10], eax
.text:00D29001 mov [ebp+var_80], 3
.text:00D29007 mov [ebp+var_60], 8
.text:00D2900D mov [ebp+var_70], si
.text:00D29011 mov [ebp+var_50], 8
.text:00D29017 mov edi, 0FAh ; 0xfa是从这里来的
.text:00D2901C jz short loc_D29059
.text:00D2901E lea ecx, [ebp+var_2C]
.text:00D29021 push ecx
.text:00D29022 push ebx
.text:00D29023 lea ecx, [ebp+var_50]
.text:00D29026 push ecx
.text:00D29027 push esi
.text:00D29028 lea ecx, [ebp+var_70]
由于以上函数都是微软未文档化的函数,所以具体含义只能从语义上来判断,参数等结构信息无法得到。
上面的分析过程花 费了我很长时间来跟踪调试,最终发现是一个错误的判断。过程是这样的,本来以为自己找到了关键点,但是有一个疑问是这样的:
既然找到了0xfa的出处,但是什么样的原因导致程序走到了这个流程中来,与点击的超链接有什么样子的一个关系,超链接是如何影响程序执行流程的,
这个疑问是始终没有解决,也不敢妄下结论。
下面就开始验证自己的想法,把虚拟机调试环境克隆了一份出来,进行两台虚拟机一起调试,一台是IE正常的环境,另一台就是IE崩溃环境,
跟踪调试那一条指令出现了差异导致程序的执行流程发生了变化。这个时候就发现正常情况与崩溃情况下都会出现0xfa,程序都会执行到Adfilter.dll中代码,
区别只有传递给wcslen()函数的指针是否是空指针的差别。
以下即是过程:
现在开始查找恶意字符串是如何影响到oxfa这个流程的。。。。
s -a 0 l fffffff "file:///c:"
0378934b 66 69 6c 65 3a 2f 2f 2f 63 3a 5c 77 69 6e 64 6f 77 73 file:///c:\windows
0378935d 5c 73 79 73 74 65 6d 33 32 5c 42 42 42 42 42 42 42 42 \system32\BBBBBBBB
0378936f 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBBBB
03789381 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBBBB
03789393 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 BBBBBBBBBBBBBBBBBB
s -u 0 l?fffffff "file:///c:"
ba w1 0211e424 "r eip;.echo check mem;dd 0211e424;.echo changed;gc"
Normal:正常情况
eip=00d28fa8
check mem
0211e424 02110000 0211e518 00000064 00000000
0211e434 00000000 00000000 00000000 ffffffff
0211e444 00000000 00000000 00175d70 00000000
0211e454 03c4afd4 00175d4c 0211e4a4 00d365b0
0211e464 00175d04 002901d0 00175d04 00c4c408
0211e474 00000000 00000040 00000000 00000000
0211e484 00000000 00000000 0211f5c8 0211f5c8
0211e494 002239d4 00202020 00000000 00175d04
changed
eip=00d28fb9
check mem
0211e424 03c4adac 00000000 00000064 00000000 03c4adac为字符串指针
0211e434 00000000 00000000 00000000 ffffffff
0211e444 00000000 00000000 00175d70 00000000
0211e454 03c4afd4 00175d4c 0211e4a4 00d365b0
0211e464 00175d04 002901d0 00175d04 00c4c408
0211e474 00000000 00000040 00000000 00000000
0211e484 00000000 00000000 0211f5c8 0211f5c8
0211e494 002239d4 00202020 00000000 00175d04
changed
BUG:崩溃情况
eip=00d28fa8
check mem
0211e424 02110000 0211e518 00000064 00000000
0211e434 00000000 00000000 00000000 ffffffff
0211e444 00000000 00000000 00175ea8 00000000
0211e454 00000000 00175e84 0211e4a4 00d365b0
0211e464 00175e3c 000c025e 00175e3c 00c4ec48
0211e474 00000000 00000040 00000000 00000000
0211e484 00000000 00000000 0211f5c8 0211f5c8
0211e494 002231d4 00201820 00000000 00175e3c
changed
eip=00d28fb9
check mem
0211e424 00000000 00000000 00000064 00000000 这里没有字符串指针存在。。。。
0211e434 00000000 00000000 00000000 ffffffff
0211e444 00000000 00000000 00175ea8 00000000
0211e454 00000000 00175e84 0211e4a4 00d365b0
0211e464 00175e3c 000c025e 00175e3c 00c4ec48
0211e474 00000000 00000040 00000000 00000000
0211e484 00000000 00000000 0211f5c8 0211f5c8
0211e494 002231d4 00201820 00000000 00175e3c
changed
可以明显的看出来,执行到eip=00d28fb9后0211e424的这个地址又了明显的变化。。。
正常情况下是又指针的,异常情况下没有写入字符串的指针。。。
poi(poi(esi)+58)+8
现在来看00d28fb9是在执行什么操作:
.text:00D28FAB mov [ebp+var_40], 8
.text:00D28FB1 call _SysAllocString@4 ; SysAllocString(x)
.text:00D28FB6 mov [ebp+SystemTimeAsFileTime], eax //这里写入字符串指针。。。。
.text:00D28FB9 xor eax, eax
.text:00D28FBB lea edi, [ebp+var_7E]
此时的堆栈调用如下:
0:008> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0211e45c 00d365b0 IEFRAME!Ordinal218+0x2b76
0211e4a4 63777bcb IEFRAME!Ordinal303+0x4052
0211f568 63776ac8 mshtml!CWebOCEvents::BeforeNavigate2+0x29f
0211f750 63776610 mshtml!CDoc::DoNavigate+0xab5
0211f870 637788a2 mshtml!CDoc::FollowHyperlink2+0xda7
0211f8f0 636b6de9 mshtml!CDoc::FollowHyperlink+0x9d
0211f994 636b6c68 mshtml!CHyperlink::ClickAction+0x31c
0211f9a4 636b57da mshtml!CAnchorElement::ClickAction+0x10
0211f9d8 636b2be9 mshtml!CElement::DoClick+0x155
0211fa08 636b2b5a mshtml!CAnchorElement::DoClick+0x6d
0211faa4 635f1fea mshtml!CDoc::PumpMessage+0xf10
接着找写入数据的地方
ba w1 0211e454 "r eip;.echo check mem;dd 0211e454;.echo changed;gc"
正常情况下,跟踪到这个处理函数
.text:00D284AE ; int __stdcall ATL__CComBSTR__operator_(OLECHAR *psz)
.text:00D284AE ??4CComBSTR@ATL@@QAEAAV01@PBG@Z proc near
.text:00D284AE ; CODE XREF: CDataList::SetTitle(ushort const *)+Ep
.text:00D284AE ; CDocObjectHost::_PublishParsedData(int)-32DD9p ...
.text:00D284AE
.text:00D284AE psz = dword ptr 8
.text:00D284AE
.text:00D284AE mov edi, edi
.text:00D284B0 push ebp
.text:00D284B1 mov ebp, esp
.text:00D284B3 push esi
.text:00D284B4 mov esi, ecx
.text:00D284B6 push dword ptr [esi] ; bstrString
.text:00D284B8 call _SysFreeString@4 ; SysFreeString(x)
.text:00D284BD push [ebp+psz] ; psz
.text:00D284C0 call _SysAllocString@4 ; SysAllocString(x)
.text:00D284C5 mov [esi], eax
.text:00D284C7 mov eax, esi
.text:00D284C9 pop esi
.text:00D284CA pop ebp
.text:00D284CB retn 4
.text:00D284CB ??4CComBSTR@ATL@@QAEAAV01@PBG@Z endp
异常情况下,不会走到这个流程中。。。
堆栈信息如下:
0:008> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0211d34c 00d288b9 IEFRAME!Ordinal218+0x2079
0211e3b0 00d28ef2 IEFRAME!Ordinal218+0x2484
0211e45c 00d365b0 IEFRAME!Ordinal218+0x2abd
0211e4a4 63777bcb IEFRAME!Ordinal303+0x4052
0211f568 63776ac8 mshtml!CWebOCEvents::BeforeNavigate2+0x29f
异常情况下的堆栈调用关系如下:
0:008> k
ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames may be wrong.
0211d34c 00d288b9 IEFRAME!Ordinal218+0x2079
0211e3b0 00d28ef2 IEFRAME!Ordinal218+0x2484
0211e45c 00d365b0 IEFRAME!Ordinal218+0x2abd
0211e4a4 63777bcb IEFRAME!Ordinal303+0x4052
0211f568 63776ac8 mshtml!CWebOCEvents::BeforeNavigate2+0x29f
发现都会调用0211f568 63776ac8 mshtml!CWebOCEvents::BeforeNavigate2+0x29f函数,在这个函数上下断点,跟踪处理流程上的变化
63776a99 ff74247c push dword ptr [esp+7Ch]
63776a9d 52 push edx
63776a9e ff742428 push dword ptr [esp+28h]
63776aa2 8b542428 mov edx,dword ptr [esp+28h]
63776aa6 ffb42484000000 push dword ptr [esp+84h]
63776aad ffb42480000000 push dword ptr [esp+80h]
63776ab4 51 push ecx
63776ab5 ff30 push dword ptr [eax]
63776ab7 8b8c24d4000000 mov ecx,dword ptr [esp+0D4h]
63776abe 8d442454 lea eax,[esp+54h]
63776ac2 50 push eax
63776ac3 e8010f0000 call mshtml!CWebOCEvents::BeforeNavigate2 (637779c9) //断在这里
此时ECX中保存了 超链接的路径
0:008> du ecx
037c67b0 "file:///c:/windows/system32/BBBB"
037c67f0 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c6830 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c6870 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c68b0 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c68f0 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c6930 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c6970 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c69b0 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c69f0 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c6a30 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
037c6a70 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
求得字符串长度
63777a51 8d5002 lea edx,[eax+2]
63777a54 668b08 mov cx,word ptr [eax]
63777a57 40 inc eax
63777a58 40 inc eax
63777a59 663bce cmp cx,si
63777a5c 75f6 jne mshtml!CWebOCEvents::BeforeNavigate2+0x8b (63777a54)
63777a5e 2bc2 sub eax,edx //计算结果为字符串长度
63777a60 d1f8 sar eax,1
计算结果
nromal:
0:008> r eax
eax=0000010b
Bug:
0:008> r eax
eax=00000255
....
63777ab9 ffb580efffff push dword ptr [ebp-1080h] //压栈,把字符串指针压栈 源地址
63777abf 8bb58cefffff mov esi,dword ptr [ebp-1074h] ss:0023:0211e4f4=0211f5c8
63777ac5 832600 and dword ptr [esi],0
63777ac8 53 push ebx
63777ac9 8d85b0efffff lea eax,[ebp-1050h]
63777acf 50 push eax //目的地址
63777ad0 e871e2e7ff call mshtml!StringCchCopyW (635f5d46) 字符串拷贝函数,
.....
63777bbb 8b8598efffff mov eax,dword ptr [ebp-1068h]
63777bc1 8b08 mov ecx,dword ptr [eax]
63777bc3 83c714 add edi,14h
63777bc6 57 push edi
63777bc7 50 push eax
63777bc8 ff510c call dword ptr [ecx+0Ch] ds:0023:00d2b850=00d36523 这里进入iframe.dll中的处理流程
.text:00D36592 push edi ; int
.text:00D36593 push [ebp+arg_20] ; int
.text:00D36596 push [ebp+Size] ; Size
.text:00D36599 push [ebp+Src] ; Src
.text:00D3659C push eax ; int
.text:00D3659D push [ebp+arg_10] ; int
.text:00D365A0 push ebx ; int
.text:00D365A1 push ecx ; struct _ITEMIDLIST_ABSOLUTE *
.text:00D365A2 push [ebp+ppvOut] ; int
.text:00D365A5 push dword ptr [esi+48h] ; int
.text:00D365A8 push [ebp+ppvOut] ; punk
.text:00D365AB call _FireEvent_BeforeNavigate@44 ; FireEvent_BeforeNavigate(x,x,x,x,x,x,x,x,x,x,x) //这个函数中进入不同的处理流程。。。。
.text:00D28ED4 lea eax, [ebp+pcszURL]
.text:00D28ED7 push eax ; int
.text:00D28ED8 push [ebp+arg_C] ; struct _ITEMIDLIST_ABSOLUTE *
.text:00D28EDB mov [ebp+var_4.lpVtbl], esi
.text:00D28EDE mov [ebp+ppvOut], esi
.text:00D28EE1 mov [ebp+var_10], esi
.text:00D28EE4 mov [ebp+bstrString], esi
.text:00D28EE7 mov [ebp+var_14], esi
.text:00D28EEA mov [ebp+pcszURL], esi
.text:00D28EED call ?GetEventURL@@YGXPBU_ITEMIDLIST_ABSOLUTE@@AAVCComBSTR@ATL@@@Z ; GetEventURL(_ITEMIDLIST_ABSOLUTE const *,ATL::CComBSTR &)
.text:00D2887D mov esi, [ebp+arg_4]
.text:00D28880 push edi
.text:00D28881 jz loc_C482EE
.text:00D28887 and [ebp+psz], 0
.text:00D2888F push 8000h ; __int16
.text:00D28894 lea ecx, [ebp+psz]
.text:00D2889A push ecx ; unsigned __int16 *
.text:00D2889B push eax ; struct _ITEMIDLIST_ABSOLUTE *
.text:00D2889C call _IEGetDisplayName@12 ; 函数返回值在正常与异常情况下不同。。。
.text:00D288A1 mov edi, eax
.text:00D288A3 test edi, edi
.text:00D288A5 jl loc_C482EE
.text:00D288AB lea eax, [ebp+psz]
.text:00D288B1 push eax ; psz
.text:00D288B2 mov ecx, esi
.text:00D288B4 call ??4CComBSTR@ATL@@QAEAAV01@PBG@Z ; ATL::CComBSTR::operator=(ushort const *)
.text:00D288B9 cmp dword ptr [esi], 0
.text:00D288BC jz loc_D776E4
最终是 call _IEGetDisplayName@12 此函数正常情况下与崩溃情况下的返回值不同,导致了执行流程的不同,最终导致Adfilter.dll调用wcslen()函数出错。。。
IEGetDisplayName最终调用:
.text:00C3589B push [ebp+cwchBuf] ; cwchBuf
.text:00C3589E push esi ; pwszDst
.text:00C3589F push [ebp+arg_4] ; int
.text:00C358A2 push [ebp+ppidlLast] ; int
.text:00C358A5 push [ebp+ppv] ; int
.text:00C358A8 call _DisplayNameOfW@20 ; DisplayNameOfW(x,x,x,x,x)
.text:00C35784 mov eax, [ebp+arg_0]
.text:00C35787 mov esi, [ebp+pwszDst]
.text:00C3578A and word ptr [esi], 0
.text:00C3578E mov ecx, [eax]
.text:00C35790 lea edx, [ebp+var_10C]
.text:00C35796 push edx
.text:00C35797 push [ebp+arg_8]
.text:00C3579A push [ebp+arg_4]
.text:00C3579D push eax
.text:00C3579E call dword ptr [ecx+2Ch] ; 这里返回了0x8007006f错误,执行的函数是SHELL32!ILRemoveLastID()
ILRemoveLastID()函数解释如下:
Removes the last SHITEMID structure from an ITEMIDLIST structure,
至此此次360网盾 广告拦截模块 的bug才算分析完毕,其中也有一些不太明白的地方,但程序基本的处理流程就是这样的。现在可以总结一下这个360bug的触发原因了。。。
三.总结
前前后后花了很长时间来分析这个bug,也走了很长的弯路。
360网盾的广告拦截模块给IE注册了很多个回调函数,但在处理回调的数据时存在问题,直接引用并下发IE ieframe.dll的GetEventURL()函数的返回值,
并没有判断返回值是否为空情况的情况存在,传递给了wcslen()函数,导致IE崩溃。
Assay_Bug.txt
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
上传的附件: