简单的顺序结构逆向分析-->四则运算
使用工具:VS2013,Ollydbg
源代码:
#include <windows.h>
//定义全局变量
int nGlobal = 5;
char cGlobal = 'a';
int main()
{
//定义局部变量
int nLocal_1 = 0;
int nLocal_2 = 8;
//变量之间进行运算
nLocal_1 += nGlobal;
nLocal_2 = nLocal_1 * 4;
nLocal_1 += cGlobal;
cGlobal += 2;
nLocal_1 -= 5;
nLocal_2 /= 2;
return 0;
}
为了更好的锻炼读汇编的能力,在VS的debug或者release目录下,把pdb符号文件删除,否则加载到Ollydbg中会出现很多提示,导致分析难度大大降低
使用OllyDbg加载后,main()函数:
00121380 55 push ebp
00121381 8BEC mov ebp,esp
00121383 81EC D8000000 sub esp,0xD8
00121389 53 push ebx
0012138A 56 push esi ; Reverse_.<ModuleEntryPoint>
0012138B 57 push edi
0012138C 8DBD 28FFFFFF lea edi,dword ptr ss:[ebp-0xD8]
00121392 B9 36000000 mov ecx,0x36
00121397 B8 CCCCCCCC mov eax,0xCCCCCCCC
0012139C F3:AB rep stos dword ptr es:[edi]
0012139E C745 F8 0000000>mov dword ptr ss:[ebp-0x8],0x0 ; 局部变量1 int nLocal_1 = 0;
001213A5 C745 EC 0800000>mov dword ptr ss:[ebp-0x14],0x8 ; 局部变量2 int nLocal_2 = 8;
001213AC 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] ; 局部变量1放到EAX中,为后面的加法做准备
001213AF 0305 00801200 add eax,dword ptr ds:[0x128000] ; nLocal_1 = nLocal_1+nGlobal
001213B5 8945 F8 mov dword ptr ss:[ebp-0x8],eax ; 把加完之后的值赋值给nLocal_1,相当于更新nLocal_1的值
001213B8 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] ; 把新的nLocal_1的值放到EAX中,为后面的乘法做准备
001213BB C1E0 02 shl eax,0x2 ; 左移2,就相当于*4
001213BE 8945 EC mov dword ptr ss:[ebp-0x14],eax ; nLocal_2 = nLocal_1 *
001213C1 0FBE05 04801200 movsx eax,byte ptr ds:[0x128004] ; 把全局变量cGlobal放到EAX中,准备加法
001213C8 0345 F8 add eax,dword ptr ss:[ebp-0x8] ; nLocal_1 = nLocal_1+cGlobal
001213CB 8945 F8 mov dword ptr ss:[ebp-0x8],eax ; 更新nLocal_1的值
001213CE 0FBE05 04801200 movsx eax,byte ptr ds:[0x128004]
001213D5 83C0 02 add eax,0x2
001213D8 A2 04801200 mov byte ptr ds:[0x128004],al ; cGlobal = cGlobal + 2;
001213DD 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
001213E0 83E8 05 sub eax,0x5
001213E3 8945 F8 mov dword ptr ss:[ebp-0x8],eax ; nLocal_1 = nLocal_1 - 5;
001213E6 8B45 EC mov eax,dword ptr ss:[ebp-0x14]
001213E9 99 cdq ; 下面会有介绍
001213EA 2BC2 sub eax,edx
001213EC D1F8 sar eax,1 ; 右移1位,相当于除以2
001213EE 8945 EC mov dword ptr ss:[ebp-0x14],eax ; nLocal_2 = nLocal_2 / 2;
001213F1 33C0 xor eax,eax ; 把EAX清零
001213F3 5F pop edi ; 还原edi,保证edi的值和刚进入函数时的值相同
001213F4 5E pop esi ; 还原esi
001213F5 5B pop ebx ; 还原ebx
001213F6 8BE5 mov esp,ebp
001213F8 5D pop ebp ; Reverse_.<ModuleEntryPoint>
001213F9 C3 retn
上面出现了几个不是很常见的指令,接下来单独介绍一下:
1、rep stos
rep是重复指令,stos表示把EAX中的值放到EDI指向的地址中,相当于是给堆栈初始化一片区域
EDI地址变化的方向由DF决定,DF = 1,EDI的值会往递减的方向变化;如果DF= 0,那么EDI的值就会往递增的方向变化
完整的指令:
mov ecx,0x36
mov eax,0xCCCCCCCC
rep stos dword ptr es:[edi]
上面的指令功能就是把edi中地址指向的值赋值成EAX中的值,赋值的次数为ECX中的值
可以在OD中观察一下指令执行之后堆栈中的变化
在指令执行之前,EDI的值是0xDDFB40
DF = 0
堆栈中情况是这样的:
接下来执行一下rep stos这条指令
可以看到从0xDDFB40这个地址开始,堆栈中的值都被赋值成了0xCCCCCCCC
因为 DF = 0,所以可以看到EDI的地址是往递增的方向变化的
循环次数ECX = 0x36,所以地址的变化是0x36 * 4 = 0xD8,也就是赋值一直到0xDDFB40 + 0xD8 = 0xDDFC18 为止
2、movsx
movsx eax,byte ptr ds:[0x128004]
带符号扩展,并把扩展之后的值给eax
如果最高位是1,那么扩展位就用1填充,如果最高位是0,那么扩展位就用0填充
比如说
mov BL,0x80
movsx AX,BL
执行完movsx之后AX中的值就是0xFF80,因为0x80转换成二进制来看的话就是1000 0000,对于有符号的数来说,最高位为1就是表示负数,所以由一个字节扩展成两个字节,结果就是1111 1111 1000 0000,扩展位用1填充,转换成十六进制就是0xFF80
如果是
mov BL,0x50
movsx AX,BL
那就是0x50 = 0101 0000,最高位为0,扩展位也是用0填充,一个字节扩展成两字节就是 0000 0000 0101 0000,执行完movsx之后AX=0x0050
3、cdq
Convert Double to Quad,意思就是把双字(四字节,一个字 = 两字节)扩展为四字(八字节),因为扩展成了8字节,一个EAX放不到,所以把八字节的高四字节放到了EDX中。
这个指令常用于扩展 被除数 ,在以前指令集规定除数必须是被除数的一半长,目前还在用这个规定。
这个指令一般是在计算除法的时候出现
4、shl
逻辑左移,最低位补0
格式:
SHL DST,常量
或者
SHL DST,CL
左移的位数根据常量的值和CL的值来确定
比如说:
mov eax,0x12
shl eax,2
首先0x12 = 0001 0010
左移2位,结果变成 0100 1000=0x48
左移指令可以用在乘法计算中,左移1位表示乘以2,左移2位表示乘以4,以此类推
所以0x12左移2位就是0x12*4=0x48
5、sar
算术右移,最高位补什么值由符号位决定,如果最高位是0,那么补0;如果最高位是1,那么补1,也就是算术右移最高位保持不变
比如说
mov eax,0x80
sar eax,1
由于0x80最高位是符号位,所以右移的时候最高位补1
1000 0000 -> 1110 0000
如果是
mov eax,0x52
sar eax,2
那么结果就是 0101 0010 -> 0001 0100
右移也相当于是除法,右移一位除以2,右移2位除以4
6、xor
用来计算两个数异或的结果,如果是异或两个相同的寄存器,那就是在执行寄存器置零操作
搜索关注公众号[逆向小生],不定期更新逆向工程师需要掌握的技能,包括Windows和Android方面的逆向,还有作为一个逆向工程师的思维模式。