互联网点歌台 V3.91 算法分析
日期:2005年4月2日 破解人:Baby2008
――――――――――――――――――――――――――――――――――――――――――― 【软件名称】:互联网点歌台 V3.91
【软件大小】:1.46M
【下载地址】:http://mpt.zj.com/
【软件简介】:随着宽带网的普及,网速越来越快,网上听音乐已不成问题。使用互联网点歌台,只用鼠标轻轻一“点歌”,就能让您听遍全世界的音乐,免去寻找下载歌曲的麻烦。
互联网点歌台是国内第一款高度集成歌曲搜索及播放功能的音乐软件。把自己想要听的歌曲输入,就能一下子听到你想要的歌曲,看你想要的歌词。支持几乎所有歌曲类型,如mp3、wma、rm等,可编程播放。音质媲美winamp。
第19届全国科技创新大赛(CASTIC Computer Science)获奖作品 中国・成都
【功能介绍】:
1. 搜索播放网络歌曲,支持mp3、wma等搜索,广泛的歌曲搜索量(“全部”类型歌曲更多,更强大),支持Windows Media Player格式、Real Player格式。
2. 最新版本完全采用了MPT-Link第二代技术,最大限度保证了搜索的成功率,支持同名歌曲的智能区分,点歌率>97%。
3. 支持点歌单系统,可编程搜索播放,并能保存点歌单。点歌单文件具有地址自动记忆功能,可直接双击启动互联网点歌台顺序播放。
4. 歌曲同步批量下载(断点续传)。
5. 自动搜索权威音乐排行榜TOP100,搜索播放歌手与专辑。
6. 同步显示歌词。
7. 能最小化至托盘播放,悬浮窗显示,方便快捷。
【软件限制】:
V3.9 未注册版与注册版的区别:
1.全部类型选择(更多的歌曲数量,更快的点歌速度).
2.享受歌手与专辑/网络广播电台/历史记录导入/候选地址导入功能.
3.未注册版在搜索歌词后歌词不能保存,且会在歌词底部增加注册信息.
4.未注册版不具有批量下载功能.
5.注册版可查看全部排行榜.
6.注册版可以享有所有售后服务.
【保护方式】:注册码保护
【破解声明】:初学Crack,只是感兴趣,失误之处敬请诸位大侠赐教!
【破解工具】:OllyDbg.V1.10聆风听雨汉化第二版、PeID 0.93
--------------------------------------------------------------------------------------------------------------
【破解过程】:
先用PEID 0.93汉化增强版查壳,Borland Delphi 6.0 - 7.0,无壳? OD 直接载入MusicPoint.exe,查找参考字符串"感谢你的注册..."双击后向上查找,来到这里:
005189B5 55 push ebp
005189B6 68 6B8A5100 push <MusicPoi.->System.@HandleFinally>
005189BB 64:FF30 push dword ptr fs:[eax]
005189BE 64:8920 mov dword ptr fs:[eax],esp
005189C1 8D55 FC lea edx,dword ptr ss:[ebp-4]
005189C4 > 8B83 F8020000 mov eax,dword ptr ds:[ebx+2F8] ; *Tfrmreg.Edit1:TLabeledEdit
005189CA > E8 AD49F4FF call MusicPoi.0045D37C ; ->Controls.TControl.GetText(TControl):TCaption;
005189CF 837D FC 00 cmp dword ptr ss:[ebp-4],0 ; 机器码不能为空
005189D3 74 11 je short MusicPoi.005189E6
005189D5 > 8B83 04030000 mov eax,dword ptr ds:[ebx+304] ; *Tfrmreg.btnget:TBitBtn
005189DB 66:BE EBFF mov si,0FFEB
005189DF > E8 0CB6EEFF call MusicPoi.00403FF0 ; ->System.@CallDynaInst 本破文关键所在!!!
005189E4 EB 0C jmp short MusicPoi.005189F2
005189E6 B8 808A5100 mov eax,MusicPoi.00518A80 ; 机器码为空提示"请生成机器码"
005189EB > E8 FCD0F1FF call MusicPoi.00435AEC ; ->Dialogs.ShowMessage(AnsiString);
005189F0 EB 5E jmp short MusicPoi.00518A50
005189F2 8D55 F8 lea edx,dword ptr ss:[ebp-8]
005189F5 > 8B83 FC020000 mov eax,dword ptr ds:[ebx+2FC] ; *Tfrmreg.Edit2:TLabeledEdit
005189FB > E8 7C49F4FF call MusicPoi.0045D37C ; ->Controls.TControl.GetText(TControl):TCaption;
00518A00 8B45 F8 mov eax,dword ptr ss:[ebp-8] ; 注册码
00518A03 8B15 3C415200 mov edx,dword ptr ds:[52413C] ; 计算后的机器码
00518A09 > E8 42C6EEFF call MusicPoi.00405050 ; ->System.@LStrCmp;
00518A0E 75 2C jnz short MusicPoi.00518A3C ; 明码比较,可以爆破或制作内存注册机 ^_^
00518A10 6A 40 push 40
00518A12 68 908A5100 push MusicPoi.00518A90
00518A17 68 A08A5100 push MusicPoi.00518AA0 ; 感谢你的注册...
00518A1C 8BC3 mov eax,ebx
00518A1E > E8 A5B2F4FF call MusicPoi.00463CC8 ; ->QForms.TCustomForm.GetClientHandle(TCustomForm):QWorkspaceH;
00518A23 50 push eax
00518A24 > E8 5BF1EEFF call <jmp.&user32.MessageBoxA> ; ->user32.MessageBoxA()
00518A29 8BC3 mov eax,ebx ; 提示注册成功。
00518A2B E8 AC000000 call MusicPoi.00518ADC ; 保存注册信息到注册表中
00518A30 A1 38415200 mov eax,dword ptr ds:[524138]
00518A35 > E8 1A13F6FF call MusicPoi.00479D54 ; ->Forms.TCustomForm.Close(TCustomForm);
00518A3A EB 14 jmp short MusicPoi.00518A50
00518A3C B8 D08A5100 mov eax,MusicPoi.00518AD0
00518A41 > E8 A6D0F1FF call MusicPoi.00435AEC ; ->Dialogs.ShowMessage(AnsiString);
00518A46 A1 38415200 mov eax,dword ptr ds:[524138]
00518A4B > E8 0413F6FF call MusicPoi.00479D54 ; ->Forms.TCustomForm.Close(TCustomForm);
00518A50 33C0 xor eax,eax
00518A52 5A pop edx
00518A53 59 pop ecx
00518A54 59 pop ecx
00518A55 64:8910 mov dword ptr fs:[eax],edx
00518A58 68 728A5100 push MusicPoi.00518A72
00518A5D 8D45 F8 lea eax,dword ptr ss:[ebp-8]
00518A60 BA 02000000 mov edx,2
00518A65 > E8 FEC1EEFF call MusicPoi.00404C68 ; ->System.@LStrArrayClr(void;void;Integer);
00518A6A C3 retn
00518A6B >^ E9 18BBEEFF jmp MusicPoi.00404588 ; ->System.@HandleFinally;
00518A70 ^ EB EB jmp short MusicPoi.00518A5D
00518A72 5E pop esi
00518A73 5B pop ebx
00518A74 59 pop ecx
00518A75 59 pop ecx
00518A76 5D pop ebp
00518A77 C3 retn
一看是明码比较,晕。本想注册一下能用就行了,但发现作者使用虚拟方法保护,兴趣来了…… 从互联网上查的有关System.@CallDynaInst 函数的一些资料,方便大家参考: 虚拟方法表格和动态方法表格
Delphi 中虚方法是通过类的虚拟方法表格(Virtul Method Table,简称 VMT)或者动态方法表格(Dynamic Method Table,简称 DMT)实现的。VMT 中存放着类及其基类声明的所有虚方法的指针[4]。每个类都具有一个唯一的 VMT,并且是在编译期间就确定的,所有由该类创建的对象都共享同一个 VMT,VMT 中除了一个虚方法表外,还包括其他有关一个类的信息[4]。
DMT 是 Delphi 中实现虚方法的另一种方式。利用 DMT 可以有效减小 VMT 的体积,但执行效率稍差。
DMT 中存有类所声明的动态方法和消息句柄,但并不包括从祖先类继承的方法。用关键字 dynamic 或者 message 声明的方法都会以动态方法实现。从 VMT + vmtDynamicTable 偏移处取出的 DWORD 值就是指向类的 DMT 指针。DMT 的结构在 Delphi 中是 Undocumented 的,但通过阅读 VCL 源码和 Debug 很容易写出 DMT 在内存中的逻辑布局:
type
TDynmethodTable = packed record
Count: Word;
Indexes: packed array[1..Count] of SmallInt;
Addresses: packed array[1..Count] of Pointer;
end;
假如我们声明了如下的类:
TMyObject = class
private
procedure DM1; dynamic;
procedure DM2; dynamic;
procedure WMCommand(var Message); message WM_COMMAND;
public
procedure DM3; dynamic;
end;
凡是带 dynamic 的关键字的方法都会被编译器按照在它们在类中声明的顺序赋予一个编号,或者说 ID,编号用一个 SmallInt 值表示,顺序为 $FFFF,$FFFE,$FFFD...当编译器遇到 message 时,方法的编号则用消息 ID 表示,例如 WM_COMMAND 代表 $0111,有了这些分析我们很容易把这个类的 DMT 按照上面的内存布局描述出来:
4 // Count =4,即包括4个动态方法
$FFFF // DM1 的编号
$FFFE // DM2 的编号
$0111 // WMCommand 的消息 ID
$FFFD // DM3 的编号
DM1 入口地址
DM2 入口地址
WMCommand 入口地址
DM3 入口地址
这些方法是如何被调用的呢?对于带 dynamic 关键字的方法是通过调用 System._CallDynaInst 实现的。通常这样:
mov eax,ebx ; eax = point to current object
mov si,$ffff ; si 为方法编号
call @CallDynaInst
而System._CallDynaInst 继而会调用 System.GetDynaMethod 获得对应方法编号的方法入口地址,并直接跳到方法入口地址,执行该方法:
procedure _CallDynaInst;
asm
PUSH EAX
PUSH ECX
MOV EAX,[EAX] ;注意调用时传递的是 VMT
CALL GetDynaMethod
POP ECX
POP EAX
JE @@Abstract
JMP ESI ; ESI 指向动态方法入口地址
@@Abstract:
POP ECX
JMP _AbstractError
end; System.GetDynaMethod 则在 DMT 中寻找该动态方法的编号,如果找到了,就从 DMT 中取得该方法的入口地址;如果找不到,则继续在父类的 DMT 中寻找该方法编号直至 TObject 的 DMT(TObject 中指向 DMT 的指针为 nil,循环到这里自然会停止):
procedure GetDynaMethod;
{ function GetDynaMethod(vmt: TClass; selector: Smallint) : Pointer; }
asm
{ -> EAX vmt of class }
{ SI dynamic method index }
{ <- ESI pointer to routine }
{ ZF = 0 if found }
{ trashes: EAX, ECX }
PUSH EDI
XCHG EAX,ESI
JMP @@haveVMT
@@outerLoop:
MOV ESI,[ESI]
@@haveVMT:
MOV EDI,[ESI].vmtDynamicTable
TEST EDI,EDI
JE @@parent
MOVZX ECX,word ptr [EDI]
PUSH ECX
ADD EDI,2
REPNE SCASW
JE @@found
POP ECX
@@parent:
MOV ESI,[ESI].vmtParent
TEST ESI,ESI
JNE @@outerLoop
JMP @@exit
@@found:
POP EAX
ADD EAX,EAX
SUB EAX,ECX { this will always clear the Z-flag ! }
MOV ESI,[EDI+EAX*2-4]
@@exit:
POP EDI
end; 大家再看看上面的注册验证代码中的几行代码:
--------------------------------------------------------------------------------------------------------------
005189D5 > 8B83 04030000 mov eax,dword ptr ds:[ebx+304] ; *Tfrmreg.btnget:TBitBtn
005189DB 66:BE EBFF mov si,0FFEB
005189DF > E8 0CB6EEFF call MusicPoi.00403FF0 ; ->System.@CallDynaInst
-------------------------------------------------------------------------------------------------------------- 跟进005189DF > E8 0CB6EEFF call MusicPoi.00403FF0
--------------------------------------------------------------------------------------------------------------
00403FF0 50 push eax
00403FF1 51 push ecx
00403FF2 8B00 mov eax,dword ptr ds:[eax]
00403FF4 E8 C7FFFFFF call MusicPoi.00403FC0 //这就是 GetDynaMethod 啦
00403FF9 59 pop ecx
00403FFA 58 pop eax
00403FFB 74 02 je short MusicPoi.00403FFF
00403FFD FFE6 jmp esi //看牢这里,它可是指向动态方法入口地址哦
00403FFF 59 pop ecx
00404000 ^ E9 53ECFFFF jmp MusicPoi.00402C58
00404005 C3 retn
--------------------------------------------------------------------------------------------------------------
和上面的理论一一对应了,继续…… 0043CDB0 53 push ebx ; 动态方法入口地址
0043CDB1 8BD8 mov ebx,eax
0043CDB3 8A83 21020000 mov al,byte ptr ds:[ebx+221]
0043CDB9 2C 03 sub al,3
0043CDBB 74 1F je short MusicPoi.0043CDDC
0043CDBD 2C 03 sub al,3
0043CDBF 75 52 jnz short MusicPoi.0043CE13
0043CDC1 8BC3 mov eax,ebx
0043CDC3 E8 80790300 call MusicPoi.00474748
0043CDC8 85C0 test eax,eax
0043CDCA 74 07 je short MusicPoi.0043CDD3
0043CDCC E8 83CF0300 call MusicPoi.00479D54
0043CDD1 EB 47 jmp short MusicPoi.0043CE1A
0043CDD3 8BC3 mov eax,ebx
0043CDD5 E8 5E710100 call MusicPoi.00453F38
0043CDDA 5B pop ebx
0043CDDB C3 retn
0043CDDC 8BC3 mov eax,ebx
0043CDDE EB 03 jmp short MusicPoi.0043CDE3
0043CDE0 8B40 30 mov eax,dword ptr ds:[eax+30]
0043CDE3 85C0 test eax,eax
0043CDE5 74 09 je short MusicPoi.0043CDF0
0043CDE7 83B8 58010000 0>cmp dword ptr ds:[eax+158],0
0043CDEE ^ 74 F0 je short MusicPoi.0043CDE0
0043CDF0 85C0 test eax,eax
0043CDF2 74 16 je short MusicPoi.0043CE0A
0043CDF4 8B15 0C2C5200 mov edx,dword ptr ds:[522C0C] ; MusicPoi.00523BF0
0043CDFA 8B12 mov edx,dword ptr ds:[edx]
0043CDFC 8B80 58010000 mov eax,dword ptr ds:[eax+158]
0043CE02 92 xchg eax,edx
0043CE03 E8 2C0C0400 call MusicPoi.0047DA34
0043CE08 EB 10 jmp short MusicPoi.0043CE1A
0043CE0A 8BC3 mov eax,ebx
0043CE0C E8 27710100 call MusicPoi.00453F38
0043CE11 5B pop ebx
0043CE12 C3 retn
0043CE13 8BC3 mov eax,ebx
0043CE15 E8 1E710100 call MusicPoi.00453F38 ; 注册算法,关键跟进
0043CE1A 5B pop ebx
0043CE1B C3 retn call MusicPoi.00453F38函数,注册码关键算法:
--------------------------------------------------------------------------------------------------------------
005188D0 > 55 push ebp ; <-Tfrmreg@btngetClick
005188D1 8BEC mov ebp,esp
005188D3 6A 00 push 0
005188D5 6A 00 push 0
005188D7 53 push ebx
005188D8 56 push esi
005188D9 57 push edi
005188DA 8BD8 mov ebx,eax
005188DC 33C0 xor eax,eax
005188DE 55 push ebp
005188DF 68 8B895100 push <MusicPoi.->System.@HandleFinally>
005188E4 64:FF30 push dword ptr fs:[eax]
005188E7 64:8920 mov dword ptr fs:[eax],esp
005188EA B8 3C415200 mov eax,MusicPoi.0052413C
005188EF > E8 50C3EEFF call MusicPoi.00404C44 ; ->System.@LStrClr(void;void);
005188F4 8D55 FC lea edx,dword ptr ss:[ebp-4]
005188F7 > 8B83 F8020000 mov eax,dword ptr ds:[ebx+2F8] ; *Tfrmreg.Edit1:TLabeledEdit
005188FD > E8 7A4AF4FF call MusicPoi.0045D37C ; ->Controls.TControl.GetText(TControl):TCaption;
00518902 8B45 FC mov eax,dword ptr ss:[ebp-4] ; 机器码 MachineCode
00518905 > E8 FAC5EEFF call MusicPoi.00404F04 ; ->System.@LStrLen(String):Integer;<+>
0051890A 8BF0 mov esi,eax ; 机器码长度 Length
0051890C 85F6 test esi,esi
0051890E 7E 4B jle short MusicPoi.0051895B ; 如果长度<=0 则Over!
00518910 BF 01000000 mov edi,1 ; i=1
00518915 8B45 FC mov eax,dword ptr ss:[ebp-4] ; 机器码
00518918 33DB xor ebx,ebx
0051891A 8A5C38 FF mov bl,byte ptr ds:[eax+edi-1] ; MachineCode[i]
0051891E 83FB 41 cmp ebx,41 ; 假如 MachineCode[i]<'A' 则MachineCode[i]+1E
00518921 7C 14 jl short MusicPoi.00518937
00518923 83FB 5A cmp ebx,5A ; 假如 MachineCode[i]>'Z' 则MachineCode[i]+1E
00518926 7F 0F jg short MusicPoi.00518937
00518928 83FB 49 cmp ebx,49 ; 假如 MachineCode[i]>='I' 则MachineCode[i]-5
0051892B 7D 05 jge short MusicPoi.00518932
0051892D 83C3 07 add ebx,7 ; 假如 MachineCode[i]<'I' 则MachineCode[i]+7
00518930 EB 08 jmp short MusicPoi.0051893A
00518932 83EB 05 sub ebx,5
00518935 EB 03 jmp short MusicPoi.0051893A
00518937 83C3 1E add ebx,1E
0051893A 8D45 F8 lea eax,dword ptr ss:[ebp-8]
0051893D 8BD3 mov edx,ebx
0051893F > E8 E8C4EEFF call MusicPoi.00404E2C ; ->System.@LStrFromChar(String;String;Char);<+>
00518944 8B55 F8 mov edx,dword ptr ss:[ebp-8]
00518947 B8 3C415200 mov eax,MusicPoi.0052413C
0051894C 8B0D 3C415200 mov ecx,dword ptr ds:[52413C]
00518952 > E8 F9C5EEFF call MusicPoi.00404F50 ; 字符串的连接->System.@LStrCat3;注意是倒序
00518957 47 inc edi
00518958 4E dec esi
00518959 ^ 75 BA jnz short MusicPoi.00518915 ; 循环
0051895B B8 3C415200 mov eax,MusicPoi.0052413C
00518960 8B0D 3C415200 mov ecx,dword ptr ds:[52413C] ; 连接后的字符串
00518966 BA A4895100 mov edx,MusicPoi.005189A4 ; 字符 '8'
0051896B > E8 E0C5EEFF call MusicPoi.00404F50 ; ->System.@LStrCat3;
00518970 33C0 xor eax,eax ; '8'+连接后的字符串,即为有效注册码
00518972 5A pop edx
00518973 59 pop ecx
00518974 59 pop ecx
00518975 64:8910 mov dword ptr fs:[eax],edx
00518978 68 92895100 push MusicPoi.00518992
0051897D 8D45 F8 lea eax,dword ptr ss:[ebp-8]
00518980 BA 02000000 mov edx,2
00518985 > E8 DEC2EEFF call MusicPoi.00404C68 ; ->System.@LStrArrayClr(void;void;Integer);
0051898A C3 retn
0051898B >^ E9 F8BBEEFF jmp MusicPoi.00404588 ; ->System.@HandleFinally;
00518990 ^ EB EB jmp short MusicPoi.0051897D
00518992 5F pop edi
00518993 5E pop esi
00518994 5B pop ebx
00518995 59 pop ecx
00518996 59 pop ecx
00518997 5D pop ebp
00518998 C3 retn
--------------------------------------------------------------------------------------------------------------
【算法总结】:
注册算法很简单,虚拟方法保护这方法不错,我也会了!
Delphi 7.0 注册机源代码如下:
Procedure TForm1.btn1Click(Sender: TObject);
Var
i: Integer;
SN, MachineCode: String;
Begin
MachineCode := edt1.Text;
SetLength(SN, Length(MachineCode));
For i := 1 To Length(MachineCode) Do
Begin
If (Ord(MachineCode[i]) < $41) Or (Ord(MachineCode[i]) > $5A) Then
SN[i] := Char(Ord(MachineCode[i]) + $1E)
Else If (Ord(MachineCode[i]) >= $49) Then
SN[i] := Char(Ord(MachineCode[i]) - 5)
Else
SN[i] := Char(Ord(MachineCode[i]) + 7);
End;
edt2.Text := '8' + ReverseString(SN);
End; 我的机器码='14 60-206' (不包括引号)
注册码=8TNPKNT>>>>>>RO
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!