逆向分析-之深入理解函数

程序都是由具有不同功能的函数组成的,

因此在逆向分析中将重点放在 函数的识别 和 参数的传递 上是明智的,

这样做可以将注意力集中在某一段代码上。函数是一个程序模块,用来实现一个特定的功能。一个函数包括函数名,入口参数,返回值,函数功能等部分。

1. 函数的识别

  程序通过调用程序来调用函数,在函数执行后又返回调用程序继续执行。函数如何知道要返回的函数的地址呢?

  实际上,调用函数的代码中保存了一个返回地址,该地址会与参数一起传递给被调用的函数。有多种方法可以实现这个功能,在绝大多数情况下,编译器都使用call和ret 指令来调用函数及返回调用指令。

  call指令与跳转指令类似。

  不同的是,call指令保存返回信息,即将其之后的指令地址压入栈顶,当遇到ret指令时返回这个地址。

  也就是说,call指令给出的地址就是被调用函数的起始地址。ret指令则用于结束函数的执行(当然,不是所有的ret指令都标志着函数的结束)。

  通过这一机制可以很容易地把函数调用和其他跳转指令区分开来。

  因此,可以通过定位call机器指令或利用ret指令结束的标志来识别函数。call指令的操作数就是所调用函数的首地址。

  这种函数直接调用方式使程序变得很简单。

2.函数的参数

  函数传递参数有3种方式,栈方式,寄存器方式以及通过全局变量进行隐含参数传递方式。如果参数是通过栈传递的,就需要定义参数在栈中的顺序,并约定函数被调用后由谁来平衡栈。如果参数是通过寄存器传递的,就要确定参数存放在哪个寄存器中。

  每种机制都有优缺点,并且和使用的编译语言有关。

  (1)栈方式

    栈是一种先进后出的存储区,栈顶指针esp 指向栈中第一个可用的数据项。在调用函数时,调用者依此把参数压入栈,然后调用函数,函数被调用后,在栈中取得数据并进行计算。函数计算结束后,由调用者或函数本身修改栈,使栈恢复原样。即平衡。

    在参数的传递中有俩个很重要的问题:

    1.当参数个数多于1个时,按照什么顺序把参数压入栈?

    2.函数结束后,由谁来平衡栈?这些都必须有约定。这种在程序设计语言中为了实现函数调用而建立的协议称为调用约定。这种协议规定了函数中的参数传送方式,参数是否可变和由谁处理栈问题等。

    C举例子。C的参数传递顺序为从右到左,平衡栈者是调用者。

  

push param3//参数从右到左
push param2
push param1
call test1
add esp,0C;//平衡栈

  函数对参数的存取及局部变量都是通过栈定义的,非优化编译器用一个专门的寄存器(通常是 ebp)对参数进行寻址。C,C++,pascal等高级语言的函数执行过程基本一致情况如下:

    调用者将函数(子程序)执行完毕时应返回的地址,参数压入栈。

    子程序使用 ”ebp指针+偏移量“对栈中的参数进行寻址并取出,完成操作

    子程序使用ret或retf返回 。此时CPU将eip置为栈中保存的地址,并继续执行它。

    栈在整个过程中发挥着非常重要的作用。栈是一个先进后出的区域,只有一个出口,即当前栈顶。栈操作的对象只能说双操作数(占4字节) 。

    在许多时候,编译器会按优化方式来编译程序,栈寻址稍有不同。这时,编译器为了节省ebp寄存器或尽可能减少代码以提高速度,会直接通过esp对参数进行寻址。esp的值在函数执行期间会发生变化,该变化出现在每次有数据进出栈时。要想确定对那个变量进行寻址,就要知道程序当前位置的esp的值,为此必须从函数的开始部分进行跟踪。

   (2)利用寄存器传递参数

    寄存器传递参数的方式没有标准,所有与平台相关的方式都是由编译器开发人员制定的。尽管没有标准,但绝大多数编译器提供商都在不对兼容性进行声明的情况下遵循相应的规范,即fastcall规范。fastcall 顾名思义,特点就是快(因为它是靠寄存器传递参数的)

    不同编译器实现的Fastcall 稍有不同。Microsoft Visual C++ 编译器在采用Fastcall 规范传递参数时,左边的2个不大于4字节的参数分别放在ecx和edx寄存器中,寄存器用完后就要使用栈,其余参数仍按照从左到右的顺序压入栈,被调用的函数在返回前清理传送参数的栈,浮点值,远指针和__int64 类型总是通过栈来传递的。而Borland Delphi/C++ 编译器在采用Fastcall 规范传递参数时候,左边的3个不大于4字节(dword)的参数分别放在eax,edx,和ecx寄存器中,寄存器使用完之后,其余参数按照从左到右的PASCAL方式压入栈。

    另有一款编译器Watcom C 总是通过寄存器来传递参数,它严格为每一个参数分配一个寄存器,默认情况下第一个参数用eax,第二个参数用edx,第三个参数用ebx,第四个阐述用ecx。如果寄存器用完,就会用栈来传递参数。因此,其参数实际上可能通过任何寄存器进行传递。

   (3)名称修饰约定

    为了允许使用操作符和函数重载,C++编译器往往会按照某种规则改写每一个入口点的符号名,从而允许同一个名字(具有不同的参数类型或者不同的作用域) 有多个用法且不会破坏现有的基于C的链接器。这项技术通常称为名称改编或者名称修饰。许多C++编译器厂商都制定了自己的名称修饰方案。

    在VC++中,函数修饰名由变异类型(C或C++),函数名,类名,调用约定,返回类型,参数等共同决定。关于名称的内容很多,

     stdcall调用约定在输出函数名前面加一个下划线前缀,在后面加一个”@“符号以及参数的字节数,格式为”[email protected]“

    _cdecl调用约定仅在输出函数名前面加一个下划线前缀,格式为”_functionname“

    Fastcall 调用约定在输出函数名前面加一个”@“符号,在后面加一个”@“符号及其参数的字节数,格式为”@[email protected]“

3.函数的返回值

  函数被调用执行后,将向调用者返回1个或多个执行结果,称为函数返回值。返回值最常见的形式是return操作符,还有通过参数按传引用的方式返回值,通过全局变量返回值等。

  (1) 用return 操作符返回值

    在一般情况下,函数的返回值放在eax寄存器中返回,如果处理结果的大小超过eax寄存器的容量,其高32位就会放到edx寄存器中。

  (2)通过参数按照引用方式返回值

    给参数传递的方式有俩种,分别是传值和传引用。进行传值调用时,会建立参数的一份复本,并把它传给调用函数,在调用函数中修改参数的复本不会影响原始的变量值。

    传引用调用允许调用参数修改原始变量的值。调用某个函数,当把变量的地址传递给函数时,可以在函数中用间接运算符修改调用函数内存单元该变量的值。

原文地址:https://www.cnblogs.com/godoforange/p/10367470.html

时间: 2024-11-09 05:01:32

逆向分析-之深入理解函数的相关文章

《C++反汇编与逆向分析技术揭秘》--认识启动函数,找到用户入口

<C++反汇编与逆向分析>和<程序员的自我修养>都是以VC6的代码作为例子讲解的.这里是在vs2017下,CRT代码有些区别,但整体流程上都是初始化环境,设置参数,最后转到用户main函数. class COne { public: COne() { printf("COne \r\n"); } ~COne() { printf("~COne \r\n"); } }; COne g_One; int main() { printf("

逆向分析智能窗帘频射协议

近来我热衷于对家庭自动化设备的破解,然后将它们添加到我的Homekit集成包之中.这事情要从几个月前说起,当时我爸订购了大批量的RAEX 433MHz射频电动窗帘,以替代老式的手动式窗帘. 注意:你可以在Spotlight网店搜索Motion Motorised Roller Blind找到该窗帘. 我对这个电动窗帘非常满意,有了它我就不用跑上跑下的去打开/关闭窗户,可是为了控制它们,你需要购买RAEX的遥控器.RAEX有许多不同类型的遥控器产品,其中我选择购买了以下两种遥控器: R 型遥控器

对Xposed的全面分析(一)--- xposedInstall apk文件,逆向分析

本次分析主要是 对源代码的分析, 分成两块1. xposedInstall 逆向分析java代码, 2. 分析框架 替换的app_process和框架hook的原理. 第一部分: 主要是希望分析的尽量透彻,这是第二次分析AnDa,之前对第二部分分析完(源码看过,文献多次阅读)之后理解不深刻,而且当时第一部分根本就没当回事,因为做项目比较赶.所以现在为了将漏网之鱼一网打尽,决定"内外"兼修,java和C++带上系统一起学习总结一下,达到融汇贯通就可以了. 之所以再次分析xposed是觉得

“金山杯2007逆向分析挑战赛”第一阶段第二题

注:题目来自于以下链接地址: http://www.pediy.com/kssd/ 目录:第13篇 论坛活动 \ 金山杯2007逆向分析挑战赛 \ 第一阶段 \ 第二题 \ 题目 \ [第一阶段 第二题] 题目描述: 己知是一个 PE 格式 EXE 文件,其三个(section)区块的数据文件依次如下:(详见附件)  _text,_rdata,_data 1. 将 _text, _rdata, _data合并成一个 EXE 文件,重建一个 PE 头,一些关键参数,如 EntryPoint,Imp

病毒木马查杀实战第016篇:U盘病毒之逆向分析

       本系列教程版权归"i春秋"所有,转载请标明出处.        本文配套视频教程,请访问"i春秋"(www.ichunqiu.com). 比对脱壳前后的程序 我们这次所要研究的是经过上次的脱壳操作之后,所获取的无壳病毒样本.其实我们这里可以先进行一下对比,看看有壳与无壳的反汇编代码的区别.首先用IDA Pro载入原始病毒样本: 图1 可以发现此时IDA Pro的Functionwindow是空的,说明很多函数没能解析出来,并且还无法切换到图形模式,而图

“金山杯2007逆向分析挑战赛”第一阶段第一题分析

题目来自于如下网址: http://www.pediy.com/kssd/ 第13篇 论坛活动 \ 金山杯2007逆向分析挑战赛 \ 第一阶段 \ 第一题 \ 题目 \ [第一阶段 第一题]: 现将此题目概述粘贴如下: CrackMe.exe 是一个简单的注册程序,见附件,请写一个注册机: 要求: 1. 注册机是KeyGen,不是内存注册机或文件Patch 2. 注册机可以使用ASM,VC,BC,VB,Delphi等语言书写,其他谢绝使用. 3. 注册机必须可以运行在Windows系统上. ..

病毒木马查杀第006篇:熊猫烧香之逆向分析(中)

一.前言 上一篇文章讲解了"熊猫烧香"病毒样本的反汇编代码入口处的分析,虽然尚未研究到病毒的核心部分,但其实我们后续的分析与之前的思想是一致的.而越到核心部分,可能会遇到越来越多的API函数,结合所调用函数的参数进行分析,反而有助于我们更容易地理解病毒的行为.应当将分析出的每一个CALL函数,改为我们能够理解的名字,这往往也有助于对后续程序的理解. 二.病毒功能分析 上一篇文章的最后,我留下了三个CALL没有分析,现在进入第一个CALL,即sub_408024的内部查看一下: 图1 s

自制反汇编逆向分析工具 迭代第四版本

上一个版本,本工具支持的第一个逆向策略,(对反汇编的函数体内的分支控制结构逆向生成cpp代码,)一直都可以正常工作,直到遇上了一个函数.这个使我的逆向策略算法失效的函数,主要的特点是它含有众多无条件跳转指令(,都是在函数体内的跳转). 为什么无条件跳转指令会使得我的第一个逆向算法失效,因为这些无条件跳转指令让函数的执行流变得不易于线性理解. 在一个没有反汇编出来的函数汇编代码中,如果无条件跳转指令很少,特殊地连一条无条件跳转指令也没有时,将汇编代码的执行流当作行文阅读,总可以找到一个特例让所有条

从逆向的角度去理解C++虚函数表

很久没有写过文章了,自己一直是做C/C++开发的,我一直认为,作为一个C/C++程序员,如果能够好好学一下汇编和逆向分析,那么对于我们去理解C/C++将会有很大的帮助,因为程序中所有的奥秘都藏在汇编中,很多问题我们从表面上无法看出到底是为什么,只要用逆向工具一分析,很快就能知道其中的所以然来.我们都知道虚函数表是放在类对象的最前面,但是很多人并不知道虚函数表的第一项存放的是什么,下面我用IDA分析一下C++的虚函数调用,从汇编的角度去理解虚函数.此文只适合具有逆向分析基础的读者,如果没有逆向分析