当前位置: 首页 > news >正文

逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值.

 

      逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值.

在反汇编中,我们常常的会看到各种的函数调用,或者通过逆向的手段,单独的使用这个函数,那么此时,我们就需要认识一下怎么识别函数了.

一丶识别__cdecl 函数(俗称C Call),函数参数,函数返回值

首先写一个C Call的函数

1.返回值 int类型, 参数int 类型

高级代码:

int __cdecl MyAdd(int a,int b)
{
    return a + b;
}
int main(int argc, char* argv[])
{
    MyAdd(3,4);
    return 0;
}

main函数调用我们的自己写的

 

Debug下的汇编代码

在Debug版本下的调用处,我们会看到这种代码,没有流水线优化,没有任何优化

看到了,两个push,紧接着一个Call,然后平栈在外面

识别参数

  有经验的可能会说两个push 就是两个参数,其实不然,我们要进入函数内部,看内部的代码用了几个参数,要通过这个来识别.

  

  有两处使用,所以是两个参数. 而且直接给eax反回了,此时我们就可以在main函数位置,调用此函数的位置往上数几个push了,这些push才是属于自己这个函数的.

识别参数类型:

  参数类型还是很好识别了,使用参数的地方用的直接是4个字节的寄存器,那么我们可以暂定为int类型

识别调用约定

  如果在函数外面平栈,那么就是C调用约定,从识别参数来看,函数内部的 retn并没有平栈.

 识别返回值

  从上面的识别参数我们看到,eax重新写入了,那么返回值就是int类型

Release版本下的汇编

Release版本和Debug版本差不多一样,优化了少许代码,但是核心代码不变

总结:

  1.识别参数,看其函数内部使用了几个参数,然后在函数调用的地方往上数几个push这些是属于自己函数的.

  2.识别参数类型,看其参数是怎么使用.

  3.识别调用约定,看其函数内部是否平栈

  4.识别返回值类型,看其eax是否是被重写,如果被重写,则是返回值是int类型

2.返回值 __int64 C调用约定,参数是浮点和double的情况下

高级代码:

  

__int64 __cdecl MyAdd(float a,double b)
{
    return a + b;
}
int main(int argc, char* argv[])
{
    MyAdd(3,4);
    return 0;
}

Debug下的汇编代码

1.main 函数调用处

2.函数内部

3.函数内部调用的__ftol

讲解:

  1.识别C约定和上面一样,外面平栈

  2.识别参数,看其我们的的函数调用处,发现有三个push,如果不知道,则会陷入坑,直接认为是三个参数.,但是跟随到函数内部,我们发现只有两个参数,而第二个参数是double,所以在32位下要push 两个四字节,其中高位是0,低位是常量(4)的浮点编码.

  3.识别参数类型,在MyAdd内部,发现了两处使用参数的地方,用的指令分别是 fld 和fadd指令,这些都是浮点相关的.

识别技巧.

  fld指令 将实数压入浮点协处理器,那么此时我们看下汇编指令,(使用IDA的K命令,可以不是符号显示,也就是下方贴出的汇编指令)

    

FLD 第一个是一个Dword 那么可以确定为是一个32位浮点

Fadd指令,使用Fadd指令的时候,发现是第二个QWORD,难么可以确定是一个double类型的浮点.功能的还原和汇编逆向前10讲一样,里面都是各种流程和指令

  4.识别返回值,在识别返回值的时候,我们发现调用了一个_ftol函数,看到这个函数可以确定返回的是一个__int64,当然我们进入函数内部看到了

下方使用eax 和edx了,而且直接反会了,那么我们知道,在32位系统下,返回一个64位数字,在汇编中的表现形式就是edx.eax的存储方式.

Release版本下的汇编

 

熟悉总结的四句话,以不变应万变即可,因为类型都不一样.

二丶识别stdcall  函数参数,返回值,参数类型

stdcall比较简单.但是和fastcall还是有区别的.因为fastcall会有寄存器传参,所以把两个的区别搞明白就可以了.

高级代码:

  

__int64 __stdcall MyAdd(float a,double b,int c)
{
    return a + b + c;
}
int main(int argc, char* argv[])
{
    MyAdd(3.0f,4.0f,6);
    return 0;
}

直接一次性的把各种参数类型,以及返回值设置不一样.观看汇编

Debug下的汇编代码

1.函数调用处

  

2.MyAdd函数内部

1.识别函数的参数,发现了push 4个进去,但是不要被骗了,在MyAdd内部 分别得出了使用三个参数的位置,所以得出第一个参数为  float 第二个参数类型是 double,第三个参数 是int,又因为其中有一个double参数,所以在调用外面可以看到4个push,因为double是8个字节

2.识别参数个数,stdcall最好的就是它是内部平栈,也就是retn 10h,当然也可以通过这个来判断函数参数的个数

3.识别函数返回值类型

  函数返回值类型,在MyAdd中调用了_ftol函数,其内部则是返回__int64,返回值是 edx.eax

Release下的汇编

  

和Debug汇编一样,有少许优化.

总结:

  1.识别参数类型: 识别参数类型可以通过函数内部使用参数的时候用的指令,比如第一个 float,使用的是fld指令,fld系列指令就是操作浮点的,而又因为它是一个dword,所以判断是float,第三个参数是一个int,使用的是fixxx指令,fixxx指令就是操作的整数,因为它也是一个dword所以判断是int(当然可以看函数参数使用过程中其指令使用的时候表明这个参数是什么类型的)

  2.识别参数个数, 识别参数个数在stdcall中有两种方式,第一种,直接看内部指令使用参数的地方,第二种,看平栈的时候平了多少.比如上面的例子, retn 10h(16),也就是4个参数的大小,但因为double是8字节,所以判断是三个参数

  3.识别返回值,识别返回值 如果是int指令,那么返回值则放在eax中,如果是__int64指令,返回值则是在 edx.eax中,如果是浮点返回值,返回值则是在浮点协处理器中.

  4.识别调用约定,函数内部平栈,如果没有寄存器传参则是stdcall,如果有寄存器传参,则是fastcall

三丶识别 fastcall 函数,参数个数,参数类型,返回值

高级代码:

  

double __fastcall MyAdd(float a,double b,int c)
{
    return a + b + c;
}

float __fastcall MySub(__int64 a,int b)
{
    return a - b;
}
int main(int argc, char* argv[])
{
    MyAdd(3.0f,4.0f,6);
    MySub(4,3);
    return 0;
}

 

Debug下的汇编代码

  1.main函数调用的时候的汇编代码

    

  2. MyAdd函数内部

    

  3.MySub函数内部

    

    1.识别调用约定, 我们看MyAdd内部,还是MySub内部,里面都是用了外面传入的ecx,并且没有保存.那么fastcall就是ecx传参了.平栈和stdcall一样,函数内部平栈

    2.识别函数的个数,识别函数的个数也有两种方法,第一种,看retn的时候,然后加上寄存器, 我们看myadd内部,retn 0ch,平了3个参数,但外面更改了ecx,里面使用了ecx,那么就是4个参数,但因为其中一个参数类型是double,所以还是三个参数.

    3.识别参数类型,看指令来判断是什么类型,fld指令是浮点,fixxx指令则是使用的int,如果看edx.eax并且符号扩展了,则是__int64

    4.上面返回值类型么有更改为doubLe和float,可以看出,在main函数下面是用浮点的出栈指令 fstp指令,从浮点协处理器出栈,浮点协处理器是64位的,所以返回double

总结:

  1.调用约定,如果是c call那么外面平栈,stdcall函数内部平栈,fastcall函数内部平栈,但是会使用外面的寄存器.

  2.识别参数个数,类型,同上

  3.识别返回值,同上.

  

转载于:https://www.cnblogs.com/iBinary/p/7906670.html

相关文章:

  • crc16 ibm c语言,CRC16常见几个标准的算法及C语言实现
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • c语言打印http协议的代码,基于http协议的C语言客户端代码
  • [poj3686]The Windy's(费用流)
  • c语言x图形界面,「分享」C语言如何编写图形界面
  • 网站访问慢体系
  • android+手机+用短信发pdf文件,iPhone如何将PDF通过短信邮件发给别人【仅限iPhone6/6s】...
  • himall微信支付
  • android 特殊字符转,如何转义特殊字符,如’在sqlite在android
  • o'Reill的SVG精髓(第二版)学习笔记——第五章
  • android动态添加列数,Android 具有动态跨度计数的GridLayoutManager
  • SDN第二次作业
  • android usb传输图片,关于Android接入USB外接摄像头以及控制拍照并保存图片
  • 如何将html文件放入Android,关于android:将HTML文件加载到WebView中
  • nginx3解析php
  • [ JavaScript ] 数据结构与算法 —— 链表
  • Angular 响应式表单之下拉框
  • axios请求、和返回数据拦截,统一请求报错提示_012
  • Cumulo 的 ClojureScript 模块已经成型
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • HTTP 简介
  • js学习笔记
  • mysql_config not found
  • node入门
  • QQ浏览器x5内核的兼容性问题
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • React16时代,该用什么姿势写 React ?
  • Redis字符串类型内部编码剖析
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • TypeScript实现数据结构(一)栈,队列,链表
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 阿里云ACE认证学习知识点梳理
  • 国内开源镜像站点
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #etcd#安装时出错
  • #LLM入门|Prompt#3.3_存储_Memory
  • #快捷键# 大学四年我常用的软件快捷键大全,教你成为电脑高手!!
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (PyTorch)TCN和RNN/LSTM/GRU结合实现时间序列预测
  • (zhuan) 一些RL的文献(及笔记)
  • (ZT)一个美国文科博士的YardLife
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .NET CORE 第一节 创建基本的 asp.net core
  • .NET core 自定义过滤器 Filter 实现webapi RestFul 统一接口数据返回格式
  • .NET Core6.0 MVC+layui+SqlSugar 简单增删改查
  • .NET Core跨平台微服务学习资源
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?
  • .NET/C# 使用反射注册事件