pwn glibc堆利用——chunk结构

glibc动态内存分配 写C语言时,如果想申请或释放内存,就需要 malloc 和 free 函数。从linux内核层面来看,往往会用到这两个系统调用: mmap brk 其中, brk 会将进程的heap段向内存高位扩充,可以理解为把堆顶抬高了。而 mmap 则在heap段和stack段之间申请匿...
pwn glibc堆利用——chunk结构
pwn glibc堆利用——chunk结构

glibc动态内存分配

写C语言时,如果想申请或释放内存,就需要mallocfree函数。从linux内核层面来看,往往会用到这两个系统调用:

  • mmap
  • brk

其中,brk会将进程的heap段向内存高位扩充,可以理解为把堆顶抬高了。而mmap则在heap段和stack段之间申请匿名内存(不用担心这片区域被填满,64位系统下,你的电脑内存加起来都没它大)。

结合linux进程的简化内存分布,更加直观一些:


低地址
[text]
[data]
[bss]
[heap]

[mmap region]

[stack]
高地址


释放内存的系统调用也有两个:

  • munmap
  • brk

(没错,brk也可以收缩堆空间)

这两种分配方式的区别在于,mmap一般用来申请大块内存(默认值大约是128KB以上)

但是呢,问题出现了。每次进行系统调用时,都要从ring3态切换到ring0态,中间还要进行一堆安全检查,完事了还得再换回来,极其消耗系统资源。为此,glibc开局就申请了一大块内存,根据进程的需要来细细分割,管理这些内存的管理器就是ptmalloc2。接下来我们详细说说它。

结构初识

为了弄清楚这玩意,我查了不少资料。刚开始读源码不容易看懂;ctf wiki的内容全面,可是一上来就是一堆细节,看了头晕;相应的视频和书籍虽然权威,但基本都停留在2.29之前的版本。所以嘛…我想写一点新的东西,就比如这篇教程。

下面按照由浅入深的顺序,介绍一些概念:

  • malloc : 申请内存
  • free : 释放内存
  • chunk : 申请的内存块,每次malloc都会申请到一个chunk
  • bin : 垃圾桶,chunkfree释放后会进入bin
  • arena : 一个arena相当于一个堆,有自己的一套bins
  • main_arena : 主线程使用的arena,目前我们只研究单线程程序,也就是只有main_arena一个arena

malloc

分两种情况
申请大块内存:使用mmap系统调用
释放普通内存:正常流程(从bin获取,没有的话再进行分配)

free

也分两种情况
释放mmap申请的内存:使用munmap系统调用
申请小块内存:正常流程(进bin

chunk

每一个chunk都是有自己的结构滴,这个结构的全称叫做malloc_chunk,好在不复杂。

// malloc/malloc.c:1153
struct malloc_chunk {
  INTERNAL_SIZE_T      mchunk_prev_size;  /* 前一个 chunk 大小 */
  INTERNAL_SIZE_T      mchunk_size;       /* 当前 chunk 大小 */
  struct malloc_chunk* fd;                /* 前向指针 */
  struct malloc_chunk* bk;                /* 后向指针 */
  struct malloc_chunk* fd_nextsize;       /* large bin 专用 */
  struct malloc_chunk* bk_nextsize;       /* large bin 专用 */
};

讲解一下字段的含义
INTERNAL_SIZE_T就是size_t,前两个变量存放chunk大小,它们被称为chunk头部。因为malloc申请完内存之后,用户可以使用的部分是从第三个字段开始的,也就是说,后面的四个指针变量,正常情况下是没用的,会被用户写入的数据覆盖。

用户申请内存时,必须是16字节的整数倍(32位是8字节),就算你申请了1字节,ptmalloc2也会给你16字节。而chunk头部就占据了16字节(以后默认64位系统),所以一个chunk最小也有32字节。

这时,细心的你会发现,既然每个chunk都是16的倍数,那么mchunk_size(简称size)字段的低4位就处于未使用的状态,那就拿来当标志位吧:

位 宏 含义 bit 0 PREV_INUSE 前一个 chunk 是否被分配 bit 1 IS_MMAPPED 是否通过 mmap 分配 bit 2 NON_MAIN_ARENA 是否不属于 main arena

只用3个比特位,是为了兼容32位的版本。

chunk进入bin后,fd指针就指向前一个进入该binchunkbk指针指向后一个进入该binchunk。这样的话,在bin中形成链表结构,方便存取。至于fd_nextsizebk_nextsize,现在并不重要,待以后慢慢细讲。

先说这么多吧,多了不好消化。

知识库

  • how2heap :一个学习堆利用技术的绝佳资源库,包含了针对不同glibc版本的各种利用方法(如fastbin_dup, unsafe unlink, house of系列等)的教学代码。
  • CTF Wiki :由社区持续维护的CTF知识库,PWN部分涵盖了从入门基础到高级利用的系统性教程。
  • ctf pwn之glibc堆利用 : 我的教程(混进了什么奇怪的东西)

本人第一次写教程,求佬友批评指正,我会不断改进的。 :dizzy:

1 个帖子 - 1 位参与者

阅读完整话题

来源: LinuxDo 最新话题查看原文