yingyue's CrackMe【BUG】版本的暴力破解的可能性探讨 + 穷举注册机的实现!
今天下载了yingyue兄在论坛上的一个CrackMe,后来被证实为BuG版本,开始还以为是yingyue兄故意这样写的哪!不得已用暴力穷举计算的方式,得到了正确的注册码!写出来的目的,就是让大家论论,其实那个BUG版本,我个人认为理论上还是可以被注册成功的,最后将放上菜鸟我写的一个暴力注册机!
先看算法,如下:
》》》》》》
00401598 |. E8 E7E90000 call <jmp.&msvcrt.strlen> ; \strlen
0040159D |. 83C4 10 add esp, 10
004015A0 |. 8945 A4 mov dword ptr [ebp-5C], eax
004015A3 |. 8B45 A4 mov eax, dword ptr [ebp-5C]
004015A6 |. 48 dec eax ; i := 注册名长度 - 1;
004015A7 |. 8945 AC mov dword ptr [ebp-54], eax ; 54 : i
004015AA |. 8DB6 00000000 lea esi, dword ptr [esi]
004015B0 |> 837D AC 00 /cmp dword ptr [ebp-54], 0
004015B4 |. 7D 02 |jge short 练习.004015B8
004015B6 |. EB 3C |jmp short 练习.004015F4
004015B8 |> 8D45 C0 |lea eax, dword ptr [ebp-40] ; 注册名 -> EAX
004015BB |. 8B55 AC |mov edx, dword ptr [ebp-54] ; i ->EDX
004015BE |. 0FBE0402 |movsx eax, byte ptr [edx+eax] ; ord(cz[i+1])
004015C2 |. 89C2 |mov edx, eax
004015C4 |. 2B55 AC |sub edx, dword ptr [ebp-54] ; ord(cz[i+1]) - i -》 EDX
004015C7 |. 8955 9C |mov dword ptr [ebp-64], edx ; -> key[length(注册名)]
004015CA |. 8B45 A4 |mov eax, dword ptr [ebp-5C] ; 注册名长度
004015CD |. 8B4D AC |mov ecx, dword ptr [ebp-54] ; i
004015D0 |. 89C2 |mov edx, eax
004015D2 |. 29CA |sub edx, ecx ; 注册名长度 - i
004015D4 |. 89D0 |mov eax, edx
004015D6 |. 0345 AC |add eax, dword ptr [ebp-54] ; 注册名长度 -> eax
004015D9 |. 8D55 B0 |lea edx, dword ptr [ebp-50] ; [0242FF10] -> edx
004015DC |. 8A4D 9C |mov cl, byte ptr [ebp-64] ; key[length(注册名)] -> cl
004015DF |. 880C10 |mov byte ptr [eax+edx], cl ; cl -> [0242FF10 + 注册名长度]
004015E2 |. 8D45 B0 |lea eax, dword ptr [ebp-50] ; [0242FF10] -> eax
004015E5 |. 8B55 AC |mov edx, dword ptr [ebp-54] ; i -> edx
004015E8 |. 0FBE0402 |movsx eax, byte ptr [edx+eax] ; [0242FF10 + i] -> eax
004015EC |. 0145 A0 |add dword ptr [ebp-60], eax ; 累加 temp1
004015EF |. FF4D AC |dec dword ptr [ebp-54] ; i - 1
004015F2 |.^ EB BC \jmp short 练习.004015B0
004015F4 |> 8B45 A0 mov eax, dword ptr [ebp-60] ; temp2A-> eax
004015F7 |. 89C2 mov edx, eax ; -> edx
004015F9 |. 8D0412 lea eax, dword ptr [edx+edx] ; 2 x (edx) -> eax
004015FC |. 8D90 D6860000 lea edx, dword ptr [eax+86D6] ; + $86D6 ->edx
00401602 |. 89D1 mov ecx, edx
00401604 |. 83F1 06 xor ecx, 6 ; xor $6
00401607 |. 894D A0 mov dword ptr [ebp-60], ecx ; temp2A
0040160A |. 0FBE45 B0 movsx eax, byte ptr [ebp-50] ; key[0] -> eax
0040160E |. 8D90 EF78FFFF lea edx, dword ptr [eax+FFFF78EF] ; + $FFFF78EF -> edx
00401614 |. 8B45 98 mov eax, dword ptr [ebp-68] ; 注册码->16进制->eax
00401617 |. 29D0 sub eax, edx ; 注册码 - (key[0] + $FFFF78EF)
00401619 |. 89C2 mov edx, eax
0040161B |. 83F2 06 xor edx, 6 ; xor 6
0040161E |. 8955 98 mov dword ptr [ebp-68], edx ; temp3
00401621 |. 0FBE55 B1 movsx edx, byte ptr [ebp-4F] ; key[1]
00401625 |. 8B4D 98 mov ecx, dword ptr [ebp-68]
00401628 |. 29D1 sub ecx, edx ; temp4 := temp3 - key[1]
0040162A |. 898D 7CFFFFFF mov dword ptr [ebp-84], ecx
00401630 |. 8B75 A0 mov esi, dword ptr [ebp-60] ; temp2A
00401633 |. 89F0 mov eax, esi ; -> eax
00401635 |. 99 cdq
00401636 |. F77D A4 idiv dword ptr [ebp-5C] ; temp2B:=temp2 div 注册名长度
00401639 |. 89C1 mov ecx, eax ; -> ecx
0040163B |. 8B85 7CFFFFFF mov eax, dword ptr [ebp-84] ; temp4
00401641 |. 01C8 add eax, ecx ; temp4 := temp4 + temp2B; -> eax
00401643 |. 8B55 98 mov edx, dword ptr [ebp-68] ; temp3
00401646 |. 29C2 sub edx, eax ; temp5(edx) := temp3(edx) - temp4(eax);
00401648 |. 8955 94 mov dword ptr [ebp-6C], edx ; -> edx temp5
0040164B |. 8B45 98 mov eax, dword ptr [ebp-68] ; temp3
0040164E |. 8B55 94 mov edx, dword ptr [ebp-6C] ; temp5
00401651 |. 29D0 sub eax, edx ; temp6 := temp3 - temp5;
00401653 |. 8D55 B0 lea edx, dword ptr [ebp-50] ; key[0]
00401656 |. 8B4D AC mov ecx, dword ptr [ebp-54] ; $FFFFFFFF
00401659 |. 0FBE1411 movsx edx, byte ptr [ecx+edx] ; $FFFFFFFF -> edx
0040165D |. 89C1 mov ecx, eax ; temp6 -> ecx
0040165F |. 29D1 sub ecx, edx ; temp6 := temp6 - $FFFFFFFF
00401661 |. 894D 90 mov dword ptr [ebp-70], ecx
00401664 |. 8B45 98 mov eax, dword ptr [ebp-68] ; temp3
00401667 |. 8B55 94 mov edx, dword ptr [ebp-6C] ; temp5
0040166A |. 89C1 mov ecx, eax
0040166C |. 29D1 sub ecx, edx ; ecx := temp3 - temp5
0040166E |. 898D 7CFFFFFF mov dword ptr [ebp-84], ecx ; temp4 = ecx
00401674 |. 8B75 A0 mov esi, dword ptr [ebp-60] ; temp2A
00401677 |. 89F0 mov eax, esi
00401679 |. 99 cdq
0040167A |. F77D A4 idiv dword ptr [ebp-5C] ; temp2A/注册名长度
0040167D |. 89C1 mov ecx, eax
0040167F |. 8B95 7CFFFFFF mov edx, dword ptr [ebp-84] ; temp4
00401685 |. 29CA sub edx, ecx ; temp7 := temp4 - temp2B;
00401687 |. 8955 8C mov dword ptr [ebp-74], edx
0040168A |. 8B45 A0 mov eax, dword ptr [ebp-60] ; ? -> eax
0040168D |. 8B4D 94 mov ecx, dword ptr [ebp-6C] ; temp5 -> ecx
00401690 |. 8D1401 lea edx, dword ptr [ecx+eax] ; +
00401693 |. 89D0 mov eax, edx
00401695 |. 2B45 98 sub eax, dword ptr [ebp-68] ; - temp3 -> EAX
00401698 |. 8B55 90 mov edx, dword ptr [ebp-70] ; temp6
0040169B |. 8B4D 8C mov ecx, dword ptr [ebp-74] ; temp7
0040169E |. 01CA add edx, ecx ; temp6 + temp7 ->EDX
004016A0 |. 39D0 cmp eax, edx
004016A2 75 1C jnz short 练习.004016C0
004016A4 |. 83C4 F8 add esp, -8
004016A7 |. 68 30134000 push 练习.00401330 ; \n 严重佩服,你真行
004016AC |. 68 28504100 push 练习.00415028 ; d`A
004016B1 |. E8 921B0000 call 练习.00403248
004016B6 |. 83C4 10 add esp, 10
004016B9 |. EB 1A jmp short 练习.004016D5
004016BB | 90 nop
004016BC | 8D7426 00 lea esi, dword ptr [esi]
004016C0 |> 83C4 F8 add esp, -8
004016C3 |. 68 70134000 push 练习.00401370 ; \n 恭喜你,你还要努力
004016C8 |. 68 28504100 push 练习.00415028 ; d`A
004016CD |. E8 761B0000 call 练习.00403248
004016D2 |. 83C4 10 add esp, 10
004016D5 |> 83C4 F8 add esp, -8
004016D8 |. 68 9E134000 push 练习.0040139E ; \n
004016DD |. 68 28504100 push 练习.00415028 ; d`A
004016E2 |. E8 611B0000 call 练习.00403248
004016E7 |. 83C4 10 add esp, 10
004016EA |. E8 6DE80000 call <jmp.&msvcrt.getchar> ; [getchar
004016EF |. 8D53 04 lea edx, dword ptr [ebx+4]
》》》》》》 通过上面的算法,我们知道了其计算的方式如下(我是以注册名8位数及以下为探讨):
var
temp1 : byte;
temp2A, temp2B, temp3, temp4, temp5, temp6,
temp7, tempT, czCode, i, SN1, SN2 : integer;
czName, str : string;
const
key: array[0 .. 7] of byte =
($AD,$AE,$C0,$77,$38,$07,$93,$7C);
begin
czName := EditName.Text;
czCode := strToint(EditCode.text);
temp1 := 0;
for i := (length(czName)-1) downto 0 do
begin
key[length(czName)] := ord(czName[i+1]) - i;
temp1 := temp1 + key[i];
end;
str := '$FFFFFF' + intTohex(temp1,2);
temp2A:= strToint(str);
temp2A := ((temp2A * 2) + $86D6) xor $6;
str := '$FFFFFF' + intTohex(key[0],2);
tempT := strToint(str); //key[0] = $FFFFFFAD
temp3 := tempT + $FFFF78EF;
temp3 := (czCode - temp3) xor 6; //与注册吗有关
str := '$FFFFFF' + intTohex(key[1],2);
tempT := strToint(str); //key[1] = $FFFFFFAE
temp4 := temp3 - tempT;
temp2B := temp2A div length(czName); //除以注册名长度
temp4 := temp4 + temp2B;
temp5 := temp3 - temp4;
temp6 := (temp3 - temp5) - $FFFFFFFF;
temp4 := temp3 - temp5;
temp7 := temp4 - temp2B;
SN1 := intTohex((temp2A + temp5) - temp3,8);
SN2 := intTohex((temp6 + temp7),8);
通过上面反汇编的代码我们知道:当 SN1 = SN2 时,即寄存器 EAX=EDX 时便注册成功了!
那么,我们就可以写一个用穷举的方式来得到本地计算机的正确注册码的KeyGen了!
思路如下:
将上面的总算法独立分成SN1和SN2来独立运算,SN1运算中带入注册名,SN2运算则带入从-99999开始的数字,依次增大,然后对SN1的运算结果值和SN2
的运算结果值比较,如若当SN1=SN2时,那么带入SN2中的那个数字就是正确的注册码了!
呵呵,最后放上我的暴力注册机的源代码:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls;
type
TForm1 = class(TForm)
EditName: TEdit;
EditCode: TEdit;
EditSN1: TEdit;
EditSN2: TEdit;
Button1: TButton;
Button2: TButton;
Button3: TButton;
Memo1: TMemo;
Button4: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
var
czI : integer;
{$R *.DFM}
//SN1
function SN1(Name: String): integer; //定义函数
var
temp1 : byte;
temp2A, temp2B, temp3, temp4, temp5, temp6,
temp7, tempT, czCode, i, SN1, SN2 : integer;
czName, str : string;
const
key: array[0 .. 7] of byte =
($AD,$AE,$C0,$77,$38,$07,$93,$7C);
begin
czName := Form1.EditName.Text;
czCode := czI; //?????
temp1 := 0;
for i := (length(czName)-1) downto 0 do
begin
key[length(czName)] := ord(czName[i+1]) - i;
temp1 := temp1 + key[i];
end;
str := '$FFFFFF' + intTohex(temp1,2);
temp2A:= strToint(str);
temp2A := ((temp2A * 2) + $86D6) xor $6;
str := '$FFFFFF' + intTohex(key[0],2);
tempT := strToint(str); //key[0] = $FFFFFFAD
temp3 := tempT + $FFFF78EF;
temp3 := (czCode - temp3) xor 6; //####与注册吗有关
str := '$FFFFFF' + intTohex(key[1],2);
tempT := strToint(str); //key[1] = $FFFFFFAE
temp4 := temp3 - tempT;
temp2B := temp2A div length(czName); //除以注册名长度
temp4 := temp4 + temp2B;
temp5 := temp3 - temp4;
temp6 := (temp3 - temp5) - $FFFFFFFF;
temp4 := temp3 - temp5;
temp7 := temp4 - temp2B;
//当 SN1 = SN2 便成功!
Result := (temp2A + temp5) - temp3;
//Result := (temp6 + temp7);
end;
//SN2
function SN2(Code: integer): integer; //定义函数
var
temp1 : byte;
temp2A, temp2B, temp3, temp4, temp5, temp6,
temp7, tempT, czCode, i, SN1, SN2 : integer;
czName, str : string;
const
key: array[0 .. 7] of byte =
($AD,$AE,$C0,$77,$38,$07,$93,$7C);
begin
czName := Form1.EditName.Text;
czCode := czI; //???
temp1 := 0;
for i := (length(czName)-1) downto 0 do
begin
key[length(czName)] := ord(czName[i+1]) - i;
temp1 := temp1 + key[i];
end;
str := '$FFFFFF' + intTohex(temp1,2);
temp2A:= strToint(str);
temp2A := ((temp2A * 2) + $86D6) xor $6;
str := '$FFFFFF' + intTohex(key[0],2);
tempT := strToint(str); //key[0] = $FFFFFFAD
temp3 := tempT + $FFFF78EF;
temp3 := (czCode - temp3) xor 6; //####与注册吗有关
str := '$FFFFFF' + intTohex(key[1],2);
tempT := strToint(str); //key[1] = $FFFFFFAE
temp4 := temp3 - tempT;
temp2B := temp2A div length(czName); //除以注册名长度
temp4 := temp4 + temp2B;
temp5 := temp3 - temp4;
temp6 := (temp3 - temp5) - $FFFFFFFF;
temp4 := temp3 - temp5;
temp7 := temp4 - temp2B;
//当 SN1 = SN2 便成功!
//Result := (temp2A + temp5) - temp3;
Result := (temp6 + temp7);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
temp1 : byte;
temp2A, temp2B, temp3, temp4, temp5, temp6,
temp7, tempT, czCode, i, SN1, SN2 : integer;
czName, str : string;
const
key: array[0 .. 7] of byte =
($AD,$AE,$C0,$77,$38,$07,$93,$7C);
begin
czName := EditName.Text;
czCode := strToint(EditCode.text);
temp1 := 0;
for i := (length(czName)-1) downto 0 do
begin
key[length(czName)] := ord(czName[i+1]) - i;
temp1 := temp1 + key[i];
end;
str := '$FFFFFF' + intTohex(temp1,2);
temp2A:= strToint(str);
//showmessage(intTohex(temp2A,8));
temp2A := ((temp2A * 2) + $86D6) xor $6;
//showmessage(intTohex(temp2A,8));
str := '$FFFFFF' + intTohex(key[0],2);
tempT := strToint(str); //key[0] = $FFFFFFAD
temp3 := tempT + $FFFF78EF;
//showmessage(intTohex(temp3,8));
temp3 := (czCode - temp3) xor 6; //与注册吗有关
//showmessage(intTohex(temp3,8));
str := '$FFFFFF' + intTohex(key[1],2);
tempT := strToint(str); //key[1] = $FFFFFFAE
temp4 := temp3 - tempT;
//showmessage(intTohex(temp4,8));
temp2B := temp2A div length(czName); //除以注册名长度
//showmessage(intTohex(temp2B,8));
temp4 := temp4 + temp2B;
temp5 := temp3 - temp4;
//showmessage(intTohex(temp5,8));
temp6 := (temp3 - temp5) - $FFFFFFFF;
temp4 := temp3 - temp5;
temp7 := temp4 - temp2B;
//当 SN1 = SN2 便成功!
EditSN1.Text := intTohex((temp2A + temp5) - temp3,8);
EditSN2.Text := intTohex((temp6 + temp7),8);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
czI := strToint(EditCode.Text);
EditSN1.Text := intTohex(SN1(EditName.Text),8);
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
czI := strToint(EditCode.Text);
EditSN2.Text := intTohex(SN2(czI),8);
end;
procedure TForm1.Button4Click(Sender: TObject);
var
pos : integer;
begin
for pos := -30000 to -10000 do
begin
czI := pos;
if SN2(czI) = SN1(EditName.Text)
then begin
showmessage('正确注册码:'+ intTostr(czI));
EditCode.Text := intTostr(czI);
exit;
end
else begin
memo1.Lines.Add(intTostr(czI));
if pos = -10000
then begin
showmessage('没有找到正确的注册码!'+#13+'扩大范围再试一试?');
exit;
end;
end;
end;
//timer.Enabled := True;
end;
end. 而且,通过测试,事实证明,这个KeyGen确实起到作用了! 最后,感谢 yingyue 兄的这个【BUG】版本的CM,让我有幸写出个穷举注册机了!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: