栈溢出分析-stackoverfow-Segmentation fault: 11

66 篇文章 0 订阅
36 篇文章 0 订阅

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

前言:

stack:
栈,也常常称作堆栈;

stack vs heap:
栈(堆栈)与堆,从stack翻译来看一叠,一摞,一堆,感觉和heap翻译差不多,不过heap表达的是凌乱的一堆,stack表达的也是一堆,但是一层层叠放的、一摞一摞的,有规律的一堆:-)
使用时也确实和他们的翻译很新近,stack是一层层的,heap呢?具体分配随不同程序各不相同,随逻辑分支不同也会各不相同,确实有点零乱呢。

stack overflow
堆栈溢出,这个可是一个非常常见的错误。大名鼎鼎的stackoverflow网站,可是一个查找程序问题的好去处。也侧面说明了stack overflow错误真的很有代表性啊。

触发stack overflow

什么方法可以触发overflow呢?对于overflow来说,溢出了;如果整体是杯子的话,满溢的情况可以算作溢出了;从局部角度看,一层层函数杯子的杯子山,一个杯子漏到另一个杯子也可以算作溢出了。基于这两个方向可以触发溢出。

对于stack而言,通常程序预留的空间是有限的,常见缺省2M/8M/…,不同系统编译程序缺省可能不同吧,想从满了的方式溢出的话,可以在栈里多放些东西让它溢出。想从局部漏了/损坏的角度让它异常溢出,那就是局部用超了,或是把程序的堆栈破坏了,从而让它溢出。

空间满溢

从满的方式让它溢出,就是把占用stack的东西多放一些,已知的话

  1. 全局变量是不占stack的,编译程序时初始化的全局变量放在.data段,未初始化的全局变量方法.bss段。
  2. 编译出的代码也是不占stack的,放入在.code段里。
  3. new/malloc分配空间,是从heap上分配的。(new repalcement方式除外)
  4. 局部变量是初始化放在stack里面的,占用stack空间。
  5. 函数参数是放在stack里面的,占用stack空间。
  6. 函数调用时上层代码指针eip是必须要放入stack里面的。
  7. 函数调用时堆栈检查/gs校验信息是要放入stack里面的。
  8. 等等
    总体来看,主要两块编码可控制部分占用堆栈:一块是局部变量初始化占用空间,一块是函数调用占用空间。

例子1:局部变量占用空间过大导致溢出

#include <stdio.h>
int main()
{
    int a[2*1024*1024] = {0};
    printf("length of a: %zd\n", sizeof(a));
    return 0;
}

上面程序在mac-shell上执行报错:
Segmentation fault: 11

例子2:程序调用过多导致溢出

#include <stdio.h>
void subfunc(int n)
{
    if (n > 0)
    {
        subfunc(n-1);
    }
}
int main()
{
    int n = 300000;
    printf("call subfunc levels\n", n);
    subfunc(n);
    return 0;
}

上面程序在mac-shell上执行报错:
call subfunc levels: 300000
Segmentation fault: 11

stack异常溢出

stack异常溢出算是一种局部异常溢出,它代表的是某个函数使用栈超出了编译器预先分配的范围,影响到了上层函数的分配空间内容,也或者把堆栈给破坏掉了,上层堆栈例如代码指针eip信息给改了。

在当前编译器里,通常已经不在保存上层函数的栈指针了,而是使用编译时预先计算函数要使用的栈空间大小,进入函数后,调整偏移量来预留出函数使用的栈空间。
这样就意味着函数预留栈空间是编译时计算好了的,不能用超啊,超了就算溢出了。
使用vs时,编译参数里的/gs检查就是来查“超员”的,在分配给函数的空间地址靠近栈低一侧最近的地方记个标记,一会执行完函数时检查这个标记是否被改掉了,改掉了说明函数肯定就用超空间了。这个标记主要是检查不要向栈低用超,毕竟栈低是已分配的部分,已分配给别人的用超,肯定要管的;栈顶用超是不被检查的,毕竟还没分配,一会函数退出时就丢掉了。

堆栈指向溢出的话,通常就是堆栈被破坏导致的溢出,常见的错误例如有/gs检查堆栈溢出,也有代码指针指向错误类的。

栈的空间分配:从高地址向低地址分配,意味着最开始的main函数在高地址,之后函数依次向低地址进行分配使用;函数向高地址用的超出函数分配地址范围,就会形成踩踏,踩踏了上层函数的堆栈空间,形成堆栈踩踏。
低地址… … … 高地址
栈顶 … … … … 栈低

也写一个简单的局部溢出例子吧:

#include <stdio.h>
void testFunc()
{
	int* p1 = NULL;
	int** pp1 = &p1;
	for (int i=3; i<8; i++){
	   *(pp1 + i) = 0;
	}
}
int main()
{
   testFunc();
}

执行结果:
Segmentation fault: 11

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

春夜喜雨

稀罕你的喜欢!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值