windows用戶端崩潰分析和調試

來源:互聯網
上載者:User

標籤:windows   調試   c++   

本文介紹windows上崩潰分析的一些手段,順便提多進程調試、死結等。

1.崩潰分析過程
1.1 確認錯誤碼
無論是用windbg還是用vs,首先應該注意的是錯誤碼,而90%以上的崩潰都是非法訪問。
在非法訪問時,可以看一下訪問的目標地址。地址是0,或者離0很近(0x00000008或0xfffffffc),
一般和null 指標相關。如果是一個貌似正常的地址,一般是對象已析構後訪問其資料,或者堆破壞。

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,找到對應的模組,位移量,打上斷點,可以在本地還原出崩潰時的執行環境。當然,
在本地執行到對應位置時不一定崩潰,但是,有了更多上下文資訊,可以比較容易確定對應的C++操作。

使用IDA:
可以使用IDA讓彙編代碼更好看,較容易分析流程。

關閉alsr,指定建議模組載入地址:
這樣可能使得更容易分析。但是會使得安全性降低,可以用於小流量版本。

ln指令:
windbg中ln指令可以根據地址,還原出對應的資訊,比如該地址是在某個類的某個方法中。有時可能會
還原出幾個資訊:A::foo() + 0x??, B::foo1() + 0x??,這就需要自己根據上下文判斷了。

代碼最佳化:
代碼最佳化使得分析更難,可以嘗試改變一些編譯選項,降低最佳化層級,保留棧楨,關閉應用程式全域最佳化,
使得在release下的分析容易些。

所見並非真實:
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,使得調試更加容易。另外可以開多tab,然後關閉,然後再開,來測試同一個進程
有多外activex執行個體的情況。更進一步,可以自己調用IWebBrowser2,來類比更多的情況。

np外掛程式:
chrome中這個簡單些。一個外掛程式一個進程,多個執行個體,共用主線程。
還有些開源工具將activex適配為np外掛程式,使得可以在chrome中調用ax,調試。

多機調試:
前面說過,windbg,vs都支援。

死結:
死結現場不會是線上問題(可以通過一定手段,使得線上上發生死結時報告,但是基本上沒用過,對應的
手段線上下有玩過)。線下問題一般會有現場,或者能拿到full dump。一般使用windbg來看,用~*kb或者
這系列的命令看看線程都在幹什麼。而重點關注的則是WaitForSingleObjectEx之類的調用。通過分析調用
對應的參數,進一步能還原出,拿到 分發器 對象後不歸還的線程是誰。或者也可以!runaway找到佔用CPU
高的線程,然後看看該線程在幹什麼。線程迴圈等待,則死結了。線程一直在那裡跑,可能是死迴圈了。

線下的死結檢測,一般可以向主線程發一個訊息來實現。

warning:有可能某個線程拿到分發器對象,但是該線程已經掛掉了。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.