• 标 题:一个ReverseME破解 SynApsus's ReverseME #1 (13千字)
  • 作 者:RoBa  
  • 时 间:2004-01-19 01:06:12
  • 链 接:http://bbs.pediy.com

SynApsus's ReverseME #1

感谢hello兄提供的NCG的CrackMe,正为CCG的CrackMe郁闷,先找几个简单的下手.从其中挑了一个ReverseMe.这个要求修改程序,使其在输入NAME的同时把正确的SERAIL在下面显示出来.

关键代码如下:

:004011E8 6A32                    push 00000032
:004011EA 6825304000              push 00403025 <-NAME放在403025
:004011EF FF3505304000            push dword ptr [00403005] <-这是NAME文本框的句柄

* Reference To: USER32.GetWindowTextA, Ord:015Bh
                                  |
:004011F5 E808010000              Call 00401302
:004011FA A315304000              mov dword ptr [00403015], eax
:004011FF 3B0519304000            cmp eaxdword ptr [00403019]
:00401205 0F84DD000000            je 004012E8 <-???莫名其妙,其实大有用处
:0040120B 803D2530400000          cmp byte ptr [00403025], 00
:00401212 0F84D0000000            je 004012E8 <-判断是否为空
:00401218 6A05                    push 00000005

* Reference To: KERNEL32.Sleep, Ord:0273h
                                  |
:0040121A E807010000              Call 00401326
:0040121F 6A32                    push 00000032
:00401221 6857304000              push 00403057 <-Serial放在403057
:00401226 FF3509304000            push dword ptr [00403009] <-这是Serial文本框的句柄

* Reference To: USER32.GetWindowTextA, Ord:015Bh
                                  |
:0040122C E8D1000000              Call 00401302
:00401231 A31D304000              mov dword ptr [0040301D], eax
:00401236 3B0521304000            cmp eaxdword ptr [00403021]
:0040123C 0F84A6000000            je 004012E8
:00401242 803D5730400000          cmp byte ptr [00403057], 00
:00401249 0F8499000000            je 004012E8
:0040124F 33C0                    xor eaxeax
:00401251 33DB                    xor ebxebx
:00401253 33C9                    xor ecxecx
:00401255 8D0525304000            lea eaxdword ptr [00403025]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040126B(C)
|
:0040125B 8A8B25304000            mov clbyte ptr [ebx+00403025] <-把NAME依次取出
:00401261 03C1                    add eaxecx <-和403025h累加起来
:00401263 43                      inc ebx
:00401264 80BB2530400000          cmp byte ptr [ebx+00403025], 00
:0040126B 75EE                    jne 0040125B <-循环计算
:0040126D BB09030000              mov ebx, 00000309 
:00401272 F7E3                    mul ebx <-再乘以309h
:00401274 03C3                    add eaxebx <-再加倍
:00401276 33DB                    xor ebxebx
:00401278 33D2                    xor edxedx
:0040127A 33F6                    xor esiesi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401290(U)
|
:0040127C B906000000              mov ecx, 00000006 
:00401281 F7E1                    mul ecx <-乘6循环累加
:00401283 8A9E25304000            mov blbyte ptr [esi+00403025]
:00401289 84DB                    test blbl
:0040128B 7405                    je 00401292
:0040128D 03C3                    add eaxebx
:0040128F 46                      inc esi
:00401290 EBEA                    jmp 0040127C <-循环计算,得到EAX为最终结果(计算过程在这里并不重要)

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040128B(C)
|
:00401292 50                      push eax <-结果入栈
:00401293 33C0                    xor eaxeax 
:00401295 8D3557304000            lea esidword ptr [00403057] <-取出Serial
:0040129B 8A1E                    mov blbyte ptr [esi]
:0040129D 46                      inc esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004012AF(C)
|
:0040129E 80EB30                  sub bl, 30 <-每一位减30h
:004012A1 BF0A000000              mov edi, 0000000A <-乘以A
:004012A6 F7E7                    mul edi
:004012A8 03C3                    add eaxebx <-累加
:004012AA 8A1E                    mov blbyte ptr [esi]
:004012AC 46                      inc esi
:004012AD 84DB                    test blbl
:004012AF 75ED                    jne 0040129E <-循环计算,可以看出实际上是把输入的SERIAL当成十进制数转为了数值
:004012B1 5B                      pop ebx <-前面NAME的计算结果出栈
:004012B2 8D0D15304000            lea ecxdword ptr [00403015]
:004012B8 8D151D304000            lea edxdword ptr [0040301D]
:004012BE 890D19304000            mov dword ptr [00403019], ecx
:004012C4 891521304000            mov dword ptr [00403021], edx
:004012CA 3BC3                    cmp eaxebx <-把NAME的结果和SERAIL的结果比较
:004012CC 751A                    jne 004012E8 <-关键跳转
:004012CE C6050030400001          mov byte ptr [00403000], 01
:004012D5 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"Good serial !!!"
                                  |
:004012D7 68E0304000              push 004030E0

* Possible StringData Ref from Data Obj ->"Good Job Cracker ! But it's not "
                                        ->"enough, a serial to find... reverse "
                                        ->"this program now !"
                                  |
:004012DC 6889304000              push 00403089
:004012E1 6A00                    push 00000000

* Reference To: USER32.MessageBoxA, Ord:01BBh
                                  |
:004012E3 E826000000              Call 0040130E <-对话框出现,说这样还不够,要让程序自己出现注册码

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004011DB(C), :00401205(C), :00401212(C), :004012CC(C), :00401357(U)
|
:004012E8 61                      popad
:004012E9 33C0                    xor eaxeax
:004012EB C9                      leave
:004012EC C21000                  ret 0010

先整理一下思路,程序把NAME经过一番计算后与十进制的SERIAL比较,所以我们要把NAME的计算结果直接转成字串显示出来就OK了.

温习一下编程,把数值转成字串用什么函数呢?当然是wsprintf啦.还要把字串显示出来,就用SetWindowText吧.用LordPE给它加一个IID数组(人家说了: u can use all the tools you need.) ,加上USER32.DLL里的这两个函数,看看ThunkRVA是多少,我的是604C和6050.(作者还专门给我们留了一个空块放东西,有趣)

一开始改好后,一进入就会陷入死循环,我猜想程序是通过处理WM_这样的系统消息来执行这段代码,而我们写进去的东西要把正确的注册码显示在文本框,必然要再次触发这条消息,这样形成死循环.苦思冥想N十分钟后,想起程序里有两行很奇怪,就是在读出NAME和SERAIL以后有一个CMP是判断是否为空,还有一个却不知所云.原来这就是关键的地方,只要在这里设一个标志就OK了.

现在祭出HIEW,垒代码吧.还得来回转换什么文件偏移内存偏移,好痛苦.修改以后的代码如下:

:004011E8 6A32                    push 00000032
:004011EA 6825304000              push 00403025
:004011EF FF3505304000            push dword ptr [00403005]
:004011F5 E808010000              call 00401302 
:004011FA A315304000              mov dword ptr [00403015], eax 
:004011FF 3B0519304000            cmp eaxdword ptr [00403019] <-就是上面两句,EAX是文本的字符个数,把它放在[403015]以后,再把它和[403019]处的数比较,如果相同就跳走不做处理.就是说我们可以在每次显示出正确注册码后把[403015]处和[403019]处设为相同,这样如果NAME的位数不变,程序就不会处理.而每次NAME改变时EAX就会变化,程序继续处理新的NAME.(我考试时脑袋有这时的一半灵活就好了)
:00401205 0F84DD000000            je 004012E8
:0040120B 803D2530400000          cmp byte ptr [00403025], 00
:00401212 0F84D0000000            je 004012E8
:00401218 6A05                    push 00000005
:0040121A E807010000              call 00401326 <-上面没有变化,读取NAME
:0040121F 6A32                    push 00000032
:00401221 6857304000              push 00403057
:00401226 FF3509304000            push dword ptr [00403009]
:0040122C E8D1000000              call 00401302 <-读取SERIAL
:00401231 A31D304000              mov dword ptr [0040301D], eax
:00401236 3B0521304000            cmp eaxdword ptr [00403021]
:0040123C 90                      nop <-把这里对SERIAL的判断去掉
:0040123D 90                      nop                   <-不然如果SERIAL为空就永远会跳走
:0040123E 90                      nop
:0040123F 90                      nop
:00401240 90                      nop
:00401241 90                      nop
:00401242 803D5730400000          cmp byte ptr [00403057], 00
:00401249 90                      nop <-NOP大法非常实用:)
:0040124A 90                      nop
:0040124B 90                      nop
:0040124C 90                      nop
:0040124D 90                      nop
:0040124E 90                      nop
:0040124F 33C0                    xor eaxeax
:00401251 33DB                    xor ebxebx
:00401253 33C9                    xor ecxecx
:00401255 8D0525304000            lea eaxdword ptr [00403025]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040126B(C)
|
:0040125B 8A8B25304000            mov clbyte ptr [ebx+00403025]
:00401261 03C1                    add eaxecx
:00401263 43                      inc ebx
:00401264 80BB2530400000          cmp byte ptr [ebx+00403025], 00
:0040126B 75EE                    jne 0040125B
:0040126D BB09030000              mov ebx, 00000309
:00401272 F7E3                    mul ebx
:00401274 03C3                    add eaxebx
:00401276 33DB                    xor ebxebx
:00401278 33D2                    xor edxedx
:0040127A 33F6                    xor esiesi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401290(U)
|
:0040127C B906000000              mov ecx, 00000006
:00401281 F7E1                    mul ecx
:00401283 8A9E25304000            mov blbyte ptr [esi+00403025]
:00401289 84DB                    test blbl
:0040128B 7405                    je 00401292
:0040128D 03C3                    add eaxebx
:0040128F 46                      inc esi
:00401290 EBEA                    jmp 0040127C <-上面是对NAME的计算,我们知道结果在EAX就行了

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040128B(C)
|
:00401292 E995000000              jmp 0040132C <-跳到自己写的代码里去
:00401297 90                      nop
:00401298 90                      nop <-这里没用了,NOP掉好看一些
:00401299 90                      nop
:0040129A 90                      nop
:0040129B 8A1E                    mov blbyte ptr [esi]
:0040129D 46                      inc esi

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004012AF(C)
|
:0040129E 80EB30                  sub bl, 30
:004012A1 BF0A000000              mov edi, 0000000A
:004012A6 F7E7                    mul edi
:004012A8 03C3                    add eaxebx
:004012AA 8A1E                    mov blbyte ptr [esi]
:004012AC 46                      inc esi
:004012AD 84DB                    test blbl
:004012AF 75ED                    jne 0040129E
:004012B1 5B                      pop ebx
:004012B2 8D0D15304000            lea ecxdword ptr [00403015]
:004012B8 8D151D304000            lea edxdword ptr [0040301D]
:004012BE 890D19304000            mov dword ptr [00403019], ecx
:004012C4 891521304000            mov dword ptr [00403021], edx
:004012CA 3BC3                    cmp eaxebx
:004012CC 751A                    jne 004012E8
:004012CE C6050030400001          mov byte ptr [00403000], 01
:004012D5 6A00                    push 00000000

* Possible StringData Ref from Data Obj ->"Good serial !!!"
                                  |
:004012D7 68E0304000              push 004030E0

* Possible StringData Ref from Data Obj ->"Good Job Cracker ! But it's not "
                                        ->"enough, a serial to find... reverse "
                                        ->"this program now !"
                                  |
:004012DC 6889304000              push 00403089
:004012E1 6A00                    push 00000000
:004012E3 E826000000              call 0040130E

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004011DB(C), :00401205(C), :00401212(C), :004012CC(C), :00401357(U)
|
:004012E8 61                      popad
:004012E9 33C0                    xor eaxeax
:004012EB C9                      leave
:004012EC C21000                  ret 0010

......

(自己的代码)

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401292(U) <-第一次用汇编写这么长,很烂,高手莫笑
|
:0040132C 60                      pushad <-保险起见 
:0040132D 50                      push eax <-这就是上面的计算结果

* Possible StringData Ref from Code Obj ->"%lu" <-wsprintf的参数(我查了半天书才查到)
                                  |
:0040132E 6860134000              push 00401360 <-直接把它扔在后面了,一大片空地
:00401333 57                      push edi <-用EDI保存字符串的地址(?)
:00401334 8B0D15304000            mov ecxdword ptr [00403015] <-这里是关键的两句
:0040133A 890D19304000            mov dword ptr [00403019], ecx <-后加的这两句插在这里了
:00401340 FF1550604000            call dword ptr [00406050] <-这个CALL就是wsprintfA了
:00401346 83C40C                  add esp, 0000000C <-平衡堆栈
:00401349 57                      push edi <-转换得到的字符串
:0040134A FF3509304000            push dword ptr [00403009] <-这是那个输入SERIAL文本框的句柄
:00401350 FF154C604000            call dword ptr [0040604C] <-这个CALL是SetWindowTextA
:00401356 61                      popad 
:00401357 E98CFFFFFF              jmp 004012E8 <-终于完工喽
:0040135C 00000000                BYTE  4 DUP(0)

菜鸟问题:我想把得到的字符串放在401370处,不知为什么却不成功,后来只好蒙了一个EDI来放.
第一次搞这种东东(好像还称不上"逆向工程"),请各位高手指教.