unlink
unlink
前言
unlink是一个比较复杂的东西,可能很难说清
unlink的触发条件
- 当释放一个不是fastbin的块的时候,如果这个chunk的前面或者后面是free状态的,那就会触发unlink,将前后和这个块合并成一个更大的块
会有一些检查
- size是否是prev_size
p->fd->bk==p
p->bk->fd==p
所以我们需要:
- 修改当前要free堆块的prev_in_use为0
- 在上一个堆块上构造一个fake chunk
样例与解释
uint64_t *p0 = malloc(0x90);
uint64_t *p1 = malloc(0x90);
创建两个连续的堆。
p0[2] = &p0 - 3*0x8;
p0[3] = &p0 - 2*0x8;
这里是在p0所在的chunk里面再创了一个fake_chunk,这个chunk要满足fake_chunk->fd->bk=fake_chunk
即fake_chunk->fd + 3*0x8=fake_chunk => fake_chunk->fd=fake_chunk-3*0x8
,并且fake_chunk->bk->fd=fake_chunk
即fake_chunk->bk+2*0x8=fake_chunk => fake_chunk->bk=fake_chunk-2*0x8
,于是乎这样就在里面出现了一个更小的chunk,这个chunk被设置成了上面还连着一个chunk,但是实际上上面啥也没连,它只是两个指针满足了条件罢了。
p1[-2] = 0x80;
p1[-1] &= ~1;
这两步是为了修改prev_size和prev_in_use,修改大小为合适的大小,然后修改使用为0,这样就会发生前向合并即unlink。
free(p1);
当p1正在被释放的时候,glibc的代码会让它去检查prev_in_use,如果为0,那么就会把前面那一块也装过来,而前面一块的指针会被它断掉,具体怎么断的呢:
FD = fake_chunk->fd;
BK = fake_chunk->bk;
FD->bk = BK;
BK->fd = FD;
有点绕,可以看出,(fake_chunk->fd)->bk=(fake_chunk-3*0x8)->bk=fake_chunk = fake_chunk->bk=&fake_chunk-2
,而又有(fake_chunk->bk)->fd=(fake_chunk-2*0x8)->fd=fake_chunk = fake_chunk->fd=&fake_chunk-3
,于是原本指向fake_chunk的那个指针就变成了指向&fake_chunk-3了,这样原本在堆中的一个指针就从指向堆变成了指到存储堆指针的位置。
这时如果我们对fake_chunk进行修改,则变成了修改存储堆的指针。
然后再edit某个堆块,就变成了修改任意地址了!