how2heap 问题汇总(上)
字数 1492 2025-08-25 22:59:09
How2Heap 问题解析与教学文档
1. 内存分配基础概念
1.1 Bin 的分类
在 glibc 的内存分配器中,free 后的 chunk 会根据大小被放入不同的 bin 中:
- fastbins: 用于小内存块的快速分配 (通常小于等于 0x80 字节)
- unsortedbin: 所有非 fastbins 的 chunk 被 free 后首先进入这里
- smallbins: 用于中等大小的内存块 (通常小于 512 字节)
- largebins: 用于大内存块的分配
1.2 Unsorted Bin 的特殊性
Unsorted bin 有以下特点:
- 所有非 fastbins 大小的 chunk 被 free 后首先进入 unsorted bin
- 只有一个链表结构(不是复数形式)
- 在 malloc 时充当缓冲区角色
- 当无法满足分配请求时,会将 chunk 转移到合适的 smallbins 或 largebins
2. First Fit 分配策略分析
2.1 基础示例分析
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char* a = malloc(512);
char* b = malloc(256);
free(a);
char* c = malloc(500);
}
调试观察:
- free(a) 后,512 字节的 chunk 进入 unsorted bin
- malloc(500) 时直接从 unsorted bin 中分配
2.2 Remainder Chunk 现象
修改示例:
int main() {
char* a = malloc(512);
char* b = malloc(256);
free(a);
char* c = malloc(10); // 只取走 0x20 字节
char* d = malloc(512);
}
观察:
- malloc(10) 从 unsorted bin 中切割出 0x20 字节
- 剩余 0x1f0 字节成为 remainder chunk 仍留在 unsorted bin
- malloc(512) 时 remainder chunk 不够大,从 top chunk 分配
- remainder chunk 被转移到 smallbins
3. Consolidate 机制深入研究
3.1 极端示例分析
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int main() {
void* p1 = malloc(0x70);
void* p3 = malloc(0x70);
void* p4 = malloc(0x70);
void* p5 = malloc(0x70);
void* p6 = malloc(0x70);
void* p7 = malloc(0x70);
void* p8 = malloc(0x70);
void* p9 = malloc(0x70);
void* p10 = malloc(0x70);
void* p2 = malloc(0x70);
free(p1); free(p3); free(p4); free(p5); free(p6);
free(p7); free(p8); free(p9); free(p10);
void* p12 = malloc(0x400);
}
调试观察:
- 多个 0x70 大小的 chunk 被 free 后进入 fastbins
- malloc(0x400) 触发 consolidate
- fastbins 中的 chunk 被合并并转移到 unsorted bin
- 合并后的 chunk 满足 malloc(0x400) 的需求
4. Unsafe Unlink 技术详解
4.1 Unlink 基本过程
Unlink 操作的核心代码:
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
4.2 利用原理
-
构造 fake chunk:
- 设置 fd 和 bk 指针
- 绕过安全检查(如 size 与 next chunk 的 prev_size 匹配)
-
通过 unlink 实现任意写:
- 修改指针指向目标地址
- 通过指针操作实现任意地址读写
4.3 简单示例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
uint64_t *chunk0_ptr;
int main() {
char b[8] = "aaaa";
chunk0_ptr = &b;
chunk0_ptr[0] = 0x4242424242424242LL;
printf("%s\n", b);
}
这个简单示例展示了如何通过指针修改来改变内存内容,类似于 unlink 攻击后的效果。
5. 关键点总结
-
Unsorted Bin 优先原则:非 fastbins 的 chunk 总是先进入 unsorted bin,只有在无法满足分配时才转移到其他 bins。
-
Remainder Chunk:当从 unsorted bin 中切割分配时,剩余部分会成为 remainder chunk 保留在 unsorted bin 中。
-
Consolidate 触发条件:分配大内存块时会触发 fastbins 的合并,合并后的 chunk 进入 unsorted bin。
-
Unlink 攻击核心:通过伪造 chunk 的 fd 和 bk 指针,利用 unlink 操作实现任意地址写。
-
安全检查绕过:成功的 unlink 攻击需要精心构造 fake chunk 以通过 glibc 的安全检查。