第25天 32位软件逆向分析技术

跌跌撞撞,今天来到解密篇。

讲述的逆向工程的含义,再次讲述F5插件的强大反编译功能,而软件破解只是逆向工程非常初级的部分。

1启动函数
编写Win32程序时都需要实现一个WinMain函数,但Windows执行是先执行一个启动函数,启动函数初始化进程之后才调用WinMain函数
启动函数的作用基本相同,包括检索指向新进程的命令行指针、检索指向新进程的环境变量指针、全局变量初始化和内存栈初始化等
书中列举的一段c++的启动函数的反汇编代码。
开发人员可以修改启动代码,分析过程中,可以略过启动代码,直接把重点放在WinMain函数体上

2函数
①函数的识别
调用函数时返回地址会与参数一起传递给被调用的函数。一般情况下编译器用call和ret来调用函数及返回调用位置
我们可以通过定位CALL指令和ren指令来识别函数的开始结束部分,Call指令的操作数就是所调用函数的首地址
图中列举了一段例子,由于之前有过一定的基础就不一一列举出来
之间调用很方便,但是通过寄存器传递函数地址或动态计数函数地址调用就稍稍有点麻烦
例如:
call [4*eax+10h]

②函数的参数

函数传参有3种方式,栈方式、寄存器方式和全局变量方式。
如果参数是栈传递,就需要定义参数在栈中的顺序,并约定由谁来平衡栈
如果参数是通过寄存器传递,就需要缺点参数放在哪个寄存器中。
(1)利用栈传递参数
常见的3中调用约定,这里直接举例子说明:
__cdecl调用约定
push par3
push par2
push par1
call test1
add esp,0C

pascal调用约定
push par1
push par2
push par3
call test1

stdcall调用约定
push par3
push par2
push par1
call test1

这里只要计一个stdcall Win32常用的标准调用约定
__cdecl与他的区别是调用者平衡堆栈
pascal与他的区别是参数的入栈顺序正好相反

栈是一种后进先出的机制,就像把一个个乒乓球放入杯子里面,只能最上面的先拿出来
注意有时候编译器为了优化编译程序,减少EBP寄存器或尽可能减少代码,会直接通过ESP对
参数寻址,所以就必须从函数的开始部分进行跟踪参数的调用。
举个例子:
push par2
push par1
call test2
{
mov eax,dword ptr [esp+04]
mov ecx,dword ptr [esp+08]
...
ret 8
}
K ......
K-04h 参数2 esp+8h
K-08h 参数1 esp+4h
K-0Ch 返回地址 esp(当前esp指针)

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

寄存器传递参数没用标准,但一般都是遵守Fastcall(kuai)规范
左边的2个不大于4自己的参数分别放在ECX,EDX寄存器中,寄存器用完后使用栈

另一个规范thiscall也用到了寄存器传递参数,对象的每个函数都隐含接收this参数,通过ecx寄存器额外传递一个this指针
举个例子:
定义一个类,类中定义一个成员函数
class Csum
{
public:
int Add(int a, int b)
{
return (a+b);
}
}
void main()
{
CSum sum;
sum.add(1, 2);
}

push 00000002
push 00000001
lea ecx, [ebp-04]
call 00401020

CALL内代码
push ecx
mov [ebp-04],ecx
mov eax,[ebp+08]
add eax,[ebp+0C]

(3)名称修饰约定
为了允许操作符和函数重载,编译器会按照某种规则改写每一个入口点的符号名
目的就是允许同一个名字(不同参数或者不同作用域)有多个用法。

C编译时函数名称规则:
○stdcall会在函数名前加下滑线,后面加@和参数字节数,格式:_functionname@number
○__cdecl只在在函数名前加下滑线,格式:_functionname
○Fastcall会在函数名前加@,后面加@和参数字节数,格式:@functionname@number
他们都不改变输出函数名中的字符大小写。而Pascal约定输出的函数不能有修饰且必须为大写

C++编译时函数名称规则:
●stdcall
“?(函数名开始)”
+函数名称
+“@@YG(参数表开始)”
+参数表(第1项为函数返回类型,后面以此是参数的数据类型,指针标识在其所指数据类型前)
+@Z(标识整个名称的结束,没函数参数就Z标识结束)
格式:“?funcitonname@@YG*****@Z”或者“?funcitonname@@YG*XZ”
●__cdecl调用约定与stdcall规则相同,只是参数表的开始标识由@@YG变成@@YA
●Fastcall调用约定与stdcall规则相同,只是参数表的开始标识由@@YG变成@@YI

③函数的返回值

函调执行完成后会返回1个或者多个执行结果,称为函数返回值。
返回值最常见的是return操作符,还有通过参数按传引用方式返回值,通过全局变量返回值等
(1)return操作符返回值
一个返回值放在eax中,超过eax寄存器的容量,其高32位就放到edx寄存器中。
(2)通过参数按传引用方式返回值

posted on 2020-06-09 09:40  YXGust  阅读(238)  评论(0编辑  收藏  举报

导航