0

我正在尝试让 glibc 检测堆栈粉碎,我使用以下代码:

 #include <stdio.h>
 #include <string.h>

 static const int n = 5;

 int main(int argc, char *argv[])
 {
  if (argc != 2)
  {
     printf("usage: %s string\n", argv[0]);
     return -1;
  }
  printf("%s, len = %d\n", 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 必须是二维的)

4

2 回答 2

3

默认情况下,GCC 仅在您执行特别危险的操作alloca(或gets,如您在评论中提到的)或声明大型自动数组时添加代码以检测堆栈粉碎。

如果要为所有功能启用保护,请使用该-fstack-protector-all选项。您还可以使用 请求有关未受保护功能的警告-Wstack-protector

于 2013-10-18T11:06:31.367 回答
1

似乎 gcc 中决定何时启用堆栈保护的逻辑有点棘手。文档中的第一个注释:

-fstack-保护器

发出额外的代码来检查缓冲区溢出,例如堆栈粉碎攻击。这是通过将保护变量添加到具有易受攻击对象的函数来完成的。这包括调用 alloca 的函数,以及缓冲区大于 8 字节的函数。进入函数时初始化守卫,然后在函数退出时检查。如果保护检查失败,则会打印一条错误消息并退出程序

因此,我们应该期望本地缓冲区小于 8 字节的函数不受保护。例如,这个:

int unprotected() {
    char a[5];
    strcpy(a, "this is much too long");
    return a[0];
}

用 编译gcc -fstack-protector -Wstack-protector,给出类似的警告

警告:堆栈保护器未保护功能:所有本地数组的长度都小于 8 字节 [-Wstack-protector]

因此,您可能会认为您char[5][5]将受到保护,因为它的长度超过 8 个字节。但是,当我将它编译到汇编器时,我没有收到任何警告或堆栈保护(您可以在Dobbs 博士的这篇文章中找到要查找的汇编器)。似乎 gcc 将其视为 5 个 5 字节的缓冲区,而不是 25 字节的单个缓冲区。

您可以通过向 gcc 显示大于 8 字节的单个缓冲区来说服 gcc 启用堆栈保护:

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.

于 2013-10-18T11:06:18.430 回答