ptmalloc堆概述
1 概述
堆的概念
在程序運行過程中,堆可以提供動態分配的內存,允許程序申請大小未知的內存。堆其實就是程序虛擬地址空間的一塊連續的線性區域,它由低地址向高地址方向增長。
堆管理器
我們一般稱管理堆的那部分程序爲堆管理器。
· dlmalloc – General purpose allocator
· ptmalloc2 – glibc
· jemalloc – FreeBSD and Firefox
· tcmalloc – Google
· libumem – Solaris
堆管理器處於用戶程序與內核中間,主要做以下工作
1. 響應用戶的申請內存請求,向操作系統申請內存,然後將其返回給用戶程序。同時,爲了保持內存管理的高效性,內核一般都會預先分配很大的一塊連續的內存,然後讓堆管理器通過某種算法管理這塊內存。只有當出現了堆空間不足的情況,堆管理器纔會再次與操作系統進行交互。
2. 管理用戶所釋放的內存。一般來說,用戶釋放的內存並不是直接返還給操作系統的,而是由堆管理器進行管理。這些釋放的內存可以來響應用戶新申請的內存的請求。
Dlmalloc
Linux 中早期的堆管理器。在並行處理多個線程時,會共享進程的堆內存空間。
Ptmalloc
Wolfram Gloger 在 Doug Lea 的基礎上進行改進使其可以支持多線程,這個堆分配器就是 ptmalloc 。在 glibc-2.3.x. 之後,glibc 中集成了ptmalloc2。
可以下載glibc源碼查看ptmalloc
查看glibc版本
millionsky@ubuntu-16:~/tmp$ ldd --version ldd (Ubuntu GLIBC 2.23-0ubuntu9) 2.23 |
堆的基本操作
#include <stdlib.h>
/* ** 爲0時返回最小的chunk,32位系統爲16,64位系統爲24/32。 */ void *malloc(size_t size);
/* **除非被mallopt禁用,釋放很大的內存空間時,程序會將這些內存空間還給系統。 */ void free(void *ptr); |
系統調用
brk/mmap/munmap
glibc函數
brk/sbrk/mmap/munmap
多線程支持
相對於dlmalloc,在glibc的ptmalloc實現中,比較好的一點就是支持了多線程的快速訪問。在新的實現中,所有的線程共享多個堆。
2 ptmalloc堆數據結構
2.1 Chunk
2.1.1 Chunk概述
1. malloc 申請的內存爲 chunk 。這塊內存在 ptmalloc 內部用 malloc_chunk 結構管理chunk。當程序申請的 chunk 被 free 後,會被加入到相應的空閒管理列表中。
l Chunk的對齊:32位爲8字節;64位爲16字節
l Chunk的最小尺寸:32位爲16字節;64位爲24/32字節
/* The smallest possible chunk */
#define MIN_CHUNK_SIZE (offsetof(struct malloc_chunk, fd_nextsize))
2. Chunk有4種類型:
· Allocated chunk
· Free chunk
· Top chunk
· Last Remainder chunk
2.1.2 malloc_chunk數據結構
1. Ptmalloc使用malloc_chunk結構管理chunk。
這個結構跨越了兩個chunks
第1個字段屬於之前的chunk,第2/3/4字段屬於當前的chunk;
只有當(size&1)==0時,prev_size字段纔會被定義;
#define INTERNAL_SIZE_T size_t #define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk *fd; /* double links -- used only if free. */ struct malloc_chunk *bk;
/* Only used for large blocks: pointer to next larger size. */ struct malloc_chunk *fd_nextsize; /* double links -- used only if free. */ struct malloc_chunk *bk_nextsize; }; |
l fd 指向下一個(非物理相鄰)空閒的 chunk,free chunk中才有
l bk 指向上一個(非物理相鄰)空閒的 chunk,free chunk中才有
通過 fd 和 bk 可以將空閒的 chunk 塊加入到空閒的 chunk 塊鏈表進行統一管理
l fd_nextsize 指向前一個與當前 chunk 大小不同的第一個空閒塊,不包含 bin 的頭指針。只用於large chunk。
l bk_nextsize 指向後一個與當前 chunk 大小不同的第一個空閒塊,不包含 bin 的頭指針。只用於large chunk。
l 一般空閒的 large chunk 在 fd 的遍歷順序中,按照由大到小的順序排列。這樣做可以避免在尋找合適chunk 時挨個遍歷。
2. 一個已經分配的 chunk 的樣子如下。我們稱前兩個字段稱爲 chunk header,後面的部分稱爲user data。每次 malloc 申請得到的內存指針,其實指向user data的起始處。
3. Chunk的標誌位
² NON_MAIN_ARENA(A),當前 chunk 是否不屬於主線程,1表示不屬於,0表示屬於。
² IS_MAPPED(M),當前 chunk 是否是由 mmap 分配的。
如果M位被設置,則其它位被忽略(因爲mapped chunks既不在arena中,也不和free chunk相鄰);(PS:這裏沒有怎麼理解)
² PREV_INUSE(P),前一個 chunk 塊是否被分配。
如果爲0,則prev_size包含前一個chunk的大小;可計算出前一個chunk的位置;
如果爲1,則不能確定前一個chunk的大小;
分配的第一個chunk通常會設置此位,防止訪問不存在或不擁有的內存;
4. chunk中的空間複用
當一個 chunk 處於使用狀態時,它的下一個 chunk 的 prev_size 域無效,所以下一個 chunk 的該部分也可以被當前chunk使用。這就是chunk中的空間複用。
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, in bytes |A|M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_size() bytes) .
next . |
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| (size of chunk, but used for application data) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.1.3 Allocated chunk
l prev_size
前一個chunk(物理相鄰)如果爲free chunk(size&1==0),則指示其大小;
l Size
該 chunk 的大小,大小必須是 2 * SIZE_SZ 的整數倍。
由於chunk必須是8的整數倍,低3比特對size字段沒有意義,從高到低分別表示:
² NON_MAIN_ARENA(N),當前 chunk 是否不屬於主線程,1表示不屬於,0表示屬於。
² IS_MAPPED(M),當前 chunk 是否是由 mmap 分配的。
² PREV_INUSE(P),前一個 chunk 塊是否被分配。
堆中第一個被分配的內存塊的 size 字段的P位都會被設置爲1,以便於防止訪問前面的非法內存。
2.1.4 Free chunk
prev_size: 兩個free chunks不會連在一起,兩個連在一起的chunk被釋放時,會合併成一個free chunk。因此free chunk的前一個chunk是被分配的,由此prev_size包含前一個chunk的用戶數據;
size: 包含free chunk的大小;
fd: Forward pointer – 指向相同bin中的下一個(非物理相鄰的下一個chunk)free chunk;
bk: Backward pointer – 指向相同bin中的上一個(非物理相鄰的上一個chunk)free chunk;
2.1.5 Top chunk
程序第一次進行 malloc 的時候,heap 會被分爲兩塊,一塊給用戶,剩下的那塊就是 top chunk。其實,所謂的top chunk 就是處於當前堆的物理地址最高的 chunk。這個 chunk 不屬於任何一個 bin,它的作用在於當所有的bin 都無法滿足用戶請求的大小時,如果其大小不小於指定的大小,就進行分配,並將剩下的部分作爲新的 top chunk。否則,就對heap進行擴展後再進行分配。在main arena中通過sbrk擴展heap,而在thread arena中通過mmap分配新的heap。
需要注意的是,top chunk 的 prev_inuse 比特位始終爲1,否則其前面的chunk就會被合併到top chunk中。
1. Top chunk:位於arena頂部邊界的chunk稱爲top chunk;
Top chunk不屬於任何bin;
由於top chunk沒有下一個連續的chunk,它不能使用下一個chunk的prev_size字段;
初始化之後,top chunk就一直存在,如果小於MINSIZE,則會被補充;
當任何bins中都沒有可用的chunk時,就使用top chunk來處理用戶的請求;
如果top chunk的大小大於用戶請求的大小,則top chunk被切分成兩個:
· User chunk (用戶請求的大小) – 返回給用戶.
· Remainder chunk (of remaining size) – 成爲新的top chunk
如果top chunk的大小小於用戶請求的大小,則使用sbrk(main arena)或mmap(thread arena)擴展top chunk;
2.1.6 Last Remainder chunk
Last Remainder chunk:最近對small chunk請求進行切分的剩餘部分。Last remainder chunk幫助提高引用的位置,即連續的malloc請求small chunk很可能分配到相鄰的位置;
當用戶請求small chunk時,如果small bin或unsorted bin中沒有可用的,則掃描binmaps查找next largest bin。找到後,進行切分,返回給用戶,剩餘的chunk加入unsorted bin。這個剩餘的chunk就成爲新的last remainder chunk。
當用戶後續請求small chunk時,如果last remainder chunk是unsorted bin中的唯一的chunk,則last remainder chunk被切分,返回給用戶,剩餘的chunk加入unsorted bin。這個剩餘的chunk就成爲新的last remainder chunk。這樣後續的內存分配都是相鄰的。
2.2 Bins
注意:Bin的翻譯爲箱子,chunk的翻譯爲塊;
1. Bin:freelist數據結構,用於保存free chunks。
2. 基於chunk size,bins被劃分爲以下幾種類型:
l Fast bin(單鏈表,大小相同)
l Unsorted bin(環形雙鏈表,亂序狀態,空閒 chunk 迴歸所屬 bin 之前的緩衝區)
l Small bin(環形雙鏈表,大小相同)
l Large bin(環形雙鏈表,按大小降序排列)
【bins】
l Bins:bin header的數組;
l 每個bin是free chunk的雙鏈表;
l Bins這個數組(malloc_state中)存儲unsorted、small、large bins;
#define NBINS 128
mchunkptr bins[ NBINS * 2 - 2 ];
l 總共128個bins,劃分如下:
· Bin 0 – 不存在
· Bin 1 – Unsorted bin
· Bin 2 to Bin 63 – Small bin
· Bin 64 to Bin 126 – Large bin
3. 大部分bin持有的chunk大小,相對於malloc請求的大小是很罕見的,更常見的是chunk分片和合並。
4. 所有的過程都維護着一個不變式:合併後的chunk之間不能物理相鄰;因此鏈表中的每個chunk的物理前面和後面都跟着使用中的chunk或內存的結尾;
5. bin中的chunks是按照大小和時間(最近最少使用)排序的;
排序對Small bins是不需要的,因爲small bin中所有的chunk大小相同。
順序排列可以提高效率,不需要足夠的遍歷就可以找到用戶請求的數據;
相同大小的chunk是這樣鏈接的:最近釋放的放在前面;分配是從後面開始的。這會導致LRU(Least Recently Used,FIFO)分配順序,每個chunk都會有相同的機會和毗鄰的free chunk合併。
6. 爲了簡化雙鏈表的使用,每個bin header就像malloc_chunk一樣。這避免了類型轉換。但爲了節省空間,和提高位置,我們只分配了fd/bk這兩個指針;然後使用重定位技巧將fd/bk視爲一個malloc_chunk的字段;
注意:bins數組存儲的不是mchunkptr,而是malloc_chunk中的fd/bk兩個字段。Bins數組有127*2個元素,每個bin header佔兩個,因而總共可以存儲127個bin。
索引對應關係爲:
bin索引 Bins數組索引
0 不存在
1 2*(-1)
2 2*(0)
3 2*(1)
126 2*(124)
127 2*(125)
2.2.1 Fast bin
1. 大小爲16~80/32~160字節的chunk稱爲fast chunk;
存儲fast chunk的bins稱爲fast bins;
在所有的bins中,Fast bins在內存分配和釋放上更快;
Fast bin是一個存儲最近釋放的small chunks的數組;
Fast bin特別設計爲很多小的結構、對象或字符串;
大多數程序經常會申請以及釋放一些比較小的內存塊。如果將一些較小的 chunk 釋放之後發現存在與之相鄰的空閒的 chunk 並將它們進行合併,那麼當下一次再次申請相應大小的 chunk 時,就需要對 chunk 進行分割,這樣就大大降低了堆的利用效率。因爲我們把大部分時間花在了合併、分割以及中間檢查的過程中。因此,ptmalloc 中專門設計了 fast bin,對應的變量就是 malloc state 中的 fastbinsY。
2. fastbinsY這個數組存儲fast bin。
typedef struct malloc_chunk *mfastbinptr; mfastbinptr fastbinsY[ NFASTBINS ]; |
相關的定義如下:
#define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[ idx ])
/* offset 2 to use otherwise unindexable first 2 bins */ #define fastbin_index(sz) \ ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2)
/* The maximum fastbin request size we support */ #define MAX_FAST_SIZE (80 * SIZE_SZ / 4)
#define NFASTBINS (fastbin_index(request2size(MAX_FAST_SIZE)) + 1) |
3. Fast Bin的數目:10
每個fast bin包含一個free chunks的單鏈表(即binlist);
因爲fast bins中chunk不能從鏈表的中間移除,因此使用了單鏈表;
Chunk在fast bin鏈表中的添加和刪除只發生在鏈表的頭部--LIFO(一般的bin是FIFO);
4. Chunk size的間隔是8字節(32位)或16字節(64位)
Chunk size與fast bin index的對應:
Fast bin Index | Chunk size(32) | Chunk size(64) |
0 | 16 | 32 |
1 | 24 | 48 |
2 | 32 | 64 |
3 | 40 | 80 |
4 | 48 | 96 |
5 | 56 | 112 |
6 | 64 | 128 |
7 | 72 | 144 |
8 | 80 | 160 |
9 | 88 | 176 |
5. 某個特定的fast bin中chunks的大小是相同的
6. Malloc初始化時,最大的fastbin(用戶數據)設置爲64(32位)或128(64位)
因此默認時,大小[16,72]/[32,144]的chunks是fast chunks;
/* DEFAULT_MXFAST 64 (for 32bit), 128 (for 64bit) */ static void malloc_init_state(mstate av) { if (av == &main_arena) set_max_fast(DEFAULT_MXFAST); |
7. 不合並(No Coalescing)
fastbin的inuse位總是被設置;
兩個free chunks可以是相鄰的,不會合併成一個free chunk;
不合並會導致更多的分片,但可以提高速度;
Fast bin中的chunks只會成塊合併(bulk);
8. Malloc(fast chunk)
· 初始時,fast bin max size and fast bin indices是空的,即時用戶請求一個fast chunk,malloc也不會進入fast bin code,而是進入small bin code。
/* Maximum size of memory handled in fastbins. */ static INTERNAL_SIZE_T global_max_fast;
/* Fastbins */ mfastbinptr fastbinsY[ NFASTBINS ];
/* Fast bin code */ if ((unsigned long) (nb) <= (unsigned long) (get_max_fast())) {}
/* small bin code */ if (in_smallbin_range(nb)) {} |
· 之後,當fast bin max size and fast bin indices非空時,會計算fast bin index,並獲取對應的binlist;
· Binlist中的第一個chunk從binlist中移除並返回給用戶;
9. free(fast chunk) –
· 計算Fast bin index,獲取對應的binlist;
· 將free chunk加入到獲取的binlist的前端(front position);
2.2.2 Unsorted bin
chunk切分後的剩餘部分、所有釋放後返回的chunk,都會被放入unsorted bin,而不是對應的bins。Malloc會掃描unsorted bin來重用最近被釋放的chunk,然後它們會被放入對應的bins。
因此,基本上,unsorted_chunks是一個隊列。chunks在釋放、切分的時候放入;在malloc的時候重用或放入對應的bins。
1. Bin的數目 - 1
Unsorted bin包含一個free chunks的環形雙鏈表(即binlist);
2. Chunk size - 沒有大小限制,任何大小的chunks都屬於此bin;
unsorted bin 中的空閒 chunk 處於亂序狀態,主要有兩個來源
· 當一個較大的 chunk 被分割成兩半後,如果剩下的部分大於MINSIZE,就會被放到 unsorted bin 中。
· 釋放一個不屬於 fast bin 的 chunk,並且該 chunk 不和 top chunk 緊鄰時,該 chunk 會被首先放到 unsorted bin 中。
此外,Unsorted Bin 在使用的過程中,採用的遍歷順序是 FIFO 。
2.2.3 Small bin
1. 小於512/1024字節的chunk稱爲small chunk。
存儲small chunk的bins稱爲small bins;
在內存的分配和釋放上,Small bins比large bins快,比fast bins慢;
2. 相關定義
INTERNAL_SIZE_T:4/8字節
SIZE_SZ:4/8
MALLOC_ALIGNMENT:對齊單位,默認爲8/16
#ifndef INTERNAL_SIZE_T
# define INTERNAL_SIZE_T size_t
#endif
#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
#ifndef MALLOC_ALIGNMENT
# define MALLOC_ALIGNMENT (2 * SIZE_SZ < __alignof__ (long double) \
? __alignof__ (long double) : 2 * SIZE_SZ)
#endif
MIN_LARGE_SIZE:512/1024
#define NBINS 128 #define NSMALLBINS 64 #define SMALLBIN_WIDTH MALLOC_ALIGNMENT #define SMALLBIN_CORRECTION (MALLOC_ALIGNMENT > 2 * SIZE_SZ) #define MIN_LARGE_SIZE ((NSMALLBINS - SMALLBIN_CORRECTION) * SMALLBIN_WIDTH)
#define in_smallbin_range(sz) \ ((unsigned long) (sz) < (unsigned long) MIN_LARGE_SIZE) |
3. bins的數目--62
每個small bin包含一個free chunks的環形雙鏈表(即binlist);
使用雙鏈表的原因是:在small bins中chunks是從鏈表的中間拆除的。而鏈表是FIFO,添加發生在前端,刪除發生在鏈表的尾端;
4. Chunk size的間隔 - 8字節/16字節;
Bin 2是第一個small bin;
一個Small bin中的chunks的大小是相同的,因而不需要排序;
32位 64位
Bin2 16 32
Bin3 24 48
Bin4 30 64
......
Bin63 504 1008
5. 合併
兩個相鄰的free chunk會合併成一個free chunk;
合併消除了碎片,但降低了速度;
6. malloc(small chunk) –
l 開始時,所有的small bins都是NULL,即時用戶請求一個small chunk,也會進入unsorted bin code,而不是small bin code;
l 同樣在第一次調用malloc的時候,small bin和large bin的數據結構(malloc_state中的bins)會被初始化,bins會指向自身表明它是空的;
l 之後當small bin非空時,對應的binlist中的最後一個chunk被移除和返回給用戶;
7. free(small chunk) –
l 釋放chunk時,檢查它的前面和後面的chunk是否free chunk。如果是則合併;從相關的鏈表中unlink這些chunks,將新合併的chunk加入到unsorted bin的鏈表的開始;
2.2.4 Large bin
1. 大小大於等於512字節的chunk稱爲large chunk;
存儲large chunk的bins稱爲large bins;
在內存的分配和釋放上,large bins比small bins慢;
2. bins的數目--63
l 每個large bin包含一個free chunks的環形雙鏈表(即binlist);
l 使用雙鏈表的原因是:在large bins中chunks是從鏈表的任何位置增加和移除的(前面、中間、後面);
l Bin的索引和chunk size間隔(32位)
Bin count | Bin index | Chunk size |
31 | 64 ... 94 | [512, +64) ... [512+64*30, +64) [2432, 2496) |
17 | 95 ... 111 | [2496, 2560) ... [10240, +512) |
9 | 112 ... 120 | [10752, 12288) ...+4096 [40960, +4096) [40960,45056) |
3 | 120 121 122 123 | [45056, 65536) ...+32768
[131072, +32768) |
2 | 124 125 126 | [163840, 262144) [262144,+262144) [524288,+262144) |
1 | 126 | [786432, *) |
相關代碼爲:
3. 和small bin不一樣,一個large bin中的chunks不是相同大小的;因此按照降序排列,大的chunk存儲在前端,小的chunk存儲在後端;
4. 合併
兩個free chunk可以相鄰,會合併成一個free chunk;
5. malloc(large chunk)
l 開始時,所有的large bins都是NULL,即時用戶請求一個large chunk,也會進入next largest bin code,而不是large bin code;
l 同樣在第一次調用malloc的時候,small bin和large bin的數據結構(malloc_state中的bins)會被初始化,bins會指向自身表明它是空的;
l 之後當large bin非空時,如果bin list中最大的chunk size大於用戶請求的大小,會從後往前遍歷binlist,找到一個最近大小的chunk。該chunk被劃分爲兩個chunk:
· User chunk (用戶請求的大小) – 返回給用戶.
· Remainder chunk (of remaining size) – 增加到 unsorted bin.
l 如果用戶請求的大小比binlist中最大的chunk還大,則進入next largest bin code。Next largest bin掃描binmaps(ps:應該是下一個大的binlist)來找到非空的next largest bin,如果找到了,則從該binlist中返回此chunk,切分後返回給用戶,剩餘的增加到unsorted bin;如果沒有找到,則嘗試使用top chunk處理用戶的請求;
6. Fd_nextsize鏈表
l fd_nextsize 指向前一個與當前 chunk 大小不同的第一個空閒塊,不包含 bin 的頭指針。只用於large chunk。
l bk_nextsize 指向後一個與當前 chunk 大小不同的第一個空閒塊,不包含 bin 的頭指針。只用於large chunk。
l 一般空閒的 large chunk 在 fd 的遍歷順序中,按照由大到小的順序排列。這樣做可以避免在尋找合適chunk 時挨個遍歷。大小相同時總是插入第二個位置。取出時也是取出第二個位置。這樣可以避免調整fd_nextsize/bk_nextsize指針。
l 由於bin頭不包含fd_nextsize/bk_nextsize指針,它不在fd_nextsize/bk_nextsize鏈表中。
如何理解呢?_int_malloc的源碼中有插入和取出的邏輯
Large chunk插入示例
Ø 通過fd/bk鏈接起來的big chunk,fd鏈表中按大小降序排列
fd鏈表:Chunk1 > chunk2 > chunk3 > chunk6
此時fd_nextsize/bk_nextsize鏈表也是這樣的
Fd_nextsize鏈表:Chunk1 > chunk2 > chunk3 > chunk6
Ø chunk4插入鏈表中,它的大小和chunk3相同
Chunk1 > chunk2 > chunk3 = chunk4 > chunk6 //fd鏈表
Chunk1 > chunk2 > chunk3 > chunk6 //fd_nextsize鏈表
Ø Chunk5插入鏈表中,它的大小和chunk3相同
Chunk1 > chunk2 > chunk3 = chunk5 = chunk4 > chunk6 //fd鏈表
Chunk1 > chunk2 > chunk3 > chunk6 //fd_nextsize鏈表
Large chunk取出示例
Ø 取出一個和chunk3大小相同的chunk
Chunk1 > chunk2 > chunk3 = chunk4 > chunk6 //fd鏈表
Chunk1 > chunk2 > chunk3 > chunk6 //fd_nextsize鏈表
3 實例
來自https://exploit-exercises.com/protostar/heap3/
測試環境:Ubuntu 16.04 64
測試結果:
l free chunk中只有fd鏈表,沒有bk鏈表;
l Free chunk中prev_size爲0
l free時P標誌(PREV_INUSE)沒有被清除
Heap3.c
millionsky@ubuntu-16:~/tmp/malloc$ gcc -m32 heap3.c (gdb) b main Breakpoint 1 at 0x8048531 (gdb) r AAAA BBBB CCCC |
3個malloc後面下斷
(gdb) b *0x08048540 Breakpoint 2 at 0x8048540 (gdb) b *0x08048550 Breakpoint 3 at 0x8048550 (gdb) b *0x08048560 Breakpoint 4 at 0x8048560 |
【第一次分配】
第一次分配後
查看堆的位置和大小
堆的位置:0x0804b000
堆的大小:0x21000(132KB)
millionsky@ubuntu-16:~$ cat /proc/`pgrep a.out`/maps 08048000-08049000 r-xp 00000000 08:01 11143849 /home/millionsky/tmp/malloc/a.out 08049000-0804a000 r--p 00000000 08:01 11143849 /home/millionsky/tmp/malloc/a.out 0804a000-0804b000 rw-p 00001000 08:01 11143849 /home/millionsky/tmp/malloc/a.out 0804b000-0806c000 rw-p 00000000 00:00 0 [heap] |
查看分配的chunk
當前有兩個chunk
第一個chunk的size爲0x28,P位爲1;
第二個chunk的size爲0x20fd8,P爲爲1
這兩個chunk佔用了整個當前的堆的空間;
(gdb) p/x $eax $1 = 0x804b008 (gdb) x/14wx $eax-8 0x804b000: 0x00000000 0x00000029 0x00000000 0x00000000 0x804b010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b020: 0x00000000 0x00000000 0x00000000 0x00020fd9 0x804b030: 0x00000000 0x00000000 |
【第三次分配】
現在有4個chunk了
(gdb) p/x $eax $3 = 0x804b058 (gdb) x/34wx 0x804b000 0x804b000: 0x00000000 0x00000029 0x00000000 0x00000000 0x804b010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b020: 0x00000000 0x00000000 0x00000000 0x00000029 0x804b030: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b040: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b050: 0x00000000 0x00000029 0x00000000 0x00000000 0x804b060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b070: 0x00000000 0x00000000 0x00000000 0x00020f89 0x804b080: 0x00000000 0x00000000 |
【3個free後面下斷】
(gdb) b *0x080485b6 Breakpoint 5 at 0x80485b6 (gdb) b *0x080485c4 Breakpoint 6 at 0x80485c4 (gdb) b *0x080485e2 Breakpoint 7 at 0x80485e2 |
【free之前】
4個chunk,前面3個有數據
(gdb) x/34wx 0x804b000 0x804b000: 0x00000000 0x00000029 0x41414141 0x00000000 0x804b010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b020: 0x00000000 0x00000000 0x00000000 0x00000029 0x804b030: 0x42424242 0x00000000 0x00000000 0x00000000 0x804b040: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b050: 0x00000000 0x00000029 0x43434343 0x00000000 0x804b060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b070: 0x00000000 0x00000000 0x00000000 0x00020f89 0x804b080: 0x00000000 0x00000000 |
【free(c);】
Chunk 3的數據被擦除了
(gdb) x/34wx 0x804b000 0x804b000: 0x00000000 0x00000029 0x41414141 0x00000000 0x804b010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b020: 0x00000000 0x00000000 0x00000000 0x00000029 0x804b030: 0x42424242 0x00000000 0x00000000 0x00000000 0x804b040: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b050: 0x00000000 0x00000029 0x00000000 0x00000000 0x804b060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b070: 0x00000000 0x00000000 0x00000000 0x00020f89 0x804b080: 0x00000000 0x00000000 |
【free(b);】
Chunk 2的fd指向chunk 3
Chunk 2的數據被擦除了
(gdb) x/34wx 0x804b000 0x804b000: 0x00000000 0x00000029 0x41414141 0x00000000 0x804b010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b020: 0x00000000 0x00000000 0x00000000 0x00000029 0x804b030: 0x0804b050 0x00000000 0x00000000 0x00000000 0x804b040: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b050: 0x00000000 0x00000029 0x00000000 0x00000000 0x804b060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b070: 0x00000000 0x00000000 0x00000000 0x00020f89 0x804b080: 0x00000000 0x00000000 |
【free(a);】
Chunk 1的fd指向chunk 2
Chunk 2的fd指向chunk 3
(gdb) x/34wx 0x804b000 0x804b000: 0x00000000 0x00000029 0x0804b028 0x00000000 0x804b010: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b020: 0x00000000 0x00000000 0x00000000 0x00000029 0x804b030: 0x0804b050 0x00000000 0x00000000 0x00000000 0x804b040: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b050: 0x00000000 0x00000029 0x00000000 0x00000000 0x804b060: 0x00000000 0x00000000 0x00000000 0x00000000 0x804b070: 0x00000000 0x00000000 0x00000000 0x00020f89 0x804b080: 0x00000000 0x00000000 |
4 附件
5 結論
理解ptmalloc堆chunk結構
6 參考文檔
1. https://ctf-wiki.github.io/ctf-wiki/pwn/heap/heap_overview/。
2. Exploiting the heap。http://www.win.tue.nl/~aeb/linux/hh/hh-11.html。
3. https://raw.githubusercontent.com/iromise/glibc/master/malloc/malloc.c。
4. https://exploit-exercises.com/protostar/heap3/。
5. https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/comment-page-1/