ptmalloc堆概述

ptmalloc堆概述

概述

堆的概念

在程序運行過程中,堆可以提供動態分配的內存,允許程序申請大小未知的內存。堆其實就是程序虛擬地址空間的一塊連續的線性區域,它由低地址向高地址方向增長。

 

堆管理器

我們一般稱管理堆的那部分程序爲堆管理器。

· 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

http://ftp.gnu.org/gnu/glibc/

查看glibc版本

millionsky@ubuntu-16:~/tmp$ ldd --version

ldd (Ubuntu GLIBC 2.23-0ubuntu9) 2.23

 

堆的基本操作

 #include <stdlib.h>

 

/*

** 0時返回最小的chunk32位系統爲16,64位系統爲24/32

*/

 void *malloc(size_t size);

 

/*

**除非被mallopt禁用,釋放很大的內存空間時,程序會將這些內存空間還給系統。

*/

 void free(void *ptr);

 

系統調用

brk/mmap/munmap

 

glibc函數

brk/sbrk/mmap/munmap

 

 

多線程支持

相對於dlmalloc,在glibcptmalloc實現中,比較好的一點就是支持了多線程的快速訪問。在新的實現中,所有的線程共享多個堆。

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. Chunk4種類型:

· 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 指向下一個(非物理相鄰)空閒的 chunkfree chunk中才有

l bk 指向上一個(非物理相鄰)空閒的 chunkfree chunk中才有

通過 fd bk 可以將空閒的 chunk 塊加入到空閒的 chunk 塊鏈表進行統一管理

l fd_nextsize 指向前一個與當前 chunk 大小不同的第一個空閒塊,不包含 bin 的頭指針。只用於large chunk

l bk_nextsize 指向後一個與當前 chunk 大小不同的第一個空閒塊,不包含 bin 的頭指針。只用於large chunk

一般空閒的 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 chunksize&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中的下一個(非物理相鄰的下一個chunkfree chunk
bk: Backward pointer – 指向相同bin中的上一個(非物理相鄰的上一個chunkfree 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,它不能使用下一個chunkprev_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 binunsorted bin中沒有可用的,則掃描binmaps查找next largest bin。找到後,進行切分,返回給用戶,剩餘的chunk加入unsorted bin。這個剩餘的chunk就成爲新的last remainder chunk

 

當用戶後續請求small chunk時,如果last remainder chunkunsorted bin中的唯一的chunk,則last remainder chunk被切分,返回給用戶,剩餘的chunk加入unsorted bin。這個剩餘的chunk就成爲新的last remainder chunk。這樣後續的內存分配都是相鄰的。

2.2 Bins

注意:Bin的翻譯爲箱子,chunk的翻譯爲塊;

 

1. Binfreelist數據結構,用於保存free chunks

2. 基於chunk sizebins被劃分爲以下幾種類型:

l Fast bin(單鏈表,大小相同)

l Unsorted bin(環形雙鏈表,亂序狀態,空閒 chunk 迴歸所屬 bin 之前的緩衝區)

l Small bin環形雙鏈表,大小相同

l Large bin環形雙鏈表,按大小降序排列

 

bins

l Binsbin header的數組;

每個binfree chunk的雙鏈表;

 

l Bins這個數組(malloc_state)存儲unsortedsmalllarge bins

#define NBINS 128

mchunkptr bins[ NBINS * 2 - 2 ];

 

總共128bins,劃分如下:

· 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 UsedFIFO)分配順序,每個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佔兩個,因而總共可以存儲127bin

索引對應關係爲:

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 chunkbins稱爲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 binschunk不能從鏈表的中間移除,因此使用了單鏈表;

Chunkfast bin鏈表中的添加和刪除只發生在鏈表的頭部--LIFO(一般的binFIFO);

4. Chunk size的間隔是8字節(32位)或16字節(64位)

Chunk sizefast 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 binchunks的大小是相同的

6. Malloc初始化時,最大的fastbin(用戶數據)設置爲6432位)或12864位)

因此默認時,大小[16,72]/[32,144]chunksfast 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

fastbininuse位總是被設置;

兩個free chunks可以是相鄰的,不會合併成一個free chunk

不合並會導致更多的分片,但可以提高速度;

Fast bin中的chunks只會成塊合併(bulk);

 

8. Malloc(fast chunk)

· 初始時,fast bin max size and fast bin indices是空的,即時用戶請求一個fast chunkmalloc也不會進入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中的第一個chunkbinlist中移除並返回給用戶;

 

9. free(fast chunk) –

· 計算Fast bin index獲取對應的binlist

· free chunk加入到獲取的binlist的前端(front position;

2.2.2 Unsorted bin

chunk切分後的剩餘部分、所有釋放後返回的chunk,都會被放入unsorted bin,而不是對應的binsMalloc會掃描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 chunkbins稱爲small bins

在內存的分配和釋放上,Small binslarge bins快,比fast bins慢;

 

2. 相關定義

INTERNAL_SIZE_T4/8字節

SIZE_SZ4/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_SIZE512/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 binschunks是從鏈表的中間拆除的。而鏈表是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) –

開始時,所有的small bins都是NULL,即時用戶請求一個small chunk,也會進入unsorted bin code,而不是small bin code

同樣在第一次調用malloc的時候,small binlarge bin的數據結構(malloc_state中的bins)會被初始化,bins會指向自身表明它是空的;

之後當small bin非空時,對應的binlist中的最後一個chunk被移除和返回給用戶;

7. free(small chunk) –

釋放chunk時,檢查它的前面和後面的chunk是否free chunk。如果是則合併;從相關的鏈表中unlink這些chunks,將新合併的chunk加入到unsorted bin的鏈表的開始;

2.2.4 Large bin

1. 大小大於等於512字節的chunk稱爲large chunk

存儲large chunkbins稱爲large bins

在內存的分配和釋放上,large binssmall bins慢;

2. bins的數目--63

每個large bin包含一個free chunks的環形雙鏈表(即binlist);

使用雙鏈表的原因是:在large binschunks是從鏈表的任何位置增加和移除的(前面、中間、後面);

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)

開始時,所有的large bins都是NULL,即時用戶請求一個large chunk,也會進入next largest bin code,而不是large bin code

同樣在第一次調用malloc的時候,small binlarge bin的數據結構(malloc_state中的bins)會被初始化,bins會指向自身表明它是空的;

之後當large bin非空時,如果bin list中最大的chunk size大於用戶請求的大小,會從後往前遍歷binlist,找到一個最近大小的chunk。該chunk被劃分爲兩個chunk

· User chunk (用戶請求的大小) – 返回給用戶.

· Remainder chunk (of remaining size) – 增加到 unsorted bin.

如果用戶請求的大小比binlist中最大的chunk還大,則進入next largest bin codeNext 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

一般空閒的 large chunk fd 的遍歷順序中,按照由大到小的順序排列。這樣做可以避免在尋找合適chunk 時挨個遍歷。大小相同時總是插入第二個位置。取出時也是取出第二個位置。這樣可以避免調整fd_nextsize/bk_nextsize指針。

由於bin頭不包含fd_nextsize/bk_nextsize指針,它不在fd_nextsize/bk_nextsize鏈表中。

 

如何理解呢?_int_malloc的源碼中有插入和取出的邏輯

Large chunk插入示例

Ø 通過fd/bk鏈接起來的big chunkfd鏈表中按大小降序排列

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鏈表

實例

來自https://exploit-exercises.com/protostar/heap3/

 

測試環境:Ubuntu 16.04 64

測試結果:

l free chunk中只有fd鏈表,沒有bk鏈表;

l Free chunkprev_size0

l freeP標誌(PREV_INUSE)沒有被清除

 

Heap3.c 

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

void winner()
{
printf("that wasn't too bad now, was it? @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
char *a, *b, *c;

a = malloc(32);
b = malloc(32);
c = malloc(32);

strcpy(a, argv[1]);
strcpy(b, argv[2]);
strcpy(c, argv[3]);

free(c);
free(b);
free(a);

printf("dynamite failed?\n");
}

 

millionsky@ubuntu-16:~/tmp/malloc$ gcc -m32 heap3.c

(gdb) b main

Breakpoint 1 at 0x8048531

(gdb) r AAAA BBBB CCCC

 

3malloc後面下斷

(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

第一個chunksize0x28P位爲1

第二個chunksize0x20fd8P爲爲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

 

【第三次分配】

現在有4chunk

(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

 

3free後面下斷】

(gdb) b *0x080485b6

Breakpoint 5 at 0x80485b6

(gdb) b *0x080485c4

Breakpoint 6 at 0x80485c4

(gdb) b *0x080485e2

Breakpoint 7 at 0x80485e2

 

free之前】

4chunk,前面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 2fd指向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 1fd指向chunk 2

Chunk 2fd指向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

附件

結論

理解ptmallocchunk結構

參考文檔

1. https://ctf-wiki.github.io/ctf-wiki/pwn/heap/heap_overview/

2. Exploiting the heaphttp://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/

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章