windows客戶端崩潰分析和除錯

2021-06-26 10:02:57 字數 3760 閱讀 1036

本文介紹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,找到對應的模組,偏移量,打上斷點,可以在本地還原出崩潰時的執行環境。當然,

在本地執行到對應位置時不一定崩潰,但是,有了更多上下文資訊,可以比較容易確定對應的c++操作。

使用ida:

可以使用ida讓彙編**更好看,較容易分析流程。

這樣可能使得更容易分析。但是會使得安全性降低,可以用於小流量版本。

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都支援。

死鎖:這系列的命令看看執行緒都在幹什麼。而重點關注的則是waitforsingleobjectex之類的呼叫。通過分析呼叫

對應的引數,進一步能還原出,拿到 分發器 物件後不歸還的執行緒是誰。或者也可以!runaway找到占用cpu

高的執行緒,然後看看該執行緒在幹什麼。執行緒迴圈等待,則死鎖了。執行緒一直在那裡跑,可能是死迴圈了。

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

warning:有可能某個執行緒拿到分發器物件,但是該執行緒已經掛掉了。

胖客戶端 瘦客戶端和富客戶端

以c s結構開發的網路應用程式,需要為客戶端開發專用的客戶端軟體,相對而言其客戶端比較龐大,在客戶端可以實現很多功能,分擔伺服器的負擔,屬於胖客戶端型別。以b s結構開發的web應用,其客戶端只是乙個瀏覽器,所有業務邏輯由伺服器端進行處理,相對而言客戶端比較瘦小,故稱為瘦客戶端。目前比較流行的一種開...

非同步客戶端和同步客戶端

先寫下我的理解,方便後邊閱讀資料校驗。一 同步客戶端 比如乙個連線有兩個請求,請求1 和 請求2,請求1 先發起請求,請求2後發起請求,則請求2 要等待請求1 響應完成才能接收到響應。舉個棗子,httpclient 傳送get請求,執行緒會一致阻塞,直到有響應結果。二 非同步客戶端 比如乙個連線有兩...

瘦客戶端 胖客戶端 智慧型客戶端

胖客戶端模式將應用程式處理分成了兩部分 由使用者的桌面計算機執行的處理和最適合乙個集中的伺服器執行的處理。乙個典型的胖客戶端包含乙個或多個在使用者的pc上執行的應用程式,使用者可以檢視並運算元據 處理一些或所有的業務規則 同時提供乙個豐富的使用者介面做出響應。伺服器負責管理對資料的訪問並負責執行一些...