-
-
[原创]在GDK8下观察glibc堆
-
发表于: 2021-11-16 16:24 10613
-
GDK8产品主页:Nano Code
在程序运行过程中,堆可以提供动态分配的内存,允许程序申请大小未知的内存。堆其实就是程序虚拟地址空间的一块连续的线性区域,它由低地址向高地址方向增长。我们一般称管理堆的那部分程序为堆管理器。
堆在系统中 的布局可以分成两大类,分别是main_arena主场地和non_main_aren辅场地。
在main_arena中,当程序第一次动态分配内存的时候,堆会被分成两块,一块是分配区,另一块是Top Chunk,Top Chunk的起始地址亦是top指针所指向的位置;在之后动态分配内存的过程中,只要Top Chunk的位置还够用,分配区就会从Top Chunk中划分空间供自己使用,但是假如Top Chunk中的空间不够用了,那么就会通过brk机制扩展空间。
在main_arena中的top指针记录了分配区的结束位置的地址,top指针的上方为top chunk;而分配区的开始位置的地址,如下图所示。
non_main_aren辅场地布局如下图所示:
non_main_arena中如果分配内存数量不够大的话,那么分配区的开始位置的地址和结束位置的地址同main_arena中的情况是一样的;但是如果分配内存数量足够的大,那么在non_main_arena中的分配区会划分成1个又1个的子堆;不管non_main_arena是否划分了子堆,在主子堆中,分配区的开始位置永远是[bins指针+mchunk_size(去标志位;bins指针对应malloc_chunk中的mchunk_size)],而其他子堆分配区的起始位置的地址为[heap info的地址+0x20]。
假如想要获取全部的子堆,就要遍历一下链表,不过这个链表并不是循环的,而且需要依靠perv指针找上一个),直到找到第1个为止。
注意:主子堆的heap info内的prev指针永远是空(主子堆一般排在链表的第一个),此链表并不是循环的
chunk的布局如下图所示:
无论是空闲chunk还是被分配的chunk,它们都是由malloc_chunk结构体进行描述,关于空闲的chunk并不会介绍太多,主要介绍一下被分配的chunk;在被分配的chunk中,假如上一个chunk尚未被释放,那么一般情况下prev size中不会记录任何东西(当然也有记录的时候,见下方),但是如果上一个chunk已经被释放了,那么perv size的大小就是上一个块的大小;而size中则会记录当前块的大小及当前块的属性,标志位的具体介绍见下方。
在malloc_chunk中的fd及bk字段,假如当前块处于被分配的状态,那么从fd开始就是数据;但是假如当前块是空闲的,那么fd及bk会分别指向下/上1个的空闲块。
fd_nextsize, bk_nextsize这两个指针指在块空闲的时候使用,指向下/上第1个大小与当前块不同的空闲块
!heap [-arena地址] [-显示选项]
显示选项说明见下面。
1. 首先先要和GDK8建立连接;关于建立连接的具体步骤可以参考下面的链接。
GDK8上手指南 - Powered by MinDoc
2. 输入.sympath+ c:\temp载入libc符号文件;如下图所示。
注意:如果还没有下载libc符号,可以通过`sudo apt install libc6-dbg`命令进行下载;完成下载以后,可以通过`sudo find /usr/lib/debug -name “libc-x.xx.so”`命令查找libc符号所在位置 [ 一般在/usr/lib/debug/lib目录下 ];找到libc符号后,将其复制到主机的c:\temp目录下即可。
3. 设置断点,输入g后,等待断点命中;如下图所示。
4. 输入`x libc_so!main_arena`手动载入libc符号;如下图所示。