为啥粉碎后没有立即出现“检测到堆栈粉碎”?
Posted
技术标签:
【中文标题】为啥粉碎后没有立即出现“检测到堆栈粉碎”?【英文标题】:Why does "stack smashing detected" not appear immediately after smashing?为什么粉碎后没有立即出现“检测到堆栈粉碎”? 【发布时间】:2014-03-27 20:06:44 【问题描述】:我了解“检测到堆栈粉碎”是什么意思。这里已经有很多关于这个的问题。但是我没有找到以下问题的答案。取C代码
int main(int argc, char **args)
char puffer[5];
strcpy(puffer, *++args);
printf("%s\n",puffer);
return EXIT_SUCCESS;
当我在 Ubuntu 13.10 中使用 gcc 4.8.1 编译时,./buffer 123456789
会引发预期的错误 stack smashing detected
。但是为什么./buffer 12345678
没有引发错误?我希望每个超过 5 个字符的字符串都会引发错误。
【问题讨论】:
你可能真的想要-fmudflap
或 Valgrind。
【参考方案1】:
理论上你是对的,这就是它应该表现的方式。当您的程序使用超过 5 个字节时,这可能会导致未定义的行为。但出于各种性能原因,堆栈指针通常是aligned to certain boundaries。对齐因架构而异。因此,对于大于 5 的每个输入,您都不会看到此问题。
您的程序的反汇编如下所示。查看sub $0x20,%rsp
指令,该指令在堆栈上为此函数分配了 16 字节的内存。
(gdb) disassemble main
Dump of assembler code for function main(int, char**):
0x00000000004008b0 <+0>: push %rbp
0x00000000004008b1 <+1>: mov %rsp,%rbp
=> 0x00000000004008b4 <+4>: sub $0x20,%rsp
0x00000000004008b8 <+8>: mov %edi,-0x14(%rbp)
0x00000000004008bb <+11>: mov %rsi,-0x20(%rbp)
0x00000000004008bf <+15>: mov %fs:0x28,%rax
0x00000000004008c8 <+24>: mov %rax,-0x8(%rbp)
0x00000000004008cc <+28>: xor %eax,%eax
0x00000000004008ce <+30>: addq $0x8,-0x20(%rbp)
0x00000000004008d3 <+35>: mov -0x20(%rbp),%rax
0x00000000004008d7 <+39>: mov (%rax),%rdx
0x00000000004008da <+42>: lea -0x10(%rbp),%rax
0x00000000004008de <+46>: mov %rdx,%rsi
0x00000000004008e1 <+49>: mov %rax,%rdi
0x00000000004008e4 <+52>: callq 0x400770 <strcpy@plt>
0x00000000004008e9 <+57>: lea -0x10(%rbp),%rax
0x00000000004008ed <+61>: mov %rax,%rdi
0x00000000004008f0 <+64>: callq 0x400710 <puts@plt>
0x00000000004008f5 <+69>: mov $0x0,%eax
0x00000000004008fa <+74>: mov -0x8(%rbp),%rcx
0x00000000004008fe <+78>: xor %fs:0x28,%rcx
0x0000000000400907 <+87>: je 0x400918 <main(int, char**)+104>
0x0000000000400909 <+89>: jmp 0x400913 <main(int, char**)+99>
0x000000000040090b <+91>: mov %rax,%rdi
0x000000000040090e <+94>: callq 0x400790 <_Unwind_Resume@plt>
0x0000000000400913 <+99>: callq 0x400760 <__stack_chk_fail@plt>
0x0000000000400918 <+104>: leaveq
0x0000000000400919 <+105>: retq
【讨论】:
是的,SSE2 指令的 16 字节堆栈对齐。【参考方案2】:编译器实现了不同的堆栈粉碎保护,但通常为了防止堆栈溢出利用,金丝雀被放在保存的帧指针和返回地址之前。金丝雀在这里是为了保护保存的帧指针和返回地址的覆盖。这里基本上是编译器在缓冲区末尾和帧指针/返回地址之间插入了一些填充。
【讨论】:
以上是关于为啥粉碎后没有立即出现“检测到堆栈粉碎”?的主要内容,如果未能解决你的问题,请参考以下文章