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 ebp, esp
.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 eax, eax ★这里是典型的注册比较跳转●
.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 eax, eax
.text:004503F2 repne scasb
.text:004503F4 not ecx
.text:004503F6 sub edi, ecx
.text:004503F8 mov esi, edi
.text:004503FA mov eax, ecx
.text:004503FC mov edi, edx
.text:004503FE shr ecx, 2
.text:00450401 rep movsd
.text:00450403 mov ecx, eax
.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 esp, ebp
.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 ebp, esp
.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 eax, eax
.text:004504F4 repne scasb
.text:004504F6 not ecx
.text:004504F8 add ecx, 0FFFFFFFFh
.text:004504FB test ecx, ecx
.text:004504FD jnz short loc_450506 ;一定跳,除非你没有输入
.text:004504FF xor eax, eax
.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 eax, eax
.text:0045050E repne scasb
.text:00450510 not ecx
.text:00450512 add ecx, 0FFFFFFFFh
.text:00450515 test ecx, ecx
.text:00450517 jz short loc_45052D ;一定不跳
.text:00450519 mov edi, [ebp+var_14]
.text:0045051C or ecx, 0FFFFFFFFh
.text:0045051F xor eax, eax
.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 eax, eax
.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 eax, eax
.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 eax, eax
.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 eax, byte ptr [edx]
.text:00450583 mov ecx, [ebp+var_4]
.text:00450586 add ecx, eax
.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 eax, eax
.text:004505F0
.text:004505F0 loc_4505F0: CODE XREF: sub_4504C0+41j
.text:004505F0 sub_4504C0+6Fj ...
.text:004505F0 pop edi
.text:004505F1 mov esp, ebp
.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;