深度剖析函数的调用

109 篇文章 0 订阅
48 篇文章 4 订阅
C语言是面向过程的计数机语言,其本质就是函数的调用,从汇编的角度深层次剖析函数之间的调用关系,将对C语言的理解更上一层楼。
先来看一段代码
            #include<stdio.h>
            int myfun(int x, int y)
            {
                int z = x + y;
                return z;
            }
            int main()
            {
                int a = 0xAAAAAAAA;
                int b = 0xBBBBBBBB;
                int c = myfun(a, b);
                printf("i am back \n");//查看调用函数是否返回
                printf("ret=%d\n", c);
                return 0;
            }
这个实例实现了myfun()函数的调用,接下来分析myfun是如何被调用的。

首先转到反汇编(我使用的工具是VS2013)

 int main()
     9: {
010613C0  push        ebp  
010613C1  mov         ebp,esp  
010613C3  sub         esp,0E4h  
010613C9  push        ebx  
010613CA  push        esi  
010613CB  push        edi  
010613CC  lea         edi,[ebp+FFFFFF1Ch]  
010613D2  mov         ecx,39h  
010613D7  mov         eax,0CCCCCCCCh  
010613DC  rep stos    dword ptr es:[edi]  
    10:     int a = 0xAAAAAAAA;
010613DE  mov         dword ptr [ebp-8],0AAAAAAAAh  
    11:     int b = 0xBBBBBBBB;
010613E5  mov         dword ptr [ebp-14h],0BBBBBBBBh  
    12:     int c = myfun(a, b);
010613EC  mov         eax,dword ptr [ebp-14h]  
010613EF  push        eax  
010613F0  mov         ecx,dword ptr [ebp-8]  
010613F3  push        ecx  
010613F4  call        01061096  

即看到的是下面三行汇编,其作用是为main开辟栈桢,这里姑且将它命名为mainebp是栈底,esp是栈顶,自栈顶底向栈顶生长,push是压栈指令,作用是将指令压入栈,压入都是从栈顶压入。

010613C0  push        ebp  
010613C1  mov         ebp,esp  
010613C3  sub         esp,0E4h 

main中int aint b的初始化,[ebp-8]ebp是栈底,-8自下初始化a,同样,[ebp-14h]ebp向下14h初始化。

10:     int a = 0xAAAAAAAA;
010613DE  mov         dword ptr [ebp-8],0AAAAAAAAh  
11:     int b = 0xBBBBBBBB;
010613E5  mov         dword ptr [ebp-14h],0BBBBBBBBh  

实参到形参,从上一段汇编可以看到,[ebp-14h]-->b[ebp-8]-->a,而先入栈的是b,最后才是a。即调用函数中临时变量的建立是从右往左的,这里从原理上直接体现了出来

12:     int c = myfun(a, b);
010613EC  mov         eax,dword ptr [ebp-14h]  
010613EF  push        eax  
010613F0  mov         ecx,dword ptr [ebp-8]  
010613F3  push        ecx  

mian的开辟

函数临时变量已经建立好,call语句将计算机要执行的命令从当前的main跳转至myfun,其本质是将目标函数的地址覆盖到EIP中(EIP:程序指针,用来存放正在执行的指令的下一条指令的地址

010613F4  call        01061096  
010613F9  add         esp,8  

call “0106096”学过微机原理我们知道call不能直接调用子程序,所以call了jmp间接跳转)这个地址就是用来覆盖EIP的地址,清楚的看到,程序跳转到了“0106096”这个地址,而且图右边的EIP也将指令变为“0106096” 值得注意的是这条指令的地址010613F9 add esp,8010613F9在下方的内存中查看栈顶esp的写入情况,可以看到010613F9已经被push进去了,至于为什么压入这条指令其实显而易见,callEIP中当前正在执行指令的下一条指令地址覆盖为010613F9,而函数的调用最终会结束并回到原函数继续执行,所以必须把指令地址保存起来。
call

进去到函数之后

3: int myfun(int x, int y)
     4: {
01061470  push        ebp  
01061471  mov         ebp,esp  
01061473  sub         esp,0CCh  
01061479  push        ebx  
0106147A  push        esi  
0106147B  push        edi  
0106147C  lea         edi,[ebp+FFFFFF34h]  
01061482  mov         ecx,33h  
01061487  mov         eax,0CCCCCCCCh  
0106148C  rep stos    dword ptr es:[edi]  
     5:     int z = x + y;
0106148E  mov         eax,dword ptr [ebp+8]  
01061491  add         eax,dword ptr [ebp+0Ch]  
01061494  mov         dword ptr [ebp-8],eax  
     6:     return z;
01061497  mov         eax,dword ptr [ebp-8]  
     7: }
0106149A  pop         edi  
0106149B  pop         esi  
0106149C  pop         ebx  
0106149D  mov         esp,ebp  
0106149F  pop         ebp  
010614A0  ret  

和主函数一样,首先开辟栈桢

01061470  push        ebp  
01061471  mov         ebp,esp  
01061473  sub         esp,0CCh  

栈桢

当前栈底是接着esp之前push010613F9开辟的,所以ebp到010613F9ebp+4(地址int),而010613F9下是a(a是int再加4),所以ebp+8是a,b在a下,即ebp+och(12)add是对取回的ab求和

0106148E  mov         eax,dword ptr [ebp+8]  
01061491  add         eax,dword ptr [ebp+0Ch]  

ebp-8自然是进入到栈桢空间,将eax内的a+b存入

mov         dword ptr [ebp-8],eax

又将a+b的结果存入eax用于返回值

mov         eax,dword ptr [ebp-8]

开辟栈桢

函数的调用大致完成,撤销栈桢

0106149D  mov         esp,ebp

栈底恢复

这里pop弹出函数一开始pushebp地址,也就是主函数时的栈底

0106149F  pop         ebp 

程序重新回到主函数,之前被push010613F9重新回到EIP中,到此,函数的调用完成。
finish

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值