在程序返回的一条语句堆栈项目处,用新函数的起始地址覆盖,将会跳转到执行新函数。
现在系统对这个行为做了判断,已经无法实施这类攻击或技巧。
1,测试代码
#include <stdio.h>
void cc()
{printf("I am cc( )\n");
}void bb(unsigned long int * x)
{x[7] = (unsigned long int)cc;printf("I am bb(...)\n");
}void aa(int y)
{unsigned long int a[6] = {0,1,2,3,4,5};printf("I am aa(...)\n");bb(a);
}int main()
{int z=100;aa(z);printf("cc = %p\n", cc);return 0;
}
2,运行效果
下图可见,并不是在修改堆栈时terminated,而是在返回时检测到了 stack smashing
3,原理分析
3.1如何防止 stack smash
"stack smashing detected"错误通常是由于缓冲区溢出(buffer overflow)导致的。这种错误会破坏程序的堆栈结构,被系统检测到并终止程序以防止安全漏洞。
要解决这个问题,您可以考虑以下几点:
1. 检查代码中的数组边界:确保在使用数组时不会超出其边界,避免写入超出数组大小的数据。
2. 使用安全的字符串处理函数:避免使用不安全的字符串处理函数(如strcpy、strcat等),改用安全的函数(如strncpy、strncat等)来处理字符串。
3. 使用堆内存分配:考虑使用动态内存分配(如malloc、calloc、realloc等)来避免栈溢出问题。
通过检查代码中的缓冲区操作和内存访问,可以帮助您解决"stack smashing detected"错误。
3.2 系统检测 stack smash 的原理
系统通过一种叫做"Stack Protector"的机制来检测stack smashing。这个机制在编译时会向程序中插入一些额外的代码,用于保护函数的返回地址和栈帧结构不被破坏。
具体来说,Stack Protector会在栈帧中插入一个特殊的"canary value"(金丝雀值),然后在函数返回时检查这个值是否被修改。如果检测到"canary value"被修改,系统就会认为发生了stack smashing,并终止程序的执行。
这种机制可以有效防止缓冲区溢出攻击,提高程序的安全性。
3.3,如何让系统放弃检测 stack smash
要阻止系统进行stack smashing检测,您可以在编译时禁用Stack Protector机制。具体取决于您使用的编译器,以下是一些常见编译器的选项:
- 对于GCC编译器,您可以使用-fno-stack-protector选项来禁用Stack Protector机制。
- 对于Clang编译器,您可以使用-fno-stack-protector选项来禁用Stack Protector机制。
- 对于MSVC编译器,您可以使用/GS-选项来禁用堆栈保护。
通过在编译时使用这些选项,您可以阻止系统进行stack smashing检测。请注意,禁用Stack Protector可能会降低程序的安全性,因此请谨慎使用。
测试放弃检测实验:
编译方式:
$ gcc hello_func_stack.c -fno-stack-protector
由于修改了堆栈中返回后第一条指令的地址,所以函数 cc() 被调用,