【文章标题】: 爆破与反爆破菜菜鸟的学习之路
【文章作者】: AsmDebuger  
【作者邮箱】: zhpbeyond@126.com
【作者QQ号】: 38250367
【使用工具】: OD,IDA
【操作平台】: XP-SP2
【软件简介】: 一个英语学习软件
【作者声明】: 这是我第一次爆破经历,还谈不上破解,请大牛和菜鸟们多多包涵,菜菜鸟需要你们的鼓励;如果我的这篇文章对软件作者有什么冒犯的话,也请多包涵,希望这篇文章能对您今后的改进有些帮助。

    说起看雪,我大概2002就知道了,当时正在自学汇编语言,对破解非常感兴趣,然而这感兴趣的状态却随着我的工作一直保持到了现在,因为我一直做单片机开发方面的工作,平日里虽然也在用汇编,但却一直没有时间、精力静下心来开始自己的破解之路,直到有一天,金融风暴把所有手头上的工作都卷走了,公司的订单少的可怜,于是在闲坐之余开始重拾破解梦,而正好手头上有一个软件由于过期无法使用,于是乎就有了这第一篇的破文。
说干就干,先到看雪注册个 ID,然后到工具下载里把大牛们说的几乎所有工具都下到硬盘里,什么OD、IDA、DeDe、Win32Dsm,SoftIce,用过的只有 SoftIce,却只安装过,连呼出的热键都没搞清楚就放弃了,好在现在有了OD,咱菜菜鸟就用它了。先恶补这些工具的帮助,嗯,越看越像单片机仿真器,不过功能可强大了,废话少说,不然成了水帖了,谁让咱是第一次发帖呢?
先从硬盘角落里找到那过期的软件,哇靠,都是2006年版本的了,还是先下载个最新版破,更有成就感(难度也会大多了,当时可没想到,也不知道,菜菜鸟什么都不懂,什么也不怕!顺利下载、安装,兴匆匆双击运行,晕,又提示已运行 20次,无法使用,看来电脑里一定有什么标志,硬盘茫茫,哪里去找?嘿嘿,在这儿我又学了一招装个虚拟机,整一个新的系统试试情况如何。
    装好了虚拟机,把该软件安装一下,双击,OK,运行次数:第一次!赶紧在虚拟机里装好破解工具,按照大牛们的步骤进行:
第一步:用Peid检测,加了“UPX 0.89.6 - 1.02 / 1.05 - 1.24 (Delphi) stub -> Markus & Laszlo”的壳,菜菜鸟先不学脱壳,找脱壳机,我用“超级巡警虚拟机自动脱壳机1.5”顺利脱壳。双击脱壳后的文件,
靠!无反应直接退出。再回头双击一下原程序,晕,又提示运行20次到期!火冒三丈,非破了你不可。
  第二步:既然不能运行,只得“系统还原”一次,这次长点儿头脑,不脱壳,直接OD载入运行,
忽略提示,程序停在:
0065E380 > $  60            PUSHAD
0065E381   .  BE 00905A00   MOV     ESI, SpokenKi.005A9000
0065E386   .  8DBE 0080E5FF LEA     EDI, DS:[ESI+FFE58000]
0065E38C   .  C787 C4C01B00>MOV     DWORD PTR DS:[EDI+1BC0C4], 21B45>
0065E396   .  57            PUSH    EDI                              ;  ntdll.7C930738
0065E397   .  83CD FF       OR      EBP, FFFFFFFF
0065E39A   .  EB 0E         JMP     SHORT SpokenKi.0065E3AA
这是壳程序的入口,按Ctrl+F查找“POPAD”命令:
A,
0065E4D1   > \61            POPAD
0065E4D2   .  C3            RETN
B,
0065E504   > \61            POPAD
0065E505   .^ E9 CE10F5FF   JMP     SpokenKi.005AF5D8
嗯,B处看起来就是脱壳完成点了,005AF5D8应该就是程序的入口点!设断点在此句,运行,
停在这里,看起来好像是OEP,不过后面的都是乱码,不着急,在代码上点右键,选“分析”->“分析代码”怎么样?不是乱码了吧?
脱壳完成:
005AF5D8   > /55            PUSH    EBP
005AF5D9   . |8BEC          MOV     EBP, ESP
005AF5DB   . |B9 96010000   MOV     ECX, 196
005AF5E0   . |6A 00         PUSH    0
005AF5E2   ? |6A 00         PUSH    0
005AF5E4   . |49            DEC     ECX                              ;  IMM32.75E02465
005AF5E5   ?^|75 F9         JNZ     SHORT SpokenKi.005AF5E0
005AF5E7   . |53            PUSH    EBX
005AF5E8   . |56            PUSH    ESI                              ;  KERNEL32.77E79FF0
005AF5E9   ? |57            PUSH    EDI                              ;  KERNEL32.77E813FD
005AF5EA   ? |B8 F8F05A00   MOV     EAX, SpokenKi.005AF0F8
005AF5EF   . |E8 B878E5FF   CALL    SpokenKi.00406EAC
005AF5F4   . |8B1D DC475C00 MOV     EBX, DS:[5C47DC]                 ;  SpokenKi.005C8FF4
005AF5FA   ? |8B35 244A5C00 MOV     ESI, DS:[5C4A24]                 ;  SpokenKi.005C838C
005AF600     |33            DB      33                               ;  CHAR '3'
005AF601     |C0            DB      C0
005AF602     |55            DB      55                               ;  CHAR 'U'
005AF603     |68            DB      68                               ;  CHAR 'h'
005AF604     |2F            DB      2F                               ;  CHAR '/'
005AF605     |BC            DB      BC
005AF606     |5B            DB      5B                               ;  CHAR '['
005AF607     |00            DB      00
005AF608     |64            DB      64                               ;  CHAR 'd'
005AF609     |FF            DB      FF

分析完成:

005AF5D8   > /55            PUSH    EBP
005AF5D9   . |8BEC          MOV     EBP, ESP
005AF5DB   . |B9 96010000   MOV     ECX, 196
005AF5E0   > |6A 00         PUSH    0
005AF5E2   . |6A 00         PUSH    0
005AF5E4   . |49            DEC     ECX                              ;  IMM32.75E02465
005AF5E5   .^|75 F9         JNZ     SHORT SpokenKi.005AF5E0
005AF5E7   . |53            PUSH    EBX
005AF5E8   . |56            PUSH    ESI                              ;  KERNEL32.77E79FF0
005AF5E9   . |57            PUSH    EDI                              ;  KERNEL32.77E813FD
005AF5EA   . |B8 F8F05A00   MOV     EAX, SpokenKi.005AF0F8
005AF5EF   . |E8 B878E5FF   CALL    SpokenKi.00406EAC
005AF5F4   . |8B1D DC475C00 MOV     EBX, DS:[5C47DC]                 ;  SpokenKi.005C8FF4
005AF5FA   . |8B35 244A5C00 MOV     ESI, DS:[5C4A24]                 ;  SpokenKi.005C838C
005AF600   . |33C0          XOR     EAX, EAX
005AF602   . |55            PUSH    EBP
005AF603   . |68 2FBC5B00   PUSH    SpokenKi.005BBC2F
005AF608   . |64:FF30       PUSH    DWORD PTR FS:[EAX]
005AF60B   . |64:8920       MOV     FS:[EAX], ESP
005AF60E   . |6A 00         PUSH    0                                ; /lParam = 0
005AF610   . |68 30E05A00   PUSH    SpokenKi.005AE030                ; |Callback = SpokenKi.005AE030
005AF615   . |E8 3683E5FF   CALL    SpokenKi.00407950                ; \EnumWindows
  此时可以直接Dump得到完整的脱壳后的程序,这一步我们先不做(下面就知道为什么了),继续分析:
看到没有:CALL    <JMP.&user32.EnumWindows>,查资料得知这是反破解措施之一,我们认识一下,上一行的“Callback”程序就是反破解处理,咱也进去看看,学点儿见识。
005AE030  /.  55            PUSH    EBP
;;;;此处略去若干行。
005AE065  |.  E8 A69AE5FF   CALL    <JMP.&user32.GetWindowTextA>     ; \GetWindowTextA
005AE06A  |.  85C0          TEST    EAX, EAX            ;得到窗口名称。
005AE06C  |.  7E 29         JLE     SHORT SpokenKi.005AE097
005AE06E  |.  68 FF000000   PUSH    0FF                              ; /Count = FF (255.)
005AE073  |.  8D85 FCFDFFFF LEA     EAX, SS:[EBP-204]                ; |
005AE079  |.  50            PUSH    EAX                              ; |Buffer = NULL
005AE07A  |.  56            PUSH    ESI                              ; |hWnd = FFFFFFFF
005AE07B  |.  E8 2899E5FF   CALL    <JMP.&user32.GetClassNameA>      ; \GetClassNameA ;得到类别名称
005AE080  |.  85C0          TEST    EAX, EAX
005AE082  |.  7E 13         JLE     SHORT SpokenKi.005AE097
005AE084  |.  8D45 FC       LEA     EAX, SS:[EBP-4]
005AE087  |.  8D95 FCFEFFFF LEA     EDX, SS:[EBP-104]
005AE08D  |.  B9 00010000   MOV     ECX, 100
005AE092  |.  E8 7D6DE5FF   CALL    SpokenKi.00404E14
005AE097  |>  8B13          MOV     EDX, DS:[EBX]
005AE099  |.  8B45 FC       MOV     EAX, SS:[EBP-4]
005AE09C  |.  E8 B7FEFFFF   CALL    SpokenKi.005ADF58    ;比较是否是“ExeSpy”。
005AE0A1  |.  84C0          TEST    AL, AL
005AE0A3  |.  0F85 79060000 JNZ     SpokenKi.005AE722
在005AE097设断点,运行到此,
BX=005C88BC
DS:[005C88BC]=00FF34C8, (ASCII "ExeSpy")
EDX=00FF34C8, (ASCII "ExeSpy")
看一下内存:
00FF34C8  45 78 65 53 70 79 00 00 16 00 00 00 01 00 00 00  ExeSpy.. ... ...
00FF34D8  05 00 00 00 77 78 72 39 35 00 00 00 16 00 00 00   ...wxr95... ...
00FF34E8  01 00 00 00 06 00 00 00 52 65 67 6D 6F 6E 00 00   ... ...Regmon..
00FF34F8  1E 00 00 00 01 00 00 00 0C 00 00 00 46 69 6C 65   ... .......File
00FF3508  20 4D 6F 6E 69 74 6F 72 00 00 00 00 1A 00 00 00   Monitor.... ...
00FF3518  01 00 00 00 0A 00 00 00 50 72 6F 63 44 75 6D 70   .......ProcDump
00FF3528  33 32 00 00 1A 00 00 00 01 00 00 00 08 00 00 00  32.. ... ... ...
00FF3538  52 65 67 4D 6F 6E 45 78 00 00 00 00 22 00 00 00  RegMonEx...."...
00FF3548  01 00 00 00 10 00 00 00 57 69 6E 64 6F 77 20 44   ... ...Window D
00FF3558  65 74 65 63 74 69 76 65 00 00 00 00 1A 00 00 00  etective.... ...
00FF3568  01 00 00 00 09 00 00 00 44 65 62 75 67 56 69 65   .......DebugVie
00FF3578  77 00 00 00 16 00 00 00 01 00 00 00 07 00 00 00  w... ... ......
00FF3588  72 65 67 73 6E 61 70 00 16 00 00 00 01 00 00 00  regsnap. ... ...
00FF3598  06 00 00 00 52 65 73 53 70                        ...ResSp
再往下走更不得了,原来是这样:

跳转来自 005AE0A3, 005AE0B6, 005AE0C9, 005AE0DC, 005AE0EF, 005AE102, 005AE115, 005AE128, 005AE13B, 005AE14E,
 005AE161, 005AE174, 005AE187, 005AE19A, 005AE1AD, 005AE1C0, 005AE1D3, 005AE1E6, 005AE1F9, 005AE20C,
 005AE21F, 005AE232, 005AE245, 005AE258

005AE722  |> \6A 00         PUSH    0                                ; /lParam = 0
005AE724  |.  6A 00         PUSH    0                                ; |wParam = 0
005AE726  |.  6A 12         PUSH    12                              ; |Message = WM_QUIT;退出消息。
005AE728  |.  56            PUSH    ESI                              ; |hWnd = 10080
005AE729  |.  E8 FA94E5FF   CALL    <JMP.&user32.PostMessageA>       ; \PostMessageA  ;广播消息函数
现在知道了,所谓的反破解手段之一“EnumWindows”大法就是列举所有正在运行的窗口,发现和跟踪、调试相关的软件后发送一个“WM_QUIT”的消息使其关闭(把005AE726  PUSH    12  这一行改成“PUSH 0”,即发送一个NULL信息,就破掉了这个反调试,不过我们现在并不破解,只是跟踪一下程序流程而已)。PS:竟然OD也在黑名单,嘿嘿,太小看我们 OD了吧?你在我手心里还想反过来整我? 没门儿!OD是不会被终结的。
  继续运行到这里:
005AF63F   .  8B45 EC       MOV     EAX, SS:[EBP-14]
005AF642   .  8B96 08040000 MOV     EDX, DS:[ESI+408]
005AF648   .  E8 6359E5FF   CALL    SpokenKi.00404FB0
005AF64D   .  74 47         JE      SHORT SpokenKi.005AF696
= = = =
EAX 00FFA540 ASCII "spokenking.exe"
ECX 00FFA538
EDX 00FF7AA8 ASCII "spokenking.exe"
原来文件名也不能改。----幸亏没有DUMP!
再往下走就更多的限制了,文件修改日期也不能改,运行文件夹里不能存在其他exe文件,如果存在“C:\WINNT\System32\MSSpeechEAPI.dll”文件,也同样提示过期,这些也不能一一道来,最后在做个总结吧,反正都是反破解的手段,关键是只要一次出错,就运行次数到期(我也是在重装了几次系统之后知道的,别以为我是高手,高手才不会爆破呢),我们需要的是找到次数运算的地方。先粗略地 F8一下,结果这一粗略,我按了N次(N>50)F8,最后来到:
005BBAF5   > \A1 7C4A5C00   MOV     EAX, DS:[5C4A7C]
005BBAFA   .  8B00          MOV     EAX, DS:[EAX]
005BBAFC   .  E8 AB5CECFF   CALL    SpokenKi.004817AC ;--这个Call后出现运行窗口。
005BBB01   .  E8 6A2CFFFF   CALL    SpokenKi.005AE770
这一路下来,分析得知:
  运行次数保存在注册表和系统目录的文件中,每次运行都会交替在System32中和Help文件夹生成次数文件,每次的文件名是固定的,但不相同,文件名字串也经过加密后放在程序里,程序会根据文件中和注册表中的次数运行,两者不一致直接KO,中途还会有很多暗桩,不下3、40处,不过都是比较明显的判断,现在我们要说了,这么多的点,怎么爆破呢?我要说,当你知道了程序流程之后,爆破纯粹就是力气活了(怪不得大牛们不屑爆破,但菜菜鸟倒应该从爆破学起)。在这里我采取“倒叙法”,就是从上面的运行窗口出现的地方开始,往上走,看怎样才能到这里。
1,
005BB9C6   .  E8 E594E4FF   CALL    SpokenKi.00404EB0
005BB9CB   .  8B85 58F3FFFF MOV     EAX, SS:[EBP-CA8]                ;  SpokenKi.0065006E
005BB9D1   .  E8 22E2E4FF   CALL    SpokenKi.00409BF8
005BB9D6   .  84C0          TEST    AL, AL
005BB9D8   .  0F84 17010000  JE      SpokenKi.005BBAF5          ;需要改为jmp(以下所有爆破点均需要在脱壳后的程序上修改,不再明示)。
2,
005BB7F8   .  8B85 64F3FFFF MOV     EAX, SS:[EBP-C9C]
005BB7FE   .  E8 F5E3E4FF   CALL    SpokenKi.00409BF8
005BB803   .  84C0          TEST    AL, AL
005BB805   .  0F84 1A010000 JE      SpokenKi.005BB925          ;需要改为jmp。
中间只要看到跳转我们就直接改为JMP,这就是爆破者的特征^-^,此处我就略过N个,
到这里:
005BB43E   > \833D 20935C00>CMP     DWORD PTR DS:[5C9320], 0  ;运行次数不能等0,否则OVER.
005BB445   .  74 72         JE      SHORT SpokenKi.005BB4B9
005BB447   .  68 792C0000   PUSH    2C79
005BB44C   .  8D85 8CF3FFFF LEA     EAX, SS:[EBP-C74]
005BB452   .  50            PUSH    EAX
005BB453   .  B9 DD570000   MOV     ECX, 57DD
005BB458   .  BA 870A0000   MOV     EDX, 0A87
005BB45D   .  B8 7CC05B00   MOV     EAX, SpokenKi.005BC07C  ; ASCII "OGw=";经过加密后的最大次数“20”。
005BB462   .  E8 A189F8FF   CALL    SpokenKi.00543E08
005BB467   .  8B85 8CF3FFFF MOV     EAX, SS:[EBP-C74]    ;解密得到字符串“20”。
005BB46D   .  E8 BAE3E4FF   CALL    SpokenKi.0040982C    ;转换成十六进制“14”到EAX中,
005BB472   .  3B05 20935C00 CMP     EAX, DS:[5C9320]    ;和当前运行次数备份进行比较。
005BB478   .  74 3F         JE      SHORT SpokenKi.005BB4B9;相等则OVER。
005BB47A   .  68 792C0000   PUSH    2C79
005BB47F   .  8D85 88F3FFFF LEA     EAX, SS:[EBP-C78]
005BB485   .  50            PUSH    EAX
005BB486   .  B9 DD570000   MOV     ECX, 57DD
005BB48B   .  BA 870A0000   MOV     EDX, 0A87
005BB490   .  B8 7CC05B00   MOV     EAX, SpokenKi.005BC07C ; ASCII "OGw=" ASCII "OGw=";经过加密后的次数“20”。
005BB495   .  E8 6E89F8FF   CALL    SpokenKi.00543E08
005BB49A   .  8B85 88F3FFFF MOV     EAX, SS:[EBP-C78]
005BB4A0   .  E8 87E3E4FF   CALL    SpokenKi.0040982C    ;再次得到最大运行次数定义值“14”。
005BB4A5   .  3BF8          CMP     EDI, EAX        ;EDI中为当前运行次数。
005BB4A7   .  74 10         JE      SHORT SpokenKi.005BB4B9 ;相等则OVER。
005BB4A9   .  85FF          TEST    EDI, EDI                         ;  ntdll.7C930738
005BB4AB   .  74 0C         JE      SHORT SpokenKi.005BB4B9  ;等“0”也OVER。
005BB4AD   .  3B3D 20935C00 CMP     EDI, DS:[5C9320]    ;比较两个方式存放的运行次数是否相等。
005BB4B3   .  0F84 88010000 JE      SpokenKi.005BB641    ;相等则OK。
上面这一段不必爆破,原因是如果你再走走可以发现,这样的判断有多个,要爆破工作量太大,且还不能保证程序运行中是否还有次数判断,这就给了我们方向把次数都设置成1即可,我们再往上走,看看这两个次数是怎么得到的。
PS:通过分析“CALL    SpokenKi.00543E08”之后我们得知,这个是解密程序,其实质是一个BASE64的变形版本,只不过多了三个运算参数,而这三个参数看起来和每一个加密字串都一一对应,包括程序资源中的所有字符串。所以如果你想更改系统资源串,工作量就非常大,我就特意写了一个加密器,来对付这个软件,把我们的加密串替换掉程序中的加密串,就可以显示我们自己的信息了。这些都是题外话了,暂且不提。
再往上走:
005BB2E8   > \A1 44935C00   MOV     EAX, DS:[5C9344]                 ; 又一个次数备份:
005BB2ED   .  3B05 20935C00 CMP     EAX, DS:[5C9320]                 ; |
005BB2F3   .  74 05         JE      SHORT SpokenKi.005BB2FA          ; 次数比较,相等则OK。
;;;
005BA5A0   > \A1 44935C00   MOV     EAX, DS:[5C9344]                 ;
005BA5A5   .  3B05 20935C00 CMP     EAX, DS:[5C9320]                 ; |
005BA5AB   .  74 05         JE      SHORT SpokenKi.005BA5B2          ; |
DS:[005C9344]=00000000
EAX=00000000
跳转来自 005B39CB, 005B4338, 005B4871, 005B4DAA, 005B52E3, 005B581C, 005B5D55, 005B628E,
   005B67C7, 005B6D00, 005B7239, 005B7772,005B7CAB, 005B81E4, 005B871D, 005B8C56,
   005B918F, 005B96C8, 005B9C05

; 这个地方有很多程序跳过来,数一数,19个!和运行次数20次相比,少一个,除去最后一次运行,正好,现在就猜到上面都是运行次数有效后的跳转,我们直接找到第二次跳转“005B4338”(任何一个都可以破解,为了方便,我们找第二个)。
005B42D7   > \8B15 00495C00 MOV     EDX, DS:[5C4900]                 ;  SpokenKi.005C8C9C
005B42DD   .  8B52 28       MOV     EDX, DS:[EDX+28]
005B42E0   .  B9 75670000   MOV     ECX, 6775
005B42E5   .  A1 24935C00   MOV     EAX, DS:[5C9324]
005B42EA   .  E8 F1ACFFFF   CALL    SpokenKi.005AEFE0        ;从文件中得到运行次数。
005B42EF   .  FF05 20935C00 INC     DWORD PTR DS:[5C9320]        ;运行次数加一,
====》快刀斩乱麻:不跟踪得到运行次数的Call,直接送1
;;005B42EA   .  C705 20935C00>MOV     DWORD PTR DS:[5C9320], 1    ;搞定一个运行次数。
;;005B42F4   .  90            NOP

005B42F5   .  8B15 00495C00 MOV     EDX, DS:[5C4900]                 ;  SpokenKi.005C8C9C
005B42FB   .  8B52 2C       MOV     EDX, DS:[EDX+2C]
005B42FE   .  B9 3C540000   MOV     ECX, 543C
005B4303   .  A1 28935C00   MOV     EAX, DS:[5C9328]
005B4308   .  E8 A3ABFFFF   CALL    SpokenKi.005AEEB0      ;生成下一次运行次数的文件,NOP掉。
005B430D   .  8B0D 00495C00 MOV     ECX, DS:[5C4900]                 ;  SpokenKi.005C8C9C
005B4313   .  8B49 28       MOV     ECX, DS:[ECX+28]
005B4316   .  8D85 7CFBFFFF LEA     EAX, SS:[EBP-484]
005B431C   .  8B15 24935C00 MOV     EDX, DS:[5C9324]
005B4322   .  E8 890BE5FF   CALL    SpokenKi.00404EB0
005B4327   .  8B85 7CFBFFFF MOV     EAX, SS:[EBP-484]
005B432D   .  E8 320DE5FF   CALL    SpokenKi.00405064
005B4332   .  50            PUSH    EAX                           ; /FileName = NULL  ;NOP掉。
005B4333   .  E8 882EE5FF   CALL    <JMP.&KERNEL32.DeleteFileA>   ; \DeleteFileA.删除运行次数文件,NOP掉
005B4338   . /E9 63620000    JMP     SpokenKi.005BA5A0
继续向上看。
005B3E1A   .  8B85 DCFBFFFF MOV     EAX, SS:[EBP-424]                ;判断第一个运行文件是否存在。
005B3E20   .  E8 D35DE5FF   CALL    SpokenKi.00409BF8
005B3E25   .  84C0          TEST    AL, AL    
005B3E27   .  0F84 10050000 JE      SpokenKi.005B433D          ;直接JMP。
;EAX=0106C7F8, (ASCII "C:\\WINDOWS\\system32\\SCBZDEIES.DLL");第一次运行后生成的文件,我们默认存在即可。

005B3BC2   .  8B15 00495C00 MOV     EDX, DS:[5C4900]                 ;  SpokenKi.005C8C9C
        ;;EDX=01065530, (ASCII "{97BB3C69-D52A-11D0-D149-17748809437B}"
005B3BC8   .  8B52 20       MOV     EDX, DS:[EDX+20]
005B3BCB   .  B9 01000000   MOV     ECX, 1
005B3BD0   .  A1 1C935C00   MOV     EAX, DS:[5C931C]
005B3BD5   .  E8 DEF2EDFF   CALL    SpokenKi.00492EB8        ;从注册表中得到运行次数备份。
爆破点! 直接修改为:
005B3BD5      33C0          XOR     EAX, EAX              ;搞定第二个运行次数。
005B3BD7      40            INC     EAX
005B3BD8      90            NOP
005B3BD9      90            NOP

005B3BDA   .  8BF8          MOV     EDI, EAX              ;得到运行次数到EDI!
005B3BDC   .  68 712C0000   PUSH    2C71
005B3BE1   .  8D85 FCFBFFFF LEA     EAX, SS:[EBP-404]
005B3BE7   .  50            PUSH    EAX
005B3BE8   .  B9 D5570000   MOV     ECX, 57D5
005B3BED   .  BA 7F0A0000   MOV     EDX, 0A7F
005B3BF2   .  B8 1CC05B00   MOV     EAX, SpokenKi.005BC01C           ;  ASCII "O3Y="
005B3BF7   .  E8 0C02F9FF   CALL    SpokenKi.00543E08
005B3BFC   .  8B85 FCFBFFFF MOV     EAX, SS:[EBP-404]
005B3C02   .  E8 255CE5FF   CALL    SpokenKi.0040982C
005B3C07   .  3BF8          CMP     EDI, EAX
005B3C09   .  7E 2D         JLE     SHORT SpokenKi.005B3C38  ;判断次数是否>=20.
;;
005B3C38   > \47            INC     EDI                              ;  增加运行次数,NOP掉。
这里是关键,既然EDI里面存的是运行次数,那我们可以直接送它一个“1”,后面的INC也可以不用了。
继续向上,
005B39D0   > \E8 B3ADFFFF   CALL    SpokenKi.005AE788
005B39D5   .  803D 59935C00>CMP     BYTE PTR DS:[5C9359], 1
005B39DC   .  74 43         JE      SHORT SpokenKi.005B3A21  ;改为JMP
;;;
005B2B64   > \8B15 00495C00 MOV     EDX, DS:[5C4900]                 ;  SpokenKi.005C8C9C
005B2B6A   .  8B52 20       MOV     EDX, DS:[EDX+20]
005B2B6D   .  B1 01         MOV     CL, 1
005B2B6F   .  A1 1C935C00   MOV     EAX, DS:[5C931C]
005B2B74   .  E8 7306EEFF   CALL    SpokenKi.004931EC
005B2B79   .  A2 58935C00   MOV     DS:[5C9358], AL
005B2B7E   .  A0 40935C00   MOV     AL, DS:[5C9340]
005B2B83   .  3A05 58935C00  CMP     AL, DS:[5C9358]
005B2B89   .  0F84 1F010000  JE      SpokenKi.005B2CAE
由于DS:[5C9358]会在后续判断中用到,且其值需为0,DS:[5C9340] 又为运行次数!故在此一并修改之。
005B2B6D   .  33C0          XOR     EAX, EAX
005B2B6F   .  90            NOP
005B2B70   .  90            NOP
005B2B71   .  A2 58935C00   MOV     DS:[5C9358], AL
005B2B76   .  C605 40935C00>MOV     BYTE PTR DS:[5C9340], 1
005B2B7D   .  E9 2C010000   JMP     SpokenKi.005B2CAE
到此为止,关键的爆破点都已被我们拆除,剩下的工作就是往上走,碰到有可能偏离轨道的跳转全部JMP之,
特殊点儿的地方:
005B0E3E   .  E8 41CAEDFF   CALL    SpokenKi.0048D884
005B0E43   .  40            INC     EAX
005B0E44   .  0F85 96000000 JNZ     SpokenKi.005B0EE0    ;JMP之。
由于很多爆破点都很雷同,我就不一一说明,如果你还记得刚开始程序会判断文件名和文件修改日期的话,这些地方不必爆破,我们可以用文件属性修改器把文件修改日期改为:2008-12-02 - 18:18:18即可正常运行,否则程序运行中仍然有判断点,没必要去爆破,欺骗是一种好手段(在社会上混可不能这样哦)。

好的,记下了这么多,咱们可以直接把脱壳后的文件改名成原文件名,日期也修改好,爆破点一一爆破,删除原来的程序文件,双击我们的程序,怎么样?可以正确地开始回到一次运行次数了吧?如果你的仍然提示过期或者不是第一次运行,不要紧,再用OD载入,F8运行,看什么地方会跳走就可以了,一定是没有修改完爆破点。
采用倒叙法,可以很轻松地实现运行次数不变,只是现在的软件越来越多的反爆破点,使爆破花费的时间更多,不过,只要有耐心,还是很容易搞定的,因为流程非常简单。
我正高兴着呢,突然程序又无提示关闭了,嗯,看来还用到了定时器检测!好,现在我们用IDA载入程序,找到ExitProcess,有两个地址,咱在OD里把两个地方都设置断点然后运行。
UPX0:004071E0                 jmp     ds:__imp_ExitProcess
UPX0:00401344                 jmp     ds:__imp_ExitProcess_0
等待几秒钟后,程序停在这里:
004071DE    8BC0            MOV     EAX, EAX
004071E0  - FF25 80A45C00   JMP     NEAR DS:[<&KERNEL32.ExitProcess>>; kernel32.ExitProcess
看堆栈:
0012F12C   005A154E  /CALL 到 ExitProcess 来自 SpokenKi.005A1549  ;这里是退出的地方。
0012F130   0012F1E4  \ExitCode = 12F1E4
我们看看:
005A13CD    8D40 00         LEA     EAX, DS:[EAX]
005A13D0    53              PUSH    EBX
005A13D1    56              PUSH    ESI
005A13D2    57              PUSH    EDI                              ; ntdll.7C930738
005A13D3    8BD8            MOV     EBX, EAX
005A13D5    6A 00           PUSH    0
005A13D7    68 EC955900     PUSH    SpokenKi.005995EC          ;退出前还要破坏我们的好心情,这个回调函数也要把它爆破掉。
005A13DC    E8 6F65E6FF     CALL    <JMP.&user32.EnumWindows>
005A13E1    33D2            XOR     EDX, EDX                         ; ntdll.KiFastSystemCallRet
005A13E3    8B83 40060000   MOV     EAX, DS:[EBX+640]
005A13E9    E8 2250E9FF     CALL    SpokenKi.00436410
005A13EE    8B15 2C4C5C00   MOV     EDX, DS:[5C4C2C]                 ; SpokenKi.005C8A04
005A13F4    8B52 78         MOV     EDX, DS:[EDX+78]
005A13F7    A1 F8925C00     MOV     EAX, DS:[5C92F8]
005A13FC    E8 FB2EFAFF     CALL    SpokenKi.005442FC
005A1401    84C0            TEST    AL, AL
005A1403   /0F84 45010000     JE      SpokenKi.005A154E        ;;直接在这里JMP即可。
 005A1408    00A1 2C4C5C00   ADD     DS:[ECX+5C4C2C], AH    ;下面这些咱就不必看了,省略N行代码。
…………
005A153D    E8 6622EDFF     CALL    SpokenKi.004737A8
005A1542    C683 51060000 0>MOV     BYTE PTR DS:[EBX+651], 1
005A1549    E8 925CE6FF     CALL    <JMP.&KERNEL32.ExitProcess>    ;原来这里是退出点!
005A154E    5F              POP     EDI                              ; kernel32.7C816FD7
005A154F    5E              POP     ESI                              ; kernel32.7C816FD7
005A1550    5B              POP     EBX                              ; kernel32.7C816FD7
005A1551    C3              RETN
再次存盘退出,记得文件修改日期还要恢复一下。双击之后,等啊等,嗯,一分钟过去了,十分钟过去了,没有退出,看来我们的大功已经告成!嘿!一小时后又退出!这个地方建议还是不要爆破的好,作者的限制成了好意,谁愿意对着电脑学习一个小时不休息啊?还是休息一下,过一会儿再来学习不是更好?

通过爆破这个软件,我们可以意识到,现在的软件反爆破的能力有所提高,这对于我们这些充满高涨学习热情的菜菜鸟来说,真的是个噩耗,有多少菜菜鸟被打击的裹足不前,没有成功的经历做鼓励,我们菜菜鸟什么时候才能成为大牛啊?不过回头看看,我们也应该意识到,不好好打基础,想提高自己实在是太难了,所谓“学无止境”是也。