开个堆系列,先声明一下,本人堆学的不是太好,如果有错误,烦请各位师傅指出,谢谢!
1.arena:堆内存本身
(1)主线程的main_arena:由sbrk函数创建。
①最开始调用sbrk函数创建大小为(128 KB + chunk_size) align 4KB 的空间作为 heap
②当不够用时,用sbrk或者mmap增加heap大小。
(2)其它线程的per thread arena:由mmap创建。
①最开始调用 mmap 映射一块大小为HEAP_MAX_SIZE(32 位系统上默认为 1MB,64 位系统上默认为 64MB)的空间作为 sub-heap。
②当不够用时,会调用 mmap 映射一块新的 sub-heap,也就是增加 top chunk 的大小,每次 heap 增加的值都会对齐到4KB。
2.malloc_state:管理arena的一个结构,包括堆状态信息,bins链表等等
(1)main_arena对应的malloc_state存储在glibc的全局变量中
(如果可以泄露malloc_state结构体的地址,那么就可以泄露glibc的地址)
(2)per thread arena对应的malloc_state存储在各自本身的arena
3.bins:用链表结构管理空闲的chunk块,通过free释放进入的chunk块(垃圾桶)
4.chunks:一般意义上的堆内存块
▲内存中的堆情况:
全局变量glibc:main_arena = struct malloc_state:
Free
chunk1
Free
chunk2
Top
chunk
低地址------------------------------------------------------------- ------------------>高地址
由sbrk创建的main_arena
(1)可以把bin也当作一个chunk,不同Bins管理结构不同,有单向链表管理和双向链表管理。
(2)top里的bk保存Topchunk的首地址。
(bk和fd只用于Bins链表中,allocated_chunk中是属于用户可以使用的内容)
1.在使用中的allocated_chunk和未被使用的free_chunk:
(1)allocated_chunk:
(2)free_chunk:
2.prev_size:8字节,保存前一个chunk的大小,在allocatedchunk中属于用户数据,参考上述的图片,free_chunk的下一个chunk的pre_size位为该free_chunk的size。
3.size:8字节,保存当前chunk大小。(free和allocated都有用)一个chunk的size以0x10递增,以0x20为最小chunk。
(1)malloc(0x01):会有0x20这么大,实际用户可用数据就是0x18。size=0x21
(2)malloc(0x01-0x18):仍然0x20这么大,实际用户可用数据就是0x18。size=0x21
(3)malloc(0x18):会有0x30这么大,实际用户可用数据是0x28。size=0x31
所以size这个8字节内存的最低4位都不会被用到,所以malloc管理机制给最低的3位搞了个特殊形式标志位,A,M,P,分别代表不同内容。
①A:NON_MAIN_ARENA,代表是否属于非main_arena,1表是,0表否。就是线程的不同。
②M:IS_MMAPPED,代表当前chunk是否是mmap出来的。
③P:PREV_INUSE,代表前一个chunk是否正在被使用,处于allocated还是free。
(标志位为1都代表是,0都代表否)
1.fastbins:放在struct malloc_state中的mfastbinptr fastbinsY[NFASTBINS]数组中。
(1)归类方式:只使用fd位
①bin的index为1,bins[0],bins[1]组成一个bin。
②规定大小的chunk归到一类,但个数有限,不同版本不同,同时也可以设置其范围:
M_MXFAST即为其最大的参数,可以通过 mallopt()进行设置,但最大只能是80B。
(2)单向链表:
▲例子:a=malloc(0x10); b=malloc(0x10); c=malloc(0x10); d=malloc(0x10)
FastbinY,d,c,b,a
①free(a)之后:
②free(b)之后:
③free(c)之后:
④free(d)之后:
(3)后进先出:
①m=malloc(0x10): m->d
②n=malloc(0x10): n->c
(4)不改变IN_USE位(p位):
如果某个chunk进入到fastbin中,那么该chunk的下一个chunk的IN_USE位还是为1,不会改变成0。
例子:a=malloc(0x10); b=malloc(0x10); c=malloc(0x10);
①free(a)之后: b.p=1
②free(b)之后: c.p=1; b.p=1
p位不会变成0,如果该chunk进入到fastbins中。
可以进行free(0),free(1),free(0),但是不能直接free(0)两次。
(5)除了malloc_consolidate函数会清空fastbins,其它的操作都不会减少fastbins中chunk的数量。
2.smallbins:放在bins[2]-bins[125]中,共计62组,是一个双向链表。最大chunk的大小不超过1024字节
(1)归类方式:
①相同大小的chunk归到一类:大小范围[0x20,0x3f0],0x20、0x30、....0x3f0。每组bins中的chunk大小一定。
②每组bin中的chunk大小有如下关系:Chunk_size=2 * SIZE_SZ * index,index即为2-63,下图中64即为largebins的范围了。(SIZE_SZ在32,64位下分别位4,8。)
(2)双向链表:
▲例子:a=malloc(0x100); b=malloc(0x100); c=malloc(0x100)
①free(a)之后: smallbin,a
②free(b)之后: smallbin,b,a
③free(c)之后: smallbin,c,b,a
(fd,bk为bins[n],bins[n+1]。fd和bk共同构成一个Binat。)
(3)先进先出:
①m=malloc(0x100): m->a
②n=malloc(0x100): n->b
(4)当有空闲chunk相邻时,Chunk会被合并成一个大chunk,这里指的是物理上的地址相邻,不是链表中相邻。通过判断当前chunk的in_use位的值来判断前一个chunk是否处于Free,如果是,那么合并。再通过当前chunk首地址加上size取得下一个chunk首地址,再将下一个chunk首地址加上它自己的size,取得下下个chunk的首地址,然后判断下下个chunk的in_use位的值看是否下个chunk处于Free,如果处于Free,则合并。
3.largebins:放在bins[126]-bins[251],共计63组,bin的index为64-126,最小chunk的大小不小于1024个字节。
(1)归类方式:范围归类,例如bins[126]-bins[127]中保存chunk范围在[0x400,0x440]。且chunk在一个bin中按照从大到小排序,便于检索。其它与smallbins基本一致。
①范围模式:由以下代码定义范围:
②范围具体实例:
(2)双向链表,但是有两种排列方式:
①取用排列:
首先依据fd_nextsize,bk_nextsize两个指针大小找到适合的,然后按照正常的FIFO先进先出原则,通过fd,bk来排列。
②大小排列:
每个进入largebin的chunk
其chunk_addr+0x20处即为其fd_nextsize指针,chunk_addr+0x28处为其bk_nextsize指针。
然后通过fd_nextsize,bk_nextsize两个指针依据从大至小的顺序排列:
(这张图片我也忘记从哪里整的了...侵删)
其中size顺序为:D>C>B>A,但是释放顺序却不一定是这样的。设置这个的原因是当申请特定大小的堆块时,可以据此来快速查找,提升性能。
(3)特殊解链:
由于largebin中会存在fd_nextsize指针和bk_nextsize指针,所以通常的largebin_attack就是针对这个进行利用的。
借用星阑科技的一张图说明一切:
4.unsortedbins:bins[0]和bins[1]中,bins[0]为fd,bins[1]为bk,bin的index为1,双链表结构。
(1)归类方式:只有一个bin,存放所有不满足fastbin,未被整理的chunk。
(2)双向链表:
a=malloc(0x100); b=malloc(0x100); c=malloc(0x100)
①free(a)之后: unsortedbin,a
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-8-8 23:26
被PIG-007编辑
,原因: