栈帧的形成和关闭

  • 当栈顶指针小于栈底指针时就形成了栈帧 (esp < ebp)

    • 需要注意的是,栈的增长方向是地址减小的方向
  • 进入到新函数的时候,就会相应生成栈帧,当结束的时候,就会清除使用的栈空间(栈平衡)
  • Debug版本中,在函数退出的时候,会检查esp是否等于ebp以此检查栈平衡。若不平衡则调用__chkesp() 弹出提示框。
  • 实际进入函数过程
    • 先保存原来的ebp,edi、esi寄存器到栈中
    • 然后调整ebp的位置到esp。也即是调整新函数栈帧
    • 接着通过sub esp,xxh 打开0x40大小的栈空间,是留给局部变量使用的
    • 结束的时候 又会通过 add esp,xxh 释放栈空间
    • 将原本的ebp地址从栈中弹出,恢复调用函数的栈帧

通过使用O2选项,就不会有检查栈平衡的代码,还可能没有保存环境、使用ebp保存当前栈底等一系列操作,代码会变得简洁高效。

各种调用方式的考察

因为函数调用会有不定参数的问题,如果参数是不定参数的时候,被调用函数不知道具体的参数数目,就需要调用他的函数平衡栈。但是正常情况下,被调用函数可以自己平衡栈。

三种调用约定

  • _cdecl: C/C++默认调用方式,调用函数平衡栈,可使用不定参数
  • _stdcall: 被调用函数平衡栈,所以不能使用不定参数
  • _fastcall: 寄存器方式传参,被调用函数平衡栈,不能使用不定参数

所以,可以通过传参方式和平衡栈的方式来判断调用方式

_stdcall

  • 退出函数时会通过 ret x; 的方式平衡栈顶,等价于 esp += x

  • 有时不一定通过ret平衡,也可能通过pop等指令平衡,具体需要看代码怎么使用栈的

_cdecl

  • 在被调用函数中不需要操作,调用函数的call指令下面add esp,x;来平衡栈

  • 复写传播,_cdecl方式的函数在同一作用域多次使用的时候,,最后可以一起平衡栈

_fastcall

  • 使用寄存器传参,但是寄存器智能使用edx和ecx多余的参数依旧需要栈传参

所以,实际传参效率 _fastcall > _cdecl > _stdcall

使用ebp或者esp寻址

在不是O2选项时,会使用ebp寻址局部变量

否则在O2选项中,为了节省寄存器,使用esp寻址

寻址的本质不过是对ebp或者esp做加减法操作,使得地址产生偏移,获取对应的值

因为IDA考虑到方便区分参数和局部变量,所以对于局部变量的寻址使用负数标号

因为调用函数的过程,是先将参数压栈,然后使用call指令,所以参数的偏移应该是正数。

函数调用过程

  • 根据调用方式不同,参数压栈或者寄存器赋值
  • 执行call指令,此时将call的下一条指令压栈
  • 然后执行push ebp保存调用函数的栈底指针
  • mov ebp,esp 则将调整出被调用函数的栈帧,此时ebp == esp,此时参数相对于ebp来说是高地址
  • 一般来说,会通过sub esp,xxh 来分配局部变量的空间
  • 在Debug模式下,会将他们通过rep stos 指令填充cccccccc也即是int 3中断防止这里的东西被执行
  • 退出的时候,根据上面sub esp,xxh的值,给他add esp,xxh回去,就是释放局部变量
  • 根据调用方式,_fastcall 和 _stdcall需要函数中平衡栈
  • pop出调用函数的ebp还原调用函数的栈帧
  • ret指令返回
  • 若是_cdecl 调用,则通过add esp,xxh的方式平衡栈

某次调用函数时的栈结构

函数参数

使用push指令将数据压入栈中,而push实际上是把操作数复制到栈顶,所以此时压入栈的数据和原数据是不同的,相互独立。所以函数中修改参数,不会影响原来的数据。

不定长参数

C/C++将不定长参数的函数定义为:

  • 至少有一个参数
  • 所有不定长的参数类型传入是都是dword类型
  • 需要在某一个参数中说明参数个数或者最后一个参数赋值为结尾标记

只要获得第一个参数的地址,那么只要对这个地址多加法就能获得其他参数。

获取参数类型是为了解释地址中的数据。

printf就是通过第一个参数获取参数总数的,字符串中几个%就是几个参数(%%)除外

函数的返回值

一般来说都是给eax赋值来传递返回值的

而如果是结构体,成员只有两个就使用eax和edx传递返回值。

[C++逆向] 6 函数的工作原理的更多相关文章

  1. eval函数的工作原理

    如果您想详细了解eval和JSON请参考以下链接: eval  :https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Glob ...

  2. C++虚函数的工作原理

    静态绑定与动态绑定 讨论静态绑定与动态绑定,首先需要理解的是绑定,何为绑定?函数调用与函数本身的关联,以及成员访问与变量内存地址间的关系,称为绑定. 理解了绑定后再理解静态与动态. 静态绑定:指在程序 ...

  3. C++中虚函数的作用和虚函数的工作原理

    1 C++中虚函数的作用和多态 虚函数: 实现类的多态性 关键字:虚函数:虚函数的作用:多态性:多态公有继承:动态联编 C++中的虚函数的作用主要是实现了多态的机制.基类定义虚函数,子类可以重写该函数 ...

  4. 《C++反汇编与逆向分析技术揭秘》——函数的工作原理

    各种调用方式的考察 示例: cdecl方式是调用者清空堆栈: 如果执行的是fastcall: 借助两个寄存器传递参数: 参数1和2借助局部变量来存储: 返回值 如果返回值是结构体: 返回值存放在eax ...

  5. 虚函数列表: 取出方法 // 虚函数工作原理和(虚)继承类的内存占用大小计算 32位机器上 sizeof(void *) // 4byte

    #include <iostream> using namespace std; class A { public: A(){} virtual void geta(){ cout < ...

  6. C++虚函数工作原理

    一.虚函数的工作原理      虚函数的实现要求对象携带额外的信息,这些信息用于在运行时确定该对象应该调用哪一个虚函数.典型情况下,这一信息具有一种被称为 vptr(virtual table poi ...

  7. python中的函数、生成器的工作原理

    1.python中函数的工作原理 def foo(): bar() def bar(): pass python的解释器,也就是python.exe(c编写)会用PyEval_EvalFramEx(c ...

  8. 从零入门 Serverless | 一文搞懂函数计算及其工作原理

    作者 | 孔德慧(夏莞) 阿里云函数计算开发工程师 什么是函数计算 大家都了解,Serverless 并不是没有服务器,而是开发者不再需要关心服务器.下图是一个应用从开发到上线的对比图: 在传统 Se ...

  9. day15生成器send方法,递归,匿名函数,max结合匿名工作原理,常用的内置函数

    复习 ''' 1.带参装饰器 - 自定义 | wraps def wrap(info) def outer1(func): from functools import wraps @wraps(fun ...

  10. 20140415 HOG 不同继承方式的访问特性 虚函数工作原理

    1.HOG block重叠的好处 由于行人通常其形状可以视为柔体,人 的边缘位置不固定,而有一些移动,block 重叠后,一个边缘的梯度信息在两个相邻重叠的 block 中都能有所表达,这样即使边缘的 ...

随机推荐

  1. CTT 总结

    Day 1 T1 肝 4 个半小时,主要是前面各种假,中途各种改.出来听说 T2 才是签子.但是 T2 只写了 15 分暴力.100+15+0=115. Day 2 T1 肝 4 个小时,但是 8 点 ...

  2. 【构造,图论,建模】Loj3629「2021 集训队互测」序列

    Problem Link 有一个长为 \(n\) 的未知序列,给定 \(m\) 个限制,每个限制形如给定 \(i,j,k,x\),要求 \(a_i,a_j,a_k\) 的中位数为 \(x\).构造一个 ...

  3. Vue3中ref和toRef的区别

    1. ref是复制,视图会更新 如果利用ref将某一个对象中的某一个属性值变成响应式数据 我们修改响应式数据是不会影响原始数据的; 同时视图会跟新. ref就是复制 复制是不会影响原始数据的 < ...

  4. el-dialog组件无法跟新视图上的数据

    <el-dialog title="提示" :visible.sync="dialogVisible" width="30%"> ...

  5. 【JS 逆向百例】PEDATA 加密资讯以及 zlib.gunzipSync() 的应用

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  6. LeetCode刷题日记 2020/8/28

    题目描述: 最长有效括号 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度. 示例 1: 输入: "(()" 输出: 2 解释: 最长有效括号子串为 ...

  7. 微服务保护-Sentinel

    1.初识Sentinel 1.1.雪崩问题及解决方案 1.1.1.雪崩问题 微服务中,服务间调用关系错综复杂,一个微服务往往依赖于多个其它微服务. 如图,如果服务提供者I发生了故障,当前的应用的部分业 ...

  8. Java执行Shell和传输文件

    特性: 多线程批量执行 多密码尝试 引入依赖: <dependency> <groupId>ch.ethz.ganymed</groupId> <artifa ...

  9. shell find 根据时间获取文件列表

    根据时间得到文件,可以使用find进行查找,支持查找: find以时间为条件查找可用选项: -amin n:查找n分钟以前被访问过的所有文件. -atime n:查找n天以前被访问过的所有文件. -c ...

  10. 小知识:MySQL修改lower_case_table_names参数

    环境:MySQL 5.7.25 起初创建环境时没有要求表名称不区分大小写,后续应用使用提出要设置lower_case_table_names=1的需求,期望表名不再区分大小写. 修改这个参数需要重启实 ...