逆向分析细节总结
2016-02-23 00:45
225 查看
1、函数识别及参数传递
Call指令 == 跳转指令+返回地址
Call指令跳转的地址即被调用函数的起始地址
函数参数传递方式:
1)堆栈传递参数
2)寄存器传递参数(__fastcall、thiscall(寄存器传递this指针))
参数入栈顺序,平衡堆栈:
1)C/C++和MFC程序默认使用__cdecl调用约定,参数从右到左入栈,由调用者平衡堆栈;
2) Win32 API函数使用stdcall调用约定,参数从右到左入栈,由被调用者平衡堆栈(参数个数固定,函数知道传入的参数个数,因此被调用函数可在返回前用一条“retn”指令直接清理传递参数的堆栈);VC++默认使用。
2、函数返回值
返回方式:return返回、引用返回、全局变量返回。
函数返回值一般放在EAX中返回,若超出容量,高32位放入EDX中;
在调试程序时,不要见Call就跟进,在Call之前所做的所有PUSH动作以及对寄存器的操作都可能是在给函数传递参数,而函数的返回值一般都放在EAX里面,当然这个值可能是一个指针,指向一个数据结构。从汇编角度来看,主要有如下形式:
1)通过寄存器返回函数值;
2)通过参数按引用方式返回函数值;
3)通过全局变量返回函数值;
4)通过处理器标志返回函数值;
一般情况下,由retrun操作符返回的值放在EAX寄存器之中,如果结果超过这个寄存器的位容量,那么该结果的高32位会加载到EDX寄存器中。 如果返回一个含有几百个字节的结构或者一个近似大小的对象,编译器会在不告诉程序的情况下,给函数传递一个隐式参数,这个指针指向保存的返回结果。
3、数据结构
局部变量:sub esp,8 ;分配空间
push reg;分配4个字节空间
[ebp-xxx];寻址调用变量
[ebp+xxx];寻址调用参数,优化模式时,通过esp寄存器对局部变量与参数寻址;
全局变量:通常位于数据区固定地址上,访问时通过固定地址直接对内存寻址;
初始化:mov指令,push指令直接将值压入堆栈;
寄存器存放:编译器尽量使用寄存器存放局部变量,不够则使用堆栈;
注:局部变量生命周期短,必须及时确定当前寄存器存放的是哪个变量;
程序运行(调试)过程中,我们可以得到指定代码段的虚拟地址(VA),减去其加载基址,即为其相对虚拟地址(RVA),此时可得到该代码段在PE文件的哪个区段,转换公式为:
文件偏移 = RVA - 节偏移 (由存储单位差异引起的节基址差称为节偏移)
4、启动函数
首先我们明确,Windows程序的执行并不是从WinMain函数开始,首先被执行的是启动函数相关代码,这段代码是编译器生成的。这段启动代码作用是初始化进程,然后调用WinMain( )函数。即Start( ) ->WinMain( )
在WinMain( )函数原型中,参数hInstance(实例句柄)一般通过GetModuleHandleA函数进行获取,这对识别WinMain函数会有些帮助。
对WinMain的调用通常放在启动函数代码结尾部分,后面通常跟着诸如exit或XcptFilter之内的两、三个函数。
另一标志:___security_cookie
Call指令 == 跳转指令+返回地址
Call指令跳转的地址即被调用函数的起始地址
函数参数传递方式:
1)堆栈传递参数
2)寄存器传递参数(__fastcall、thiscall(寄存器传递this指针))
参数入栈顺序,平衡堆栈:
1)C/C++和MFC程序默认使用__cdecl调用约定,参数从右到左入栈,由调用者平衡堆栈;
2) Win32 API函数使用stdcall调用约定,参数从右到左入栈,由被调用者平衡堆栈(参数个数固定,函数知道传入的参数个数,因此被调用函数可在返回前用一条“retn”指令直接清理传递参数的堆栈);VC++默认使用。
2、函数返回值
返回方式:return返回、引用返回、全局变量返回。
函数返回值一般放在EAX中返回,若超出容量,高32位放入EDX中;
在调试程序时,不要见Call就跟进,在Call之前所做的所有PUSH动作以及对寄存器的操作都可能是在给函数传递参数,而函数的返回值一般都放在EAX里面,当然这个值可能是一个指针,指向一个数据结构。从汇编角度来看,主要有如下形式:
1)通过寄存器返回函数值;
2)通过参数按引用方式返回函数值;
3)通过全局变量返回函数值;
4)通过处理器标志返回函数值;
一般情况下,由retrun操作符返回的值放在EAX寄存器之中,如果结果超过这个寄存器的位容量,那么该结果的高32位会加载到EDX寄存器中。 如果返回一个含有几百个字节的结构或者一个近似大小的对象,编译器会在不告诉程序的情况下,给函数传递一个隐式参数,这个指针指向保存的返回结果。
3、数据结构
局部变量:sub esp,8 ;分配空间
push reg;分配4个字节空间
[ebp-xxx];寻址调用变量
[ebp+xxx];寻址调用参数,优化模式时,通过esp寄存器对局部变量与参数寻址;
全局变量:通常位于数据区固定地址上,访问时通过固定地址直接对内存寻址;
初始化:mov指令,push指令直接将值压入堆栈;
寄存器存放:编译器尽量使用寄存器存放局部变量,不够则使用堆栈;
注:局部变量生命周期短,必须及时确定当前寄存器存放的是哪个变量;
程序运行(调试)过程中,我们可以得到指定代码段的虚拟地址(VA),减去其加载基址,即为其相对虚拟地址(RVA),此时可得到该代码段在PE文件的哪个区段,转换公式为:
文件偏移 = RVA - 节偏移 (由存储单位差异引起的节基址差称为节偏移)
4、启动函数
首先我们明确,Windows程序的执行并不是从WinMain函数开始,首先被执行的是启动函数相关代码,这段代码是编译器生成的。这段启动代码作用是初始化进程,然后调用WinMain( )函数。即Start( ) ->WinMain( )
在WinMain( )函数原型中,参数hInstance(实例句柄)一般通过GetModuleHandleA函数进行获取,这对识别WinMain函数会有些帮助。
对WinMain的调用通常放在启动函数代码结尾部分,后面通常跟着诸如exit或XcptFilter之内的两、三个函数。
另一标志:___security_cookie
相关文章推荐
- [040130]命运之夜Fate Stay Night【汉化硬盘版】[带全通关CG回想完美&跳过序章初始存档&中文攻略]
- java列出指定后缀名(*.jpg)的图片
- 平衡查找树之B树
- iOS 设置TextView控件内容行间距
- impala+hue
- 下载APP显示 服务器检索信息出错 ”RPC:S-7:AEC-0“等
- Remove Duplicate Letters
- 关于苹果开发的知识总结
- HTML一个form表单中有两个(多个)submit,后台如何区分(纯HTML实现,无需javascript)
- Lua学习Day02
- JVM学习(3)——总结Java内存模型
- 016-Storm借助tick消息定时器统计统计周期性业务
- 【慕课笔记】第四章 JAVA中的集合框架(上) 第6节 学生选课—课程查询
- windows7_64+vs2013_32位配置PCL_1.6.0
- Codeforces 620D Lipshitz Sequence RMQ+二分
- 3.7 代码注释和编码规范
- form提交前验证的几种方法
- nodejs爬取页面内容(utf8编码)
- 图像分割简单处理
- 多点移动电子地图定位