前期学习的时候,我们可能有很多困惑? 比如:
学会和明白函数的创建和销毁,就都知道了

相关寄存器 eax:通用寄存器,保留临时数据,常用于返回值 ebx:通用寄存器,保留临时数据 ebp:栈底寄存器 esp:栈顶寄存器 eip:指令寄存器,保存当前指令的下一条指令的地址
mov:数据转移指令 push:数据入栈,同时esp栈顶寄存器也要发生改变 pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变 sub:减法命令 add:加法命令 call:函数调用,1. 压入返回地址 2. 转入目标函数 jump:通过修改eip,转入目标函数,进行调用 ret:恢复返回地址,压入eip,类似pop eip命令
案例:
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 0;
int b = 0;
int c = 0;
c = Add(a, b);
return 0;
}先为main函数开辟空间

正在调用那个函数,ebp和esp就调用哪个函数,ebp和esp之间的空间一直在变
执行时,每个函数都要开辟空间,如下图所示

从调用main函数的函数开始

转到反汇编

先执行第一条

过程如下:esp往上移

验证:打开内存,可以看到esp里面装的就是ebp里面的值


ebp指向esp所在的位置

验证:


esp往上走,就可以为main函数开辟栈帧


每push一次,esp往上走一次,esp里面的值依次装ebx、esi、edi

详细翻译:从edi开始向下的ecx或39h个(空间),这么多个double word(4个字节)的数据改成0CCCCCCCCh,lea语句执行完edi变为edp-0E4h,最后执行rep sto 语句后edi变为esp
把edp-0E4h到edp之间的空间变为0CCCCCCCCh

以int a=10为例,把10赋值到ebp-8里面去

因为a为整型类型,所以ebp-8存储变量a(4个字节)

b、c同理


翻译:由于ebp-14h里面存放的是b的值,先放到eax里面,再压栈进去,esp往上走(先传b变量),后面a同理

call:函数调用,1. 压入返回地址 2. 转入目标函数

当进入到add里面去,需要返回地址,从这个地址回来后再往下执行

按F11,再往下执行进入add函数里面

跟前面main函数分配函数栈帧做的准备一样

当运行z=x+y时

刚好ebp+8对应a和ebp+12对应b,参数是从右向左传,先b再a,并且形参根本不是在add函数内部创建的,找的是传参压进来的空间(验证了形参是实参的临时拷贝,改变形参不会影响实参)

上述,z已经算出来了,该如何返回

把ebp-8的值放到eax寄存器当中,寄存器不会销毁(相当于全局变量)
pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变,把数据弹出到对应的寄存器中,esp依次往后加4,

然后ebp赋给esp

再弹出edp,edp装的是main函数原来的地址,又返回edp这个寄存器了,使得edp返回到原来main函数栈底的位置,esp接着往下走,main函数的空间又由esp和edp维护

回到了main函数的空间,应该从call指令下一条语句开始执行,执行ret指令,之所以存放地址,就是为了能够回来从call指令下一跳指令回来

接着,esp加8,往下走,把形参的空间返回去,并且把eax的值放到ebp-20h中(也就是变量c中)


回答前面所提到的问题
局部变量是怎么创建的
首先为函数分配好栈帧空间,栈帧空间里面初始化一部分之后,然后局部变量在栈帧中分配空间
为什么局部变量的值是随机值?
因为里面放的是随机值0xCCCCCCCCh,如果初始化的话,就把随机值覆盖掉了
函数是怎么传参的?传参的顺序是怎样的?
其实还没调用函数的时候,形参就从右向左压栈压进去了,当进入形参函数以后,通过指针的偏移量来访问形参
形参和实参是什么关系?
形参是在压栈时开辟的空间,它和实参只是值上面是相同的,空间是独立的,形参是实参的临时拷贝
函数调用是怎么做的?

函数调用是结束后怎么返回的?
在调用函数之前就把Call指令的下一条指令的地址压栈压进去,把调用这个函数的上一个函数的edp压栈压进去了,当函数调用完放回时,弹出edp后,edp里面装的更新为上一个main函数的edp,跳到Call指令后一个地址,esp回到原来的栈帧空间,返回值是通过寄存器的方式带回来的