1. 程式人生 > >堆之*bin理解

堆之*bin理解

ber 記錄 link padding 如果 http 出了 a star usr

在程序運行中,使用bins結構對釋放的堆塊進行管理,以減少向系統申請內存的開銷,提高效率。

chunk數據結構

從內存申請的所有堆塊,都使用相同的數據結構——malloc_chunk,但在inuse和free狀態,表現形式上略有差別。

chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             Size of previous chunk, if unallocated (P clear)  |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             Size of chunk, in bytes                     |A|M|P|
  mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             User data starts here...                          .
        .                                                               .
        .             (malloc_usable_size() bytes)                      .
next    .                                                               |
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             (size of chunk, but used for application data)    |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             Size of next chunk, in bytes                |A|0|1|
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

以上為malloc所得到的chunk的結構,前兩個size_t為chunk_header,分別保存前一個(物理相鄰)chunk的size(如果前一個chunk為空閑,則保存其size;若為使用狀態則歸前一個chunk作為usrdata區域使用) 和本chunk的size。因分配的空間會向2*size_t進行對齊,所以後3bit沒有意義,因而將其作為三個標記位

  • A : NON_MAIN_ARENA,記錄當前 chunk 是否不屬於主線程,1表示不屬於,0表示屬於
  • M : 記錄當前 chunk 是否是由 mmap 分配的
  • P : 記錄前一個 chunk 塊是否被分配。

chunk被free之後,其usrdata區域被復用,作為bin中的鏈表指針,其結構如下

chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             Size of previous chunk, if unallocated (P clear)  |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`head:‘ |             Size of chunk, in bytes                     |A|0|P|
  mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             fd                                                |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             bk                                                |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             (fd_nextsize)                                     |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             (bk_nextsize)                                     |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             Unused space (may be 0 bytes long)                .
        .                                                               .
 next   .                                                               |
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`foot:‘ |             Size of chunk, in bytes                           |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |             Size of next chunk, in bytes                |A|0|0|
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • fd和bk指針分別指向bin中在其之前和之後的chunkfd指向先進入bin者;bk指向後來者
  • fastbin中只有fd指針,使用單向鏈表進行維護。
  • fd_nextsize和bk_nextsize只存在與large bin中(chunk的size不大時不需要這兩個變量,也可能沒有他們的空間),指向前/後一個更大size的chunk。

fastbin

對於size較小(小於max_fast)的chunk,在釋放之後進行單獨處理,將其放入fastbin中。

max_fast:

在32位系統中,fastbin裏chunk的大小範圍從16到64;

在64位系統中,fastbin裏chunk的大小範圍從32到128。

fastbin是main_arena中的一個數組,每個元素作為特定size的空閑堆塊的鏈表頭,指向被釋放並加入fastbin的chunk。

fastbin鏈表采用單向鏈表進行連接

技術分享圖片

如圖所示,在free之後,會將被free掉指向的地址“掛”在fastbin相應大小的條目下,以便於下次分配時節省時間 (曾經為了節省free指針的時間而不free,原來浪費了這麽多時間,心疼我的無數個TLE)

在分配空間時,首先檢查fastbin數組對應大小的條目下是否有“空閑”的空間,有則直接取下進行分配,同時修改fd指針,維護單向鏈表。

  1. 在fastbin條目下,無論是free掉的空間地址加進來,還是將空閑的空間地址分配出去,都是在根部操作
    • 加入free的空間時,新加入的連在根部,(如新加入chunk3,插入鏈表根部,chunk3->fd指向原來最靠近bin的chunk1),類似蛋白質的翻譯過程
    • 分配空間時,若在對應大小的條目下有空閑的空間,則按蛋白質翻譯的逆順序進行操作(上圖中取出chunk3,將chunk3->fd = chunk1鏈在bin上)
  2. malloc(n)時,實際申請的空間sizeof(chunk) = (n + 4) align to 8 (x86)
    • 實際申請的空間從chunk開始,當堆中物理相鄰的前一個chunk為free時,Size of previous chunk標記前一個chunk的大小,否則可以存儲前一個chunk的數據。之後是本chunk的大小,由於分配的必定是24bytes(64位為28bytes)的整數倍,最後三位沒有影響用作三個標記位。
    • malloc函數範圍的指針是從mem開始的用戶可用空間。

unsorted bin

unsorted bin 可以作為chunk 被釋放和分配的緩沖區。在malloc&free剖析中解釋了malloc和free活動中對unsorted bin的使用。這裏從更微觀的角度解釋unsorted bin如何工作。

main_arena

main_arena,主分配區,是一個靜態全局變量,其中存儲著進行堆塊管理的各種變量和指針。

技術分享圖片

fastbin各項指針、topchunk、和bins指針都存在於這個變量當中。

unsorted bin指針就是bins指針的前兩項,ptmalloc共維護128個bin,都存放於bins數組中。

  • 前兩項為unsorted bin的指針
  • bins[2] - bins[65]的64個元素為small bin指針
  • bins[66] - bins[127]為large bin

unsorted bin

下面通過這段代碼分析在釋放和分配chunk時unsorted bin中各指針的工作細節:

# include <stdio.h>
# include <stdlib.h>
int main()
{   
    void *a, *b, *c, *d, *e;
    a = malloc(128);
    b = malloc(128);
    c = malloc(128);
    d = malloc(128);
    e = malloc(128);
    printf("a >> %p\nb >> %p\nc >> %p\nd >> %p\ne >> %p\n",a,b,c,d,e);
    puts("free d and b, remember the bins");
    free(d);
    free(b);
    //puts("free c,look at the unsorted bin");
    //free(c);
    puts("malloc(128) again, what will happen?");
    void * newd = malloc(128);
    printf("new d -> %p\n", newd);
    return 0;
} 
//make file(x64):
//gcc -o unsortedbin ./test_unosrted -no-pie

運行後得到分配的五個chunk的地址,由於直接輸出了返回給用戶的指針,所以指向的都是usrdata,指向實際chunk頭的地址應該減去0x10。

a >> 0x602010
b >> 0x6020a0
c >> 0x602130
d >> 0x6021c0
e >> 0x602250

  1. 沒有發生free之前

    技術分享圖片

    bins數組的前兩個可以看做unsorted bin的fd和bk指針,在unsorted bin為空的時候都指向top (main_arena+88)

    CTFwiki對這個過程具體流程和背後原理的示意圖不太準確:unsorted bin鏈表頭並不是malloc_chunk結構體,而是main_arena變量中bins列表的前兩項分別做fd和bk指針,指向的位置也不是pre_size,而是main_arena中的top,top指向top chunk。我的理解是這樣的,如有錯誤,還請指出。

  2. free(d)

    pwndbg> unsortedbin 
    unsortedbin
    all: 0x6021b0 —? 0x7ffff7dd3b58 (main_arena+88) —? 0x6021b0 ?— 0x7ffff7dd3b58
    pwndbg> p main_arena 
    $2 = {
      mutex = 0, 
      flags = 1, 
      fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
      top = 0x6026e0, 
      last_remainder = 0x0, 
      bins = {0x6021b0, 0x6021b0, 0x7ffff7dd3b68 <main_arena+104>, 0x7ffff7dd3b68 <main_arena+104>...
    pwndbg> telescope 0x6021b0
    00:0000│   0x6021b0 ?— 0x0
    01:0008│   0x6021b8 ?— 0x91
    02:0010│   0x6021c0 —? 0x7ffff7dd3b58 (main_arena+88) —? 0x6026e0 ?— 0x0
    ... ↓
    

    技術分享圖片

    此時unsorted bin的兩個指針均指向被釋放的d,d的fd、bk指針指向top

  3. free(b)

    pwndbg> p main_arena 
    $3 = {
      mutex = 0, 
      flags = 1, 
      fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
      top = 0x6026e0, 
      last_remainder = 0x0, 
      bins = {0x602090, 0x6021b0, 0x7ffff7dd3b68 <main_arena+104>,...
    pwndbg> unsortedbin 
    unsortedbin
    all: 0x602090 —? 0x6021b0 —? 0x7ffff7dd3b58 (main_arena+88) —? 0x602090 ?— 0x6021b0
    pwndbg> telescope 0x602090 
    00:0000│   0x602090 ?— 0x0
    01:0008│   0x602098 ?— 0x91
    02:0010│   0x6020a0 —? 0x6021b0 ?— 0x0
    03:0018│   0x6020a8 —? 0x7ffff7dd3b58 (main_arena+88) —? 0x6026e0 ?— 0x0
    04:0020│   0x6020b0 ?— 0x0
    ... ↓
    pwndbg> telescope 0x6021b0
    00:0000│   0x6021b0 ?— 0x0
    01:0008│   0x6021b8 ?— 0x91
    02:0010│   0x6021c0 —? 0x7ffff7dd3b58 (main_arena+88) —? 0x6026e0 ?— 0x0
    03:0018│   0x6021c8 —? 0x602090 ?— 0x0
    04:0020│   0x6021d0 ?— 0x0
    

    技術分享圖片

    新釋放的b會連載unsortedbin的根部,各指針的關系如圖。

  4. malloc(128)

    pwndbg> p main_arena 
    $4 = {
      mutex = 0, 
      flags = 1, 
      fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
      top = 0x6026e0, 
      last_remainder = 0x0, 
      bins = {0x602090, 0x602090, 0x7ffff7dd3b68 <main_arena+104>, ...
    pwndbg> unsortedbin 
    unsortedbin
    all: 0x602090 —? 0x7ffff7dd3b58 (main_arena+88) —? 0x602090 ?— 0x7ffff7dd3b58
    pwndbg> telescope 0x602090
    00:0000│   0x602090 ?— 0x0
    01:0008│   0x602098 ?— 0x91
    02:0010│   0x6020a0 —? 0x7ffff7dd3b58 (main_arena+88) —? 0x6026e0 ?— 0x0
    ... ↓
    04:0020│   0x6020b0 ?— 0x0
    

    此時unsorted bin又只有一個chunk,指針關系與剛剛free(d)時相同,但在bin中的是b,先進入的d被再次分配,由此得到,unsorted bin中遵循FIFO原則,先進入的chunk在size合適的情況下會被優先分配。

    在unsorted bin中進行分配的時候,size不合適的chunk會被放入small bin或large bin,這個unlink的過程沒有對chunk進行檢查,所以被篡改過的chunk也能通過unlink,破壞掉鏈表中的fd、bk指針,即unsorted bin attack。

small bins & large bin

chunk進入small bin和large bin的唯一機會是在分配chunk時,在unsorted bin中進行遍歷,size不合適的chunk會被unlink過來。

small bin和large bin都是采用雙向鏈表進行維護,遵循FIFO原則。

其中large bin中的chunk有fd_nextsize和bk_nextsize,分別指向之前/之後更大的chunk,加快尋找速度。

在分配chunk的時候,如果前面的步驟都沒有找到合適的chunk,則在small bin和large bin中找到最小的large enough的chunk,進行分割,unlink,分配完成。


Ref:

安全技術精粹

CTF wiki


作者:辣雞小譜尼
出處:http://www.cnblogs.com/ZHijack/
如有轉載,榮幸之至!請隨手標明出處;

堆之*bin理解