栈溢出(一)—— 函数调用栈
一、函数调用栈过程总结
Fig 1. 函数调用发生和结束时调用栈的变化
Fig 2. 将被调用函数的参数压入栈内
Fig 3. 将被调用函数的返回地址压入栈内
Fig 4. 将调用函数的基地址(ebp)压入栈内,并将当前栈顶地址传到 ebp 寄存器内
Fig 5. 将被调用函数的局部变量压入栈内
二、函数调用栈实例说明
首先我们来看看以下程序调用栈的过程:
int sum(int a,int b) { int temp = 0; temp = a+b; return temp; } int main() { int a = 10; int b = 20; int ret = 0; ret = sum(a,b); cout<<ret<<endl; return 0; }
如下调用过程:
每个函数在调用前都会做一件事情。 (1)把调用方的ebp入到自己的栈里去 (2)用esp指向新的栈顶位置,把esp的值赋给ebp,产生新的ebp (3)开辟空间,初始0XCCCCCCCC。 ebp入栈,实参入栈,形参入栈,调用call指令。call有两步,把下一行指令的地址入栈,跳转到调用的函数执行。形参内存主调方开辟,主调方释放。
main函数栈帧
开辟sum函数栈帧
sum函数内存布局
三、函数调用栈在栈溢出中的难点
栈溢出的shellcode中经常会出现:
payload : padding1 + address of shellcode + padding2 + shellcode
payload: padding1 + address of system() + padding2 + address of “/bin/sh”
这几种payload,很多教程对于padding2这个没有解释的很清楚,ctf wiki上是这样说的 :“如果是正常调用 system 函数,我们调用的时候会有一个对应的返回地址”
因为不用考虑相应的getshell的结束,我们可以从之前的介绍得知这是main函数调用后的下一条指令的地址,也就是return完system后我们将要继续执行的指令,也就是说如果我们还想继续调用的话这个padding2可以放下一个要执行的地址。