本文介紹windows上崩潰分析的一些手段,順便提多程序除錯、死鎖等。
1.崩潰分析過程
1.1 確認錯誤碼
不管是用windbg還是用vs。首先應該注意的是錯誤碼,而90%以上的崩潰都是非法訪問。
在非法訪問時。能夠看一下訪問的目標位址。
位址是0,或者離0非常近(0x00000008或0xfffffffc)。
一般和空指標相關。假設是乙個貌似正常的位址,通常是物件已析構後訪問其資料,或者堆破壞。
1.2確認崩潰相應的c++操作
什麼是確認崩潰相應的c++操作:
比方非法訪問,通常得有個mov指令才會觸發記憶體訪問,然後導致崩潰。而mov指標相應於c++的哪一步呢?
比方a->b->c->foo();
在看到源**時,會定位於這一行,可是,並不清楚是哪一步訪問失敗。
所以這個時候要檢視相應彙編**。
大概會有好幾個mov,簡單的分析就知道是哪一步時訪問失敗。
對編碼的影響:
這就要求,不要在單個語句中寫太複雜的東西比方
x ? b[i] : y > 0 ? c->member[8] : *ptr;
這種**崩潰。要還原到錯誤的地方非常難。
虛函式呼叫:
通常mov edx, dword ptr [ecx]
mov edx, dword ptr [edx+0x??
]call edx
意味著虛函式呼叫。每一行都可能是崩潰位置(在call內崩潰時,vs會標註出下一條語句的位置)。
在第一行崩潰意味著拿到乙個非法指標,可能是空,也能夠指向非法位址。
在第二行崩潰意味著物件已經析構,ecx指向能夠訪問,可是值不正確。所以拿到的虛函式表不正確。
在最後一行崩潰一般另乙個崩潰棧,可是看不到棧幀,在vs中相應的棧楨顯示乙個位址。沒其他內容。
也一般意味著物件析構。
物件及析構函式:
析構函式是常常發生崩潰的地方,假設沒實使用者提供的析構函式,會定位到幾行彙編。所以沒事,就寫個
析構函式吧,至少能定位到是析構函式。
在析構函式外部還會有一大堆彙編**,裡面是物件成員析構的**。崩潰在裡面的時候,難以確認
是哪個物件析構。
假設用指標,在析構函式中主動呼叫release或delete,這樣能夠顯示呼叫,不用去猜是誰在析構,當然
用指標或值物件。在邏輯上各有其優點。在此不表。
假設崩潰位置是call或jmp到某個a::~a()的位置。能夠推測到析構的物件的型別是a。
物件析構順序是從後往前,從子類到基類,依據這點,結合崩潰位置。能夠推測誰析構。
利用物件布局,比方成員在物件中的偏移,可能有得於推測誰析構出問題。
能夠人為地在物件布局中引入一些填充的位元組,使得能看到this物件(線上崩潰沒有堆上的資料,由於
擷取fulldump並上報有操作上的難度。所以this指向堆上時可能看不到,而在棧上則有可能),有利於分析。
還原上下文:
線上拿到的dump的資訊少,不能除錯。所以能夠依據崩潰所在模組。崩潰在模組中的偏移量,在本地
除錯相應的bin,找到相應的模組。偏移量。打上斷點,能夠在本地還原出崩潰時的執行環境。
當然,在本地執行到相應位置時不一定崩潰,可是,有了很多其它上下文資訊。能夠比較easy確定相應的c++操作。
使用ida:
能夠使用ida讓彙編**更好看,較easy分析流程。
有時可能會
還原出幾個資訊:a::foo() + 0x?
?, b::foo1() + 0x??。這就須要自己依據上下文推斷了。
**優化:
**優化使得分析更難,能夠嘗試改變一些編譯選項,減少優化級別,保留棧楨,關閉應用程式全域性優化,
使得在release下的分析easy些。
所見並不是真實:
windbg和vs看到的棧幀可能是假的:可能在中間某一些可能已經亂了,可能棧楨省略使得vs分析的結果不正確。
(通常是vs分析得不正確。另外也有windbg杯具的時候)
對於在中間已經亂掉的棧。能夠依據返回位址。棧引數,棧楨省略資料等,又一次還原出棧。只是在90%的情況
下,即使還原出來,也不知道下一步怎麼辦。
物件還原:
線上崩潰沒有堆,能夠將感興趣的物件拷貝複製到棧上(自己得控制深拷貝)。然後崩潰上報中就能夠看到
物件的狀態了。
(注意**優化可能使得拷貝無效)
1.3c++上的邏輯
在確定崩潰和c++操作的關係後,就是自己邏輯上的問題了,基本上能遇到的問題都是物件生命週期管理
不當,進而造成非法訪問。
指標的判空能規避一處的非法訪問,可是能夠把錯誤進一步擴散。指標判空。且用且珍惜。
在設計或編碼時,應當考慮**的可除錯性。比方chromium中的執行緒池中,加入任務時,會生成當前呼叫
資訊。和task繫結,以使於定位錯誤。
1.4堆破壞
基本無解,崩潰現場和引入錯誤的點相差太遠。
僅僅能盡人事,聽天命了。
比方,開一下頁堆。存在一定概率使得崩潰出現,看人品。
比方。換乙個crt堆。或者自己寫個,增強錯誤檢測。
比方。crt本身。尤其是除錯的堆,堆上有些填充資訊。使得在看到的時候或多或少嘆口氣:大概認識這些
填充資訊,想要很多其它的資訊,難啊。。
比方。能夠自己寫個偵錯程式,自己插入頁堆,或者使用系統的頁堆。使得檢測自己主動化,然後通過大規模資料
使之重現。
2.其他
多程序除錯:
能夠通過在
hkey_local_machine\software\microsoft\windows nt\currentversion\image file execution options
建立關心的程序名的項。填上debugger鍵值,值為偵錯程式路徑,使得程序建立時就attach。(gflags也是
改這裡)。
可是問題是,有的模組是按需載入的,在這個時候還不能在相應模組中下斷點。
另外能夠自己在關心的位置加上messagebox或atlassert之類的**,等彈出對話方塊時再attach到相應程序。
activex:
attach方法同理。
可是,ie的多程序模型會使得attach不方便。
在ie9及以上,其程序模型是乙個主程序,控制多個tab程序,按一定規則建立tab程序,將任務分派到
tab上。同乙個網頁開啟兩次,可能分配到不同的程序上,也可能是同樣的程序。在同乙個程序中,同一
個activex可能有多個例項,並且每乙個例項相應的主線程還不一定是同乙個執行緒。
通常會控制僅僅開乙個tab,使得除錯更加easy。另外能夠開多tab。然後關閉,然後再開,來測試同乙個程序
有多外activex例項的情況。更進一步,能夠自己呼叫iwebbrowser2,來模擬很多其它的情況。
np外掛程式:
chrome中這個簡單些。乙個外掛程式乙個程序,多個例項。共享主線程。
還有些開源工具將activex適配為np外掛程式,使得能夠在chrome中呼叫ax。除錯。
多機除錯:
前面說過,windbg,vs都支援。
一般使用windbg來看,用~*kb或者
這系列的命令看看執行緒都在幹什麼。而重點關注的則是waitforsingleobjectex之類的呼叫。
通過分析呼叫
相應的引數。進一步能還原出,拿到 分發器 物件後不歸還的執行緒是誰。
或者也能夠!runaway找到占用cpu
高的執行緒,然後看看該執行緒在幹什麼。執行緒迴圈等待,則死鎖了。執行緒一直在那裡跑,可能是死迴圈了。
線下的死鎖檢測,一般能夠向主線程發乙個訊息來實現。
warning:有可能某個執行緒拿到分發器物件,可是該執行緒已經掛掉了。
利用「崩潰軌跡」分析崩潰
原文出自 聽雲技術部落格 崩潰,嚴重傷害使用者的情感,嚴重損害使用者體驗,罪惡行徑簡直令人髮指,特請xx獅 xx猿火速緝拿案犯歸案,刻不容緩,欽此。上圖所示,已經定位到某原始檔的某行,再加上崩潰message,崩潰的原因就顯而易見了。但有些崩潰的原因就不是那麼顯而易見了,往往需要尋找更多蛛絲馬跡來定...
利用「崩潰軌跡」分析崩潰
更多技術乾貨請戳 聽雲部落格 崩潰,嚴重傷害使用者的情感,嚴重損害使用者體驗,罪惡行徑簡直令人髮指,特請xx獅 xx猿火速緝拿案犯歸案,刻不容緩,欽此。上圖所示,已經定位到某原始檔的某行,再加上崩潰message,崩潰的原因就顯而易見了。但有些崩潰的原因就不是那麼顯而易見了,往往需要尋找更多蛛絲馬跡...
iOS應用崩潰(三) 崩潰日誌
當我們在模擬器上除錯時,可能經常遇到下面的記憶體訪問錯誤 該錯誤是對乙個已經釋放的物件進行操作,定位如下 2 終端輸入 info malloc history 命令,即可得到堆疊資訊,從而分析具體問題所在 gdb info malloc history 0x12e4b0 3 也可輸入如下資訊 gdb...