堆块结构不包含上一节信息

逆向工程 二元分析 雷达2 开发 外壳代码
2021-06-20 01:02:04

我正在阅读Shellcoder's Handbook以了解有关漏洞利用和溢出的更多信息。我读到了堆溢出的章节。这本书提到堆被分成多个块,每个块包含两个重要的信息:

  • 前一个块的大小(如果已分配)
  • 当前块的大小
  • 数据

下图取自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个回答

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

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

在此处输入图片说明

资料来源: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 行设置断点

在此处输入图片说明

当断点命中时,让我们检查三个块。

在此处输入图片说明

malloc返回指向 的指针chunk + 8,因此我们从每个地址中减去 8。此外,我还在上图中突出显示了块大小(堆块的第二个成员)。

然而,块大小实际上不是0x409Glibc 堆块总是对齐到 8 个字节,这意味着最后三位总是零。因此,这三个位用于存储其他信息(AMP位)。

0x409当转换为二进制时出来是100 0000 1001.

在此处输入图片说明

为了获得真正的块大小,我们必须丢弃最后三位 ( A M P),即我们应该只考虑100 0000 1000将其转换为十进制后,我们得到 1032。

在此处输入图片说明

真正的块大小确实是 1032。1024 字节用于我们的数据,另外 8 字节用于前两个成员。

回到 gdb,让我们跳过free调用并再次检查块。

在此处输入图片说明

块 2 已被释放。让我们来看看块 3 ( buf3)。

在此处输入图片说明

块3的第一构件现在包含0x408它是1032在十进制-这是块2的尺寸。因此以前的大小被存储在块3中仅块2已被释放之后,而不是当它在使用中

P我之前提到位代表PREV_INUSE. 当使用前一个块时设置该位。由于前一个块现在是空闲的,这个位被设置为 0。结果第二个成员现在也包含0x408(而不是0x409)它等于100 0000 1000

在此处输入图片说明

如上图所示,该P位未设置。希望,这可以清除您的理解。