@瞌睡龍讓俺仔細介紹一下,所以我就把之前文章http://drops.wooyun.org/papers/1020的一節的東西單獨提出來了,之前百度瀏覽器5.0版本子窗口堆溢出的那版的程序我已經找不太到了,找到最接近的版本修復的只剩一個棧空間不足的問題而已了,這個小毛病我就擱在事后分析里面好了。
我找到的早期版本,比如4.5版,它的內存損壞和5.x的內存損壞效果類似,而且崩潰邏輯、位置都大體相同,5.x那個小報告中我提到的“低版本程序不測試直接轉移到高版本”就是讓這一個問題橫跨幾個世紀的主要原因。
當然,以下是我晚上幾個小時調(口)試(胡)出來的,由于家里的電腦下WDK一直失敗,所以沒用上最新科技,只用了一個很老的windbg版本,有的地方還有些問題,所以我會粘來一點之前草稿里的數據,不過我確定這個對結果沒有影響的。還有就是由于代碼上班的時候調了一點,下班后回家繼續調了后面的(俺是新開的例程),所以可能會有一些神推理名推測和超劇情發展的東西,這個請見諒。
由于代碼實在太長,所以我會適當的標記出我在干什么,以及我想要得到什么結果,所以如果你看到了莫名其妙的黑色粗體標記,那肯定是我的注解。另外,由于這個windbg貼的代碼本身實在已經是錯綜復雜了,所以我不會一句句的解釋了,我會直接介紹函數在作甚,而且由于個人水平渣,寫的時候卡殼了多次,所以這我已經預感到難免會出問題,這個也請大家見諒,各位發現不對的話請及時糊我熊臉!
一個調試器是必要的,由于涉及事后調試和即時調試,俺就使用windbg來進行啦。由于目標程序是32位的,所以我選擇了x86版的windbg。操作系統是Windows?7?sp1?簡體中文版,說是oem但是看起來像盜版系統的系統。
然后就是幾個基本的命令,t?步入,p?步過,?gu?執行到返回,dd?查看dword,?da?查看ansi,?k?顯示調用棧,?poi?析取指針,等等,遇到新不明物種的話,windbg的F1里面的介紹是非常詳盡的。
事后分析我們就用百度瀏覽器5.0的一個棧內存不夠分導致的Stack?Buffer?Overrun為例好了,poc還是那個,口胡我第一,那么開始唄。
首先,我們可以確定的是我們的poc可以導致這個程序崩潰,而通常我們的目標程序也會自帶有一個dump文件,例如:
? 圖:通常在%temp%
下可能有崩潰文件,也有可能在%appdata%
下
打開windbg,載入dump文件,進行簡單的!analyze?-v
。顯示信息如下:
#!bash
The stored exception information can be accessed via .ecxr.
(51fc.a978): Stack overflow - code c00000fd (first/second chance not available)
eax=00000000 ebx=00000000 ecx=0046700c edx=0061e3e4 esi=000002a8 edi=00000000
eip=77a0f8d1 esp=0061bbe0 ebp=0061bc4c iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!NtWaitForSingleObject+0x15:
77a0f8d1 83c404 add esp,4
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
FAULTING_IP:
browsercore+dbda7
033fbda7 8500 test dword ptr [eax],eax
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 033fbda7 (browsercore+0x000dbda7)
ExceptionCode: c00000fd (Stack overflow)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00522000
PROCESS_NAME: baidubrowser.exe
ERROR_CODE: (NTSTATUS) 0xc00000fd - <Unable to get error code text>
EXCEPTION_CODE: (NTSTATUS) 0xc00000fd - <Unable to get error code text>
EXCEPTION_PARAMETER1: 00000000
EXCEPTION_PARAMETER2: 00522000
NTGLOBALFLAG: 0
FAULTING_THREAD: 0000a978
DEFAULT_BUCKET_ID: STACK_OVERFLOW
PRIMARY_PROBLEM_CLASS: STACK_OVERFLOW
BUGCHECK_STR: APPLICATION_FAULT_STACK_OVERFLOW_INVALID_POINTER_READ
LAST_CONTROL_TRANSFER: from 045b7a85 to 033fbda7
STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
0061e2a8 045b7a85 0061e3e4 0061e634 00000001 browsercore+0xdbda7
0061e2c4 0337100c 0061e3e4 0061e634 0061e7f4 browsercore+0x1297a85
0061e620 03333292 0061e7f4 00000000 00000000 browsercore+0x5100c
……(略)
0061fe88 001e63bf 00100000 00000000 00352318 baidubrowser+0x65da
0061ff18 7703336a 7efde000 0061ff64 77a29f72 baidubrowser+0xe63bf
0061ff24 77a29f72 7efde000 2a534886 00000000 kernel32!BaseThreadInitThunk+0xe
0061ff64 77a29f45 001e6412 7efde000 00000000 ntdll!__RtlUserThreadStart+0x70
0061ff7c 00000000 001e6412 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b
STACK_COMMAND: ~0s; .ecxr ; kb
FOLLOWUP_IP:
browsercore+dbda7
033fbda7 8500 test dword ptr [eax],eax
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: browsercore+dbda7
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: browsercore
IMAGE_NAME: browsercore.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 526f3e67
FAILURE_BUCKET_ID: STACK_OVERFLOW_c00000fd_browsercore.dll!Unknown
BUCKET_ID: APPLICATION_FAULT_STACK_OVERFLOW_INVALID_POINTER_READ_browsercore+dbda7
WATSON_IBUCKET: 2118667
WATSON_IBUCKETTABLE: 17
WATSON_STAGEONE_URL: http://watson.microsoft.com/StageOne/baidubrowser_exe/2_210_0_42889/526f3e61/browsercore_dll/10_0_0_17/526f3e67/c00000fd/000dbda7.htm?Retriage=1
Followup: MachineOwner
---------
我們可以由上得到很多重要的信息:
例如最上層的調用棧、BUCKET信息(分類漏洞)、出錯的位置、出錯的語句、它并沒有對這個異常的處理程序等等,還有最重要的信息:我們根本沒它的符號,淦。
好在它沒有做什么其他的事兒,我們查看faulting?ip附近的代碼即可知道他為什么會崩。
#!bash
FAULTING_IP:
browsercore+dbda7
033fbda7 8500 test dword ptr [eax],eax
執行
#!bash
uf?browsercore+dbda7
得到:
#!bash
0:000> uf browsercore+dbda7
Unable to load image C:\Program Files (x86)\baidu\BaiduBrowser\browsercore.dll, Win32 error 0n2
*** WARNING: Unable to verify timestamp for browsercore.dll
*** ERROR: Module load completed but symbols could not be loaded for browsercore.dll
browsercore+0xdbd94:
033fbd94 3bc8 cmp ecx,eax
033fbd96 720a jb browsercore+0xdbda2 (033fbda2)
browsercore+0xdbd98:
033fbd98 8bc1 mov eax,ecx
033fbd9a 59 pop ecx
033fbd9b 94 xchg eax,esp
033fbd9c 8b00 mov eax,dword ptr [eax]
033fbd9e 890424 mov dword ptr [esp],eax
033fbda1 c3 ret
browsercore+0xdbda2:
033fbda2 2d00100000 sub eax,1000h
browsercore+0xdbda7:
033fbda7 8500 test dword ptr [eax],eax ;崩潰在此
033fbda9 ebe9 jmp browsercore+0xdbd94 (033fbd94)
但是這是什么?看起來為什么這么像__alloca_probe的驗證代碼?對比一下實實在在的\__alloca_probe看來是沒跑了。
#!bash
__alloca_probe:
push ecx
cmp eax,1000h
lea ecx,[esp+8]
jb lastpage
probepages:
sub ecx,1000h
sub eax,1000h
test dword ptr [ecx],eax
cmp eax,1000h
jae probepages
lastpage:
sub ecx,eax
mov eax,esp
test dword ptr [ecx],eax
mov esp,ecx
mov ecx,dword ptr [eax]
mov eax,dword ptr [eax+4]
push eax
Ret
崩在了最后的棧校驗上。查看一下完整的調用棧,使用~*kvn
查看所有線程的調用棧:
#!bash
0:000> ~*kvn
. 0 Id: 51fc.a978 Suspend: 0 Teb: 7efdd000 Unfrozen
# ChildEBP RetAddr Args to Child
00 0061bbe0 76fa149d 000002a8 00000000 00000000 ntdll!NtWaitForSingleObject+0x15 (FPO: [3,0,0])
01 0061bc4c 77031194 000002a8 ffffffff 00000000 KERNELBASE!WaitForSingleObjectEx+0x98 (FPO: [SEH])
02 0061bc64 77031148 000002a8 ffffffff 00000000 kernel32!WaitForSingleObjectExImplementation+0x75 (FPO: [3,0,4])
03 0061bc78 001056c7 000002a8 ffffffff 006e9948 kernel32!WaitForSingleObject+0x12 (FPO: [2,0,0])
WARNING: Stack unwind information not available. Following frames may be wrong.
04 0061dd78 00105467 ac68c25f 0010542d 7710030c baidubrowser+0x56c7
05 0061dda0 7706fffb 0061de58 ac7abd84 00000000 baidubrowser+0x5467
06 0061de28 77a674ff 0061de58 77a673dc 00000000 kernel32!UnhandledExceptionFilter+0x127 (FPO: [SEH])
07 0061de30 77a673dc 00000000 0061ff64 77a1c550 ntdll!__RtlUserThreadStart+0x62 (FPO: [SEH])
08 0061de44 77a67281 00000000 00000000 00000000 ntdll!_EH4_CallFilterFunc+0x12 (FPO: [Uses EBP] [0,0,4])
09 0061de6c 77a4b499 fffffffe 0061ff54 0061dfa8 ntdll!_except_handler4+0x8e (FPO: [4,5,4])
0a 0061de90 77a4b46b 0061df58 0061ff54 0061dfa8 ntdll!ExecuteHandler2+0x26 (FPO: [Uses EBP] [5,3,1])
0b 0061deb4 77a4b40e 0061df58 0061ff54 0061dfa8 ntdll!ExecuteHandler+0x24 (FPO: [5,0,3])
0c 0061df40 77a00133 0061df58 0061dfa8 0061df58 ntdll!RtlDispatchException+0x127 (FPO: [2,25,4])
0d 0061df40 033fbda7 0061df58 0061dfa8 0061df58 ntdll!KiUserExceptionDispatcher+0xf (FPO: [2,0,0]) (CONTEXT @ 0061dfa8)
0e 0061e2a8 045b7a85 0061e3e4 0061e634 00000001 browsercore+0xdbda7
0f 0061e2c4 0337100c 0061e3e4 0061e634 0061e7f4 browsercore+0x1297a85
10 0061e620 03333292 0061e7f4 00000000 00000000 browsercore+0x5100c
11 0061e814 048b515c 0061e82c fffffffa 00000200 browsercore+0x13292
12 0061e854 03334634 02ec7d94 02ec2780 02ec2780 browsercore+0x159515c
13 0061e974 046e67c9 02ec7d94 02ec7d94 02e63c30 browsercore+0x14634
1 Id: 51fc.9920 Suspend: 1 Teb: 7efda000 Unfrozen
# ChildEBP RetAddr Args to Child
00 02aff61c 76fa15e9 00000003 02aff66c 00000001 ntdll!ZwWaitForMultipleObjects+0x15 (FPO: [5,0,0])
01 02aff6b8 770319fc 02aff66c 02aff6e0 00000000 KERNELBASE!WaitForMultipleObjectsEx+0x100 (FPO: [SEH])
02 02aff700 770341d8 00000003 7efde000 00000000 kernel32!WaitForMultipleObjectsExImplementation+0xe0 (FPO: [5,8,4])
…………blablabla
然后我們可以很開心的看到:
#!bash
0:000> ~*kvn
. 0 Id: 51fc.a978 Suspend: 0 Teb: 7efdd000 Unfrozen
# ChildEBP RetAddr Args to Child
00 0061bbe0 76fa149d 000002a8 00000000 00000000 ntdll!NtWaitForSingleObject+0x15 (FPO: [3,0,0])
01 0061bc4c 77031194 000002a8 ffffffff 00000000 KERNELBASE!WaitForSingleObjectEx+0x98 (FPO: [SEH])
02 0061bc64 77031148 000002a8 ffffffff 00000000 kernel32!WaitForSingleObjectExImplementation+0x75 (FPO: [3,0,4])
03 0061bc78 001056c7 000002a8 ffffffff 006e9948 kernel32!WaitForSingleObject+0x12 (FPO: [2,0,0])
WARNING: Stack unwind information not available. Following frames may be wrong.
04 0061dd78 00105467 ac68c25f 0010542d 7710030c baidubrowser+0x56c7
05 0061dda0 7706fffb 0061de58 ac7abd84 00000000 baidubrowser+0x5467
06 0061de28 77a674ff 0061de58 77a673dc 00000000 kernel32!UnhandledExceptionFilter+0x127 (FPO: [SEH])
07 0061de30 77a673dc 00000000 0061ff64 77a1c550 ntdll!__RtlUserThreadStart+0x62 (FPO: [SEH])
08 0061de44 77a67281 00000000 00000000 00000000 ntdll!_EH4_CallFilterFunc+0x12 (FPO: [Uses EBP] [0,0,4])
09 0061de6c 77a4b499 fffffffe 0061ff54 0061dfa8 ntdll!_except_handler4+0x8e (FPO: [4,5,4])
0a 0061de90 77a4b46b 0061df58 0061ff54 0061dfa8 ntdll!ExecuteHandler2+0x26 (FPO: [Uses EBP] [5,3,1])
0b 0061deb4 77a4b40e 0061df58 0061ff54 0061dfa8 ntdll!ExecuteHandler+0x24 (FPO: [5,0,3])
0c 0061df40 77a00133 0061df58 0061dfa8 0061df58 ntdll!RtlDispatchException+0x127 (FPO: [2,25,4])
0d 0061df40 033fbda7 0061df58 0061dfa8 0061df58 ntdll!KiUserExceptionDispatcher+0xf (FPO: [2,0,0]) (CONTEXT @ 0061dfa8)
0e 0061e2a8 045b7a85 0061e3e4 0061e634 00000001 browsercore+0xdbda7
0f 0061e2c4 0337100c 0061e3e4 0061e634 0061e7f4 browsercore+0x1297a85
我們看看它的上層函數做了什么:
#!bash
0:000> uf 045b7a85
No code found, aborting
居然顯示沒有代碼,看來事后調試已經滿足不了我們了,不過既然跑到了_alloca_probe
,那真相就只有一個了!對,在棧上分配的東西太大了,比棧還大,比棧還牛逼,于是棧不干了,結果就拋個異常罷工了。
這只是一個小演示而已,反正我們沒符號,反正我們沒代碼,但是我們有PoC,我們需要的是實時調試,下面進入正題。
下面使用的目標程序是百度瀏覽器v4.5,由于我們的poc簡單粗暴,為了防止代碼一加載就崩,我們在它崩潰前加一個alert(1),給我們和windbg一個心理準備,使得poc變成下面這樣:
#!html
<script>
var s="A";
var i=1;
for(i=1;i<599559;i++)
s+="A";
alert(1);
window.open(s);
</script>
運行瀏覽器,最好是直接把html拖圖標上,這樣之后方便我們找哪個進程容納著html,如下:
? 圖:?暴風雨前夕
姨媽大,給它Attach上,
? 圖:這樣就能找到pid了
找到對應進程的PID,然后Attach,之后按下g讓它跑起來:
#!bash
(9df0.3724): Break instruction exception - code 80000003 (first chance)
eax=7ef42000 ebx=00000000 ecx=00000000 edx=77a8f8ea esi=00000000 edi=00000000
eip=77a0000c esp=0c52fe4c ebp=0c52fe78 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!DbgBreakPoint:
77a0000c cc int 3
0:034> g
……省略
STATUS_STACK_BUFFER_OVERRUN encountered
(9df0.3500): Break instruction exception - code 80000003 (first chance)
eax=00000000 ebx=55b23f30 ecx=77070174 edx=0653d16d esi=00000000 edi=001b7281
eip=7706ff55 esp=0653d3b4 ebp=0653d430 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
kernel32!UnhandledExceptionFilter+0x5f:
7706ff55 cc int 3
程序崩潰。查看一下是怎么產生的,執行kvn
#!bash
0:006> kvn
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\baidu\BaiduBrowser\bdlogicmain.dll -
# ChildEBP RetAddr Args to Child
00 0653d430 55a57789 55b23f30 34bb28ee cb44d711 kernel32!UnhandledExceptionFilter+0x5f (FPO: [SEH])
WARNING: Stack unwind information not available. Following frames may be wrong.
01 0653d764 5580757b 001b7281 656c6966 00000000 bdlogicmain!BrowserLogicInit+0x198229
02 0653f500 55a4b5f4 0f1a0020 12960020 001b7281 bdlogicmain+0x757b
03 0653f5b8 559f51a6 12700020 0653f5e4 11bef338 bdlogicmain!BrowserLogicInit+0x18c094
04 0653f5fc 558c5b95 12700020 0653f640 559b4105 bdlogicmain!BrowserLogicInit+0x135c46
05 0653f608 559b4105 062b2a1c 559f5040 00000000 bdlogicmain!BrowserLogicInit+0x6635
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\baidu\BaiduBrowser\bdcommon.dll -
06 0653f640 653d18ec 0eed1240 0653f688 653d1b77 bdlogicmain!BrowserLogicInit+0xf4ba5
07 0653f64c 653d1b77 0653f664 00f7cac9 03329ce0 bdcommon!Util::Help::GetMimeTypeByExt+0x314a
08 0653f688 653cf6a9 ffffffff 00000000 03329ce0 bdcommon!Util::Help::GetMimeTypeByExt+0x33d5
09 0653f6ac 653ceb15 00000001 03329bb0 03329bb0 bdcommon!Util::Help::GetMimeTypeByExt+0xf07
0a 0653f6cc 653cf0f7 03329bb0 00000000 00000001 bdcommon!Util::Help::GetMimeTypeByExt+0x373
0b 0653f6e0 653cefcd 03329bb0 03329bb0 653cf8d5 bdcommon!Util::Help::GetMimeTypeByExt+0x955
0c 0653f724 653cfe36 0653f798 653d2576 03329bb0 bdcommon!Util::Help::GetMimeTypeByExt+0x82b
0d 0653f72c 653d2576 03329bb0 c5a8d143 00000000 bdcommon!Util::Help::GetMimeTypeByExt+0x1694
0e 0653f798 653d2a0c 0653f7d8 653dc835 032cdeb8 bdcommon!Util::Help::GetMimeTypeByExt+0x3dd4
0f 0653f7a0 653dc835 032cdeb8 c5a8d103 00000000 bdcommon!Util::Help::GetMimeTypeByExt+0x426a
10 0653f7d8 653dc8bf 00000000 0653f7f0 7703336a bdcommon!Util::Common::Timer::EraseTimerCallback+0x5b6b
11 0653f7e4 7703336a 03329bb0 0653f830 77a29f72 bdcommon!Util::Common::Timer::EraseTimerCallback+0x5bf5
12 0653f7f0 77a29f72 03329bb0 2c33d9fc 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
13 0653f830 77a29f45 653dc85b 03329bb0 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
以下行為用于將上下文定位到我們出錯的代碼上,而不是kernel32里面
然后,由于我們在UnhandledExceptionFilter里面,我們查看一下錯誤信息,
#!bash
# ChildEBP RetAddr Args to Child
00 0653d430 55a57789 55b23f30 34bb28ee cb44d711 kernel32!UnhandledExceptionFilter+0x5f (FPO: [SEH])
UnhandledExceptionFilter該函數的定義是(msdn當然是我們的好幫手,實在不濟百度百科也湊合吧……):
#!bash
LONG WINAPI UnhandledExceptionFilter(
_In_ struct _EXCEPTION_POINTERS *ExceptionInfo
);
那第一個參數必然指向_EXCEPTION_POINTERS,而該結構體的定義為:
#!bash
typedef struct _EXCEPTION_POINTERS {
PEXCEPTION_RECORD ExceptionRecord;
PCONTEXT ContextRecord;
} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
而這則正是我們需要的內容,于是,我們先看看這個結構體在哪兒
#!bash
0:006> dt _EXCEPTION_POINTERS 55b23f30
ATL80!_EXCEPTION_POINTERS
+0x000 ExceptionRecord : 0x55bb98a0 _EXCEPTION_RECORD
+0x004 ContextRecord : 0x55bb98f8 _CONTEXT
然后,我們查看異常的信息
#!bash
0:006> .exr 0x55bb98a0
ExceptionAddress: 5580757b (bdlogicmain+0x0000757b)
ExceptionCode: c0000409 (Stack buffer overflow)
ExceptionFlags: 00000001
NumberParameters: 0
設置異常上下文
#!bash
0:006> .cxr 0x55bb98f8
eax=00000000 ebx=0f1a0020 ecx=34bb2841 edx=00414141 esi=12960020 edi=001b7281
eip=5580757b esp=0653d76c ebp=0653f500 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216
bdlogicmain+0x757b:
5580757b 8be5 mov esp,ebp
現在我們的異常上下文已經被更正如上。
再回溯一次:
#!bash
0:006> kvn
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0653f500 55a4b5f4 0f1a0020 12960020 001b7281 bdlogicmain+0x757b
01 0653f5b8 559f51a6 12700020 0653f5e4 11bef338 bdlogicmain!BrowserLogicInit+0x18c094
02 0653f5fc 558c5b95 12700020 0653f640 559b4105 bdlogicmain!BrowserLogicInit+0x135c46
03 0653f608 559b4105 062b2a1c 559f5040 00000000 bdlogicmain!BrowserLogicInit+0x6635
04 0653f640 653d18ec 0eed1240 0653f688 653d1b77 bdlogicmain!BrowserLogicInit+0xf4ba5
05 0653f64c 653d1b77 0653f664 00f7cac9 03329ce0 bdcommon!Util::Help::GetMimeTypeByExt+0x314a
06 0653f688 653cf6a9 ffffffff 00000000 03329ce0 bdcommon!Util::Help::GetMimeTypeByExt+0x33d5
07 0653f6ac 653ceb15 00000001 03329bb0 03329bb0 bdcommon!Util::Help::GetMimeTypeByExt+0xf07
08 0653f6cc 653cf0f7 03329bb0 00000000 00000001 bdcommon!Util::Help::GetMimeTypeByExt+0x373
09 0653f6e0 653cefcd 03329bb0 03329bb0 653cf8d5 bdcommon!Util::Help::GetMimeTypeByExt+0x955
0a 0653f724 653cfe36 0653f798 653d2576 03329bb0 bdcommon!Util::Help::GetMimeTypeByExt+0x82b
0b 0653f72c 653d2576 03329bb0 c5a8d143 00000000 bdcommon!Util::Help::GetMimeTypeByExt+0x1694
0c 0653f798 653d2a0c 0653f7d8 653dc835 032cdeb8 bdcommon!Util::Help::GetMimeTypeByExt+0x3dd4
0d 0653f7a0 653dc835 032cdeb8 c5a8d103 00000000 bdcommon!Util::Help::GetMimeTypeByExt+0x426a
0e 0653f7d8 653dc8bf 00000000 0653f7f0 7703336a bdcommon!Util::Common::Timer::EraseTimerCallback+0x5b6b
0f 0653f7e4 7703336a 03329bb0 0653f830 77a29f72 bdcommon!Util::Common::Timer::EraseTimerCallback+0x5bf5
10 0653f7f0 77a29f72 03329bb0 2c33d9fc 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [1,0,0])
11 0653f830 77a29f45 653dc85b 03329bb0 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [SEH])
12 0653f848 00000000 653dc85b 03329bb0 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [2,2,0])
現在看起來舒服多了,我們可以清楚的看到崩潰的位置代碼如下:
#!bash
0:006> ub bdlogicmain+0x757b
bdlogicmain+0x7565:
55807565 8b4dfc mov ecx,dword ptr [ebp-4]
55807568 83c40c add esp,0Ch
5580756b c6441eff00 mov byte ptr [esi+ebx-1],0
55807570 5e pop esi
55807571 33cd xor ecx,ebp
55807573 33c0 xor eax,eax
55807575 5b pop ebx
55807576 e823f82400 call bdlogicmain!BrowserLogicInit+0x19783e (55a56d9e); 之后崩潰
找到崩潰點之后,閱讀主程序代碼,了解崩潰的原因
簡單計算可知bdlogicmain+0x7576就是我們的目標,那么我們就在這兒看一下程序是如何崩潰的吧。
退出程序,重新打開主程序,直接Attach,然后bp?bdlogicmain+0x7576:
#!bash
(a6c.8b4): Break instruction exception - code 80000003 (first chance)
eax=7ef4b000 ebx=00000000 ecx=00000000 edx=77abf8ea esi=00000000 edi=00000000
eip=77a3000c esp=0abafe40 ebp=0abafe6c iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!DbgBreakPoint:
77a3000c cc int 3
0:031> bp bdlogicmain+0x7576
breakpoint 0 redefined
0:031> bl
0 e 62067576 0001 (0001) 0:**** bdlogicmain+0x7576
0:031> g
之后載入PoC,斷在:
#!bash
Breakpoint 0 hit
eax=00000000 ebx=0bcd0020 ecx=d8dfe9d1 edx=00414141 esi=28920020 edi=001b7278
eip=62067576 esp=002fc6fc ebp=002fe490 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
bdlogicmain+0x7576:
62067576 e823f82400 call bdlogicmain!BrowserLogicInit+0x19783e (622b6d9e)
我們繼續,可以看見這之中會檢查是否有調試器加載(IsDebuggerPresentStub)并拋出異常,看來這個函數應該是處理異常用的,
#!bash
0:000>
eax=27201628 ebx=0bcd0020 ecx=d8dfe9d1 edx=00414141 esi=28920020 edi=001b7278
eip=622b7763 esp=002fc3cc ebp=002fc6f4 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200216
bdlogicmain!BrowserLogicInit+0x198203:
622b7763 ff150cf12e62 call dword ptr [bdlogicmain!BrowserLogicInit+0x1cfbac (622ef10c)] ds:002b:622ef10c={kernel32!IsDebuggerPresentStub (756249fd)}
0:000>
eax=27201628 ebx=0bcd0020 ecx=d8dfe9d1 edx=00414141 esi=28920020 edi=001b7278
eip=756249fd esp=002fc3c8 ebp=002fc6f4 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200216
kernel32!IsDebuggerPresentStub:
756249fd eb05 jmp kernel32!IsDebuggerPresent (75624a04)
0:000> k
ChildEBP RetAddr
002fc3c4 622b7769 kernel32!IsDebuggerPresentStub
WARNING: Stack unwind information not available. Following frames may be wrong.
002fc6f4 6206757b bdlogicmain!BrowserLogicInit+0x198209
002fe490 622aeb21 bdlogicmain+0x757b
那么這個bdlogicmain+0x7576附近又有什么代碼呢?既然這兒處理異常了,那肯定是之前某個地方出了問題,讓我們使用uf查看一下代碼:
#!bash
bdlogicmain+0x7550:
62067550 8bb56ce2ffff mov esi,dword ptr [ebp-1D94h]
62067556 56 push esi
62067557 8d95fcefffff lea edx,[ebp-1004h]
6206755d 52 push edx
6206755e 53 push ebx
6206755f ff15c4f42e62 call dword ptr [bdlogicmain!BrowserLogicInit+0x1cff64 (622ef4c4)]
62067565 8b4dfc mov ecx,dword ptr [ebp-4]
62067568 83c40c add esp,0Ch
6206756b c6441eff00 mov byte ptr [esi+ebx-1],0
62067570 5e pop esi
62067571 33cd xor ecx,ebp
62067573 33c0 xor eax,eax
62067575 5b pop ebx
62067576 e823f82400 call bdlogicmain!BrowserLogicInit+0x19783e (622b6d9e) ;fails here
6206757b 8be5 mov esp,ebp
6206757d 5d pop ebp
6206757e c3 ret
看起來這個函數很像是最終用來檢測Security?Cookie的函數。讓我們對比一下其他Security?Cookie的處理調用:
#!bash
mov ecx, [ebp+SOMETHING] ; get the adjusted cookie.
xor ecx, ebp ; un-adjust it, since
; ((N xor X) xor X) == N.
call @__sec_check_cookie ; check the cookie.
不直觀嗎?看看隨便一個C++程序的編譯后結果:
#!bash
.text:004010D4 mov ecx, [ebp+var_4]
.text:004010D7 xor ecx, ebp
.text:004010D9 xor eax, eax
.text:004010DB pop esi
.text:004010DC call @[email protected] ; __security_check_cookie(x) ;對比下其他程序,其實就是__security_check_cookie失敗了
.text:004010E1 mov esp, ebp
.text:004010E3 pop ebp
.text:004010E4 retn
.text:004010E4 _wmain endp
好吧,看來我們得出第一個結論:Security?Cookie校驗失敗了。
那我們在函數稍前的位置(除了Security?cookie?check的上一個Call)設置斷點:
#!bash
0:030> bp bdlogicmain+0x7550
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files (x86)\Baidu\BaiduBrowser\bdlogicmain.dll -
0:030> g
重復上述步驟,斷在了:
#!bash
Breakpoint 0 hit
eax=00000000 ebx=28a80020 ecx=4b8aa8e3 edx=00000420 esi=0038c220 edi=001b7278
eip=6c3e7550 esp=0038c214 ebp=0038dfb0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
bdlogicmain+0x7550:
6c3e7550 8bb56ce2ffff mov esi,dword ptr [ebp-1D94h] ss:002b:0038c21c=001b7278
0:000> u
bdlogicmain+0x7550:
6c3e7550 8bb56ce2ffff mov esi,dword ptr [ebp-1D94h]
6c3e7556 56 push esi
6c3e7557 8d95fcefffff lea edx,[ebp-1004h]
6c3e755d 52 push edx
6c3e755e 53 push ebx
6c3e755f ff15c4f4666c call dword ptr [bdlogicmain!BrowserLogicInit+0x1cff64 (6c66f4c4)]
6c3e7565 8b4dfc mov ecx,dword ptr [ebp-4]
6c3e7568 83c40c add esp,0Ch
我們可以看到有一個可能有三個參數的函數調用:
#!bash
6c3e7556 56 push esi
6c3e7557 8d95fcefffff lea edx,[ebp-1004h]
6c3e755d 52 push edx
6c3e755e 53 push ebx
6c3e755f ff15c4f4666c call dword ptr [bdlogicmain!BrowserLogicInit+0x1cff64 (6c66f4c4)]
由于沒有符號,我們只好t步入:
#!bash
0:000> t
eax=00000000 ebx=28a80020 ecx=4b8aa8e3 edx=00000420 esi=001b7278 edi=001b7278
eip=6c3e7556 esp=0038c214 ebp=0038dfb0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
bdlogicmain+0x7556:
6c3e7556 56 push esi
0:000> dd esi
001b7278 00000000
看來第三個參數是0,
#!bash
0:000> t
eax=00000000 ebx=28a80020 ecx=4b8aa8e3 edx=00000420 esi=001b7278 edi=001b7278
eip=6c3e7557 esp=0038c210 ebp=0038dfb0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
bdlogicmain+0x7557:
6c3e7557 8d95fcefffff lea edx,[ebp-1004h]
0:000>
eax=00000000 ebx=28a80020 ecx=4b8aa8e3 edx=0038cfac esi=001b7278 edi=001b7278
eip=6c3e755d esp=0038c210 ebp=0038dfb0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
bdlogicmain+0x755d:
6c3e755d 52 push edx
0:000> dd edx
0038cfac 656c6966 2f2f2f3a 552f3a45 73726573
0038cfbc 616c422f 53547473 7365442f 706f746b
0038cfcc 4141412f 41414141 41414141 41414141
0038cfdc 41414141 41414141 41414141 41414141
0038cfec 41414141 41414141 41414141 41414141
0038cffc 41414141 41414141 41414141 41414141
0038d00c 41414141 41414141 41414141 41414141
0038d01c 41414141 41414141 41414141 41414141
0:000> t
eax=00000000 ebx=28a80020 ecx=4b8aa8e3 edx=0038cfac esi=001b7278 edi=001b7278
eip=6c3e755e esp=0038c20c ebp=0038dfb0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
bdlogicmain+0x755e:
6c3e755e 53 push ebx
第二個參數,edx現在存儲著指向我們字符串的指針,而且字符只有0x1004字節。
看看第一個參數ebx,這是它的信息:
#!bash
0:000> !address 28a80020
ProcessParametrs 007607f0 in range 00760000 00860000
Environment 09e98c48 in range 09e10000 0a210000
28a80000 : 28a80000 - 001b8000
Type 00020000 MEM_PRIVATE
Protect 00000004 PAGE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsageHeap
Handle 07790000
它指向一片空的內存,應該是剛剛申請的,指針位于頭部后0x20個字節。
#!bash
0:000> ?(28c38000-28a80000)
Evaluate expression: 1802240 = 001b8000
這片內存堆的可用大小為1802240字節。
這三個參數都知道了(1:ebx,一個很大空間的緩沖區,?2:edx,指向我們地址的指針,?3:0),繼續走,
#!bash
0:000> t
eax=00000000 ebx=28a80020 ecx=4b8aa8e3 edx=0038cfac esi=001b7278 edi=001b7278
eip=6c3e755f esp=0038c208 ebp=0038dfb0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
bdlogicmain+0x755f:
6c3e755f ff15c4f4666c call dword ptr [bdlogicmain!BrowserLogicInit+0x1cff64 (6c66f4c4)] ds:002b:6c66f4c4={MSVCR100!strncpy (6d0e2ad0)}
我們可以看到它其實是strncpy,這樣,我們就知道它的調用了。
我們p出來:
#!bash
0:000> p
eax=28a80020 ebx=28a80020 ecx=00000000 edx=00414141 esi=001b7278 edi=001b7278
eip=6c3e7565 esp=0038c208 ebp=0038dfb0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00200246
bdlogicmain+0x7565:
6c3e7565 8b4dfc mov ecx,dword ptr [ebp-4] ss:002b:0038dfac=4bb27741
看看返回值上有什么:
#!bash
0:000> dd eax
28a80020 656c6966 2f2f2f3a 552f3a45 73726573
28a80030 616c422f 53547473 7365442f 706f746b
28a80040 4141412f 41414141 41414141 41414141
28a80050 41414141 41414141 41414141 41414141
28a80060 41414141 41414141 41414141 41414141
28a80070 41414141 41414141 41414141 41414141
28a80080 41414141 41414141 41414141 41414141
28a80090 41414141 41414141 41414141 41414141
確實是拷貝進去了。
試驗環境:
1&2、VS2010,Debug+Release,也就是分別看看文中的malloc(0)會弄出什么情況。
3&4、gcc,Debug+Release查看malloc(0)是什么情況。
所謂正確的實踐是檢驗真理的唯一標準,我們分別看看會產生什么問題。
注:以下都是從調試器直接啟動的,堆是調試器友好的。
1、VC2010?Debug?+?malloc(0)
? 返回了一個非NULL的堆。
?
損壞。
2、?VC2010?Release?+?malloc(0)
? 情形類似
HEAP[testMalloc.exe]: Heap block at 005455B8 modified at 005455C1 past requested size of 1 Windows 已在 testMalloc.exe 中觸發一個斷點。
其原因可能是內存損壞,這說明 testMalloc.exe 中或它所加載的任何 DLL 中有 Bug。
原因也可能是用戶在 testMalloc.exe 具有焦點時按下了 F12。
輸出窗口可能提供了更多診斷信息。 程序“[4256] testMalloc.exe: 本機”已退出,返回值為 0 (0x0)。
3、gcc?Debug+malloc(0)
? 分配了一個非NULL但是可以釋放的堆。
? 成功覆蓋了堆的信息
? 同樣崩潰
4、gcc?Release+malloc(0)
? 情形類似
果不其然四個情況下都表明:VS和G++編譯器編譯后malloc(0)返回的不是NULL,而是一個活生生的堆,而且你動它一下,它就賴地上讓你賠錢了。
雖然和瀏覽器堆破壞的這一例不一樣,但是為了演示一下malloc(0)分配的內存到底為何不能操作,我們還是使用VC2010+Relase+malloc(0)?32位環境下的程序來試試看吧:
我們模擬一下那個創建小窗口前的一些準備工作:
#!cpp
// testMalloc.cpp : 定義控制臺應用程序的入口點。
//
#include "stdafx.h"
#include <stdlib.h>
#include <string>
#include <windows.h>
#define URL_LENGTH 26
int _tmain(int argc, _TCHAR* argv[])
{
TCHAR * test = (TCHAR *)malloc(0); //很不幸,這兒的參數是0
memset(test, 0x11 , URL_LENGTH); //為了方便查看,我把改成了0x11
TCHAR url[27] = _T("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
//正好26字,假設我們的url就是這個拉
int i = WideCharToMultiByte(CP_ACP, 0, url, -1, NULL, 0, 0,FALSE);
char * szTmp = new char[i];
WideCharToMultiByte(CP_ACP, 0, url, -1, szTmp, i, 0, FALSE);
memcpy(test, szTmp, i); //復制到我們的堆中
free(test);
return 0;
}
在wmain處下斷點,l-t之后并用p執行完malloc,然后查看當前分配來的堆的信息:
? 可以看到當前的堆內數據如下,其中數據使用了堆的填充模式abababe8?abababab,而這看起來像是一個已分配的堆空間的末尾的填充模式(為什么呢?因為如果分配了字節的話,堆會用baadf00d這個填充模式來填充將來可能會被使用的空間,而這個abababe8?blablabla則是像是這個填充模式的末尾,或者,更像是說ANSI字符串的中止符號“”的東西):
? 這是memset完,我們26個字符填充進去之后的效果:
??
這個相當于什么?相當于在一個堆的用戶可操作區域之外做寫入操作,這樣數據就已然超出了堆的申請大小,讓它活生生的演變成了堆溢出,之后程序還會對它進行free,必然是錯上加錯。
讓我們換一點代碼,我們把malloc(0)修改為malloc(27),重新載入。
這回,我們看到了一個新的填充模式,baadf00d?,而這正是我們之前提到的可用但是未初始化的堆空間的填充模式。
而如此如此運行之后,程序正常退出。
當在調試器下啟動進程的時候,堆管理器將修改所有新堆的請求,并且設置創建標志,用于啟用“調試友好”的堆,這會應用到內存里面所有堆,包括默認的進程堆,這種堆與原先的最大的差異是堆塊中包含了額外的“填充模式”區域,這個區域位于用戶可訪問的部分之后,堆管理器通過這個區域來確認堆的完整性。如果這個填充模式被修改了,堆管理器會立刻中斷進入調試器內。
堆管理器在分配堆時自動的使用某個填充模式來初始化某片內存,填充模式的內容取決于堆塊的狀態,當堆塊最初被返回給調用者時,堆管理器將使用填充模式來填充堆塊中用戶可訪問的部分,其中在填充模式中包含的值就是baadf00d,這表示這個堆塊雖然分配成功,但是沒有初始化。如果有程序在沒有初始化堆塊之前就對其進行解引用操作,就會產生一個存取違例的異常;如果程序正確的初始化了堆塊,那么程序將繼續執行,這個堆塊被釋放后,堆管理器將再次對堆塊中用戶可以訪問的部分進行填充,使用的值是feeefeee。設置完這些個填充模式之后,調試器將通過檢測這個填充模式的值是否被修改,來跟蹤在釋放后堆塊上所發生的訪問操作。 ?
這個函數在棧上分配空間,超過0x1000的每次按0x1000的大小(如果剩余未分配的大小大于0x1000的話)不斷在棧上分配內存(sub?ecx,1000h;?sub?eax,1000h;?test?[ecx],eax;?ecx是之前參數在棧中的地址,這樣可以檢測是否真的分配上了0x1000字節),test?[ecx],eax用來檢查是否棧已經不夠用了(棧的大小是固定的,所以如果分配的地址取不到的話[ecx]就會存取違例了),如果已經撐爆了通常會報告“Stack?Overrun”(也會看到Stack?Overflow)的報警。
當應用程序啟動時,程序的cookie(4字節(dword),無符號整型)被計算出來(偽隨機數)并保存在加載模塊的.data節中,在函數的開頭這個cookie被拷貝到棧中,位于EBP和返回地址的正前方(位于返回地址和局部變量的中間)。?
在函數的結尾處,程序會把這個cookie和保存在.data節中的cookie進行比較。?
如果不相等,就說明進程棧被破壞,進程必須被終止。?
為了盡量減少額外的代碼行對性能帶來的影響,只有當一個函數中包含字符串緩沖區或使用_alloca函數在棧上分配空間的時候編譯器才在棧中保存cookie。另外,當緩沖區至少于5個字節時,在棧中也不保存cookie。?
在典型的緩沖區溢出中,棧上的返回地址會被數據所覆蓋,但在返回地址被覆蓋之前,cookie早已經被覆蓋了,因此就導致了exploit的失效(但仍然可以導致拒絕服務),因為在函數的結尾程序會發現cookie?已經被破壞,接著應用程序會被結束。
微軟分類崩潰類型,用一系列牛逼算法所生成并使用的一個標識符號。
官方是修復了,不過縱觀全局他這代碼也太粗心了,俺的最大的建議就是每個返回值都檢查一遍,千萬別嫌麻煩……
0x06?參考資料
Mario?Heweardt,?Daniel?Pravat?《windows高級調試》
http://bbs.pediy.com/archive/index.php?t-126858.html
http://msdn.microsoft.com/en-us/library/ee480951.aspx
?
?