此处是前置知识的复习,通过how2heap这个项目我们将重新学习一下有关堆分配机制地实现和它对应的攻击手段。
项目地址:shellphish/how2heap: A repository for learning various heap exploitation techniques.
前置复习:
1、堆的数据结构:
(摘自ctfwiki,详细文章参见这个)
2、chunk和bins
温故知新:
(1)、首先我们需要大概分清楚两种状态:已经被分配了、空闲着(只有针对free状态下,fd和bk指针才会有效)
(2)、简单说说已经分配的堆块的事情:这个时候的chunk是不参与链表构成的,物理布局即标识自己的布局,通过size字段的低位prev_inuse标识前一个块是否在使用
(3)、无论是bins也好,我们所说的双向链表,所以落到最后的对象其实是我们这些空闲的chunk
这里用chat给的一个例子来更好的理解一下:
简单的说就是,有这样一个机制,来回收我们的碎片,从宏观上表示组成一个更大的碎片。
由此,我们引出了我们的bins。这里需要明确两点:
(1)、空闲的chunks构成了一个大大的双向链表,通过这样的形式,最后从宏观上实现了组成一个巨大的内存块的作用
(2)、bins是chunk的链表集合,由空闲块的大小的不同又由此产生了不同的bins
(3)、bins指向链表头
到这里为止,我们从一个比较静态的角度简单讲述了一下我们这个板块的主角,但是,这里我们还没有涉及到内存空间的重新分配和释放。很显然,堆机制是为了缓解频繁的内存空间使用而产生的。
接下来这里我将简单地讲述一下在这个过程中因为重新申请和释放对我们地bins、空闲chunk的使用。
这个也不要想的太复杂,简单说就是一句话,我们直接和chunk交换的过程交由我们的中间人bins实现,当我申请堆块的时候,bins会判断合适的大小的空闲chunks给我们使用,同理,当我有了空闲的chunk之后,也会有bins决定放到自己的对应的bins里面。
接下来,我们还需要考虑一个问题,在程序最最开始的情况下,我们的bins和我们的chunk又是一种什么样的状态呢?
这里首先要简单提及两个产生我们的堆块的函数:malloc和free,先记住他,后面我们再来说这两个函数。
最初始状态的chunk
在程序刚启动的时候,堆是空的,没有分配任何块。第一个分配的块会初始化堆的起始地址,并分配一个初始块(本质上是malloc调用触发sbrk实现堆空间的增长)
最初始状态的bins
程序最初的状态下,没有bins!!!
回想我们之前有关bins的解释,在于这个是针对free的块的管理,所以这里默认状态下肯定是什么都没有的啦。
产生bins的多种原因:
1、释放内存块(调用 free())。
2、合并空闲块(释放相邻块时发生)。
3、分裂未使用的内存块(分配时剩余内存被分裂)。
4、手动整理内存(调用如 malloc_trim 的内存管理函数)
3、bins类别介绍
- Fast bins: 快速存储小块,无需合并。
- Small bins: 临时进入
unsorted bin
,稍后转移到合适的small bin
。 - Large bins: 临时进入
unsorted bin
,然后按大小排序插入到large bin
。 - Unsorted bin: 所有释放块的中转站,优先满足后续分配请求。
这里本质上要对应free的块大小来的,这个在后面我们会重点说一下,以及针对他们的攻击手段,这里就先简单地放个介绍。
fastbin:
单链表,头插法实现新的空余chunk的插入,释放时不会合并
smallbin:
双向循环列表,首先会放入unsortedbin中,后续存在其他整理操作的时候会实现放入,相邻会合并放进新的bin里面
largebin同理
unsortedbin:
- 过渡状态:
unsorted bin
是空闲块的临时存储区域,块释放后首先进入这里。 - 分配优先: 后续的内存分配优先使用
unsorted bin
中的块。 - 再分配: 如果未被分配,块最终会被转移到合适的
small bin
或large bin
。
参考文献: