第4章:逆向分析技术--32位软件逆向技术

启动函数

Win32 应用程序源码中,都会有一个 WinMain 函数.但 Windows 程序的执行并非是从 WinMain 函数开始的.这段代码由编译器生成,完成进程初始化.

 

函数返回值后,进行一些必要的处理,最后调用 ExitProcess() 函数.

 

函数

call 指令会奖其之后的指令地址压栈, ret 指令用于结束函数的执行,但是并不是所有的 ret 都标志着函数的结束.

①利用栈传递参数:

 

C/C++ 和 MFC 程序默认使用 __cdecl ,由调用者负责清除栈. 

stdcall 是 Win32 API 的默认方式,由被调用者清理栈.部分 API ( wsprintf )采用 __cdecl 方式.

esp 是栈指针,所以一般使用 ebp 来存取栈,此时 [ ebp + xx ] 是使用参数,  [ ebp - xx ] 是使用局部变量.若编译器开启了优化,则会直接使用 esp 来存取栈中数据.使用指令 add esp,8   即可清除局部变量,最后使用 ret 8 (相当于 ret ; add esp, 8 ),平栈.

 

enter 指令 == push ebp ; mov ebp, esp ; sub esp, xxx .

leave 指令 == add esp, xxx ; pop ebp .

 

②利用寄存器传递参数

大多数都采用 Fastcall 规范. Visual C++ 在编译时, 左边两个不大于4字节的参数放入 edx 和 ecx . 其余的从右至左压栈.浮点值, 远指针, __int64 类型使用栈传递. 

thiscall 调用规范是 C++ 非静态成员函数的默认调用约定,同样使用寄存器传递参数,并由被调用者平栈. 每个对象隐含接受一个通过 ecx 传递的额外参数-- this 指针.

 

注意此处 this 指针传递的时机,不要看错。

 

③名称修饰约定

为了允许使用操作符和函数重载, C++编译器会改写函数名称.

 

 

通过传值调用的参数,会创建副本传入函数;通过传引用的参数,会直接传入参数的地址。

 

数据结构

①局部变量

1#. 利用栈存放

通过命令 push ecx  和  sub esp, n   都可以实现。

 

2#. 通过寄存器存放

寄存器不够用时就会使用栈。

 

②全局变量

一般存放在 .data 节区,通过硬编码进行寻址,放在可以读写的区块里(在只读区块则为常量)。

 

③数组

一般是通过 “基址+变址 ” 寻址。

 

虚函数

C++ 中的虚函数,在程序运行时定义的函数,在编译时不能确定,在调用时才确定。

所有对虚函数的引用通常放在一个专用数组 —— 虚函数表( Virtual Table ,VTBL )。调用虚函数时,先取出虚函数表指针(Virtual Talble Pointer,VPTR)。

 

C++ 中 this 指针的传递是隐含的。

 

控制语句

1# .IF —— Else语句

在编译代码时,选择优化,不会使用 cmp 指令。

 

 

 

2#. Switch - Case 语句

 

 

如果 case 的取值表示一个算术级数,那么编译器会利用一个 跳转表(Jump Table)来实现。

 

 

 

注意跳转地址的计算。

 

 neg 指令改变 CF 位, sbb 指令带 CF 位进行减法运算。

 

数学运算符

①加减法

lea 指令允许用户在一个时钟内完成加减法运算。

 

②乘法

 

③除法

除法的运算代价很高,比乘法大约多出10倍的CPU时钟。

 

2E8BA2E9 = 2 • ( 1/11 • 232 ).  232 代表了寄存器大小 方便其溢出。

而多乘一个 2 是为了防止计算时,数值刚好小了一点,避免在计算 11/11 时出现 0.9xxxxx 的情况。

得出结果后,只需将存放溢出的寄存器的值 /2 即可。

 

 

 

posted @ 2020-08-25 15:30  Rev_omi  阅读(276)  评论(0编辑  收藏  举报