分享

深度剖析函数四个部分(返回值,参数,函数名,函数体)

 happy害虫 2014-03-14
    本文汇编代码来自于程序反汇编Release模式下禁止优化。
  函数分为四个部分,返回值,参数,函数名,函数体,其中返回值,参数,还有函数内部中的局部变量等都是数据,函数体都是指令,函数名实际上内存中就是一个4字节的地址。
  下面首先写一个函数来解析一下函数的参数:
#include "stdafx.h"
void add(int a)
{
int b=a;
}

int _tmain(int argc, _TCHAR* argv[])
{
add(5);
return 0;
}
首先定义一个函数为add,参数为int类型,写到这里不知道读者是否有个疑问参数a(粉红色标记)存放在什么地方了,看看调用add函数(谈蓝色部分)反汇编是怎么实现的:
013C1013  push        5  
013C1015  call        add (13C1000h)  
013C101A  add         esp,4  
其中红色部分为对应指令的地址,蓝色部分为汇编指令:从中可以看出在调用函数之前做了一个工作就是压栈,从中可以说明函数的参数是放在栈中的。然后第二步是开始调用函数add,()里的是函数的对应地址,就是函数的首地址(函数名对应的地址),接下来当调用函数以后就会进入函数内部(这里在调用函数会做2步操作,其一:会把esp(堆栈指针)向低位偏移4个位置(堆栈是从高到低往上涨)然后把参数存到esp指向的位置 其二:jmp(跳转)到函数地址开始执行(13C1000h))。
然后在看看函数的汇编:
00C51000  push        ebp  
00C51001  mov         ebp,esp  
00C51003  sub         esp,0Ch  
从中可以发现进入函数以后首先把上一个函数执行到什么位置的下一条语句的指令地址压栈(以便函数返回时能够继续向下执行),然后是吧esp寄存器里存放的地址拷贝到ebp寄存器里
然后把esp寄存器存放的地址减去12个字节(0Ch 16进制数12)就是把esp指针向上移动12个字节(这里是关键局部函数就是这样开辟空间的(一个函数所能操作的栈大小取决于栈顶与栈底之间的差值,如果超出就会得到错误的结果,当超过整个操作系统分配给线程的栈大小时,就会程序崩溃,栈溢出)),这是进入函数需要做的准备工作接下来就是函数里面具体指令:
00D71006  mov         eax,dword ptr [a]  
00D71009  mov         dword ptr [b],eax  
这里简单的一个赋值就不介绍了,接下来看看栈空间是如何回收的,函数怎么退出的:
00D7100C  mov         esp,ebp  
00D7100E  pop         ebp  
00D7100F  ret  
首先第一步是吧之前00C51001    存到寄存器ebp的值取回来存到esp中,通过这一步就重新回到了压栈之后(00C51000)esp指向的代码的位置(实际上是一个地址),接下来把esp指向的代码的位置里放的值弹到ebp里,然后是ret退出这个函数(这里在调用函数会做2步操作,其一:jmp(跳转)到之前没有进入函数刚刚压栈哪里,由于这里是先前压的参数的地址 其二:会把esp(堆栈指针)向高位偏移4个位置(堆栈是从高到低往上涨),代码地址在这个参数下面故要加4个字节):
013C101A  add         esp,4   

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多