首页
论坛
课程
招聘

[原创]360网盾 广告拦截模块崩溃分析

2012-12-10 18:41
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
上传的附件:

声明:该文观点仅代表作者本人,转载请注明来自看雪专栏
最新评论 (0)
登录后即可评论