分享给开始学习反汇编的朋友们

逆向基础知识学习

  
声明:这份是看雪论坛学习交流小组内部交流的资料, 现在放出来

一直建议学习解密的朋友,一定要掌握好汇编和Win32编程(Charles Petzold著的《Windows程序设计》),磨刀不误砍柴功。

代码的逆向分析基础也没过多的技巧,掌握一些基本原理后,自己写些小程序,然后再看反汇编代码理解意思,进步会很快的。

有一些内容,在新兵论坛的置顶帖里己有,如: 【资料】解密基础知识入门 。

第1部分 函数

1.1 函数参数
传递参数有如下情况:
①利用堆栈传递参数;
②利用寄存器传递参数。
③通过全局变量进行隐含参数的传递
函数调用中需要解决几个问题:
1. 当参数个数多于一个时,按照什么顺序把参数压入堆栈。
2. 函数调用后,由谁来把堆栈恢复。
在高级语言中,通过调用约定来说明这两个问题。
常见的调用约定有:__cdecl调用约定,__PASCAL调用约定,__stdcall调用约定,thiscall调用约定。
假设调用函数是:test1(Par1,Par2, Par3):


1.1.1 __stdcall调用约定
按__stdcall约定调用函数test2(Par1, Par2)
push par2 ; 参数2
push par1 ; 参数1
call test2;
{
push ebp ; 保护现场原先的EBP指针
mov ebp, esp ; 设置新的EBP指针,指向栈顶
mov eax, [ebp+0C] ; 调用参数2
mov ebx, [ebp+08] ; 调用参数1
sub esp, 8 ; 创建局部变量,在堆栈中分配些空间

add esp, 8 ; 释放局部变量占用的堆栈
pop ebp ; 恢复现场的ebp指针
ret 8 ; 返回(相当于ret; add esp,8)
}
用OllyDBG打开实例add.rar ,对照下图理解堆栈的参数是如何传递的。



1.1.2 this指针
在C ++程序中,对象的每个函数隐含地接受this参数——指向对象实例的指针,函数调用是通过对象实例进行的。只要使用this指针,就能确定被调用的函数 究竟属于哪个对象实例。由于虚函数的地址是在调用即将进行之前加以确定的,因此如果不存在this指针,重载函数之间的层次结构将不可能做到的。
一般情况下,每个编译器都有自己的特别之处。下面是不同编译器中传递this指针的简表:
名称: 03.gif 查看次数: 2309 文件大小: 6.0 KB

1.2 函数返回值

函数的返回值通常是由return操作符返回的一个值。从汇编角度来看,主要有如下形式:

1)通过寄存器返回函数值;
2)通过参数按引用方式返回函数值;
3)通过全局变量返回函数值;
4)通过处理器标志返回函数值;

一般情况下,由retrun操作符返回的值放在EAX寄存器之中,如果结果超过这个寄存器的位容量,那么该结果的高32位会加载到EDX寄存器中。
如果返回一个含有几百个字节的结构或者一个近似大小的对象,编译器会在不告诉程序的情况下,给函数传递一个隐式参数,这个指针指向保存的返回结果。
1.3 局部变量

在子程序内部说明的变量称为局部变量,局部变量的作用域是其所在的子程序。从汇编角度来看,局部变量就是一个临时堆栈缓存,用完释放。


其反汇编代码如下(红体字为局部变量):

00401000 >/$ 6A 04 push 4 ; /Arg2 = 00000004
00401002 |. 6A 03 push 3 ; |Arg1 = 00000003
00401004 |. E8 16000000 call 0040101F ; /Add.0040101F
00401009 |. 8BD8 mov ebx, eax
0040100B |. 6A 00 push 0 ; /ExitCode = 0
0040100D /. FF15 00204000 call [<&KERNEL32.ExitProcess>] ; /ExitProcess


0040101F /$ 55 push ebp ; 保护现场原先的EBP指针
00401020 |. 8BEC mov ebp, esp ; 设置新的EBP指针,指向栈顶
00401022 |. 83EC 04 sub esp, 4 ; 分配局部变量所有空间
00401025 |. 8B45 0C mov eax, [ebp+C] ; 调用参数2
00401028 |. 8B5D 08 mov ebx, [ebp+8] ; 调用参数1
0040102B |. 895D FC mov [ebp-4] , ebx ; 参数1放局部变量里
0040102E |. 0345 FC add eax,[ebp-4] ; 参数2与局部变量相加
00401031 |. 83C4 04 add esp, 4 ; 释放局部变量所有空间
00401034 |. 5D pop ebp ; 恢复现场的ebp指针
00401035 /. C2 0800 retn 8


第2部分 控制语句

2.1 IF-THEN-ELSE语句

语句IF-THEN-编译后,其汇编代码形式一般如下:
cmp a,b
jz xxxx

各类条件跳转指令形式,各位一定要搞清楚,破解时经常用到。
如不清楚,可以看看这资料: http://www.pediy.com/tutorial/chap2/Chap2-3.htm

2.2  SWITCH-CASE语句

《黑客反汇编》这方面内容比较仔细,到时请哪位摘录些过来


第3部分 循环语句


第4部分 全局变量

全局变量作用于整个程序,一直是存在的,它放在全局变量的内存区;而局部变量则是存在于函数的堆栈区,当函数调用结束后便消失。

这个比较好识别,如:
mov eax, dword ptr [403000]:直接调用全局变量,共中0x403000是全局变量的地址

第5部分 数据运算符

5.1 加法

加法运算符一般编译成ADD指令,编译优化时,都比较喜欢用LEA指令来代替ADD指令。LEA指令允许用户在一个时钟内完成d=a+b+c计算,会编译成LEA c,[a+b+c]指令。这个很常见,大家一定要注意。

5.2 减法

减法比较简单,都是SUB指令,好识别

5.3 乘法
(摘自《黑客反汇编》)








5.4 除法


第2部分 启动函数

在 编写Win32应用程序时,都必须在源码里实现一个WinMain函数。但Windows程序执行并不是从WinMain函数开始的,首先被执行的是启动 函数相关代码,这段代码是编译器生成的。启动代码完成初始化进程,再调用WinMain。标准编译器通常包含启动代码在内的库文件源码,例如 Visual C++中,启动代码存放在CRT/SRC/crt0.c文件中。
所有的C/C++运行时启动函数的作用基本都是相同的:检索指向新进程的命令行指针,检索指向新进程的环境变量指针,全局变量初始化,内存堆栈初始化等。当所有的初始化操作完毕后,启动函数就调用应用程序的进入点函数。
调用WinMain如下所示:
GetStartupInfo (&StartupInfo);
Int nMainRetVal = WinMain(GetModuleHandle(NULL),NULL,pszCommandLineAnsi,(StartupInfo.dwFlags&STARTF_USESHOWWINDOW)?StartupInfo.wShowWindow:SW__SHOWDEFAULT);
当进入点返回时,启动函数便调用C运行库期的exit函数,将返回值(nMainRetVal)传递给它,进行一些必要处理,最后调用系统函数ExitProcess退出。
其他一些编译器,如Delphi、BorLand C++开发包中都有相应的启动代码。
在绝大数情况下,我们对启动代码并不需要关心。 对于逆向分析人员来说,首要的任务是找到Winmain函数。

WinMain函数原型如下:
int WINAPI WinMain(
HINSTANCE hInstance, // 当前实例的句柄
HINSTANCE hPrevInstance, // 前一个实例的句柄
LPSTR lpCmdLine, // 命令行的指针
int nCmdShow // 窗口的显示状态
);
其 中参数hInstance一般通过GetModuleHandleA函数进行获取的,这对识别WinMain函数有些帮助。另外,对WinMain的调用 通常放在启动函数代码结尾部分,后面通常跟着诸如exit或XcptFilter之内的两、三个函数。例如下面这段代码:
.text:004010DC push eax ; nShowCmd
.text:004010DD push [ebp+lpCmdLine] ; lpCmdLine
.text:004010E0 push esi ; hPrevInstance
.text:004010E1 push esi ; lpModuleName
.text:004010E2 call ds:GetModuleHandleA
.text:004010E8 push eax ; hInstance
.text:004010E9 call WinMain(x,x,x,x)
.text:004010EE mov [ebp+var_60], eax
.text:004010F1 push eax ; int
.text:004010F2 call _exit
许多开发人员可以得到启动源代码的情况下对启动代码进行修改,这样,程序的执行可能不是从WinMain开始,而是从任何其他的函数开始。

读者可以用OllyDBG调试一下附件的实例(Visual C++ 6.0编译)。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值