最近来论坛看看,发现一篇求助脱壳的帖子,反正目标已经是keygen了,也就干脆拿来作为例子,简单的分析一下yoda的保护以及扒壳的方法。
原贴见
http://bbs.pediy.com/showthread.php?t=67591
先用PEiD看一下,发现
yoda's Protector 1.03.3 -> Ashkbiz Danehkar
值得注意的地方是每次我用peid查壳,都要去看一下linker info。这是一个很容易被忽略的地方。不能小看这个信息对定位oep的重要性,这个在下面会讲到。
此处版本为7.10,初步推断为VC7.1
用OD载入,
004176ED > E8 03000000 call 004176F5
004176F2 EB 01 jmp short 004176F5
004176F4 E8 BB550000 call 0041CCB4
004176F9 00E8 add al, ch
004176FB 0300 add eax, dword ptr [eax]
004176FD 0000 add byte ptr [eax], al
004176FF EB 01 jmp short 00417702
00417701 E8 E88E0000 call 004205EE
00417706 00E8 add al, ch
00417708 0300 add eax, dword ptr [eax]
0041770A 0000 add byte ptr [eax], al
0041770C EB 01 jmp short 0041770F
0041770E E9 E8810000 jmp 0041F8FB
00417713 00E8 add al, ch
连续3个call,都有花指令,不过没关系,F7跟进去,来到
00417795 33C0 xor eax, eax
00417797 64:FF30 push dword ptr fs:[eax]
0041779A 64:8920 mov dword ptr fs:[eax], esp
0041779D 4B dec ebx
0041779E CC int3
0041779F C3 retn
预告着下一步要找的是异常处理,执行到int3时看堆栈,得到异常处理在417707
ctrl+g输入地址,f2下断,然后按f9执行。
出现一个call,很明显的看出下面2个花指令jmp,无所谓,按f7跟入2个call后出现相同的异常跳转,同样的方法重复几次,将来到
00417736 83FB 55 cmp ebx, 55
00417739 E8 03000000 call 00417741
0041773E EB 01 jmp short 00417741
00417740 C2 752D retn 2D75
00417743 E8 03000000 call 0041774B
00417748 EB 01 jmp short 0041774B
0041774A E8 60E80000 call 00425FAF
0041774F 0000 add byte ptr [eax], al
00417751 5D pop ebp
00417752 81ED 07E24000 sub ebp, 0040E207
00417758 8BD5 mov edx, ebp
0041775A 81C2 56E24000 add edx, 0040E256
00417760 52 push edx
单步几次后又会出现异常跳转,无聊的重复,一直跟下去,来到
0041774B 60 pushad
0041774C E8 00000000 call 00417751
00417751 5D pop ebp
00417752 81ED 07E24000 sub ebp, 0040E207
00417758 8BD5 mov edx, ebp
0041775A 81C2 56E24000 add edx, 0040E256
00417760 52 push edx
00417761 E8 01000000 call 00417767
一路单步,会经过一段乱序的解码过程,直到出现
00417849 ^\E2 9C loopd short 004177E7
0041784B E8 A98C25CC call CC6704F9
00417850 324B 3B xor cl, byte ptr [ebx+3B]
00417853 0FBCAF C2166414 bsf ebp, dword ptr [edi+146416C2]
0041785A 14 92 adc al, 92
0041785C 65:56 push esi
0041785E 2F das
0041785F DE545A 1D ficom word ptr [edx+ebx*2+1D]
00417863 A4 movs byte ptr es:[edi], byte ptr [esi>
00417864 66:5F pop di
00417866 20D7 and bh, dl
00417868 D281 9D1BA960 rol byte ptr [ecx+60A91B9D], cl
0041786E 3F aas
在417849这里下断,f9几次,直到他的下一条代码完整解码后取消断点,在
00417849 ^\E2 9C loopd short 004177E7
0041784B E8 03000000 call 00417853
41784B处下断,F9完成这段解码。
继续单步,出现同要的解码,找到loopd以后同要的方法执行完,经过一段乱序后来到
00417F57 55 push ebp
00417F58 8BEC mov ebp, esp
00417F5A 53 push ebx
00417F5B 51 push ecx
00417F5C 52 push edx
00417F5D 56 push esi
00417F5E 57 push edi
00417F5F 36:8B45 08 mov eax, dword ptr [ebp+8]
00417F63 36:8B4D 0C mov ecx, dword ptr [ebp+C]
00417F67 8BF8 mov edi, eax
00417F69 33C0 xor eax, eax
00417F6B 33DB xor ebx, ebx
00417F6D 33D2 xor edx, edx
00417F6F 8A07 mov al, byte ptr [edi]
00417F71 F7E2 mul edx
00417F73 03D8 add ebx, eax
00417F75 42 inc edx
00417F76 47 inc edi
00417F77 ^ E2 F6 loopd short 00417F6F
00417F79 93 xchg eax, ebx
00417F7A 5F pop edi
00417F7B 5E pop esi
00417F7C 5A pop edx
00417F7D 59 pop ecx
00417F7E 5B pop ebx
断在417f79,然后f9过循环,继续往下走
00418393 8DB5 440B4100 lea esi, dword ptr [ebp+410B44]
00418399 8D85 72EE4000 lea eax, dword ptr [ebp+40EE72]
0041839F 3E:8946 08 mov dword ptr [esi+8], eax
004183A3 8BFD mov edi, ebp
004183A5 8D85 62074100 lea eax, dword ptr [ebp+410762]
004183AB 33DB xor ebx, ebx
004183AD 50 push eax
004183AE 64:FF33 push dword ptr fs:[ebx]
004183B1 64:8923 mov dword ptr fs:[ebx], esp
004183B4 66:B8 0400 mov ax, 4
004183B8 EB 01 jmp short 004183BB
注意这个jmp以后是一个int3,注意看堆栈。
然后是一段恢复异常的过程,会走到ntdll去,一路过,会遇到一个sysenter,然后回到主程序继续
004183BE 33DB xor ebx, ebx
004183C0 64:8F03 pop dword ptr fs:[ebx]
004183C3 83C4 04 add esp, 4
004183C6 3C 04 cmp al, 4
004183C8 74 05 je short 004183CF
004183CA EB 01 jmp short 004183CD
004183CC - E9 61C3C3E8 jmp E9054732
004183D1 0300 add eax, dword ptr [eax]
004183D3 0000 add byte ptr [eax], al
004183D5 EB 01 jmp short 004183D8
004183D7 C2 8BC4 retn 0C48B
004183DA 8BCD mov ecx, ebp
004183DC 81C1 98EE4000 add ecx, 0040EE98
004183E2 83C0 04 add eax, 4
004183E5 8B10 mov edx, dword ptr [eax]
004183E7 81FA 00000100 cmp edx, 10000
004183ED ^ 72 F3 jb short 004183E2
004183EF 3BD1 cmp edx, ecx
004183F1 ^ 73 EF jnb short 004183E2
004183F3 8B1A mov ebx, dword ptr [edx]
004183F5 81E3 FFFF0000 and ebx, 0FFFF
004183FB 81FB 4D5A0000 cmp ebx, 5A4D
00418401 ^ 75 DF jnz short 004183E2
00418403 8995 E0074100 mov dword ptr [ebp+4107E0], edx
00418409 C3 retn
一路单步,会来到这里,看到对loader的导入表进行处理
00418477 8B85 E0074100 mov eax, dword ptr [ebp+4107E0]
0041847D 0340 3C add eax, dword ptr [eax+3C]
00418480 05 80000000 add eax, 80
00418485 8B08 mov ecx, dword ptr [eax]
00418487 038D E0074100 add ecx, dword ptr [ebp+4107E0]
0041848D 83C1 10 add ecx, 10
00418490 8B01 mov eax, dword ptr [ecx]
00418492 0385 E0074100 add eax, dword ptr [ebp+4107E0]
00418498 8B18 mov ebx, dword ptr [eax]
0041849A 899D 21DF4000 mov dword ptr [ebp+40DF21], ebx
004184A0 83C0 04 add eax, 4
004184A3 8B18 mov ebx, dword ptr [eax]
004184A5 899D 25DF4000 mov dword ptr [ebp+40DF25], ebx
004184AB 8DBD 26DB4000 lea edi, dword ptr [ebp+40DB26]
004184B1 8D95 2DDF4000 lea edx, dword ptr [ebp+40DF2D]
004184B7 8D8D 29E04000 lea ecx, dword ptr [ebp+40E029]
004184BD 83C1 02 add ecx, 2
004184C0 52 push edx
004184C1 51 push ecx
004184C2 8BC7 mov eax, edi
004184C4 50 push eax
004184C5 BB 21DF4000 mov ebx, 0040DF21
004184CA FF541D 00 call dword ptr [ebp+ebx] ; kernel32.LoadLibraryA
004184CE 8BF0 mov esi, eax
004184D0 57 push edi
004184D1 E8 BF070000 call 00418C95
004184D6 83C4 04 add esp, 4
004184D9 03F8 add edi, eax
004184DB 59 pop ecx
然后就是一些anti了,
00417937 E8 03FDFFFF call 0041763F ; jmp 到 kernel32.GetTickCount
0041793C 8985 68084100 mov dword ptr [ebp+410868], eax
00417942 E8 03000000 call 0041794A
00417947 EB 01 jmp short 0041794A
00417949 - E9 E8C30A00 jmp 004C3D36
0041794E 00E8 add al, ch
00417950 0300 add eax, dword ptr [eax]
00417952 0000 add byte ptr [eax], al
00417954 EB 01 jmp short 00417957
00417956 E8 E82BFDFF call 003EA543
0041795B FF89 85F80741 dec dword ptr [ecx+4107F885]
00417961 006A 00 add byte ptr [edx], ch
00417964 8D85 350C4100 lea eax, dword ptr [ebp+410C35]
0041796A 50 push eax
0041796B E8 1DFDFFFF call 0041768D ; jmp 到 User32.FindWindowA
00417970 8985 FC074100 mov dword ptr [ebp+4107FC], eax
00417976 E8 18FDFFFF call 00417693 ; jmp 到 User32.GetTopWindow
0041797B 8985 04084100 mov dword ptr [ebp+410804], eax
00417981 E8 03000000 call 00417989
00417986 EB 01 jmp short 00417989
00417988 C2 E827 retn 27E8
0041798B FC cld
0041798C FFFF ??? ; 未知命令
0041798E 50 push eax
0041798F 50 push eax
00417990 E8 68FCFFFF call 004175FD ; jmp 到 kernel32.GetPriorityClass
00417995 8985 0C084100 mov dword ptr [ebp+41080C], eax
0041799B 58 pop eax
0041799C 68 80000000 push 80
004179A1 50 push eax
004179A2 E8 50FCFFFF call 004175F7 ; jmp 到 kernel32.SetPriorityClass
004179A7 F785 F4074100 0>test dword ptr [ebp+4107F4], 8
004179B1 75 07 jnz short 004179BA
004179B3 6A 01 push 1
004179B5 E8 BBFCFFFF call 00417675 ; jmp 到 User32.BlockInput
004179BA BA 00000000 mov edx, 0
004179BF F785 14084100 0>test dword ptr [ebp+410814], 1
004179C9 75 05 jnz short 004179D0
004179CB BA 000000F0 mov edx, F0000000
004179D0 52 push edx
004179D1 6A 01 push 1
004179D3 8D85 F6DE4000 lea eax, dword ptr [ebp+40DEF6]
这里走的时候要细心,注意千万不要让他popad,走到那里就挂了。
值得注意的地方是,它会枚举所有进程和线程,把他们suspend一下再恢复,这个要手动跳过,并且这些anti会多次重复,然后比较执行时间,我跟的时候手动改了一下那个函数,让他始终返回固定值。
完了发现oep被偷,不过程序是跑起来了,没关系,直接f9停下来,切换到程序领空
这个时候查找参考文本,发现已经是裸露的程序了,下一步是确定oep。
这个时候就要用到那个linker version了。其实也未必,由于导入表也是裸的,找一下关键函数的调用就可以了。我具体说一下方法。
我就近找了peid,它的linker version也是7.1
脱了简单的压缩壳后来到OEP
0045A8CC > $ 6A 60 push 60
0045A8CE . 68 10CF4200 push 0042CF10
0045A8D3 . E8 B4180000 call 0045C18C
0045A8D8 . BF 94000000 mov edi, 94
0045A8DD . 8BC7 mov eax, edi
0045A8DF . E8 5CFDFFFF call 0045A640
0045A8E4 . 8965 E8 mov dword ptr [ebp-18], esp
0045A8E7 . 8BF4 mov esi, esp
0045A8E9 . 893E mov dword ptr [esi], edi
0045A8EB . 56 push esi ; /pVersionInformation
0045A8EC . FF15 90114000 call dword ptr [<&KERNEL32.GetVersion>; \GetVersionExA
0045A8F2 . 8B4E 10 mov ecx, dword ptr [esi+10]
0045A8F5 . 890D 80A24600 mov dword ptr [46A280], ecx
0045A8FB . 8B46 04 mov eax, dword ptr [esi+4]
0045A8FE . A3 8CA24600 mov dword ptr [46A28C], eax
0045A903 . 8B56 08 mov edx, dword ptr [esi+8]
0045A906 . 8915 90A24600 mov dword ptr [46A290], edx
0045A90C . 8B76 0C mov esi, dword ptr [esi+C]
0045A90F . 81E6 FF7F0000 and esi, 7FFF
0045A915 . 8935 84A24600 mov dword ptr [46A284], esi
0045A91B . 83F9 02 cmp ecx, 2
0045A91E . 74 0C je short 0045A92C
0045A920 . 81CE 00800000 or esi, 8000
0045A926 . 8935 84A24600 mov dword ptr [46A284], esi
0045A92C > C1E0 08 shl eax, 8
0045A92F . 03C2 add eax, edx
0045A931 . A3 88A24600 mov dword ptr [46A288], eax
0045A936 . 33F6 xor esi, esi
0045A938 . 56 push esi ; /pModule => NULL
0045A939 . 8B3D AC104000 mov edi, dword ptr [<&KERNEL32.GetMo>; |kernel32.GetModuleHandleA
0045A93F . FFD7 call edi ; \GetModuleHandleA
由于从yoda壳得知肯定偷了头,所以要想办法找个位置让程序停下来,因为我们是带着壳调试的,没法预先在被释放的程序里面下断点。于是搜索一下GetModuleHandle的调用,经过比较很快就能找到oep
004048C6 . 6A 18 push 18
004048C8 . 68 00054100 push 00410500
004048CD . E8 06360000 call 00407ED8
004048D2 . BF 94000000 mov edi, 94
004048D7 . 8BC7 mov eax, edi
004048D9 . E8 12410000 call 004089F0
004048DE . 8965 E8 mov dword ptr [ebp-18], esp
004048E1 . 8BF4 mov esi, esp
004048E3 . 893E mov dword ptr [esi], edi
004048E5 . 56 push esi
004048E6 . FF15 14004100 call dword ptr [410014]
004048EC . 8B4E 10 mov ecx, dword ptr [esi+10]
004048EF . 890D E4524100 mov dword ptr [4152E4], ecx
004048F5 . 8B46 04 mov eax, dword ptr [esi+4]
004048F8 . A3 F0524100 mov dword ptr [4152F0], eax
004048FD . 8B56 08 mov edx, dword ptr [esi+8]
00404900 . 8915 F4524100 mov dword ptr [4152F4], edx
00404906 . 8B76 0C mov esi, dword ptr [esi+C]
00404909 . 81E6 FF7F0000 and esi, 7FFF
0040490F . 8935 E8524100 mov dword ptr [4152E8], esi
00404915 . 83F9 02 cmp ecx, 2
00404918 . 74 0C je short 00404926
0040491A . 81CE 00800000 or esi, 8000
00404920 . 8935 E8524100 mov dword ptr [4152E8], esi
00404926 > C1E0 08 shl eax, 8
00404929 . 03C2 add eax, edx
0040492B . A3 EC524100 mov dword ptr [4152EC], eax
00404930 . 33FF xor edi, edi
00404932 . 57 push edi
00404933 . FF15 0C004100 call dword ptr [41000C]
由于偷了头,所以跳过几个字节下硬件断点,CTRL+F2
然后停下来了,dump时填入oep:4048c6,修复下导入表就好了。
后话:
yoda这个系列的壳,终究还只能算压缩壳,带了一些anti,但是对程序本身的修改太少了,甚至连IAT都完整地保留了,等练熟了也是属于秒杀一类的。
Trackback(0)

|