• 标 题:AC3D3.5 For Windows (13千字)
  • 作 者:lq7972
  • 时 间:2003-09-29 16:41:27
  • 链 接:http://bbs.pediy.com

SoftWare:AC3D3.5 For Windows
http://www.ac3d.org
这是个跨平台的3D模型制作软件,提供了Linux,Windows 95/NT,和SGI等操作平台;未注册不能保存
Tools:IDA v4.5、TRW2000
Cracker:lq7972[bruceyu13@sina.com]
Notes:学习。需要静态反汇编和动态跟踪配合

这个软件反跟踪的能力很强,用TRW直接载入,倒能正常运行,一般的Win API断点根本不起作用,RegQueryValue能够拦住,在Kernel和user领空转了几下就退出了,但可以看出是关闭了注册表RegCloseKey。
OllyDbg根本不能直接载入,否则就死了;附加也在DebugBreak(SEH)终止,退出,死了。
SofeICE我没试。RegMon和RegSnap以及Regedit.exe在软件运行时处于打开的话会给关闭;FileMon能用
不罗唆了,我关心的是怎样弄到它的注册算法;下面开始~

首先讲一下,软件注册码的输入格式:“xx...123456701...”
xx... 1234567 01...←输入的(数字部分)
  ↑      ↑
输入的用户名(字符部分)     任意数字(7位),其中第一位须为1
用IDA反汇编:
搜索字符串:“registration key invalid”
.text:004503C0  S U B R O U T I N E
.text:004503C0 
.text:004503C0  Attributes: bp-based frame
.text:004503C0 
.text:004503C0  void __cdecl _rk67dll(char *)
.text:004503C0                 public ?_rk67dll@@YAXPAD@Z
.text:004503C0 ?_rk67dll@@YAXPAD@Z proc near
.text:004503C0 
.text:004503C0 var_4           = dword ptr -4
.text:004503C0 arg_0           = dword ptr  8
.text:004503C0 
.text:004503C0                 push    ebp
.text:004503C1                 mov     ebpesp
.text:004503C3                 push    ecx
.text:004503C4                 push    esi
.text:004503C5                 push    edi
.text:004503C6                 mov     eax, [ebp+arg_0] ;我们的输入
.text:004503C9                 push    eax
.text:004503CA                 call    sub_4504C0 ☆------〇
.text:004503CF                 add     esp, 4 
.text:004503D2                 test    eaxeax ★这里是典型的注册比较跳转●
.text:004503D4                 jz      short loc_450418 ☆------〇
.text:004503D6                 mov     ecx, [ebp+arg_0]
.text:004503D9                 push    ecx
.text:004503DA                 call    sub_450430
.text:004503DF                 add     esp, 4
.text:004503E2                 mov     [ebp+var_4], eax
.text:004503E5                 mov     edi, [ebp-4]
.text:004503E8                 mov     edx, offset aNobody  "nobody"
.text:004503ED                 or      ecx, 0FFFFFFFFh
.text:004503F0                 xor     eaxeax
.text:004503F2                 repne scasb
.text:004503F4                 not     ecx
.text:004503F6                 sub     ediecx
.text:004503F8                 mov     esiedi
.text:004503FA                 mov     eaxecx
.text:004503FC                 mov     ediedx
.text:004503FE                 shr     ecx, 2
.text:00450401                 rep movsd
.text:00450403                 mov     ecxeax
.text:00450405                 and     ecx, 3
.text:00450408                 rep movsb
.text:0045040A                 mov     ecx, [ebp+8]
.text:0045040D                 push    ecx
.text:0045040E                 call    sub_450740 ;注册成功,Thanks
.text:00450413                 add     esp, 4
.text:00450416                 jmp     short loc_450425
.text:00450418  =====================================
.text:00450418 
.text:00450418 loc_450418:                              CODE XREF: _rk67dll(char *)+14j
.text:00450418                 push    offset aRegistrationKe  "Registration key invalid."
.text:0045041D                 call    windows_message_dialog
.text:00450422                 add     esp, 4
.text:00450425 
.text:00450425 loc_450425:                              CODE XREF: _rk67dll(char *)+56j
.text:00450425                 pop     edi
.text:00450426                 pop     esi
.text:00450427                 mov     espebp
.text:00450429                 pop     ebp
.text:0045042A                 retn

;跟进004503CA CALL sub_4504C0,这就是注册算法了--有点让我出乎意料 ^_^
.text:004504BC                 align 8
.text:004504C0 
.text:004504C0   S U B R O U T I N E
.text:004504C0 
.text:004504C0  Attributes: bp-based frame
.text:004504C0 
.text:004504C0 sub_4504C0      proc near                CODE XREF: _rk67dll(char *)+Ap
.text:004504C0                                          sub_450D30+Ap ...
.text:004504C0 
.text:004504C0 var_24          = dword ptr -24h
.text:004504C0 var_20          = dword ptr -20h
.text:004504C0 var_1C          = dword ptr -1Ch
.text:004504C0 var_18          = dword ptr -18h
.text:004504C0 var_14          = dword ptr -14h
.text:004504C0 var_10          = dword ptr -10h
.text:004504C0 var_C           = dword ptr -0Ch
.text:004504C0 var_8           = dword ptr -8
.text:004504C0 var_4           = dword ptr -4
.text:004504C0 arg_0           = dword ptr  8
.text:004504C0 
.text:004504C0                 push    ebp
.text:004504C1                 mov     ebpesp
.text:004504C3                 sub     esp, 24h
.text:004504C6                 push    edi
.text:004504C7                 mov     eax, [ebp+arg_0]
.text:004504CA                 push    eax
.text:004504CB                 call    sub_450430 ;把输入的东西复制到某区
.text:004504D0                 add     esp, 4
.text:004504D3                 mov     [ebp+var_20], eax
.text:004504D6                 mov     ecx, [ebp+arg_0]
.text:004504D9                 push    ecx
.text:004504DA                 call    sub_450600
.text:004504DF                 add     esp, 4
.text:004504E2                 mov     [ebp+var_14], eax
.text:004504E5                 mov     [ebp+var_4], 0
.text:004504EC                 mov     edi, [ebp+var_20] ;字符部分并得到它的长度
.text:004504EF                 or      ecx, 0FFFFFFFFh
.text:004504F2                 xor     eaxeax
.text:004504F4                 repne scasb
.text:004504F6                 not     ecx
.text:004504F8                 add     ecx, 0FFFFFFFFh
.text:004504FB                 test    ecxecx
.text:004504FD                 jnz     short loc_450506 ;一定跳,除非你没有输入
.text:004504FF                 xor     eaxeax
.text:00450501                 jmp     loc_4505F0
.text:00450506  ====================================
.text:00450506 
.text:00450506 loc_450506:                              CODE XREF: sub_4504C0+3Dj
.text:00450506                 mov     edi, [ebp+var_14] ;数字部分并得到它的长度
.text:00450509                 or      ecx, 0FFFFFFFFh
.text:0045050C                 xor     eaxeax
.text:0045050E                 repne scasb
.text:00450510                 not     ecx
.text:00450512                 add     ecx, 0FFFFFFFFh
.text:00450515                 test    ecxecx
.text:00450517                 jz      short loc_45052D ;一定不跳
.text:00450519                 mov     edi, [ebp+var_14]
.text:0045051C                 or      ecx, 0FFFFFFFFh
.text:0045051F                 xor     eaxeax
.text:00450521                 repne scasb
.text:00450523                 not     ecx
.text:00450525                 add     ecx, 0FFFFFFFFh
.text:00450528                 cmp     ecx, 8                   数字部分的长度一定大于8
.text:0045052B                 jnb     short loc_450534 ;显然,一定要跳
.text:0045052D 
.text:0045052D loc_45052D:                              CODE XREF: sub_4504C0+57j
.text:0045052D                 xor     eaxeax
.text:0045052F                 jmp     loc_4505F0
.text:00450534  ====================================
.text:00450534 
.text:00450534 loc_450534:                              CODE XREF: sub_4504C0+6Bj
.text:00450534                 lea     edx, [ebp+var_8]
.text:00450537                 push    edx
.text:00450538                 mov     eax, [ebp+arg_0]
.text:0045053B                 push    eax
.text:0045053C                 call    sub_450660 ;这个我没有跟
.text:00450541                 add     esp, 8
.text:00450544                 mov     [ebp+var_18], eax
.text:00450547                 cmp     [ebp+var_18], 0
.text:0045054B                 jnz     short loc_450554 ;要跳
.text:0045054D                 xor     eaxeax
.text:0045054F                 jmp     loc_4505F0
.text:00450554  =====================================
.text:00450554 
.text:00450554 loc_450554:                              CODE XREF: sub_4504C0+8Bj
.text:00450554                 mov     [ebp+var_24], 0
.text:0045055B                 jmp     short loc_450566
.text:0045055D ===================这里是一个循环,关于字符部分的运算
.text:0045055D 
.text:0045055D loc_45055D:                              CODE XREF: sub_4504C0+CBj
.text:0045055D                 mov     ecx, [ebp+var_24]
.text:00450560                 add     ecx, 1
.text:00450563                 mov     [ebp+var_24], ecx
.text:00450566 
.text:00450566 loc_450566:                              CODE XREF: sub_4504C0+9Bj
.text:00450566                 mov     edi, [ebp+var_20]
.text:00450569                 or      ecx, 0FFFFFFFFh
.text:0045056C                 xor     eaxeax
.text:0045056E                 repne scasb
.text:00450570                 not     ecx
.text:00450572                 add     ecx, 0FFFFFFFFh
.text:00450575                 cmp     [ebp+var_24], ecx
.text:00450578                 jnb     short loc_45058D ;循环完否?
.text:0045057A                 mov     edx, [ebp+var_20]
.text:0045057D                 add     edx, [ebp+var_24]
.text:00450580                 movsx   eaxbyte ptr [edx]
.text:00450583                 mov     ecx, [ebp+var_4]
.text:00450586                 add     ecxeax
.text:00450588                 mov     [ebp+var_4], ecx
.text:0045058B                 jmp     short loc_45055D
.text:0045058D  ================其实就是把字符部分的每个字符ASCII码累加
.text:0045058D 
.text:0045058D loc_45058D:                              CODE XREF: sub_4504C0+B8j
.text:0045058D                 mov     edx, [ebp+var_14] ;数字部分
.text:00450590                 add     edx, 7 ;从第8位起
.text:00450593                 mov     [ebp+var_1C], edx
.text:00450596                 mov     eax, [ebp+var_1C]
.text:00450599                 push    eax              char *
.text:0045059A                 call    _atoi      把字符串转整数,不需要跟
.text:0045059F                 add     esp, 4
.text:004505A2                 mov     [ebp+var_10], eax ;结果
.text:004505A5                 mov     eax, [ebp+var_10]
.text:004505A8                 cdq
.text:004505A9                 idiv    [ebp+var_8] ;除数就是7
.text:004505AC                 mov     [ebp+var_C], eax ;相除的商
.text:004505AF                 mov     ecx, [ebp+var_C] ;字符部分累加的值
.text:004505B2                 cmp     ecx, [ebp+var_4] ;比较
.text:004505B5                 jnz     short loc_4505D8 ;当然,不要跳
.text:004505B7                 cmp     dword_5AD09C, 0
.text:004505BE                 jz      short loc_4505D1
.text:004505C0                 mov     edx, [ebp+arg_0]
.text:004505C3                 push    edx
.text:004505C4                 push    offset aKeySOk   "key %s OK\n"
.text:004505C9                 call    _printf ;按格式(见前)输出
.text:004505CE                 add     esp, 8
.text:004505D1 
.text:004505D1 loc_4505D1:                              CODE XREF: sub_4504C0+FEj
.text:004505D1                 mov     eax, 1 ;注册Flag
.text:004505D6                 jmp     short loc_4505F0
.text:004505D8  ====================================
.text:004505D8 
.text:004505D8 loc_4505D8:                              CODE XREF: sub_4504C0+F5j
.text:004505D8                 cmp     dword_5AD09C, 0
.text:004505DF                 jz      short loc_4505EE
.text:004505E1                 push    offset aKeySInvalidKey  "key %s invalid\n, key"
.text:004505E6                 call    _printf
.text:004505EB                 add     esp, 4
.text:004505EE 
.text:004505EE loc_4505EE:                              CODE XREF: sub_4504C0+11Fj
.text:004505EE                 xor     eaxeax
.text:004505F0 
.text:004505F0 loc_4505F0:                              CODE XREF: sub_4504C0+41j
.text:004505F0                                          sub_4504C0+6Fj ...
.text:004505F0                 pop     edi
.text:004505F1                 mov     espebp
.text:004505F3                 pop     ebp
.text:004505F4                 retn
.text:004505F4 sub_4504C0      endp
.text:004505F4 

IDA反汇编出来的东西,几乎不需要写注释;真是强大的工具--Powerful!

【总结】
由上面的分析可以看出,软件是把用户输入的注册码(按前述格式)的字符部分累加后的值,与数字部分(从第8位开始)除以7后的值相比较,同则注册成功。
如果注册成功,就在注册表的\HKEY_CURRENT_USER\Software下新建主键\AC\AC3D\data并写入注册信息(二进制值)

【注册机】
//<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//    AC3D v3.5 KeyGen
//    The KeyGen by lq7972,with Delphi 6
//    E-Mail:bruceyu13@sina.com

procedure TForm1.Button1Click(Sender: TObject);
var
  i:Integer;
  Temp:integer;
  RanSum:string;
  Name:string;
  Code:string;
begin
  Name:=Edit1.Text;
  Temp:=0;
  if Name='' then
  begin
    ShowMessage('请输入!');
    Edit1.SetFocus;
  end;

   for i:=1 to Length(Name) do
     Temp:=Temp+Ord(Name[i]);

   for i:=1 to 6 do
   begin
     Randomize;
     RanSum:=RanSum+IntToStr(Random(i));
   end;

   Code:=Name+'1'+RanSum+IntToStr(Temp*7);

   Edit2.Text:=Code;
end;