一、什么是last remainder
如果在bins链中存在freechunk时,当我们去malloc的时候,malloc的请求大小比freechunk的大小小,那么arena就会切割这个freechunk给malloc使用,那么切割之后剩余的chunk就被称为“last remainder”。 当产生last remainder之后,表示arena的malloc_state结构体中的last_remainder成员 指针就会被初始化 ,并且指向这个last remainder。
二、last remainder产生的情景(重点)
malloc的时候,不论malloc的大小,首先会去检查每个bins链(出去fastbins链)是否有与malloc相等大小的freechunk,如果没有就去检查bins链中是否有大的freechunk可以切割,如果切割,那么就切割大的freechunk,那么切割之后的chunk成为last remainder,并且last remainder会被放入到unsortedbin中。 切割unsortedbin其实就是_int_malloc函数的for大循环的第一步,切割smallbins、largebins其实就是_int_malloc函数的for大循环的第三步(见_int_malloc函数文章:https://blog.csdn.net/qq_41453285/article/details/99005759 )。
切割unsortedbin中的大chunk时:
当unsortedbin有对应的freechunk可以给malloc切割使用时,unsortedbin会发生以下步骤:
①先将这些freechunk放置到对应大小的bins链上(放入smallbin或largebin)。 ②放置到对应的bins链上之后,切割此freechunk。 ③切割之后会产生last remainder,再将last remainder放到unsortedbin上。 例如:
此时,unsortedbin有两个0x800的freechunk。 此时申请一个0x600的chunk。 那么unsortedbin会先consolidate,把两个0x800的freechunk先移动到largebin中。 然后再切割largebin的freechunk,将切割后余留下的0x200放入unsortedbin。
切割smallbins、largebins中的大chunk时
①切割smallbins或者largebins中的大freechunk,产生last remainder。 ②将last remainder放入到unsortedbin中。
注意(重点): malloc永远不会去检测切割fastbins(详情见_int_malloc函数的执行顺序)。
三、last remainder的consolidate
概念: 当我们切割一个bins链中的大chunk时产生last reminader时,会发生consolidate,但是此种consolidate不会去整理fastbins中的freechunk(重点)。
四、演示案例
#include <stdio.h>
#include<malloc.h>
#include<unistd.h>
#include<string.h>
int main(){
int size = 0x120;
void *p = malloc(size);
void *junk = malloc(size); //放置释放p和q之后,p和q发生合并
void *q = malloc(size);
void *r = malloc(size); //放置释放q之后,q和topchunk发生合并
printf("p:0x%x\n",p);
printf("q:0x%x\n",q);
printf("r:0x%x\n",r);
strcpy(p,"aaaaaaaabbbbbbbb");
strcpy(q,"ccccccccdddddddd");
strcpy(r,"eeeeeeeeffffffff");
sleep(0); //只为了打断点使用,无其他作用
free(p);
sleep(0);
free(q);
sleep(0);
malloc(0x90);
sleep(0);
return 0;
}
第二步: c一下,来到第二个sleep,此时p已经被释放。
第三步: c一下,来到第三个sleep,此时q也被释放。
第四步: c一下,来到第四个sleep,此时申请了一块0x90的chunk,在glibc中其实是申请了0xa0的chunk。因为p是先被放入unsortedbin的,所以malloc(0x90)时切割的就是p所指的chunk,可以看到原先的0x130的堆块前0xa0给malloc使用了,剩下的0x30就是我们的last remainder 并且通过bins可以看到,此次切割还触发了一次unsortedbin的consolidate,使q所指的chunk被整理到了smallbin中,但是切割之后的last remainder还留在unsortedbin中
第五步: 查看一下表示arnea的struct malloc_state结构体的last_remainder指针此时指向了我们的last remainder。
五、演示案例
#include <unistd.h>
#include <malloc.h>
int main()
{
int size=0x30;
int size2=0x200;
int *p1=malloc(size);
int *p2=malloc(size2);
int *temp=malloc(size); //防止p2和p3都被释放之后,p2和p3发生合并
int *p3=malloc(size2);
int *temp2=malloc(size); //防止p3被释放之后与topchunk合并
sleep(0); //只为打断点使用
free(p1);
sleep(0);
free(p2);
free(p3);
sleep(0);
malloc(0x100);
sleep(0);
return 0;
}
第二步: c一下释放p1,可以看到其被加到fastbin中。
第三步: c一下,释放p2和p3,可以看到p2和p3都被加到unsortedbin中。
第四步: c一下,又申请了一个0x100的堆块,此时会去切割unsortedbin中的freechunk,并且触发consolidate,但是这个consolidate没有去整理fastbins,fastbins中的chunk没有变动。
六、演示案例
#include <unistd.h>
#include <malloc.h>
int main()
{
int size=0x300;
int *p1=malloc(size);
int *p2=malloc(size); //防止p1被释放之后与topchunk合并
sleep(0); //为了程序打断点使用,无其他作用
free(p1);
sleep(0);
malloc(0x700);
sleep(0);
malloc(0x200);
return 0;
}
第一步: 打断点到sleep函数,运行程序,查看一下当前的堆块。
第二步: n几下,释放p1,可以看到p1被放入unsortedbin中。
第三步: n几下,malloc(0x700)的堆块,此时unsortedbin中的chunk(p1)被整理到smallbins中。
第四步: n几下,然后申请一个0x200的堆块,申请的时候malloc发现smallbins中有满足的chunk可以切割,于是就去切割smallbins中的堆块,并产生last remainder,将last remainder放入unsortedbin中
七、演示案例
#include <unistd.h>
#include <malloc.h>
int main()
{
int size=0x300;
int *temp=malloc(0x30);
int *p1=malloc(size);
//防止p1释放并consolidate到smallbins中之后,再释放p2导致p1和p2合并,合并之后又被放入unsortedbin中
int *temp2=malloc(0x30);
int *p2=malloc(0x100);
int *p3=malloc(size);//防止p2被释放之后与topchunk合并
sleep(0); //为了程序打断点使用,无其他作用
free(p1);
sleep(0);
malloc(0x700);
sleep(0);
free(temp);
free(p2);
sleep(0);
malloc(0x200);
return 0;
}
第一步: 打断点到sleep,运行程序,并查看当前堆块(由于屏幕原因,topchunk没有截图下来)。
第三步: c一下,malloc(0x700),将unsortedbin中的p1整理到smallbin中。
第四步: c一下,释放temp和p2,此时temp被放入fastbins中,p2被放入unsortedbin中(注意:如果没有temp2这个堆块,此处释放p2之后,由于p1和p2是物理相邻的chunk,那么p1和p2就会合并然后一起放入到unsortedbin中了)。
第五步: n几下,malloc(0x200),此时malloc会切割smallbins中的p1,并产生last remainder(0x602250),并把last remainder放入到unsortedbin中。由于切割chunk并产生last remainder会发生consolidate,所以unsortedbin中的p2从unsortedbin中整理到smallbins中(由于此种情况的consolidate不会整理fastbins中的chunk,所以fastbins中的chunk仍然在fastbins中)。
我是小董,V公众点击"笔记白嫖"解锁更多【堆漏洞挖掘】资料内容。