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"