我正在阅读《 Shellcoder手册》,以了解有关利用和溢出的更多信息。我到达了有关堆溢出的章节。该书提到将一个堆拆分为多个块,每个块包含两个重要信息: br />数据

下图取自Blackhat演示文稿。下面是代码:

    1 #include <stdio.h>
    2 #include <string.h>
    3 #include <stdlib.h>
    4
    5 int
    6 main (int argc, char *argv[]){
    7     char *buf, *buf2;
    8
    9     buf = (char *) malloc(1024);
   10     buf2 = (char *) malloc(1024);
   11
   12     printf("buf=%p\n", buf);
   13     printf("buf2=%p\n", buf2);
   14     strcpy(buf, argv[1]);
   15     strcpy(buf2, argv[2]);
   16     printf("buf=%s\n", buf);
   17     printf("buf2=%s\n", buf2);
   18     free(buf2);
   19     return 0;
   20 }


我在第18行上放置了一个断点,并检查了内存。这是运行以下命令之后的转储:

./basicheap $(python -c 'print("A"*1000 + " " +"XXXXABCDEFGH")')

这是buf1的内存转储:

[0x08048543]> pxw 0x50 @ [fcnvar.local_1ch]-8
0x0804b158  0x00000000 0x00000411 0x41414141 0x41414141  ........AAAAAAAA
0x0804b168  0x41414141 0x41414141 0x41414141 0x41414141  AAAAAAAAAAAAAAAA
0x0804b178  0x41414141 0x41414141 0x41414141 0x41414141  AAAAAAAAAAAAAAAA
0x0804b188  0x41414141 0x41414141 0x41414141 0x41414141  AAAAAAAAAAAAAAAA
0x0804b198  0x41414141 0x41414141 0x41414141 0x41414141  AAAAAAAAAAAAAAAA


这是buf2的内存转储:

[0x08048543]> pxw 0x50 @ [fcnvar.local_20h]-8
0x0804b568  0x00000000 0x00000411 0x58585858 0x44434241  ........XXXXABCD
0x0804b578  0x48474645 0x00000000 0x00000000 0x00000000  EFGH............
0x0804b588  0x00000000 0x00000000 0x00000000 0x00000000  ................
0x0804b598  0x00000000 0x00000000 0x00000000 0x00000000  ................
0x0804b5a8  0x00000000 0x00000000 0x00000000 0x00000000  ................


输出显示0x411为两个块的大小,无论如何都是1024 + 17位。这是两个块的标头中的第一条(也是唯一一条)信息。在Shellcoder中,作者正在尝试演示如何溢出buf1来覆盖buf2的标头信息。

glibc的编写者是否放弃了标头信息中前一个块的大小,还是其他的东西

PS:我使用的构建命令是gcc -no-pie -g basicheap.c -o basicheap

#1 楼

您链接到的图似乎是错误的。前一个块的大小存储在当前块iff中,前一个块是空闲的。

此图像更适合于分配的堆块的外观。

来源:https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/

下面的代码来演示这一点。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main (int argc, char *argv[])
{
    char *buf1, *buf2, *buf3;
    buf1 = (char *) malloc(1024);
    buf2 = (char *) malloc(1024);
    buf3 = (char *) malloc(1024);

    printf("buf1=%p\n", buf1);
    printf("buf2=%p\n", buf2);
    printf("buf3=%p\n", buf3);

    memset(buf1, 'A', 1024);
    memset(buf2, 'B', 1024);
    memset(buf3, 'C', 1024);

    free(buf2);
    return 0;
}


注意:我取了三个块,而不是两个块,否则释放第一个块可以将其合并为顶部块。我们将释放中间块并在执行free调用之前和之后检查块内容。

二进制文件是使用gcc -m32 -g ./heap.c编译的。像往常一样在gdb中调试二进制文件,并在free(buf2)调用处的第20行设置一个断点。 br />


malloc返回指向chunk + 8的指针,因此我们从每个地址中减去了8。此外,我还在上面的图像中突出显示了块大小(堆块的第二个成员)。 Glibc堆块始终对齐为8个字节,这意味着最后三位始终为零。因此,这三个位用于存储其他信息(0x409AM位)。

P转换为二进制时会变成0x409。 />
要获得真正的块大小,我们必须舍弃最后三个位(100 0000 1001 A M),即,我们应仅考虑P。当将其转换为十进制时,我们得到1032。

回到gdb,让我们跳过100 0000 1000调用并再次检查这些块。



块2已被释放。让我们看一下第3块(free)。因此,先前的大小仅在释放块2之后才存储在块3中,而不是在使用中时存储。

我之前指的buf3位代表0x408。当使用上一个块时,将设置此位。由于先前的块现在可用,因此该位设置为0。因此,第二个成员现在还包含等于1032P(而不是PREV_INUSE)。如上图所示,0x408位未设置。希望这能清除您的理解。

评论


太棒了!非常感谢您的努力!

–Solidak
18-09-9在9:22