堆机制
当一个small chunk被free掉时,会有如下操作:
-
检查是否能向后合并,如果相邻低位的chunk也处于被free的状态,则向后合并。
- 合并两个chunk的内存
- 修改当前chunk指针为前一个chunk指针
- 触发unlink操作将前一个chunk从双向链表(bin)中移除
-
检查是否能向前合并,如果相邻高位的chunk处于被free的状态,则向前合并,同样能出发unlink 操作。
unlink的操作为:
static void unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");
fd->bk = bk;
bk->fd = fd;
if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if (p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr ("corrupted double-linked list (not small)");
if (fd->fd_nextsize == NULL)
{
if (p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}
其中重要的操作为:(主要就是将chunk从双向链表中移除)
mchunkptr fd = p->fd;
mchunkptr bk = p->bk;
fd->bk = bk;
bk->fd = fd;
还有需要注意的检查:
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");
if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");
Exploit
由于有了检查机制,所以unlink所能做的操作有所限制。具体操作为:
- 构造
p->fd=(p)-12
,p->bk=(p)-8
- 触发unlink(p)
- 于是unlink操作变为了
(p)=(p)-8
=>(p)=(p)-12
所以最终结果为(p)
指针-12,(p)
可以是任何存放p指针的地方。
虽然操作有限,但在一些场合下可以与堆溢出,user after free等配合,从而任意地址写。
网友评论