搁置了很久的文章了。。最近在逆一个压缩壳,忽然又想起了这篇。。
目标程序:用Yoda1.2加壳的asm程序
目的: 全过程分析该程序解压的过程
时间: 最近时间比较紧,每天分析100行左右吧
已分析: 解压壳代码+检测SoftIce+获取壳所需函数
-------------------7月22日-------------------因为此壳在数月前我已经完全看过代码,所以以这个开始逆壳旅程再好不过了,如果遇到不懂的直接看源码就明白了
说下我写的东西的格式:在一段代码之前可能会说一些跟代码意思没直接关系的话,代码之后会解释代码的意思
每一部分代码会有明显空行。
OD载入程序
00404060 a> 60 pushad
00404061 E8 00000000 call asm.00404066
00404066 5D pop ebp
00404067 81ED F31D4000 sub ebp,asm.00401DF3
正确找到壳数据起始位置,这个东西一般在加壳,感染PE时都会用到,详细参考:
http://blog.csdn.net/u010887709/article/details/9069511
例如 Jmp 401001,那你就把401000给nop掉,清楚的代码如下:
0040406D B9 7B090000 mov ecx,97B
00404072 8DBD 3B1E4000 lea edi,dword ptr ss:[ebp+401E3B]
00404078 8BF7 mov esi,edi
0040407A AC lods byte ptr ds:[esi]
0040407B C0C0 18 rol al,18
0040407E 90 nop
0040407F 02C1 add al,cl
00404081 2AC1 sub al,cl
00404083 90 nop
00404084 02C1 add al,cl
00404086 34 6C xor al,6C
00404088 EB 01 jmp short asm.0040408B
0040408A 90 nop
0040408B C0C0 58 rol al,58
0040408E 90 nop
0040408F 2C 8C sub al,8C
00404091 2AC1 sub al,cl
00404093 2C D3 sub al,0D3
00404095 EB 01 jmp short asm.00404098
00404097 90 nop
00404098 2AC1 sub al,cl
0040409A FEC8 dec al
0040409C 2C 9A sub al,9A
0040409E C0C8 E9 ror al,0E9
004040A1 C0C0 BD rol al,0BD
004040A4 04 40 add al,40
004040A6 EB 01 jmp short asm.004040A9
004040A8 90 nop
004040A9 34 22 xor al,22
004040AB AA stos byte ptr es:[edi]
004040AC ^ E2 CC loopd short asm.0040407A
观察0040407A的esi值和004040AB处的edi(等于4040AE,也就是loopd的下一行)值,以及结合所有代码可以看出是在解密壳的代码
现在可以估计这个壳是先用一小段代码把壳代码解密,再用壳代码解密原程序
004040AE 8B4424 20 mov eax, dword ptr [esp+20]
004040B2 40 inc eax
004040B3 78 0A js short 004040BF
004040B5 C785 78254000 0>mov dword ptr [ebp+402578], 1
[esp+20]存放着一个kernel32库的地址,如果这个地址+1之后是负数则当前系统不是NT系列(不必深究)
如果不是NT系列则跳过下一行语句
004040BF 8D85 ED1D4000 lea eax, dword ptr [ebp+401DED] ; 壳的入口
004040C5 B9 2A060000 mov ecx, 62A ; 大小
004040CA E8 41020000 call 00404310
{
00404310 8BF8 mov edi, eax
00404312 33C0 xor eax, eax
00404314 33DB xor ebx, ebx
00404316 33D2 xor edx, edx
00404318 8A07 mov al, byte ptr [edi]
0040431A F7E2 mul edx
0040431C 03D8 add ebx, eax
0040431E 42 inc edx
0040431F 47 inc edi
00404320 ^ E2 F6 loopd short 00404318
00404322 93 xchg eax, ebx
00404323 C3 retn
}
004040CF 8985 74254000 mov dword ptr [ebp+402574], eax
将入口处的一些代码算成一个数,放在[ebp+402574],所谓的校验和吧
004040D5 8B85 6C254000 mov eax, dword ptr [ebp+40256C]
004040DB 83E0 01 and eax, 1
004040DE 74 40 je short 00404120
004040E0 8DB5 E4264000 lea esi, dword ptr [ebp+4026E4]
004040E6 8D85 9A1E4000 lea eax, dword ptr [ebp+401E9A]
004040EC 8946 08 mov dword ptr [esi+8], eax
004040EF 8BFD mov edi, ebp ; 保存下ebp以便还原
004040F1 8D85 02254000 lea eax, dword ptr [ebp+402502] ; SEH 处理程序
004040F7 33DB xor ebx, ebx
004040F9 50 push eax
004040FA 64:FF33 push dword ptr fs:[ebx]
004040FD 64:8923 mov dword ptr fs:[ebx], esp
00404100 BD 4B484342 mov ebp, 4243484B
00404105 66:B8 0400 mov ax, 4
00404109 EB 01 jmp short 0040410C
0040410B 90 nop ; 本来是花指令
0040410C CC int3
0040410D 8BEF mov ebp, edi
0040410F 33DB xor ebx, ebx
00404111 64:8F03 pop dword ptr fs:[ebx]
00404114 83C4 04 add esp, 4
00404117 3C 04 cmp al, 4
00404119 74 05 je short 00404120 ; 该指令后有5条花指令
开头三行在判断应不应该执行下面的代码(其实下面的代码如果真正自己去逆是逆不出什么意思的),如果你选了检测Softice的话
下面的代码就会执行,大体就是触发一个异常,在异常里面将eax置为4,出去异常之后(返回到异常指令的下一条指令)如果判断eax还是4的话就证明没有SoftIce
SoftIce检测原理详细参考:
http://blog.csdn.net/u010887709/article/details/9156285
00404120 8B85 64254000 mov eax, dword ptr [ebp+402564]
00404126 0340 3C add eax, dword ptr [eax+3C]
00404129 05 80000000 add eax, 80 ; 定位到数据目录的输入表项
0040412E 8B08 mov ecx, dword ptr [eax]
00404130 038D 64254000 add ecx, dword ptr [ebp+402564] ; 输入表的RVA+基址
00404136 83C1 10 add ecx, 10 ; 跳过IID结构的开头16字节,来到FirstThunk
00404139 8B01 mov eax, dword ptr [ecx]
0040413B 0385 64254000 add eax, dword ptr [ebp+402564]
00404141 8B18 mov ebx, dword ptr [eax]
00404143 899D F0264000 mov dword ptr [ebp+4026F0], ebx ; 得到LoadLibrary地址
00404149 83C0 04 add eax, 4 ; 下一个ImageThunk,就是GetProcAddress咯
0040414C 8B18 mov ebx, dword ptr [eax]
0040414E 899D F4264000 mov dword ptr [ebp+4026F4], ebx ; 得到LoadLibrary地址
00404154 8D85 F8264000 lea eax, dword ptr [ebp+4026F8]
0040415A 50 push eax
0040415B FF95 F0264000 call dword ptr [ebp+4026F0] ; kernel32.LoadLibraryA
00404161 8BF0 mov esi, eax
00404163 8985 05274000 mov dword ptr [ebp+402705], eax
00404169 8D85 09274000 lea eax, dword ptr [ebp+402709]
0040416F E8 96000000 call 0040420A
00404174 8985 1A274000 mov dword ptr [ebp+40271A], eax
0040417A 8D85 1E274000 lea eax, dword ptr [ebp+40271E]
00404180 E8 85000000 call 0040420A
00404185 8985 2D274000 mov dword ptr [ebp+40272D], eax
0040418B 8D85 31274000 lea eax, dword ptr [ebp+402731]
00404191 E8 74000000 call 0040420A
00404196 8985 44274000 mov dword ptr [ebp+402744], eax
0040419C 8D85 48274000 lea eax, dword ptr [ebp+402748]
004041A2 E8 63000000 call 0040420A
004041A7 8985 54274000 mov dword ptr [ebp+402754], eax
004041AD 8D85 58274000 lea eax, dword ptr [ebp+402758]
004041B3 E8 52000000 call 0040420A
004041B8 8985 64274000 mov dword ptr [ebp+402764], eax
004041BE 8D85 68274000 lea eax, dword ptr [ebp+402768]
004041C4 E8 41000000 call 0040420A
004041C9 8985 73274000 mov dword ptr [ebp+402773], eax
004041CF 8D85 77274000 lea eax, dword ptr [ebp+402777]
004041D5 E8 30000000 call 0040420A
004041DA 8985 80274000 mov dword ptr [ebp+402780], eax
004041E0 8D85 84274000 lea eax, dword ptr [ebp+402784]
004041E6 E8 1F000000 call 0040420A
004041EB 8985 90274000 mov dword ptr [ebp+402790], eax
004041F1 8D85 94274000 lea eax, dword ptr [ebp+402794]
004041F7 E8 0E000000 call 0040420A
004041FC 8985 A0274000 mov dword ptr [ebp+4027A0], eax
00404202 8D85 A01F4000 lea eax, dword ptr [ebp+401FA0]
00404208 50 push eax
00404209 C3 retn
将输入表中的函数LoadLibrary和GetProcAddress拿出来,然后再利用这两个函数获取更多壳需要用到的函数
这些函数的名字你在OD可以看到。在最后push了一个地址(eax)然后retn,相当于直接跳到这个地址了
接着跳到的这个地址来看: