互联网点歌台 V3.91   算法分析

 日期:2005年4月2日   破解人:Baby2008
———————————————————————————————————————————


【软件名称】:互联网点歌台 V3.91
【软件大小】:1.46M
【下载地址】:http://mpt.zj.com/
【软件限制】:

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