大家好,又见面了,我是你们的朋友全栈君。
《缓冲区溢出攻击实践》以实践者角度介绍了初级缓冲区溢出攻击方法,本文从原理上对该方法做原理性介绍。
函数帧结构
现在高级语言C(或者C ),在函数开头的几指令要建立好函数帧结构,而函数返回时要撤消函数帧。当前在不同的CPU体系加构或者ABI标准,这些函数帧结构有一些差别,但原理上是相通的。
我们还是以之前的示例代码作为分析对象,讨论在fread函数填数据到buf变量前的栈结构。
下面是机器上对stack1程序 main函数反编译的结果:
代码语言:javascript复制(gdb) disassemble main
Dump of assembler code for function main:
0x08048484 < 0>: push �p
0x08048485 < 1>: mov %esp,�p
0x08048487 < 3>: and $0xfffffff0,%esp
0x0804848a < 6>: sub $0x40,%esp
0x0804848d < 9>: mov $0x80485e0,�x
0x08048492 < 14>: mov $0x80485e2,�x
0x08048497 < 19>: mov �x,0x4(%esp)
0x0804849b < 23>: mov �x,(%esp)
0x0804849e < 26>: call 0x80483c0 <fopen@plt>
0x080484a3 < 31>: mov �x,0x3c(%esp)
0x080484a7 < 35>: cmpl $0x0,0x3c(%esp)
0x080484ac < 40>: jne 0x80484c1 <main 61>
0x080484ae < 42>: movl $0x80485ea,(%esp)
0x080484b5 < 49>: call 0x8048380 <perror@plt>
0x080484ba < 54>: mov $0x1,�x
0x080484bf < 59>: jmp 0x80484ff <main 123>
0x080484c1 < 61>: lea 0x1c(%esp),�x
0x080484c5 < 65>: mov 0x3c(%esp),�x
0x080484c9 < 69>: mov �x,0xc(%esp)
0x080484cd < 73>: movl $0x1,0x8(%esp)
0x080484d5 < 81>: movl $0x400,0x4(%esp)
0x080484dd < 89>: mov �x,(%esp)
0x080484e0 < 92>: call 0x8048390 <fread@plt>
0x080484e5 < 97>: mov $0x80485f0,�x
0x080484ea < 102>: lea 0x1c(%esp),�x
0x080484ee < 106>: mov �x,0x4(%esp)
0x080484f2 < 110>: mov �x,(%esp)
0x080484f5 < 113>: call 0x8048370 <printf@plt>
0x080484fa < 118>: mov $0x0,�x
0x080484ff < 123>: leave
0x08048500 < 124>: ret
End of assembler dump.
在函数体里面最先执行的几条指令,通常称为 function prologue ,它完成建立函数帧的功能。
0x08048484 < 0> push �p
0x08048485 < 1> mov %esp,�p
0x08048487 < 3> and $0xfffffff0,%esp
0x0804848a < 6> sub $0x40,%esp
它的功能是:先将调用者的ebp压到栈上,然后将此时的esp作为被调用者的ebp(栈顶),然后根据函数局部变量的大小,将esp将压地址扩展,作为被调用者的esp(栈底);这样ebp和esp这对寄存器描述的栈空间就函数帧的空间。
在函数返回时,它总执行以下两条指令,通常称为 function epilogue
0x080484ff < 123> leave
0x08048500 < 124> ret
它的功能是:先将当前函数的ebp赋给esp,然后再从栈中弹出(pop)调用者的ebp值到ebp寄存器,然后再从栈中弹出EIP值到pc寄存器。指令执行完后,ebp和esp就是父函数的函数帧。
示例程序的栈帧结构
根据上面stack1中main的反编译结果,画出如图1的栈结构:
图1: fread函数调用函数,栈帧结构图
这里重点关注一下buf变量在栈中的位置,当buf变量发生溢出时,就会往高地址空间覆盖。先是覆盖main函数的其它局部变量(图1没有画出来),然后是父函数的ebp,再次重点是eip,最后是父函数的栈空间。我们不关心覆盖父函数的栈空间,因为我们根本不打算返回父函数执行。
缓冲区溢出后栈内容
当前fread从bad.txt读取文件内容到buf缓冲区并发生溢出后,整个栈空间内容如图2所示:
图2:fread从bad.txt文件读取数据产生溢出后的栈数据
当函数返回时,ret指令将0xffffd710弹给pc寄存,就开始执行shellcode了。
小结
本文以示例程序为蓝本,分析程序的栈帧结构,以及攻击方法如何利用该结构控制EIP,改变程序执行流程,从而让程序掉到shellcode的坑里面。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/158142.html原文链接:https://javaforall.cn