扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
文章作者:仓鼠小布
软件名称:文明4原版1.61
软件类型:游戏
工具:peid看雪版(附带fly老大制作的特征库),OllyICE(OllyDBG的修改版),LoadPE,ucfir16f,winhex。
前言:
Safedisc是Macrovision公司开发的一个商用加壳软件,说道Macrovision公司,大家很快就能够想到installshield,但是它的safedisc在早年的游戏加密市场,那可谓龙头老大,虽然现在已经不复当年之勇(主要还是竞争对手过于强悍),但是仍旧是一款十分优秀的加密技术,完全值得一个cracker花下精力和时间去研究它。
本文选择文明4进行分析主要有两个原因,一个。。。按照老外的说法,文明4所使用的safedisc加密是最为齐全的,基本上把safedisc对游戏的加密手段都用上了,第二个原因。。。本人手头上是safedisc加密的游戏只有两个,一个是文明4,一个是中世纪2,中世纪2实在太大,只能选文明4了(囧),好了,废话不多说了,现在开始吧。
PS:下文为了方便起见把safedisc简称为sd,把OllyICE称为od。
第一章:躲过Anti—debug,到达OEP
首先,万年不变的第一步,先用peid查壳,我们发现,游戏是safedisc 4.6加密。
设置OllyICE忽略所有的异常选项。用IsDebuggerPresent插件去掉OllyDBD的调试器标志。
好了,我们用od载入文明4的exe文件,然后OD会停在这里:
00E0D06E > 55 push ebp
00E0D06F 8BEC mov ebp, esp
00E0D071 60 pushad //看,多么友好的提示啊。
00E0D072 BB 6ED0E000 mov ebx, offset <模块入口点>
00E0D077 B8 0DD0E000 mov eax, 00E0D00D
00E0D07C 33C9 xor ecx, ecx
00E0D07E 8A08 mov cl, byte ptr [eax]
00E0D080 85C9 test ecx, ecx
00E0D082 74 0C je short 00E0D090
00E0D084 B8 E4D0E000 mov eax, 00E0D0E4
00E0D089 2BC3 sub eax, ebx
00E0D08B 83E8 05 sub eax, 5
00E0D08E EB 0E jmp short 00E0D09E
00E0D090 51 push ecx
00E0D091 B9 2BD1E000 mov ecx, 00E0D12B
00E0D096 8BC1 mov eax, ecx
00E0D098 2BC3 sub eax, ebx
00E0D09A 0341 01 add eax, dword ptr [ecx+1]
00E0D09D 59 pop ecx
00E0D09E C603 E9 mov byte ptr [ebx], 0E9
00E0D0A1 8943 01 mov dword ptr [ebx+1], eax
00E0D0A4 51 push ecx
00E0D0A5 68 D9CFE000 push 00E0CFD9
00E0D0AA 33C0 xor eax, eax
00E0D0AC 85C9 test ecx, ecx
00E0D0AE 74 05 je short 00E0D0B5
00E0D0B0 8B45 08 mov eax, dword ptr [ebp+8]
00E0D0B3 EB 00 jmp short 00E0D0B5
00E0D0B5 50 push eax
00E0D0B6 E8 25FCFFFF call 00E0CCE0 //程序启动
00E0D0BB 83C4 08 add esp, 8
00E0D0BE 59 pop ecx
00E0D0BF 83F8 00 cmp eax, 0
00E0D0C2 74 1C je short 00E0D0E0
00E0D0C4 C603 C2 mov byte ptr [ebx], 0C2
00E0D0C7 C643 01 0C mov byte ptr [ebx+1], 0C
00E0D0CB 85C9 test ecx, ecx
00E0D0CD 74 09 je short 00E0D0D8
00E0D0CF 61 popad
00E0D0D0 5D pop ebp
00E0D0D1 B8 00000000 mov eax, 0
00E0D0D6 ^ EB 96 jmp short <模块入口点>
00E0D0D8 50 push eax
00E0D0D9 B8 F9CFE000 mov eax, 00E0CFF9
00E0D0DE FF10 call dword ptr [eax]
00E0D0E0 61 popad
00E0D0E1 5D pop ebp
00E0D0E2 EB 47 jmp short 00E0D12B
00E0D0E4 807C24 08 00 cmp byte ptr [esp+8], 0
00E0D0E9 75 40 jnz short 00E0D12B
00E0D0EB 51 push ecx
00E0D0EC 8B4C24 04 mov ecx, dword ptr [esp+4]
00E0D0F0 890D 25D1E000 mov dword ptr [E0D125], ecx
00E0D0F6 B9 02D1E000 mov ecx, 00E0D102
00E0D0FB 894C24 04 mov dword ptr [esp+4], ecx
00E0D0FF 59 pop ecx
00E0D100 EB 29 jmp short 00E0D12B
00E0D102 50 push eax
00E0D103 B8 FDCFE000 mov eax, 00E0CFFD
00E0D108 FF70 08 push dword ptr [eax+8]
00E0D10B 8B40 0C mov eax, dword ptr [eax+C]
00E0D10E FFD0 call eax
00E0D110 B8 FDCFE000 mov eax, 00E0CFFD
00E0D115 FF30 push dword ptr [eax]
00E0D117 8B40 04 mov eax, dword ptr [eax+4]
00E0D11A FFD0 call eax
00E0D11C 58 pop eax
00E0D11D B8 25D1E000 mov eax, 00E0D125
00E0D122 FF30 push dword ptr [eax]
00E0D124 C3 retn
00E0D125 72 16 jb short 00E0D13D
00E0D127 61 popad
00E0D128 1360 0D adc esp, dword ptr [eax+D]
00E0D12B - E9 5E31BAFF jmp 009B028E //OEP
00E0D130 66:833D 9043EA0>cmp word ptr [EA4390], 0
00E0D138 74 05 je short 00E0D13F
00E0D13A ^ E9 91FEFFFF jmp 00E0CFD0
00E0D13F C3 retn
破解过sd的cracker们会很容易发现每个sd的头部,无论其版本,竟然惊人得相似,而且代码的第三行的pushad永远是那么友好,有经验的cracker们一定知道,只要找到一个popad,下面的一个大跳转就是OEP了,但是下方有3个popad,分别是00E0D0CF,00E0D0E0,00E0D127处,究竟是哪一个呢,经验告诉我们第三个popad后的跳转就是真正的OEP了,不过,知道OEP还是没有用,大家可以看下面两张截图。
第一张是直接强跳到OEP后看到的代码,第二张是被破解的以后的OEP处的代码,二者的不同点相信大家都看到了吧,sd把加密过的PE分为两部分,第一部分是解码部分,通过Anti—debug和光盘验证以后,释放出95%的源代码,然后到达OEP,看来我们还是要到壳里面去逛逛才能见分晓了。
Sd对od的Anti—debug可以说是非常少的,sd会在你的C:\Documents and Settings\用户名\Local Settings\Temp目录下生成~e5.0001.XXX.XXX的文件夹,其中里面有一个文件叫~df394b.tmp,sd的所有秘密都在这里面了。
据我所知,sd对od的Anti—debug只有IsDebuggerPresent,而我们有有IsDebuggerPresent插件躲过检测,所以可以高枕无忧了,但是还是看看比较好,bp IsDebuggerPresent,F9运行程序,到达断点以后,删除断点(切记是删除),Alt+F9,我们看到如下代码。
6670AF8C 8BF0 mov esi, eax
6670AF8E 66:85F6 test si, si
6670AF91 74 13 je short 6670AFA6 //IsDebuggerPresent检测
6670AF93 E8 2A63FFFF call 667012C2
6670AF98 66:8BF0 mov si, ax
6670AF9B 66:F7DE neg si
6670AF9E 1BF6 sbb esi, esi
6670AFA0 46 inc esi
6670AFA1 66:85F6 test si, si
6670AFA4 75 0F jnz short 6670AFB5
6670AFA6 8B4424 08 mov eax, dword ptr [esp+8]
6670AFAA 8120 EA894267 and dword ptr [eax], 674289EA
6670AFB0 66:8BC6 mov ax, si
6670AFB3 5E pop esi
6670AFB4 C3 retn
下面我们F8单步调试下去,到达如下代码。
6670557A 83C4 04 add esp, 4
6670557D 66:85C0 test ax, ax
66705580 0F85 02010000 jnz 66705688
66705586 F6C3 04 test bl, 4
66705589 76 12 jbe short 6670559D
6670558B 56 push esi
6670558C E8 EF580000 call 6670AE80
66705591 83C4 04 add esp, 4
66705594 66:85C0 test ax, ax
66705597 0F85 EB000000 jnz 66705688
6670559D F6C3 08 test bl, 8
667055A0 76 12 jbe short 667055B4
667055A2 56 push esi
667055A3 E8 18580000 call 6670ADC0
667055A8 83C4 04 add esp, 4
667055AB 66:85C0 test ax, ax
667055AE 0F85 D4000000 jnz 66705688
667055B4 F7C3 00080000 test ebx, 800
667055BA 76 12 jbe short 667055CE
667055BC 56 push esi
667055BD E8 1E570000 call 6670ACE0
667055C2 83C4 04 add esp, 4
667055C5 66:85C0 test ax, ax
667055C8 0F85 BA000000 jnz 66705688
667055CE F7C3 00100000 test ebx, 1000
667055D4 76 12 jbe short 667055E8
667055D6 56 push esi
667055D7 E8 44560000 call 6670AC20
667055DC 83C4 04 add esp, 4
667055DF 66:85C0 test ax, ax
667055E2 0F85 A0000000 jnz 66705688
667055E8 F7C3 00200000 test ebx, 2000
667055EE 76 12 jbe short 66705602
667055F0 56 push esi
667055F1 E8 7A550000 call 6670AB70
667055F6 83C4 04 add esp, 4
667055F9 66:85C0 test ax, ax
667055FC 0F85 86000000 jnz 66705688
66705602 F6C3 01 test bl, 1
66705605 76 0E jbe short 66705615
66705607 56 push esi
66705608 E8 53540000 call 6670AA60
6670560D 83C4 04 add esp, 4
66705610 66:85C0 test ax, ax
66705613 75 73 jnz short 66705688
66705615 F6C3 02 test bl, 2
66705618 76 0E jbe short 66705628
6670561A 56 push esi
6670561B E8 20530000 call 6670A940
66705620 83C4 04 add esp, 4
66705623 66:85C0 test ax, ax
66705626 75 60 jnz short 66705688
66705628 F6C3 10 test bl, 10
6670562B 76 0E jbe short 6670563B
6670562D 56 push esi
6670562E E8 BD520000 call 6670A8F0
66705633 83C4 04 add esp, 4
66705636 66:85C0 test ax, ax
66705639 75 4D jnz short 66705688
6670563B F6C3 20 test bl, 20
6670563E 76 0E jbe short 6670564E
66705640 56 push esi
66705641 E8 0A520000 call 6670A850
66705646 83C4 04 add esp, 4
66705649 66:85C0 test ax, ax
6670564C 75 3A jnz short 66705688
6670564E F6C3 40 test bl, 40
66705651 76 0E jbe short 66705661
66705653 56 push esi
66705654 E8 57510000 call 6670A7B0
66705659 83C4 04 add esp, 4
6670565C 66:85C0 test ax, ax
6670565F 75 27 jnz short 66705688
66705661 F7C3 80000000 test ebx, 80
66705667 76 0E jbe short 66705677
66705669 56 push esi
6670566A E8 11500000 call 6670A680
6670566F 83C4 04 add esp, 4
66705672 66:85C0 test ax, ax
66705675 75 11 jnz short 66705688
66705677 F7C3 00400000 test ebx, 4000
6670567D 76 09 jbe short 66705688
6670567F 56 push esi
66705680 E8 EB4E0000 call 6670A570
66705685 83C4 04 add esp, 4
66705688 5E pop esi
66705689 5B pop ebx
6670568A C3 retn
由于我们的od已经使用IsDebug插件了,所以这里不必处理,只是借用此断点来继续下面的流程,不过看看对大家还是有好处的,经过了6670568A处的retn以后,我们到了这里。
6670147F 83C4 08 add esp, 8
66701482 66:8945 F0 mov word ptr [ebp-10], ax
66701486 58 pop eax
66701487 0FB745 F0 movzx eax, word ptr [ebp-10]
6670148B 85C0 test eax, eax
6670148D 74 07 je short 66701496 //跳转完成
6670148F B8 00400000 mov eax, 4000
66701494 EB 05 jmp short 6670149B
66701496 B8 00000100 mov eax, 10000 // UNICODE "=::=::\"
6670149B C9 leave
6670149C C3 retn
到达下面的代码。
66702E61 3D 00200000 cmp eax, 2000
66702E66 59 pop ecx
66702E67 59 pop ecx
66702E68 74 26 je short 66702E90
66702E6A 3D 00400000 cmp eax, 4000
66702E6F ^ 74 D9 je short 66702E4A
66702E71 33C9 xor ecx, ecx
66702E73 3D 00000100 cmp eax, 10000 ; UNICODE "=::=::\"
66702E78 0F94C1 sete cl
66702E7B 49 dec ecx
66702E7C 81E1 6AFFFFFF and ecx, FFFFFF6A
66702E82 81C1 FA000000 add ecx, 0FA
66702E88 890D 2C0B7966 mov dword ptr [66790B2C], ecx
66702E8E C9 leave
66702E8F C3 retn
经过66702E8F处的retn,我们到达了下面,接下来就是一路的F8了,由于代码比较长,我就不全都打出来了,到达66704812处,我们可以停下了,接受光盘检测。
6670445B 6A 1C push 1C
6670445D E8 5AD20500 call 667616BC
66704462 83C4 20 add esp, 20
66704465 3BC7 cmp eax, edi
66704467 74 0A je short 66704473
66704469 57 push edi
6670446A 8BC8 mov ecx, eax
。
。
。
。
。
66704809 57 push edi
6670480A 8D4D 28 lea ecx, dword ptr [ebp+28]
6670480D E8 C81F0100 call 667167DA
66704812 E8 A6CEFFFF call 667016BD //光盘检测
66704817 66:85C0 test ax, ax
6670481A 75 17 jnz short 66704833
6670481C E8 B49D0100 call 6671E5D5
66704821 8D4D 28 lea ecx, dword ptr [ebp+28]
在此我想稍稍提及一下sd的光盘检测,sd加密过的光盘会有一个弱扇区,这个是故意损坏的扇区,比较老的虚拟光驱和刻录机是无法识别和复制这个弱扇区,sd的程序会检测光盘上是否有这个弱扇区,然后再检测光盘上的某几个其他普通扇区,然后才能进入游戏,以此来达到反盗版的目的,由于检测的普通扇区都是分开的,所以sd加密的游戏基本只能做出稀疏镜像,也就是所谓的极大镜像来骗过光盘验证。
这里由于本人学艺不精,无法在没有光盘的情况下手动骗过光盘验证,只能用极大镜像配合yasu混过光盘验证,如果有高人的话,请指导一下在下,在下不胜感激。
接下来就是一路F8不回头了,直到00E0D12B处,此处是一个大跳转,F8进入把,终于到了OEP了。
009B028E 6A 74 push 74
009B0290 68 587BB900 push 00B97B58
009B0295 E8 E6060000 call 009B0980
009B029A 33DB xor ebx, ebx
009B029C 895D E0 mov dword ptr [ebp-20], ebx
009B029F 53 push ebx
009B02A0 8B3D 8071B600 mov edi, dword ptr [B67180] ; kernel32.GetModuleHandleA
009B02A6 FFD7 call edi
看到了吧,一个很标准的用VC编译的头部,好了,我们用LoadPE把程序dump出来一份,取名为dumped.exe留着吧。
PS:其实sd的Anti—debug还不止IsDebuggerPresent这一个,用softICE调试过以后你就会发现暗桩密布,而且sd还有断点检测的Anti—debug,这就是为什么我开始要大家删除断点的理由,记住,破解时候就要养成好习惯,用过的断点要清理干净,如果实在要用就记在纸上,还有一个Anti—debug就是timeout之类的函数,如果你把程序载入以后,过个3分钟再跑,那是怎么都到不了OEP的。。。。。上面两个Anti—debug都完全可以避免,所以在这里就不赘述了。
第二章:输入表(IAT)修复
在进行这阶段前,希望大家对IAT有所了解,不熟悉的要去找资料学习学习,有了这个基础,下面的工作才好做.IAT修复就是对IAT表进行正常修复,加密软件几乎都处理IAT表,使IAT的内容改变成壳特定的内容,以防止dump出来后还原文件.我觉得这个阶段是难度较大而且也是赋挑战性的工作,是体现破解水平的一个方面也是整个解密工作的关键阶段.要完成这个阶段需要有很好的经验.
虽然我们dump出来了一份干净的程序,但是经过sd处理过的输入表自然是不能用了,没办法,修吧。
我们看到sd还是很厚道的,第一个GetModuleHandleA就是没有处理过的输入表,我们就从这里入手吧,看到指向GetModuleHandleA的指针了吗,B67180,再减去文件偏移400100,得到的地址是767080,这是第一个aip调用,这个时候就是用到winhex的时候了,我们用winhex打开文明4的原始执行文件,到达767080处看看。
我们在767080附近逛逛,寻找IAT的起始点,在IAT开始位置,一般前面是空记录(很多连续的00000000),在刚出现地址,并且是属于程序调用的模块的有效地址时,这就是IAT_start,记录下地址来,在这里是:767000。
IAT_stop比IAT_start更难找一点,需要经验和好的眼神,总的来说,一般是在结束时,后面跟的数据是属于无效的地址,并且"无效地址"数据前是00 00 00 00,这个是IAT表的规律,不同模块间间隔是00 00 00 00,这个只能靠多看程序,获得经验了,这里我们发现是在767bc0附近。
看到767bc0那一行了吧,好了,东西就在这里了,我们打开ucfir16f,选择civilization4.exe进程,自动查找IAT,ucfir16f果然是最好的输入表修复软件,找输入表果然准,得到的结果和真实结果相差很小,我们只需要把大小改为BD0即可,输入表这东西,个人推荐范围找大一点,宁可错杀1000,不可放过一个,我们得到如下结果。
我们把模块展开,发现有很多无效的指针,其实不是无效,而是被壳处理过了,我们可以看到,有130个指针要修复。。。。。。。可怕的东西啊,大家准备好干粮,别修到一半饿晕了,这个问题就麻烦了,^_^!!!。
我先来说明一下sd是如何加密IAT的,在程序通过验证以后,sd的~df394b.tmp并没有从内存中被释放出来,而是继续很猥琐地蹲在那里,干着它那邪恶的勾搭,那些指针指向代码惊人地相似,到后面我们就会看到,它压入一个特定值进入堆栈,然后交给~df394b.tmp处理一下,用~df394b.tmp间接调用需要的api,很天才的想法,这里我们只能够进入~df394b.tmp分析代码,以得到正确的api调用,好了开始吧。
我们载入原始执行文件,手动到达OEP,由于IAT的修复工作量巨大,而且都是机械工作,所以我就不一个一个讲了,我们以第一个模块中的第个指针作为例子来讲解,其他的就是照瓢画葫芦了。
看到第三个指针指向的地方了吗(这里我本来是想修第二个的,结果眼花一下,选到第三个去了,囧),01c5cf85,好,我们过去看看,结果看到如下代码。
01C5CF85 68 8612EABF push BFEA1286
01C5CF8A 9C pushfd //保护现场
01C5CF8B 60 pushad //保护现场
01C5CF8C 54 push esp
01C5CF8D 68 C5CFC501 push 1C5CFC5
01C5CF92 E8 45ADB164 call ~df394b.66777CDC //用壳间接调用api
01C5CF97 83C4 08 add esp, 8
01C5CF9A 6A 00 push 0
01C5CF9C 58 pop eax
01C5CF9D 61 popad //恢复现场
01C5CF9E 9D popfd //恢复现场
01C5CF9F C3 retn //调用完毕,跑路
我们设置01C5CF85为新的EIP,F8到01C5CF92处停下,F7步入,进入壳里面去逛逛,由于这是壳代码,我们要做的就是保证它一路向下即可,记得随时注意堆栈的变化。
66777CDC 55 push ebp
66777CDD 8BEC mov ebp, esp
66777CDF 83EC 34 sub esp, 34
66777CE2 53 push ebx
66777CE3 56 push esi
66777CE4 57 push edi
66777CE5 E8 94C6FBFF call 6673437E
66777CEA EB 0A jmp short 66777CF6
66777CEC EB 50 jmp short 66777D3E
。
。
。
。
。
66777F7C 8945 CC mov dword ptr [ebp-34], eax
66777F7F 8B45 CC mov eax, dword ptr [ebp-34]
66777F82 8B4D E0 mov ecx, dword ptr [ebp-20]
66777F85 8908 mov dword ptr [eax], ecx
66777F87 8B45 CC mov eax, dword ptr [ebp-34]
66777F8A 83C0 04 add eax, 4
66777F8D 50 push eax
66777F8E E8 59CDFBFF call 66734CEC
66777F93 59 pop ecx
66777F94 E8 01C4FBFF call 6673439A
66777F99 8B65 0C mov esp, dword ptr [ebp+C]
66777F9C 61 popad
66777F9D 9D popfd
66777F9E C3 retn //调用完毕,跑路
如果大家仔细一点的话,会发现右下角的堆栈的变化还是很令人欣喜的,在堆栈中出现了被间接调用的api的名字,ADVAPI32.GetUserNameA。
我们再看看第一个没有被处理过的api,也是ADVAPI32模块中的api,好了,就是它了,我们就把它推到正确的地方去。
好了,我们修好了第一个指针,庆贺一下,鲜花,掌声。。。。但是,还有129个指针,慢慢来吧,这个只有靠耐心了。
这里要说明一下,最后两个模块中的指针本来就是废指针,我们完全可以无视,附上我修好的IAT表,在下面附件下载,完成以后一共有730个指针,被落下哦。
修好以后按“修复转存文件”,我们选择那个dumped.exe,转存为dumped_.exe。
第三章:去暗桩
好了,你还活着吗,IAT修得够爽吧,呵呵,别以为这是结束,这个只是噩梦的开始>_<,原则上来说,IAT修好了,程序应该可以跑起来了,但是不然,我们点击dumped_.exe试试,毫无反应,呵呵,还有暗桩要去除,记得前面说过,sd在通过验证以后只会恢复95%的源代码,其他的5%只有靠我们自己修了。
这里先给sd的暗桩分分级,最为常见的就是call加密,前面虽然我们修好了IAT,但是对IAT的调用也是加密过的,不同的地址调用同样的指针,结果会call出不一样的api,比如:
45678912 call dword ptr [123456] // call a
98765432 call dword ptr [123456] // call b
这个是sd最喜欢用的加密了,由于api数量巨大分布广泛,而且有真有假,唯一的办法就是一个个检查了(让我想到了军训时候一个少将说过的东风31,囧)。
第二类暗桩就是SDK加密,比如00408990处的call
004010A9,我们到004010A9里面去看看,发现下面的代码。
004010A9 51 push ecx
004010AA 50 push eax
004010AB E8 630F0000 call 00402013 //交给壳处理一下
004010B0 56 push esi
004010B1 8BF1 mov esi, ecx
004010B3 8B06 mov eax, dword ptr [esi]
004010B5 83E8 0C sub eax, 0C
004010B8 74 10 je short 004010CA
004010BA 3D 5015CB00 cmp eax, 00CB1550
004010BF 74 09 je short 004010CA
004010C1 50 push eax
004010C2 E8 89F05A00 call 009B0150 //jmp 到MSVCR71.operatordelete[]
004010C7 83C4 04 add esp, 4
004010CA C706 5C15CB00 mov dword ptr [esi], 00CB155C
004010D0 5E pop esi
004010D1 C3 retn
看到了吧,00408990处的call
004010A9干的活就是让程序被壳处理一下,然后调用jmp .MSVCR71.operatordelete[],这个好办,我们只要搜索全部的call
004010A9,然后一个个分析一下,改一下调用即可,但是数量也不少哦。
第三种还是call的加密(sd真喜欢处理call),call里面干的活就是校验一下内存,这个其实也好办,只要稍稍改动一下代码即可,而且校验是连续的,所以不用担心漏掉哪个(要漏就是漏一串,^_^!!!)。
最后一种加密就比较可怕了,我们会发现一些地方的代码很搞笑,啪啪啪参数压好了,应该调用函数了,结果等来的却是一个jmp XXXXXXXX,跟进去一看,又是被~df394b.tmp处理,处理中间接调研api,然后一路F8,我们就会到达那个jmp XXXXXXXX的下面一步,这时候我们会很惊奇地发现,jmp XXXXXXXX下面的代码竟然和原来的完全不同,我们用鼠标滚轮滚两下,代码又变回原样,明白了吧,这就是sd的动态解压,以一个jmp代替call,然后再动态释放4~5行代码,有时候一个jmp可以代替3~4个call,由于这种jmp的加密数量稀少,但是分布得非常分散,稍不留神就会中标。。。。。可怕的东西。
由于这里的暗桩星云密布,我都已经不记得我修过几个了,只能从头开始,和大家一起来一遍,而且安装数量巨大,不可能都将一遍,我会各选取一个例子讲解一下,接下来就是靠大家自己了。
好了,我们先说第一种,我们单步运行dump出来的修好IAT的exe,会发现类似如下错误。
这个就是call了错误的api造成的结果,接下来大家还会碰到很多很多,让我们看如下代码
0069AD62 FF15 7475B600 call dword ptr [<&msvcr71._fullpath>] ; MSVCR71._fullpath
0069AD68 83C4 0C add esp, 0C
0069AD6B 8D4424 0C lea eax, dword ptr [esp+C]
0069AD6F E8 2C60D7FF call 00410DA0
0069AD74 8B07 mov eax, dword ptr [edi]
0069AD76 50 push eax
0069AD77 FF15 8070B600 call dword ptr [<&gdi32.AddFontResour>; GDI32.AddFontResourceA
0069AD7D 85C0 test eax, eax
0069AD7F 74 20 je short 0069ADA1
0069AD81 6A 00 push 0
0069AD83 6A 00 push 0
0069AD85 6A 00 push 0
0069AD87 6A 00 push 0
0069AD89 6A 1D push 1D
0069AD8B 68 FFFF0000 push 0FFFF
0069AD90 FF15 F476B600 call dword ptr [<&user32.GetClipboard>; USER32.GetClipboardData
0069AD96 5F pop edi
看看很干净吧,其实问题就出来了,0069AD90处的api调用其实是错误的,这个就是IAT的调用加密,这个没有办法,用od打开原始exe,跑到0069AD90处,我们入壳分析。
0069AD62 FF15 7475B600 call dword ptr [B67574] ; MSVCR71._fullpath
0069AD68 83C4 0C add esp, 0C
0069AD6B 8D4424 0C lea eax, dword ptr [esp+C]
0069AD6F E8 2C60D7FF call 00410DA0
0069AD74 8B07 mov eax, dword ptr [edi]
0069AD76 50 push eax
0069AD77 FF15 8070B600 call dword ptr [B67080]
0069AD7D 85C0 test eax, eax
0069AD7F 74 20 je short 0069ADA1
0069AD81 6A 00 push 0
0069AD83 6A 00 push 0
0069AD85 6A 00 push 0
0069AD87 6A 00 push 0
0069AD89 6A 1D push 1D
0069AD8B 68 FFFF0000 push 0FFFF
0069AD90 FF15 F476B600 call dword ptr [B676F4] //F7跟进
。
。
。
01C69879 68 C213EABF push BFEA13C2
01C6987E 9C pushfd
01C6987F 60 pushad
01C69880 54 push esp
01C69881 68 B998C601 push 1C698B9
01C69886 E8 51E4B064 call ~df394b.66777CDC //F7跟进
01C6988B 83C4 08 add esp, 8
01C6988E 6A 00 push 0
01C69890 58 pop eax
01C69891 61 popad
01C69892 9D popfd
01C69893 C3 retn
。。。又是这样的代码,相信大家修IAT的时候已经看了不要看了,还是像修IAT一样F7跟进,一路F8,注意堆栈,我们很快就会发现堆栈里面藏了我们需要的东西,原来调用的不是GetClipboard,而是SendMessageCallbackA。
知道了以后就好办了,我们只需要把0069AD90处的call dword ptr [00B676F4]改为
call dword ptr [00B676EC]即可,这里啰嗦一句,为什么是00B676EC ,007676EC是SendMessageCallbackA的IAT地址,加上文件偏移00400000,007676EC+00400000=00B676EC
好了,第一类暗桩介绍过了,这个东西实在可怕,call有真有假,大量的机械工作,如果有高手写出修IAT的脚本的话,我也要一份,反正我现在看到这种call加密就想吐。
好了,我们开始第二类的讲解,SDK这种东西相当于壳中有肉,肉中又有壳的类型,不过还算是比较厚道的,我选择0040904B处的代码进行讲解。
我们跑到0040904B处进去看看。
004010A9 51 push ecx ; MSVCR71.7C34218F
004010AA 50 push eax
004010AB E8 630F0000 call 00402013 //入壳处理
004010B0 56 push esi
004010B1 8BF1 mov esi, ecx
004010B3 8B06 mov eax, dword ptr [esi]
004010B5 83E8 0C sub eax, 0C
004010B8 74 10 je short 004010CA
004010BA 3D 5015CB00 cmp eax, 00CB1550
004010BF 74 09 je short 004010CA
004010C1 50 push eax
004010C2 E8 89F05A00 call 009B0150 ; jmp 到 MSVCR71.operator delete[]
004010C7 83C4 04 add esp, 4
004010CA C706 5C15CB00 mov dword ptr [esi], 00CB155C
004010D0 5E pop esi
004010D1 C3 retn
如果~df394b.tmp不在内存里面,就会出现如下错误。
也是老朋友了,其实如果我们在0040904B处直接F8过去的话,我们会发现代码有变动,变成了mov esi, 0CB155C,又是动态解码,call代替mov esi, 0CB155C,这个就比较麻烦了,我们可以搜索所有的call 004010A9,然后进行分析,切记,要仔细分析,并不是所有的call 004010A9都是代替mov的,比如00408990处的call 004010A9就是代替call 009B0150 //jmp 到MSVCR71.operatordelete[],这个只有一个个分析,一个个修了。。。。。。
好了,第二类暗桩我们也搞定了,接下来是第三类,我选择00411E15处的代码进行分析。
00411E04 51 push ecx
00411E05 C74424 44 A843D>mov dword ptr [esp+44], 00D743A8
00411E0D C74424 48 0844D>mov dword ptr [esp+48], 00D74408
00411E15 E8 56240000 call 00414270 //这里会发生内存错误
00411E1A 50 push eax
00411E1B 68 1FEAEACD push CDEAEA1F
00411E20 E8 BB210000 call 00413FE0 //这里会发生内存错误
00411E25 68 744BAF89 push 89AF4B74
00411E2A 68 B1109239 push 399210B1
00411E2F E8 CC230000 call 00414200 //这里会发生内存错误
00411E34 6A 03 push 3
00411E36 6A 03 push 3
00411E38 8BF8 mov edi, eax
00411E3A E8 31240000 call 00414270 //这里会发生内存错误
00411E3F 50 push eax
00411E40 68 23EBFB6D push 6DFBEB23
00411E45 E8 96210000 call 00413FE0 //这里会发生内存错误
00411E4A 68 366BE9EF push EFE96B36
00411E4F 57 push edi
00411E50 E8 AB200000 call 00413F00 //这里会发生内存错误
00411E55 68 F2750EFE push FE0E75F2
00411E5A 50 push eax
00411E5B E8 10210000 call 00413F70 //这里会发生内存错误
00411E60 83C4 38 add esp, 38
00411E63 3D 5D4BBD0B cmp eax, 0BBD4B5D
我们到00411E15里面去看看。
00414270 83EC 20 sub esp, 20
00414273 56 push esi //esi压栈
00414274 8B35 D83EC900 mov esi, dword ptr [C93ED8] //赋予esi内存C93ED8的值
0041427A 85F6 test esi, esi //校验
0041427C 75 08 jnz short 00414286
0041427E 3935 DC3EC900 cmp dword ptr [C93EDC], esi
00414284 74 36 je short 004142BC
00414286 53 push ebx
00414287 8B1D 643FC900 mov ebx, dword ptr [C93F64]
0041428D 57 push edi
0041428E 8B3D 603FC900 mov edi, dword ptr [C93F60]
00414294 8D4424 34 lea eax, dword ptr [esp+34]
00414298 50 push eax
00414299 8D4C24 34 lea ecx, dword ptr [esp+34]
0041429D 51 push ecx
0041429E 8D4424 14 lea eax, dword ptr [esp+14]
004142A2 E8 E9040000 call 00414790
004142A7 8D4424 0C lea eax, dword ptr [esp+C]
004142AB 99 cdq
004142AC 52 push edx
004142AD 50 push eax
004142AE 53 push ebx
004142AF 57 push edi
004142B0 FFD6 call esi
004142B2 83C4 10 add esp, 10
004142B5 5F pop edi
004142B6 5B pop ebx
004142B7 5E pop esi
004142B8 83C4 20 add esp, 20
004142BB C3 retn
004142BC 8B5424 2C mov edx, dword ptr [esp+2C]
004142C0 8B4424 28 mov eax, dword ptr [esp+28]
004142C4 52 push edx
004142C5 50 push eax
004142C6 E8 65050000 call 00414830
004142CB 83C4 08 add esp, 8
004142CE 5E pop esi
004142CF 83C4 20 add esp, 20
004142D2 C3 retn
这个校验非常莫名,根据带壳的程序,我们发现内存C93ED8的值为6672E2D4,是指向壳代码的地方,然后0041427C处的跳转成功,接下来跑到004142B0处的call esi,就是交给~df394b.tmp验证一下,然后跑到004142BB的retn处返回,这个验证我实在看不出有什么用处,除了浪费一下内存,几乎没有任何用处,希望有大侠可以指点一下。
对付这个,其实也不难,只要把C93ED8出的代码清零,让0041427C处的跳转不实现,接下来00414284处的跳转实现,跑到了004142BC,然后返回,这样就躲过了~df394b.tm的验证,这里实在是搞不懂safedisc脑子里面是怎么想的。。。。。。至少这样修代码没有遇到什么bug,如果有什么更好的办法,还请大侠指点迷津啊,在此拜谢,至于上方贴出的代码,其他会出错的地方用同样的方法就可以混过去了。。。。。。
经过上面的修复,相信大家已经身经百战了,我们来到最后一类暗桩,我选择0069B2AC处的代码进行分析,首先先跑到0069B2AC处看看,这里还是截图比较好。
大家也看到注释了,我们就到0069B2B3的jmp里面去逛逛,看到如下代码。
00E0799E 53 push ebx
00E0799F E8 A3FEFFFF call 00E07847 //F7跟进看看
。
。
。
。
00E07847 870424 xchg dword ptr [esp], eax
00E0784A 9C pushfd
00E0784B 05 0B160000 add eax, 160B
00E07850 8B18 mov ebx, dword ptr [eax]
00E07852 EB 68 jmp short 00E078BC //一个跳转
。
。
。
00E078BC 6BDB 0F imul ebx, ebx, 0F
00E078BF 0358 04 add ebx, dword ptr [eax+4]
00E078C2 9D popfd
00E078C3 58 pop eax
00E078C4 871C24 xchg dword ptr [esp], ebx
00E078C7 C3 retn //做好处理,跑路
。
。
。
01A14C2C 68 B9B26900 push 69B2B9
01A14C31 68 1312EABF push BFEA1213
01A14C36 9C pushfd
01A14C37 60 pushad
01A14C38 54 push esp
01A14C39 68 6C4CA101 push 1A14C6C
01A14C3E E8 9930D664 call ~df394b.66777CDC //老朋友了,F7跟进吧
01A14C43 83C4 08 add esp, 8
01A14C46 6A 00 push 0
01A14C48 58 pop eax
01A14C49 61 popad
01A14C4A 9D popfd
01A14C4B C3 retn
接下来的活大家都应该很熟悉了吧,前面做了不要做的东西了,一路往下跑,注意堆栈即可,我也懒得再贴代码了,直接截图吧。
接下来我们就一路F8到达了0069B2B9,接下来神奇的事情发生了,大家可以把下面的这张截图和上面再上面一张截图做比较,代码完全变了,用鼠标滚两下,代码又变回原样了。
这个,我们只需要把sd动态释放出来的代码复制下来,再patch到原来的代码上面就可以了,代码修好以后如下所示。
0069B2B3 FF15 4876B600 call dword ptr [00B67648]
0069B2B9 8BF0 mov esi, eax
0069B2BB 8BC3 mov eax, ebx
0069B2BD E8 DE5AD7FF call 00410DA0
0069B2C2 85F6 test esi, esi
0069B2C4 A1 589EC900 mov eax, dword ptr [C99E58]
0069B2C9 0F84 8A010000 je 0069B459
0069B2CF C74424 0C 5C15C>mov dword ptr [esp+C], 00CB155C
0069B2D7 8B50 FC mov edx, dword ptr [eax-4]
这下,这类jmp加密也修好了,至此safedisc算是彻底破解了,庆祝一下吧,好好睡个觉,弥补一下这两天的睡眠不足,可怜我已经是大熊猫了。。。。。
第四章:代码优化
虽然已经可以玩了,但是作为一个合格的程序员,我们还是要把exe优化一下,看上去更像一个exe。
把我们修好的exe复制一份,用LordPE把最后三个区段删掉。
其中.mackt是ucfir16f创建的输入表区段,stxt774和stxt371是safedisc的动态解码区段,然后重新修好输入表,把.mackt区段改名为.idata区段,至此优化完毕。。。。。
结语:
总结一下我的这篇文章,感觉自己实在是啰嗦,有些地方完全可以不写,结果还是写上去了,让各位大侠见笑了,我同学叫我唐僧果然还是有原因的Y_Y。。。。。
这个还是那句老话,cracker不是一日能够造就的,经过safedisc 4.6的洗礼大家应该明白了吧,最后只能说,safedisc还是一个很有趣的加密技术,那个给光盘加上刻录机无法识别的弱扇区,调用一个tmp文件作为dll,处理后面的加密,还有驱动验证,都是很天才的想法,你的时间和精力花在上面绝对是值得的,希望我的我的这篇文章可以给大家一点帮助,谢谢。
感谢:
感谢我在写这篇文章的时候水记得朋友对我的鼓励,没有你们,我也没有什么毅力搞定safedisc,至此送上我最真挚的谢意。。。。。。。某心,J雪,头套,ZL,证书,FL,OX,Allen,谢谢大家。
工具下载地址:http://tel.6disk.com/
用户名:zhang19880129
密码:123aaa
最后在PS一下:在老外的论坛上也找到一篇破解safedisc 4.6的文章,巧合的是老外破解的也是文明4,不过是没有打过升级补丁的,我就一起传上来,供大家学习。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者