函数栈及缓冲区溢出攻击(一)
来源:互联网 发布:青少年违法犯罪数据 编辑:程序博客网 时间:2024/05/16 11:08
关于函数栈空间
假设函数A调用函数B,我们称A函数为"调用者",B函数为“被调用者”则函数调用过程可以这么描述:
(1)先将调用者(A)的堆栈的基址(ebp)入栈,以保存之前任务的信息。
(2)然后将调用者(A)的栈顶指针(esp)的值赋给ebp,作为新的基址(即被调用者B的栈底)。
(3)然后在这个基址(被调用者B的栈底)上开辟(一般用sub指令)相应的空间用作被调用者B的栈空间。
(4) 函数B返回后,从当前栈帧的ebp即恢复为调用者A的栈顶(esp),然后调用者A再从恢复后的栈顶可弹出之前的ebp值。
在这里,我们写一个具体的例子,演示一下函数调用过程中,堆栈及寄存器ebp、esp的变化情况
void B(int a){ int b = a;}void A(){ int a[10] = {0}; B(a[1]);}void main(){ A();}
使用vc6.0编译得到如下汇编代码
函数A:
0000055 push ebp 000018b ec mov ebp, esp 0000383 ec 68 sub esp, 104 0000653 push ebx 0000756 push esi 0000857 push edi 000098d 7d 98 lea edi, DWORD PTR [ebp-104] 0000cb9 1a 00 00 00 mov ecx, 26 00011b8 cc cc cc cc mov eax, -858993460 00016f3 ab rep stosd 00018c7 45 d8 00 0000 00 mov DWORD PTR _a$[ebp], 0 0001fb9 09 00 00 00 mov ecx, 9 0002433 c0 xor eax, eax 000268d 7d dc lea edi, DWORD PTR _a$[ebp+4] 00029f3 ab rep stosd 0002b8b 45 dc mov eax, DWORD PTR _a$[ebp+4] 0002e50 push eax 0002fe8 00 00 00 00 call ?B@@YAXH@Z 0003483 c4 04 add esp, 4 000375f pop edi 000385e pop esi 000395b pop ebx 0003a83 c4 68 add esp, 104 0003d3b ec cmp ebp, esp 0003fe8 00 00 00 00 call __chkesp 000448b e5 mov esp, ebp 000465d pop ebp 00047c3 ret 0
函数B:
0000055 push ebp 000018b ec mov ebp, esp 0000383 ec 44 sub esp, 68 0000653 push ebx 0000756 push esi 0000857 push edi 000098d 7d bc lea edi, DWORD PTR [ebp-68] 0000cb9 11 00 00 00 mov ecx, 17 00011b8 cc cc cc cc mov eax, -858993460 00016f3 ab rep stosd 000188b 45 08 mov eax, DWORD PTR _a$[ebp] 0001b89 45 fc mov DWORD PTR _b$[ebp], eax 0001e5f pop edi 0001f5e pop esi 000205b pop ebx 000218b e5 mov esp, ebp 000235d pop ebp 00024c3 ret 0
由上面的汇编代码可以看出,函数A和函数B在调用之前都做了如下处理:
0000055 push ebp 000018b ec mov ebp, esp这两句代码分别对应函数调用过程的(1)、(2),即保存当前的栈底地址(edp),将当前的栈顶地址(esp)作为该函数的栈底。在保持完这些状态之后,开始开辟函数的栈空间,代码如下
0000383 ec 44 sub esp, 68 ;开辟68字节的栈空间: 44H = 68D
而在函数执行完之后,又做了如下处理:
000218b e5 mov esp, ebp 000235d pop ebp这两句代码对应的是调用过程的(4),即还原调用函数前的栈状态。
单步跟踪运行,在调用B之前,查看此时的寄存器状态如下图:
根据EBP的值,我们可看到此时函数栈的状态如下图:
单步执行 call @ILT+10(B)(0040100f) 后,堆栈状态如下:
我们注意到Call指令已经将函数B的返回地址(0x401094)压入了栈,在函数返回时,将该地址赋于寄存器EIP,使得程序能正确返回并继续往下执行。如果我们修改这个地址的值,那么在函数返回时,程序将跳到你指定的地址继续执行。缓冲区溢出攻击的原理就是如此,通过越界访问缓冲区以修改函数返回地址,从而获得程序控制权。
当程序进入函数B之后,寄存器的状态如下:
栈空间状态如下:
可以看出,函数B的栈底地址指向的值(4字节)是函数A的栈底地址,函数B的栈底地址指向的下一个值(4字节)是函数B的返回地址,我们只要能定位到这个返回地址并将之修改为我们自己的函数地址,那我们就掌握了程序的控制权。
在下一章,将给出一个例子,讨论如何准备一些特殊的数据,将这些数据传给没有做溢出检测的函数,从而获得程序的控制权。
- 函数栈及缓冲区溢出攻击(一)
- 函数栈及缓冲区溢出攻击(二)
- 缓冲区溢出攻击实验(一)
- Linux下缓冲区溢出攻击及Shellcode
- 缓冲区溢出攻击及防御技术
- 缓冲区溢出攻击实验
- 缓冲区溢出*攻击*
- 缓冲区溢出攻击
- 缓冲区溢出攻击原理
- 缓冲区溢出攻击原理
- 缓冲区溢出攻击
- 缓冲区溢出攻击原理
- ret2reg缓冲区溢出攻击
- ret2reg缓冲区溢出攻击
- 缓冲区溢出攻击
- 缓冲区溢出攻击
- 阻止缓冲区溢出攻击
- 缓冲区溢出漏洞攻击
- post 调用restfull接口 解决出现的ActionController::InvalidAuthenticityToken错误
- java---字符串
- Solr使用初探——SolrJ的使用
- 进程间通信系列 之 信号实例
- Java项目打包成Jar文件后图片不显示的问题解决
- 函数栈及缓冲区溢出攻击(一)
- 高效产生一组不重复的随机数
- Solr使用初探——Solr的安装环境与配置
- 在菜单中显示图标
- 南阳理工OJ_日期计算
- ubuntu 下软件安装卸载与查看
- 关于strlen函数的一个问题
- HDOJ 1358 Period
- 直接插入排序