栈溢出原理
前言:栈
- 栈是一种LIFO的数据结构。
- 应用程序有一到多个用户态栈。
- 栈自底向上增长,由指令PUSH和POP引起其动态变化。
- 局部变量布局在栈中。
- 调用函数时参数由栈传递,返回地址也存储于栈中。
- 函数调用上下文与局部变量共同组成了栈帧——Stack Frame.
栈帧 = 局部变量 + 函数调用上下文 栈帧实际上只是一个通俗的说法,关于栈帧的上下界历来有两种说法,一曰以EBP和ESP之间的栈空间视为栈帧,这也是主流说法;一曰以调用参数和ESP之间的栈空间视为栈帧,我个人更倾向于这种说法,因为它便于理解。
函数调用过程中栈的变化
一、栈溢出原理
栈溢出是缓冲区溢出中的一种。函数的局部变量通常保存在栈上。如果这些缓冲区发生溢出,就是栈溢出。最经典的栈溢出利用方式是覆盖函数的返回地址,以达到劫持程序控制流的目的。
x86构架中一般使用指令call调用一个函数,并使用指令ret返回。CPU在执行call指令时,会先将当前call指令的下一条指令的地址入栈,再跳转到被调用函数。当被调用函数需要返回时,只需要执行ret指令。CPU会出栈栈顶的地址并赋值给EIP寄存器。这个用来告诉被调用函数自己应该返回到调用函数什么位置的地址被称为返回地址。理想情况下,取出的地址就是之前调用call存入的地址。这样程序可以返回到父函数继续执行了。编译器会始终保证即使子函数使用了栈并修改了栈顶的位置,也会在函数返回前将栈顶恢复到刚进入函数时候的状态,从而保证取到的返回地址不会出错。
二、栈保护技术
栈溢出利用难度很低,危害巨大。为了缓解栈溢出带来的日益严重的安全问题,编译器开发者们引入Canary机制来检测栈溢出攻击。
Canary中文译为金丝雀。以前矿工进入矿井时都会随身带一只金丝雀,通过观察金丝雀的状态来判断氧气浓度等情况。Canary保护的机制与此类似,通过在栈保存rbp的位置前插入一段随机数,这样如果攻击者利用栈溢出漏洞覆盖返回地址,也会把Canary一起覆盖。编译器会在函数ret指令前添加一段会检查Canary的值是否被改写的代码。如果被改写,则直接抛出异常,中断程序,从而阻止攻击发生
三、常发生栈溢出的危险函数
- 输入:gets(),直接读取一行,到换行符’\n’为止,同时’\n’被转换为’\x00’;scanf(),格式化字符串中的%s不会检查长度;vscanf(),同上。
- 输出:sprintf(),将格式化后的内容写入缓冲区中,但是不检查缓冲区长度
- 字符串:strcpy(),遇到’\x00’停止,不会检查长度,经常容易出现单字节写0(off by one)溢出;strcat(),同上。
四、可利用的栈溢出覆盖位置
可利用的栈溢出覆盖位置通常有3种:
- 覆盖函数返回地址,之前的例子都是通过覆盖返回地址控制程序。
- 覆盖栈上所保存的BP寄存器的值。函数被调用时会先保存栈现场,返回时再恢复,具体操作如下(以x64程序为例)。调用时:
返回时:如果栈上的BP值被覆盖,那么函数返回后,主调函数的BP值会被改变,主调函数返回指行ret时,SP不会指向原来的返回地址位置,而是被修改后的BP位置。 3. 根据现实执行情况,覆盖特定的变量或地址的内容,可能导致一些逻辑漏洞的出现。
总结
提示:这里对文章进行总结: 例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
|