参考:
董哥的黑板报(堆漏洞挖掘):https://blog.csdn.net/qq_41453285/category_9150569.html
CTFwiki:https://wiki.x10sec.org/
以及互联网其它文章、博客等
概述:
堆(heap)是程序在运行过程中,所申请的动态分配和使用的内存区域,对比栈,虽然二者都在程序运行内存里,但是堆没有默认的生存周期。程序可以动态的申请和释放不同大小的堆内存区域,如果没有明确的释放堆内存操作,则堆内存区域一直有效。
堆内存的数据通过指向内存的指针进行调用,这里的调用主要是对结构体的成员变量和成员函数的调用。
堆管理器:
libc和glibc都是linux下的C函数库,glibc是GNU旗下的C标准库,现在linux发行版本都用的glibc,libc已经不再维护。
目前 Linux 标准发行版中主要使用的堆分配器是 glibc 中的:ptmalloc2。
其它内存管理机制:dlmalloc,tcmalloc,jemalloc...
ptmalloc2 主要是通过 malloc/free 函数来分配和释放内存块。
malloc(size_t n)
- 返回值:所申请堆块开始位置的指针。
- 当 n=0 时,返回当前系统允许的堆的最小内存块,32位是8字节,64位是16字节。
- 当 n 为负数时,由于在大多数系统上,size_t 是无符号数(这一点非常重要),所以程序就会申请很大的内存空间,但通常来说都会失败,因为系统没有那么多的内存可以分配。
- malloc用mmap来创建独立映射段,其背后用brk函数申请内存地址。
- 使用malloc申请的内存,释放后,若再次申请同样大小的堆时,还是从申请到第一次释放的那个堆。(UAF,double free)
free(void* p)
- free 函数会释放由 p 所指向的内存块。这个内存块有可能是通过 malloc 函数得到的,也有可能是通过其它函数得到的。
- 当 p 为空指针时,函数不执行任何操作。
- 当 p 已经被释放之后,再次释放会导致double free错误。
- 除了被禁用(mallopt)的情况下,当释放很大的内存空间时,程序会将这些内存空间还给系统,以便于减小程序所使用的内存空间。
- feer释放堆时会检查之前的 chunk 是否被释放,若已经释放,则和当前释放的 chunk 合并,之后再放入Unsorted Bin。
glibc 的堆管理实现:
arena 指的是堆内存区域本身,并不是结构;主线程的 main arena 通过 sbrk 创建;其他线程的 arena 通过 mmap 创建。
malloc_state 管理 arena 的核心结构,包含堆的状态信息、bins 链表等;main arena 对应的 malloc state 结构存储在 glibc 全局变量中;其他线程 arena 对应的 malloc_state 存储在 arena 本身中
bins 用来管理空闲内存块,通常用链表的结构来进行组织
chunks 内存块结构
在内存分配与使用中只有当真正去访问一个地址的时候,系统才会建立虚拟页面与物理内存的映射关系