我对从我的 C 代码中获得的这种行为感到既困惑又兴奋。我不明白这到底是怎么回事!在进一步讨论之前,让我们看一下代码-
#include <stdio.h>
int main(){
char string[2];
printf("Enter your string here: ");
gets(string);
printf("%s \n", string);
return 0;
}
很明显- 这里没有什么特别的。实际上,这是我的计算机、数据和网络安全类(class)的作业,我应该在其中演示 BufferOverflow。
直到 13 个字符它都可以正常工作; 15 个或更多章程会导致所需的 BufferOverflow。 当我正好输入 14 个字符时,灾难发生了:代码开始表现得像一个循环!就像 main
函数被一次又一次地调用-
我正在使用 CodeBlocks-16.01 和 GNU GCC 编译器。我还在 TutorialsPoint 上执行了代码但那里没有遇到这个问题。
最佳答案
您的代码会产生缓冲区溢出——真实的。越过 string
的末尾可以覆盖 main
完成后应该返回的 [在堆栈上] 的返回地址。
如果选择正确,它可以循环回到 main
或跳转到内存中的任何地方。它实际做什么取决于编译器、链接器、加载器、程序加载的地址。
并且,输入的字符串的值(即)一些字符串会崩溃,其他的可能会循环,有些可能会产生愚蠢的结果,但不会循环。一个字符串可能在给定环境中执行 X 行为,而在另一个环境中执行 Y 行为。不同的字符串可能会反转这些结果。
您真正想做的是演示(即模拟)缓冲区溢出,而不做任何会使您的程序崩溃的事情。
这是一个安全的方法:
#include <stdio.h>
#define SEED 0xFF // sentinel value
// NOTE: using a struct guarantees that over will appear directly after string
// (i.e.) over is higher in memory than string
struct buffer {
char string[4]; // buffer that can overflow
unsigned char over[80]; // safe place for the overflow
};
int
main(void)
{
struct buffer buf;
int idx;
int over;
// prefill the "overflow detection buffer" with a sentinel value (e.g. one
// that can't be input via fgets [under normal circumstances])
for (idx = 0; idx < sizeof(buf.over); ++idx)
buf.over[idx] = SEED;
printf("Enter your string here: ");
fflush(stdout);
// NOTE: this fgets will never _really_ cause any harm -- the "10" slop
// factor guarantees this
fgets(buf.string,sizeof(buf) - 10,stdin);
// overflow is anything that ran past string into over
over = 0;
for (idx = 0; idx < sizeof(buf.over); ++idx) {
if (buf.over[idx] != SEED) {
over = 1;
break;
}
}
if (over)
printf("buffer overflowed\n");
else
printf("buffer did not overflow\n");
return 0;
}
更新:
you should specifically admonish against the use of
gets
通常,我会的。由于这个问题的特殊性,我对此持观望态度。
(even though he wants a buffer overflow, you should add why he may very well get one he doesn't want using the removed
gets
function)
IMO,这是通过在我的示例代码中使用 fgets
来暗示的,但可能不是特别推断。所以,很公平......
在我的示例代码中,使用 gets(buf.string)
而不是 fgets
可以/将产生相同的 [desired] 效果。但是,这仍然不安全,因为读取的长度仍然没有限制。它可以超过总结构长度 sizeof(string) + sizeof(over)
并像以前一样产生真正的缓冲区溢出。
由于您试图导致缓冲区溢出,因此使用gets
进行编码更容易,但您会遇到不希望的行为。
[正如其他人指出的那样] gets
正因为这个原因被弃用。如果您只想正常使用,请将 gets(string)
替换为 fgets(string,sizeof(string),stdin)
所以,永远不要 使用gets
并始终使用 fgets
。
关于没有循环的 C 程序意外地表现得像一个循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38559949/