【问题标题】:Can someone assist in explaining the backtrace output having the term 'stack smash'?有人可以帮助解释具有术语“堆栈粉碎”的回溯输出吗?
【发布时间】:2009-04-25 23:01:11
【问题描述】:

请解释运行程序后堆栈粉碎的以下结果,其中我提供的输入远大于字符数组的容量。

    *** stack smashing detected ***: ./a.out terminated
             ======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x48)[0xb7f856d8]
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x0)[0xb7f85690]
./a.out[0x804845f]
    [0x666a6473]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:07 91312      /home/mawia/a.out
08049000-0804a000 r--p 00000000 08:07 91312      /home/mawia/a.out 
0804a000-0804b000 rw-p 00001000 08:07 91312      /home/mawia/a.out
084cd000-084ee000 rw-p 084cd000 00:00 0          [heap]
b7e6d000-b7e7a000 r-xp 00000000 08:07 221205     /lib/libgcc_s.so.1
b7e7a000-b7e7b000 r--p 0000c000 08:07 221205     /lib/libgcc_s.so.1
b7e7b000-b7e7c000 rw-p 0000d000 08:07 221205     /lib/libgcc_s.so.1
b7e8a000-b7e8b000 rw-p b7e8a000 00:00 0 
b7e8b000-b7fe3000 r-xp 00000000 08:07 238955     /lib/tls/i686/cmov/libc-2.8.90.so
b7fe3000-b7fe5000 r--p 00158000 08:07 238955     /lib/tls/i686/cmov/libc-2.8.90.so
b7fe5000-b7fe6000 rw-p 0015a000 08:07 238955     /lib/tls/i686/cmov/libc-2.8.90.so
b7fe6000-b7fe9000 rw-p b7fe6000 00:00 0 
b7ff6000-b7ff9000 rw-p b7ff6000 00:00 0 
b7ff9000-b8013000 r-xp 00000000 08:07 221196     /lib/ld-2.8.90.so
b8013000-b8014000 r-xp b8013000 00:00 0          [vdso]
b8014000-b8015000 r--p 0001a000 08:07 221196     /lib/ld-2.8.90.so
b8015000-b8016000 rw-p 0001b000 08:07 221196     /lib/ld-2.8.90.so
bfd00000-bfd15000 rw-p bffeb000 00:00 0          [stack]
Aborted

请解释以下内存映射的细节以及本报告中给出的各种细节的意义。

编辑

代码只是输入一个字符串。我故意输入大小大于我定义的字符数组的长度的字符串以产生堆栈粉碎。 代码:

 int main()

 {
  int a;
  char s[10];
  scanf("%s",s);
  return 0;
 }

谢谢。

【问题讨论】:

  • 你贴的没用,贴出你的源码吧。
  • 我可以从回溯中唯一知道的是您没有使用兼容 VDSO(这与这个问题完全无关)。除此之外,如果没有代码的链接(或发布),输出将毫无用处。但是,请不要投票结束这个问题,这是一个有效的问题,只有 17 小时,有些人需要几天时间根据提供的答案更新他们的问题。
  • 我还想到您可能正在使用、返回和修改静态分配的数组..但是我的答案应该足够通用,让您可以在自己的代码中找到相同的模式..但是 functionb( ) 可能会破坏 functiona() 的堆栈,如果这确实是你在做的。
  • 考虑打开一个关于如何读取内存映射的新问题,可能链接到这个问题。或者,考虑更改此问题的标题。

标签: c linux


【解决方案1】:

编辑:只需重新阅读问题的标题即可。你想知道为什么它被称为 Stack Smash。当您调用函数时,在 C 中创建的数组会为您的所有局部变量、函数的参数和函数的返回地址生成一个框架。该帧是在堆栈上制作的,称为堆栈帧;听起来很公平。这个堆栈帧应该只属于那个函数,并且应该与它周围的其他堆栈帧有边界;如果它可以改变其他堆栈帧,后果可能是可怕的。它会打破整个“功能有自己的范围”的想法。因此,因为你的数组是一个局部变量,所以它被放置在那个堆栈帧中,当你在其中放置太多信息时,你一直在写,直到你到达堆栈帧的边界,然后你继续前进,C 将让你这样做。它设定了界限,让你随意打破它们。这种超出框架边界的行为被称为“粉碎”堆栈,因为您直接在其他重要数据上运行。粉碎堆栈是在不应该写入的地方破坏堆栈数据。

首先我应该说它不会给你太多信息,除非你碰巧知道哪些 c 指令被放置在内存的哪个部分。

Backtrace 告诉您失败之前正在运行的代码;即你的程序在你溢出的数组上调用了 libc 代码。

内存映射告诉您内存的哪些部分专用于什么,例如您的程序在哪里,它调用的库在哪里,堆在哪里以及堆栈在哪里。它还为您提供了那些内存位置 rwxp(读、写、可执行、PROC_STACK)的权限,尽管我不确定 PROC_STACK 位。

基本上,除非您知道程序在内存中的映射,否则这是无用的信息。您也可以使用更有用的调试器。这告诉你一些事情:

  1. 您的程序中断了 libc,这可能意味着多种情况。
  2. 您的代码破坏了堆栈。

我假设您知道您的数组是在调用它的函数的堆栈帧中初始化的,因此,当您压入太多值时,您会跳出帧并破坏堆栈。

我希望这会有所帮助。如果您想了解更多信息,请询问。

【讨论】:

  • 哦,非常感谢您的回复。我知道堆栈被破坏了。我想知道如何解释这些内存映射并增强对程序在内存中的模式和位置​​的理解。请解释或提供一些链接以了解我们程序的内存映射。我知道堆、数据、文本和堆栈段。我想知道它们在内存中的排列方式。谢谢回复!
【解决方案2】:

听起来您的程序或 libc 是使用 buffer overflow protection 构建的,您想了解它转储的数据。

获取程序崩溃的地址 (0x804845f),从程序的 .text 段的基地址 (0x08048000) 中减去它,然后在程序的 .map 文件中查找结果偏移量。

如果您没有.map 文件,请编辑您的 Makefile 以传递启用映射文件生成的链接器选项(这取决于您使用的编译器/链接器)。以下是 GCC 的操作方法:-Wl,-Map,output.map

您还可以使用调试器查找符号地址。

【讨论】:

    【解决方案3】:

    您似乎无法(出于某种原因)发布您的代码。所以,这里有一些代码会触发相同的行为。您可以使用它来帮助确定您自己的程序中哪里出了问题:

    #include <stdio.h>
    
    /* Feel free to edit this snippet, I wrote it in a hurry and it smells
     * like feet -- Tinkertim */
    
    int main(void)
    {
            char buff[10];
            unsigned int i;
            int n;
    
            printf("Stack protector complains in ");
    
            for (i = 0, n = 10 * sizeof(buff); i < n; i++, n--) {
                    buff[i] = 'c';
                    printf("%d, ", n);
            }
    
            /* This should not be reached if the stack protector is
             * enabled */
    
            printf("\nLook ma, no stack protector!\n");
    
            return 0;
    }
    

    一旦我尝试写入 buff[] 大小的 2 倍或 20,这就会触发我的堆栈保护器(gcc / Linux)。我不知道其他系统上的各种堆栈保护器有多相似。

    到目前为止,每个人都告诉您的内容是 100% 准确的。我提供此答案作为补充,以帮助说明 它是如何通过一个非常简短的示例发生的。

    用 gcc -Wall -o crushsmash.c 编译 .. 就像 C 语言一样(请原谅双关语),这种语言可以让你打破所有规则。它会让你犯下谋杀罪,但不会让你逍遥法外(在大多数情况下)。

    尝试(再次)编译它,但添加编译器标志 -fno-stack-protector 以查看由溢出引起的未定义行为。

    注意事项:

    • 这并不意味着禁用堆栈保护器可以解决您的问题:)
    • 堆栈保护器不防止溢出,它们只是保护堆栈,两者之间有很大的区别。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-02
      • 1970-01-01
      • 1970-01-01
      • 2017-08-13
      • 2011-11-18
      相关资源
      最近更新 更多