【文章标题】: 简单修复Themida加壳的VC7+去除软件自校验
【文章作者】: wuhanqi
【作者邮箱】: wuhanqi@qq.com
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
题外话:
其实这个VC7的程序我本来都放一边了,因为实在是找不到对比OEP的无壳程序,可是昨天Nisy 老大发给我一个蛮有意思的注册验证程序,DJ Java Decompiler 3.11,无壳的,我用OD载入后一看到Push 60就一下子想起了这个TMD加壳的VC7,遂有了此文。
加壳程序是国产的。我就不透漏名称了。
这就是无壳程序的OEP:
00477F88 > $ 6A 60 PUSH 60
00477F8A . 68 30805000 PUSH 00508030
00477F8F . E8 744D0000 CALL 0047CD08
00477F94 . BF 94000000 MOV EDI,94
00477F99 . 8BC7 MOV EAX,EDI
00477F9B . E8 70FEFFFF CALL 00477E10
00477FA0 . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
00477FA3 . 8BF4 MOV ESI,ESP
00477FA5 . 893E MOV DWORD PTR DS:[ESI],EDI
00477FA7 . 56 PUSH ESI ; /pVersionInformation
00477FA8 . FF15 78934F00 CALL DWORD PTR DS:[<&KERNEL32.GetVersion>; \GetVersionExA
00477FAE . 8B4E 10 MOV ECX,DWORD PTR DS:[ESI+10]
00477FB1 . 890D CC465400 MOV DWORD PTR DS:[5446CC],ECX
00477FB7 . 8B46 04 MOV EAX,DWORD PTR DS:[ESI+4]
00477FBA . A3 D8465400 MOV DWORD PTR DS:[5446D8],EAX
00477FBF . 8B56 08 MOV EDX,DWORD PTR DS:[ESI+8]
00477FC2 . 8915 DC465400 MOV DWORD PTR DS:[5446DC],EDX
00477FC8 . 8B76 0C MOV ESI,DWORD PTR DS:[ESI+C]
00477FCB . 81E6 FF7F0000 AND ESI,7FFF
00477FD1 . 8935 D0465400 MOV DWORD PTR DS:[5446D0],ESI
00477FD7 . 83F9 02 CMP ECX,2
00477FDA . 74 0C JE SHORT 00477FE8
00477FDC . 81CE 00800000 OR ESI,8000
00477FE2 . 8935 D0465400 MOV DWORD PTR DS:[5446D0],ESI
00477FE8 > C1E0 08 SHL EAX,8
00477FEB . 03C2 ADD EAX,EDX
00477FED . A3 D4465400 MOV DWORD PTR DS:[5446D4],EAX
00477FF2 . 33F6 XOR ESI,ESI
00477FF4 . 56 PUSH ESI ; /pModule => NULL
00477FF5 . 8B3D 5C934F00 MOV EDI,DWORD PTR DS:[<&KERNEL32.GetModu>; |kernel32.GetModuleHandleA
00477FFB . FFD7 CALL EDI ; \GetModuleHandleA
00477FFD . 66:8138 4D5A CMP WORD PTR DS:[EAX],5A4D
00478002 . 75 1F JNZ SHORT 00478023
废话不多说了,OD载入程序打开内存镜像看TMDBASE是多少,565000,用编辑工具修改一下Nooby牛的脚本,然后跑脚本...这个过程我就不多叙述了。
脚本结束后停在OEP附近的第一个CALL中。
00471940 68 A0F14600 push 0046F1A0
00471945 64:A1 00000000 mov eax,dword ptr fs:[0]
0047194B 50 push eax
0047194C 8B4424 10 mov eax,dword ptr ss:[esp+10]
00471950 896C24 10 mov dword ptr ss:[esp+10],ebp
00471954 8D6C24 10 lea ebp,dword ptr ss:[esp+10]
00471958 2BE0 sub esp,eax
0047195A 53 push ebx
0047195B 56 push esi
0047195C 57 push edi
0047195D 8B45 F8 mov eax,dword ptr ss:[ebp-8]
00471960 8965 E8 mov dword ptr ss:[ebp-18],esp
00471963 50 push eax
00471964 8B45 FC mov eax,dword ptr ss:[ebp-4]
00471967 C745 FC FFFFFFF>mov dword ptr ss:[ebp-4],-1
0047196E 8945 F8 mov dword ptr ss:[ebp-8],eax
00471971 8D45 F0 lea eax,dword ptr ss:[ebp-10]
00471974 64:A3 00000000 mov dword ptr fs:[0],eax
0047197A C3 retn
看堆栈:
0012FF54 007FE908 SuperRec.007FE908
0012FF58 0049F740 SuperRec.0049F740 第②句PUSH
0012FF5C 00000060 第①句PUSH
0012FF60 00400208 ASCII " "
0012FF64 0012FFC4
0012FF68 00792EB8 SuperRec.00792EB8
可以猜出前三句OEP为
push 60
push 0049f740
call 00471940
接下来走出这个call。
007FE908 68 16EB2925 push 2529EB16
007FE90D ^ E9 29B9FFFF jmp 007FA23B
单步走过上面的jmp,来到:
007FA23B 6A 00 push 0
007FA23D 0F89 15000000 jns 007FA258
打开内存镜像。在00401000段F2,然后F9,停在OEP附近第二个call处。
00470540 3D 00100000 cmp eax,1000
00470545 73 0E jnb short 00470555
00470547 F7D8 neg eax
00470549 03C4 add eax,esp
0047054B 83C0 04 add eax,4
0047054E 8500 test dword ptr ds:[eax],eax
00470550 94 xchg eax,esp
00470551 8B00 mov eax,dword ptr ds:[eax]
00470553 50 push eax
00470554 C3 retn
此时可以对比无壳的程序看一下代码,这是无壳的前几句:
00477F88 > $ 6A 60 PUSH 60
00477F8A . 68 30805000 PUSH 00508030
00477F8F . E8 744D0000 CALL 0047CD08
00477F94 . BF 94000000 MOV EDI,94
00477F99 . 8BC7 MOV EAX,EDI
00477F9B . E8 70FEFFFF CALL 00477E10
00477FA0 . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
再看看我们的寄存器。
EAX 00000094
ECX 00020048
EDX 00020048
EBX 0012FF64
ESP 0012FEDC
EBP 0012FF5C
ESI F63945F1
EDI 00000094
EIP 00470540 SuperRec.00470540
的确EDI与EAX都变红而且都为94
那接下来三句就是
mov edi,94
mov eax,edi
call 00470540
接下来走出这个call:
007FE91A 68 7FE9110B push 0B11E97F
007FE91F ^ E9 17B9FFFF jmp 007FA23B
依旧走过这个jmp后在code段下断再F9
此时停在这里:
019F8947 FF33 push dword ptr ds:[ebx] ; kernel32.GetVersionExA
019F8949 ^ E9 F580F5FF jmp 01950A43
看信息窗口:
ds:[004932AC]=77AB9D76 (kernel32.GetVersionExA)
这个地址很关键,我们可以对照着看无壳程序的OEP,在下一个call就是call这个函数,
那这个call就应该这样写了:call dword ptr ds:[004932ac]
又因为VC7的OEP相对固定,
那接下来几句就是:
MOV DWORD PTR SS:[EBP-18],ESP
MOV ESI,ESP
MOV DWORD PTR DS:[ESI],EDI
PUSH ESI
call dword ptr ds:[004932ac]
插句题外话:其实你记不住这个地址也没问题,因为GetVersionExA和GetModuleHandleA在IAT输入表里很接近.而程序下面就就有调用GetModuleHandleA,找到了GetModuleHandleA就等于找到了GetVersionExA.
我们再继续走jmp下code段的断点。此过程我就不做记录了。
值的注意的是走到这里的时候:
019FD621 F3:A4 rep movs byte ptr es:[edi],byte ptr ds:[>
019FD623 68 016D0000 push 6D01
019FD628 890C24 mov dword ptr ss:[esp],ecx
在019fd621需要是F7再F8,否则程序跑飞,前功尽弃。
最终我们停在这里:
0046EF05 A3 00B65100 mov dword ptr ds:[51B600],eax
0046EF0A 8B56 08 mov edx,dword ptr ds:[esi+8]
0046EF0D 8915 04B65100 mov dword ptr ds:[51B604],edx
0046EF13 8B76 0C mov esi,dword ptr ds:[esi+C]
0046EF16 81E6 FF7F0000 and esi,7FFF
0046EF1C 8935 F8B55100 mov dword ptr ds:[51B5F8],esi
0046EF22 83F9 02 cmp ecx,2
0046EF25 74 0C je short 0046EF33
0046EF27 81CE 00800000 or esi,8000
0046EF2D 8935 F8B55100 mov dword ptr ds:[51B5F8],esi
0046EF33 C1E0 08 shl eax,8
0046EF36 03C2 add eax,edx
0046EF38 A3 FCB55100 mov dword ptr ds:[51B5FC],eax
0046EF3D 33F6 xor esi,esi
0046EF3F 56 push esi
0046EF40 8B3D A8324900 mov edi,dword ptr ds:[4932A8] ; kernel32.GetModuleHandleA
0046EF46 FFD7 call edi
0046EF48 66:8138 4D5A cmp word ptr ds:[eax],5A4D
上面距离call GetVersionExA 这个函数依旧还有三句话:
这是无壳程序的那三句话:
00477FAE . 8B4E 10 MOV ECX,DWORD PTR DS:[ESI+10]
00477FB1 . 890D CC465400 MOV DWORD PTR DS:[5446CC],ECX
00477FB7 . 8B46 04 MOV EAX,DWORD PTR DS:[ESI+4]
00477FBA . A3 D8465400 MOV DWORD PTR DS:[5446D8],EAX
这下有人晕了,这可怎么搞,没有地址怎么修复?
其实很简单,我们用无壳程序的这两句地址相减,即5446d8-5446cc=c
那我们需要的那个地址就应该是 51b600-c=51B5F4
那我们加壳程序的接下来三句就应该是:
MOV ECX,DWORD PTR DS:[ESI+10]
MOV DWORD PTR DS:[51B5F4],ECX
MOV EAX,DWORD PTR DS:[ESI+4]
总结一下被偷的OEP就是:
push 60
push 0049f740
call 00471940
mov edi,94
mov eax,edi
call 00470540
MOV DWORD PTR SS:[EBP-18],ESP
MOV ESI,ESP
MOV DWORD PTR DS:[ESI],EDI
PUSH ESI
call dword ptr ds:[004932ac]
MOV ECX,DWORD PTR DS:[ESI+10]
MOV DWORD PTR DS:[51B5F4],ECX
MOV EAX,DWORD PTR DS:[ESI+4]
这样脱好壳之后,运行程序,窗口一闪而过。
自校验。。
我用的是bp GetFileSize
因为程序是窗口一闪而过,所以要在显示窗口后再返回程序。
返回程序领空只需要F8即可来到这里:
0042A126 > \3B7424 20 cmp esi,dword ptr ss:[esp+20]
0042A12A . 74 14 je short 0042A140
0042A12C . 8B5424 24 mov edx,dword ptr ss:[esp+24]
0042A130 . 8B42 2C mov eax,dword ptr ds:[edx+2C]
0042A133 . 53 push ebx ; /lParam
0042A134 . 53 push ebx ; |wParam
0042A135 . 6A 12 push 12 ; |Message = WM_QUIT
0042A137 . 50 push eax ; |ThreadId
0042A138 . 90 nop ; |
0042A139 . E8 50DB1877 call user32.PostThreadMessageW ; \PostThreadMessageW
0042A13E . EB 15 jmp short 0042A155
0042A140 > 57 push edi
0042A141 . E8 FC580500 call 0047FA42
0042A146 . 8B8D 68AB0200 mov ecx,dword ptr ss:[ebp+2AB68]
0042A14C . 83C4 04 add esp,4
0042A14F . C701 08000000 mov dword ptr ds:[ecx],8
0042A155 > 8D4C24 28 lea ecx,dword ptr ss:[esp+28]
0042A159 . 885C24 40 mov byte ptr ss:[esp+40],bl
0042A15D . E8 6A700500 call 004811CC
0042A162 . 5D pop ebp
把42a12a的74改成eb即可。
第一次看到结束程序用PostThreadMessageW的这个函数的。。
--------------------------------------------------------------------------------
【经验总结】
其实VC7的OEP真的很好修复的.不是一般的好修复.
只不过很难找到一个门当户对的无壳程序来对比OEP修复.
这次是我运气好,也感谢Nisy老大~呵呵.
--------------------------------------------------------------------------------
【版权声明】: 菜鸟一个,没啥版权..
2009年08月10日 8:59:29
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)