文章作者:仓鼠小布

软件名称:文明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 SettingsTemp目录下生成~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,不过是没有打过升级补丁的,我就一起传上来,供大家学习。