当前位置:首页 > 百科

堆栈溢出

堆栈是一个在计算机科学中经常使用的抽象数据类型。堆栈中的物体具有一个特性: 最后一个放入堆栈中的物体总是被最先拿来自出来, 这个特性通常称为后进先出(LIFO)队列。 堆360百科栈中定义了一些操作。 两个最重要的是PUSH和PO州演需科冲P。 PUSH操作在堆栈的顶部加入一 个黑节支老元素。POP操作相反, 在堆栈顶部移去一个元素, 并将堆栈的大小减一。

堆栈溢出的很得书产生是由于过多的函数调用,导致调用堆栈无法容纳这些调用的返回地址,一般在递归中产生。堆栈溢出很可能由无限了功同思递归(Infinite r适安注杨ecursion)产生,但也可能仅仅是过多的堆栈层级。

  • 中文名 堆栈溢出
  • 应用学科· 计算机科学
  • 类别 高级语言
  • 技术 过程和函数
  • 内存 连续内存

堆栈溢出

  堆栈溢出就是不顾堆栈中分配的局部数据块大小,向该数据块写入了过多的数据,导致数据越界,结果覆来自盖了别的数据。 可以理解为 在长超校字符串中嵌入一段代码,并将过程的返回地址覆盖为360百科这段代码的地址,这样当过程返回时,程序就转而开始执行这段自编的代码了定称适互伟保找料木

  比如如下这段程序:

  #include<stdio.h>

  int main()

  {

  char nam茶脸又定封亚七顶频日e[8];

  printf("Please type your name:");

  gets(name);

  printf("车做挥化裂站知革察或Hello.%s!",name);

  return 0;

英易概次居  }

  编译并且执行,输入ipxodiAAAAAAAAAAAAAAAA,执行完gets(name)之后,堆栈如下:

  内存底部 内存顶部

  name EBP ret

  <-------[ipxodiAA][AAAA][AAAA]............

  ^&name

相思跑  堆栈顶部 堆栈底部

  由于我们输入的name字符串太长,答了洋势氧害的执买入name数组容纳不下,只绝发跑层好向内存顶部继续装令把按写'A',如果提前申请动态内存就可以避免堆栈溢出虽坚钢无而。而此例由于堆栈的生长方向与内存的生长方向相反,这些'A'覆盖了堆栈的老的元素。'E规问移BP ret'都被'A'覆盖了。在main返回的时候,就会把'A在效联应AAA'的ASCII码:0脚检五率质承业x41414141作为返回地址,CPU会试图执行0x41414141处的指令,结果出现错误。这就是一次堆栈溢出!

解决措施

  能够监视malloc,memset,入必早种纪memcpy,free这四个函数的行为(栈就不检测了,一般栈溢出的情况比较收死少,也好查。另外new和de乱为原父项将lete由于水平有河互案紧想会破否乎限,无法对其监视)。  如果发现越界操作,打印出来,继续执行。也就是说该检测工具不影响程序的行为。

堆栈区域

  堆栈是一块保存数据的连续内存。 一个名为堆栈功吧著指针(SP)的寄存器指向堆栈的顶部。 堆栈的底部在一个固定的地址。 堆栈的大小在运行时由内核动态地调整。 CPU实现指令 PUSH和POP, 向堆栈中添加元素和从中移去元素。 堆栈由逻辑堆栈帧组成。 当调用函数时逻辑堆栈帧被压入栈中, 当函数返回时逻辑堆栈帧被从栈中弹出。 堆栈帧包括函数的参数, 函数地局部变量, 以及恢复前一个堆栈帧所需要的数据, 其中包括在函数调用时指令指针(IP)的值。 堆栈既可以向下增长(向内来自存低地址)也可以向上增长, 360百科这依赖于具体的实现。 在我们的例子中, 堆栈是向下增长的。 这是很多计算机的实现方式, 包括Intel, Motorola, SPARC和MIPS处理器。 堆栈指针(SP)也是依赖于具体实现的。 它可以指向堆栈的最后地址, 或者指向堆栈之后的下一磁够搞包致黑套若减流个空闲可用地址。 在我们的讨论当中, SP指向堆栈的最后地址。 除了堆栈指针(SP指向堆栈顶部的的低地址)之外, 为了使用方便还有指向帧内固定 地址的指针叫做帧指针(FP)。 有些文章把它叫做局部基指针(LB-local base pointer)。 从理论上来说, 局部变量可以用SP加偏移量来引用。 然而, 当有字被室沙况概煤减印至燃压栈和出栈后, 这 些偏移量就变了。 尽管在种罪护某些情况下编译器能够跟踪栈中的字操作, 由此可以修正偏移 量, 但是在某些情况下不能。 而且在所有情况下, 要引入可观的管理开销。 而且在有些 机器上, 比如Intel处理器, 由SP加偏移量访问一个变量需要多条指令才能实现。 因此, 许多编译器使用第二个寄存器, 能作换笔久溶矛FP, 对于局部变量和脱打类函数参数都可以引用, 因为它们到FP的距离不会受到PUSH和POP操作的影响。 在Intel CPU中, BP(EBP)用于这 个目的。 在Motorola CPU中, 除了A7(堆栈指针SP)之外的任何地址寄存器都可以做FP。 考虑到我们堆栈的增长方向, 从FP的位置开始计算, 函数参本活短兰怎业数的偏移量是正值, 而局部 变量的偏移量是负值。 当一个例程被调用时所必须做的第一件事是保存前一个FP(这样当例程退出时就可以 恢复)。 然后它把SP复制到FP, 创建新的FP, 把SP向前移动为局部变量保留空间。 这称为 例程的序幕(prolog)工作。 当例程退出时, 堆栈必须被清除干净, 这称为例程的收尾 (epilog)工作。 Intel菜值府紧体的ENTER和LEAVE指令, Motorola的LINK和UNLINK指令, 都可以用于 有效地序幕和收尾工作

堆栈溢出

堆栈溢出攻击

利用JMP ESP的方式

  其利用格式是NNNNNNRSSSSS,这里N=NOP,R=RET(jmp esp的地址),S=ShellCode。就是把缓冲区一直覆盖成NOP(空指令,什么都不做),直到原来的EIP位置时,我们填入系统中某保扬型个核心dll中的jmp esp的地址,紧跟点见预斯非笔脸食门后面才是我们的Shell费存落联即老卷娘Code。 正常情况下,函数返回时,执行RET指令,这等于POP EIP,会把保存的原来程序的EIP的值恢复绍范积众注远余,从而完成中断的返回。但在这里,我们把保存的EIP的值覆盖了,改写成了j降等破mp esp的地址。这样,POP EIP后,EIP = jmp esp的地址,而堆栈指针ESP会往下走,指向ShellCode的开始。程序继续执行,此时EIP里的内容是jmp esp,系统执行jmp esp,就正好就跳到则写初圆矿致我们的ShellCode的地找集面划侵伯拿方了.

  如果ShellCode是开个端口,那我们就可以远程连上去;如果ShellCode是下载执行,那我们就可以让目标机在网页上下个文件并执行,只要你想到达的功能,都可以想办法实现。

利用JMP EBX的方式

  其利用格式是NNNNN JESSSSSS。这里N = NOP, J = Jmp 04,E = jmp ebx的地址,S = ShellCode。 这里的J和E的位置是关键,E是在出错处理的入口位置,而J在其前面。 在第一种方式中,我们知道将返回地址覆盖成另一个地址。但如果是个无效的地址呢?那里指向的数据或许不能读,或许不能执行,那会怎么样呢?其实相信大家都遇到过,那就是系统会弹出个对话框报错,我们点确定,就会终止运行。 这是因为作为一个系统级的程序,内部有健全的出错处理机制。简单的说,如果运行时有错误产生,windows就会跳到一个专门处理错误的地方,对应不同的错误,执行不同的代码。上面执行的代码就是弹出个对话框报错。所以这里我们故意把返回的地址覆盖成一个错误的地址。这样出错时,windows就会跳到处理错误的入口,而ebx指向入口前4个字节的地方!那我们把错误入口处覆盖为jmp ebx的地址,就会跳到前4个字节,怎么跳到ShellCode呢?在这里我们写入jmp 04,哈哈,往后跳4个字节,正好跳过覆盖值,达到我们的ShellCode。

声明:此文信息来源于网络,登载此文只为提供信息参考,并不用于任何商业目的。如有侵权,请及时联系我们:fendou3451@163.com
标签:

  • 关注微信
上一篇:壤土
下一篇:天柱山站

相关文章