今天下午写篇博客吧,分析分析c语言中函数调用的本质,首先我们知道c语言中函数的本质就是一段代码,但是给这段代码起了一个名字,这个名字就是他的的这段代码的开始地址

这也是函数名的本质,其实也就是汇编中的标号。下面我们会接触到一些东西 比如 eip 就是我们常常说的程序计数器,还有ebp和esp (这里是俩个指针,记得我们以前学8086也就一个sp堆栈指针)分别为EBP是指向栈底的指针,在过程调用中不变,又称为帧指针。ESP指向栈顶,程序执行时移动,ESP减小分配空间,ESP增大释放空间,ESP又称为栈指针。当然现在不理解没关系(在堆栈中变量分布是从高地址到低地址分布)。

好了我们开始正式话题吧:

1.先看图,下面我先贴出一个调用代码。

# include <stdio.h>

int fun(int a, int b)
{
int c = 0;
c= a + b;
return c; }
int main(void)
{
int a = 1;
int b = 3;
fun(a,b); return 0;
}

  反汇编后的代码

--- 汇编代码-----------------------------------------------------------------------

__CxxUnhandledExceptionFilter:
00A51113 jmp __CxxUnhandledExceptionFilter (0A525E0h)
___CxxSetUnhandledExceptionFilter:
00A51118 jmp __CxxSetUnhandledExceptionFilter (0A52660h)
_QueryPerformanceCounter@4:
00A5111D jmp _QueryPerformanceCounter@4 (0A53BB0h) 、 _fun: ;注意了 fun在这里 00A51122 jmp fun (0A513C0h) ;可以看出fun还要跳转 这次跳到了0A513C0h __unlock:
00A51127 jmp __unlock (0A536F4h)
_GetCurrentProcessId@0:
00A5112C jmp _GetCurrentProcessId@0 (0A53BB6h)
@_RTC_CheckStackVars2@12:
00A51131 jmp _RTC_CheckStackVars2 (0A51490h)
___set_app_type:
00A51136 jmp ___set_app_type (0A5269Eh) --- 被调函数的真正地址 ----------------------------------------- 1: # include <stdio.h>
2:
3: int fun(int a, int b)
4: {
00A513C0 push ebp ;压栈 ebp 保护ebp
00A513C1 mov ebp,esp ;将现在的esp地址给ebp换句话说ebp现在指向了这里
;其实也就是栈帧的最下面
00A513C3 sub esp,0CCh
00A513C9 push ebx
00A513CA push esi
00A513CB push edi
00A513CC lea edi,[ebp-0CCh]
00A513D2 mov ecx,33h
00A513D7 mov eax,0CCCCCCCCh
00A513DC rep stos dword ptr es:[edi]
5: int c = 0;
00A513DE mov dword ptr [c],0
6: c= a + b;
00A513E5 mov eax,dword ptr [a]
00A513E8 add eax,dword ptr [b]
00A513EB mov dword ptr [c],eax
7: return c;
00A513EE mov eax,dword ptr [c]
8:
9: }
00A513F1 pop edi
00A513F2 pop esi
00A513F3 pop ebx
00A513F4 mov esp,ebp
00A513F6 pop ebp
00A513F7 ret --- 主调函数 ----------------------------------------------------------------------- 10: int main(void)
11: {
00A51A11 mov ebp,esp
00A51A13 sub esp,0D8h
00A51A19 push ebx
00A51A1A push esi
00A51A1B push edi
00A51A1C lea edi,[ebp-0D8h]
00A51A22 mov ecx,36h
00A51A27 mov eax,0CCCCCCCCh
00A51A2C rep stos dword ptr es:[edi]
12: int a = 1;
00A51A2E mov dword ptr [a],1 ;定义变量a
13: int b = 3;
00A51A35 mov dword ptr [b],3 ;定义变量b
14: fun(a,b);
00A51A3C mov eax,dword ptr [b] ;把变量b给eax
00A51A3F push eax ;eax压栈 也就b压栈
00A51A40 mov ecx,dword ptr [a] ;同上
00A51A43 push ecx
00A51A44 call _fun (0A51122h) ;汇编开始调用,在汇编中函数名前面加下划线当标号处理
;地址是0A51122h,现在我们去哪里
00A51A49 add esp,8
15:
16:
17: return 0;
00A51A4C xor eax,eax
18: }
00A51A4E pop edi
00A51A4F pop esi
00A51A50 pop ebx
00A51A51 add esp,0D8h
00A51A57 cmp ebp,esp
00A51A59 call __RTC_CheckEsp (0A5113Bh)
00A51A5E mov esp,ebp
00A51A60 pop ebp
00A51A61 ret
--- 无源文件 -----------------------------------------------------------------------

  2.是不是看上面的已经懵逼了,没关系了,我来介绍一下

上面我是在vs中进行了反汇编,原本准备gcc下搞,后来懒得折腾了。先讲一下函数调用的过程,函数调用的时候其实也就是汇编中的地址的跳转,汇编中的跳转源于标号地址。其实这个也好理解,不知道地址,你让我如何找你。但是在找的开始,我们需要先记录一下回家地址,当前的一些寄存器状态(这是因为调用到里面也可能用到这些寄存器)注意还要压入一些函数调用参数。来张图我们看看

我们从上面的图可以看到,函数调用的时候依次压栈从右到左。压栈完毕调用call。call的作用有俩个,就是压栈返回值,然后修改程序计数器eip,实现程序跳转到被调函数。接着压栈ebp里面的内容(是什么我们先不讲)然后将esp赋值给ebp。也就是ebp里面的内容被改变,变为现在的esp内容,esp不就是栈顶,也就是说现在都指向了栈顶,然后压栈结束了或者可能换有一些其他的参数,比如我们递归调用,那下面就是下一个函数的参数,返回地址等等等。现在我们讨论的是ebp的作用是什么:那就是ebp指向了一个堆栈中一个栈帧的底部。而esp指向了顶部。我们可以利用ebp的偏移实现,局部变量和参数的访问。下面我们要讨论的就是如何返回。其实就是参数依次出栈,最后老ebp弹出到现在ebp。ebp指后到上一次的栈帧底部。但我们问一下参数是如何出栈的,难道是弹出,吗?弹出还有什么用,因为局部变量用完后就没用了呀,也没必要弹出给寄存器,其实是ebp将值赋给esp,esp由以前的栈顶指向栈底也就是ebp的地方。然后老ebp弹出到ebp。ebp归为到以前的ebp。esp再减4。esp回到返回地址处,然后在修改eip返回。然后esp再减4,回到新的栈顶。而返回的指令源于ret。

其实这个过程也不是很难,就是繁琐。需要对着栈图分析。需要理解局部变量的抛弃源于ebp对esp的修改。

c语言中函数调用的本质从汇编角度分析的更多相关文章

  1. 从一个新手容易混淆的例子简单分析C语言中函数调用过程

    某天,王尼玛写了段C程序: #include <stdio.h> void input() { int i; ]; ; i < ; i++) { array[i] = i; } } ...

  2. C语言中数据类型的本质

    数据类型可以理解为固定内存大小的别名.比如int类型,就是表示占用4字节的内存. 1 数据类型的大小 用sizeof操作符获得数据类型的大小. 比如 int a[5];   sizeof(a)就可以得 ...

  3. 16.C语言中数据类型的本质含义是:表示一个内存格子的长度和解析方法。

    数据类型决定长度的含义:我们一个内存地址(0x30000000),本来这个地址只代表1个字节的长度,但是实际上我们可以通过给他一个类型(int),让他有了长度(4),这样这个代表内存地址的数字(0x3 ...

  4. C语言中函数调用过程(如何管理栈空间)

    ps:先做草稿,以后有时间再整理并贴图,:) 主要是利用栈底寄存器(ebp).栈顶寄存器(esp)跟eax寄存器(存储返回值)来实现. 假设P调用Q: P() { Q(1,2); } (跟实际情况可能 ...

  5. 从汇编角度分析C语言的过程调用

    ➠更多技术干货请戳:听云博客 基本术语定义 1.系统栈(system stack)是一个内存区,位于进程地址空间的末端. 2.在将数据压栈时,栈是自顶向下增长的,该内存区用于函数的局部变量提供内存.它 ...

  6. C++语言中数组指针和指针数组彻底分析

    #################################                              ##       基本知识               ##        ...

  7. JVM系列之:从汇编角度分析Volatile

    目录 简介 重排序 写的内存屏障 非lock和LazySet 读的性能 总结 简介 Volatile关键字对熟悉java多线程的朋友来说,应该很熟悉了.Volatile是JMM(Java Memory ...

  8. JVM系列之:从汇编角度分析NullCheck

    目录 简介 一个普通的virtual call 普通方法中的null check 反优化的例子 总结 简介 之前我们在讲Virtual call的时候有提到,virtual call方法会根据传递的参 ...

  9. 深入理解 C 语言的函数调用过程

    来源: wjlkoorey 链接:http://blog.chinaunix.net/uid-23069658-id-3981406.html 本文主要从进程栈空间的层面复习一下C语言中函数调用的具体 ...

随机推荐

  1. Listview和Gridview自定义分割线

    1,ListView和GridView中的每一个条目都有分割线,属性android:footerDividersEnabled表示是否显示分割线,默认是为true,即可见.可以自定义的设置分割线的颜色 ...

  2. Java高效编程之三【类和接口】

    本部分包含的一些指导原则,可以帮助哦我们更好滴利用这些语言元素,以便让设计出来的类更加有用.健壮和灵活. 十二.使类和成员的访问能力最小化 三个关键词访问修饰符:private(私有的=类级别的).未 ...

  3. Server2003系统上的内置服务器设置某类IP无法访问问题

    最近测试过程中遇到了一个很奇怪的现象,把服务器(测试产品)部署在Server2003系统的外网A上,把客户端(测试产品)部署在内网B,网络A,B用路由器相连,设置网络A为200.1.1.255,发现客 ...

  4. YII2 Activedataprovider 类分页的使用

    下面以管理员列表为例说明Activedataprovider分页的具体使用 1.控制器中 public function actionIndex(){ $model=new Admin(); $dat ...

  5. php防sql注入

    [一.在服务器端配置] 安全,PHP代码编写是一方面,PHP的配置更是非常关键. 我 们php手手工安装的,php的默认配置文件在 /usr/local/apache2/conf/php.ini,我们 ...

  6. SSIS 关于并发的两个设置

    1.MaxConcurrentExecutables(包级别的并发度控制) MaxConcurrentExecutables, a package level property in SSIS det ...

  7. THE HANDLER_READ_* STATUS VARIABLES

    Because I do a lot of Performance Tuning gigs I get often in contact with these status variables. In ...

  8. 161118、linux(centos) 项目部署阶段相关命令汇总

    1.ssh免密码登陆 主要命令 cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys-->添加公钥 service sshd restart ...

  9. 160923、项目管理模式:如何去除SVN标记

    项目管理模式:如何去除SVN标记 当我们从工作空间中拷贝一个项目,发现项目特别大.那是因为当使用svn里面保留了每个版本的信息,我们可以通过这个方法来进行去除 当项目不需要SVN标志的时候,我们一般怎 ...

  10. mysql字段varchar区分大小写utf8_bin、utf8_general_ci编码区别

    mysql字段varchar区分大小写utf8_bin.utf8_general_ci编码区别 在mysql中存在着各种utf8编码格式:utf8_bin将字符串中的每一个字符用二进制数据存储,区分大 ...