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

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

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

一丶识别__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.识别返回值,同上.

  

时间: 2024-12-28 16:40:57

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

java Servlet+mysql 调用带有输入参数和返回值的存储过程(原创)

这个数据访问的功能,我在.NET+Mysql .NET+Sqlserver  PHP+Mysql上都实现过,并且都发布在了我博客园里面,因为我觉得这个功能实在是太重要,会让你少写很多SQL语句不说,还能提高程序的执行效率, 今天在JAVA+Mysql上也实现了这个功能下面我贴出代码,这次我会讲详细点,让看的朋友能更加清楚它的好处在哪里. 一.封装的代码存储过过程调用方法   关于返回类CallableStatement的解释: CallableStatement 对象为所有的DBMS 提供了一种

javascript学习笔记(二):定义函数、调用函数、参数、返回值、局部和全局变量

定义函数.调用函数.参数.返回值 关键字function定义函数,格式如下: function 函数名(){ 函数体 } 调用函数.参数.返回值的规则和c语言规则类似. 1 <!DOCTYPE html> 2 <html> 3 <head lang="en"> 4 <meta chaset="UTF-8"> 5 <title></title> 6 </head> 7 <body

254 在js调用函数时,传递变量参数时, 是值传递还是引用传递

问题: 在js调用函数时,传递变量参数时, 是值传递还是引用传递 理解1: 都是值(基本/地址值)传递 理解2: 可能是值传递, 也可能是引用传递(地址值) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>02_关于数据传递问题</title> </head> <body> <

C++笔记(3):函数的参数和返回值

刚学C++那会,做课程设计的时候总是会去网上很找别人写好的程序来参考,那时候看到函数参数列表里各种复杂的类型和奇怪的写法就头大,后来总算是慢慢搞清楚了,在此对函数各种类型的形参以及函数的返回值进行一下总结. 1.普通形参 传递普通形参也就是值传递,传递的是实际参数的一个副本,当函数被调用时,形参复制实参,也就是说此时形参和实参的值是一样的,但形参在内存中拥有自己的地址.当函数结束时形参的生命周期终止,函数内部的操作不会影响到实参的值.经典的值交换函数代码如下: void swap1(int a,

深入理解PHP内核(六)函数的定义、传参及返回值

一.函数的定义 用户函数的定义从function 关键字开始,如下 function foo($var) { echo $var; } 1.词法分析 在Zend/zend_language_scanner.l中我们找到如下所示的代码: <ST_IN_SCRIPTING>"function" { return T_FUNCTION; } 它所表示的含义是function将会生成T_FUNCTION标记.在获取这个标记后,我们开始语法分析. 2.语法分析 在Zend/zend_

atitit.架构设计---方法调用结果使用异常还是返回值

atitit.架构设计---方法调用结果使用异常还是返回值 1. 应该返回BOOL类型还是异常 1 2. 最终会有四种状况,抛出异常.返回特殊值.阻塞.超时 1 3. 异常的优缺点点 1 4. java BlockingQueue的提示 2 5. 方案::两个都使用,一个api返回bool,一个throw 异常... 2 1. 应该返回BOOL类型还是异常 现在我遇到一个问题,我有一个函数,它要实现的功能是启动一个线程,然后让此线程监视一个事件. 但我应该返回BOOL类型还是异常哪? 作者:: 

ioctlsocket()函数是干什么用的?它返回值是什么?共有几个参数?它的各个参数是干什么用的?

1. ioctlsocket()  简述:   控制套接口的模式. #include <winsock.h> int PASCAL FAR ioctlsocket( SOCKET s, long cmd, u_long FAR* argp); s:一个标识套接口的描述字.   cmd:对套接口s的操作命令.   argp:指向cmd命令所带参数的指针. 注释:   本函数可用于任一状态的任一套接口.它用于获取与套接口相关的操作参数,而 与具体协议或通讯子系统无关.支持下列命令:   FIONB

托管调试助手 &quot;PInvokeStackImbalance&quot;:的调用导致堆栈不对称。原因可能是托管的 PInvoke 签名与非托管的目标签名不匹配。请检查 PInvoke 签名的调用约定和参数与非托管

在C#中一定要检查引用时的数据类型 WinAPI 的数据类型 默认是32位的,但是引用时外部的是 Long类型默认是64位的.所以引用时需要将 long 改为 int 型. 参照 http://blog.sina.com.cn/s/blog_8248282d0101hcbd.html https://blog.csdn.net/jinhuicao/article/details/83584973 情况一: 对 PInvoke 函数"TestDLL!TestDLL.Form1::mySum&quo

Python——变量的引用和函数的参数和返回值的传递方式

变量的引用 在python中,所有的变量都是指向地址,变量本身不保存数据,而是保存数据在内存中的地址.我们用下面的程序来理解: 1 a = 10 2 print(id(a)) 3 a = 11 4 print(id(a)) (利用id( )方法查看数据的地址) 输出结果为: 可以发现:修改变量的值,变量保存的地址随之改变. python中: 变量和数据是分开存储的 变量 中保存着数据在内存中的地址 我们把变量中记录数据的地址的行为,叫做 引用. 通俗的来说,在 Python 中,变量的名字类似于