参考文献:https://github.com/DhavalKapil/heap-exploitation

void * _int_malloc (mstate av, size_t bytes)

  1. 修改bytes,保证对齐之类的

  2. 若av是NULL(缺少可用的arena),调用sysmalloc,使用mmap来分配一个chunk。若成功,调用alloc_perturb(memset,设为perturb_byte&0xff),最后返回指针。

  3. .

    1. 如果大小bytes在fastbin范围内:
      1. 根据大小算下标,然后得到fastbin数组里大小合适的bin
      2. 把bin里的第一个chunk删掉,把victim指针指向它(全在REMOVE_FB里面,REMOVE_FB参数名字跟调用时的变量名顺序不一样!!)
      3. 如果victim是NULL,转到smallbin
      4. 检查victim的大小是不是真的属于那个fastbin,否则malloc(): memory corruption (fast)
      5. 调用alloc_perturb 并返回指针 [[内部函数#static-void-alloc_perturb-char-p-size_t-n|static void alloc_perturb (char *p, size_t n)]]
    2. 如果在smallbin范围内
      1. 根据大小算下标,然后得到smallbin数组里大小合适的bin。
      2. 如果此bin中没有chunk,就用下一个。这是通过比较指针 binbin->bk 来检查的。
      3. victim = bin->bk (bin中的最后一个chunk)。(仅限旧版)如果为 NULL(发生在 初始化期间),则调用 malloc_consolidate 并跳过检查不同的bin的完整步骤。
      4. 否则,检查 victim->bk->fdvictim 是否相等。如果它们不相等,则会抛出错误malloc(): smallbin double linked list corrupted
      5. 为物理相邻的下一个chunk设置 PREV_INSUSE bit。
      6. 从bin list中删除此chunk。
      7. 根据 av 设置此chunk的适当arena bit。
      8. 调用 alloc_perturb ,然后返回指针。
    3. 如果大小不在smallbin范围内:
      1. 根据请求大小,在largebin数组中获取适当的bin,赋值给idx。
      2. 检查avhave_fastchunks。如果是1,在av上调用malloc_consolidate。 have_fastchunks
  4. 如果尚未返回指针,则表示出现以下一种或多种情况:

    1. 大小属于“fastbin”范围,但没有可用的fastchunk。
    2. 大小属于“smallbin”范围,但没有可用的smallchunk(在初始化期间调用malloc_contegrate)。
    3. 大小属于“largbin”范围。
  5. 然后,检查unsorted chunks,并将已遍历的数据块放入bin。这是唯一一个将chunk放入bin的地方。从尾部开始遍历unsorted bin。

    1. victim 指向当前正在处理的chunk。
    2. 检查 victim 的块大小是否在最小(2*SIZE_SZ)和最大(avsystem_mem)范围内。否则抛出错误malloc(): memory corruption。 corruption: 腐败;贪污;贿赂;腐蚀;受贿;(单词或短语的)变体;使人堕落的行为
    3. 如果(请求的数据块大小在 smallbin 范围内)且(victim是Last remainder chunk)且(它是unsorted bin 中唯一的chunk)且(chunk size >= 请求的cshunk size): 将chunk分成两个:
      • 第一个chunk与请求的大小一致,并返回。
      • 剩余的chunk成为last remainder chunk。它将被插回unsorted bin。
        1. 为两个数据块设置适当的 “chunk_size “和 “chunk_prev_size “字段。
        2. 调用alloc_perturb后返回第一个数据块。
    4. 不满足上面的条件时,从unsorted bin中移除 victim。如果victim的大小等于请求的大小,则调用alloc_perturb后返回victom。
    5. 如果victim的大小在smallbin范围内,则在合适的smallbin头部添加该chunk。
    6. 否则,在保持排序顺序的情况下,插入到相应的 largebin 中:
      • 首先检查最后一个chunk(最小的)。如果 victim 比最后一个chunk小,就把它插入最后一个。
      • 否则,循环查找大小 >=victim 的chunk。如果大小完全相同,则始终插入第二个位置。
    7. 整个步骤最多重复 MAX_ITERS (10000) 次,或者直到unsorted bin中的所有chunk都用完为止。
  6. 检查unsorted bin后,如果检查请求的大小不在 smallbin 范围内,则检查 largebin。

    1. 根据请求的大小,获取 largebin 数组的索引,以访问相应的 bin。(用的是上面的idx)
    2. 如果最大chunk(bin 中的第一个数据块)的大小大于请求的大小,则
      1. 从尾部开始迭代,找到最小的,大小>=请求大小的数据块,赋值给victim。
      2. 调用 unlink从bin中删除 victim chunk。
      3. 计算 victim chunk的 remainder_size(即victim’s chunk size - requested size)。
      4. 如果remainder_size>=MINSIZE(最小chunk大小,包括头部),则将chunk分割成两个。否则,将返回整个 “victim” chunk。将剩余的chunk插入unsorted bin(尾部)。在unsorted bin中检查 unsorted_chunks(av)->fd->bk == unsorted_chunks(av)。否则就会产生错误(malloc(): corrupted unsorted chunks)。
      5. 调用 alloc_perturb 后返回 victim 块。
  7. 目前为止,我们已经检查了unsorted 以及 fast, small or large bin. 注意一个单独的bin (fast or small) 是通过 精确地 请求chunk的大小来检查的. 接下来,重复一下步骤直到所有bin 都被用完:

    1. idx++.(很久很久以前,它被设置为了请求大小对应的bin, small或者large)(到现在没有返回只能说明这个bin是空的(?))
    2. 使用av->binmap map 来跳过空bin.
    3. victim 指向当前bin的尾部.
    4. 上两步使用binmap来保证:跳过的bin绝对是空的。但是它不能保证所有的空bin都被跳过了。 所以要再检查一遍。跳过空的。(continue),直到一个非空bin。
    5. 切开牺牲品(victim) (victim非空bin的最后一块)(切成两块)(除非剩得太少). 剩余chunk插进unsorted bin (尾部). 插入前有一个检查 unsorted_chunks(av)->fd->bk == unsorted_chunks(av). 失败时抛出”malloc(): corrupted unsorted chunks 2”.
    6. 调用alloc_perturb并返回victim.
  8. 如果到这儿还没找到bin,就用top chunk //Top chunk

    1. victim指向 av->top.
    2. 如果 ‘top’ chunk 大小 >= ‘请求大小’ + MINSIZE, 分裂,剩余部分变成新的top chunk,调用alloc_perturb并返回前面的chunk.
    3. 检查avhave_fastchunks。如果是1,在av上调用malloc_consolidate。返回第5步。
    4. 调用 sysmallocalloc_perturb, 返回.

__libc_malloc (size_t bytes)

  1. 调用 arena_get 得到一个mstate 指针.
  2. 用上面的指针和bytes调用 _int_malloc .
  3. 解锁arena.
  4. 返回之前有个断言,要求满足下面条件中的至少一个:
    • Returned pointer is NULL
    • Chunk is MMAPPED
    • Arena for chunk is the same as the one found in 1.

_int_free (mstate av, mchunkptr p, int have_lock)

  1. p + chunksize(p) 是否超出了内存的最大地址 .超过则抛出 “free(): invalid pointer”

  2. 检查chunk的大小是否至少为MINSIZE、是否是MALLOC_ALIGNMENT的倍数。有一样不符合则会引发错误(“free(): invalid size”)。

  3. 如果chunk的大小在fastbin范围:

    1. 检查下一个chunk的大小是否在最小值和最大值(av->system_mem)之间,否则引发错误(“free(): invalid next size (fast)”)。
    2. 在chunk上调用free_perturb函数。
    3. av设置FASTCHUNKS_BIT为true。
    4. 根据chunk大小获取fastbin数组的索引。
    5. 检查快速bin中的顶部是不是要添加的chunk,不是则引发错误(“double free or corruption (fasttop)”)。
    6. 检查快速bin顶部的chunk大小是否与要添加的chunk大小相同,不相同则引发错误(“invalid fastbin entry (free)”)。
    7. 将chunk插入fastbin的顶部并返回。
  4. 如果chunk不是mmapped:

    1. 检查chunk是否为top chunk。如果是top chunk,引发错误(“double free or corruption (top)”)。
    2. 检查下一个chunk(按内存顺序)是否在arena的边界内。如果不在边界内,引发错误(“double free or corruption (out)”)。
    3. 检查下一个chunk(按内存顺序)的前置使用位是否被标记为未使用。如果未使用,引发错误(“double free or corruption (!prev)”)。
    4. 检查下一个chunk的大小是否在最小值和最大值(av->system_mem)之间。如果不在范围内,引发错误(“free(): invalid next size (normal)”)。
    5. 在chunk上调用free_perturb函数。
    6. 如果前一个chunk(按内存顺序)未被使用,调用unlink函数解除前一个chunk的链接。并且把当前指针指向它,更新size变量。
    7. 如果下一个chunk(按内存顺序)不是top chunk:
      1. 如果下一个chunk未被使用,调用unlink函数解除下一个chunk的链接。
      2. 合并chunk与前一个和下一个(按内存顺序)chunk,如果有任何一个是自由的,并将其添加到未排序bin的头部。在插入之前,检查unsorted_chunks(av)->fd->bk == unsorted_chunks(av)是否成立,如果不成立,引发错误(“free(): corrupted unsorted chunks”)。
    8. 如果下一个chunk(按内存顺序)是top chunk,则将这些chunk适当合并成一个单独的top chunk。
  5. 如果chunk是映射的(mmapped),则调用munmap_chunk函数。

__libc_free (void *mem)

  1. Return if mem is NULL.
  2. If the corresponding chunk is mmapped, call munmap_chunk if the dynamic brk/mmap threshold needs adjusting.
  3. Get arena pointer for that corresponding chunk.
  4. Call _int_free.