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的圖標
Su_ICO ICON "Suunb.ico" //自己做的一個圖標
//-------------------------------------------<以上是一些常量的定義>---------
KeyGenMain DIALOGEX 10, 10, 163, 94
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 DIALOGEX 10, 10, 143, 141
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,就到這兒。