-
-
[原创]关于fastbin合并问题的研究
-
发表于:
2020-2-19 11:03
10206
-
最近做题遇到了fastbin合并相关的内容,对于合并时机与合并过程一头雾水,于是结合glibc源码(glibc-2.23)看了看,做个小总结。
首先帮助大家快速回顾一下fastbin,直接从走位的文章中搬运了过来。fastbin的chunksize为16到80字节,在内存分配和释放中,fastbin是速度最快的。fastbin的两个特点:
1、fastbin的个数为10个。
2、fastbin由单链表构成,无论是添加还是移除fastchunk,都对链表尾进行操作,采取后入先出算法。fastbinsY数组中每个fastbin元素均指向了该链表的rear end(尾结点),而尾结点通过其fd指针指向前一个结点。
如图所示:
fastbin会在以下情况下进行合并(合并是对所有fastbin中的chunk而言)。
malloc:
1、在申请large chunk时。
2、当申请的chunk需要申请新的top chunk时。
free:
3、free的堆块大小大于fastbin中的最大size。(注意这里并不是指当前fastbin中最大chunk的size,而是指fastbin中所定义的最大chunk的size,是一个固定值。)
另外:malloc_consolidate既可以作为fastbin的初始化函数,也可以作为fastbin的合并函数。
截取了malloc中的部分关键代码。其中nb为所申请的chunk的真实大小。
第一个malloc_consolidate (av)的作用是初始化fastbin,在这里跳过。我们聚焦第二个malloc_consolidate (av)。这里首先进行了in_smallbin_range (nb),判断所申请的大小是否在smallbin所定义的大小中,如果不是,则会对fastbin进行合并。我们来写个小程序验证一下。
在free了ptr1与ptr2之后,此时的堆情况:
在malloc了一个large chunk之后,此时的堆情况:
我们发现fastbin已经进行了合并。
这里有人可能觉得这么做太激进了,因为可能在我们申请large chunk的时候根本用不上这些释放的空间,但是首先这能很好的解决碎片化问题,其次程序很少会连续的既使用小堆块,又使用大堆块,因此这种情况发生的并不多。
首先看一下关键代码
首先判断top chunk的size是否足够我们进行下一次的分配,如果不够,那么判断是否有fastbin的存在,如果存在fastbin,那么则进行合并。然后再去与smallbin和largebin匹配。如果都不匹配,则扩展top chunk。
我们还是通过一个小程序来说明
在malloc(0x70)之前,此时top chunk的size已经小于0x70
此时的堆情况
然后我们再申请一个比较小但是大于top size的堆
此时对fastbin进行了合并。
在free(chunk)的时候,如果chunk的大小大于fastbin中所定义的最大堆块的大小,则进行合并。
这里还是拿小程序进行演示。
在free(ptr1),free(ptr2)之后,此时的堆情况
在free(ptr4)以后的堆情况
可以看到fastbin被合并了。这里需要注意由于是合并到了unsortbin中,所以有时我们可以利用这里泄漏出libc基地址。
那么fastbin到底是怎么在进行合并的呢?这里我们结合malloc_consolidate函数来分析
大概过程(会循环fastbin中的每一块):
1、首先将与该块相邻的下一块的PREV_INUSE置为1。
2、如果相邻的上一块未被占用,则合并,再判断相邻的下一块是否被占用,若未被占用,则合并。
3、不管是否完成合并,都会把fastbin或者完成合并以后的bin放到unsortbin中。(如果与top chunk相邻,则合并到top chunk中)这点尤其要注意,这也是为什么可以泄漏出libc基地址的原因。
还是通过小程序来演示:
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)