一、源C代码&编译指令
//源C代码 cs1g.c
//gcc -g cs1g.c -o cs1g
#include<stdio.h>
#include<stdlib.h>
int main(void)
{
int i;
void *xy1,*xy2,*xy3,*xy4,*xy5,*xy6; //0之前vmmap指令查看不到heap堆
xy1 = malloc(0x30);
xy2 = malloc(0x30);
xy3 = malloc(0x30);
free(xy1);
*(long long *)((long long)xy2) = 0x112233445566778899121314151617181920;
*(long long *)((long long)xy3) = 0x55667788;
xy4 = malloc(0x30);
xy5 = malloc(0x30);
free(xy2);
xy6 = malloc(0x30);
free(xy3);
free(xy4);
free(xy5);
free(xy6);
return 0;
}
二、一些调试指令
pwndbg> top_chunk #打印top chunk初始地址
pwndbg> vmmap #查看vmmap表
pwndbg> bin #查看bin表
pwndbg> heap #查看堆
pwndbg> file ./<二进制文件名> #导入源代码,方便调试
pwndbg> x/150gx 0x<地址> #打印堆信息 x/150gx 0x555555559000
pwndbg> x/150xb 0x<地址>
其中,x是指定大小起始内存地址,即查看起始内存地址上指定大小的内存里的值。如x /3b 0x11223344,就是查看以0x11223344开始的3个字节的值,也可以是x /3w 0x11223344,就是查看以0x11223344开始的3个word的值
命令格式:x/<n/f/u> <addr>
n是一个正整数,表示需要显示的内存单元的个数
f 表示显示的格式(可取如下值: x 按十六进制格式显示变量。d 按十进制格式显示变量。u 按十进制格式显示无符号整型。o 按八进制格式显示变量。t 按二进制格式显示变量。a 按十六进制格式显示变量。i 指令地址格式c 按字符格式显示变量。f 按浮点数格式显示变量。)
u 表示从当前地址往后请求的字节数 默认4byte,u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节
<addr>表示一个内存地址
x/xw addr 显示某个地址处开始的16进制内容,如果有符号表会加载符号表
x/x $esp 查看esp寄存器中的值
如x $ebp-0x2c
x/s addr 查看addr处的字符串
x/b addr 查看addr处的字符
x/i addr 查看addr处的反汇编结果
三、调试过程
1、目前还未新建堆

2、新建一个大小为0x30的堆块

3、新建三个大小为0x30的堆块


(因为堆的分配是从低地址向高地址分配,上图地址弄错了,即高地址和低地址变换一下)

trunk是用户申请内存的单位,也是堆管理器管理内存的基本单位,malloc()返回的指针指向一个chunk的数据区域。

chunk 的分类
按状态
按大小
特定功能
top chunk
last remainder chunk
malloced chunk
已被分配且填写了相应数据的chunk

free chunk
被释放掉的malloced chunk成为free chunk

top chunk
arena中从未被使用过的内存区域

last remainder chunk
malloc分割原chunk后剩余的部分

Chunk 的微观结构

size
占据一字长的低3bits以后的地址,用于表示当前chunk的大小(整个chunk的大小,包括chunk头)
A flag
NON_MAIN_ARENA,记录当前 chunk是否不属于主线程,1 表示不属于,0 表示属于。
M flag
IS_MAPPED,记录当前 chunk 是否是由 mmap 分配的。
P flag
PREV_INUSE,记录前一个 chunk 块是否被分配。一般来说,堆中第一个被分配的内存块的 size 字段的 P 位都会被设置为 1,以便于防止访问前面的非法内存。当一个 chunk 的 size 的 P 位为 0 时,我们能通过 prev_size 字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲 chunk 之间的合并。
fd pointer
在bin中指向下一个(非物理相邻)空闲的 chunk.
bk pointer
在bin中指向上一个(非物理相邻)空闲的 chunk
fd_nextsize
在large bin中指向前一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针
bk_nextsize
在large bin中指向后一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针
一般空闲的 large chunk 在 fd 的遍历顺序中,按照由大到小的顺序排列。这样做可以避免在寻找合适 chunk 时挨个遍历
参考文献:
https://blog.csdn.net/weixin_61823031/article/details/125937900
https://blog.csdn.net/xy_369/article/details/130718514