先复习一下overlappingchunk
- 第一种overlapping,适用与glibc<2.26
```c
/*
A simple tale of overlapping chunk.
This technique is taken from
http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf
*/
#include
#include #include #include
int main(int argc , char* argv[]){ //这个技术需要在没有tcache机制的libc中使用(就是glic2.26以前)
intptr_t *p1,*p2,*p3,*p4;
p1 = malloc(0x100 - 8); //首先我们malloc三个chunk
p2 = malloc(0x100 - 8);
p3 = malloc(0x80 - 8);
fprintf(stderr, "它们的位置分别在:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3);
memset(p1, '1', 0x100 - 8); //分别存上不同的数据
memset(p2, '2', 0x100 - 8);
memset(p3, '3', 0x80 - 8);
free(p2); //free chunk2,现在p2在unsorted bin中,如果有新的malloc大小符合条件,那么就是使用这个p2
int evil_chunk_size = 0x181; //现在我们假装覆盖到p2的size,我们将最后一位设置成1(前一个chunk处于分配状态)
int evil_region_size = 0x180 - 8; //malloc的大小
fprintf(stderr, "We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n",
evil_chunk_size, evil_region_size);
*(p2-1) = evil_chunk_size; // 假装覆盖 p2的size
p4 = malloc(evil_region_size);
memset(p4, '4', evil_region_size); //现在我们将p4全写成4
fprintf(stderr, "p4 = %s\n", (char *)p4);
fprintf(stderr, "p3 = %s\n", (char *)p3);
memset(p3, '3', 80); //现在我们将p3全写成3
fprintf(stderr, "p4 = %s\n", (char *)p4);
fprintf(stderr, "p3 = %s\n", (char *)p3); } ``` 填满数字后的chunk ``` gdb-peda$ x/90gx 0x603000 0x603000: 0x0000000000000000 0x0000000000000101 <-p1 0x603010: 0x3131313131313131 0x3131313131313131 0x603020: 0x3131313131313131 0x3131313131313131 0x603030: 0x3131313131313131 0x3131313131313131 0x603040: 0x3131313131313131 0x3131313131313131 0x603050: 0x3131313131313131 0x3131313131313131 0x603060: 0x3131313131313131 0x3131313131313131 0x603070: 0x3131313131313131 0x3131313131313131 0x603080: 0x3131313131313131 0x3131313131313131 0x603090: 0x3131313131313131 0x3131313131313131 0x6030a0: 0x3131313131313131 0x3131313131313131 0x6030b0: 0x3131313131313131 0x3131313131313131 0x6030c0: 0x3131313131313131 0x3131313131313131 0x6030d0: 0x3131313131313131 0x3131313131313131 0x6030e0: 0x3131313131313131 0x3131313131313131 0x6030f0: 0x3131313131313131 0x3131313131313131 0x603100: 0x3131313131313131 0x0000000000000101 <-p2 0x603110: 0x3232323232323232 0x3232323232323232 0x603120: 0x3232323232323232 0x3232323232323232 0x603130: 0x3232323232323232 0x3232323232323232 0x603140: 0x3232323232323232 0x3232323232323232 0x603150: 0x3232323232323232 0x3232323232323232 0x603160: 0x3232323232323232 0x3232323232323232 0x603170: 0x3232323232323232 0x3232323232323232 0x603180: 0x3232323232323232 0x3232323232323232 0x603190: 0x3232323232323232 0x3232323232323232 0x6031a0: 0x3232323232323232 0x3232323232323232 0x6031b0: 0x3232323232323232 0x3232323232323232 0x6031c0: 0x3232323232323232 0x3232323232323232 0x6031d0: 0x3232323232323232 0x3232323232323232 0x6031e0: 0x3232323232323232 0x3232323232323232 0x6031f0: 0x3232323232323232 0x3232323232323232 0x603200: 0x3232323232323232 0x0000000000000081 <-p3 0x603210: 0x3333333333333333 0x3333333333333333 0x603220: 0x3333333333333333 0x3333333333333333 0x603230: 0x3333333333333333 0x3333333333333333 0x603240: 0x3333333333333333 0x3333333333333333 0x603250: 0x3333333333333333 0x3333333333333333 0x603260: 0x3333333333333333 0x3333333333333333 0x603270: 0x3333333333333333 0x3333333333333333 0x603280: 0x3333333333333333 0x0000000000020d81 ``` free(p2)之后 ``` gdb-peda$ x/90gx 0x603000 0x603000: 0x0000000000000000 0x0000000000000101 <-p 0x603010: 0x3131313131313131 0x3131313131313131 0x603020: 0x3131313131313131 0x3131313131313131 0x603030: 0x3131313131313131 0x3131313131313131 0x603040: 0x3131313131313131 0x3131313131313131 0x603050: 0x3131313131313131 0x3131313131313131 0x603060: 0x3131313131313131 0x3131313131313131 0x603070: 0x3131313131313131 0x3131313131313131 0x603080: 0x3131313131313131 0x3131313131313131 0x603090: 0x3131313131313131 0x3131313131313131 0x6030a0: 0x3131313131313131 0x3131313131313131 0x6030b0: 0x3131313131313131 0x3131313131313131 0x6030c0: 0x3131313131313131 0x3131313131313131 0x6030d0: 0x3131313131313131 0x3131313131313131 0x6030e0: 0x3131313131313131 0x3131313131313131 0x6030f0: 0x3131313131313131 0x3131313131313131 0x603100: 0x3131313131313131 0x0000000000000101 <-p2 0x603110: 0x00007ffff7dd47b8 0x00007ffff7dd47b8 <-unsort bin 指向了main_arena+88 0x603120: 0x3232323232323232 0x3232323232323232 0x603130: 0x3232323232323232 0x3232323232323232 0x603140: 0x3232323232323232 0x3232323232323232 0x603150: 0x3232323232323232 0x3232323232323232 0x603160: 0x3232323232323232 0x3232323232323232 0x603170: 0x3232323232323232 0x3232323232323232 0x603180: 0x3232323232323232 0x3232323232323232 0x603190: 0x3232323232323232 0x3232323232323232 0x6031a0: 0x3232323232323232 0x3232323232323232 0x6031b0: 0x3232323232323232 0x3232323232323232 0x6031c0: 0x3232323232323232 0x3232323232323232 0x6031d0: 0x3232323232323232 0x3232323232323232 0x6031e0: 0x3232323232323232 0x3232323232323232 0x6031f0: 0x3232323232323232 0x3232323232323232 0x603200: 0x0000000000000100 0x0000000000000080 <-p3 0x603210: 0x3333333333333333 0x3333333333333333 0x603220: 0x3333333333333333 0x3333333333333333 0x603230: 0x3333333333333333 0x3333333333333333 0x603240: 0x3333333333333333 0x3333333333333333 0x603250: 0x3333333333333333 0x3333333333333333 0x603260: 0x3333333333333333 0x3333333333333333 0x603270: 0x3333333333333333 0x3333333333333333 0x603280: 0x3333333333333333 0x0000000000020d81 ``` 覆盖p2的size ``` gdb-peda$ x/90gx 0x603000 0x603000: 0x0000000000000000 0x0000000000000101 <-p1 0x603010: 0x3131313131313131 0x3131313131313131 0x603020: 0x3131313131313131 0x3131313131313131 0x603030: 0x3131313131313131 0x3131313131313131 0x603040: 0x3131313131313131 0x3131313131313131 0x603050: 0x3131313131313131 0x3131313131313131 0x603060: 0x3131313131313131 0x3131313131313131 0x603070: 0x3131313131313131 0x3131313131313131 0x603080: 0x3131313131313131 0x3131313131313131 0x603090: 0x3131313131313131 0x3131313131313131 0x6030a0: 0x3131313131313131 0x3131313131313131 0x6030b0: 0x3131313131313131 0x3131313131313131 0x6030c0: 0x3131313131313131 0x3131313131313131 0x6030d0: 0x3131313131313131 0x3131313131313131 0x6030e0: 0x3131313131313131 0x3131313131313131 0x6030f0: 0x3131313131313131 0x3131313131313131 0x603100: 0x3131313131313131 0x0000000000000181 <- 被覆盖(size变大) 0x603110: 0x00007ffff7dd47b8 0x00007ffff7dd47b8 0x603120: 0x3232323232323232 0x3232323232323232 0x603130: 0x3232323232323232 0x3232323232323232 0x603140: 0x3232323232323232 0x3232323232323232 0x603150: 0x3232323232323232 0x3232323232323232 0x603160: 0x3232323232323232 0x3232323232323232 0x603170: 0x3232323232323232 0x3232323232323232 0x603180: 0x3232323232323232 0x3232323232323232 0x603190: 0x3232323232323232 0x3232323232323232 0x6031a0: 0x3232323232323232 0x3232323232323232 0x6031b0: 0x3232323232323232 0x3232323232323232 0x6031c0: 0x3232323232323232 0x3232323232323232 0x6031d0: 0x3232323232323232 0x3232323232323232 0x6031e0: 0x3232323232323232 0x3232323232323232 0x6031f0: 0x3232323232323232 0x3232323232323232 0x603200: 0x0000000000000100 0x0000000000000080 <-p3 0x603210: 0x3333333333333333 0x3333333333333333 0x603220: 0x3333333333333333 0x3333333333333333 0x603230: 0x3333333333333333 0x3333333333333333 0x603240: 0x3333333333333333 0x3333333333333333 0x603250: 0x3333333333333333 0x3333333333333333 0x603260: 0x3333333333333333 0x3333333333333333 0x603270: 0x3333333333333333 0x3333333333333333 0x603280: 0x3333333333333333 0x0000000000020d81 ``` 把p4全部填充4之后 ``` gdb-peda$ x/90gx 0x603000 0x603000: 0x0000000000000000 0x0000000000000101 <-p1 0x603010: 0x3131313131313131 0x3131313131313131 0x603020: 0x3131313131313131 0x3131313131313131 0x603030: 0x3131313131313131 0x3131313131313131 0x603040: 0x3131313131313131 0x3131313131313131 0x603050: 0x3131313131313131 0x3131313131313131 0x603060: 0x3131313131313131 0x3131313131313131 0x603070: 0x3131313131313131 0x3131313131313131 0x603080: 0x3131313131313131 0x3131313131313131 0x603090: 0x3131313131313131 0x3131313131313131 0x6030a0: 0x3131313131313131 0x3131313131313131 0x6030b0: 0x3131313131313131 0x3131313131313131 0x6030c0: 0x3131313131313131 0x3131313131313131 0x6030d0: 0x3131313131313131 0x3131313131313131 0x6030e0: 0x3131313131313131 0x3131313131313131 0x6030f0: 0x3131313131313131 0x3131313131313131 0x603100: 0x3131313131313131 0x0000000000000181 <-p2 p4 0x603110: 0x3434343434343434 0x3434343434343434 0x603120: 0x3434343434343434 0x3434343434343434 0x603130: 0x3434343434343434 0x3434343434343434 0x603140: 0x3434343434343434 0x3434343434343434 0x603150: 0x3434343434343434 0x3434343434343434 0x603160: 0x3434343434343434 0x3434343434343434 0x603170: 0x3434343434343434 0x3434343434343434 0x603180: 0x3434343434343434 0x3434343434343434 0x603190: 0x3434343434343434 0x3434343434343434 0x6031a0: 0x3434343434343434 0x3434343434343434 0x6031b0: 0x3434343434343434 0x3434343434343434 0x6031c0: 0x3434343434343434 0x3434343434343434 0x6031d0: 0x3434343434343434 0x3434343434343434 0x6031e0: 0x3434343434343434 0x3434343434343434 0x6031f0: 0x3434343434343434 0x3434343434343434 0x603200: 0x3434343434343434 0x3434343434343434 <-p3 0x603210: 0x3434343434343434 0x3434343434343434 0x603220: 0x3434343434343434 0x3434343434343434 0x603230: 0x3434343434343434 0x3434343434343434 0x603240: 0x3434343434343434 0x3434343434343434 0x603250: 0x3434343434343434 0x3434343434343434 0x603260: 0x3434343434343434 0x3434343434343434 0x603270: 0x3434343434343434 0x3434343434343434 0x603280: 0x3434343434343434 0x0000000000020d81 ``` 再把p3改为3 ``` gdb-peda$ x/90gx 0x603000 0x603000: 0x0000000000000000 0x0000000000000101 <-p1 0x603010: 0x3131313131313131 0x3131313131313131 0x603020: 0x3131313131313131 0x3131313131313131 0x603030: 0x3131313131313131 0x3131313131313131 0x603040: 0x3131313131313131 0x3131313131313131 0x603050: 0x3131313131313131 0x3131313131313131 0x603060: 0x3131313131313131 0x3131313131313131 0x603070: 0x3131313131313131 0x3131313131313131 0x603080: 0x3131313131313131 0x3131313131313131 0x603090: 0x3131313131313131 0x3131313131313131 0x6030a0: 0x3131313131313131 0x3131313131313131 0x6030b0: 0x3131313131313131 0x3131313131313131 0x6030c0: 0x3131313131313131 0x3131313131313131 0x6030d0: 0x3131313131313131 0x3131313131313131 0x6030e0: 0x3131313131313131 0x3131313131313131 0x6030f0: 0x3131313131313131 0x3131313131313131 0x603100: 0x3131313131313131 0x0000000000000181 <-p2 p4 0x603110: 0x3434343434343434 0x3434343434343434 0x603120: 0x3434343434343434 0x3434343434343434 0x603130: 0x3434343434343434 0x3434343434343434 0x603140: 0x3434343434343434 0x3434343434343434 0x603150: 0x3434343434343434 0x3434343434343434 0x603160: 0x3434343434343434 0x3434343434343434 0x603170: 0x3434343434343434 0x3434343434343434 0x603180: 0x3434343434343434 0x3434343434343434 0x603190: 0x3434343434343434 0x3434343434343434 0x6031a0: 0x3434343434343434 0x3434343434343434 0x6031b0: 0x3434343434343434 0x3434343434343434 0x6031c0: 0x3434343434343434 0x3434343434343434 0x6031d0: 0x3434343434343434 0x3434343434343434 0x6031e0: 0x3434343434343434 0x3434343434343434 0x6031f0: 0x3434343434343434 0x3434343434343434 0x603200: 0x3434343434343434 0x3434343434343434 <-p3 0x603210: 0x3333333333333333 0x3333333333333333 0x603220: 0x3333333333333333 0x3333333333333333 0x603230: 0x3333333333333333 0x3333333333333333 0x603240: 0x3333333333333333 0x3333333333333333 0x603250: 0x3333333333333333 0x3333333333333333 0x603260: 0x3434343434343434 0x3434343434343434 0x603270: 0x3434343434343434 0x3434343434343434 0x603280: 0x3434343434343434 0x0000000000020d81 ``` 2. 第二种overlapping,对glibc版本没有要求 ```c /* Yet another simple tale of overlapping chunk. This technique is taken from https://loccs.sjtu.edu.cn/wiki/lib/exe/fetch.php?media=gossip:overview:ptmalloc_camera.pdf.
This is also referenced as Nonadjacent Free Chunk Consolidation Attack. */
#include
int main(){
intptr_t *p1,*p2,*p3,*p4,*p5,*p6;
unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;
int prev_in_use = 0x1;
p1 = malloc(1000); //首先malloc5个chunk
p2 = malloc(1000);
p3 = malloc(1000);
p4 = malloc(1000);
p5 = malloc(1000);
real_size_p1 = malloc_usable_size(p1);
real_size_p2 = malloc_usable_size(p2);
real_size_p3 = malloc_usable_size(p3);
real_size_p4 = malloc_usable_size(p4);
real_size_p5 = malloc_usable_size(p5);
memset(p1,'A',real_size_p1); //填满数字
memset(p2,'B',real_size_p2);
memset(p3,'C',real_size_p3);
memset(p4,'D',real_size_p4);
memset(p5,'E',real_size_p5);
free(p4);
*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; //将p2的size改写
free(p2);//这时allocator认为p2的下一个chunk是p4,因为p4是freeed,,然后p2和p4就会合并
p6 = malloc(2000); //现在我们就可以malloc一个更加大一些的chunk
memset(p6,'F',1500); //现在我们将p6填充一些数据
fprintf(stderr, "%s\n",(char *)p3);//这些数据将p3的值也覆盖 } ``` 填满数字之后 ``` gdb-peda$ x/4gx 0x603000 0x603000: 0x0000000000000000 0x00000000000003f1 <-p1 0x603010: 0x4141414141414141 0x4141414141414141
gdb-peda$ x/4gx 0x603000+0x3f0 0x6033f0: 0x4141414141414141 0x00000000000003f1 <-p2 0x603400: 0x4242424242424242 0x4242424242424242
gdb-peda$ x/4gx 0x603000+0x3f0+0x3f0 0x6037e0: 0x4242424242424242 0x00000000000003f1 <-p3 0x6037f0: 0x4343434343434343 0x4343434343434343
gdb-peda$ x/4gx 0x603000+0x3f0+0x3f0+0x3f0 0x603bd0: 0x4343434343434343 0x00000000000003f1 <-p4 0x603be0: 0x4444444444444444 0x4444444444444444
gdb-peda$ x/4gx 0x603000+0x3f0+0x3f0+0x3f0+0x3f0 0x603fc0: 0x4444444444444444 0x00000000000003f1 <-p5 0x603fd0: 0x4545454545454545 0x4545454545454545
free(4)之后
gdb-peda$ x/4gx 0x603000+0x3f0+0x3f0+0x3f0 0x603bd0: 0x4343434343434343 0x00000000000003f1 <-p4 0x603be0: 0x00007ffff7dd47b8 0x00007ffff7dd47b8
将p2的size改写
gdb-peda$ x/4gx 0x603000+0x3f0 0x6033f0: 0x4141414141414141 0x00000000000007e1 <-p2 0x603400: 0x4242424242424242 0x4242424242424242
再free(p2)
gdb-peda$ x/4gx 0x603000+0x3f0 0x6033f0: 0x4141414141414141 0x0000000000000bd1 <-p2 0x603400: 0x00007ffff7dd47b8 0x00007ffff7dd47b8
malloc(p6),此时p6和p2重叠了
gdb-peda$ x/4gx 0x603400-0x10 0x6033f0: 0x4141414141414141 0x00000000000007e1 <-p2 p6 0x603400: 0x00007ffff7dd4d98 0x00007ffff7dd4d98
p6写入东西之后
gdb-peda$ x/4gx 0x603400-0x10 0x6033f0: 0x4141414141414141 0x00000000000007e1 <-p2 p6 0x603400: 0x4646464646464646 0x4646464646464646
然后数据就覆盖到了p3里
#### 堆块重用机制
在chunk结构的学习中我们已经了解到,presize字段仅在前一个堆块是空闲时才有意义,也就是说,当前一个堆块是inuse态时,presize是可有可无的。考虑到这一点,libc采用了一种机制:当一个堆块是inuse态时,它会把下一个堆块的presize字段也作为自己的用户区,这样就可以节省内存空间,这种把presize字段在pre_chunk非空闲时用作pre_chunk的数据区的处理机制就是堆块重用。
**然而,并不是所有情况下都会使用堆块重用!这也是今天要讲的要点:**
我们知道,堆块分配时,它的大小要进行内存对齐,32位操作系统中,会以8字节进行对齐(即堆块的大小必须是8字节的整数倍),而64位操作系统中,会以16字节进行对齐(即堆块的大小必须是16字节的整数倍)。
而堆块重用只出现在如下情况:申请的内存大小在按照上述规则进行向大取整后,得到的应有大小比原大小大出的值大于等于对齐字节量的一半!
比如64位操作系统中,malloc(0x88),向大取整后是0x90,比原来大出了8个字节,而64位下的对齐字节量是16字节,8字节大于等于16的一半,因此会进行堆块重用:0x88中的最后8字节会存在下一个chunk的presize字段位置。而如果是malloc(0x79),向大取整后是0x80,比原来大出7个字节,小于16的一半,就不会发生堆块重用。
为什么呢?堆块重用的初衷就是节约内存,当符合上述重用条件时,用户申请的大小mod对齐字节量后多出的那块大小是小于等于presize字段长度(如64位下是8字节)的,因此多出的这块小尾巴就正好可以顺便放进presize字段存储,相比来说,如果不重用presize来存,而是继续按16字节对齐,将会产生较大的内存浪费;而当不符合重用条件时,多出来的小尾巴是大于presize长度的,presize就存不下了,而size字段人家自己还有用你又不能拿来占,因此就没法进行堆块重用了。
总结一下堆块重用条件:申请的内存大小在按照上述规则进行向大取整后,得到的应有大小比原大小大出的值>=对齐字节量的一半(presize字段长度). =>也即:申请的内存大小mod对齐字节量<=对齐字节量的一半(presize字段长度).
#### size字段对齐计算方式
当采用了重用时,计算出来的size字段的值是舍弃了“小尾巴”的,即重用的presize字段长度并未算进来!也就是说,无论是否重用,抽象掉计算过程来看,最终得到的size字段值一定是从chunk_head到next_chunk_head间的长度!
**注意**
1. off by one 可以覆盖inuse位:必须在进行了重用的情况下才能实现!(而且是完全的重用)
2. 泄露堆地址时,加减的偏移量应取多少,需要考虑是否有重用!
#### 关于tcache
线程本地缓存:tcache(Thread Local Caching)是libc2.26之后引进的一种新机制.故名思意,是个缓存,与其线程对应;说到缓存,应该想到“优先存取”的特点,事实上也确实如此.它也是个堆表,而且是单链表,其特点和fastbin基本相同,只是更弱,没有首块double free检查也没有size校验,tcache特殊的一点是,它的fd指针是指向用户区的,而不是块首,这是和其他bin的一个重要区别
此外这个东西有一个奇葩的地方,人家别的堆表都待在arena里,但是tcache却存储在堆区;tcache的位置位于堆区的起始处,一共有64个链表,这64个链表的索引结点(也就是链首结点用于存放链表中第一个堆块地址的结点)依次存放在堆区起始处;每个链表最多维护7个堆块
tcache 中新增了两个结构体,分别是 tcache_entry 和 tcache_pertheread_struct
```c
/* We overlay this structure on the user-data portion of a chunk when the chunk is stored in the per-thread cache. */
typedef struct tcache_entry
{
struct tcache_entry *next;
} tcache_entry;
/* There is one of these for each thread, which contains the per-thread cache (hence "tcache_perthread_struct"). Keeping overall size low is mildly important. Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons. */
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;
static __thread tcache_perthread_struct *tcache = NULL;
ida中查看main函数
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
__int64 v3; // ST08_8@1
int v4; // eax@4
v3 = *MK_FP(__FS__, 40LL);
puts_stuff();
qword_202050 = (__int64)calloc(0xA0uLL, 1uLL);// 置零,位置在0x202050
if ( !qword_202050 )
{
puts("init error!");
puts_bybe();
}
while ( 1 )
{
while ( 1 )
{
puts_choice();
v4 = read_choice();
if ( v4 != 2 )
break;
free_morty();
}
if ( v4 > 2 )
{
if ( v4 == 3 )
{
puts_content();
}
else if ( v4 == 4 )
{
puts_bybe();
}
}
else if ( v4 == 1 )
{
malloc_morty();
}
}
}
读取的函数
__int64 __fastcall read_content(_BYTE *a1, int a2)
{
unsigned int v3; // [sp+14h] [bp-Ch]@1
__int64 v4; // [sp+18h] [bp-8h]@1
v4 = *MK_FP(__FS__, 40LL);
v3 = 0;
if ( a2 )
{
while ( 1 )
{
read(0, &a1[v3], 1uLL);
if ( a2 - 1 < v3 || !a1[v3] || a1[v3] == '\n' )// 有截断处理
break;
++v3;
}
a1[v3] = 0;
a1[a2] = 0;
}
else
{
*a1 = 0;
}
return *MK_FP(__FS__, 40LL) ^ v4;
}