我在c ++中测试一个简单的缓冲区溢出。该示例是一个测试,如果没有检查,恶意用户可以使用缓冲区溢出覆盖变量。
该示例定义了一个缓冲区,然后定义了一个变量,这意味着应该为缓冲区分配空间,然后为变量分配空间。该示例从cin
读取长度为5的缓冲区,然后检查admin变量是否设置为0以外的其他值,如果是,则用户在概念上获得了管理员访问权限。
#include <iostream>
using namespace std;
int main()
{
char buffer[5];
int admin = 0;
cin>>buffer;
if(strcmp(buffer,"in") == 0)
{
admin = 1;
cout<<"Correct"<<endl;
}
if(admin != 0)
cout << "Access" << endl;
return 0;
}
我有3台机器,1台Windows和2台Linux系统。
当我在Windows(CodeBlocks)上测试它时(逻辑上)
输入超过5个字符溢出并重写admin
变量的字节
现在我的第一个linux系统也可以工作,但只有当我输入13个字符时,这是否与不同的编译器有关,以及它们如何为程序分配内存?
我的第二台linux机器根本无法溢出。它只会在第13个字符后发出转储错误。
为什么他们差异很大?
答案 0 :(得分:1)
正如您所发现的,未定义的行为是未定义的。试图解释它通常不是非常有效。
在这种情况下,几乎可以肯定是由于堆栈和填充字节的排列在编译器/系统之间变化的局部变量之间/之间插入。
答案 1 :(得分:1)
您应该检查反汇编。从那里你会看到恰恰发生了什么。
一般来说,有两件事需要考虑:
编译器完成填充以对齐堆栈变量。
编译器对堆栈变量的相对位置。
第一点:您的数组char buffer[5];
将被填充,因此int admin;
将在堆栈上正确对齐。我希望通常在 x86 或 x64 上填充8个字节,因此要覆盖9个符号。但编译器可能会根据它认为合适的方式做不同的事情。尽管如此,似乎 Windows和Linux机器 x86 (32位)。
第二点:编译器不需要按照声明的顺序将堆栈变量放在堆栈上。在Windows上,第一台Linux机器编译器确实将char buffer[5];
置于int admin;
之下,因此您可以溢出它。在第二台Linux机器上,编译器选择以相反的顺序放置它,因此在写入超出为int admin;
分配的空间之后,您正在破坏main()
调用者的堆栈帧,而不是溢出到char buffer[5];
。
这是我对类似问题的回答的无耻链接 - an example of examining of such overflow。