查看原文
其他

large chunk分配过程调试

dolphindiv 看雪学苑 2022-07-01


本文为看雪论坛优秀文章
看雪论坛作者ID:dolphindiv


最近研究ubuntu18.04.5下chunk分配过程,由于large chunk的分配最为复杂,对于深入学习掌握堆块的分配和释放算法要求比较高,理解起来具有一定的难度,具有一般的参考意义,故拿来进行分析。




代码简介


#include<stdio.h>#include<stdlib.h>int main(){ int *p1 =malloc(8); int *p2 =malloc(8); fprintf(stderr,"malloc two fastbin chunk: p1=%p p2=%p\n",p1,p2); void * p3 = malloc(0x500); //malloc large chunk from top chunk void * p4 = malloc(0x8); //void the freed large chunk consolidated with top chunk void * p5 = malloc(0x600); //malloc another large chunk void * p6 = malloc(0x8); //void the freed large chunk consolidated with top chunk free(p3); //free p3 into unsortedbin free(p5); //free p5 into unsortedbin void * p7 = malloc(0x550); //malloc large chunk, remove p3 p5 into largebin fprintf(stderr,"malloc large chunk:p7=%p\n",p7); }

由于64位机上tcache bin存放的chunk的大小不超过1032字节,为了避免释放后的chunk进入tcache,申请和释放大于1032字节的large chunk,这样在释放后进入unsortedbin而不是tcache。
 
先malloc 1个大小为0x500的chunk p3 ,再malloc 1个大小为0x600的chunk p5,为防止这两个large chunk释放后互相合并、与top chunk合并,需要在malloc 第1个chunk和第2个chunk之后,各malloc 1个大小为0x8的chunk,分别为p4、p6。然后,先后释放第1个chunk p3、第2个chunk p5(释放到unsortedbin中),再malloc 1个大小为0x550的chunk。




申请large chunk过程调试


编译程序:

在 free(p3)处设置断点,运行程序,命中断点,然后单步执行free(p5),执行完之后,可以看到chunk p3、chunk p5已经释放到unsortedbin。


(一)在各种bin中搜索合适大小的chunk时,将p3、p5从unsortedbin移到largebin中。


使用s命令进入 malloc.c进行单步调试。

执行到3530行处的_int_malloc()函数,在3591行处设置断点,运行命中断点。

3591行代码的作用是检查fastbin(小于0x80字节)中是否有合适的chunk,显然没有,继续执行到3649行。


3649行代码的作用是检查smallbin(小于0x400字节)中是否有合适的chunk,显然没有,继续执行到3711行。

3711、3712行代码的作用是计算large bin的idx,判断fastbins中是否有释放的chunk。如果有,调用malloc_consolidate()整理fastbins,并放入unsorted bin。malloc_consolidate()先向后合并空闲的chunk,再向前合并空闲的chunk,最后将合并的chunk放入unsorted bin。
 
接下来是判断tcachebin中是否有合适的chunk,也没有。程序执行到3739行处进入大的外层for循环,包含了_int_malloc()函数之后的所有过程。紧接着是内层的第一个while循环,它会遍历unsorted bin中的每一个chunk,如果大小正好合适,就将其取出,否则就将其放入small bin 或者large bin。这是唯一的将chunk 放进smallbin或者largebin的过程。

搜索到unsortedbin中第1个chunk,即victim。

bck为victim后面的chunk,即unsortedbin中第2个chunk。

进行victim大小合法性检查(大于2 * SIZE_SZ,且小于 av->system_mem)后,计算victim的大小。

程序执行到3759行,当请求的chunk属于smallbin、unsortedbin只有一个chunk为lastremainder并且满足拆分条件时,就将其拆分。这里不满足条件。

程序执行到3788、3789行,将victim从unsortedbin中取出。

如果victim的大小正好等于请求chunk的大小,则将victim返回给用户。这里不满足条件。


如果chunk大小不合适,就将其插入对应的bin中。插入过程也就是双链表插入节点的过程。其中插入largebin的操作最为复杂,因为涉及到修改fd_nextsize和bk_nextsize指针。
 
这里victim大小为0x500,属于large chunk,下面的调试显示了将unsortedbin中的0x555555756290处的victim插入largebin的过程。首先根据victim大小计算在largebin中的index,然后得到对应largebin的头结点,并搜索到头结点指向的chunk,再判断largebin是否为空。

此时largenbin为空,修改victim的fd_nextsize和bk_nextsize指向victim自身。

最后修改bk和fd指针,将victim掺入到largebin中。


然后再次执行3742行处的while循环,按照以上过程,将unsortedbin中0x5555557567c0处的大小为0x600字节的chunk放入另外1个largebin。

程序执行到这里,p3、p5两个chunk分别移到对应大小的largebin中,第一个循环结束。


(二)遍历largebin,找到合适大小的chunk,进行切分操作,将用户申请的chunk返回给用户,剩余部分移到unsortedbin中。

由于用户申请的是large chunk,程序进行搜索largebin的过程,为加快搜索速度,反向遍历nextsize链表,找到第一个不小于nb的chunk。
 

根据前面计算的largebin idx,计算出largebin的头结点0x7ffff7dce0e0 ,这个头结点指向大小为0x500的chunk(0x555555756290),显然这个大小小于用户申请的大小,3919至3921行的判断条件不满足。

程序执行3987行,对idx进行加1操作,并计算出下一个largebin的头结点。

接下来,进入内层第二个for循环。根据binmap来搜索bin,因为申请的chunk大小所对应bin没有找到合适的chunk,所以就从下一个bin中搜索(large bin中的chunk大小在一定范围之内,各个bin之间按照一定的等差数列增加大小)。
 
根据idx取得对应的bin,地址为0x7ffff7dce0f0,还没有到达0x5555557567c0处的chunk p5(大小为0x600)所在的largebin( 0x7ffff7dce110 )。
在内层第二个循环内部,寻找第一个不为空的block,再根据bit位找到合适的bin,此时bit=64,map=272。
找到next_bin,地址为0x7ffff7dce100 ,仍没有到达0x7ffff7dce110 处的largebin。继续while循环,此时bit=128。

继续寻找next_bin,地址为0x7ffff7dce110 ,即搜索到chunk p5(大小为0x600)所在的largebin,此时bit=256 ,已经不满足(bit & map) == 0的条件,跳出while循环。


取出largebin( 0x7ffff7dce110 )中的chunk p5(大小为0x600)为victim。

 
4021行处的代码判断搜索到的largebin是否为空,这里显然不为空,条件不成立。

程序执行到4030行代码处,计算victim的大小。

验证完victim的大小大于请求chunk的大小后,将victim的大小减去请求chunk的大小,并进行unlink操作,将victim从largebin中解链。

如果victim中剩余部分的大小小于MINSIZE,则将整个victim返回给用户,显然不成立。可以看到,unlink操作后,0x600字节大小的largebin中已经没有victim。


对victim进行切分操作,将0x550大小的chunk返回给用户,并将剩余部分remainder插入unsortedbin。内层第二个循环到此结束。






如果上面的操作还不能满足要求,就只能从top chunk上进行分配。



 


看雪ID:dolphindiv

https://bbs.pediy.com/user-home-717768.htm

*本文由看雪论坛 dolphindiv 原创,转载请注明来自看雪社区






# 往期推荐

1. 混淆后OKHTTP框架的通用抓包方案探索

2. Android漏洞挖掘三板斧——drozer+Inspeckage(Xposed)+MobSF

3.CVE-2017-17215(华为HG532远程命令执行漏洞)复现学习

4. 高Glibc版本下的堆骚操作解析

5. 新人PWN堆Heap总结off-by-null专场

6. CVE-2012-3569 VMware OVF Tool格式化字符串漏洞分析



公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存