最近在学习pwn中的堆利用,因此来系统的学习一下ptmalloc的机制。主要看的是Glibc内存管理–ptmalloc2源代码分析。

malloc

这是照着书上画的流程图,可以快速了解malloc的整体逻辑。

graph TD
malloc(malloc分配):::es
-->calSize(将请求的大小计算成要分配的chunk大小)
-->isFast(chunk_size <= max_fast 
在Fast Bins范围中) isFast--T-->tryFastbin(尝试从Fast bins中取) tryFastbin--T-->e1(结束):::es tryFastbin--F-->isSmall(chunk_size < 512B
判断是否在Small Bins范围中) isFast--F-->isSmall isSmall--T-->trySmallBins(找到具体所在的某个small bin
尝试从尾部取) trySmallBins--T-->e2(结束):::es isSmall--F-->mergeFB(合并fast bins中相邻chunk,
并链接到unsorted bin中) trySmallBins--F-->mergeFB -->checkUB(判断unsorted bin是否满足下列条件:
1.unsorted bin 只有一个 chunk
2.这个 chunk 在上次分配时被使用过
3.所需分配的 chunk 大小属于 small bins
4. chunk 的大小大于等于需要分配的大小) checkUB--T-->split(对此chunk切割出目标大小)-->e3(结束):::es checkUB--F-->UB2bins(将unsorted bin中每个chunk放入对应bins中) -->tryLB(从large bins中寻找
按照smallest-first best-fit原则) tryLB--T-->e4(结束):::es tryLB--F-->checkTC(判断Top Chunk大小是否满足条件) checkTC--T--> split2(从Top Chunk切割出目标大小) -->e5(结束):::es checkTC--F--> needMmap(目标大小等于mmap分配阈值) --T-->useMmap(使用mmap系统调用开内存映射
返回给用户) -->e6(结束):::es needMmap--F-->isMain(判断是否为主配分区) isMain--T-->needInit(是否为第一次调用) --T-->初始化堆-->split2 isMain--F-->调用sbrk增加heap空间-->split2 isMain--F-->newSubHeap(调用mmap来分配一个新的sub-heap
增加top chunk大小) -->split2 classDef es fill:#bbf

free

同样是流程图:

graph TD
free(free):::es
-->ifMmap(是否通过mmap)
--T-->realse(munmap解除映射)
-->ifAutoAdjust(开启了mmap分配阈值的动态调整机制
且当前回收chunk大于mmap分配阈值) --F-->e1(结束):::es ifAutoAdjust--T-->set(将mmap分配阈值设置为该chunk的大小
将mmap 收缩阈值设定为mmap分配阈值的2倍) -->e1 ifMmap--F-->ifFastAndNearTop(判断chunk_size <= max_fast
且不与Top Chunk相邻) --T-->putinFB(将该chunk放入Fast Bin中,
且不修改状态位P,不合并邻近chunk) -->e2(结束):::es ifFastAndNearTop--F-->isFreeBK(前一个chunk是否空闲) --T-->merge1(与前一个chunk合并) -->isNextTop isFreeBK--F-->isNextTop(判断下一个块是否为Top chunk) --T-->merge2(与Top Chunk合并) -->needMergeFB isNextTop--F-->isFreeFD(后一个chunk是否空闲) --F-->needMergeFB isFreeFD--T-->merge3(与后一个chunk合并) -->putinUB(将合并后的chunk放到unsorted bin中) -->needMergeFB(判断合并后的chunk的大小是否大于
ASTBIN_CONSOLIDATION_THRESHOLD) --F-->shrinkTC needMergeFB--T-->mergeFB(将Fast Bins相邻chunk合并
全部放入 unsorted bin中) -->shrinkTC(判断top chunk的大小是否大于mmap收缩阈值) --F-->e3(结束):::es shrinkTC--T-->isMain(是否为主分配区) --T-->realease2(归还top chunk中的一部分给操作系统
最先分配的128KB空间不会归还) -->e4(结束):::es isMain--F-->isTCWhole(top chunk为整个sub-heap) --T-->realease3(把整个sub-heap还回给操作系统) -->e5(结束):::es isTCWhole--F-->realease4(sub-heap 收缩) -->e5 classDef es fill:#bbf

unlink

其它漏洞配合上unlink可以达到任意地址写的效果。

这是旧版unlink函数,是宏的形式。

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
26
27
28
29
30
/* Take a chunk off a bin list */
#define unlink(P, BK, FD) { \
FD = P->fd; \
BK = P->bk; \
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
malloc_printerr (check_action, "corrupted double-linked list", P); \
else { \
FD->bk = BK; \
40
BK->fd = FD; \
if (!in_smallbin_range (P->size) \
&& __builtin_expect (P->fd_nextsize != NULL, 0)) { \
assert (P->fd_nextsize->bk_nextsize == P); \
assert (P->bk_nextsize->fd_nextsize == P); \
if (FD->fd_nextsize == NULL) { \
if (P->fd_nextsize == P) \
FD->fd_nextsize = FD->bk_nextsize = FD; \
else { \
FD->fd_nextsize = P->fd_nextsize; \
FD->bk_nextsize = P->bk_nextsize; \
P->fd_nextsize->bk_nextsize = FD; \
P->bk_nextsize->fd_nextsize = FD; \
} \
} else { \
P->fd_nextsize->bk_nextsize = P->bk_nextsize; \
P->bk_nextsize->fd_nextsize = P->fd_nextsize; \
} \
} \
} \
}

这是新版的unlink新增了presize的检查,利用起来更难了。。

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

/* Take a chunk off a bin list. */
static void
unlink_chunk (mstate av, mchunkptr p)
{
if (chunksize (p) != prev_size (next_chunk (p)))
malloc_printerr ("corrupted size vs. prev_size");

mchunkptr fd = p->fd;
mchunkptr bk = p->bk;

if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
malloc_printerr ("corrupted double-linked list");

fd->bk = bk;
bk->fd = fd;
if (!in_smallbin_range (chunksize_nomask (p)) && p->fd_nextsize != NULL)
{
if (p->fd_nextsize->bk_nextsize != p
|| p->bk_nextsize->fd_nextsize != p)
malloc_printerr ("corrupted double-linked list (not small)");

if (fd->fd_nextsize == NULL)
{
if (p->fd_nextsize == p)
fd->fd_nextsize = fd->bk_nextsize = fd;
else
{
fd->fd_nextsize = p->fd_nextsize;
fd->bk_nextsize = p->bk_nextsize;
p->fd_nextsize->bk_nextsize = fd;
p->bk_nextsize->fd_nextsize = fd;
}
}
else
{
p->fd_nextsize->bk_nextsize = p->bk_nextsize;
p->bk_nextsize->fd_nextsize = p->fd_nextsize;
}
}
}

具体的利用方式去ctf-wiki上去看吧,就不再写一遍了。