深夜除錯某瀏覽器記憶體損壞的小記錄
0x00 引言
@瞌睡龍讓俺仔細介紹一下,所以我就把之前文章/papers/?id=1020的一節的東西單獨提出來了,之前百度瀏覽器5.0版本子視窗堆溢位的那版的程式我已經找不太到了,找到最接近的版本修復的只剩一個棧空間不足的問題而已了,這個小毛病我就擱在事後分析裡面好了。
我找到的早期版本,比如4.5版,它的記憶體損壞和5.x的記憶體損壞效果類似,而且崩潰邏輯、位置都大體相同,5.x那個小報告中我提到的“低版本程式不測試直接轉移到高版本”就是讓這一個問題橫跨幾個世紀的主要原因。
當然,以下是我晚上幾個小時調(口)試(胡)出來的,由於家裡的電腦下WDK一直失敗,所以沒用上最新科技,只用了一個很老的windbg版本,有的地方還有些問題,所以我會粘來一點之前草稿裡的資料,不過我確定這個對結果沒有影響的。還有就是由於程式碼上班的時候調了一點,下班後回家繼續調了後面的(俺是新開的例程),所以可能會有一些神推理名推測和超劇情發展的東西,這個請見諒。
由於程式碼實在太長,所以我會適當的標記出我在幹什麼,以及我想要得到什麼結果,所以如果你看到了莫名其妙的黑色粗體標記,那肯定是我的註解。另外,由於這個windbg貼的程式碼本身實在已經是錯綜複雜了,所以我不會一句句的解釋了,我會直接介紹函式在作甚,而且由於個人水平渣,寫的時候卡殼了多次,所以這我已經預感到難免會出問題,這個也請大家見諒,各位發現不對的話請及時糊我熊臉!
0x01 基本需求
一個偵錯程式是必要的,由於涉及事後除錯和即時除錯,俺就使用windbg來進行啦。由於目標程式是32位的,所以我選擇了x86版的windbg。作業系統是Windows 7 sp1 簡體中文版,說是oem但是看起來像盜版系統的系統。
然後就是幾個基本的命令,t 步入,p 步過, gu 執行到返回,dd 檢視dword, da 檢視ansi, k 顯示呼叫棧, poi 析取指標,等等,遇到新不明物種的話,windbg的F1裡面的介紹是非常詳盡的。
0x02 簡單的事後分析(介紹)
事後分析我們就用百度瀏覽器5.0的一個棧記憶體不夠分導致的Stack Buffer Overrun為例好了,poc還是那個,口胡我第一,那麼開始唄。
0x02a 開始
首先,我們可以確定的是我們的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資訊(分類漏洞)、出錯的位置、出錯的語句、它並沒有對這個異常的處理程式等等,還有最重要的資訊:我們根本沒它的符號,淦。
0x01b 檢視崩潰附近的資料
好在它沒有做什麼其他的事兒,我們檢視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,我們需要的是實時除錯,下面進入正題。
0x03 實時除錯
下面使用的目標程式是百度瀏覽器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+ebw-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+ebw-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
確實是複製進去了。
0x04 試驗
試驗環境:
1&2、VS2010,Debug+Release,也就是分別看看文中的malloc(0)會弄出什麼情況。
3&4、gcc,Debug+Release檢視malloc(0)是什麼情況。
所謂正確的實踐是檢驗真理的唯一標準,我們分別看看會產生什麼問題。
注:以下都是從偵錯程式直接啟動的,堆是偵錯程式友好的。
0x04a 直觀的編譯、執行
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,而是一個活生生的堆,而且你動它一下,它就賴地上讓你賠錢了。
0x04b 具體發生了什麼?
雖然和瀏覽器堆破壞的這一例不一樣,但是為了演示一下malloc(0)分配的記憶體到底為何不能操作,我們還是使用VC2010+Relase+malloc(0) 32位環境下的程式來試試看吧:
0x04b.1 編譯源程式
我們模擬一下那個建立小視窗前的一些準備工作:
#!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;
}
0x04b.2 載入windbg中:
在wmain處下斷點,l-t之後並用p執行完malloc,然後檢視當前分配來的堆的資訊:
可以看到當前的堆內資料如下,其中資料使用了堆的填充模式abababe8 abababab,而這看起來像是一個已分配的堆空間的末尾的填充模式(為什麼呢?因為如果分配了位元組的話,堆會用baadf00d這個填充模式來填充將來可能會被使用的空間,而這個abababe8 blablabla則是像是這個填充模式的末尾,或者,更像是說ANSI字串的中止符號“”的東西):
這是memset完,我們26個字元填充進去之後的效果:
這個相當於什麼?相當於在一個堆的使用者可操作區域之外做寫入操作,這樣資料就已然超出了堆的申請大小,讓它活生生的演變成了堆溢位,之後程式還會對它進行free,必然是錯上加錯。
0x04b.3 證明我們的想法
讓我們換一點程式碼,我們把malloc(0)修改為malloc(27),重新載入。
這回,我們看到了一個新的填充模式,baadf00d ,而這正是我們之前提到的可用但是未初始化的堆空間的填充模式。
而如此如此執行之後,程式正常退出。
0x04c 幾個小名詞
除錯友好
當在偵錯程式下啟動程式的時候,堆管理器將修改所有新堆的請求,並且設定建立標誌,用於啟用“除錯友好”的堆,這會應用到記憶體裡面所有堆,包括預設的程式堆,這種堆與原先的最大的差異是堆塊中包含了額外的“填充模式”區域,這個區域位於使用者可訪問的部分之後,堆管理器透過這個區域來確認堆的完整性。如果這個填充模式被修改了,堆管理器會立刻中斷進入偵錯程式內。
填充模式
堆管理器在分配堆時自動的使用某個填充模式來初始化某片記憶體,填充模式的內容取決於堆塊的狀態,當堆塊最初被返回給呼叫者時,堆管理器將使用填充模式來填充堆塊中使用者可訪問的部分,其中在填充模式中包含的值就是baadf00d,這表示這個堆塊雖然分配成功,但是沒有初始化。如果有程式在沒有初始化堆塊之前就對其進行解引用操作,就會產生一個存取違例的異常;如果程式正確的初始化了堆塊,那麼程式將繼續執行,這個堆塊被釋放後,堆管理器將再次對堆塊中使用者可以訪問的部分進行填充,使用的值是feeefeee。設定完這些個填充模式之後,偵錯程式將透過檢測這個填充模式的值是否被修改,來跟蹤在釋放後堆塊上所發生的訪問操作。
__alloca_probe
這個函式在棧上分配空間,超過0x1000的每次按0x1000的大小(如果剩餘未分配的大小大於0x1000的話)不斷在棧上分配記憶體(sub ecx,1000h; sub eax,1000h; test [ecx],eax; ecx是之前引數在棧中的地址,這樣可以檢測是否真的分配上了0x1000位元組),test [ecx],eax用來檢查是否棧已經不夠用了(棧的大小是固定的,所以如果分配的地址取不到的話[ecx]就會存取違例了),如果已經撐爆了通常會報告“Stack Overrun”(也會看到Stack Overflow)的報警。
Security cookie
當應用程式啟動時,程式的cookie(4位元組(dword),無符號整型)被計算出來(偽隨機數)並儲存在載入模組的.data節中,在函式的開頭這個cookie被複製到棧中,位於EBP和返回地址的正前方(位於返回地址和區域性變數的中間)。
[buffer][cookie][savedEBP][savedEIP]
在函式的結尾處,程式會把這個cookie和儲存在.data節中的cookie進行比較。
如果不相等,就說明程式棧被破壞,程式必須被終止。
為了儘量減少額外的程式碼行對效能帶來的影響,只有當一個函式中包含字串緩衝區或使用_alloca函式在棧上分配空間的時候編譯器才在棧中儲存cookie。另外,當緩衝區至少於5個位元組時,在棧中也不儲存cookie。
在典型的緩衝區溢位中,棧上的返回地址會被資料所覆蓋,但在返回地址被覆蓋之前,cookie早已經被覆蓋了,因此就導致了exploit的失效(但仍然可以導致拒絕服務),因為在函式的結尾程式會發現cookie 已經被破壞,接著應用程式會被結束。
BUCKET
微軟分類崩潰型別,用一系列牛逼演算法所生成並使用的一個識別符號號。
0x05 修復
官方是修復了,不過縱觀全域性他這程式碼也太粗心了,俺的最大的建議就是每個返回值都檢查一遍,千萬別嫌麻煩……
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
相關文章
- ArkTS 的記憶體快照與記憶體洩露除錯2024-10-29記憶體洩露除錯
- 谷歌Chrome瀏覽器引入省記憶體/省電模式:減少記憶體佔用2022-09-16谷歌Chrome瀏覽器記憶體模式
- 記憶體洩漏除錯工具2024-03-17記憶體除錯
- Node除錯指南-記憶體篇2019-02-16除錯記憶體
- 記記憶體條硬體損壞藍色畫面的 dump 檔案分析2024-07-10記憶體
- 谷歌瀏覽器除錯技巧2018-04-21谷歌瀏覽器除錯
- Google瀏覽器外掛之快閃記憶體過濾器2019-07-04Go瀏覽器記憶體過濾器
- 【瀏覽器】(內附面試題)瀏覽器中堆疊記憶體的底層處理機制2020-02-10瀏覽器面試題記憶體
- 瀏覽器performance工具介紹及記憶體問題表現與監控記憶體的幾種方式2021-01-19瀏覽器ORM記憶體
- 使用瀏覽器inspect除錯app2022-12-18瀏覽器除錯APP
- 10.3 除錯事件轉存程式記憶體2023-10-05除錯事件記憶體
- 用瀏覽器測試幾種閉包占用記憶體的情況2018-06-21瀏覽器記憶體
- 微軟發現一個 ChromeOS 遠端記憶體損壞漏洞2022-11-19微軟Chrome記憶體
- 使用 JavaScript 操作瀏覽器歷史記錄 API2020-11-19JavaScript瀏覽器API
- 【問題記錄】- 谷歌瀏覽器 Html生成PDF2021-08-08谷歌瀏覽器HTML
- 前端chrome瀏覽器除錯總結2018-05-16前端Chrome瀏覽器除錯
- 谷歌瀏覽器除錯移動端2018-07-23谷歌瀏覽器除錯
- 案例:商品瀏覽記錄的實現2021-09-09
- GDB除錯使用記錄2020-11-02除錯
- 善用瀏覽器的一些除錯技巧2023-12-28瀏覽器除錯
- 記一次 .NET 某餐飲小程式 記憶體暴漲分析2023-09-21記憶體
- 【PB案例學習筆記】-02 目錄瀏覽器2024-05-19筆記瀏覽器
- 前端入門技巧之瀏覽器除錯2021-12-03前端瀏覽器除錯
- Mac上的Safari瀏覽器如何檢視歷史記錄?2022-04-08Mac瀏覽器
- UE4 記憶體寫壞導致異常崩潰問題記錄2023-04-21記憶體
- C/C++程式除錯和記憶體檢測2019-03-19C++除錯記憶體
- Python 學習除錯記錄2020-10-27Python除錯
- GitHub學習除錯記錄2020-11-09Github除錯
- Supervisor 安裝除錯記錄2021-07-05除錯
- win10系統刪除快速訪問中瀏覽記錄的方法2018-12-23Win10
- windows10系統谷歌瀏覽器佔用記憶體太多怎麼辦2018-04-04Windows谷歌瀏覽器記憶體
- 微軟曝新IE瀏覽器全系指令碼引擎記憶體損壞零日遠端攻擊漏洞,野外已利用暫無補丁,附緩解命令2020-01-18微軟瀏覽器指令碼記憶體
- 記錄node記憶體瓶頸分析2018-03-23記憶體
- 深信服EDR快速釋出Windows condrv.sys記憶體損壞漏洞防護2021-01-21Windows記憶體
- NIO的JVM記憶體和機器記憶體的選擇2019-03-18JVM記憶體
- 瀏覽器錄屏技術:探索網頁內容的視覺記錄之道2024-02-23瀏覽器網頁視覺
- 筆記|軟體除錯的技巧2019-07-07筆記除錯
- 後臺任何搜尋功能,用360瀏覽器都會提示記憶體溢位2020-04-04瀏覽器記憶體溢位