为什么缓冲区没有被这段代码溢出?

Why the buffer isn't overflowing with this code?

这是我正在编译的 C 代码:

#include <stdio.h>
#include <stdlib.h>

int main(){
long val=0x41414141;
char buf[20];

printf("Correct val's value from 0x41414141 -> 0xdeadbeef!\n");
printf("Here is your chance: ");
scanf("%24s",&buf);

printf("buf: %s\n",buf);
printf("val: 0x%08x\n",val);

if(val==0xdeadbeef)
system("/bin/sh");
else {
printf("WAY OFF!!!!\n");
exit(1);
}

return 0;
}

在这里,如果用户输入 24 个字符长的字符串并更改 val 中的值,我预计 long val 会发生溢出。但即使字符串足够长,它也不会溢出。有人可以解释一下这种行为吗?

我在 macOS 上。这是gcc -v吐出来的:

Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/c++/4.2.1
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin16.0.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

此外,在谷歌搜索了一下之后,我尝试了带有这些标志的 gcc:

gcc -g -fno-stack-protector -D_FORTIFY_SOURCE=0 -o overflow_example overflow_example.c

结果还是一样

此代码是 overthewire 上的 narnia 兵棋推演挑战的一部分。我设法在他们的遥控器 shell 上破解了这个挑战,它的表现符合预期。现在,我正试图在我的本地系统上重现同样的挑战并面临这个问题。请帮忙。

编辑:对于所有大声疾呼 UB 的人:就像我说的,这是 overthewire 上要解决的挑战之一,所以它不能有 UB。有一些博客(这里是我发现的)提供了这个挑战的演练,并对为什么代码的行为方式做出了合理的逻辑解释,我同意。我也明白编译后的二进制文件是平台相关的。 那么,我该怎么做才能在我的本地系统上生成这个可能溢出的二进制文件?

这是未定义的行为,因为 C 函数不检查参数是否对其缓冲区来说太大。

我不太清楚你的意思。

1) 您是否担心您的输入字符串会创建一个太大而无法存储在 long 中的数字。这不会发生,数字会简单地循环。 2)你担心你会读到超出buf范围的内存吗?在 C 中,这将产生未定义的行为,不一定是崩溃。

buf 在栈上(也可以在堆上)所以你可以从 buf 开始的地址继续写入内存。编译器将生成不会为您进行边界检查的代码。因此,如果您超出 20 字节,您最终将开始覆盖不属于您为 buf 预留的内存块的其他内存部分。

显然,变量在您 mac 的堆栈中的布局不同。

将它们包装在一个结构中将确保它们按照您想要的顺序放置。

既然有padding的可能,那我们就关掉吧。对于 gcc,预编译器直接 #pragma pack 控制结构打包。

int main(){
#pragma pack(1)
  struct {
    char buf[20];
    long val=0x41414141;
  } s;
#pragma pack()
  printf("Correct val's value from 0x41414141 -> 0xdeadbeef!\n");
  printf("Here is your chance: ");
  scanf("%24s",&s.buf);

  printf("buf: %s\n",s.buf);
  printf("val: 0x%08x\n",s.val);

  if(s.val==0xdeadbeef)
    system("/bin/sh");
  else {
    printf("WAY OFF!!!!\n");
    exit(1);
  }

  return 0;
}