ptmalloc堆概述

ptmalloc堆概述

概述

堆的概念

在程序运行过程中,堆可以提供动态分配的内存,允许程序申请大小未知的内存。堆其实就是程序虚拟地址空间的一块连续的线性区域,它由低地址向高地址方向增长。

堆管理器

我们一般称管理堆的那部分程序为堆管理器。

· dlmalloc – General purpose allocator

· ptmalloc2 – glibc

· jemalloc – FreeBSD and Firefox

· tcmalloc – Google

· libumem – Solaris

扫描二维码关注公众号,回复: 526999 查看本文章

堆管理器处于用户程序与内核中间,主要做以下工作

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/

猜你喜欢

转载自blog.csdn.net/luozhaotian/article/details/80267403