堆栈结构浅析

0x00 堆栈结构

   堆和栈是一种计算机科学中常用的抽象数据类型,虽说都是数据存储结构,但是却是两种不同的结构。

0x01 数据结构和内存空间中对堆栈不同理解

 内存空间中:

  一般的,系统会划分出两种不同的内存空间,也就是堆和栈。

  在这其中,栈是有结构的,每个区块按照一定的次序存放,可以明确知道每个区块的大小和地址。而堆则不是这样的,它没有结构,数据时随意存放的,杂乱无章。因而寻址速度当然是栈的快,使用栈的效率也是很快的。还有一方面就是,一般的话,每个线程分配一个栈,每个进程分配一个堆,也就是一个栈对应一个线程,一个堆对应好几个线程。由上面的这些特性决定的是一般局部变量和一些空间确定的数据存放于栈中,其余的都存放在堆里。

 数据结构中:

  在数据结构中,栈就是一种存储数据的结构而已,特点是FILO,也就是先进后出。栈的结构就像是一个箱子,先来的数据被压入是肯定是在箱底,后来的肯定是在上层的,这样一来,当需要出去的时候只能上面的先出,箱底的后出,这样就是先进后出了。

  而堆在数据结构中就是一种特殊的完全二叉树,也叫优先队列。特点就是所有的父节点的值大于或小于两个子节点的值,分别称为大顶堆和小顶堆。

0x02 栈溢出

  栈溢出就是缓冲区溢出的一种。栈溢出就是想缓冲区写入超过缓冲区本身长度的数据,让缓冲区无法容纳以致缓冲区以外的存储单元被改写。缓冲区长度一般与用户自己定义的缓冲变量的类型有关。

  栈溢出可以将原来栈中的数据覆盖,或者将恶意代码插入到栈帧数据中,并以此达到自己的特殊目的。

0x03 栈溢出攻击

  一般攻击是在远程通过网络实施栈溢出攻击,攻击报文通过网络找到特定的服务器主机,并且在服务器主机进行函数调用时进行恶意破坏行为。典型的为以下的三种:修改相邻数据,修改函数的返回值和修改SEH。

  代码如下:

    int vulnerable_func(char * input)
    {
      int isSafe = 0;
      char vulnerable_var[16];
      strcpy(vulnerable_var,input);
      if(isSafe == 0)
      {
        printf("Check error!\n");
        return 1;
      }
      printf("Check Pass!\n");
        return 0;
    }
    int main(int argc,char* argv[])
    {
      vulnerable_func("Hello World!\n");
      vulnerable_func("Hello World!AAAAAAAAAAAAAAA");
      return 0;
    }

(1)修改相邻数据

   若以上代码是服务器的安全模块的一部分,输入的是用户信息,isSafe是服务器中存放的判断用户的关键数据,就可以利用栈溢出来修改isSafe变量,从而造成一次非法用户绕过安全检查的成功入侵,但这种情况很少出现。

(2)修改函数返回值

  相较于前一种方式,这是一种更常见的攻击。若当输入的数据够大,就足以覆盖函数的返回地址,再者的话如果输入精心构造的shellcode,就能够精确地修改函数返回地址,使得程序直接就跳到shellcode中去执行其中的函数,这样也就是可以获得进程对应的权限进行远程植入自己想要执行的函数,达到破坏或者利用目的。

(3)修改SEH(Structured Exception Handling)

  其实这还是一种修改函数返回地址的方式,先使用溢出导致函数出错,调用异常处理函数,接着就可再利用溢出来覆盖异常处理函数的地址直接跳转到shellcode里面运行,进而达到自己目的。由于比较高效,它也成为栈溢出的经典攻击方法。

0x04 栈溢出的防护

  主要是有两个方面:一个是在编写程序的时候就进行提示,进行及时的预防,如微软在vs系列中添加栈溢出检查的编译选项;二是进行防御,在操作系统下增加一些安全机制进行防御,如Windows系统中添加了SEHOP来阻止攻击者通过修改SEH来利用漏洞。