我试图理解缓冲区溢出。这是我的代码:
#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;
}
buf
数组大小为 5,并用 0es 初始化。所以(空终止)我有四个字符的空间。如果我输入五个字符(例如 stack),我会覆盖空终止字符,printf
应该打印“Hello stacku!”因为后续变量 x
。但事实并非如此。它只是打印“堆栈”。有人可以解释一下原因吗?
最佳答案
简短的解释是,仅仅因为您在“buf”之后的源代码行中声明了“x”,并不意味着编译器将它们放在堆栈中并排放置。对于显示的代码,'x' 根本没有被使用,所以它可能没有被放在任何地方。即使您确实以某种方式使用了“x”(并且它必须是一种防止它被塞入寄存器的方法),编译器也很有可能将它排序为 below“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;
}
因为 struct
的字段 总是按照它们在源代码中出现的顺序排列在内存中。1 原则上有可以在 S.buf
和 S.x
之间填充,但是 char
必须有 1 的对齐要求,所以 ABI 可能不需要.
但即使您那样做,它也不会打印“Hello 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
看看它如何始终打印您键入的内容,但是 x[0]
确实被覆盖了,首先是 NUL,然后是 's'?
(您已经阅读了 Smashing the Stack for Fun and Profit 了吗?您应该阅读。)
1 学究脚注:如果涉及位域,则内存中的域顺序将部分由实现定义。但这对于这个问题的目的并不重要。
关于c - C缓冲区溢出的解释,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52003862/