基于GLIBC 2.25的嵌入式设备堆内存匿名区块分析求助
针对GLIBC 2.25堆内存的离线解析方法
GLIBC 2.25的堆内存以chunk为单位管理,new底层调用malloc分配的缓冲区本质是chunk的用户数据区。要判断缓冲区的分配状态和大小,核心是解析chunk的头部结构,步骤如下:
1. 定位目标缓冲区对应的chunk头部
用户拿到的缓冲区地址是chunk的数据区起始地址,需要往前偏移找到chunk的头部:
- 64位系统:chunk头部由两个8字节字段(
prev_size和size)组成,头部起始地址 = 缓冲区地址 - 16字节 - 32位系统:chunk头部由两个4字节字段组成,头部起始地址 = 缓冲区地址 - 8字节
如果是从/proc/PID/maps转储的匿名区块文件,先计算缓冲区在dump文件中的偏移:
假设maps中区块起始地址为
start_addr,缓冲区地址为buf_addr,则dump文件内的偏移为:offset = buf_addr - start_addr
对应chunk头部在dump文件中的偏移为:header_offset = offset - 16(64位)或header_offset = offset -8(32位)
2. 解析chunk的大小
用十六进制工具读取chunk头部的size字段(第二个字段),该字段低3位是标志位,需剥离后得到实际chunk总大小:
- 标志位定义:
- 第0位(P):前一个chunk的分配状态(1=已分配,0=空闲)
- 第1位(M):当前chunk是否为mmap分配(1=是,0=堆内chunk)
- 第2位(A):是否属于主分配区(1=是,0=非主分配区)
- 实际chunk总大小 =
size字段值 & ~0x7(忽略低3位) - 用户缓冲区大小 = 实际chunk总大小 - 头部长度(64位减16,32位减8)
示例(64位小端序):
从dump文件读取头部的16字节为:00 00 00 00 00 00 00 00 21 00 00 00 00 00 00 00
- 第二个8字节是
size字段,小端序解析为0x21 - 实际chunk总大小 =
0x21 & ~0x7 = 0x20(32字节) - 缓冲区大小 =
0x20 - 16 = 0x10(16字节)
3. 判断缓冲区的分配状态
通过chunk的结构和标志位判断:
- 已分配状态:
size字段的P位(第0位)通常为1(前一个chunk已分配,避免合并)- 缓冲区起始地址后的内存不是有效的堆内链表指针(即没有
fd/bk这类指向其他chunk的地址)
- 已释放状态:
size字段的P位可能为0(允许和前一个空闲chunk合并)- 缓冲区起始地址后的8字节(64位)或4字节(32位)会有
fd指针,指向同空闲链表中的下一个chunk;再往后的对应长度是bk指针,指向链表中的上一个chunk - 若chunk属于tcache(GLIBC 2.23+默认启用的小型空闲缓存),
fd指针会指向tcache链表中的下一个空闲chunk
注意事项
- 先确认设备是32位还是64位:可从
/proc/PID/maps的地址长度判断(8位十六进制为32位,12位为64位) - 注意字节序:嵌入式设备多为小端序,解析十六进制数据时需反转字节顺序
- 大内存块(
size字段M位为1)是mmap分配的,结构略有差异,空闲时会通过fd_nextsize/bk_nextsize链接
内容的提问来源于stack exchange,提问作者Kokomelom
相关产品推荐
相关产品推荐

