For
Delphi,让你的注册机变小一些
请抛弃VCL,如果你嫌自己用Delphi编译出来的注册机个头儿过大的话...
事实上这种事情确实在发生,就在今天,就在刚才,在偶还没吃饭的时候就看到一个网友在这样抱怨...
的确如此,KeyGen的个头儿与它的界面一样让人失望,简单的form再来上两个Edit,三个Button。普普通通的界面有着360K这样的大体积,而上过壳后那150K的体积同样不能让人满意...
会造成这样的结果,与VCL是脱不了关系的,事实上你用Delphi编译一个什么也不做的程序也会有351K,否则你以为为何用Delphi写程序是一件相当轻松的事情?
不过这样沉重的代价付到我们可爱的KeyGen上就有点儿太过残忍了,很难想像某天要给一个大小200K左右的软件写注册机时会是怎样一个情景...
来吧,让我们想点儿办法,目前有一些控件可以很好的完成这个工作,用了它们后你编译出来的可执行文件的个头儿要比平常小很多,不过我并不打算介绍它们
这并不是什么太高深的东西,相信也不会很难
扔掉了VCL,我们能使用的东西就只有Pascal语言与API函数了,但实际上这就已经足够了:)
这之前先要找个软件做例子,大大大大大大大前天看到一个杂志在海吹敏思硬盘卫士,就Down了一个下来,后来才发现它对我没任何用处。呵呵,好老的一个软件了,一年多没更新了吧,估计注册机漫天飞的都有
先用TRW2000来摆平它,启动软件,我的机器码是yjeyj239416,随便输入一个注册码,然后用hmemcpy做断点......
在004a4165处的时候,我们会发现程序会把正确的注册码装入edx中,比如这我边儿的是3887361024,不过我们对它不感兴趣,在它之上的的004a4160处,才是我们真正感兴趣的地方,正确的注册码就是这个CALL来计算的。
按F8跟进去,来到004a3a08处:
0167:004a3a08
push ebp
0167:004a3a09 mov ebp,esp
0167:004a3a0b
push byte +00
0167:004a3a0d push byte
+00
0167:004a3a0f push byte +00
0167:004a3a11 push
ebx
0167:004a3a12 push esi
0167:004a3a13
mov esi,ecx
0167:004a3a15 mov
ebx,edx
0167:004a3a17 mov [ebp-04],eax
0167:004a3a1a
mov eax,[ebp-04]
0167:004a3a1d call
00404050
0167:004a3a22 xor eax,eax
0167:004a3a24
push ebp
0167:004a3a25 push dword 004a3a6e
0167:004a3a2a
push dword [fs:eax]
0167:004a3a2d
mov [fs:eax],esp
<--挂SEH,不关咱的事儿
0167:004a3a30 lea
edx,[ebp-0c]
0167:004a3a33 mov eax,[ebp-04]
0167:004a3a36
call 004085a8
<--将机器码转换为大写字符
0167:004a3a3b mov eax,[ebp-0c]
0167:004a3a3e
lea ecx,[ebp-08]
0167:004a3a41 movzx
edx,bx <--edx装入Dh,即十进制数13
0167:004a3a44
call 004a3958
<--完成计算正确注册码的第一步
0167:004a3a49 mov edx,esi
0167:004a3a4b
mov eax,[ebp-08]
0167:004a3a4e call
004a3bdc <-完成计算正确注册码的第二步
0167:004a3a53
xor eax,eax
0167:004a3a55 pop
edx
0167:004a3a56 pop ecx
0167:004a3a57
pop ecx
0167:004a3a58 mov [fs:eax],edx
0167:004a3a5b
push dword 004a3a75
0167:004a3a60 lea
eax,[ebp-0c]
0167:004a3a63 mov edx,03
0167:004a3a68
call 00403c40
0167:004a3a6d ret
0167:004a3a6e
jmp 00403630
0167:004a3a73 jmp
short 004a3a60
0167:004a3a75 pop esi
0167:004a3a76
pop ebx
0167:004a3a77 mov esp,ebp
0167:004a3a79
pop ebp
0167:004a3a7a ret
004a3a44处的所谓计算注册码的第一步就是指先对机器码进行一些处理,我们按F8跟进去:
0167:004a3958
push ebp
0167:004a3959 mov ebp,esp
0167:004a395b
add esp,byte -14
0167:004a395e push
ebx
0167:004a395f push esi
0167:004a3960 push
edi
0167:004a3961 xor ebx,ebx
0167:004a3963
mov [ebp-14],ebx
0167:004a3966 mov
[ebp-0c],ecx
0167:004a3969 mov [ebp-08],edx
0167:004a396c
mov [ebp-04],eax
0167:004a396f mov
eax,[ebp-04]
0167:004a3972 call 00404050
0167:004a3977
xor eax,eax
0167:004a3979 push
ebp
0167:004a397a push dword 004a39f9
0167:004a397f
push dword [fs:eax]
0167:004a3982 mov
[fs:eax],esp
0167:004a3985 mov eax,[ebp-0c]
0167:004a3988
call 00403c1c
0167:004a398d mov eax,[ebp-08]
0167:004a3990
add eax,[ebp-08]
0167:004a3993 mov
[ebp-10],eax
0167:004a3996 mov eax,[ebp-04]
0167:004a3999
call 00403e9c
0167:004a399e
mov edi,eax
<--eax中此时装的是机器码的位数
0167:004a39a0 test edi,edi
<--测试edi
0167:004a39a2
jng 004a39db
<--为0就跳到004a39db处
0167:004a39a4 mov esi,01
<--esi置1,用于后边儿的循环
0167:004a39a9
mov eax,[ebp-04]
<--机器码的地址装入eax中,此时的机器码已转换为大写字符
0167:004a39ac xor ebx,ebx
<--ebx清0
0167:004a39ae
mov bl,[eax+esi-01] <--得到机器码
0167:004a39b2
test esi,01
<--test esi,01可以理解为测试esi中装的值是否为双数
0167:004a39b8
jnz 004a39bf
<--不为双数就跳到004a39bf处
0167:004a39ba add ebx,[ebp-08]
<--ebp-08处装的是Dh,也就是那个13,用其加上ebx中的机器码的ASCII码
0167:004a39bd
jmp short 004a39c2 <--无条件跳转到004a39bd处
0167:004a39bf
add ebx,[ebp-10]
<--ebp-10处装的是1Ah,即十进制数26,用其加上ebx中装的机器码的ASCII码
0167:004a39c2 lea
eax,[ebp-14]
0167:004a39c5 mov
edx,ebx
0167:004a39c7 call 00403dc4
0167:004a39cc
mov edx,[ebp-14]
0167:004a39cf mov
eax,[ebp-0c]
0167:004a39d2 call 00408594
<--将得到的ASCII码与26或13相加后的结果保存起来
0167:004a39d7
inc esi
<--esi加上1
0167:004a39d8 dec edi
<--edi减去1
0167:004a39d9
jnz 004a39a9
<--edi不为零,也就是机器码还没有全计算完,就跳回004a39a9处继续
0167:004a39db xor
eax,eax
0167:004a39dd pop edx
0167:004a39de
pop ecx
0167:004a39df pop ecx
0167:004a39e0
mov [fs:eax],edx
0167:004a39e3 push
dword 004a3a00
0167:004a39e8 lea eax,[ebp-14]
0167:004a39eb
call 00403c1c
0167:004a39f0 lea eax,[ebp-04]
0167:004a39f3
call 00403c1c
0167:004a39f8 ret
0167:004a39f9
jmp 00403630
0167:004a39fe jmp
short 004a39e8
0167:004a3a00 pop edi
0167:004a3a01
pop esi
0167:004a3a02 pop ebx
0167:004a3a03
mov esp,ebp
0167:004a3a05 pop
ebp
0167:004a3a06 ret
这个CALL的作用呢,就是这样的:将机器码中的每个字符的ASCII码,加上26或13(单位,如第1、3、5位加上26,双位,如第2、4、6位加上13),并将之保存起来,以便后面的应用。与我的机器对应的是:sW_fd?MFN>P
好的,我们继续,等到返回后,接着走两步,我们就会到达004a3a4e处,在这个CALL里,会完成计算注册码的最后一步,一起去看看吧:
0167:004a3bdc
push ebp
0167:004a3bdd mov ebp,esp
0167:004a3bdf
add esp,byte -10
0167:004a3be2 push
ebx
0167:004a3be3 mov [ebp-08],edx
0167:004a3be6
mov [ebp-04],eax
0167:004a3be9 mov
eax,[ebp-04]
0167:004a3bec call 00404050
0167:004a3bf1
xor eax,eax
0167:004a3bf3 push
ebp
0167:004a3bf4 push dword 004a3c62
0167:004a3bf9
push dword [fs:eax]
0167:004a3bfc mov
[fs:eax],esp
0167:004a3bff mov eax,[ebp-08]
0167:004a3c02
call 00403c1c
0167:004a3c07 mov ebx,1e61
<--ebx装入1e61h,即十进值数7777
0167:004a3c0c
mov eax,[ebp-04]
0167:004a3c0f call
00403e9c
0167:004a3c14 mov edx,eax
0167:004a3c16
test edx,edx
0167:004a3c18 jna 004a3c32
0167:004a3c1a
mov eax,01
<--eax中装入1,用于下边儿的循环
0167:004a3c1f mov
ecx,[ebp-04] <--ebp-04中装的正是字符串sW_fd?MFN>P的地址
0167:004a3c22
movzx ecx,byte [ecx+eax-01] <--ecx中装入本轮用于计算的字符
0167:004a3c27
add ecx,eax
<--ecx加上eax的值
0167:004a3c29 imul ecx,ebx
<--乘以ebx,第一次的时候ebx中的值为前边装入的7777
0167:004a3c2c
mov ebx,ecx
<--结果装入ebx中
0167:004a3c2e inc eax
<--eax加上1,用于得到下一个字符
0167:004a3c2f
dec edx
<--edx减去1
0167:004a3c30 jnz 004a3c1f
<--不为零就跳回去继续
0167:004a3c32
mov eax,[ebp-08]
0167:004a3c35 push
eax
0167:004a3c36 mov [ebp-10],ebx
0167:004a3c39
mov byte [ebp-0c],00
0167:004a3c3d lea
edx,[ebp-10]
0167:004a3c40 xor ecx,ecx
0167:004a3c42
mov eax,004a3c78
0167:004a3c47 call
004098ec
0167:004a3c4c xor eax,eax
0167:004a3c4e
pop edx
0167:004a3c4f pop ecx
0167:004a3c50
pop ecx
0167:004a3c51 mov [fs:eax],edx
0167:004a3c54
push dword 004a3c69
0167:004a3c59 lea
eax,[ebp-04]
0167:004a3c5c call 00403c1c
0167:004a3c61
ret
0167:004a3c62 jmp 00403630
0167:004a3c67
jmp short 004a3c59
0167:004a3c69 pop
ebx
0167:004a3c6a mov esp,ebp
0167:004a3c6c
pop ebp
0167:004a3c6d ret
嘿嘿,“平民算法”嘛
现在我们手里所掌握的,已足够写出注册机了,通常情况下,我们可以声明这样一个函数用于计算正确的注册码:
function
GetKey(Name: String): String;
var
User,Serial:String;
i:integer;
ASC,jia,Zhi:Cardinal;
begin
zhi:=7777;
User:=UpperCase(Name);
if Length(User)=0 then Result:='请输入您的机器码...'
else
begin
for i:=1 to Length(User)
do
begin
ASC:=Ord(User[i]);
if i mod 2 =0 then ASC:=ASC+13
else
ASC:=ASC+26;
ASC:=ASC+i;
jia:=ASC*zhi;
zhi:=jia;
end;
Serial:=inttostr(zhi);
Result:=Serial;
end;
end;
然后我们就可以在Delphi中通过VCL来增大我们KeyGen的体积了,但今天我们不准备这么做
OK,启动你的Delphi,然后新建一个应用程序,然后就在代码窗口中右键Unit1单元->Close
Page,接着选No来把Unit1.Pas给删除掉。
接着选菜单View->Units,双击Project1打开它,我们就靠它吃饭了
随便找个文件夹把它保存一下,比如说保存为KeyGen.dpr。
然后我们可以把它的内容给全部删掉,只留下三行:
program KeyGen;
begin
end.
呵呵,按F9吧,你会什么也感觉不到,但事实上这已经是一个程序了。
如果你看一下所生成的KeyGen.exe的大小,你会发现,尽管我们什么也没有写,可它已经有了8K大。这是因为在任何DELPHI的目标程序中,都会自动包含System单元中的代码,如果你用Delphi5的话,它会是16K。在这小小的8K中,包含了支撑整座DELPHI大厦的基石,VeryVery牢靠。
以后随着我们代码的增加,程序的大小也会从8K开始慢慢跟着增加
让我们开始吧,我把注册机与资源文件的代码贴出来:
{-<KeyGen.dpr>------------------------------------Code
made >> Suunb[CCG]-----.}
(***********Lovely CCG,wish it becorning more
prosperous every day!************)
{---------------------------------------------------------Data:April
26th,2003.}
program KeyGen;
uses
Windows,
Messages;
{---------------------------------------------------------<引入相关单元>------;}
{----Windows.pas里面包含了常量与API的定义,Messages.pas中则包含了消息的定义----;}
{---这里要说一句的是,某些单元的引入,会增加可执行文件的大小,还好这两个不会^_^-}
{$R
KeyGen.res} (*<引入资源文件,资源中包含了程序中用到的对话框等等...>*)
const
IDI_CCG =1 ;
IDC_EDIT_CODE
=3001 ;
IDC_EDIT_SERIAL =3002 ;
IDC_BUTTON_GENERATE =3003 ;
IDC_BUTTON_ABOUT
=3004 ;
IDC_BUTTON_EXIT =3005 ;
IDC_ABOUT_BUTTON_GREETING = 3006 ;
szDlgName
= 'KeyGenMain';
szAboutDlg = 'About';
{---------------------------------------------------------<以上为常量定义>----;}
function
GetKey(S: String): String;
var
Code,Serial:String;
i:integer;
ASC,Q,P:Cardinal;
NN:array [0..512]
of Char;
begin
P:=7777;
Code:=S;
if
Length(Code)=0 then Result:='请输入您的机器码...'
else
begin
CharUpperBuff(PChar(Code),Length(S));
for i:=1 to Length(Code) do
begin
ASC:=Ord(Code[i]);
if i mod 2 =0 then ASC:=ASC+13
else
ASC:=ASC+26;
ASC:=ASC+i;
Q:=ASC*P;
P:=Q;
end;
if P>2147483647 then
begin
if (P>2147483647)
and (P<3000000000) then
begin
P:=P-2000000000;
if P<100000000 then
wvsprintf(NN,'0%d',@P)
else
wvsprintf(NN,'%d',@P);
Serial:='2'+String(NN);
end
else if (P>3000000000)
and (P<4000000000) then
begin
P:=P-3000000000;
if P<100000000 then
wvsprintf(NN,'0%d',@P)
else
wvsprintf(NN,'%d',@P);
Serial:='3'+String(NN);
end
else if (P>4000000000)
and (P<5000000000) then
begin
P:=P-4000000000;
if P<100000000 then
wvsprintf(NN,'0%d',@P)
else
wvsprintf(NN,'%d',@P);
Serial:='4'+String(NN);
end
end
else
begin
wvsprintf(NN,'%d',@P);
Serial:=String(NN);
end;
Result:=Serial;
end;
end;
{-------------------------------<上面的这个函数就是用来计算正确的注册码的>----;}
function
AboutProc (Wnd: hWnd; Msg, wParam: Word; lParam: LongInt)
:LongInt;
stdcall;
var
ea:DWORD;
aa:WORD;
begin
Case Msg of
WM_CLOSE:EndDialog(Wnd,0);
WM_COMMAND:
begin
aa:=wParam;
if aa=IDC_ABOUT_BUTTON_GREETING then
EndDialog(Wnd,0);
{-------<Greeting被按下,窗口关闭>}
end;
else
Result:=1;
end;
Result:=0;
end;
{-<上面的函数用于处理“关于”对话框接收到的消息,stdcall表示其为一个回调函数>-;}
function
DlgProc (Wnd: hWnd; Msg, wParam: Word; lParam: LongInt)
:LongInt;
stdcall;
var
Code:array[0..512] of Char;
Serial:String;
ea:DWORD;
aa:WORD;
begin
Case Msg of
WM_CLOSE:
EndDialog(Wnd,0);
WM_INITDIALOG:
begin
ea:=LoadIcon(hInstance,
PChar(IDI_CCG));
SendMessage(Wnd,WM_SETICON,
ICON_SMALL, ea);
{------------------------<设置对话框的图标>}
end;
WM_COMMAND:
begin
aa:=wParam;
if aa=IDC_BUTTON_ABOUT
then
DialogBoxParam(hInstance,szAboutDlg,Wnd,
@AboutProc, 0)
{------------------<About被按下,就创建“关于”对话框>}
else if aa=IDC_BUTTON_GENERATE then
begin
GetDlgItemText(Wnd,IDC_EDIT_CODE,Code,512);
Serial:=GetKey(Code);
SetDlgItemText(Wnd,IDC_EDIT_SERIAL,PChar(Serial));
{<Generate被按下,调用上面声明的GetKey函数来计算注册码>}
end
else if aa=IDC_BUTTON_EXIT
then
SendMessage(Wnd,WM_CLOSE, 0, 0);
{------------<Exit被按下,退出>}
end;
else
Result:=1;
end;
Result:=0;
end;
{---------------------------------<主窗口的消息循环,同样为一个回调函数>------;}
begin
DialogBoxParam(hInstance,szDlgName,0,@DlgProc,0);
(*程序开始,调用DialogBoxParam来创建程序的主对话框,hInstance是Delphi提供
给我们的全局变量,不必再去调用GetModuleHandle函数*)
ExitProcess(0);
{Over,调用ExitProcess}
end.
//------------------------------------------------------------------------------
//------------------the
Shang Dynasty (16th―11th century B.C.)-----------------
//--------------------------------------------------------------------Code
end--
下面再把资源文件也贴出来:
//-KeyGen.rc----------------------------------------------------------------
//编译用rc
KeyGen.rc
//-----------------------------------------------------------by Suunb[CCG]--
#include
"resource.h"
#define
CCG_ICO
1
#define Su_ICO
2
#define IDC_EDIT_CODE
3001
#define IDC_EDIT_SERIAL
3002
#define IDC_BUTTON_GENERATE
3003
#define IDC_BUTTON_ABOUT
3004
#define IDC_BUTTON_EXIT
3005
#define IDC_ABOUT_BUTTON_GREETING
3006
CCG_ICO
ICON "ccg.ico"
//CCG的图标 //-------------------------------------------<以上是一些常量的定义>--------- KeyGenMain
Su_ICO ICON "Suunb.ico"
//自己做的一个图标
style DS_CENTER | WS_MINIMIZEBOX | WS_VISIBLE
| WS_CAPTION | WS_SYSMENU
CAPTION "CCG KeyGen for 敏思硬盘卫士 v2.2"
FONT
9, "Arial"
BEGIN
GROUPBOX "register",-1,5,3,151,44,BS_FLAT
LTEXT "Code:",-1,10,14,23,10
EDITTEXT IDC_EDIT_CODE,35,13,115,12,ES_AUTOHSCROLL,WS_EX_DLGMODALFRAME
LTEXT "Serial:",-1,10,29,23,10
EDITTEXT IDC_EDIT_SERIAL,35,29,115,12,ES_AUTOHSCROLL,WS_EX_DLGMODALFRAME
PUSHBUTTON "gENERATE",IDC_BUTTON_GENERATE,13,54,39,11,BS_CENTER
| BS_FLAT
PUSHBUTTON "aBOUT",IDC_BUTTON_ABOUT,61,54,39,11,BS_CENTER
| BS_FLAT
PUSHBUTTON "eXIT",IDC_BUTTON_EXIT,108,54,39,11,BS_CENTER
| BS_FLAT
LTEXT "wish
it becorning more prosperous every day!",-1,10,77,
143,9
GROUPBOX
"Lovely CCG",-1,6,69,151,21,BS_CENTER | BS_FLAT
END
//--------------------------------------------<以上是程序的的主窗口对话框>--------- About
style DS_MODALFRAME | DS_CENTER | WS_POPUP |
WS_VISIBLE | WS_CAPTION |
WS_SYSMENU
CAPTION "The Shang
Dynasty (16th - 11th century B.C.)"
FONT 9, "Arial"
BEGIN
ICON 1,-1,13,14,20,20
LTEXT "Code made:",-1,25,3,41,8
LTEXT "by",-1,40,13,8,8
LTEXT "Date:April 26t,2003",-1,48,25,61,9
CONTROL "",-1,"Static",SS_ETCHEDHORZ,4,36,132,1
LTEXT "Personal Individual
Greetings go to:",-1,19,41,114,8
LTEXT
"Sun Bird - JOJO - KANXUE",-1,11,51,85,9
LTEXT
"pll621 - zmworm - yyxzz",-1,46,62,73,9,SS_CENTERIMAGE
LTEXT "Personal Group Greetings
go to:",-1,22,87,105,8
LTEXT
"BCG FCG iPB DFCG DCM",-1,28,101,85,9,WS_BORDER
LTEXT
"Suunb[CCG]",-1,50,12,45,11,0,WS_EX_DLGMODALFRAME
LTEXT "and other CCG guys...",-1,29,74,92,9,SS_CENTERIMAGE
ICON 2,-1,112,14,20,20
PUSHBUTTON "gREETING",IDC_ABOUT_BUTTON_GREETING,50,120,41,12,BS_FLAT
END
//--------------------------------------------<这个是“关于”对话框>--------
你可以用记事本将这两个文件分别保存为KeyGen.dpr、KeyGen.rc,而后用rc
KeyGen.rc来得到KeyGen.res,之后将KeyGen.res与KeyGen.dpr放置同一目录下用Delphi编译KeyGen.dpr即可...
看一下编译后的KeyGen的大小吧,20.5K,再来个壳就只有10几K了,虽然界面并不怎么漂亮
OK,就到这儿。