0×00 前面的话
在了解这部分的时候,首先你最好阅读一下这两篇博客:
在内存中,堆是一个很有趣的地方,因为它可以由用户去直接的进行分配与销毁,所以也产生了一些很有趣、奇思妙想的漏洞,像unlink漏洞、House系列漏洞等等。但是在学习的过程中,我们很容易难以理解那些介绍的比较模糊的概念,比如 unsortedbin 在某些条件下会放回 smallbin 或 largebin 中,那到底是什么时候?也会对一些大佬构造的 payload 犯迷糊,为什么这里多了一个chunk,为什么这个字节要填充…,大佬们往往不对这些细节做过多的解释,但是这可难为了我们初学堆利用的新兵,所以,我想写几篇文章,将堆的运作机制,例如一些基本的概念,malloc机制、free机制、保护机制,和利用方法结合起来说一下,让大家能够对堆这一块有个较为清楚的认识,少走一些弯路。首先呢,我想在这篇文章中较为细致的介绍一下堆中的一些情况,剩下的有机会的话我会一并写成一个系列。
这篇文章主要分为四个部分:
|
|
这些内容相对比较重要,如果看完还觉得不够的,推荐大家去读一下华庭老师的《glibc内存管理ptmalloc源代码分析》。
0×01 chunk 简介
首先先说一下堆是如何分配的,在内存中,堆(低地址到高地址,属性RW)有两种分配方式(与malloc申请chunk做区分):
|
|
在内存中进行堆的管理时,系统基本是以 chunk 作为基本单位,chunk的结构在源码中有定义
|
|
INTERNAL_SIZE_T 即 size_t
|
|
我们可以打印一下本机的 sizeof(size_t),这个长度可以说是一个基准单位
|
|
这个结构不再多谈,相关的介绍网上很多,主要提一下结构体中最后两个指针 fd_nextsize 和 bk_nextsize,这两个指针只在 largebin 中使用,其他情况下为 NULL。我们可以根据 chunk 的状态将其分为三种(allocated chunk、free chunk、top chunk):
|
|
其中在 free chunk中有一种特殊的chunk(last remainder chunk):
|
|
重点强调一下:这里的上一块表示在内存的堆中连续的chunk的上一块,区别bin中的前后关系。另外 chunk 的前后关系只有在bin中是使用fd、bk指针标识的,在内存中连续的chunk则通过 prev_size 和 size 来寻找前后 chunk,当然,这也就造成了漏洞。
由于chunk会在几种状态之间切换,当其为free chunk时,最少需要4sizeof(size_t)的空间,所以有最小分配大小。并且由于prev_size的复用,所以实际申请的大小为 max(2sizeof(size_t)(chunk_header)-sizeof(size_t)(prev_size)+申请大小, 最小分配大小),而且 chunk的size是按照 2sizeof(size_t)对齐的,也就是说当你申请一个不是 2sizeof(size_t)整倍数的空间时, malloc 返回的 size 有会对齐,大于实际申请的空间。
另外提一下,当 malloc 一个chunk后,实际返回用户的地址为chunk除去chunk header后的地址,而在bin中存储的是chunk的地址,也就是说
|
|
0×02 bin简介
bin在内存中用来管理free chunk,bin为带有头结点(链表头部不是chunk)的链表数组,根据特点,将bin分为四种,分别为(fastbin、unsortedbin、smallbin、largebin):
|
|
这其中 fastbin 像是cache,用来实现快速的chunk分配,其中的chunk size大小与smallbin中的有重复(只是说大小,chunk并不重复)
unsortedbin 功能也是作为cache,尽量减少搜索合适chunk的时间。
这四个bin中,除了fastbin,其他三个都是维护双向循环链表,并且由一个长度为128 size_t的数组bins维护,bins结构如下:
NULL | unsortbin | smallbin | largebin | NULL |
---|---|---|---|---|
0 | 1 | 2-63 | 64-126 | 127 |
0×03 malloc机制
malloc功能主要由 _int_malloc() 函数实现,原型如下:
|
|
当接收到申请的内存大小后,我们看一下malloc的申请过程。
|
|
留意一点:系统实际分配的内存地址与返回的地址是不同的,返回的地址直接指向了除去 chunk header 的地址。
当然,我们注意到上面的分配过程并没有完成,当 smallbin 中没有 chunk 或者 smallbin 未初始化时,并没有返回分配结果,这种情况下的chunk分配将在后面与largebin的分配一起处理
|
|
如果这之后还未找到合适的chunk,那么就会使用top chunk进行分配,还是没有的话,如果在多线程环境中,fastbin可能会有新的chunk,再次执行合并,并向unsortedbin中重复上面,还是没有的话,就只能向系统申请了。
以上就是malloc分配的全经过。
几个malloc检查:
|
|
0×04 free机制
1.首先使用 chunksize(p) 宏获取p的size
|
|
也就是直接屏蔽了控制位信息,不过不要紧,chunk的分配是 2*sizeof(size_t) 对齐的,所以屏蔽低三位对大小无影响
2.安全检查:
|
|
3.大小为fastbin的情况(不改变inuse位)
|
|
简单的小例子
|
|
报错
|
|
没问题
4.其他情况
|
|
我们可以看到,针对free的检查主要是下一块的size和inuse位,另外fastbin的检查可以用来做double free。
0×05
以上就是对堆的情况所做的一些介绍,了解堆的保护机制后,我们便可以在攻击时想办法进行绕过,从而构造出那些光怪陆离的payload。
*本文原创作者:hellowuzekai,属于FreeBuf奖励计划,禁止转载