本文實驗的例子來自《windows高級調試》第6.2.2節,參考書中的方法進行。
1.通過windbg分析堆塊
(1)在命令行運行程序,輸入參數(輸入超過10個字符),在出現如下提示的時候,使用windbg attach到該進程。
(2)按任意鍵繼續執行,執行完後,程序崩潰到windbg,使用kb命令查看堆棧如下:
(3)我們看到報的堆棧信息是在HeapFree函數中,其實我們知道原因是wcscpy函數溢出了,但崩潰點卻不在溢出發生的地方,堆破壞的最大問題在於造成錯誤的代碼無法在發生堆破壞時被發現,我們可以通過查看當前堆的一些情況進行分析。
(4)查看默認堆的詳細情況,由於信息比較多,我僅僅把最後一部分打印出來。
(5) 我們看到最後一個堆塊是有問題的,可以看下該堆的內容,我們從004e5188地址開始打印。
(6)我們看到了一系列的0x31字符,就是我們參數輸入的“1”,我們可以用du命令再打印下:
(7)果然是我們輸入的參數,而且參數越過了堆塊004e51a8,我們把該堆塊的頭打印出來:
發現堆塊的頭全都被我們輸入的參數覆蓋了,這就導致了內存訪問異常。
2.通過windbg+普通頁堆分析
上面的程序很簡單,很容易通過分析堆塊來找出問題,然而通過分析堆塊來查找堆溢出的問題並非總是可行的,還有一種方式可以讓我們很快找到堆溢出的原因,就是頁堆,我們可以通過Application Verifier工具開啓頁堆。
通過Add Application菜單,添加我們要分析的應用程序,然後再右邊會有Heaps的選項。
右擊Heaps 選擇屬性,在彈出框中,把full去掉(我們先分析普通頁堆,之後再分析完全頁堆)。
完成後我們按照原來的方式執行,程序崩潰後,打印堆棧如下。
上面給了我們堆的內存信息,要轉儲頁堆數據的內容,我們需要將該地址減去32字節然後再轉成_DPH_BLOCK_INFORMATION結構,如下:
上述結構體最重要的一項是StackTrace,可以查找堆棧的回溯,如下:
通過上述信息,我們很快就能找到有問題堆的分配棧回溯,通過分析代碼很容易發現堆的溢出情況。
3.通過windbg+完全頁堆分析
普通頁堆還不是很直觀的發現堆溢出的地方,但是開啓了完全頁堆就不一樣了,完全頁堆開啓後,程序只要發生堆溢出就立馬崩潰,這樣就很好的發現問題所在了,開啓完全頁堆:
再次運行程序,崩潰後堆棧情況:
很快就找到問題所在了,我們可以把wcscpy拷貝的字符打印出來:
就是我們傳入的參數導致的溢出。