学习资料:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/heap/heapoverflow_basic/
之前学了一些栈溢出的知识和方法,首先比较了一下堆溢出和它的区别
堆上并不存在返回地址等可以让攻击者直接控制执行流程的数据,因此我们一般无法直接通过堆溢出来控制 EIP
那怎么办呢,第一步要了解一些堆的知识,一边学的时候一边查询就好了,知识点我只看了简单的部分
对的分配:通常来说堆是通过调用 glibc 函数 malloc 进行分配的,在某些情况下会使用 calloc 分配。calloc 与 malloc 的区别是 calloc在分配后会自动进行清空,同时还有realloc,大概就是再分配时会根据你需要的size大小进行调整堆大小,具体怎么调整可以百度一下
堆溢出和栈溢出的危险函数都差不多
- 输入
- gets,直接读取一行,忽略
'\x00'
- scanf
- vscanf
- gets,直接读取一行,忽略
- 输出
- sprintf
- 字符串
- strcpy,字符串复制,遇到
'\x00'
停止 - strcat,字符串拼接,遇到
'\x00'
停止 - bcopy
- strcpy,字符串复制,遇到
堆溢出肯定需要覆盖区域的大小:和栈溢出不同的是(我们请求多少栈空间就给多少),申请对空间的时候不知道为什么对我们特别好,偏要多给我们一点,为什么会多一些呢?,有两个原因:对齐;堆头部
对齐就不多做介绍了,搜一下就好了,无非就是32位与64位的不同
说到堆头部,就可以介绍一下堆的结构是什么样的了
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;
};
prev_size:
前一块chunk的大小(若前一块chunk空闲,否则存放前一块chunk的数据,这也就是chunk的复用)
size:
该 chunk 的大小
fd,bk:
chunk 处于分配状态时,从 fd 字段开始是用户的数据。chunk 空闲时,会被添加到对应的空闲管理链表中,其字段的含义如下
fd 指向下一个(非物理相邻)空闲的 chunk
bk 指向上一个(非物理相邻)空闲的 chunk
通过 fd 和 bk 可以将空闲的 chunk 块加入到空闲的 chunk 块链表进行统一管理
fd_nextsize, bk_nextsize:
也是只有 chunk 空闲的时候才使用,不过其用于较大的 chunk(large chunk)。
fd_nextsize 指向前一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。
bk_nextsize 指向后一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针。
一般空闲的 large chunk 在 fd 的遍历顺序中,按照由大到小的顺序排列
那我们就可以推断出分配的内存是多少了,借用CTFWIKI中的例子,假设在64位程序中,我们申请24字节的堆空间
首先chunk头部16字节是不能存放我们需要的数据的,但是还是需要申请
再则因为本快堆已经被分配了,所以下一块的pre可以写奔块堆的数据,可以多用8字节
所以实际分配的字节大小:24+16-8=32字节
稍微看了一下堆溢出的小技巧之后,觉得认识堆回收之后的管理也是重要的
以下图片来自CTFWIKI
除了fastbin,一共128个bin放在一起,这些要么极其有规律,要么极其没规律,搜一下就好了,这里单独提一下fastbin,如下图
还有一些像top chunk和last remainder还算好理解,就暂时不介绍了
这些就是chunk大概得了,其实顶上依次还有heap和arena,暂时可能hold不住就先不了解了