一篇破解教程-----面向初学者(转载请保持完整,且不得用于商业用途)
作者: Fpc
工具:Trw2000,W32dasm 8.93
软件名称:五子棋99 Ver 2.0
作 者: 华东计算技术研究所 蒋祥刚
文件大小:497KB
授权方式:共享软件
使用平台:Win95/98
软件简介:一个五子棋游戏软件,与常见的同类软件差不多,棋力不强
软件来源:《网页设计素材2000》(3CD)中的游戏目录
下载地址:暂时不可用,大家耐心等
见到需要输入注册名和注册码的软件就忍不住去按 ^D,可能是破解综合症吧 ~_~。在同事的机器上发现了一个五子棋的游戏,玩了几局,居然要注册。简直是*#*@&&,也不看看棋力有多差,都不忍心去破。想一想,权当是一个CrackMe,开始吧。
多说两句,大多数五子棋软件的棋力都不行,都是乱走棋,只要你看过一两本棋谱,对付它们不成问题。记得在学校时接触过一个“五子棋大师
二”,体积很大,但棋力非常强。其实优秀的棋类软件应该是算法和棋谱管理的结合体。象棋的“将族3”就很厉害,我连“小猪头”都对付不了,自己也是太笨:-)
用TRW2000(感谢LTT和ZNH,我决定注册,不知注册费是多少¥)载入FIVE,在“关于”中要求输入注册码。这是一个典型的“注册名”加“注册码”注册形式。在姓名中输入“Fpc”,注册码中输入“494949”(在下面的分析中你会看到,正好满足了6位注册码的条件)。
^N到TRW,下bpx hmemcpy(万能中断);^N返回,单击“注册”,立刻到TRW中,按一次^N(因为有两个输入的文本框),还会回到TRW中;
下BD *,按几次F12,你会返回到下面,见分析:
:0045E884 8B80E0020000 mov eax, dword
ptr [eax+000002E0]
:0045E88A E81DC6FCFF call
0042AEAC <====
这次调用取得 注册名
:0045E88F 8B45FC
mov eax, dword ptr [ebp-04]
:0045E892 50
push eax
* Possible StringData Ref from Code Obj ->"Name"
|
:0045E893 B92CE94500 mov ecx,
0045E92C
* Possible StringData Ref from Code Obj ->"Register"
|
:0045E898 BA3CE94500 mov edx,
0045E93C
:0045E89D A1649A4600 mov eax,
dword ptr [00469A64]
:0045E8A2 8B18
mov ebx, dword ptr [eax]
:0045E8A4 FF5304
call [ebx+04]
<==== 写入 ini 文件
:0045E8A7 8D55FC
lea edx, dword ptr [ebp-04]
:0045E8AA A15C9A4600 mov eax,
dword ptr [00469A5C]
:0045E8AF 8B80E8020000 mov eax, dword
ptr [eax+000002E8]
:0045E8B5 E8F2C5FCFF call
0042AEAC <====
取得输入的注册码
:0045E8BA 8B45FC
mov eax, dword ptr [ebp-04] 《=== 你会返回到这里
:0045E8BD 50
push eax
* Possible StringData Ref from Code Obj ->"RegNo"
|
:0045E8BE B950E94500 mov ecx,
0045E950
* Possible StringData Ref from Code Obj ->"Register"
|
:0045E8C3 BA3CE94500 mov edx,
0045E93C
:0045E8C8 A1649A4600 mov eax,
dword ptr [00469A64]
:0045E8CD 8B18
mov ebx, dword ptr [eax]
:0045E8CF FF5304
call [ebx+04]
<==== 写入 ini 文件
:0045E8D2 E855FDFFFF call
0045E62C <====
Call+Cmp+Jmp 的注册码判断模式非常常见
:0045E8D7 803D609A460000 cmp byte ptr [00469A60],
00 <==== [469A60]是个标志,如果不为0,则注册成功
:0045E8DE 750C
jne 0045E8EC
* Possible StringData Ref from Code Obj ->"名字和注册号不匹配!" <====
可惜在 TRW 和 W32dasm 的正文中都无法看到汉字 :-(
|
:0045E8E0 B860E94500 mov eax,
0045E960
:0045E8E5 E876CAFEFF call
0044B360 <====
调用MessageBoxA
:0045E8EA EB14
jmp 0045E900
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E8DE(C)
|
* Possible StringData Ref from Code Obj ->"恭喜你已经成功注册!"
|
:0045E8EC B88CE94500 mov eax,
0045E98C
:0045E8F1 E86ACAFEFF call
0044B360 <====
同样调用MessageBoxA
:0045E8F6 A15C9A4600 mov eax,
dword ptr [00469A5C]
:0045E8FB E85C51FEFF call
00443A5C
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E8EA(U)
|
:0045E900 33C0
xor eax, eax
:0045E902 5A
pop edx
:0045E903 59
pop ecx
:0045E904 59
pop ecx
:0045E905 648910
mov dword ptr fs:[eax], edx
:0045E908 681DE94500 push
0045E91D <====
这里我想不明白是如何产生的,这种调用(跳转)在W32dasm中不会给出提示
<==== 这个push 和之后的 ret 会越过下面的两个 jmp
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
<==== 跳到45E91D去执行,明白其中道理的请告诉我
|:0045E91B(U)
|
:0045E90D 8D45FC
lea eax, dword ptr [ebp-04]
:0045E910 E8AB4FFAFF call
004038C0
:0045E915 C3
ret
:0045E916 E9E149FAFF jmp 004032FC
:0045E91B EBF0
jmp 0045E90D
:0045E91D 5B
pop ebx
<====
:0045E91E 59
pop ecx
:0045E91F 5D
pop ebp
:0045E920 C3
ret
很明显,:0045E8D2 处的调用的 call 0045E62C 是判断注册码的关键。下bpx 45E8D2,^N返回到FIVE中,用原来的信息再注册。这次会直接中断到
45E8D2,按F8追进这个call:
* Referenced by a CALL at Addresses:
|:0045E8D2 , :0045E9DA
|
:0045E62C 55
push ebp
:0045E62D 8BEC
mov ebp, esp
:0045E62F 81C4CCFDFFFF add esp, FFFFFDCC
:0045E635 53
push ebx
:0045E636 56
push esi
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E5D0(C)
|
:0045E637 57
push edi
:0045E638 33C0
xor eax, eax
:0045E63A 8985CCFDFFFF mov dword
ptr [ebp+FFFFFDCC], eax
:0045E640 33C0
xor eax, eax
:0045E642 55
push ebp
:0045E643 68A0E74500 push
0045E7A0
:0045E648 64FF30
push dword ptr fs:[eax]
:0045E64B 648920
mov dword ptr fs:[eax], esp
:0045E64E 6A00
push 00000000
:0045E650 8D85CCFDFFFF lea eax, dword
ptr [ebp+FFFFFDCC]
:0045E656 50
push eax
* Possible StringData Ref from Code Obj ->"Name"
|
:0045E657 B9B8E74500 mov ecx,
0045E7B8
* Possible StringData Ref from Code Obj ->"register"
|
:0045E65C BAC8E74500 mov edx,
0045E7C8
:0045E661 A1649A4600 mov eax,
dword ptr [00469A64]
:0045E666 8B18
mov ebx, dword ptr [eax]
:0045E668 FF13
call dword ptr [ebx] <====
从ini文件中取得注册名
:0045E66A 8B95CCFDFFFF mov edx, dword
ptr [ebp+FFFFFDCC] \
:0045E670 8D85D0FDFFFF lea eax, dword
ptr [ebp+FFFFFDD0] |
:0045E676 B9FF000000 mov ecx,
000000FF |
:0045E67B E89854FAFF call
00403B18 |
:0045E680 8D95D0FDFFFF lea edx, dword
ptr [ebp+FFFFFDD0] |
:0045E686 8D459B
lea eax, dword ptr [ebp-65] \ 将注册名信息存放到
ebp-64到ebp处,ebp-65是注册名长度
/
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|
|:0045E622(C)
|
|
|
:0045E689 B164
mov cl, 64
|
:0045E68B E8F041FAFF call
00402880
/
:0045E690 6A00
push 00000000
:0045E692 8D85CCFDFFFF lea eax, dword
ptr [ebp+FFFFFDCC]
:0045E698 50
push eax
* Possible StringData Ref from Code Obj ->"RegNo"
|
:0045E699 B9DCE74500 mov ecx,
0045E7DC
* Possible StringData Ref from Code Obj ->"register"
|
:0045E69E BAC8E74500 mov edx,
0045E7C8
:0045E6A3 A1649A4600 mov eax,
dword ptr [00469A64]
:0045E6A8 8B18
mov ebx, dword ptr [eax]
:0045E6AA FF13
call dword ptr [ebx] <====
从ini文件中取得注册码
:0045E6AC 8B95CCFDFFFF mov edx, dword
ptr [ebp+FFFFFDCC] \
:0045E6B2 8D85D0FDFFFF lea eax, dword
ptr [ebp+FFFFFDD0] |
:0045E6B8 B9FF000000 mov ecx,
000000FF |
:0045E6BD E85654FAFF call
00403B18 \
将注册名信息存放到 ebp-c9到ebp-66处,ebp-ca是注册名长度
:0045E6C2 8D95D0FDFFFF lea edx, dword
ptr [ebp+FFFFFDD0] /
:0045E6C8 8D8536FFFFFF lea eax, dword
ptr [ebp+FFFFFF36] |
:0045E6CE B164
mov cl, 64
|
:0045E6D0 E8AB41FAFF call
00402880
/
:0045E6D5 8A459B
mov al, byte ptr [ebp-65]
:0045E6D8 84C0
test al, al
<==== 检查注册名长度是否为0
:0045E6DA 7409
je 0045E6E5
:0045E6DC 80BD36FFFFFF06 cmp byte ptr [ebp+FFFFFF36],
06 <==== 检查注册码长度是否为6
:0045E6E3 740C
je 0045E6F1
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E6DA(C)
|
:0045E6E5 C605609A460000 mov byte ptr [00469A60],
00 <==== 错误则置标志为0
:0045E6EC E996000000 jmp 0045E787
<====
跳到下面,返回
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E6E3(C)
|
:0045E6F1 8BF0
mov esi, eax
:0045E6F3 81E6FF000000 and esi, 000000FF
:0045E6F9 85F6
test esi, esi
:0045E6FB 7C21
jl 0045E71E
:0045E6FD 46
inc esi
<==== esi为注册名长度加1
:0045E6FE 8D4D9B
lea ecx, dword ptr [ebp-65] <====
ecx指向 注册名长度字节+注册名
:0045E701 8D9DD1FEFFFF lea ebx, dword
ptr [ebp+FFFFFED1] <==== ebx指向欲生成的注册码地址
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
<==== 下面这个循环生成注册码,设为 注册码A
|:0045E71C(C)
|
:0045E707 33C0
xor eax, eax
:0045E709 8A01
mov al, byte ptr [ecx]
:0045E70B BF0A000000 mov edi,
0000000A <====
除数为a,即为 10(十进制)
:0045E710 33D2
xor edx, edx
:0045E712 F7F7
div edi
<==== 相除后,edx中为余数
:0045E714 80C230
add dl, 30
<==== 变为 ASCII 码
:0045E717 8813
mov byte ptr [ebx], dl <====
按先后顺序存放于 [ebx]
:0045E719 43
inc ebx
:0045E71A 41
inc ecx
:0045E71B 4E
dec esi
:0045E71C 75E9
jne 0045E707
<==== 未完则继续
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E6FB(C)
|
:0045E71E 8A459B
mov al, byte ptr [ebp-65]
:0045E721 3C06
cmp al, 06
<==== 注册名长度超过6字节则不需要再处理
:0045E723 732F
jnb 0045E754
:0045E725 33C9
xor ecx, ecx
:0045E727 8AC8
mov cl, al
:0045E729 41
inc ecx
:0045E72A 83F906
cmp ecx, 00000006 <====
多此一举!!
:0045E72D 7F25
jg 0045E754
:0045E72F 8D9C0DD0FEFFFF lea ebx, dword
ptr [ebp+ecx-00000130] <==== 从 注册码A 的最后一个数开始补足7位
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
<==== 下面这个循环将生成的注册码A 补足为7位
|:0045E752(C)
|
:0045E736 33C0
xor eax, eax
:0045E738 8A03
mov al, byte ptr [ebx] <====
取得[ebx]处的数字
:0045E73A 40
inc eax
<==== 加一
:0045E73B 83E830
sub eax, 00000030
:0045E73E BE0A000000 mov esi,
0000000A
:0045E743 33D2
xor edx, edx
:0045E745 F7F6
div esi
<==== 相除,取模到edx
:0045E747 80C230
add dl, 30
<==== 变为 ASCII 码
:0045E74A 885301
mov byte ptr [ebx+01], dl <==== 存放到[ebx+1]处
:0045E74D 41
inc ecx
:0045E74E 43
inc ebx
:0045E74F 83F907
cmp ecx, 00000007 <====
不足7位则继续
:0045E752 75E2
jne 0045E736
可以分析出,注册码支持所有可打印字符和汉字;将 注册码A 补足为7位的规律是:将最后一位数加一作为下一位数,如果9加一则变为0,依次处理。例如:
注册码A=285,则补足为 2856789; 注册码A=3619,则补足为 3619012。
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0045E723(C), :0045E72D(C)
|
:0045E754 C685D8FEFFFF00 mov byte ptr [ebp+FFFFFED8],
00 <==== 很粗暴的在第八个字节处置0,之后的数字被舍弃
:0045E75B C605609A460001 mov byte ptr [00469A60],
01 <==== 预置注册标志为1(成功)
:0045E762 B906000000 mov ecx,
00000006 <====
比较的字节数
:0045E767 8D85D2FEFFFF lea eax, dword
ptr [ebp+FFFFFED2] <==== 算出的注册码,注意第一位被舍弃
:0045E76D 8D9537FFFFFF lea edx, dword
ptr [ebp+FFFFFF37] <==== 你输入的注册码
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E785(C)
|
:0045E773 8A18
mov bl, byte ptr [eax]
:0045E775 3A1A
cmp bl, byte ptr [edx]
:0045E777 7409
je 0045E782
:0045E779 C605609A460000 mov byte ptr [00469A60],
00 <==== 比较失败则置标志为0
:0045E780 EB05
jmp 0045E787
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E777(C)
|
:0045E782 42
inc edx
:0045E783 40
inc eax
:0045E784 49
dec ecx
:0045E785 75EC
jne 0045E773
<==== 不到6位则继续
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0045E6EC(U), :0045E780(U)
|
:0045E787 33C0
xor eax, eax
:0045E789 5A
pop edx
:0045E78A 59
pop ecx
:0045E78B 59
pop ecx
:0045E78C 648910
mov dword ptr fs:[eax], edx
:0045E78F 68A7E74500 push
0045E7A7
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0045E7A5(U)
|
:0045E794 8D85CCFDFFFF lea eax, dword
ptr [ebp+FFFFFDCC]
:0045E79A E82151FAFF call
004038C0
:0045E79F C3
ret
:0045E7A0 E9574BFAFF jmp 004032FC
:0045E7A5 EBED
jmp 0045E794
:0045E7A7 5F
pop edi
:0045E7A8 5E
pop esi
:0045E7A9 5B
pop ebx
:0045E7AA 8BE5
mov esp, ebp
:0045E7AC 5D
pop ebp
:0045E7AD C3
ret
<==== 返回
上面的判断过程用C表示:
int Success;
Registration()
{
int LenName, LenCode;
char UserName[100], RegCode[100], InputRegCode[6];
int i;
GetInfo( &UserName, LenName);
/* 取得注册信息 */
GetInfo( &InputRegCode, LenCode); /*
*/
if ( LenName==0 || LenCode!=6 )
{
Success=0;
return;
}
RegCode[0]= LenName%10;
for( i=0; i<LenName; i++)
RegCode[i+1]= UserName[i]%10;
if ( LenName<6 )
for( i=0; i<6-LenName; i++)
RegCode[i+LenName+1]=
(RegCode[i+lenName]+1)%10;
RegCode[7]='\0';
Success=1;
if (!strcmp( &RegCode, &InputRegCode))
Success=0;
return;
}
这基本上就是注册机,但没有必要。FIVE的注册形式比较简单,也未采取保护措施,容易分析,只要当作一个CrackMe就好。
破解完成加上这篇教程,一看时钟,ONLY 9:30,还作点什么的呢?这时有人满嘴酒气的冲进来,莫名其妙的问我是不是黑客,真是“好事不出门,坏事***”:大众根本就不懂HAKKER,CRAKKER是什么含义,也不能解释,真是扫兴。这时抬头看墙上的时钟,CALL
11:30!明天早晨又起不来了。“我单知道,TRW和SI会停系统时钟的,可怜我家的机器阿毛.......”(台下口号声此起彼伏:打倒唐僧),只有谢幕了。
@20001/03/21
- 标 题:一篇破解教程-----面向初学者 (15千字)
- 作 者:Fpc
- 时 间:2001-4-1 11:44:22
- 链 接:http://bbs.pediy.com