pwn - 堆 初步学习
堆的申请
- 首先系统会调用一些函数在内存中开辟一大片空间作为堆的分配使用空间。
- malloc函数再从这一片堆的分配使用空间中分配0x10大小的空间,将指向该空间的地址返回给ptr(余下的空间称为topchunk)
示例1

首先我们现在main函数下断点,观察此时内存情况

可以看到此时在虚拟内存中系统还没有给我们开辟堆
然后我们运行完第一个malloc之后

可以看到此时出现了一片很大的堆空间,再用heap命令观察


在fastbin中出现了一个0x21(33)大小的chunk,我们申请了0x10,为什么会是0x21呢,这就涉及到了一个chunk的最小大小,chunk是有格式的,由chunk头和用户数据区组成(使用中),其中chunk头由prev_size和size组成(之后会说),用户数据区才是我们申请的0x10
堆的数据结构

我们称运行过程中被malloc分配的内存为一个chunk,这块内存在ptmalloc中用malloc_chunk结构体表示,当程序申请的chunk被free时,会被加入相应的空闲管理列表中(也就是bins)。
chunk的两种状态
由上图知道,chunk由一个统一的结构体声明,但是它的神奇之处在于,在被使用时和空闲时却又有两种不同的状态


对应字段的解释

prev_size:如果前一个chunk是空闲的,该域表示前一个chunk的大小,如果前一个chunk不空闲,该域无意义
chunk的复用:chunk可以使用它的下一个chunk的prev_size字段
size:当前chunk的大小,并且它的二进制的后三位为标记位,记录了当前chunk和前一个chunk的一些属性,重点是最后一位(P位)
PREV_INUSE:记录前一个chunk是否被分配
fd:指向了下一个(非物理相邻)空闲的chunk,其实也就是前一个被free的chunk
bk:指向了上一个(非物理相邻)空闲的chunk,就是后一个被free的chunk
因为bins采取链表管理,新的chunk是往前插入(伪链表头),有点类似于栈,在下边实例2可以直观看到
fd_nextsize:指向前一个与当前大小不同的第一个空闲块,不包含bin的头指针(用于large chunk)
bk_nextsize:指向了后一个与当前chunk大小不同的第一个空闲块,不包含bin的头指针(用于large chunk)
- 我们一般称前两个字段为chunk header,后面的部分称为user data。每次malloc申请得到的内存指针,其实指向user data的起始处
Ptmalloc
Ptmalloc使用chunk实现内存管理,对chunk的管理基于独特的边界标记法;重要的是对地址的对齐。
Ptmalloc的地址对齐方式依赖平台定义的size_t长度,对于32位平台,size_t长度为4字节,对Linux x86_64为8字节
以64位为例
prev_size = 8byte
size = 8byte
所以一个使用中的chunk的大小的计算公式应该是:
1 | in_use_size = (用户请求的大小 + 16 - 8 ) align to 8B
|
加16也就是prev_size和size的大小,减去8是因为向一个chunk“借”了8B(prev_size)
所以在64位平台上一个最小chunk的大小为0x20(chunk header+fd+bk),再加上地址的对齐因素,所以size字段的低三位永远不会被使用,所以可以用来当作flag位
堆的释放
堆的释放一般都是用free函数实现。堆释放后,会被添加到相应bins中进行管理;对于空闲的chunk,ptmalloc采用分箱式内存管理方式,根据空闲chunk的大小和处于的状态将其放在四个不同的bin中,这四个空闲chunk的容器包括fast bin,unsorted bin,small bin 和 large bin
bins
glibc malloc分配了若干个bins,为了方便查找,glibc提供了两个数组:fastbinY和bins
Bins,英文解释为垃圾桶,在这里就是存放free的chunk

采用链表管理
struct malloc_state

- fastbinsY为拥有10(NFASTBINS)个元素的数组,用于存放每个fast chunk链表头指针,所以fast bins 最多包含10个fast chunk的单向链表(实际为7个)
- bins用于存储 unsorted bin,small bins 和 large bins的chunk链表头,small bins 一共62个,large bins一共63个,加起来一共125个bin
实例2

执行完四个malloc后

一个独有的特点:
fastbin无论在使用中还是free后,它的inuse位都是置1的
运行完第一个free后

我这里出现了tcachebins,按理说应该是fastbins,查资料
tcache 是glibc2.26之后引进的一种新机制,类似于fastbin一样的东西,每条链上最多可以有7个chunk,free的时候当tcache满了才放入fastbin,unsortbin中,当malloc的时候,优先去tcache找
我们先从glibc2.23开始学习,所以用patchelf更换成2.23,gdb再次调试

好,这样就可以看到fastbins中已经有了一个chunk的指针,然后我们再free一个chunk

可以看到fastbins链表的第一个chunk为新free的chunk

可以看到第二个chunk的fd指针变为了第一个chunk的地址
依次运行完四个free后,情况也是一样

fastbin总结
fastbins主要是用于提高小内存的分配效率,默认情况下,对于SIZE_SZ为4B的平台,小于64B的chunk分配请求,对于SIZE_SZ为128B的chunk分配请求,首先会查找fastbins中是否有所需大小的chunk存在(精确匹配),如果存在,就直接返回。(glibc2.23)
fastbins采用单链表管理。其他bins采用双链表
unsortedbins
而对于大小超出fastbins范围的chunk,会先到unsortedbin中。
分配时,如果在unsorted bin中没有合适的chunk,就会把unsorted bin 中所有的chunk分别加入到所属的bin中,然后再在bin中分配合适的chunk。
下面进行验证
实例3

我们把第一个chunk改为0x110大小,再看第一次free后的结果

可以看到出现在了unsortedbin中,符合验证。
再看再次malloc(0x110)之后的结果

可以看到已经被分配出去了
eBPF安全开发与攻防对抗
最后于 2021-6-18 23:46
被77pray编辑
,原因: