共享軟體 (Shareware) 透過「試用期限制」或「功能限制 + 註冊碼解鎖」商業化。本文基於 Win9x/NT 時代實戰經驗,系統闡述 **硬體資訊採集 → 註冊機生成碼 → 客戶端驗證 → 多點防破解** 完整工程鏈路。雖作業系統變遷,核心思想 (硬體指紋、非對稱/單向函數、分散驗證點) 至今仍具參考價值。
// 保護模式需透過選擇子映射物理記憶體
UINT Sel1, Sel2;
__asm mov Sel1, ds; // 以資料段為模板
Sel2 = AllocSelector(Sel1); // 分配新選擇子
SetSelectorBase(Sel2, 0xF0000); // 物理基址 F0000h (F000:0000)
SetSelectorLimit(Sel2, 0xFFFF); // 64KB 限制
BYTE* pRom = (BYTE*)((Sel2 << 16) | 0xFFF5); // F000:FFF5
// 讀取 pRom[0]~pRom[10] 等關鍵位元組
FreeSelector(Sel2); // 釋放選擇子 (系統上限 8192)
現代替代:WMI (`SELECT SerialNumber FROM Win32_BaseBoard`)、`SetupDiGetDeviceRegistryProperty` (硬碟)、`GetAdaptersAddresses` (MAC) —— 純 Ring 3、無需驅動、跨版本穩定。
for (i=0; i<5; i++)
keyrom[i] = (*(pRom+i) + 0x1818) ^ 0x5858; // 簡單可逆混淆
sprintf(buf, "KEYID:%04X%04X%04X%04X%04X", keyrom[0]...keyrom[4]);
// 產生 20 字元十六進位字串,用戶回報給開發者
DWORD sum = 0x1234, sum1 = 0x7456;
for (j=0; j<4; j++) { // 產生 4 組 = 16 字元
for (i=0; i<5; i++) { // 融合 5 組指紋
sum += keyrom[i];
sum1 += keyrom[i];
}
sum ^= 0x1234 << j; // 位移異或擴散
sum1 ^= 0x7456 << j;
sprintf(WinKey + j*4, "%04X", sum);
sprintf(DosKey + j*4, "%04X", sum1);
}
// WinKey: Windows 版註冊碼;DosKey: DOS/舊版相容碼
// 內部產生預期碼
UINT ImeCmpKey() {
// ... 讀取硬體指紋 ...
for (j=0; j<4; j++) {
for (i=0; i<5; i++) sum += (pRom[3+i] + 0x1818) ^ 0x5858;
sum ^= 0x1234 << j;
wsprintf(ExpectedKey + j*4, "%04X", sum);
}
// 二次加密預期碼 (簡單位運算)
for (i=16; i>0; i--) ExpectedKey[16-i] ^= (BYTE)i;
// 比對使用者輸入 (已同樣二次加密)
for (i=0; i<16; i++) if (ExpectedKey[i] != UserKey[i]) break;
return (i==16) ? 0 : -1; // 0=成功
}
**原則**:驗證邏輯 **不集中在單一函數**,而是散佈在高價值功能點、啟動路徑、關鍵資料流中。
| 類型 | 位置 | 觸發條件 | 失敗行為 |
|---|---|---|---|
| 啟動檢查 | `WinMain` / `InitInstance` | 每次啟動 | 進入試用模式/倒計時 |
| 使用次數限制 | 核心功能入口 | 計數器超閾值 (如 `UseNum >= 0x3F80`) | 強制驗證/鎖定 |
| 高級功能門檻 | 進階模組入口 | 用戶點擊付費功能 | 彈註冊對話框/降級 |
| 資料完整性 | 儲存/匯出/列印 | 關鍵資料寫入前 | 攔截/加浮水印 |
| 定時心跳 | 背景執行緒 | 每 N 分鐘 | 偵測記憶體修改/時間回滾 |
// 點 1: 使用次數門檻
if (lpImeL->UseNum >= 0x3F80) {
if (ImeCmpKey() != 0) { sImeG.ZcFlag = TRUE; } // 未註冊
else { sImeG.ZcFlag = FALSE; lpImeL->UseNum = 0; } // 重置計數
} else lpImeL->UseNum += sImeG.iWord;
// 點 2: 核心功能硬性驗證
if (ImeCmpKey() != 0) { sImeG.ZcFlag = TRUE; return; }
else { sImeG.ZcFlag = FALSE; lpImeL->UseNum = 0; }
HFILE h = _lopen(szPath, OF_READWRITE);
_llseek(h, 0x12345L, SEEK_SET); // 非標準偏移
_lwrite(h, EncryptedKey, 16); // 僅寫 16 字元核心碼
_lclose(h);
| 層面 | 措施 |
|---|---|
| 靜態分析 | 關鍵字串加密、控制流扁平化、虛假分支、去除符號表、UPX/VMProtect 殼 |
| 動態調試 | `IsDebuggerPresent`、`CheckRemoteDebuggerPresent`、時序檢測 (RDTSC)、硬體斷點檢測 (DR0-DR7) |
| 記憶體掃描 | 關鍵資料加密存儲、運行時解密即用即銷、關鍵比對分散在多個函數 |
| 時間/環境 | 系統時間回滾檢測 (對比 BIOS 時間/網路時間)、虛擬機檢測 (CPUID 葉 0x40000000)、沙箱檢測 |
| 網路驗證 | 關鍵功能需線上驗證 (License Server)、離線授權檔簽名 (ECDSA/RSA)、硬體指紋雲端比對 |
共享軟體註冊體系的核心不是「無法破解」,而是 **「提高破解成本超過購買成本」**。硬體指紋綁定 + 不可逆演算法 + 分散驗證點 + 二次加密存儲 + 多層反調試 構成纏繞防禦體系。現代架構應升級為 **非對稱簽名授權 + 離線授權檔 + 雲端風控**,將「註冊機」轉型為「授權簽名服務」,從根源消除私鑰洩露風險,同時提供更優雅的用戶體驗。