壳:Themida.V1.9.2.0猛壳。
使用软件:OllyDBG1.1 ,ImportREC ,PEid
操作系统:XP SP2
附件:用Themida.V1.9.2.0加壳过的记事本
http://www.live-share.com/files/318431/unpackme.rar.html
--------------------------------------------------------------------------------
开始:
此壳会Anit-Debug,所以先设置好OD的PhantOm插件,将"hide from PEB",
"change Olly caption -[Drivers]-","load driver","hide OllyDbg windows"打上勾。
在调试选项中勿略所有异常。
然后F3加载,unpackme.exe
提示代码可能被压缩过,取消分析。
入口点为01014014。
01014014 > B8 >mov eax, 0 //入口点
01014019 60 pushad
0101401A 0BC>or eax, eax
0101401C 74 >je short 01014086
0101401E E8 >call 01014023
01014023 58 pop eax
01014024 05 >add eax, 53
01014029 803>cmp byte ptr [eax], 0E9
0101402C 75 >jnz short 01014041
打开[Memory map],在unpackme代码段设置内存写入断点,F9走
异常后,Shift+F7,Alt+F9,又异常,Shift+F7,Alt+F9走到0083DF33,
往上看,原来这里调用了个异常(0083DF2D处)!
0083DF22 57 push edi
0083DF23 8D7>lea edi, dword ptr [ebp-3C]
0083DF26 F3:>rep movs dword ptr es:[edi], dword>
0083DF28 5F pop edi
0083DF29 8D4>lea eax, dword ptr [ebp-50]
0083DF2C 50 push eax
0083DF2D FF1>call dword ptr [<&ntdll.RtlRaiseExc>; ntdll.RtlRaiseException
0083DF33 5E pop esi ; unpackme.010D4A42
0083DF33 的ESI发现 "Themida Professional (c)2007 Oreans Technologies"字符串
Themida专业版!走吧,F9
内存写入中断出在01119D6B
01119D6B F3:>rep movs byte ptr es:[edi], byte ptr [esi] //往[edi]处写入代码,写ecx=6000h个字节
01119D6D C68>mov byte ptr [ebp+9742145], 56
01119D74 68 >push D41F6D39
01119D79 FFB>push dword ptr [ebp+97425DD]
01119D7F 8D8>lea eax, dword ptr [ebp+977C6A1]
按F7,F8走过写部份,F9继续上路。
中断在011287F5处,
011287F5 891>mov dword ptr [ebx], edx
011287F7 5B pop ebx
011287F8 5A pop edx
011287F9 F9 stc
011287FA AD lods dword ptr [esi]
011287F5处的edx=comdlg32.PageSetupDlgW,ebx=unpackme.010012C4,
看来是写入PageSetupDlgW的调用地址。
删除代码内存写入断点,F8走
走到01128A90处
01128A90 803>cmp byte ptr [edi], 90
01128A93 0F8>je 01128AC4
01128A99 F5 cmc
01128A9A 50 push eax
01128A9B B8 >mov eax, 5
01128AA0 01C>add edi, eax
01128AA2 8B0>mov eax, dword ptr [esp]
01128AA5 83C>add esp, 4
01128AA8 FC cld
01128AA9 E9 >jmp 01128C93
01128A90处进行比较,[edi]处是否90,然后去[edi]处往上看,发现很多压栈
后的代码就90,即调用函数是空的。现在这里是如果发现是空的就重写。
接着走,F8
走到01128B77处,准备写入comdlg32.PageSetupDlgW的调用地址
这里写一个字节,al=E8 (即call的机器码)
01128B77 AA stos byte ptr es:[edi]
01128B78 60 pushad
01128B79 BE >mov esi, 769CD237
01128B7E 0F8>jno 01128B86
01128B84 8BF>mov esi, ecx
F8走到01128CD8D处,写调用(call comdlg32.PageSetupDlgW)地址。
01128C8D AB stos dword ptr es:[edi]
01128C8E 60 pushad
01128C8F 0FB>movzx eax, ax
01128C92 61 popad
01128C93 AD lods dword ptr [esi]
F8走,又倒回到01128A90处,(这里edi=01006576)
01128A90 803>cmp byte ptr [edi], 90
01128A93 0F8>je 01128AC4
01128A99 F5 cmc
01128A9A 50 push eax
01128A9B B8 >mov eax, 5
F8走,走到01128B77向[edi]写入E8, 即Call的机器码,F8走到01128C8D写
调用(call comdlg32.PageSetupDlgW)地址。
F8走过漫长的一断,到01125CDC,F7进入这个Call
01125CDC E8 >call 01125CF0
01125CE1 43 inc ebx
01125CE2 D22>shl byte ptr [edx], cl
01125CE4 3F aas
01125CE5 0C >or al, 0B7
01125CE7 C8 >enter 0A4DE, 89
然后F8继续走,同样经过漫长的一断走到 0112648F这个Call,F7走入
0112648F E8 >call 011264A5
01126494 CE into
01126495 AF scas dword ptr es:[edi]
01126496 CF iretd
01126497 2B5>sub ebx, dword ptr [ebx-66]
经过一段,走到011265F1,这里F7单步走过
011265F1 E8 >call 01126605
011265F6 4A dec edx
011265F7 D8E>fsub st, st(1)
011265F9 49 dec ecx
011265FA BD >mov ebp, C2FFB29A
F8继续走,走到 0112664E
0112664E 83B>cmp dword ptr [ebp+9741E31], 1
01126655 0F8>je 0112670E //跳到IAT加密码部份
0112665B F8 clc
0112665C 3B8>cmp ecx, dword ptr [ebp+97401F9]
01126662 0F8>je 0112670E //跳到IAT加密码部份
01126668 60 pushad
01126669 0FB>movzx eax, cx
0112666C 8AE>mov ah, dh
0112666E 61 popad
0112666F E9 >jmp 0112667C
je 0112670E是跳到IAT表加密码的地方,所以这里用NOP修改je 0112670E。并把程序里
所有je 0112670E都改成NOP指令,使其取消加密IAT表。
用Ctrl+F找"je 0112670E",找到以下四个地址,这四个地址的汇编指令改成NOP
01126655
01126662
01126682
011266A3
由于改动过代码,壳的自检验未能通过,所以要修改01125EC7处的代码,
原代码为 je 01125F3D,我们修改为jmp 01125F3D
然的在ntdll.dll 的ZwFreeVirtualMemory下断点,然后F9继续走吧,
中断在7C92DA48处,取消7C92DA48处的中断,直接在7C92DA54下F2断点。
7C92DA48 > B8 >mov eax, 53
7C92DA4D BA >mov edx, 7FFE0300
7C92DA52 FF1>call dword ptr [edx]
7C92DA54 C2 >retn 10 //在这里下F2下断点
这里我们需要在7C92DA54处下断点和代码段下写入断点,按F9走,
中断在00828F5F,用F8走到 程序领空
00828F5F 8BF8 mov edi, eax
00828F61 85FF test edi, edi
00828F63 0F8C B39C0100 jl 00842C1C
00828F69 33C0 xor eax, eax
00828F6B 40 inc eax
00828F6C 5F pop edi
00828F6D 5E pop esi
00828F6E 5D pop ebp
00828F6F C2 1000 retn 10
F8继续走,走到0112BA2F程序领空,
0112BA2F F5 cmc
0112BA30 60 pushad
0112BA31 B9 5E2D4312 mov ecx, 12432D5E
0112BA36 60 pushad
0112BA37 0F80 00000000 jo 0112BA3D
0112BA3D 8BF0 mov esi, eax
然后在unpackme代码段下F2断点。
接着F9走,中断后用F8走到程序领空,然后查看代码段中断是否还有,没有的话用F2加入中断!
重复以上动作,几次循还后会停在 01007568
01007568 68 BA75>push 010075BA
0100756D 64:A1 0>mov eax, dword ptr fs:[0]
01007573 50 push eax
01007574 8B4424 >mov eax, dword ptr [esp+10]
01007578 896C24 >mov dword ptr [esp+10], ebp
0100757C 8D6C24 >lea ebp, dword ptr [esp+10]
然后F8单步走,然后将光标向下移到最近一个retn 指令,在retn处一次F4,然后F8
01119ED7 68 E07A>push 97C7AE0
01119EDC ^ E9 CC43>jmp 0109E2AD
01119EE1 68 BF7B>push 97C7BBF
01119EE6 ^ E9 C243>jmp 0109E2AD
01119EEB CC int3
01119EEC 41 inc ecx
然在代码段用F2下中断,F9走。
中断在 010ADB90处,
010ADB90 FF32 push dword ptr [edx] ; kernel32.GetModuleHandleA
010ADB92 ^ E9 5D07>jmp 0109E2F4
010ADB97 B8 FFFF>mov eax, -1
010ADB9C 01C1 add ecx, eax
010ADB9E 8B0424 mov eax, dword ptr [esp]
010ADBA1 ^ E9 7039>jmp 010A1516
这时记下[edx]的值,010010CC ,这里的010010CC是假的OEP地址。
然后按2次F8走过,然后下代码段F2中断。F9走到010073CC,
010073CC 3D 0B01>cmp eax, 10B
010073D1 74 1F je short 010073F2
010073D3 3D 0B02>cmp eax, 20B
010073D8 74 05 je short 010073DF
010073DA 895D E4 mov dword ptr [ebp-1C], ebx
010073DD EB 27 jmp short 01007406
010073DF 83B9 84>cmp dword ptr [ecx+84], 0E
从010073CC向上查找CC汇编代码,从找到的CC的后一个地址即为OEP地址。
01007399 CC int3
0100739A CC int3
0100739B CC int3
0100739C CC int3
0100739D F2: prefix repne: //真正的入口地址,从010010CC到这里的代码被偷了
0100739E 6BC5 57 imul eax, ebp, 57
010073A1 EE out dx, al
010073A2 ^ E3 90 jecxz short 01007334
010073A4 54 push esp
010073A5 4A dec edx
010073A6 A2 D6CA>mov byte ptr [E3AACAD6], al
010073AB 6C ins byte ptr es:[edi], dx
010073AC 36:2144>and dword ptr ss:[edi+ebp*4-1B],>
010073B1 BF A70B>mov edi, 18C70BA7
010073B6 EA 801F>jmp far B24B:ADD61F80
010073BD 46 inc esi
010073BE 91 xchg eax, ecx
010073BF A8 FC test al, 0FC
010073C1 0D 4B52>or eax, 176C524B
010073C6 A7 cmps dword ptr [esi], dword ptr e>
010073C7 13EA adc ebp, edx
010073C9 8929 mov dword ptr [ecx], ebp
010073CB E1 3D loopde short 0100740A
010073CD 0B01 or eax, dword ptr [ecx]
010073CF 0000 add byte ptr [eax], al
010073D1 74 1F je short 010073F2
010073D3 3D 0B02>cmp eax, 20B
010073D8 74 05 je short 010073DF
010073DA 895D E4 mov dword ptr [ebp-1C], ebx
010073DD EB 27 jmp short 01007406
这里找到的是0100739D。
由于从0100739D到010010CC处的代码被偷,所以我们从其它VC7程序的开头处复制一份到
0100739D处进行修补。修复如下:
VC7 开头的二进制代码(复制到0100739D到010073CC)
6A 70 68 98 18 00 01 E8 BF 01 00 00 33 DB 53 8B 3D CC 10 00 01 FF D7 66 81 38 4D 5A 75 1F 8B 48
3C 03 C8 81 39 50 45 00 00 75 12 0F B7 41 18 3D 0B 01 00 00
0100739D 6A 70 push 70
0100739F 68 98180001 push 01001898
010073A4 E8 BF010000 call 01007568
010073A9 33DB xor ebx, ebx
010073AB 53 push ebx
010073AC 8B3D CC100001 mov edi, dword ptr [10010CC] ; kernel32.GetModuleHandleA
010073B2 FFD7 call edi
010073B4 66:8138 4D5A cmp word ptr [eax], 5A4D
010073B9 75 1F jnz short 010073DA
010073BB 8B48 3C mov ecx, dword ptr [eax+3C]
010073BE 03C8 add ecx, eax
010073C0 8139 50450000 cmp dword ptr [ecx], 4550
010073C6 75 12 jnz short 010073DA
010073C8 0FB741 18 movzx eax, word ptr [ecx+18]
010073CC 3D 0B010000 cmp eax, 10B
010073D1 74 1F je short 010073F2
010073D3 3D 0B020000 cmp eax, 20B
010073D8 74 05 je short 010073DF
010073DA 895D E4 mov dword ptr [ebp-1C], ebx
然后用OD的OllyDump插件,入口地址重写成739D,然后 Dump 下来。
接下来是修复IAT表,可手工查找API函数地址。
在OD的数据窗口中定位到0100739D,然后查看方式为“长型”->“地址”,往上一直找。
发现API函数地址从01001000到01001344结束。
01001000 77DA6FC8 ADVAPI32.RegQueryValueExW
01001004 77DA6BF0 ADVAPI32.RegCloseKey
01001008 77DC8F7D ADVAPI32.RegCreateKeyW
0100100C 77DCD5FD ADVAPI32.IsTextUnicode
01001010 77DA7883 ADVAPI32.RegQueryValueExA
01001014 77DA761B ADVAPI32.RegOpenKeyExA
.................................................
0100132C 77C323D8 offset msvcrt._adjust_fdiv
01001330 77BEF1A4 msvcrt.__p__commode
01001334 77BEF1DB msvcrt.__p__fmode
01001338 77C0537C msvcrt.__set_app_type
0100133C 77C1EE2F msvcrt._controlfp
01001340 77C1806B msvcrt.wcsncpy
01001344 00000000 //这里结束
01001348 00000000
打开Import REC软件,选择我们脱壳的进程,
OEP为739D
RVA为1000
Size为344
然后,Get Imports,最后Fix Dump刚才从OD 中Dump出来的PE文件。
脱壳完成!
最后可删除.Themida块区,然后修复IAT表,以实现减肥。
减肥后只有88K大小。
说明,脱壳后一般不能跨平台,只以在脱壳的那台机用。因为Themida用在本机调用
LoadLibraryA和GetProcAddress得到本机API的调用地址,然后进行还原API函数的调用
地址。所以,当两台机的DLL调用地址不同时,就不能跨平台了。
这个可手工或利用工具进行修复,论坛上有相关文章。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)