1 |
|
申请512的堆
申请256的堆
释放512的堆
再申请500的堆
1 | user@ubuntu:~/workspace/pwn/first_fit$ vim first_fit.c |
==这第一个程序展示了glibc堆分配的策略,即first-fit。在分配内存时,malloc会先到unsortedbin(或者fastbins) 中查找适合的被free的chunk,如果没有,就会把unsorted bin中的所有chunk分别放入到所属的bins中,然后再去这些bins里去找合适的chunk。可以看到第三次malloc的地址和第一次相同,即malloc找到了第一次free掉的chunk,并把它重新分配。==
gdb调试这个程序
0x1 malloc(512); malloc(256);
两次malloc,并分别填上值之后部分内存: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
43pwndbg> x/6gx 0x602010-0x10
0x602000: 0x0000000000000000 0x0000000000000211
0x602010: 0x4141414141414141 0x0000000000000000
0x602020: 0x0000000000000000 0x0000000000000000
pwndbg> x/6gx 0x602220-0x10
0x602210: 0x0000000000000000 0x0000000000000111
0x602220: 0x4242424242424242 0x0000000000000000
0x602230: 0x0000000000000000 0x0000000000000000
pwndbg> p main_arena
$14 = {
mutex = 0x0,
flags = 0x1,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x602320,
last_remainder = 0x0,
bins = {0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b78 <main_arena+88>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1be8 <main_arena+200>...},
binmap = {0x0, 0x0, 0x0, 0x0},
next = 0x7ffff7dd1b20 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
pwndbg> x/14gx &main_arena.bins
0x7ffff7dd1b88 <main_arena+104>: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x7ffff7dd1b98 <main_arena+120>: 0x00007ffff7dd1b88 0x00007ffff7dd1b88
0x7ffff7dd1ba8 <main_arena+136>: 0x00007ffff7dd1b98 0x00007ffff7dd1b98
0x7ffff7dd1bb8 <main_arena+152>: 0x00007ffff7dd1ba8 0x00007ffff7dd1ba8
0x7ffff7dd1bc8 <main_arena+168>: 0x00007ffff7dd1bb8 0x00007ffff7dd1bb8
0x7ffff7dd1bd8 <main_arena+184>: 0x00007ffff7dd1bc8 0x00007ffff7dd1bc8
0x7ffff7dd1be8 <main_arena+200>: 0x00007ffff7dd1bd8 0x00007ffff7dd1bd8
......
0x7ffff7dd2338 <main_arena+2072>: 0x00007ffff7dd2328 0x00007ffff7dd2328
0x7ffff7dd2348 <main_arena+2088>: 0x00007ffff7dd2338 0x00007ffff7dd2338
0x7ffff7dd2358 <main_arena+2104>: 0x00007ffff7dd2348 0x00007ffff7dd2348
0x7ffff7dd2368 <main_arena+2120>: 0x00007ffff7dd2358 0x00007ffff7dd2358
binmap-->0x7ffff7dd2378 <main_arena+2136>: 0x0000000000000000 0x0000000000000000
0x3 free(a)
释放掉这个512Byte的堆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
28pwndbg> x/6gx 0x602010-0x10
0x602000: 0x0000000000000000 0x0000000000000211
0x602010: 0x00007ffff7dd1b78 0x00007ffff7dd1b78
0x602020: 0x0000000000000000 0x0000000000000000
pwndbg> x/6gx 0x602220-0x10
0x602210: 0x0000000000000210 0x0000000000000110
0x602220: 0x4242424242424242 0x0000000000000000
0x602230: 0x0000000000000000 0x0000000000000000
pwndbg> p main_arena
$17 = {
mutex = 0x0,
flags = 0x1,
fastbinsY = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
top = 0x602320,
last_remainder = 0x0,
bins = {0x602000, 0x602000, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b88 <main_arena+104>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1b98 <main_arena+120>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1ba8 <main_arena+136>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bb8 <main_arena+152>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bc8 <main_arena+168>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1bd8 <main_arena+184>, 0x7ffff7dd1be8 <main_arena+200>...},
binmap = {0x0, 0x0, 0x0, 0x0},
next = 0x7ffff7dd1b20 <main_arena>,
next_free = 0x0,
attached_threads = 0x1,
system_mem = 0x21000,
max_system_mem = 0x21000
}
pwndbg>
由上看出,a被free掉后,原本的用户数据域现在变成了fd域和bk域,而且被赋了值。这个0x7ffff7dd1b78是main_arena的地址+88。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
48pwndbg> heap
Top Chunk: 0x602320
Last Remainder: 0
0x602000 PREV_INUSE {
prev_size = 0x0,
size = 0x211,
fd = 0x7ffff7dd1b78 <main_arena+88>,
bk = 0x7ffff7dd1b78 <main_arena+88>,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602210 {
prev_size = 0x210,
size = 0x110,
fd = 0x4242424242424242,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x602320 PREV_INUSE {
prev_size = 0x0,
size = 0x20ce1,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x602000 ◂— 0x7ffff7dd1b78
smallbins
empty
largebins
empty
pwndbg>
,
第一个free后,被加入到unsorted bin中。这时fd和bk里的值都没undorted bins链表的表头。main_arena + 0x58处。
==注意chunk b,chunk b的pre_size字段和size的pre_inuse标志位都变了。==
0x04 malloc(500) 0x200 - 12
1 | pwndbg> heap |
所以当释放一块内存后再申请一块大小略小于的空间,那么glibc倾向于将先前被释放的空间重新分配。
且这个unsorted bins的位置是在main_arena偏移0x58处
总结
glibc堆分配的策略,即first-fit。在分配内存时,malloc会先到unsorted bin(或者fastbins)中查找适合的被free的chunk,如果没有,就会把unsorted bin中的所有chunk分别放入到所属的bins中,然后再去这些bins里去找合适的 chunk。可以看到第三次malloc的地址和第一次相同,即malloc找到了第一次 free掉的chunk,并把它重新分配.