关于如何理解Glibc堆管理器(Ⅵ——从House of Orange理解Heap是如何被拓展的)
Last Update:
本篇实为个人笔记,可能存在些许错误;若各位师傅发现哪里存在错误,还望指正。感激不尽。
若有图片及文稿引用,将在本篇结尾处著名来源(也有置于篇首的情况)。
参考文章:
https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/house-of-orange/
https://blog.csdn.net/le119126/article/details/49338003
正文:
本节没有太多内容。本想将IO_FILE一起并入说明,但似乎那样就超出了本专栏的内容了,因此便作罢,仅从一个简单的案例说明这样一个情况:
当Top chunk不足以满足用户需求时,堆是如何拓展而为用户服务的
在第一章时曾提到过,当堆的空间不足以满足申请时,堆管理器有两种拓展方式,其一是使用brk函数使堆向高地址拓展;其二则是使用mmap进行地址映射,从内核直接申请内存
以及,读者可能还不了解House of Orange,但这并不影响接下来的阅读,单纯是一个引子罢了,读者可以将其理解为:不使用free也能将chunk放入Unsorted Bin中的方法
mmap:
尽管本文的重点并不在mmap分配上,但笔者仍觉得有必要对其做些介绍
笔者将mmap的作用理解为:建立内存与磁盘的映射关系,从而达到“只要读写内存即可读写磁盘”的目的。由于只需要读写内存,因此不用read/write函数也能实现磁盘上读写
而在堆的分配中,当需要分配的chunk大小超过mmap分配的阈值(mmp_.mmap_threshold)时,管理器就会调用mmap来分配额外的heap,并在该heap完全不被使用时直接归还给内核
(注:mmp_.mmap_threshold通常为128K)
从这个角度来说,直接归还给内核的内存堆是难以利用的,因此也不在本文的主要讨论范围
可以参考:https://www.cnblogs.com/huxiao-tee/p/4660352.html
作者对mmap做了较为详细的介绍
brk:
调试代码:
1 |
|
断点定于第8行
此时的堆结构为:
1 |
|
p1为0x602000处的chunk,而Top chunk则为0x602020处的chunk
第八行代码处,我们将Top chunk的size字段修改为0x1fe1,此时如果我们再申请0x2000大小的chunk,显然Top chunk已经不足以满足我们的要求了,那么第9行代码执行之后,bins的结构将为:
1 |
|
此时,原本的Top chunk已经被放入了Unsorted Bin中,而p3获得了从0x623000处开始的chunk
问题:fake_size的值是如何得来的,其他数值是否可行?
我们可以浏览如下代码得到答案:
1 |
|
如果,原本的Top chunk还未初始化且size为0
或者,原Top chunk大小大于0x10,且前一个chunk被使用,且结束地址符合页对齐
那么则进行分配新的heap页
由于我们调用过一次malloc,因此Top chunk已经初始化,所以我们需要绕过的检查是第二个
1.伪造处的Size的最后一位必须为1,以表示前一个chunk处于使用(从实际情况考虑,只要没有遭到篡改,这是必然成立的条件)
2.结束地址符合页对齐。一个页面对应大小为4KB,既0x1000字节,也就是说,Top chunk的结束地址应该为0x1000的倍数
本例中原Top chunk为0x602020,只要保证 (0x602020+size)%0x1000==0即可,因此0x0fe1、0x1fe1等符合情况的均可
不妨试着计算一下这个新heap的大小:
1 |
|
可见其为0x23000,与第一个heap的0x21000还多出0x2000字节
说回Bins的放入规则:
堆管理器将原本的Top chunk放入Unsorted Bin,并分配一个新的Heap然后分割成chunk p3和Top chunk
至于原本的Top chunk,如果读者细看了它的size变化,应该会发现少了0x20字节,其实只是被prev_size、size、fd、bk指针占用了而已
感觉这东西似乎没什么可说的,以至于笔者有点不知道该如何描述才能将这种思路表达清楚,还望见谅
插画ID:91095963