-
-
[原创]巧脱Themida 1.8.2+
-
发表于:
2008-1-26 12:57
10187
-
巧脱Themida 1.8.2+
这里首先解释下为什么能“巧”,因为,第一,这个程序用的保护比较少的,至少没有VM,第二,这个程序脚本过不去。。。
我们的目标程序是CKwowCrack,就是Antrix,国外开发的魔兽世界模拟器,然后国内某某人拿到源代码之后,自己修正一点东西,再加一点其它的东西,然后编译了一下,加个某壳,然后就变成了www[dot]sfwowchina[dot]com上面的收费版,然后呢,其它某人把它给PJ了,然后加上了我们看见的某版本Themida,然后发布了出来……
然后我们现在有什么呢?第一,我们可以从网上下载到其它同版本号的Antrix,所以OEP对于我们不构成威胁。并以此我们还很确定的得知,其运行开始前就需要载入其目录下的其它DLL,这点为我们切入Themida的盔甲提供了便利,因为未被Themida分析过的用户DLL是不可知的,不可确定的。
所以我们首先先运行一下这个程序,然后附加进入,然后我们发现了0401000处的真实字节是0x55,然后我们重来,运用某种方法使得Themida停在前面一点的地方,附加进入,然后F9。
7C921230 ntdll.DbgBreakPoint CC int3
7C921231 C3 retn 我们现在在这里
7C921232 8BFF mov edi, edi
7C921234 90 nop
然后我们来思考一下。Themida对Kernel32、user32、advapi32三个Dll进行了重载,使得我们想在这三个Dll里面下断点变得很困难。不过我们可以对VirtualProtect反汇编看下。
7C801AD0 kernel32.VirtualPro> 8BFF mov edi, edi
7C801AD2 /. 55 push ebp
7C801AD3 |. 8BEC mov ebp, esp
7C801AD5 |. FF75 14 push dword ptr [ebp+14] ; /pOldProtect
7C801AD8 |. FF75 10 push dword ptr [ebp+10] ; |NewProtect
7C801ADB |. FF75 0C push dword ptr [ebp+C] ; |Size
7C801ADE |. FF75 08 push dword ptr [ebp+8] ; |Address
7C801AE1 |. 6A FF push -1 ; |hProcess = FFFFFFFF
7C801AE3 |. E8 75FFFFFF call VirtualProtectEx ; 这里进入\VirtualProtectEx
7C801AE8 |. 5D pop ebp
7C801AE9 \. C2 1000 retn 10
7C801A5D kernel32.VirtualPro>/$ 8BFF mov edi, edi
7C801A5F |. 55 push ebp
7C801A60 |. 8BEC mov ebp, esp
7C801A62 |. 56 push esi
7C801A63 |. 8B35 B812807C mov esi, dword ptr [<&ntdll.NtProtec>; ntdll.ZwProtectVirtualMemory
7C801A69 |. 57 push edi
7C801A6A |. FF75 18 push dword ptr [ebp+18]
7C801A6D |. 8D45 10 lea eax, dword ptr [ebp+10]
7C801A70 |. FF75 14 push dword ptr [ebp+14]
7C801A73 |. 50 push eax
7C801A74 |. 8D45 0C lea eax, dword ptr [ebp+C]
7C801A77 |. 50 push eax
7C801A78 |. FF75 08 push dword ptr [ebp+8]
7C801A7B |. FFD6 call esi ; <&ntdll.NtProtectVirtualMemory>
7C801A7D |. 8BF8 mov edi, eax
7C92DEB6 ntdll.ZwProtectVirt> B8 89000000 mov eax, 89
7C92DEBB BA 0003FE7F mov edx, 7FFE0300
7C92DEC0 FF12 call dword ptr [edx]
7C92DEC2 C2 1400 retn 14
到这里我们发现了一点什么,第一ZwProtectVirtualMemory这个函数位于Ntdll中,Themida没有偷到这边,第二其实他也不能偷,因为Kernel32队这边的调用是内存地址性的,再者这段函数搬到用户代码里面去根本就不会执行。
所以我们对ZwProtectVirtualMemory下断,这里你可以在每次断在这边的时候都看下0401000的数据,也可以下一个条件断点:byte [0401000]==55,然后F9,很快的我们就断下了。然后我们取消断点,没用了。
然后我们打开内存视图看下。
Memory map
地址=00400000 大小=00001000 (4096.) PE Header
地址=00401000 大小=00499000 (4820992.) Code+Data+IAT+...
地址=0089A000 大小=00001000 (4096.) rsrc
在下面两个是Themida的杰作,我们就不看了。
地址=033D0000 大小=00001000 (4096.)
地址=034DE000 大小=00002000 (8192.)
这里我们注意到Private内存的界限在这边,然后我们对0401000段下内存写入断点。
00CA3157 890B mov dword ptr [ebx], ecx ; 我们断在了这里
00CA3159 5B pop ebx
此时的寄存器:
EAX 007B75C8 antrix.007B75C8
ECX 71A24519 WS2_32.ioctlsocket
EDX 0000000A
EBX 007B75C8 antrix.007B75C8
ESP 0012FF74
EBP F78569DB
ESI 00C35047 antrix.00C35047
EDI 71A24519 WS2_32.ioctlsocket
EIP 00CA3157 antrix.00CA3157
我们看见了API,不过貌似没有加密,说明Themida对这个Dll放了水。
然后这条指令的目标内存是007B75C8,这里我们可以在内存窗口中移动到这里,按照win32的页面对齐,我相信我们会在007B7000这边发现点什么。
然后我们再来看一下内存视图。
Memory map, 条目 119
地址=034F0000 大小=00002000 (8192.)
地址=03500000 大小=00010000 (65536.)
地址=03510000 大小=00001000 (4096.)
我们发现多出来了这么几行,至于这几段内存是做什么的,大家可以自己下内存断点去看,我们直接用结论了。对0x034F0000段内存下写入断点。
00CA2B18 F3:A4 rep movs byte ptr es:[edi], byte ptr> ;We Are Here...
00CA2B1A 0F83 04000000 jnb 00CA2B24
00CA2B20 60 pushad
00CA2B21 B6 65 mov dh, 65
00CA2B23 61 popad
00CA2B24 ^ E9 F5FCFFFF jmp 00CA281E ;这里似乎很想往上面跳,为什么我这么感觉是循环呢……
来看看寄存器:
EAX 00000000
ECX 00000002
EDX 5ADFD735
EBX 00BEC0F4 antrix.00BEC0F4
ESP 0012FF78
EBP F78569DB
ESI 7C8126F2 kernel32.PostQueuedCompletionStatus ;我们看见了Api。。。。
EDI 034F0000
EIP 00CA2B18 antrix.00CA2B18
我们把Esi的值现抄下来。然后我们删除内存断点跳上去:
00CA2810 /0F8B 08000000 jpo 00CA281E
00CA2816 |0F8E 02000000 jle 00CA281E
00CA281C |60 pushad
00CA281D |61 popad
00CA281E \803E E8 cmp byte ptr [esi], 0E8 ;到了这里
00CA2821 0F85 EE000000 jnz 00CA2915
什么指令的Opcode是0xE8呢?call指令,下面我们就不看了,我们比较关心上面,就是循环前面的代码,因为我们不能每次都在循环里面转上半天才认识我们应该怎么做。我们看下上面的代码,我认为比较好的断点位置在00CA2810,然后我就华丽的下了断点。断下后我们看内存窗口。
007B7000 00000000 ....
007B7004 00000000 ....
007B7008 00000000 ....
007B700C 00000000 ....
007B7010 00000000 ....
007B7014 03520000 ..R ;这里
007B7018 00000000 ....
007B701C 00000000 ....
007B7020 00000000 ....
007B7024 00000000 ....
我们去3520000看看。
03520000 8BFF mov edi, edi
.........
03520255 FF15 1012807C call dword ptr [<&ntdll.NtSetIoComple>; ntdll.ZwSetIoCompletion
看到这里我想起了什么,于是将3520255的指令在Kernel32种搜索了一下。发现了一个唯一的结果:
7C8126F2 k> 8BFF mov edi, edi ;kernel32.PostQueuedCompletionStatus
7C8126F4 . 55 push ebp
7C8126F5 . 8BEC mov ebp, esp
7C8126F7 . 56 push esi
7C8126F8 . FF75 0C push dword ptr [ebp+C]
7C8126FB . 33F6 xor esi, esi
7C8126FD . 6A 00 push 0
7C8126FF . FF75 14 push dword ptr [ebp+14]
7C812702 . 46 inc esi
7C812703 . FF75 10 push dword ptr [ebp+10]
7C812706 . FF75 08 push dword ptr [ebp+8]
7C812709 . FF15 1012807C call dword ptr [<&ntdll.NtSetIoComple>; ntdll.ZwSetIoCompletion
7C81270F . 85C0 test eax, eax
7C812711 . 0F8C D4A00200 jl 7C83C7EB
7C812717 > 8BC6 mov eax, esi
7C812719 . 5E pop esi
7C81271A . 5D pop ebp
7C81271B . C2 1000 retn 10
我们似乎理解到了什么。于是把7b7020处的数值修正为7C8126F2,于是我们似乎解决了什么。然后我们进入手工循环……
很可惜的是,我在修完kernel32和user32之后程序退出了。然后我们正常的到达OEP,然后我们就剩4个advapi的了。
然后手动修一下,拿出ImportRec保存下,我们就有了正确的IAT了。
然后我们重来,修理下另外一样东西。
我们接着00CA3157 890B mov dword ptr [ebx], ecx 这里继续往下单步,然后发现了这里:
00CA346C 803F 90 cmp byte ptr [edi], 90
00CA346F 0F84 3D000000 je 00CA34B2
我们看下Edi的地方
0066B520 55 push ebp
0066B521 8BEC mov ebp, esp
0066B523 51 push ecx
0066B524 C745 FC 0100000>mov dword ptr [ebp-4], 1
0066B52B 8D45 FC lea eax, dword ptr [ebp-4]
0066B52E 50 push eax
0066B52F 68 7E660480 push 8004667E
0066B534 8B4D 08 mov ecx, dword ptr [ebp+8]
0066B537 51 push ecx
0066B538 90 nop <-这里
0066B539 90 nop
0066B53A 90 nop
0066B53B 90 nop
0066B53C 90 nop
0066B53D 90 nop
0066B53E F7D8 neg eax
0066B540 1BC0 sbb eax, eax
0066B542 40 inc eax
0066B543 8BE5 mov esp, ebp
0066B545 5D pop ebp
0066B546 C3 retn
我们来想下什么指令是6字节的,Call [imm32],Jmp [Imm32]。
然后我们知道了什么,我们继续走到了被偷过的OEP。然后我们补上OEP之后DUMP。我们得到了一个文件,很显然这个文件并不能运行。
(由于下一步我还没完成,主要是最近太懒。我说下思路吧,应该会正确的)
我们写个小程序,载入代码段,然后枚举0xE8和0xE9,然后只要判断这个目标地址是不是在IAT区间中存在,存在的话则为需要修复的,然后再确定指令前是不是0x90,然后就可以正确的进行Patch,然后收工……
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!