https://blog.csdn.net/Mr_Fmnwon/article/details/142330217
https://blog.csdn.net/2302_79542511/article/details/141200902
先放一张largebin结构图

源码分析
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
if (in_smallbin_range(size))
{
victim_index = smallbin_index(size);
bck = bin_at(av, victim_index);
fwd = bck->fd;
}
//下面那个else开始是largebin的插入
else
{
victim_index = largebin_index(size);// 获取对应 bin 的头节点
bck = bin_at(av, victim_index); // 获取 bin 中的第一个 chunk
fwd = bck->fd;
if (fwd != bck) {
size |= PREV_INUSE;
assert((bck->bk->size & NON_MAIN_ARENA) == 0);
if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) {
'''
第一种情况,比最小的小
'''
fwd = bck;
bck = bck->bk;
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
else
{
'''第二种遍历找位置'''
assert((fwd->size & NON_MAIN_ARENA) == 0);
while ((unsigned long) size < fwd->size) {
fwd = fwd->fd_nextsize;
assert((fwd->size & NON_MAIN_ARENA) == 0);
}
if ((unsigned long) size == (unsigned long) fwd->size)
fwd = fwd->fd;
else
{
'''如果fwd->bk_nextsize被提前构造那victim->bk_nextsize会被改'''
//fwd此时指向第一个比victim小的chunk
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
bck = fwd->bk;
}
}
else
victim->fd_nextsize = victim->bk_nextsize = victim;
}
mark_bin(av, victim_index);
'''这里通上 ,提前构造fwd->bk'''
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
|
插入chunk比最小的大
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
while ((unsigned long) size < fwd->size) {
fwd = fwd->fd_nextsize;
assert((fwd->size & NON_MAIN_ARENA) == 0);
}
if ((unsigned long) size == (unsigned long) fwd->size)
fwd = fwd->fd;
else
{
'''如果fwd->bk_nextsize被提前构造那victim->bk_nextsize会被改'''
//fwd此时指向第一个比victim小的chunk
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
|
此时fwd指向比victim小的最大chunk,我们提前修改fwd的bk_nextsize的话
victim->bk_nextsize->fd_nextsize = victim;就可以向fwd->bk_next_size+0x20中写一个victim地址(heap地址),这种情况只有在2.31之前有效
1
2
3
4
5
6
7
8
9
10
11
12
13
|
bck = fwd->bk;
}
}
else
victim->fd_nextsize = victim->bk_nextsize = victim;
}
mark_bin(av, victim_index);
'''这里通上 ,提前构造fwd->bk'''
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
|
这里控制fwd->bk(bck),则bck->fd = victim;将bck+0x10处改为victim地址
插入chunk比最小chunk还小
从2.31开始引入了两个检查
1
2
3
4
5
6
7
|
// line 3867
if (__glibc_unlikely (fwd->bk_nextsize->fd_nextsize != fwd))
malloc_printerr ("malloc(): largebin double linked list corrupted (nextsize)");
// line 3872
bck = fwd->bk;
if (bck->fd != fwd)
|
但是如果插入chunk比最小的largebin_chunk还小会直接被链入,没有这些检查
1
2
3
4
5
6
7
8
9
10
|
if ((unsigned long) (size) < (unsigned long) (bck->bk->size)) {
'''
第一种情况,比最小的小
'''
fwd = bck;
bck = bck->bk;
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
|
这时fwd->fd是largebin的最大chunk,我们控制fwd->fd->bk_nextsize为addr,则victim->bk_nextsize = fwd->fd->bk_nextsize; 把victim->bk_nextsize赋值为addr,
victim->bk_nextsize->fd_nextsize = victim;将addr+0x18改为victim地址
how2heap demo
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
42
|
#include <stdio.h>
#include <stdlib.h>
int main()
{
unsigned long stack_var1 = 0;
unsigned long stack_var2 = 0;
fprintf(stderr, "stack_var1 (%p): %ld\n", &stack_var1, stack_var1);
fprintf(stderr, "stack_var2 (%p): %ld\n\n", &stack_var2, stack_var2);
unsigned long *p1 = malloc(0x320);
malloc(0x20);
unsigned long *p2 = malloc(0x400);
malloc(0x20);
unsigned long *p3 = malloc(0x400);
malloc(0x20);
free(p1);
free(p2);
void* p4 = malloc(0x90);
free(p3);
p2[-1] = 0x3f1;
p2[0] = 0;
p2[2] = 0;
p2[1] = (unsigned long)(&stack_var1 - 2);
p2[3] = (unsigned long)(&stack_var2 - 4);
malloc(0x90);
fprintf(stderr, "stack_var1 (%p): %p\n", &stack_var1, (void *)stack_var1);
fprintf(stderr, "stack_var2 (%p): %p\n", &stack_var2, (void *)stack_var2);
return 0;
}
|
执行到23行后,有两个空闲chunk在unsortedbin

然后malloc(0x90),先扫描p1,p2将他们分别放进smallbin和largebin,然后分割smallbin的p1,并把分割后的chunk(0x330-0xa0)放回unsortedbin
接下来free(p3)又free了一个0x410的chunk
然后修改了largebin里的p2,把size改成0x3f1,既属于largebin,又小于p3 size

接下来一个malloc(0x90)会将p3插入largebin,触发
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
while ((unsigned long) size < fwd->size) {
fwd = fwd->fd_nextsize;
assert((fwd->size & NON_MAIN_ARENA) == 0);
}
if ((unsigned long) size == (unsigned long) fwd->size)
fwd = fwd->fd;
else
{
'''如果fwd->bk_nextsize被提前构造那victim->bk_nextsize会被改'''
victim->fd_nextsize = fwd;
victim->bk_nextsize = fwd->bk_nextsize;
fwd->bk_nextsize = victim;
victim->bk_nextsize->fd_nextsize = victim;
}
|
这个代码,此时victim相当于p3,fwd相当于p2,p3的bk_nextsize被修改成我们构造的addr2,*(addr2+4)被改成p3地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
bck = fwd->bk;
}
}
else
victim->fd_nextsize = victim->bk_nextsize = victim;
}
mark_bin(av, victim_index);
'''这里通上 ,提前构造fwd->bk'''
victim->bk = bck;
victim->fd = fwd;
fwd->bk = victim;
bck->fd = victim;
// 等价于
(fwd->bk)->fd = victim;
// 等价于
*(addr1+2) = victim;
|
p3的bk被改为addr1,*(addr1+2)被改为p2地址
利用条件
- 可以修改一个 large bin chunk 的 data
- 从 unsorted bin 中来的 large bin chunk 要紧跟在被构造过的 chunk 的后面