文章参考了了网上的一些师傅文章内容,总结了一些自己的理解。

malloc_chunk结构体成员解析

malloc_chunk结构定义

cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
This struct declaration is misleading (but accurate and necessary).
It declares a "view" into memory allowing access to necessary
fields at known offsets from a given base. See explanation below.
*/
struct malloc_chunk {

INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */

struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;

/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};

各个成员的作用

  1. prev_size

    • 如果该chunk物理地址相邻的前一chunk(两个指针的差值为前一个chunk大小)是空闲的chunk,这个字段记录的是前一个字符段的大小
    • 用于储存物理地址相邻的chunk(低地址chunk)的信息
    • 被共享,如果当前chunk大不够用,可以占用下一个chunkprev_size字段
  2. size

    • 存储当前chunk的大小,chunk的大小内存申请,与SIZE_SZ有关,如果不是2*SIZE_SZ的整数倍,会自动向上取整到2*SIZE_SZ的整数倍,32位操作系统中SIZE_SZ是32位4个字节,64位操作系统中SIZE_SZ是64位8个字节。32位必须8字节对齐,64位必须16字节对齐,无论32位还是64位,size最后三位都没有用,所用可以用来存储其他信息

      • NON_MAIN_ARENA

      • IS_MAPPED

        记录当前chunk是否是由mmap申请

      • PREV_INUSE

        记录前一个 chunk 块是否被分配。一般来说,堆中第一个被分配的内存块的 size 字段的 P 位都会被设置为 1,以便于防止访问前面的非法内存。当一个 chunk 的 size 的 P 位为 0 时,我们能通过 prev_size 字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲 chunk 之间的合并。

  3. fdbk

    • chunk处于分配状态时,fd地址段存放的是用户数据。chunk在空闲的时候通过链表管理
    • fd指向上一个空闲(非物理空闲)的chunk
    • bk指向下一个空闲(非物理空闲)的chunk
  4. fd_nextsizebk_nextsize

    -

  • chunk被使用时内存结构

    python
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Size of previous chunk, if unallocated (P clear) |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Size of chunk, in bytes |A|M|P|
    mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | User data starts here... .
    . .
    . (malloc_usable_size() bytes) .
    next . |
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | (size of chunk, but used for application data) |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Size of next chunk, in bytes |A|0|1|
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  • chunk被释放时内存结构

    python
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Size of previous chunk, if unallocated (P clear) |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `head:' | Size of chunk, in bytes |A|0|P|
    mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Forward pointer to next chunk in list |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Back pointer to previous chunk in list |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Unused space (may be 0 bytes long) .
    . .
    next . |
    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `foot:' | Size of chunk, in bytes |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    | Size of next chunk, in bytes |A|0|0|
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

内存对齐

malloc函数申请的chunk地址都是8字节或者16字节对齐(32位8字节,64位16字节),所谓字节对齐,就是所有的地址最后8位或16位都是一样的(大多数时候为8的整数倍),常见的内存对齐有8字节对齐和16字节对齐

  • 8字节对齐

    地址的最后8位(2进制位)10进制表示为8

  • 16字节对齐

    地址的最后8位(2进制位)10进制表示为0

下面用pwndbg展示32位下chunk内存结构

pwndbg分析chunk在内存中的结构

测试源码

c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>

int main()
{
int* ptr1 = (int*)malloc(8);
int* ptr2 = (int*)malloc(16);
int* ptr3 = (int*)malloc(32);
int* ptr4 = (int*)malloc(64);
int* ptr5 = (int*)malloc(128);
int* ptr6 = (int*)malloc(256);
printf("ptr1->%p\n", ptr1);
printf("ptr2->%p\n", ptr2);
printf("ptr3->%p\n", ptr3);
printf("ptr4->%p\n", ptr4);
printf("ptr5->%p\n", ptr5);
printf("ptr6->%p\n", ptr6);
free(ptr1);
free(ptr2);
free(ptr3);
free(ptr4);
free(ptr5);
free(ptr6);
return 0;
}
  • 32位程序

    32位程序malloc申请地址

  • 64位程序

    64位程序malloc申请地址

  • heap命令查看所有堆信息

  • 分析二进制数据

    64位程序chunk结构与之类似