合并优先级

  • 先合并物理低地址空闲块
  • 后合并物理高地址空闲快

合并过程

Demo 程序

基于 glibc 2.23

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

char *ptr[16];

int main(){
char *protect;

ptr[0]=malloc(0xf8);
ptr[1]=malloc(0xf8);
ptr[2]=malloc(0xf8);
protect=malloc(0xf8);

free(ptr[0]);
free(ptr[2]);
free(ptr[1]);

return 0;
}

编译指令:

1
gcc main.c -o main

Demo 说明

程序中 chunk0 chunk2 释放顺序可以互换不影响,当释放 chunk1 会同时发生向前和向后 unlink :

  • 向前 unlink :与物理地址高 chunk2 合并
  • 向后 unlink :与物理地址低 chunk0 合并

free chunk1 过程

进入 free 函数后,首先检查 hook 是否为空,不为空则跳转 hook 指向函数

image-20210622180607146

经过一轮检查分配方式之后,从 __int_free 进入熟知的释放处理过程中

image-20210622180912747

向后合并-合并低地址

首先进行向后合并-合并低地址空闲堆块,通过被释放堆块的 prev_inuse 标志位判断。合并之后堆块 size += prevsize ,然后将后面的(低地址)堆块 unlink 取出

unlink 之前 unsortedbin 状态:

image-20210622210609805

unlink 之后 unsortedbin 状态:

image-20210622210649543

注意这时还没有将取出堆块与被释放堆块进行合并,chunk0 size 还是 0x101、fd&bk 指针还是原来在 unsortedbin 的值:

image-20210622211054901

向前合并-合并高地址(下一块不是topchunk)

判断前一块堆块是不是 topchunk :

image-20210622211502597

nextinuse = inuse_bit_at_offset(nextchunk, nextsize); 通过查询前两块的 prev_inuse 标志位,判断前一块堆块是不是空闲:

image-20210622211559019

如果前一块空闲且不是 topchunk 则 unlink 出来

unlink 之前 unsortedbin 状态:

image-20210622211824509

unlink 之后 unsortedbin 状态:

image-20210622211901641

和向后合并一样现在只是将堆块 unlink 出来,并没有进行合并。

合并堆块&放入对应bin

通过 size 判断应该应该放入哪个 bin 中,再判断应该用哪个 size 的链表,最后在这里完成写入:

image-20210622212426720

image-20210622212446420

相关资料