C缓冲区溢出的说明

luk*_*991 3 c buffer-overflow

我试着理解缓冲区溢出.这是我的代码:

#include <stdio.h>

int main() 
{
    char buf[5] = { 0 };
    char x = 'u';

    printf("Please enter your name: ");
    gets(buf);

    printf("Hello %s!", buf);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

buf数组的大小为5,并使用0es初始化.所以(使用null终止)我有四个字符的空间.如果我输入五个字符(例如堆栈),我会覆盖空终止字符并printf打印"Hello stacku!" 因为后续的变量x.但事实并非如此.它只是打印"堆栈".有人可以解释一下原因吗?

zwo*_*wol 12

简短的解释是,仅仅因为你在'buf'之后在源代码行上声明'x',这并不意味着编译器将它们放在堆栈上彼此相邻.使用显示的代码,根本不使用'x',所以它可能没有被放到任何地方.即使你确实使用了"X"不知何故(和它必须是,防止它被装进一个寄存器的方式),有一个很好的机会,编译器将其排序如下 "BUF"正是如此,它并没有得到由覆盖代码溢出'buf'.

您可以强制此程序用struct构造覆盖'x' ,例如

#include <stdio.h>

int main() 
{
    struct {
        char buf[5];
        char x[2];
    } S = { { 0 }, { 'u' } };

    printf("Please enter your name: ");
    gets(S.buf);

    printf("Hello %s!\n", S.buf);
    printf("S.x[0] = %02x\n", S.x[0]);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

因为一个领域struct 在内存中的顺序总是奠定了它们出现在源代码.1 原则上可以在S.buf和之间填充S.x,但char必须具有1的对齐要求,因此ABI可能不需要.

但是,即使你做的是,它不会打印"你好stacku!",因为gets总是写入终止NUL.看:

$ ./a.out 
Please enter your name: stac
Hello stac!
S.x[0] = 75

$ ./a.out 
Please enter your name: stack
Hello stack!
S.x[0] = 00

$ ./a.out 
Please enter your name: stacks
Hello stacks!
S.x[0] = 73
Run Code Online (Sandbox Code Playgroud)

看看它总是如何打印你输入的东西,但是x[0]会被覆盖,首先是NUL,然后是's'?

(你是否已经阅读了Smashing the Stack for Fun and Profit?你应该这样做.)


1 pedants脚注:如果涉及位域,则内存中字段的顺序将部分实现定义.但这对于这个问题的目的并不重要.


Ste*_*mit 6

正如另一个答案所指出的那样,在记忆中x不会立即保证buf.但即使它确实如此:gets将覆盖它.记住:gets无法知道目标缓冲区有多大.(这是它的致命缺陷.)它总是写出它读取的整个字符串,加上终止\0.因此,如果x恰好坐在后面buf,那么如果你输入一个五个字符的字符串,printf很可能正确打印它(如你所见),如果你x之后要检查它的值:

printf("x = %d = %c\n", x, x);
Run Code Online (Sandbox Code Playgroud)

它可能会告诉你x现在是0,不是'U'.

以下是内存最初的外观:

     +---+---+---+---+---+
buf: |   |   |   |   |   |
     +---+---+---+---+---+

     +---+
  x: | U |
     +---+
Run Code Online (Sandbox Code Playgroud)

所以输入"stack"后,它看起来像这样:

     +---+---+---+---+---+
buf: | s | t | a | c | k |
     +---+---+---+---+---+

     +---+
  x: |\0 |
     +---+
Run Code Online (Sandbox Code Playgroud)

如果你输入"elephant",它将如下所示:

     +---+---+---+---+---+
buf: | e | l | e | p | h |
     +---+---+---+---+---+

     +---+
  x: | a | n   t  \0
     +---+
Run Code Online (Sandbox Code Playgroud)

不用说,那三个大字n,t\0有可能导致更多的问题.

这就是为什么人们说gets永远不要使用.它不能安全使用.