堆溢位 對空表的偵錯

2020-09-19 16:00:16

程式碼

#include <windows.h>
int main()
{
	HLOCAL h1,h2,h3,h4,h5,h6;
	HANDLE hp;
	hp = HeapCreate(0,0x1000,0x10000);//建立一個新的堆
	_asm int 3//中斷,進入OD偵錯

	h1 = HeapAlloc(hp,HEAP_ZERO_MEMORY,3);
	h2 = HeapAlloc(hp,HEAP_ZERO_MEMORY,5);
	h3 = HeapAlloc(hp,HEAP_ZERO_MEMORY,6);
	h4 = HeapAlloc(hp,HEAP_ZERO_MEMORY,8);
	h5 = HeapAlloc(hp,HEAP_ZERO_MEMORY,19);
	h6 = HeapAlloc(hp,HEAP_ZERO_MEMORY,24);
	_asm int 3
	
	HeapFree(hp,0,h1);
	HeapFree(hp,0,h3);
	HeapFree(hp,0,h5);
	_asm int 3
	HeapFree(hp,0,h4);
	_asm int 3

	return 0;
}

實驗目的

簡單理解空表

實驗準備

環境:windows xp
編譯器:vc++
偵錯程式:OD

實驗過程

1.把OD設成預設偵錯程式,開啟這段程式觸發異常正好卡在HeapCreate後面,這時候的正好把新建好的堆的地址傳入eax。
在這裡插入圖片描述
試了下Heap_Vis這個外掛,好像不是那麼好用,我用了下,直接給我卡死了,最後還是靠Ctrl+Alt+Del才救回來的(有可能是外掛衝突了?懶得去搞了

在這裡插入圖片描述
2.找到堆的地址發現堆的結構前面先是一段段表。中間是虛分配表,因為堆才初始化,沒有虛分配記錄所以全都為NULL,然後就是32位元的bitmap應該是來統計堆表的分配,如果表被佔用則,該對應的bit位上為1.
然後接下來就是空表索引區,但是由於都沒有被佔用,所有的空表都指向自己的位置。
其中零號空表(0x003a0178)指向的是尾塊。

在這裡插入圖片描述
PS:堆表表頭的結構為

ByteName
1~2Self Size 自身的長度
3~4Previous chunk size 上一節的長度
5Segment Index 段索引
6Flags 該位為1的時候,表示該表被佔用了
7Unused bytes
8Tag index
9~bFlink in freelist (佔用態)
c~fBlink in freelist (佔用態)

3.觀察到零號空表指向的尾塊表頭,表頭下面的就是指向零號空表的雙向連結串列。實際上這個堆塊開始於 0x0030680,一般參照堆塊的指標都會躍過 8 位元組的塊首,直接指向資料區。Self Size為0x130,因為堆中一個單位對應的都是八個位元組,所以總空間為 0x130 * 8。

(1)堆塊的大小包括了塊首在內,即如果請求 32 位元組,實際會分配的堆塊為 40 位元組:8位元組塊首+32 位元組塊身。
(2)堆塊的單位是 8 位元組,不足 8 位元組的部分按 8 位元組分配。
(3)初始狀態下,快表和空表都為空,不存在精確分配。請求將使用「次優塊」進行分配。這個「次優塊」就是位於偏移 0x0680 處的尾塊。
(4)由於次優分配的發生,分配函數會陸續從尾塊中切走一些小塊,並修改尾塊塊首中的size 資訊,最後把 freelist[0]指向新的尾塊位置。
在這裡插入圖片描述

3.把斷點的位置移到HeapAlloc的後面觀察每個空間的申請。
0x003a0680到0x003a06e0就是1到6號申請的空間。
開頭是02,則代表分配了2 * 8個位元組的空間。
開頭是04,則代表分配了4 * 8個位元組的空間。
然後這個時候再看尾塊的表頭的Self Size為0x120,因為前面分配了0x10的空間(2 * 4 + 4 * 2 = 0x10)
這時零表中指向尾塊的指標也變成了0x003a0700
在這裡插入圖片描述

4.把斷點的位置移到HeapFree h5的後面,觀察h1和h3鏈入了Freelist[2],h5鏈入了Freelist[4],同時觀察Flag位都變為了0,意味著恢復成了空閒態。

在這裡插入圖片描述

5.又把斷點的位置移到HeapFree之後觀察堆表h1鏈入了Freelist[2],h3脫離了Freelist[2]與h1組成的連結串列鏈入了Freelist[8],同時觀察h3開頭的02已經變成了08,此時已經完成了h3,h4,h5的合併。h5也從Freelist[4]中取下了。
堆塊合併的過程。堆塊合併可以更加有效地利用記憶體,但往往需要修改多處指標。因此,堆塊合併只發生在空表中。在強調分配效率的快表中,堆塊合併一般會被禁止(通過設定堆塊為佔用態)。另外,空表中的第一個塊不會向前合併,最後一個塊不會向後合併。
Freelist中指向的地址都自動跳過了堆塊頭的,也就是跳過了那8個位元組。

在這裡插入圖片描述

觀察此時Freelist[4]已經變為指向自身,Freelist[2]也只鏈入了一個h1
在這裡插入圖片描述

PS:偵錯堆好像不能用OD直接載入,必須的在程序中偵錯,不然堆的位置就會出現亂碼。
在這裡插入圖片描述