无论在编程中,还是在面试中,都会遇见调用函数这个东东,但是,要是让你说函数是怎么调用的,你能回答上来吗,接下来就让我们一起探索函数如何在汇编层次上实现调用的
在接下来,我们将有几个问题要去解决

函数调用如何传递参数的
函数调用如何查找调用函数的地址的
函数内部调用过程是怎么样的
函数调用如何返回结果
如果返回值是结构体又如何返回
函数调用结束后,如何返回调用之前的状态

#include "stdafx.h"int func(int a,int b){int sum ;sum = a+b;return sum;
}int _tmain(int argc, _TCHAR* argv[])
{int a ;a = func(1,2);return 0;}

(1) 函数调用如何传递参数的
下面是上面调用的反汇编代码

 int a ;a = func(1,2);
0007142E  push        2
00071430  push        1
00071432  call        func (071028h)
00071437  add         esp,8
0007143A  mov         dword ptr [a],eax

从上面的push 2;push1可以看出,函数调用参数的传递是从右向左进行压栈。然后调用函数,

函数调用如何查找调用函数的地址的


int func(int a,int b){000713D0  push        ebp
000713D1  mov         ebp,esp
000713D3  sub         esp,0CCh
000713D9  push        ebx
000713DA  push        esi
000713DB  push        edi
000713DC  lea         edi,[ebp-0CCh]
000713E2  mov         ecx,33h
000713E7  mov         eax,0CCCCCCCCh
000713EC  rep stos    dword ptr es:[edi]  int sum ;sum = a+b;
000713EE  mov         eax,dword ptr [a]
000713F1  add         eax,dword ptr [b]
000713F4  mov         dword ptr [sum],eax  return sum;
000713F7  mov         eax,dword ptr [sum]
}
000713FA  pop         edi
000713FB  pop         esi
000713FC  pop         ebx
000713FD  mov         esp,ebp
000713FF  pop         ebp
00071400  ret

上面是函数定义的汇编代码。当函数参数压栈之后,汇编代码执行了call func (071028h),下面是执行call命令之前的各个寄存器内容

执行了call指令后,汇编指令直接跳转到了下面这张图的状态。EIP和ESP寄存器发生了变化。下面这个图估计就是函数调用表,用来匹对函数和函数的入口,类似中断向量表了

这里,发现栈中又压入了一个数据0x00071437h,有没有发现这里为什么会突然压入这个数据呢,带着这个疑问继续向下看吧


再通过jmp 000713D0进入了函数调用

函数内部调用过程
首先是调用前准备工作

首先push ebp是将ebp压栈,ebp寄存器是上个调用单元的栈低寄存器,压栈是用于保护现场,然后mov ebp,esp将当前的栈顶作为栈底。sub esp 0CCh这步是给当前函数调用分配空间,所以将push ebp这部分内存还是属于上个函数调用的空间,执行完sub esp,0CCh后,接下来栈使用的空间就是属于当前的函数调用栈了,push ebx,push esi,push edi,继续保护上一个函数调用的现场。而下部分指令是对栈的保护,实际也是一种填充

000713DC  lea         edi,[ebp-0CCh]
000713E2  mov         ecx,33h
000713E7  mov         eax,0CCCCCCCCh
000713EC  rep stos    dword ptr es:[edi]

rep指令的目的是重复其上面的指令.ECX的值是重复的次数.
STOS指令的作用是将eax中的值拷贝到ES:EDI指向的地址
如果设置了direction flag, 那么edi会在该指令执行后减小,
如果没有设置direction flag, 那么edi的值会增加.
REP可以是任何字符传指令(CMPS, LODS, MOVS, SCAS, STOS)的前缀.
REP能够引发其后的字符串指令被重复, 只要ecx的值不为0, 重复就会继续.
每一次字符串指令执行后, ecx的值都会减小.
stos((store into String),意思是把eax的内容拷贝到目的地址。
用法:stos dst,dst是一个目的地址,例如:stos dword ptr es:[edi]。dword ptr前缀告诉stos,一次拷贝双字(4个字节)的数据到目的地址。为什么一次非要拷贝双字呢?这和eax寄存器有关,到底神马关系,慢慢道来。。
执行stos之前必须往eax(32为寄存器)放入要拷贝的数据。上图中,eax的内容是cccccccc,不用说都明白int3中断。
这段代码是初始化堆栈和分配局部变量用的,往分配好的局部变量空间放入int3中断的原因是:防止该空间里的东东被意外执行。

由于ecx寄存器是033h=51,一共循环51次,实际这里51x4 = 204,正好把空间填满

 int sum ;sum = a+b;
000713EE  mov         eax,dword ptr [a]
000713F1  add         eax,dword ptr [b]
000713F4  mov         dword ptr [sum],eax
000713F7  mov         eax,dword ptr [sum]
}

这部分就是取得a和b的值,然后进行运算,将运算结果存储再栈中。最后将结果放入eax的寄存器中,


000713FA  pop         edi
000713FB  pop         esi
000713FC  pop         ebx
000713FD  mov         esp,ebp
000713FF  pop         ebp
00071400  ret

上面的也就是函数调用的环境恢复过程,这里主要说一下ret指令过程,还记得我们刚刚在上面说的执行call指令时,会将当前的eip寄存器的值压入栈中,现在ret指令就是将压入栈中的值重新恢复到eip中

pop eip
add esp,4

如果返回值是结构体又如何返回
我们现在都知道返回int类型可以使用eax寄存器,但是,如果返回结构体呢,寄存器能够装的下吗,这是我们就要重新探索函数的返回类型了

#include "stdafx.h"
struct MyStruct
{int a;int b;int c;int d;
};
MyStruct func(){MyStruct a;a.a = 1;a.b = 2;a.c = 3;a.d = 4;return a;
}int _tmain(int argc, _TCHAR* argv[])
{MyStruct a ;a = func();return 0;}

上面的汇编代码如下

 MyStruct a ;a = func();
0064149E  lea         eax,[ebp-104h]
006414A4  push        eax
006414A5  call        func (0641195h)
006414AA  add         esp,4
006414AD  mov         ecx,dword ptr [eax]
006414AF  mov         dword ptr [ebp-0ECh],ecx
006414B5  mov         edx,dword ptr [eax+4]
006414B8  mov         dword ptr [ebp-0E8h],edx
006414BE  mov         ecx,dword ptr [eax+8]
006414C1  mov         dword ptr [ebp-0E4h],ecx
006414C7  mov         edx,dword ptr [eax+0Ch]
006414CA  mov         dword ptr [ebp-0E0h],edx
006414D0  mov         eax,dword ptr [ebp-0ECh]
006414D6  mov         dword ptr [a],eax
006414D9  mov         ecx,dword ptr [ebp-0E8h]
006414DF  mov         dword ptr [ebp-10h],ecx
006414E2  mov         edx,dword ptr [ebp-0E4h]
006414E8  mov         dword ptr [ebp-0Ch],edx
006414EB  mov         eax,dword ptr [ebp-0E0h]
006414F1  mov         dword ptr [ebp-8],eax  return 0;
006414F4  xor         eax,eax  }

上面可以看到,编译器将[ebp-104h]值压入栈中,这个值到底是什么呢。通过编译发现EAX = 010FFD54,但是&a = 0x010FFE44,估计和a的地址没关系了,我们继续向下查找,

老码识途1之函数调用和局部变量相关推荐

  1. 老码识途之对象函数调用

    上一期,我们讨论了普通函数的调用过程,如果没弄明白,看这里 今天所要讲的将是对象调用函数. class C{public:int a;int b;int c;void f(int t){a = t;} ...

  2. 老码识途:从机器码到框架的系统观逆向修炼之路 pdf电子书

    重要提示尊敬的用户您好,由于老码识途:从机器码到框架的系统观逆向修炼之路pdf书受百度网盘影响无法做公共分享,只能私密分享,有不到之处请多多谅解! 百度网盘链接: http://pan.baidu.c ...

  3. 《老码识途》读书笔记:第一章(上)

    <老码识途>读书笔记:第一章--欲向码途问大道,锵锵bit是吾刀(上)   1.赋值语句 对于全局变量赋值语句,例如下面这句: 1 int gi; 2 void main(int argc ...

  4. 老码识途学习笔记(一)

    第一章 全局变量引发的故事 1 程序存储区 程序存储区一般有下列几段: 程序代码区(SECTION.txt ): 用来存放可执行文件的操作指令(二进制),也就是说是它是可执行程序在内存中的镜像.代码段 ...

  5. 《老码识途:从机器码到框架的系统观逆向修炼之路》- 第1章 - 总结

    本章学到了什么 调试技巧:在VS中断点调试,查看反汇编代码,step into进行步进调试,运行过程中查看寄存器.内存地址.变量值变化等. 机器码构造能力:使用C/C++中的直接在C代码里写汇编语言的 ...

  6. 老码识途——在堆中构建mov和jmp指令

    // asmjmp.cpp : 定义控制台应用程序的入口点. // #include <stdio.h> #include <malloc.h>int gi; void * a ...

  7. 老码识途读书笔记 1

    知识点记录: 1.int 或指针类型的全局变量默认初始化为0,局部变量则为0xcccccccc.(win7 + vs2008 ) 2.内存溢出攻击即使用6个字节空间改变程序执行流程达到某种目的.话说当 ...

  8. 老码识途之构造函数和析构函数

    对象初始化过程就是先父类构造函数,再子类构造函数.,那么我们从汇编角度去探索这个过程是怎么样的 class P{public:int a ;P(){a = 1;}~P(){a = 4;} };clas ...

  9. 读书 --- 老码识途

    上周在图书馆借了这本书,这个周末细看了下,是本好书.作者应该是个大学教授叫韩宏.书中讲的很底层,一开始就告诉大家如何debug一段程序,在VS2008里面查看内存.寄存器.反汇编.通过这些来认识汇编. ...

最新文章

  1. selenium+python自动化81-html报告优化(饼图+失败重跑+兼容python23)
  2. HTML5 学习笔记(一)- video
  3. [CF843D]Dynamic Shortest Path
  4. Django 3.2.5博客开发教程:用Admin管理后台管理数据
  5. python3.7安装包多大_python3.7 pip 安装第三方包
  6. int** 赋值_关于Java语言复合赋值运算符的两个问题,快来瞧瞧
  7. 这些深度学习术语,你了解多少?(上)
  8. 自定义用户控件显示属性分类、描述、默认值
  9. 类和对象编程(一):类成员函数
  10. cmakelists语法_cmake使用教程(六)-蛋疼的语法
  11. 洞穴辐射(radiation)
  12. 【机器学习】线性回归实战案例三:股票数据价格区间预测模型(国外+国内数据)
  13. 推动5G+北斗高精度定位系统更好赋能千行百业
  14. 小米手机android版本怎么更新,基于Android 10的MIUI稳定版正式推送,你的小米手机更新了吗?...
  15. python范围缩放_如何缩放到初始绘图/缩放的特定范围?
  16. Temporal Abstraction
  17. nfc卡模式与标准模式_全功能NFC是什么意思?点对点/读写卡/卡模拟三种模式介绍...
  18. 高通Android display分析【转】
  19. python3数据库框架_python3大框架简介 小收藏
  20. 简单的基于交换机迁移的SDN控制器负载均衡实验

热门文章

  1. 闲鱼 航模穿越机鱼塘 专业的模型鱼塘
  2. 饥荒服务器怎么修改器,饥荒通用修改器TestTools
  3. 施耐德plc使用编码器
  4. 美国Xensor电化学传感器XEN-TCG3880Pt
  5. 分享如何拿到Java开发岗位网易offer的面经
  6. Valve下一代VR头显爆料汇总,看完有点期待
  7. metaRTC搭建纯C版webrtc的视频会议
  8. pandas计算含缺失值中列平均值_Pandas之DataFrame基本操作
  9. 大话DevOps监控,团队如何选择监控工具?
  10. 网络IP,算法(二进制,八进制,十进制,十六进制)