tft每日頭條

 > 科技

 > wind數據庫如何查深度報告

wind數據庫如何查深度報告

科技 更新时间:2024-12-17 10:59:55

在日常分析C 軟件異常的日常工作中,大多數情況下我們都是使用Windbg去靜态分析dump文件去排查軟件異常的,今天我們就來詳細地講一下如何使用Windbg去靜态分析dump文件,以供參考。

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)1

image.png

1、概述

基本大部分軟件都内置了異常捕獲模塊,在軟件發生閃退崩潰時,都會彈出相關的提示框,比如PC版的微信在崩潰時,其内置的異常捕獲模塊會捕獲到并生成日志及dump文件,同時會彈出如下的發送錯誤報告的提示框:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)2

image.png

提示框的下方會自動帶上崩潰相關的文件,其中最後一個文件就是我們要講的dump文件,點擊确定則會将這些文件發送到騰訊遠端的服務器上。騰訊後台的運維人員就會收到通知,然後到服務器上将dump等文件下載下去去分析。

有些軟件可能沒有上傳崩潰日志到服務器的功能,捕捉到異常時會自動将dump文件保存到指定的路徑中,事後可以到該路徑中取到對應的dump文件。如果客戶機器上遇到崩潰,可以和客戶聯系,讓客戶幫忙從對應的路徑中取來dump文件。

下面就來詳細講一下拿到dump文件之後如何使用Windbg去靜态分析dump文件。

2、靜态分析dump文件的一般步驟

用Windbg打開dump文件,先使用.ecxr命令切換到異常的上下文,然後根據切換成功後顯示的異常類型(ExceptionCode值對應的含義,比如0xC0000005對應的就是AccessViolation内存訪問違例)、發生崩潰的那條彙編指令及相關寄存器的值。通過查看彙編指令及各寄存器的值,可以初步判斷當前發生的是什麼異常,比如C 類空指針、内存訪問違例。

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)3

image.png

接着,使用kn/kv/kp查看函數調用堆棧,看看是調用了什麼函數觸發的異常。調用堆棧中會顯示函數所在模塊信息,一般我們需要拿到這些模塊的pdb文件,加載pdb文件後函數堆棧中才會顯示具體的函數名和行号的。我們需要使用lm命令查看這些模塊的時間戳,然後找來這些模塊對應的pdb文件,然後将pdb文件的路徑設置到windbg中。

然後,根據函數調用堆棧中顯示的函數名及行号,對照着源代碼去分析發生異常的原因。有時候為了搞清楚發生異常的本質,我們還需要使用IDA查看相關二進制文件的彙編代碼,查看一下發生異常的那條彙編指令的上下文,對照着C 源碼,看看那條彙編指令為啥會出現異常。

3、分析實例說明

我們為了方便展開講解,我們特意使用VisualStudio創建了一個基于MFC對話框的exe程序工程,對話框中有個名為button1的按鈕,如下所示:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)4

image.png

我們在此按鈕的響應中添加了一段引發崩潰的測試代碼,故意讓程序産生崩潰,測試然後拿到崩潰時的dump文件。

具體的測試代碼如下所示:

// 添加的一段測試代碼 SHELLEXECUTEINFO *pShExeInfo = NULL; int nVal = pShExeInfo->cbSize; // 通過空指針訪問結構體成員,導緻崩潰 CString strTip; strTip.Format( _T("nVal=%d."), nVal ); AfxMessageBox( strTip );

代碼中使用到的結構體SHELLEXECUTEINFO 定義如下:

typedef struct _SHELLEXECUTEINFOW { DWORD cbSize; // in, required, sizeof of this structure ULONG fMask; // in, SEE_MASK_XXX values HWND hwnd; // in, optional LPCWSTR lpVerb; // in, optional when unspecified the default verb is choosen LPCWSTR lpFile; // in, either this value or lpIDList must be specified LPCWSTR lpParameters; // in, optional LPCWSTR lpDirectory; // in, optional int nShow; // in, required HINSTANCE hInstApp; // out when SEE_MASK_NOCLOSEPROCESS is specified void *lpIDList; // in, valid when SEE_MASK_IDLIST is specified, PCIDLIST_ABSOLUTE, for use with SEE_MASK_IDLIST & SEE_MASK_INVOKEIDLIST LPCWSTR lpClass; // in, valid when SEE_MASK_CLASSNAME is specified HKEY hkeyClass; // in, valid when SEE_MASK_CLASSKEY is specified DWORD dwHotKey; // in, valid when SEE_MASK_HOTKEY is specified union { HANDLE hIcon; // not used #if (NTDDI_VERSION >= NTDDI_WIN2K) HANDLE hMonitor; // in, valid when SEE_MASK_HMONITOR specified #endif // (NTDDI_VERSION >= NTDDI_WIN2K) } DUMMYUNIONNAME; HANDLE hProcess; // out, valid when SEE_MASK_NOCLOSEPROCESS specified } SHELLEXECUTEINFOW, *LPSHELLEXECUTEINFOW; #ifdef UNICODE typedef SHELLEXECUTEINFOW SHELLEXECUTEINFO; typedef LPSHELLEXECUTEINFOW LPSHELLEXECUTEINFO; #else typedef SHELLEXECUTEINFOA SHELLEXECUTEINFO; typedef LPSHELLEXECUTEINFOA LPSHELLEXECUTEINFO; #endif // UNICODE

在測試代碼中定義了SHELLEXECUTEINFO結構體指針pShExeInfo,并初始化為NULL,然後并沒有給該指針賦一個有效的結構體對象地址,然後使用pShExeInfo訪問結構體的cbSize成員的内存,因為pShExeInfo中的值為NULL,所以結構體cbSize成員的内存地址是結構體對象起始地址的偏移,因為結構體對象地址為NULL,cbSize成員位于結構體的首位,所以cbSize成員就是結構體對象的首地址,就是NULL,所以就訪問64KB小地址内存塊的異常,引發内存訪問違例,導緻程序發生崩潰閃退。

至于如何在程序中設置異常捕獲模塊去捕獲異常、自動生成dump文件,可以嘗試使用google開源的CrashRpt庫,我們産品很早就有了,具體怎麼獲取目前還沒有研究過,大家需要的話可以到網上搜一下。

4、用Windbg打開dump文件,初步分析

使用Windbg打開dump文件(直接将dump文件拖入到Windbg),然後輸入.ecxr命令,切換到異常的上下文,就可以發生的異常類型、發生了異常崩潰的那條彙編及崩潰時的各個寄存器的值。

首先,我們查看一下發生的異常類型,比如AccessViolation内存訪問違例、StackOverflow線程棧溢出的異常,這樣對異常有個初步的認知,如下:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)5

image.png

接着就是查看發生異常的那條指令,查看指令的構成以及崩潰時的各個寄存器的值,可能能初步估計出發生異常的原因。如果指令中訪問了一個很小的地址或者訪問了一個很大的地址,都會觸發内存訪問違例。

1)訪問64KB小地址内存區,引發訪問違例在Windows系統中,64KB以内的内存地址是禁止訪問的,如果程序訪問這個範圍内的内存,則會觸發内存訪問違例,系統會強行将進程終止掉。2)訪問了内核态的大地址内存區,引發訪問違例對于32位程序,系統會給進程分配4GB的虛拟内存,一般情況下用戶态和内核态會各占一半,即各占2GB,我們編寫的代碼基本都是運行在用戶态的,用戶态的代碼時不能訪問内核态内存地址的(内核态地址是供系統内核模塊使用的),如果崩潰指令中訪問了一個很大的内存地址,超過用戶态的地址範圍0-2GB,内存地址大于0x8000000,則會觸發内存違例,因為用戶态的代碼是禁止訪問内核态内存地址的。

如果彙編指令中使用到了ecx寄存器進行地址計算,去訪問計算出來的内存地址,如果訪問了一個小内存地址并且ecx寄存器為0,那麼這個崩潰可能是空指針引發的。在C 中,在調用C 類的成員函數時是通過ecx寄存器傳遞C 對象地址的,所以在通過C 類對象去調用虛函數(調用虛函數時的二次尋址)及通過C 對象去訪問對象中的數據成員時,都會用到ecx寄存器的。

上面故意添加的會引發崩潰的測試代碼,運行後産生dump文件,我們來分析一下這個dump文件。用Windbg打開dump文件後,輸入.excr命令,接着輸入kn命令,查看到如下的結果:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)6

image.png

首先,這是Access Violation内存訪問違例的異常。其次,從崩潰的這條彙編指令來看,是訪問了小地址0x00000000地址,這是訪問了小于64KB小地址内存區,這個範圍的地址是禁止訪問的,所以引發了内存訪問違例。從彙編指令及寄存器的值來看,看不出來什麼明顯的線索。

所以接着輸入了kn命令,将函數調用堆棧打印出來。函數調用堆棧是從下往上看的,最上面一行就是最後調用的一個函數,也是崩潰的那條彙編指令所在的函數。從函數調用堆棧的最後一幀調用的函數來看,程序的崩潰是發生在TestDlg.exe文件模塊中,不是其他的dll模塊。顯示的函數地址是相對TestDlg.exe文件模塊起始地址的偏移,為啥看不到模塊中具體函數名稱呢?那是因為Windbg找不到TestDlg.exe對應的pdb文件,pdb文件中包含對應的二進制文件中的函數名稱及變量等信息,Windbg加載到pdb文件才能顯示完整的函數名。

查看函數調用堆棧的命令,除了kn,還有kv和kp命令,其中kv還可以看到函數調用堆棧中調用函數時傳遞的參數,如下所示:

我們需要取來pdb符号庫文件,去查看具體的函數名及行号的,這樣才好找到直接的線索的。下面就來看看如何獲取到TestDlg.exe模塊的pdb文件。

5、找到pdb文件,設置到windbg中,查看完整的函數調用堆棧

如何才能找到TestDlg.exe文件對應的dpb文件?我們可以通過查看TestDlg.exe文件的時間戳找到文件的編譯時間,通過編譯時間找到文件對應的pdb文件。在Windbg中輸入lm vm TestDlg命令,可以查看到TestDlg.exe文件的詳細信息,其中就包含文件的時間戳:(當前的lm命令中使用m通配符參數,所以在TestDlg後面加上了号)

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)7

image.png

可以看到文件是2022年6月25日8點26分23秒生成的,就可以找到對應時間點的pdb文件了。

一般在公司正式的項目中,通過自動化軟件編譯系統,每天都會自動編譯軟件版本,并将軟件的安裝包及相關模塊的pdb文件保存到文件服務器中,如下所示:

這樣我們就可以根據模塊的編譯時間找到對應版本的pdb文件了。

我們找到了TestDlg.exe對應的pdb文件TestDlg.pdb,将其所在的路徑設置到Windbg中。點擊Windbg菜單欄中的File->Symbol File Path...,打開設置pdb文件路徑的窗口,将pdb文件的路徑設置進去,如下所示:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)8

image.png

點擊OK按鈕之前,最好勾選上Reload選項,這樣Windbg就會去自動加載pdb文件了。但有時勾選了該選項,好像不會自動去加載,我們就需要使用.reload /f TestDlg.exe命令去讓Windbg強制去加載pdb文件(命令中必須是包含文件後綴的文件全名)。

設置完成後,我們可以再次運行lm vm TestDlg*命令去看看pdb文件有沒有加載進來:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)9

image.png

如果已經加載進來,則會在上圖中的位置顯示出已經加載進來的pdb文件的完整路徑。,如上所示。

加載到TestDlg.exe文件對應的pdb文件之後,我們再次執行kn命令就可以包含具體的函數名及及代碼的行号信息了,如下:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)10

image.png

我們看到了具體的函數名CTestDlgDlg::OnBnClickedButton1,還看到了對應的代碼行号312。通過這些信息,我們就能到源代碼中找到對應的位置了,如下所示:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)11

image.png

是訪問了空指針産生的異常。當然上面的代碼是我們故意這樣寫的,目的是為了構造一個異常來詳細講解如何使用Windbg進行動态調試跟蹤的。

6、将C 源代碼路徑設置到Windbg中,Windbg會自動跳轉到源代碼行号上

為了方便查看,我們可以直接在Windbg中設置C 源碼路徑,這樣Windbg會自動跳轉到源碼對應的位置。點擊Windbg菜單欄的File->Source File Path...,将源碼路徑設置進去:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)12

image.png

然後Windbg會自動跳轉到對應的函數及行号上:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)13

image.png

然後點擊函數調用堆棧中每行最前面的數字超鍊接,就可以自動切換到對應的函數中。上圖中的函數調用堆棧中很多模塊是系統庫中的,比如mfc100u、User32等,這些庫是系統庫,是沒有源碼的。我們可以點擊最下面的第23個鍊接,其位于我們應用程序的模塊中,會自動跳轉到對應的代碼中,如下:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)14

image.png

7、有時需要查看函數調用堆棧中函數的局部變量或C 類對象中變量的值

有時我們通過查看變量的值,找到排查問題的線索,比如變量中值為0或者很大的異常值。這點我們在多次問題排查中使用到,确實能找到一些線索。

可以查看函數中局部變量的值,也可以查看函數所在類對象的this指針指向的類對象中變量的值。我們要查看哪個函數,就點擊函數調用堆棧中每一行前面的數字超鍊接,如下所示:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)15

image.png

我們看到了局部變量pShExeInfo 的值:

struct _SHELLEXECUTEINFOW * pShExeInfo = 0x00000000

我們可以點擊this對象的超鍊接:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)16

image.png

就能查看當前函數對應的C 類對象中成員變量的值,如下:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)17

image.png

但有時不一定能查看變量的值,因為當前通過異常捕獲模塊自動生成的dump文件一般是minidump文件,文件也就幾MB左右,不可能包含所有變量的值。所以要在minidump文件中查看變量的值,要看運氣的,有時能查看到,有時是看不到的。這裡要講一下dump文件的分類,主要分為minidump文件和全dump文件。

我們将windbg附加到進程上使用.dump命令導出的dump文件,是全dump文件,全dump文件中包含了所有的信息,可以查看到所有變量的信息。另外通過任務管理器導出的dump文件:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)18

image.png

也是全dump文件。全dump文件因為包含了所有的信息,所以會比較大,會達到數百MB,甚至上GB的大小。但如果通過安裝在程序的異常捕獲模塊CrashReport導出的dump文件就是非全dump文件,是mini dump文件,大概隻有幾MB左右,因為異常捕獲模塊捕獲到異常後,會自動導出dump文件,保存到磁盤上,如果都導出體量很大的全dump文件,很大量消耗用戶的磁盤空間,所以我們會設置生成mini dump文件。

在異常捕獲模塊中我們是通過調用系統API函數MiniDumpWriteDump導出dump文件的,我們通過設置不同的函數調用參數去控制生成mini dump文件的。

8、有時我們需要用IDA打開二進制文件去查看彙編代碼上下文

有時通過函數調用堆棧中函數及行号,我們很難搞清楚到底為什麼會發生崩潰,這時候我們就需要回歸本源了,就需要去查看發生異常崩潰的那條彙編指令的上下文了。

彙編指令最能直觀地反映出問題的本質,通過閱讀彙編代碼,就能搞清楚為啥會觸發崩潰了。一般我們在使用.ecxr命令切換到發生異常的彙編指令時,我們可以直接在該條彙編指令的上下文了,點擊菜單欄的View->Disassembly,即可打開顯示彙編代碼的頁面,如下所示:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)19

image.png

但直接在Windbg中查看彙編代碼,對于我們這些不精通彙編代碼的人來說,是有很大困難的,我們是大概率看不懂的。

一般需要借助IDA工具打開二進制文件去查看彙編代碼的,IDA在解析出彙編時會在彙編代碼中加上一些注釋,特别是在有pdb符号庫文件時,會添加更多的注釋,通過這些注釋,我們就能對照了C 源代碼,就能大概讀懂彙編上下文了。如果能找到pdb符号庫文件,則需要将pdb文件放在目标二進制文件的同級目錄中,IDA打開二進制文件時會去自動加載pdb文件。

8.1、IDA工具介紹

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)20

image.png

IDA是比利時Hex-Rays公司出品的一款交互式靜态反彙編工具。它可以直接反彙編出二進制文件的彙編代碼,是目前軟件逆向與安全分析領域最好用、最強大的一個靜态反彙編軟件,已成為衆多軟件安全分析人員不可缺少的利器!它支持Windows、Linux等多個平台,支持Intel X84、X64、ARM、MIPS等數十種CPU指令集。

在實際工作中,我們一般使用反彙編工具IDA去打開二進制文件,查看二進制文件中的彙編代碼。IDA既支持打開Windows下的.exe、.dll等二進制文件,也可以打開Linux下的.bin、.so等二進制文件。

8.2、使用IDA打開二進制文件

IDA安裝完成後,雙擊啟動程序,會彈出如下的提示框:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)21

image.png

點擊“New”即新建一個對象。緊接着彈出讓選擇要打開的文件:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)22

image.png

可以找到目标文件的路徑,打開目标文件即可。也可以點擊取消,然後直接将文件拖到IDA中。打開文件時會讓選擇加載文件的方式:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)23

image.png

對于Windows庫,選擇PE方式即可。接下來,會彈出是否要加載pdb文件的提示框:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)24

image.png

選擇Yes即可。前面我們說過,需要将pdb文件放置到目标二進制文件的同一級目錄中,這樣IDA在打開二進制文件時就會搜索到對應的pdb文件,回去自動加載pdb文件。

打開後的效果如下:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)25

image.png

默認是先是Grapg View圖狀關系視圖,需要右鍵點擊視圖區域,在彈出的右鍵菜單中點擊Text View菜單項,切換到Text View文字視圖,這樣就能看到具體的彙編代碼了:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)26

image.png

8.3、在有pdb文件時到IDA中定位到發生異常的彙編指令的位置

Windbg中發生異常的那條彙編指令,我們需要到IDA中找到對應的位置,然後查看目标位置的彙編指令的上下文。在目标二進制文件有pdb的情況下,要在IDA中定位到發生異常的彙編指令的位置,會比較簡單。

首先在Windbg中顯示的函數調用堆棧中找到發生崩潰的那條彙編指令所在的函數名CTestDlgDlg::OnBnClickedButton1,如下:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)27

image.png

然後到IDA中,點擊菜單欄的Jump->Jumptofunction...,在打開的函數列表窗口中,點擊下方的Search按鈕,在搜索框中輸入函數名後搜索:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)28

image.png

在列表中找到函數,雙擊之久切換到該函數的彙編代碼處:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)29

image.png

這樣就看到該函數的代碼段地址,然後加上Windbg中顯示的偏移值:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)30

image.png

即:

CTestDlgDlg::OnBnClickedButton1 0x32 = 0x00401A30 0x32 = 0x00401A62

就得到發生崩潰的那條彙編指令在當前二進制中的地址,因為彙編指令就為在當前這個函數中,鼠标向下拉動找到地址即可,這樣就能找到發生異常的那條彙編指令了。

其實還有個快捷操作,在計算出發生異常的那條彙編指令的地址後,按下快捷鍵G,在彈出窗口中輸入剛才的地址0x00401A62,就可以直接go到發生異常的那條彙編指令的位置,如下:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)31

image.png

8.4、在沒有pdb文件時到IDA中定位到發生異常的彙編指令的位置

在沒有目标二進制文件的pdb文件的情況下,要在IDA中定位到發生異常的彙編指令的位置,會相對麻煩一點。

發生異常的彙編指令的地址,是程序實際運行時的代碼段地址,需要在Windbg中計算出該條彙編指令的地址相對于所在模塊起始地址的偏移值,然後加上IDA中該模塊的的默認加載地址,就能得到當前彙編指令在IDA中的靜态地址,然後直接go過去,就能看到産生異常的彙編指令的上下文了。在本例中,發生異常的彙編指令的地址就是0x00401a62,如下:(發生異常的這行彙編指令最前面的那個地址,就是當前彙編指令的地址)

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)32

image.png

從函數調用堆棧上看,當前這條發生異常的彙編指令所在的函數為:

TestDlg!CTestDlgDlg::OnBnClickedButton1 0x32

所以這條指令所屬模塊為TestDlg.exe,所以使用lm命令查看TestDlg.exe模塊的起始地址:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)33

image.png

TestDlg.exe模塊其實地址為0x00400000(這是系統将TestDlg.exe模塊加載到進程空間中的起始地址,代碼段地址 ),所以發生異常的這條彙編指令相對于所在模塊TestDlg.exe的偏移地址為:

0x00401a62 - 0x00400000 = 0x00001a62

然後再到打開TestDlg.exe二進制文件的IDA中,查看該TestDlg.exe模塊默認的加載地址:(将滾輪滾動到最上面即可看到,注意此處是默認預加載地址,并不是加載起來後的真正的地址)

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)34

image.png

這樣根據之前計算出來的偏移,加上TestDlg.exe模塊的默認加載地址,就得到發生異常的那條彙編指令在IDA打開的靜态文件中的位置:

0x00001a62 0x00400000 = 0x00401a62

然後到IDA中按下G快捷鍵,GO到0x00401a62地址處,即找到發生異常的那條彙編指令:

wind數據庫如何查深度報告(使用Windbg靜态分析dump文件)35

image.png

在IDA中找到發生異常的彙編指令的位置,就可以去查看其附近的彙編代碼上下文了。

8.5、閱讀彙編代碼上下文

我們在閱讀彙編代碼的上下文時,一般是對照着C 源碼進行的,然後依托彙編代碼上下文中的注釋,找到彙編代碼與C 源碼的對應關系。在閱讀彙編代碼時,要了解一些常用的彙編指令及常用寄存器的使用(比如EAX用來存放函數的返回值,ECX用來傳遞C 對象地址的),熟悉函數調用時的參數入棧、棧分布及參數尋址,了解内存拷貝的彙編代碼實現、了解虛函數調用的二次尋址的過程,去啃發生異常的那條彙編指令的上下文中的彙編代碼。

彙編指令比較多,我們隻需要了解一些常用的彙編指令即可,如果遇到不熟悉的彙編指令去搜索一下就可以了。此外,有一點需要注意的是,在Release下編譯器會對C 源碼會做優化,部分C 源碼可能會被優化掉,C 源碼有時不能完全和彙編代碼對應起來的,但這基本不影響彙編代碼上下文的閱讀。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关科技资讯推荐

热门科技资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved