C 语言:检测到 C 栈溢出攻击

6
我已创建了一个文件,可以根据用户输入的次数无限制地打印“Hello, world”。
#include <stdio.h>
#include <string.h>
int main() {
    char message[10];
    int count, i;

    strcpy(message, "Hello, world!");

    printf("Repeat how many times? ");
    scanf("%d", &count);

    for(i=0; i < count; i++)
        printf("%3d - %s\n", i, message);
}

无论输入什么数字,它总是导致“堆栈崩溃”。以下是程序,请问有人能得出为什么会发生这种情况的结论吗?以下是在检测到堆栈崩溃后发生的“回溯”:
sean@blue:~/programming$ ./a.out
Repeat how many times? 12
  0 - Hello, world!
  1 - Hello, world!
  2 - Hello, world!
  3 - Hello, world!
  4 - Hello, world!
  5 - Hello, world!
  6 - Hello, world!
  7 - Hello, world!
  8 - Hello, world!
  9 - Hello, world!
 10 - Hello, world!
 11 - Hello, world!
*** stack smashing detected ***: ./a.out terminated
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(__fortify_fail+0x45)[0x1f8c75]
/lib/i386-linux-gnu/libc.so.6(+0xe8c27)[0x1f8c27]
./a.out[0x8048524]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x129113]
./a.out[0x80483f1]
======= Memory map: ========
00110000-00288000 r-xp 00000000 08:01 1577912    /lib/i386-linux-gnu/libc-2.13.so
00288000-0028a000 r--p 00178000 08:01 1577912    /lib/i386-linux-gnu/libc-2.13.so
0028a000-0028b000 rw-p 0017a000 08:01 1577912    /lib/i386-linux-gnu/libc-2.13.so
0028b000-0028e000 rw-p 00000000 00:00 0 
0036b000-0036c000 r-xp 00000000 00:00 0          [vdso]
00454000-00470000 r-xp 00000000 08:01 1573818    /lib/i386-linux-gnu/libgcc_s.so.1
00470000-00471000 r--p 0001b000 08:01 1573818    /lib/i386-linux-gnu/libgcc_s.so.1
00471000-00472000 rw-p 0001c000 08:01 1573818    /lib/i386-linux-gnu/libgcc_s.so.1
00e7e000-00e9c000 r-xp 00000000 08:01 1573924    /lib/i386-linux-gnu/ld-2.13.so
00e9c000-00e9d000 r--p 0001d000 08:01 1573924    /lib/i386-linux-gnu/ld-2.13.so
00e9d000-00e9e000 rw-p 0001e000 08:01 1573924    /lib/i386-linux-gnu/ld-2.13.so
08048000-08049000 r-xp 00000000 00:14 3801591    /home/sean/programming/a.out
08049000-0804a000 r--p 00000000 00:14 3801591    /home/sean/programming/a.out
0804a000-0804b000 rw-p 00001000 00:14 3801591    /home/sean/programming/a.out
08a9e000-08abf000 rw-p 00000000 00:00 0          [heap]
b77e8000-b77e9000 rw-p 00000000 00:00 0 
b77fc000-b7800000 rw-p 00000000 00:00 0 
bff87000-bffa8000 rw-p 00000000 00:00 0          [stack]
Aborted

4
你可能会发现,将你所使用的编译器警告级别提高会很有帮助。例如,当我使用带有 -Wall 选项的 gcc 编译器时,它会产生两个警告:"warning: control reaches end of non-void function" 和 "call to __builtin___strcpy_chk will always overflow destination buffer",后者清楚地指出了问题所在。 - DSM
6个回答

44

因为"Hello, world!"超过10个字符...


18

message只能容纳10个字节。你正在复制13个字节的字符串“Hello World!”(如果计算null字符,则为13个字节),这将导致覆盖和破坏堆栈保护器cookie。

Cookie是编译器插入的随机字节,以确保在堆栈上修改返回地址时崩溃,从而防止潜在的缓冲区溢出攻击。

如果你正在使用gcc编译,在实验中,尝试向编译语句添加-fno-stack-protector开关,然后再试一次。程序可能会崩溃(但不会像那样显示错误消息),并且容易受到缓冲区溢出攻击的影响。


注意:OP使用了“Hello, World!”-14个字节和0终止符。 - mattnz
@mattnz 哦,我错过了逗号。算了吧。没关系。我不会再为此编辑帖子 :) - Mehrdad Afshari

4

您的消息数组有10个字符(从0到9),但如果计算“Hello, World!”(不带引号),它有13个字符长。因此,您正在覆盖不属于数组的内存。

作为参考,strcpy()strcat()和大多数其他C字符串函数不检查数组的长度,它们假设您给了足够的空间来工作。

所以,您需要给您的消息数组更多的空间。但需要多少?足以容纳“Hello, world!”再加上一个用于确定字符串结尾的空终止符字符'\0',因此您需要声明一个由14个字符组成的数组。

关于处理字符串和空字符的更深入的解释,我建议查看此页面。虽然它是C++页面,但它涵盖了C和C++的共同点(因为C++基于C)。

此外,正如Pearsonartphoto所说,您可以将数组声明为

char message[] = "Hello, World!";

然而,如果这是为了学校或大学的任务,确保你已经学会以这种方式做,因为有时候你会因为“急于前进”而被扣分。这些问题的想法是教授基本原理,以及某些事物的工作方式和原因,它们可能不是最容易或最有效的方法(你正在遇到的类型的堆栈溢出问题仍然在今天的主要系统中引起问题,因为程序员忘记检查大小等)。


2

您的message数组需要比您复制到其中的字符串多一个字符长度(请记住,您还需要保留隐式的'\0'空终止符)。


1
正如已经说过的那样,“Hello World!”太长了。做以下操作会容易得多。
char message[]="Hello World!";

它将自动选择正确的尺寸。


哎呀...这就是我同时使用太多编程语言的后果... - PearsonArtPhoto

-2

我在这样定义结构体时遇到了问题:

struct data {
...variables...
char text[];
};

这不会给出任何警告,但在我的情况下导致了堆栈溢出错误。我通过替换解决了这个问题。

char text[100];

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接