Windwos堆管理體系以及溢出利用


《0day安全》學習筆記,主要討論WIndows2000~WIndowsSP1平台的堆管理策略。

 

0X01 堆與棧的區別

棧空間是在程序設計時已經規定好怎么使用,使用多少內存空間。典型的棧變量包括函數內部的普通變量、數組等。棧變量在使用的時候不需要額外的申請操作,系統棧會根據函數中的變量聲明自動在函數棧中給其預留空間。棧空間由系統維護,它的分配和回收都由系統來完成,最終達到棧平衡。所有這些對程序員都是透明的。

 

堆具備以下特性:

1.堆是程序運行時動態分配的內存。所謂動態是指所需內存的大小在程序設計時不能預先決定的,需要在程序運行時參考用戶的反饋。

2.堆在使用時需要程序員使用專用的函數進行申請,如C語言中的malloc等函數、C++中的new函數等都是最常見的分配堆內存的函數。堆內存申請有可能成功,也有可能失敗,這與申請內存的大小、機器性能和當前運行環境有關。

3.一般用一個堆指針來使用申請的內存,讀、寫、釋放都是通過這個指針來完成。

4.使用完畢后要通過堆釋放函數進行回收這片內存,否則會造成內存泄漏。如free,delete等。

 

 

0X02堆的數據結構與管理策略

對於管理系統來說,響應程序的內存使用申請就意味着要在“雜亂”的堆區中辨別哪些內存是正在被使用的,哪些內存是空閑的,並最終“尋找”到一片“恰當”的空閑內存區域,以指針形式返回給程序。

 

堆塊:堆區額內存按不同大小組織成塊,以堆塊為單位進行標識,而不是傳統的按字節標識。一個堆塊包括兩個部分:塊首和塊身。塊首是一個堆塊頭部的幾個字節,用來標識這個塊首自身的信息,例如,大小、空閑或占用。塊身是緊跟在塊首后面的部分,也是最終分配給用戶使用的數據區。

注意:塊管理系統返回的指針一般是塊身的起始位置,連續申請內存就是發現返回的內存之間存在“空隙”,那就是塊首。

 

堆表:堆表一般位於堆區的起始位置,用於檢索堆區中所有堆塊的總要信息,包括堆塊的位置、堆塊的大小、空閑或占用等。 堆表的數據結構決定了整個堆區的組織方式。堆表往往不知一種數據結構:如平衡二叉樹等。

堆的內存組織如圖所示:

Windows中占用態的堆被使用它的程序管理,堆表只是管理空閑態的堆塊。

其中最重要的堆表有兩種:空閑雙向鏈表Freelist空表)以及快速單項鏈表Lookaside快表

 

空表:
空閑堆塊塊首包含一對指針,這對指針把空閑堆塊組織成雙向鏈表。按照堆塊大小的不同,空表總共被分成128條。

堆區一開始的堆表區中有一個128項的指針數組,被稱作空表索引。該數組每一項包含兩個指針,用於標識一條空表。

比如空表的第二項free[1]標識了項中所有大小為8字節的空閑堆塊,之后每隔索引指示的空閑堆塊遞增8字節,free[2]標識16字節,free[3]標識24字節空閑堆塊,free[127]標識1016字節空閑堆塊。

                        空閑堆塊大小 = 索引項 * 8字節

 

把空閑堆塊鏈入不同的空表,可以方便管理。空表第一項free[0]鏈入所有大於等於1024字節的堆塊(小於512K)。這些堆塊按照各自的大小在零號空表中升序排列。

 

 

 

 

快表

快表是Windows用來加速分配而采用的一種堆表。這類單向鏈表中不會發生堆塊合並(其中空閑堆塊塊首置為占用態)。

快表也有128條,組織結構與空表類似,只是堆塊按單鏈表組織,而且每條快表最多只有4個節點。

 

 

 

0X03  堆的操作

堆得操作分為:堆的分配、堆得釋放、堆得合並。分配與釋放是由程序執行的,堆的合並是由堆管理系統自動完成的。

 

1.堆的分配

堆分配分三類:快表分配,普通表分配,零號空表(free[0])分配。

 

從快表分配:尋找大小到大小匹配的空閑堆塊、將其狀態修改為占用態、把它從堆表中卸下,最后返回一個指向堆塊塊身的指針給程序使用。

 

普通表分配:首先尋找最優的空閑塊分配,若失敗,則尋找次優的空閑塊分配。

零號空表分配:按照大小升序鏈着大小不同的空閑塊,找最優結果。

注意:當空表中找不到最優的堆塊時,會發生次優分配,即從大塊按照請求的大小精確割出一塊進行分配,然后給剩下部分重新標注塊首,鏈入空表。

 

2.堆塊釋放

將堆塊狀態改為空閑,鏈入相應的堆表。所有的釋放塊都鏈入堆表的末尾。

 

3.堆塊合並

經過反復的申請與釋放,堆區產生很多內存碎片。堆管理系統發現兩個空閑堆塊彼此相鄰,就會進行堆塊合並。 包括將兩個塊從空閑鏈表中卸下、合並堆塊、調整合並后大塊的塊首信息、將新塊重新鏈入空閑鏈表。

 

幾個注意點:

1.快表空閑塊被置為占用態,所以不會發生堆塊合並操作。

2.快表只有精確分配時才會分配。

3.分配與失敗有限使用快表,失敗用空表。

 

 

0X04   DWORD SHOOT 堆溢出利用原理

 

堆溢出的利用的精髓就是精心構造的數據溢出下一個堆塊的塊首,改寫塊首的前向指針和后向指針,然后在分配、釋放、合並等操作發生時獲得一次向內存任意地址讀寫任意數據的機會。

原理:

 

Int remove(ListNode * node)

{

    node->blink->flink = node -> flink;

    Node->flink->blink= node ->blink;

    Return0;

}

 

 

那么四個字節的利用我們可以做什么呢?

 

1.內存變量:修改能影響程序執行的重要標志變量,例如更改身份驗證函數的返回值。

2.代碼邏輯:修改代碼段重要函數關鍵邏輯,如程序分支處的判斷邏輯。

3.函數返回地址:堆溢出也可以利用DWORD SHOOT更改函數返回地址。

4.攻擊異常處理:程序產生異常,Windows轉入異常處理機制,包括SEH等。

5.函數指針:如C++的虛函數調用。改寫這些指針后,函數調用往往就可以劫持進程。

6.PEB中線程同步函數入口地址:每個進程PEB存放着一對同步指針,指向RtlEnterCriticalSection()和RtlLeaveCriticalSection(),並且被ExitProcess()函數調用。

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM