有关堆栈粉碎保护和缓冲区溢出的问题
我正在做一些关于缓冲区溢出的研究,我想知道堆栈粉碎保护是如何工作的
我有这个代码:
int main( )
{
char Buf[16];
printf(“Digite o seu nome: ”);
gets(Buf);
printf(“%s”,Buf);
return 0;
}
我用 gcc 编译它
,然后我放入一堆字符来填充缓冲区
首先我放入 16 个字符
$ ./Exemplo1
数字名称:AAAAAAAAAAAAAAAA
奥拉啊啊啊啊啊啊啊啊啊
这是可以的,因为缓冲区大小正确
接下来我尝试 24 个字符
$ ./Exemplo1
数字名称:AAAAAAAAAAAAAAAAAAAAAAAA
奥拉啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊
为什么它仍然有效?
这不应该导致程序终止吗!?
仅当我输入 25 个或更多字符时,它才会终止程序
./示例1
数字名称:AAAAAAAAAAAAAAAAAAAAAAAAA
奥拉啊啊啊啊啊啊啊啊啊啊啊啊啊
* 检测到堆栈粉碎 *: ./Exemplo1 终止
为什么?缓冲区后面不是返回地址的是什么? 我读到的和我认为理解的是它应该有一个金丝雀值,但如果该值发生变化,它应该终止程序,并且将 24 个字符写入缓冲区,它不应该仍然给我一个检测到的堆栈粉碎如果返回地址未更改但金丝雀值已更改。
谢谢。
Im doing some research on buffer overflows and I was wondering how does stack smashing protection works
i have this code:
int main( )
{
char Buf[16];
printf(“Digite o seu nome: ”);
gets(Buf);
printf(“%s”,Buf);
return 0;
}
I compile it with gcc
and then i put in a bunch of chars to fill the buffer
First i put 16 chars
$ ./Exemplo1
Digite o seu nome: AAAAAAAAAAAAAAAA
Ola AAAAAAAAAAAAAAAA
This is ok because the buffer is the right size
Next i try 24 chars
$ ./Exemplo1
Digite o seu nome: AAAAAAAAAAAAAAAAAAAAAAAA
Ola AAAAAAAAAAAAAAAAAAAAAAAA
Why does it still works?
shouldn't this cause the program to terminate!?
It only terminates the program when I put 25 or more chars
./Exemplo1
Digite o seu nome: AAAAAAAAAAAAAAAAAAAAAAAAA
Ola AAAAAAAAAAAAAAAAAAAAAAAAA
* stack smashing detected *: ./Exemplo1 terminated
Why? what's after the buffer that is not the return address?
What I read about and what I think understand is that it should have a canary value, but its supposed to terminate the program if that value has changed and with 24 chars written to the buffer shouldn't it still give me a stack smashing detected even if the return address hasn't changed but the canary value did.
Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
根据我对生成的程序集的简要阅读,我认为会发生以下情况(64 位 Ubuntu 上的 gcc 4.4.5)。
金丝雀是一个双字(8 个字节)。堆栈(包括金丝雀)以 16 字节为增量增长。金丝雀被放置在堆栈帧的末尾。为了同时满足所有三个要求,gcc 可能需要在自动变量和金丝雀之间插入填充。
由于在代码中
Buf
的长度为 16 个字节,因此gcc
在Buf
和 canary 之间放置了 8 个字节的填充(需要使大小堆栈帧的大小是 16 字节的倍数)。这解释了为什么您可以输入最多 24 个字符而不触发堆栈粉碎检测。如果我将
Buf
更改为 8 个字节长,则不再需要任何填充,并且输入 9 个字符会触发保护:显然,这取决于编译器、硬件平台、编译器标志等
。不言而喻,人们不应该期望这种机制是无懈可击的。
如果您想进一步实验,请尝试使用不同的缓冲区大小以及使用/不使用
-fno-stack-protector
来编译代码。如果您使用-S
生成汇编代码,您将能够看到生成的代码在您调整设置时如何变化。From my brief reading of the generated assembly, here is what I think happens (gcc 4.4.5 on 64-bit Ubuntu).
The canary is a double word (8 bytes). The stack -- including the canary -- grows in increments of 16 bytes. The canary is placed right at the end of the stack frame. To satisfy all three requirements at the same time,
gcc
may need to insert padding between your automatic variables and the canary.Since in your code
Buf
is 16 bytes long,gcc
places 8 bytes of padding betweenBuf
and the canary (needed in order to make the size of the stack frame a multiple of 16 bytes). This explains why you can enter up to 24 chars without triggering the stack smashing detection.If I change
Buf
to be 8 bytes long, there's no longer need for any padding, and entering 9 chars triggers the protection:Obviously, this is dependent on the compiler, hardware platform, compiler flags etc.
It also goes without saying that one shouldn't expect this mechanism to be bullet-proof.
If you'd like to experiment further, try compiling your code with different buffer sizes and with/without
-fno-stack-protector
. If you use-S
to generate assembly code, you'll be able to see how the generated code changes as you tweak the settings.编译器不保证数据在堆栈上的组织方式。缓冲区
buf[]
可以分配在关键数据旁边,例如金丝雀、旧堆栈指针和返回地址;或者中间可能有一些额外的空间。在这种情况下,看起来好像里面可能有一些填充。The compiler makes no guarantees about how data will be organized on the stack. The buffer
buf[]
could be allocated right next to critical data like the canary, old stack pointer, and return address; or there could be some extra space in between. In this case, it looks as though there's probably some padding in there.缓冲区的实际大小可能大于您指定的大小,或者缓冲区旁边的堆栈上还有更多内容。通过越过缓冲区边界,您可以写入缓冲区中的额外内存,或者覆盖堆栈的其他内容。这不好,因为它可能会破坏一些局部变量,但它还不会覆盖返回地址。
只有当您写入分配的本地堆栈帧并覆盖当前正在执行的函数的返回地址时,才会发生不好的事情。如果你幸运的话,你的程序就会崩溃。但具体会发生什么很难预见。
The actual size of the buffer may be larger than the size you specify, or there is more on the stack beside the buffer. By going past the buffer boundary, you either write to the extra memory in the buffer, or you overwrite other content of the stack. That is not good, since it might trash some of your local variables, but it won't overwrite the return address yet.
Only if you write past the allocated local stack frame and overwrite the return address of the function that is currently executing, bad things can happen. If you are lucky, your program just crashes. But what will exactly happen is hard to foresee.
目前,大多数编译器都包含堆栈 cookies 或另一种形式的堆栈保护。可能这就是 ProPolice 在工作 http://en.wikipedia.org/wiki /Buffer_overflow_protection#GCC_Stack-Smashing_Protector_.28ProPolice.29
Currently most compilers include stack cookies or another form of stack protection. Probably this is ProPolice at work http://en.wikipedia.org/wiki/Buffer_overflow_protection#GCC_Stack-Smashing_Protector_.28ProPolice.29