B-Puzzle 5.1 完全破解

早就在以前的《论坛精华》看到大哥们介绍ADC公司的游戏,说是非常适合初学者练手。今天我就在网上下了个最新版的B-Puzzle,(所谓破鼓万人擂:)不过这个拼图游戏还挺好玩的)果然比较容易就看到了明码,但我们就到此为止了吗?不,我们的目标 是——没有蛀牙!~~~~不对不对,我不是卖牙膏的,我们的目标是——注册机!

废话少说,单击"Enter Reg Code",输入NAME: RoBa  CODE:87654321,下断追踪至此:

:0040C803 8B0D9C374A00            mov ecx, dword ptr [004A379C]
:0040C809 8B01                    mov eax, dword ptr [ecx]
:0040C80B 8B80D8020000            mov eax, dword ptr [eax+000002D8]
:0040C811 E8AA9E0400              call 004566C0
:0040C816 66C745B40800            mov [ebp-4C], 0008
:0040C81C 8B45A0                  mov eax, dword ptr [ebp-60]
:0040C81F E860080000              call 0040D084
:0040C824 66C745B42C00            mov [ebp-4C], 002C
:0040C82A 8D45F4                  lea eax, dword ptr [ebp-0C]
:0040C82D E8D24FFFFF              call 00401804
:0040C832 50                      push eax
:0040C833 FF45C0                  inc [ebp-40]
:0040C836 8D45FC                  lea eax, dword ptr [ebp-04]
:0040C839 E84258FFFF              call 00402080
:0040C83E 8BD0                    mov edx, eax
:0040C840 8B45A0                  mov eax, dword ptr [ebp-60]
:0040C843 59                      pop ecx
:0040C844 E843060000              call 0040CE8C                 <--关键CALL
:0040C849 8D55F4                  lea edx, dword ptr [ebp-0C]   <--下d *(ebp-c)可看到真码
:0040C84C 8D45F8                  lea eax, dword ptr [ebp-08]   <--下d *(ebp-c)可看到假码
:0040C84F E808B10800              call 0049795C
:0040C854 50                      push eax
:0040C855 FF4DC0                  dec [ebp-40]
:0040C858 8D45F4                  lea eax, dword ptr [ebp-0C]
:0040C85B BA02000000              mov edx, 00000002
:0040C860 E827B00800              call 0049788C
:0040C865 59                      pop ecx
:0040C866 84C9                    test cl, cl
:0040C868 0F8466010000            je 0040C9D4                   <--这里r fl z就会"注册成功"
:0040C86E B201                    mov dl, 01
:0040C870 A15C1E4300              mov eax, dword ptr [00431E5C]

跟进40C844处的CALL:

* Referenced by a CALL at Addresses:
|:0040C844   , :0040CD4E  
|
:0040CE8C 55                      push ebp
:0040CE8D 8BEC                    mov ebp, esp
:0040CE8F 81C4ACFDFFFF            add esp, FFFFFDAC
:0040CE95 894DF8                  mov dword ptr [ebp-08], ecx
:0040CE98 8955CC                  mov dword ptr [ebp-34], edx
:0040CE9B 8945D0                  mov dword ptr [ebp-30], eax
:0040CE9E B864CF4900              mov eax, 0049CF64
:0040CEA3 E82CF60700              call 0048C4D4
:0040CEA8 FF75CC                  push [ebp-34]
:0040CEAB 8D95ACFEFFFF            lea edx, dword ptr [ebp+FFFFFEAC]
:0040CEB1 52                      push edx
:0040CEB2 E821F30700              call 0048C1D8
:0040CEB7 83C408                  add esp, 00000008
:0040CEBA 8D8DACFEFFFF            lea ecx, dword ptr [ebp+FFFFFEAC]
:0040CEC0 51                      push ecx
:0040CEC1 E842F30700              call 0048C208
:0040CEC6 59                      pop ecx
:0040CEC7 83F808                  cmp eax, 00000008                 <--将长度与8比较
:0040CECA 7714                    ja 0040CEE0                       <--大于就跳过下面一段

* Possible StringData Ref from Data Obj ->"        "                <--hehe,8个空格
                                 |
:0040CECC 681DC84900              push 0049C81D                     <-这是长度小于8的处理
:0040CED1 8D85ACFEFFFF            lea eax, dword ptr [ebp+FFFFFEAC]
:0040CED7 50                      push eax
:0040CED8 E81FF20700              call 0048C0FC                     <-出来后在Name后加了8个空格
:0040CEDD 83C408                  add esp, 00000008

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CECA(C)
|

* Reference To: KERNEL32.GetTickCount, Ord:0000h
                                 |
:0040CEE0 E873B00800              Call 00497F58
:0040CEE5 8945C8                  mov dword ptr [ebp-38], eax
:0040CEE8 C745C401000000          mov [ebp-3C], 00000001

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CEF6(C)
|
:0040CEEF FF45C4                  inc [ebp-3C]                      -\  这三句在do what?
:0040CEF2 837DC464                cmp dword ptr [ebp-3C], 00000064   |->转圈100次??
:0040CEF6 7CF7                    jl 0040CEEF                       -/  你不会一直按F10吧:)

* Reference To: KERNEL32.GetTickCount, Ord:0000h
                                 |
:0040CEF8 E85BB00800              Call 00497F58
:0040CEFD 2B45C8                  sub eax, dword ptr [ebp-38]
:0040CF00 3DE8030000              cmp eax, 000003E8
:0040CF05 760D                    jbe 0040CF14
:0040CF07 8B15B4374A00            mov edx, dword ptr [004A37B4]
:0040CF0D 8B02                    mov eax, dword ptr [edx]
:0040CF0F E89CEE0300              call 0044BDB0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CF05(C)
|
:0040CF14 33D2                    xor edx, edx
:0040CF16 8955C0                  mov dword ptr [ebp-40], edx
:0040CF19 33C9                    xor ecx, ecx
:0040CF1B 894DB8                  mov dword ptr [ebp-48], ecx       <--上面一段乱烘烘的不管它

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CF90(C)                                                       <--循环开始
|
:0040CF1E FF353CC64900            push dword ptr [0049C63C]         <--这里一个串"PUZZLEVERO"
:0040CF24 E8DFF20700              call 0048C208                     <--eax=A 串的长度??
:0040CF29 59                      pop ecx
:0040CF2A 50                      push eax
:0040CF2B 8B45B8                  mov eax, dword ptr [ebp-48]       <--eax=[ebp-48]
:0040CF2E 5A                      pop edx                           <--edx=A
:0040CF2F 8BCA                    mov ecx, edx                      <--ecx=A
:0040CF31 33D2                    xor edx, edx            
:0040CF33 F7F1                    div ecx                           <--eax=eax div ecx
                                                                   <--edx=eax mod ecx
                                                                  <--这里的计算绕来绕去每次edx+2
* Possible StringData Ref from Data Obj ->"PUZZLEVERO"
                                 |
:0040CF35 A13CC64900              mov eax, dword ptr [0049C63C]       <--eax为串的开始位置
:0040CF3A 8A0410                  mov al, byte ptr [eax+edx]          <--得到第edx个字符
:0040CF3D 8B55B8                  mov edx, dword ptr [ebp-48]
:0040CF40 328415ACFEFFFF          xor al, byte ptr [ebp+edx-00000154] <--将串与Name中第edx个字符异或
:0040CF47 0FBEC8                  movsx ecx, al
:0040CF4A 894DB4                  mov dword ptr [ebp-4C], ecx
:0040CF4D 6A0A                    push 0000000A
:0040CF4F 8D45BC                  lea eax, dword ptr [ebp-44]
:0040CF52 50                      push eax
:0040CF53 FF75B4                  push [ebp-4C]
:0040CF56 E8BD380800              call 00490818                       <--关键的CALL
:0040CF5B 83C40C                  add esp, 0000000C
:0040CF5E 807DBD00                cmp byte ptr [ebp-43], 00           <--是否为0;即只有一位
:0040CF62 7504                    jne 0040CF68
:0040CF64 B231                    mov dl, 31                          <--如果为0则给'1'
:0040CF66 EB03                    jmp 0040CF6B                        

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CF62(C)
|
:0040CF68 8A55BD                  mov dl, byte ptr [ebp-43]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CF66(U)
|
:0040CF6B 8B45C0                  mov eax, dword ptr [ebp-40]
:0040CF6E 889405ACFDFFFF          mov byte ptr [ebp+eax-00000254], dl  <--把结果写入内存
:0040CF75 FF45C0                  inc [ebp-40]
:0040CF78 8B4DC0                  mov ecx, dword ptr [ebp-40]
:0040CF7B 8A45BC                  mov al, byte ptr [ebp-44]
:0040CF7E 88840DACFDFFFF          mov byte ptr [ebp+ecx-00000254], al  <--把结果写入内存
:0040CF85 FF45C0                  inc [ebp-40]
:0040CF88 8345B802                add dword ptr [ebp-48], 00000002     <--[ebp-48]+2
:0040CF8C 837DB808                cmp dword ptr [ebp-48], 00000008     <--[ebp-48]与8比较
:0040CF90 7C8C                    jl 0040CF1E                          <--小于则跳到循环开始

跟进40CF56处的CALL后我没有找到重要的东西,再进入里面唯一的CALL才到了关键部分:

* Referenced by a CALL at Addresses:
|:0049080D   , :0049083E   , :0049085B   , :00490883  
|
:00490788 55                      push ebp
:00490789 8BEC                    mov ebp, esp
:0049078B 83C4DC                  add esp, FFFFFFDC
:0049078E 53                      push ebx
:0049078F 56                      push esi
:00490790 57                      push edi
:00490791 8B7D10                  mov edi, dword ptr [ebp+10]
:00490794 8B7508                  mov esi, dword ptr [ebp+08]
:00490797 8B5D0C                  mov ebx, dword ptr [ebp+0C]
:0049079A 83FF02                  cmp edi, 00000002
:0049079D 7C4D                    jl 004907EC
:0049079F 83FF24                  cmp edi, 00000024
:004907A2 7F48                    jg 004907EC                <--确定edi的范围,可我的edi一直为A呀
:004907A4 85F6                    test esi, esi
:004907A6 7D0C                    jge 004907B4
:004907A8 807D1400                cmp byte ptr [ebp+14], 00
:004907AC 7406                    je 004907B4
:004907AE C6032D                  mov byte ptr [ebx], 2D
:004907B1 43                      inc ebx
:004907B2 F7DE                    neg esi

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004907A6(C), :004907AC(C)
|
:004907B4 8D4DDC                  lea ecx, dword ptr [ebp-24]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004907CA(C)
|
:004907B7 8BC6                    mov eax, esi            <--eax就是上边经过异或得到的那个值
:004907B9 33D2                    xor edx, edx            <--edx置0
:004907BB F7F7                    div edi                 <--除以A的余数=>edx
:004907BD 8811                    mov byte ptr [ecx], dl  <--把余数放入内存[ecx]处
:004907BF 41                      inc ecx                 <--给下一个数留下位置
:004907C0 8BC6                    mov eax, esi            <--eax还是那个值
:004907C2 33D2                    xor edx, edx            <--edx置0
:004907C4 F7F7                    div edi                 <--除以A的整数=>eax
:004907C6 8BF0                    mov esi, eax            <--eax=>esi
:004907C8 85C0                    test eax, eax
:004907CA 75EB                    jne 004907B7            <--直到eax为0为止
:004907CC EB17                    jmp 004907E5

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004907EA(C)
|
:004907CE 49                      dec ecx                
:004907CF 8A01                    mov al, byte ptr [ecx] <--倒序得到上面写进内存的值
:004907D1 3C0A                    cmp al, 0A
:004907D3 7D08                    jge 004907DD           <--大于等于A就跳
:004907D5 83C030                  add eax, 00000030      <--eax+30 也就是把数字变成相应字符的ASCII码啦:)
:004907D8 8803                    mov byte ptr [ebx], al <--写入内存中
:004907DA 43                      inc ebx                <--edx+1准备写下一个
:004907DB EB08                    jmp 004907E5

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004907D3(C)
|
:004907DD 024518                  add al, byte ptr [ebp+18]
:004907E0 04F6                    add al, F6
:004907E2 8803                    mov byte ptr [ebx], al
:004907E4 43                      inc ebx

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004907CC(U), :004907DB(U)
|
:004907E5 8D55DC                  lea edx, dword ptr [ebp-24]
:004907E8 3BCA                    cmp ecx, edx
:004907EA 75E2                    jne 004907CE               <--循环结束

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0049079D(C), :004907A2(C)
|
:004907EC C60300                  mov byte ptr [ebx], 00
:004907EF 8B450C                  mov eax, dword ptr [ebp+0C]
:004907F2 5F                      pop edi
:004907F3 5E                      pop esi
:004907F4 5B                      pop ebx
:004907F5 8BE5                    mov esp, ebp
:004907F7 5D                      pop ebp
:004907F8 C3                      ret

走完这里(4次)后你终于可以看到一个8位的码了,有什么感觉?松了一口气?慢着,先把那口气紧紧,惨痛的教训啊!我到了这里之后,觉得那个码和一开始直接看到的差不多,于是bc*,F5,开始写注册机,结果....全都不对5555~~~~:(
好,抄起家伙,咱们卷土重来:

:0040CF92 8B55C0                  mov edx, dword ptr [ebp-40]
:0040CF95 C68415ACFDFFFF00        mov byte ptr [ebp+edx-00000254], 00

* Reference To: KERNEL32.GetTickCount, Ord:0000h
                                 |
:0040CF9D E8B6AF0800              Call 00497F58
:0040CFA2 2B45C8                  sub eax, dword ptr [ebp-38]
:0040CFA5 3DE8030000              cmp eax, 000003E8
:0040CFAA 760D                    jbe 0040CFB9
:0040CFAC 8B0DB4374A00            mov ecx, dword ptr [004A37B4]
:0040CFB2 8B01                    mov eax, dword ptr [ecx]
:0040CFB4 E8F7ED0300              call 0044BDB0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CFAA(C)
|
:0040CFB9 8D95ACFDFFFF            lea edx, dword ptr [ebp+FFFFFDAC]
:0040CFBF 52                      push edx
:0040CFC0 E853340800              call 00490418            <--这个CALL是把字符串变为数值给eax
:0040CFC5 59                      pop ecx
:0040CFC6 8945B0                  mov dword ptr [ebp-50], eax       <--[ebp-50]是前面得到的8位码
:0040CFC9 33C9                    xor ecx, ecx
:0040CFCB 894DAC                  mov dword ptr [ebp-54], ecx      
:0040CFCE EB15                    jmp 0040CFE5

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CFF5(C)
|
:0040CFD0 8B45AC                  mov eax, dword ptr [ebp-54]            <--循环开始,eax由0递增
:0040CFD3 0FBE9405ACFEFFFF        movsx edx, byte ptr [ebp+eax-00000154] <--依次取出name至edx
:0040CFDB 0FAF55AC                imul edx, dword ptr [ebp-54]           <--edx=edx*eax
:0040CFDF 0155B0                  add dword ptr [ebp-50], edx            <--把edx加入那个8位码上
:0040CFE2 FF45AC                  inc [ebp-54]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040CFCE(U)
|
:0040CFE5 8D8DACFEFFFF            lea ecx, dword ptr [ebp+FFFFFEAC]
:0040CFEB 51                      push ecx
:0040CFEC E817F20700              call 0048C208                <--这是得到name长度给eax
:0040CFF1 59                      pop ecx
:0040CFF2 3B45AC                  cmp eax, dword ptr [ebp-54]
:0040CFF5 7FD9                    jg 0040CFD0                  <--循环次数为name长度(包括空格)
:0040CFF7 6A0A                    push 0000000A
:0040CFF9 8D85ACFDFFFF            lea eax, dword ptr [ebp+FFFFFDAC]
:0040CFFF 50                      push eax
:0040D000 FF75B0                  push [ebp-50]
:0040D003 E888320800              call 00490290
:0040D008 83C40C                  add esp, 0000000C
......
下面还有N个CALL,主要是把那个数值再转成字符,还变成十进制等等.....


好了,从头整理一下,先得到NAME,如果小于8个字符就用空格来补,然后取第1,3,5,7个字符与"PUZZLEVERO"中第1,3,5,7个字符进行异或运算,将每次得到的值变为十进制后作为两位CODE,(如果是2位十进制数就把它翻转;如果是1位十进制数X就写成'1X';如果是3位XYZ就写成XY),这样最后的CODE就是8位。然后用这个数再依次加上NAME中每个字符(包括加上的空格)ASCII值乘以它所占的位置。(好复杂啊,我自己都说晕了~~~~~)

看我写的注册机吧:

Program Keygon;
var st,name:string;
   s :array[1..8] of longint;
   i,p:integer;
   code :longint;
begin
    st:='PUZZLEVERO';
    write('Please input your name:');
    readln(name);
    if length(name)<=8 then name:=name+'        '; {8 blanks}
    p:=1;
    repeat i:=(ord(st[p]) xor ord(name[p]));
           if i>=100 then i:=i div 10;
           if i<10 then begin s[p]:=1; s[p+1]:=i; end
           else begin
           s[p]:=i mod 10;
           s[p+1]:=i div 10;
           end;
           p:=p+2;
    until p>8;
    code:=s[1]*10000000+s[2]*1000000+s[3]*100000
          +s[4]*10000+s[5]*1000+s[6]*100+s[7]*10+s[8];
    for p:=1 to 16 do
    code:=code+ord(name[p])*(p-1);
    writeln('code:',code);
    writeln('Crack By RoBa');
end.

一个可用的注册码: Name: RoBa
                 Code: 12422565

这个算法复杂是够复杂了,但还有一个Bug,就是程序没有判断NAME为空,如果NAME为空用注册机可算出CODE为11211007,你把NAME什么也不填,CODE填入11211007看看,呵呵,注册成功了。虽然在About里面显示USER为未注册,但其他方面已经完全是注册版了,有趣~~~~:)

补记:ADC公司另一款游戏Hex Mines和该游戏算法完全相同只是字串改为"HEXMINESVERO"