我正在尝试让 glibc 检测堆栈粉碎,我使用以下代码:
#include <stdio.h>
#include <string.h>
static const int n = 5;
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("usage: %s stringn", argv[0]);
return -1;
}
printf("%s, len = %dn", argv[1], strlen(argv[1]));
unsigned char a[n][n];
unsigned char * b = a[n - 1];
memcpy(b, argv[1], (strlen(argv[1]) + 1) * sizeof(unsigned char));
return 0;
}
如果 argv[1] 长度大于 5,我希望检测到堆栈粉碎错误,但是,我没有,并且 valgrind 没有检测到错误。我应该更改什么才能得到此错误?(数组 A 必须是二维的)
默认情况下,GCC 仅在您执行特别危险的事情(如 alloca
(或 gets
,如您在注释中提到)或声明大型自动数组时添加代码来检测堆栈粉碎。
如果要为所有功能启用保护,请使用-fstack-protector-all
选项。您还可以使用 -Wstack-protector
请求有关未受保护函数的警告。
似乎 gcc 中决定何时启用堆栈保护的逻辑有点棘手。文档中的第一个注释:
-堆叠保护器
发出额外的代码以检查缓冲区溢出,例如堆栈粉碎攻击。这是通过向具有易受攻击对象的函数添加保护变量来完成的。这包括调用 alloca 的函数,以及缓冲区大于 8 个字节的函数。在进入函数时初始化防护,然后在函数退出时检查。如果防护检查失败,则打印错误消息并退出程序
因此,我们应该期望本地缓冲区小于 8 字节的函数不受保护。例如,这个:
int unprotected() {
char a[5];
strcpy(a, "this is much too long");
return a[0];
}
用gcc -fstack-protector -Wstack-protector
编译,给出一个警告,如
警告:堆栈保护器不保护功能:所有本地阵列的长度都小于 8 字节 [-堆栈保护器]
因此,您可能会认为您的char[5][5]
将受到保护,因为它的长度超过 8 个字节。但是,当我将其编译为汇编程序时,我没有得到任何警告或堆栈保护(您可以在这篇Dr. Dobbs文章中找到要查找的汇编程序)。似乎 gcc 将其视为 5 个缓冲区,每个缓冲区 5 个字节,而不是一个 25 字节的缓冲区。
您可以说服 gcc 启用堆栈保护,方法是向它显示一个大于 8 字节的缓冲区:
void protected(char *arg) {
union {
char dummy[5 * 5];
char a[5][5];
} u;
memcpy(u.a[4], arg, (strlen(arg) + 1));
}
或者简单地使用 -fstack-protector-all
.