有人可以帮助解释具有“堆栈粉碎”一词的回溯输出吗?

时间:2009-04-25 23:01:12

标签: c linux

PLZ解释了运行编程后堆栈粉碎的后续结果,其中我给出的输入远远超过了charachter数组的容量。

    *** 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

请解释以下记忆图的详细信息以及本报告中给出的各种细节的重要性。

修改

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

 int main()

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

感谢。

3 个答案:

答案 0 :(得分:9)

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

我首先要说的是,除非您碰巧知道哪些c指令放在内存的哪一部分,否则它不会给您太多信息。

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

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

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

  1. 你的程序破解了libc,这可能意味着各种各样的事情。
  2. 你用你的代码打破了堆栈。
  3. 我假设你知道你的数组是在调用它的函数的堆栈框架中初始化的,因此,当你按下太多的值时,你就离开了框架并打破了堆栈。

    我希望有所帮助。如果你想了解更多,请问。

答案 1 :(得分:2)

听起来您的程序或libc都是使用buffer overflow protection构建的,并且您想要了解它所倾倒的数据。

取出程序崩溃的地址(0x804845f),从程序的.text段的基地址(0x08048000)中减去它,然后在.map文件中查找生成的偏移量你的计划。

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

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

答案 2 :(得分:1)

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

#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 []或20的大小2倍,这就会启动我的堆栈保护器(gcc / Linux)。我不知道其他系统上各种堆栈保护器有多相似。

到目前为止,每个人都告诉你的是100%准确。我提供这个答案作为补充,以一个非常简短的例子帮助说明如何

使用gcc -Wall -o smash smash.c ..编译,因为你将C(原谅双关语),该语言可以让你破坏任何和所有规则。它会让你犯下谋杀罪,但它不会让你逃脱它(在大多数情况下)。

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

备注:

  • 这并不意味着禁用堆栈保护程序是解决问题的方法:)
  • 堆栈保护器不防止溢出,它们只是保护堆栈,两者之间存在很大差异。