漏洞分析中常用的堆除錯支援

Ox9A82發表於2016-06-21

  我們在分析堆漏洞,如堆溢位、UAF等時常常會啟用一些堆的除錯支援。可能很多人都用過這些功能,比如gflags.exe,比如在分析UAF時使用的+UST +DPH等等。但是卻很少有人瞭解這些東西到底是什麼、是怎樣發揮的作用。這裡我就來講解一下這幾個除錯機制。內容參考自《軟體除錯》,順帶說一句,個人感覺軟體除錯是一本不可多得的好書,作者不僅精通核心而且對使用者層也有深入的瞭解,個人感覺可以和Windows Internal一起列為必看的書目了。

  首先講怎麼開啟堆除錯機制,我們最熟悉的方法是使用gflags.exe /i 程式名.exe +具體的標誌型別。但是事實上是從登錄檔中讀取的相應程式名的鍵值。也就是說gflags無非就是在登錄檔中新建了鍵值。在Windbg掛載狀態下也可以使用!gflag +標識 來啟用。

  在講解具體的除錯支援之前要明確一點,堆除錯支援是由堆分配機制提供的。可能有些讀者不太清楚堆分配與記憶體分配有什麼區別。這裡簡單講解一下,事實上,一個程式的虛擬地址空間是由一個“樹”的資料結構進行描述的,分配一塊虛擬記憶體就是在樹上增加節點。而堆分配機制是先申請一塊虛擬記憶體再利用自己的機制去分發這些記憶體。所以說堆是完全的應用層的行為,在核心來看,堆是不可見的,是透明的,核心只管程式虛擬地址的資料結構是怎麼樣的。

  之前在讀一些書的時候聽到一個名詞叫做除錯堆,意思是除錯下的堆與非除錯下的堆是不一樣的,哪裡不一樣也沒能搞清楚。這次明白了,原來在除錯狀態下會自動啟用htc hfc hpc。掛載程式後,在Windbg中用!gflag命令即可看到有哪些標誌位。此外,對於一個程式來說,堆資訊是儲存在PEB中的,在程式的PEB中有堆表和堆計數,堆計數統計了堆的數量,而堆表中儲存有每個堆的地址。

      • hfc:是用來防止二次釋放的,如果發生了二次釋放的話,二次釋放會被阻止。如果此時有偵錯程式掛載,那麼就會丟擲一個斷點。
      • ust:用來追蹤堆的分配過程。很多時候我們找到了一塊堆記憶體,卻不知道這個堆記憶體是誰分配的。ust就是用來解決這個問題的,ust有自己的一塊記憶體區作為資料庫。每次堆分配函式被呼叫時,分配函式的棧回溯都會被儲存進資料庫,這樣,想知道堆是誰分配的就可以看棧回溯了。
      • hvc:用來對於堆塊的結構進行全面驗證,可以檢測出堆溢位。如果一個堆塊的頭被破壞,再次分配這個堆塊時,hvc就會對這個分配操作丟擲斷點(僅在有偵錯程式的情況下)
      • htc:在堆尾增加8個位元組的驗證位元組。用於防止堆溢位。當這個堆塊被釋放(Free)時,會驗證堆尾的8個位元組,如果發現被篡改,就會丟擲異常

  hpa頁堆結構也是一種可以用來檢測堆溢位的機制,而且有以上幾個所不具有的優點。上述的幾個檢測堆溢位的機制都是當發生了涉及溢位堆的堆操作之後才會進行檢查產生斷點。這樣的話,得到一個異常的時候早就不在溢位的第一現場了,這樣就很難得到到底是在哪一條指令造成的溢位。hpa機制則是提供了完全不一樣的堆管理機制,優點是一但發生溢位馬上就會丟擲異常,可以直接發現是哪一條指令引發的溢位。頁堆的具體實現原理是在分配的堆後面緊挨著分配一個不可訪問屬性的記憶體頁,這樣的話,如果越界馬上就異常掉了。這麼做的基礎是因為Windows系統的記憶體管理的基本單位就是記憶體頁,一般的記憶體頁是4KB。對於我們來說的位元組、DWORD等對於記憶體管理來說是不存在的,頁是操作的最小單位,這一點可以追溯至記憶體的分頁機制。總之,想要把一段記憶體設為不可執行,那麼最少也要是一個頁的記憶體,這一點與硬碟的扇區是一樣的,硬碟一次讀是不能讀一個位元組的,至少也要讀一個扇區才行。

  如果使用者的堆不滿一個記憶體頁,就把它分配在第一個頁的末尾。這樣的話就實現了一但溢位就異常的功能。同時在堆的整體頭部也實現了一些資料結構來維護資訊。對於每個堆塊來說,有一個DPH_HEAP_BLOCK結構

相關文章