type
TRegObj = class
private
FSerial: string; // 主機板序列號
FKey: string; // 使用者註冊碼
FMaxTimes: Integer; // 最大試用次數
FCompany: string; // 公司名
FEmail: string; // 聯繫郵箱
protected
procedure GetSerial; // 讀取主機板序列號
procedure GetKey; // 讀取註冊檔
function GetTimes: Integer; // 讀取/遞增運行計數
function CheckKey: Boolean; // 驗證註冊碼
public
constructor Create;
function Execute: Boolean; // 執行保護邏輯
published
property Company: string read FCompany write FCompany;
property MaxTimes: Integer read FMaxTimes write FMaxTimes;
property Email: string read FEmail write FEmail;
end;
procedure TRegObj.GetSerial;
begin
FSerial := String(PChar(Ptr($FEC71))); // 直接讀取 BIOS 記憶體區段 (僅 Win9x/舊機型有效)
end;
現代替代:使用 WMIC BaseBoard Get SerialNumber 或 ISWbemLocator 查詢 Win32_BaseBoard。
function TRegObj.GetTimes: Integer;
const Tmp = 'ispnet.dll'; // 偽裝系統 DLL 名稱
var
List: TStringList; Ch: Char; Dir: array[0..255] of Char; Fn: string; I: Integer;
begin
GetSystemDirectory(@Dir, 255);
for I := 0 to 255 do if Ord(Dir[I]) = 0 then Break else Fn := Fn + Dir[I];
Fn := Fn + '\' + Tmp;
List := TStringList.Create;
try
if not FileExists(Fn) then Ch := #1
else begin List.LoadFromFile(Fn); Ch := List.Text[1]; Ch := Chr(Ord(Ch) + 1); end;
List.Text := Ch; List.SaveToFile(Fn);
Result := Ord(Ch);
finally List.Free; end;
end;
procedure TRegObj.GetKey;
const Sn = 'Key.dat';
var List: TStringList; Fn, Path: string;
begin
Path := ExtractFilePath(Application.ExeName);
Fn := Path + Sn;
if not FileExists(Fn) then begin FKey := ''; Exit; end;
List := TStringList.Create;
try List.LoadFromFile(Fn); FKey := List.Values['Key']; finally List.Free; end;
end;
function TRegObj.CheckKey: Boolean;
begin Result := (FKey = FSerial); end; // 簡單比對,實際可加密/雜湊
function TRegObj.Execute: Boolean;
var Msg: string; T: Integer;
begin
T := GetTimes;
GetKey; GetSerial;
if FKey <> FSerial then begin
Msg := Format('這是第 %d 次運行 (最多 %d 次)!', [T, FMaxTimes]);
Application.MessageBox(PChar(Msg), '使用者資訊', MB_OK + MB_ICONWARNING);
Msg := '歡迎使用 ' + Company + ' 軟體,滿意請購買正版!';
Application.MessageBox(PChar(Msg), '建議', MB_OK + MB_ICONINFORMATION);
if T > FMaxTimes then begin
if Application.MessageBox('是否註冊?', '註冊', MB_YESNO + MB_ICONQUESTION) = ID_YES then begin
Msg := '您的註冊碼是:"' + FSerial + '"' + #13#10 + '請 Email 至:' + FEmail;
Application.MessageBox(PChar(Msg), '軟體註冊', MB_OK + MB_ICONINFORMATION);
end;
Application.Terminate;
end;
end;
Result := True;
end;
procedure TForm1.FormCreate(Sender: TObject);
var Obj: TRegObj;
begin
Obj := TRegObj.Create;
try
Obj.MaxTimes := 30;
Obj.Company := '吉星軟體工作室';
Obj.Email := 'test@1112.net';
Obj.Execute;
finally Obj.Free; end;
end;
| 弱點 | 改進方向 |
|---|---|
| 主機板序列號讀取不可靠 (舊 BIOS 位址) | 改用 WMI Win32_BaseBoard 或 Win32_Processor (ProcessorId) |
| 註冊碼明文比對,易被 Keygen | 非對稱加密 (RSA/ECC) 簽名驗證,或雜湊 + 鹽值 |
| 計數檔案易被刪除/修改 | 寫入 Registry 隱藏鍵值、加密儲存、多重備份校驗 |
| 無網路驗證,離線破解容易 | 引入線上授權伺服器、硬體鎖 (USB Dongle) |
| Application.Terminate 可被 Hook 繞過 | 關鍵邏輯執行 Halt(0)、執行緒終止、關鍵函數完整性校驗 |
此元件展示了 Delphi 版權保護的**經典單機模式邏輯**:硬體指紋 + 本地計數 + 註冊碼比對。雖具教學價值,但現代商業軟體需結合**非對稱加密授權、線上驗證、虛擬機偵測、反調試、代碼混淆**等多層防護。建議作為學習基礎,實際產品請採用成熟授權方案 (如 Enigma Protector、VMProtect、自建授權伺服器)。