函数调用过程中 栈和寄存器的变化过程

探究以下程序在函数调用过程中   栈顶EBP  栈底ESP  的变化过程

int Plus(int x,int y)
{
	int z=2;
	return x+y+z;
}
int main(int argc, char* argv[])
{
	int r=Plus(3,4);
	return 0;
}

反汇编后代码是这样的

1.     00C113EE    6A 04           PUSH 4                         //参数3压栈               
2.     00C113F0    6A 03           PUSH 3                         //参数4压栈
3.     00C113F2    E8 A9FCFFFF     CALL 00C110A0                  //调用函数
4.     00C113F7    83C4 08         ADD ESP,8                      //平栈

 其中第3步函数调用内部汇编如下

1.		00C113EE    6A 04           PUSH 4
2.		00C113F0    6A 03           PUSH 3
3.		00C113F2    E8 A9FCFFFF     CALL 00C110A0                   //调用函数 指令跳转
----
3.1		00C110A0    E9 DB020000     JMP 00C11380                    //跳转到函数地址准备调用
3.2		00C11380    55              PUSH EBP                        //为了还原现状,保存当前栈底
3.3		00C11381    8BEC            MOV EBP,ESP                     //开辟新的函数栈,栈底提升到原栈顶的位置
3.4		00C11383    81EC CC000000   SUB ESP,0CC                     //开辟新的函数栈,栈顶向上偏移一段距离,这段距离就是缓冲区
3.5		00C11389    53              PUSH EBX                        //为了还原现状,保存当前EBX
3.6		00C1138A    56              PUSH ESI                        //为了还原现状,保存当前ESI
3.7		00C1138B    57              PUSH EDI                        //为了还原现状,保存当前EDI
3.8		00C1138C    8DBD 34FFFFFF   LEA EDI,[EBP-0CC]               //循环填充缓冲区,设置填充的起始地址
3.9		00C11392    B9 33000000     MOV ECX,33                      //循环填充缓冲区,设置填充次数
3.10	00C11397    B8 CCCCCCCC     MOV EAX,CCCCCCCC                //循环填充缓冲区,设置填充内容
3.11	00C1139C    F3:AB           REP STOS DWORD PTR ES:[EDI]     //循环填充缓冲区,开始填充
3.12	00C1139E    C745 F8 0200000 MOV DWORD PTR SS:[EBP-8],2      //将变量2写入栈中
3.13	00C113A5    8B45 08         MOV EAX,DWORD PTR SS:[EBP+8]    //计算相加,结果保存到EAX中
3.14	00C113A8    0345 0C         ADD EAX,DWORD PTR SS:[EBP+0C]   //计算相加,结果保存到EAX中
3.15	00C113AB    0345 F8         ADD EAX,DWORD PTR SS:[EBP-8]    //计算相加,结果保存到EAX中
3.16	00C113AE    5F              POP EDI                         //还原EDI
3.17	00C113AF    5E              POP ESI                         //还原ESI
3.18	00C113B0    5B              POP EBX                         //还原EBX
3.19	00C113B1    8BE5            MOV ESP,EBP                     //消除新栈,还原栈况,新栈底为原栈顶
3.20	00C113B3    5D              POP EBP                         //消除新栈,还原栈况,原栈底之前已经保存在栈中,POP即可还原
3.21	00C113B4    C3              RETN                            //指令跳转到函数调用处
----
4.		00C113F7    83C4 08         ADD ESP,8                       //平栈
5.		00C113FA    8945 F8         MOV DWORD PTR SS:[EBP-8],EAX

程序未运行时 EBP    ESP    EIP 如下

0.堆栈初始状态是这样的

1.    PUSH 4
    当第一个参数压栈时,栈底EBP不变,栈顶ESP上移一个字节,即ESP=ESP-4,指令EIP跳转到下一条指令,因为PUSH 4 这条指令是两个字节 所以EIP指令下移两个字节,EIP=EIP+2

2.    PUSH 3
    当第二个参数压栈时,栈底EBP不变,栈顶ESP上移一个字节,即ESP=ESP-4,指令EIP跳转到下一条指令
3.    CALL 00C110A0  =   PUSH EIP + jmp
    当调用函数Plus时,先将EIP的下一条指令(00C113F7)压栈保存  为的是当被调用函数结束时程序可以回来继续运行 ,栈底不变,因为有指令被压栈,所以栈顶上移


3.1    JMP 00C11380
    继续跟进程序 发现00C110A0 会 JMP 00C11380 此时 EIP指向 00C11380  因为是jmp 所以堆栈没有影响

 
3.2    PUSH EBP    
    因为CALL了一个函数,所以会生成一个新的函数栈,此时需要先将原来的栈底保存起来,为了函数调用结束之后可以还原当前的栈况,不保存会发生栈底丢失。 push操作导致栈顶上移 ESP=ESP-4


3.3    MOV EBP,ESP
3.4    SUB ESP,0CC
    生成新栈
    因为CALL了一个函数,所以会生成一个新的函数栈,在原栈顶头上开辟一个新栈,原来的栈顶当作新栈的栈底,原来的栈顶上移0CC个字节 ESP=ESP-0CC,上移的空间就是缓冲区

3.5    PUSH EBX
    保存现场,为了函数调用结束后 可以还原寄存器当前的状态,所以先将各个寄存器的状态压栈保存


3.6    PUSH ESI

 

3.7    PUSH EDI


3.8    LEA EDI,[EBP-0CC]
3.9    MOV ECX,33
3.10    MOV EAX,CCCCCCCC
3.11    REP STOS DWORD PTR ES:[EDI]

 循环填充缓冲区

LEA EDI,[EBP-0CC] 表示将  EBP-0CC 这个地址 送入EDI  EDI影响填充位置,这里是为了设置填充的起始地址 , 即从缓冲区的头部开始填充
MOV ECX,33 给ECX赋值为33  ECX影响了循环次数 
MOV EAX,CCCCCCCC  将EAX赋值CCCCCCCC  EAX影响填充的内容
REP STOS DWORD PTR ES:[EDI]   用EAX的值循环33次 填充EDI地址中的内容
 
3.12    MOV DWORD PTR SS:[EBP-8],2
    对应函数中的一个变量  int z=2   在EBP-8的位置写入2


3.13    MOV EAX,DWORD PTR SS:[EBP+8]
3.14    ADD EAX,DWORD PTR SS:[EBP+0C]
3.15    ADD EAX,DWORD PTR SS:[EBP-8]
    计算2+3+4  结果放入EAX


3.16    POP EDI
    还原寄存器的状态    3.5      3.6      3.7保存的 现在用到了


3.17    POP ESI


3.18    POP EBX
    注意 此时虽然POP了 只是给寄存器赋值 栈顶跟随改变,但是内存中的数据还是存在的 并没有随着栈顶的降低而消除

 

3.19    MOV ESP,EBP
    消除新栈,修改栈顶
3.20    POP EBP
    消除新栈,修改栈底。EBP之前已经保存到栈中,直接POP即可还原

 


3.21    RETN= POP EIP


4.    ADD ESP,8
    外平栈,由于此时多PUSH了3和4  为了栈平衡 在函数外部ESP+8  使得栈可以还原到最初状态  

 

 至此 函数调用前和函数调用后 栈空间是完全一样的

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值