【问题标题】:stack vs heap overflow detection堆栈与堆溢出检测
【发布时间】:2011-10-26 17:29:41
【问题描述】:

在像 linux 这样的按需分页系统中,我读到的页面可能约为 4k,它通过检查堆栈或堆大小是否超过给每个页面的数量来确保保护。当我创建两个变量时

char *s = (char *) malloc(100);   
char sa[100];

在 for 循环中,我可以写 s[i] = 'c'; 几乎 4000 次,然后才出现内存错误 而使用sa[i] = 'c'; 编辑:对于大于数组大小的任何内容,我都会收到segmentation faultstack smashing 错误。

我可以理解在第一种情况下存在页面错误,并且它看到没有更多的页面被分配给堆,因此内存冲突。但是在第二种情况下,gcc 会在运行时检查所有预分配的变量吗?
编辑:我在下面发布整个代码

int main(int argc,char* argv[]){
char *s = (char *) malloc(20);
char sa[400] = {0};
int i ,count;
printf(" enter the number of chars to write: ");
scanf("%d",&count);
for (i=0;i<count;i++){
printf("%d\n",i);
sa[i] = 'a';
//s[i] = 'a';
}
free(s);

}

【问题讨论】:

  • 从语言上看,这两种情况都是完全未定义的。为实现特定的问题添加了gcc 标签。
  • 仅供参考,绝不保证您只能使用堆栈分配的变量写入数组的长度。我在我的 x86_64 机器上达到了 2880(它在&amp;sa[i] == 0x7ffffffff000 上打开了一个新页面)。
  • 我认为我们应该忽略为什么要写入分配范围之外的数组的问题,对吧?
  • 所以我阅读了有关 gcc 和金丝雀(或保护字节... gcc 是否在每次堆栈增长时检查保护字节覆盖?。
  • @christian-semrau 是的...

标签: c gcc memory heap-memory stack-memory


【解决方案1】:

在许多 32 位操作系统上,堆栈向下增长。您只在数组中使用正索引,因此它取决于您的函数调用的嵌套深度。当您从数组中索引时,您将首先覆盖金丝雀。所以堆栈粉碎错误是第一位的。接下来,您将开始覆盖函数参数和返回地址。如果没有金丝雀,这将导致函数返回跳转到永无止境,通常会产生段错误。并非总是如此,它可能会意外落在有效代码上,即堆栈缓冲区溢出攻击背后的逻辑。

随着您的继续,您最终会越过堆栈顶部写入未分配的页面。那么段错误。在一个嵌套调用很少的小型测试程序中,快速发生的调用需要几千字节。

也可以尝试使用负偏移量。这可以持续一段时间,否则不会导致任何事故,因为您正在写入未分配的堆栈空间。当您写入超过分配的堆栈大小(通常为兆字节)时,就会出现段错误。在 Windows 上,您会触发堆栈保护页面,生成一个以该站点命名的异常。

【讨论】:

  • 操作系统不将堆栈、堆、代码视为不同的段吗?我最初在想堆栈得到一个页面,堆得到一个页面,以便随着它们增长更多的页面被分配。那么堆栈溢出如何覆盖代码部分呢?。
  • 它没有。不知道你为什么得出这个结论。
  • 我在 OS 文本中读到 c 编译器将程序段生成为代码、堆栈、堆全局变量等,但 linux 将代码放入 [用户代码] 段,所有其他数据(堆栈、堆等)在[用户数据]段中,然后使用分页。所以我知道linux不会对堆栈和堆进行分段。但是如果 [user data] 段的初始分配小于要求(例如递归),那么它如何扩展堆栈?。
  • Linux 一直在变化,现在不知道它在做什么。 Windows 使用保留的内存页面,这些页面会因页面错误而变成真实的内存页面。 Linux 有一段时间不支持它,如果现在支持,我不会感到惊讶。这些都不相关,您可能错过了“堆栈向下增长”的开场白。您正在写入已分配的页面。
  • 因为导致段错误的写入发生在检测到金丝雀被踩踏的函数返回之前。关键洞察力正是检查金丝雀的时间。直到函数返回。
猜你喜欢
  • 2011-12-11
  • 2019-05-18
  • 2010-10-19
  • 2017-03-30
  • 2011-06-22
  • 2016-05-15
  • 1970-01-01
  • 1970-01-01
  • 2012-04-15
相关资源
最近更新 更多