实验内容
源代码如左下图,运行结果如果右下图。
为什么会出现这种情况的运行截图,没有显示HellworldPE,直接到overflow me!?这种情况利用了什么?如何避免(就以下源代码而已,说明方案即可)。
![](https://img-blog.csdnimg.cn/img_convert/5aa45f0960e8c893ce632668c60387fa.png)
![](https://img-blog.csdnimg.cn/img_convert/1765f55bc64b615c940033cb2c3a1496.png)
实验准备:
栈溢出原理:
系统栈是由高地址往低地址增长的, 而数据的写入是按低地址到高地址的顺序写入. 如果程序没有对输入的字符数量做出限制, 就存在数据溢出当前栈帧以及覆盖返回地址的可能, 从而实现控制程序的执行流.可以利用这个漏洞在知道栈多大后用垃圾数据填充然后在垃圾数据后面再加个地址这个地址刚好覆盖了正常的返回地址使得这个函数执行完后,跳转到其它地方去了
ESP寄存器保存着栈帧的栈顶地址, EBP寄存器保存着当前函数栈帧的栈底地址
了解三个基本指令:
call指令:
跳转到指定函数地址执行指令, 在跳转之前还将当前IP寄存器中的值(下一条指令的地址)压入到了栈中. 从而可以在被调函数执行完之后, 继续执行当前函数.
leave指令:
释放函数栈帧, 并使EBP寄存器恢复旧值。
ret指令:
将栈顶保存的值出栈, 作为下一条将要执行指令的地址赋值给IP寄存器
分析:
在源代码中szShellCode定义了dd(double word)四个字节大小,但被赋予了12个字节大小,即0fffffffh(esp-4),0dddddddh(esp),0040103ah(esp+4)所有当FFFFFFFFH占满申请空间后就会发生栈溢出。
![](https://img-blog.csdnimg.cn/img_convert/254ee9c86c6410a6c5d90f1b2cffb320.png)
.
栈溢出过程:
首先OD打开stackflow.exe文件,F7单步步入,在进入循环函数的前一行观察栈的情况,记录此时的EBP地址以及EBP-4,EBP的下一个地址(函数返回地址)的值
EBP地址:0019FF68H EBP地址存放的值:0019FF80H
EBP-4值:00000000H
返回函数地址:0040102AH 即HelloWorldPE的入口地址
![](https://img-blog.csdnimg.cn/img_convert/a108a1e0948ea1c9b3bab13da3a87fe3.png)
继续F7单步步入进入循环;观察ebp上一个地址(ebp-4)存放的值的变化;发现有变化时证明垃圾数据开始填充,覆盖原来的数据
![](https://img-blog.csdnimg.cn/img_convert/f6401b5fa197d6db3e74f187f31ac550.png)
![](https://img-blog.csdnimg.cn/img_convert/44b97ebf17f2cd279bc47261f9b26840.png)
F7继续单步步入直到该地址的值全部发生变化,继续F7执行循环;发现此时的EBP地址的数据值也开始被覆盖;直至覆盖完全;即当FFFFFFFF占满后开始溢出,溢出后的数据将会覆盖周边的数据,DDDDDDDD会覆盖初始的EBP。观察到返回地址发生了改变;即从原来的0040102AH变成了0040103AH 即函数OverFlow me的入口地址
![](https://img-blog.csdnimg.cn/img_convert/4b75b47d68391c643b0efc36475df56b.png)
![](https://img-blog.csdnimg.cn/img_convert/f2d264657e19026daa5b856f0edf704c.png)
继续F7;执行retn指令,此时EIP地址由0040101DH变成0040103AH,即直接跳转到OverFlow me!函数入口地址;栈溢出覆盖结束,开始执行该函数。
![](https://img-blog.csdnimg.cn/img_convert/bd7f8772431d77d400040611cc2267f5.png)
最后执行:
![](https://img-blog.csdnimg.cn/img_convert/bc0b76b524f5fc5526d137eafdf25482.png)
原因分析:
字符串拷贝函数_memCopy在编码的时候未考虑目标缓冲区的长度,当拷贝过程中源字符串长度大于目标缓冲区长度时,将会导致超出的部分数据覆盖栈中有用的信息,造成溢出
由此,如果程序没有对输入的字符数量做出限制, 就存在数据溢出当前栈帧以及覆盖返回地址的可能, 从而实现控制程序的执行流.这种情况便是利用了这个漏洞在知道栈多大后用垃圾数据填充然后在垃圾数据后面再加个地址这个地址刚好覆盖了有用信息和正常的返回地址使得这个函数执行完后,跳转到其它地方去了
2.如何避免栈溢出
①减少栈空间的需求,不要定义占用内存较多的auto变量,应该将此类变量修改成指针,从堆空间分配内存。
②构造函数时,控制目标缓冲区的长度;同时避免使用不进行缓冲区检查的函数
③减少函数调用层次,慎用递归函数