为什么缓冲区没有被这段代码溢出?
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;
}
这是我正在编译的 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;
}