【文章标题】: Morphine.Shell.V1.5l脱壳的详细分析
【作者主页】: www.iawen.com
【软件名称】: CrackMe
【下载地址】: 见附件
【加壳方式】: Morphine
【使用工具】: OD,PETools,ImportREC
【操作平台】: XP SP3
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
——————————————————————————–
【详细过程】
这个壳是今天从UnPackCn站点上Download下来,自己加了一个试试,呵呵!
有一个很好脱壳脚本,是朋友写的(偶不会,^&^),不过,我还是想自己动手玩一下,于是就有了这篇文章,希望大家不要见笑!
1、OD载入目标程序:
00511E2B Mo> 50 push eax //OD停在这里,壳的入口
00511E2C 81FA 5378EC30 cmp edx,30EC7853
00511E32 58 pop eax
00511E33 52 push edx
00511E34 7D 05 jge short Morphed.00511E3B
00511E36 78 03 js short Morphed.00511E3B
00511E38 C1F3 40 sal ebx,40
00511E3B 5A pop edx
00511E3C 52 push edx
00511E3D 53 push ebx
00511E3E 78 01 js short Morphed.00511E41
2、单步一下,留意ESP的值的更改,然后下hr 0013FFC0断点,使用ESP定律
3、先下断点bp VirtualAlloc,再F9运行程序,留意堆栈,直到出现下面的值:
[code
0013FD74 00511A01 /CALL 到 VirtualAlloc 来自 Morphed.005119FF
0013FD78 00400000 |Address = 00400000
0013FD7C 00010000 |Size = 10000 (65536.)
0013FD80 00003000 |AllocationType = MEM_COMMIT|MEM_RESERVE
0013FD84 00000040 \Protect = PAGE_EXECUTE_READWRITE
4、取消断点,返回:
00511A01 59 pop ecx ; 返回到这里
00511A02 85C0 test eax,eax
00511A04 75 13 jnz short Morphed.00511A19
00511A06 6A 40 push 40
00511A08 68 00100000 push 1000
00511A0D 51 push ecx
00511A0E 50 push eax
00511A0F FFD3 call ebx ; 为将还原的程序分配空间
00511A11 85C0 test eax,eax ; 返回的地址,也即程序将要加载的基址
00511A13 0F84 87020000 je Morphed.00511CA0
00511A19 8945 F4 mov dword ptr ss:[ebp-C],eax
00511A1C 89C7 mov edi,eax
00511A1E 8B75 08 mov esi,dword ptr ss:[ebp+8]
00511A21 56 push esi
00511A22 89F1 mov ecx,esi
00511A24 034E 3C add ecx,dword ptr ds:[esi+3C]
00511A27 8B49 54 mov ecx,dword ptr ds:[ecx+54] ; 要复制的大小,0x400,即1024字节
00511A2A F3:A4 rep movs byte ptr es:[edi],byte ptr >; 复制PE头到上面分配的空间里
00511A2C 5E pop esi
00511A2D 0376 3C add esi,dword ptr ds:[esi+3C]
00511A30 81C6 F8000000 add esi,0F8
00511A36 8B45 08 mov eax,dword ptr ss:[ebp+8]
00511A39 0340 3C add eax,dword ptr ds:[eax+3C] ; 获取程序的区段数
00511A3C 0FB640 06 movzx eax,byte ptr ds:[eax+6]
00511A40 8D7D C8 lea edi,dword ptr ss:[ebp-38]
00511A43 57 push edi
00511A44 6A 0A push 0A
00511A46 59 pop ecx
00511A47 F3:A5 rep movs dword ptr es:[edi],dword pt>; 将区段名保存到堆栈
00511A49 5F pop edi
00511A4A 8B57 14 mov edx,dword ptr ds:[edi+14]
00511A4D 85D2 test edx,edx
00511A4F 74 14 je short Morphed.00511A65
00511A51 56 push esi ; 将下一个区段名压入堆栈
00511A52 8B75 08 mov esi,dword ptr ss:[ebp+8]
00511A55 01D6 add esi,edx
00511A57 8B4F 10 mov ecx,dword ptr ds:[edi+10] ; 取得将要拷贝的区段大小
00511A5A 8B57 0C mov edx,dword ptr ds:[edi+C] ; 取得复制位置的偏移
00511A5D 8B7D F4 mov edi,dword ptr ss:[ebp-C]
00511A60 01D7 add edi,edx ; 移动位置到要拷贝的目标地址
00511A62 F3:A4 rep movs byte ptr es:[edi],byte ptr >; 开始复制
00511A64 5E pop esi
00511A65 48 dec eax ; 区段数减1
00511A66 ^ 75 D8 jnz short Morphed.00511A40 ; 还有区段未复制完,则继续
00511A68 8B55 F4 mov edx,dword ptr ss:[ebp-C] ; 循环结束,数据也全拷贝过去了
由于加载的地址变化,接着开始处理数据的重定位了!
如果我们不想在Dump出来后,修改基址,我们这里就需要跳过,将
je short Morphed.00511ACC===========改成jmp short Morphed.00511ACC就OK了:
00511A68 8B55 F4 mov edx,dword ptr ss:[ebp-C]
00511A6B 2B55 FC sub edx,dword ptr ss:[ebp-4] ; 这里开始比较基址
00511A6E 74 5C je short Morphed.00511ACC ; 如果是默认的,则跳过,否则进行数据重定位
00511A70 8B45 F4 mov eax,dword ptr ss:[ebp-C]
00511A73 89C3 mov ebx,eax
00511A75 035B 3C add ebx,dword ptr ds:[ebx+3C]
00511A78 8B9B A0000000 mov ebx,dword ptr ds:[ebx+A0]
00511A7E 85DB test ebx,ebx
00511A80 74 4A je short Morphed.00511ACC
00511A82 01C3 add ebx,eax
00511A84 8B43 04 mov eax,dword ptr ds:[ebx+4]
00511A87 85C0 test eax,eax
00511A89 74 41 je short Morphed.00511ACC
00511A8B 8D48 F8 lea ecx,dword ptr ds:[eax-8]
00511A8E D1E9 shr ecx,1
00511A90 8D7B 08 lea edi,dword ptr ds:[ebx+8]
00511A93 0FB707 movzx eax,word ptr ds:[edi]
00511A96 52 push edx
00511A97 89C2 mov edx,eax
00511A99 C1E8 0C shr eax,0C
00511A9C 8B75 F4 mov esi,dword ptr ss:[ebp-C]
00511A9F 66:81E2 FF0F and dx,0FFF
00511AA4 0333 add esi,dword ptr ds:[ebx]
00511AA6 01D6 add esi,edx
00511AA8 5A pop edx
00511AA9 48 dec eax
00511AAA 75 07 jnz short Morphed.00511AB3
00511AAC 89D0 mov eax,edx
接着开始处理IAT信息了:
00511B37 8B43 0C mov eax,dword ptr ds:[ebx+C]
00511B3A 85C0 test eax,eax
00511B3C 0F84 92000000 je 00511BD4
00511B42 8B4B 10 mov ecx,dword ptr ds:[ebx+10]
00511B45 01F1 add ecx,esi
00511B47 894D C4 mov dword ptr ss:[ebp-3C],ecx
00511B4A 8B0B mov ecx,dword ptr ds:[ebx]
00511B4C 85C9 test ecx,ecx
00511B4E 75 03 jnz short 00511B53
00511B50 8B4B 10 mov ecx,dword ptr ds:[ebx+10]
00511B53 01F1 add ecx,esi
00511B55 894D C0 mov dword ptr ss:[ebp-40],ecx
00511B58 01F0 add eax,esi
00511B5A 50 push eax
00511B5B E8 72F7FFFF call 005112D2
00511B60 8986 00040000 mov dword ptr ds:[esi+400],eax
00511B66 8B45 10 mov eax,dword ptr ss:[ebp+10]
00511B69 FF10 call dword ptr ds:[eax]
00511B6B E8 09F8FFFF call 00511379
00511B70 85C0 test eax,eax
00511B72 0F84 28010000 je 00511CA0
00511B78 89C7 mov edi,eax
00511B7A 8B4D C0 mov ecx,dword ptr ss:[ebp-40]
00511B7D 8B11 mov edx,dword ptr ds:[ecx]
00511B7F 85D2 test edx,edx
00511B81 74 49 je short 00511BCC
00511B83 F7C2 00000080 test edx,80000000
00511B89 74 08 je short 00511B93
00511B8B 81E2 FFFFFF7F and edx,7FFFFFFF
00511B91 EB 04 jmp short 00511B97
00511B93 01F2 add edx,esi ; Morphe_1.004D0000
00511B95 42 inc edx
00511B96 42 inc edx
00511B97 52 push edx
00511B98 E8 3DF7FFFF call 005112DA
00511B9D 8996 00040000 mov dword ptr ds:[esi+400],edx
00511BA3 57 push edi
00511BA4 8B45 0C mov eax,dword ptr ss:[ebp+C]
00511BA7 FF10 call dword ptr ds:[eax] ; 取IAT函数的地址
00511BA9 E8 CBF7FFFF call 00511379 ; 对IAT函数名进行清0处理
00511BAE E8 3FF7FFFF call 005112F2 ; 对IAT地址进行加密
00511BB3 C786 00040000 0>mov dword ptr ds:[esi+400],0
00511BBD 8B4D C4 mov ecx,dword ptr ss:[ebp-3C]
00511BC0 8901 mov dword ptr ds:[ecx],eax ; 将得到虚拟地址保存到IAT地址表
00511BC2 8345 C4 04 add dword ptr ss:[ebp-3C],4 ; 移动到下一个IAT指针
00511BC6 8345 C0 04 add dword ptr ss:[ebp-40],4
00511BCA ^ EB AE jmp short 00511B7A ; 一个DLL模块完毕
00511BCC 83C3 14 add ebx,14 ; 移动指针,进行下一个模块
00511BCF ^ E9 63FFFFFF jmp 00511B37
下面的代码是对IAT地址的加密:
005112F2 60 pushad
005112F3 89C3 mov ebx,eax
005112F5 6A 00 push 0
005112F7 68 2E646C6C push 6C6C642E
005112FC 68 656C3332 push 32336C65
00511301 68 6B65726E push 6E72656B
===============这个PUSH其实是将LoadLibraryA函数名入栈
00511306 54 push esp
00511307 8B45 10 mov eax,dword ptr ss:[ebp+10]
0051130A FF10 call dword ptr ds:[eax]
0051130C 83C4 10 add esp,10 ; 获取DLL的基址
0051130F 89C7 mov edi,eax
00511311 6A 00 push 0
00511313 68 6C6C6F63 push 636F6C6C
00511318 68 75616C41 push 416C6175
0051131D 68 56697274 push 74726956
===============这个PUSH其实是将VirtualAlloc函数名入栈
00511322 54 push esp
00511323 57 push edi
00511324 8B45 0C mov eax,dword ptr ss:[ebp+C] ; 获取VirtualAlloc函数的地址
00511327 FF10 call dword ptr ds:[eax]
00511329 83C4 10 add esp,10
0051132C 89C1 mov ecx,eax
0051132E 85C0 test eax,eax
00511330 0F84 6A090000 je Morphed.00511CA0
00511336 6A 04 push 4 ; 调用VirtualAlloc分配空间
00511338 68 00300000 push 3000 ; 大小是0x18,即24个字节
0051133D 6A 18 push 18
0051133F 6A 00 push 0
00511341 FFD1 call ecx
00511343 89C6 mov esi,eax ; 将分配的空间保存到ESI
00511345 83C0 14 add eax,14 ; 在分配的地址上,先空出0x14字节
00511348 50 push eax
00511349 0F31 rdtsc==============获取当前的time stamp值
0051134B 89C2 mov edx,eax========只取其低32位
0051134D 58 pop eax
0051134E 29D3 sub ebx,edx ; 将真实地址减去上面得到的值
00511350 8918 mov dword ptr ds:[eax],ebx ; 然后复制到刚才的位置
00511352 C706 6A0050A1 mov dword ptr ds:[esi],A150006A
00511358 8946 04 mov dword ptr ds:[esi+4],eax
0051135B C646 08 05 mov byte ptr ds:[esi+8],5
0051135F 8956 09 mov dword ptr ds:[esi+9],edx
00511362 C746 0D 8944240>mov dword ptr ds:[esi+D],4244489
00511369 66:C746 11 58C3 mov word ptr ds:[esi+11],0C358
0051136F C646 13 E8 mov byte ptr ds:[esi+13],0E8
00511373 897424 1C mov dword ptr ss:[esp+1C],esi
00511377 61 popad ; 上面开始硬编码,让程序能解析出真实IAT地址
00511378 C3 retn
对IAT函数名的处理:
00511379 60 pushad
0051137A 8B9E 00040000 mov ebx,dword ptr ds:[esi+400]
00511380 C603 00 mov byte ptr ds:[ebx],0 ; 填充0字节
00511383 43 inc ebx
00511384 803B 00 cmp byte ptr ds:[ebx],0
00511387 ^ 75 F7 jnz short 00511380
00511389 61 popad
0051138A C3 retn
经过加密后的IAT地址为VirtualAlloc申请到的地址,此后程序调用都需要通过上面硬编码的一段代码来获取到真实地址,并
跳转过去。硬编码的代码格式如下:
004E0000 6A 00 push 0
004E0002 50 push eax
004E0003 A1 14004E00 mov eax,dword ptr ds:[4E0014]
004E0008 05 029F730A add eax,0A739F02
004E000D 894424 04 mov dword ptr ss:[esp+4],eax
004E0011 58 pop eax
004E0012 C3 retn
004E0013 E8 267B0C72 call 725A7B3E
004E0018 0000 add byte ptr ds:[eax],al
为了避开IAT的加密,我们只需要将下面2个CALL给NOP掉就OK了:
00511BA9 call 00511379 ; 对IAT函数名进行清0处理
00511BAE call 005112F2 ; 对IAT地址进行加密
再做了上面的修改之后,我们F9运行:
005110D3 894C24 04 mov dword ptr ss:[esp+4],ecx
005110D7 50 push eax
005110D8 C3 retn //中断在这里了,ESP断点
F8单步一下,OK,这里就是我们的OEP了,哈!
004D29D4 E8 03170000 call 004D40DC
004D29D9 ^ E9 78FEFFFF jmp 004D2856
004D29DE 8BFF mov edi,edi
004D29E0 55 push ebp
004D29E1 8BEC mov ebp,esp
然后DUMP,再修复,一切OK!
——————————————————————————–
【版权声明】: 转载请注明作者并保持文章的完整, 谢谢!
2009年01月31日 22:07:58