Heap--初探--bins神将

Previously on Heap–初探–chunk的获取与释放:我们当时最大的疑惑差不多就是Free后的chunks都怎么了,这里来逐一解答。

前情提要

ptmalloc 会统一管理 heap 和 mmap 映射区域中的空闲的 chunk。当用户再一次请求分配内存时,ptmalloc 分配器会试图在空闲的 chunk 中挑选一块合适的给用户。这样可以避免频繁的系统调用,降低内存分配的开销。
如果实在没有合适的就让商人去top chunk切一块下来
之后我们堆管理器的操作都是以chunk为最小单元,然后一般说的都是chunk地址而不是user_data_start 地址

链表

链表的每个单元都由数据域与指针域组成。
单项链表:

双向循环链表:

Fastbins

大多数程序经常会申请以及释放一些比较小的内存块。如果将一些较小的 chunk 释放之后发现存在与之相邻的空闲的 chunk 并将它们进行合并,那么当下一次再次申请相应大小的 chunk 时,就需要对 chunk 进行分割,这样就大大降低了堆的利用效率。因为我们把大部分时间花在了合并、分割以及中间检查的过程中。
因此,ptmalloc 中专门设计了 fast bin–ctfwiki-pwn
为了提高chunk获取和释放的效率,fastbins采用单链表数据结构来存储对应大小的free chunks,并且采用了FILO策略先进先出,利用的是fd进行单链表链接,而不是bk。链表如下:

所以在free一个可以被放入fastbins的chunk后,他会像压入栈里面一样压入对应的bin(对应大小的bin)中同时其fd会被填入free chunk3的地址(如上图)
这就是为啥之前我们在free一个chunk后(被放入了fastbins)我们再去看那个chunk里面的数据fd字段改变的原因。
例子:

#include<stdio.h>
#include<stdlib.h>

int main()
{
    void* p1 = malloc(0x24);
    void* p2 = malloc(0x24);
    void* p3 = malloc(0x24);
    void* p4 = malloc(0x24);

    free(p1);
    free(p2);
    free(p3);
    free(p4);
    return 0;
}

gdb执行到return 0;

In file: /home/matrix/test.c
   10 
   11     free(p1);
   12     free(p2);
   13     free(p3);
   14     free(p4);15     return 0;
   16 }
──────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────
00:0000│ rsp  0x7fffffffdda0 —▸ 0x602010 ◂— 0x0
01:00080x7fffffffdda8 —▸ 0x602040 —▸ 0x602000 ◂— 0x0
02:00100x7fffffffddb0 —▸ 0x602070 —▸ 0x602030 ◂— 0x0
03:00180x7fffffffddb8 —▸ 0x6020a0 —▸ 0x602060 ◂— 0x0
04:0020│ rbp  0x7fffffffddc0 —▸ 0x4005e0 (__libc_csu_init) ◂— push   r15
05:00280x7fffffffddc8 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov    edi, eax
06:00300x7fffffffddd0 ◂— 0x1
07:00380x7fffffffddd8 —▸ 0x7fffffffdea8 —▸ 0x7fffffffe249 ◂— '/home/matrix/a.out'
────────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────────
 ► f 0           4005d6 main+112
   f 1     7ffff7a2d840 __libc_start_main+240
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> x/32gx 0x602010-0x10
0x602000:    0x0000000000000000    0x0000000000000031  <===== chunk1
0x602010:    0x0000000000000000    0x0000000000000000  
0x602020:    0x0000000000000000    0x0000000000000000 
0x602030:    0x0000000000000000    0x0000000000000031  <===== chunk2
0x602040:    0x0000000000602000    0x0000000000000000
0x602050:    0x0000000000000000    0x0000000000000000
0x602060:    0x0000000000000000    0x0000000000000031  <===== chunk3
0x602070:    0x0000000000602030    0x0000000000000000
0x602080:    0x0000000000000000    0x0000000000000000
0x602090:    0x0000000000000000    0x0000000000000031  <===== chunk4
0x6020a0:    0x0000000000602060    0x0000000000000000
0x6020b0:    0x0000000000000000    0x0000000000000000
0x6020c0:    0x0000000000000000    0x0000000000020f41  <===== top chunk
0x6020d0:    0x0000000000000000    0x0000000000000000

可以看到在free了这四个一样的chunk后:chunk4=====>chunk3=====>chunk2=====>chunk1
注意这里按理说前一个chunk是free状态然后当前chunk 的size的最后位应该置0。但是这里设计fastbins为了防止相邻两的freechunk合并的情况,就把里面的free chunk的size最后的P位都置1,因为合并后可能又要分类啥的太花费时间了。

其他bins

除了fastbins(tache)其他的bins都是双向链表结构。很相似(除largebins)如下。其数据存放规则是FIFO:先进先出

unsortedbin

存放刚刚释放还未分类且无法进入fastbins中的free chunks,相当于相当于small bins 和large bins的缓冲区。
如果用户malloc的大小大于fastbins里面的free chunks那么对管理器会先在unsorted bin中找,如果没找到就会触发unsorted bin中的free chunks遍历并合并,归类到他们该去的bins
这里面的chunks很没有规律,大大小小的都有,只有一个bin来存放,是名副其实的垃圾堆。但是可以减少程序中的碎片free chunks

smallbins

small bins 中每个 chunk 的大小与其所在的 bin 的 index 的关系为:chunk_size = 2 * SIZE_SZ *index,具体如下
下标 | SIZE_SZ=4(32 位)| SIZE_SZ=8(64 位)
–:|:—:|:–
2 | 16 | 32
3 | 24 | 48
4 | 32 | 64
5 | 40 | 80
x | 2*4*x | 2*8*x
63 | 504 | 1008
每个bin链表中存放的chunks大小都一致

可以发现这里的free chunks大小与fastbins中的大小是有一部分重叠的,这背后的原理比较复杂先暂且放放。

largebins

有63个bins,管理大于1008bytes的free chunks (64位)但是在基础heap题中一般不会遇到


  转载请注明: Squarer Heap--初探--bins神将

 上一篇
Heap--初探--基本漏洞 Heap--初探--基本漏洞
First fit规则在malloc一个chunk时,先在bins中寻找能满足用户需求大小的chunk,也就是大于等于用户需求的大小然后返回,而不是一直遍历到完全符合的chunk。比如说在unsortedbin中,找到了一个大于用户申请的c
2020-09-09
下一篇 
Heap--初探--chunk的获取与释放 Heap--初探--chunk的获取与释放
什么是堆堆不同于栈,堆是动态分配的(由操作系统内核或者堆管理器),只有在程序中需要时才会分配。在 CTF 的 pwn 程序中,栈是程序加载进内存后就会出现,而堆是由 malloc、alloc、realloc 函数分配内存后才会出现。其生长方
2020-09-07
  目录