前几天在分析一款软件的时候,发现在分析栈的时候晕圈了,索性今天就总结一波吧:
先看一个demo:
void f1(int a, int b, int c)
{
int d = a + b + c;
}
int f(int a,int b)
{
int c = a + b;
f1(1,2,3);
return c;
}
int main()
{
f(2,3);
return 0;
}
主函数:
这是上面在主函数里面调用函数f之前的情况;
此时上一个ebp还没变;
进入f函数
保存上一个ebp(主函数的ebp);
设立一个新的ebp
设立新的esp;
safecookie前;
safecookie之后;(这里由于原来的demo的图片丢了,这里重新截图的,有随机基址的!只要了解其逻辑即可!)实际上是对局部变量赋值!(注意其方向)
调用函数前;
f1函数
调用函数时候;
保存上一个ebp;
设立一个新的ebp,设立新的esp;
上面2幅图,看栈的变化
看栈的变化
这是safecookie
仔细观察这3个图
最后一张图pop ebp恢复了上一个栈帧
即此时ebp里面装的是上一个栈帧的ebp
执行完retn指令之后
retn 就等于esp + 4,jmp 上一层函数的的下一条指令
其实就是retn = pop eip
retn N操作:先eip=esp,然后esp=esp+4+N
retn 调用 之前
retn 调用 之后
总结
能使得esp发生变化的指令:
- push ——->esp=esp-4
- pop ———>esp=esp+4
- call指令(等效于以下指令):
将call的下一条指令的地址压栈
jmp 函数地址; - ret指令(等效于ret = pop eip):
retn 就等于esp + 4,jmp 上一层函数的的下一条指令
其实就是retn = pop eip
retn N操作:先eip=esp,然后esp=esp+4+N
1.将当前栈顶中值(一般为返回地址)出栈,”放入”eip中;
2.返回至上一层函数(其实放入eip中就已经注定这样结局)sub esp, 0xXX
add esp,0xXX