作者從兩個方面考慮應用軟件的保護,第壹是防止盜版使用軟件,第二是防止競爭對手反編譯軟件,即防止軟件的逆向工程。
1,停止盜版
軟件運行時判斷自身存在的合法性,如果認為自身的存在和運行是授權合法的就運行;否則,操作終止。這樣,即使軟件可以隨意復制,只要盜版用戶沒有相應的授權信息,就無法使用軟件。
2.防止反編譯
加密編譯後的類文件,並在運行時解密。解密器不能反編譯軟件。
二、Java軟件加密的整體過程
為了保護用Java語言開發的軟件,我們設計並實現了壹種實用的高強度加密算法。以下將需要保護的Java軟件稱為“保護程序”,對“保護程序”進行加密保護的軟件稱為“加密程序”。軟件加密保護的流程如圖1所示。
第三,加密算法的分析與設計
1,用戶信息抽取器設計
為了防止用戶發布序列號,導致“壹次發布,隨處可見”的盜版問題,提取用戶機器中與硬件相關的唯壹信息——用戶電腦硬盤分區C的序列號,並要求用戶將該信息連同用戶名壹起返回。然後由“序列號生成器”根據用戶返回的信息生成壹個唯壹合法的軟件註冊序列號並發送給用戶,用戶可以用這個號碼註冊和使用軟件。
這個信息提取器是作為壹個獨立的applet使用Winclows 32匯編實現的,程序代碼如圖2所示。
2.序列號生成器和序列號合法性判斷功能的設計。
序列號生成器和序列號合法性判斷功能采用RSA加密算法。在序列號生成器中,用私鑰加密用戶返回的信息(硬盤序列號,用戶名)得到對應的註冊序列號;私鑰用於解密用戶在序列號合法性判斷功能中輸入的註冊序列號,然後與(硬盤序列號,用戶名)進行比對。如果壹致,則調用程序加載器解密程序的其他部分並加載到內存中,初始化刪除環境並運行程序主體;否則退出。
RSA加密算法的實現需要使用大數運算庫,我們使用MIRACL大數庫來實現RSA運算。序列號生成器的主要代碼如下:
Char szlnputString[]= "由機器代碼和用戶名組成的字符串";
char szSerial[256]=[0];//用於存儲生成的註冊碼。
bign,d,c,m;//MIRACL中的大數字類型
MIP→IBASE = 16;//在16基本模式下
n = MLR var(0);//初始化壹個大數
d = mirvar(0);
c = mirvar(0);//C存儲大量的輸入字符串。
m = ml RVA(o);
bytes to big( len,szlnputString,c);
//將輸入的字符串轉換成壹個大數,存儲在變量c中。
Cinstr(n,“以字符串形式表示的模數”);//初始化模數
Cinstr(d,"以字符串形式表示的公鑰")://初始化公鑰。
powmod(c,d,n,m);//計算m=cdmod n
cotstr(m,SZ serial);//m的16二進制串是註冊碼。
序列號合法性檢測功能的主要代碼如下:
Char szlnputStringL]= "由機器代碼和用戶名組成的字符串";
Char szSerial[ 256]= "用戶輸入的序列號"
bign,e,c,m;//MIRACL中的大數字類型
MIP→IBASE = 16;//在16基本模式下
cinstr(m,SZ serial);//將序列號從16轉換為大數。
Cinstr(n,“模數n的字符串形式”);//初始化模數n
Cinstr(e,“字符串形式的公鑰”);//初始化公鑰
if compare(m,n)= =-1)//m & lt;n解密前。
{
powmod(男、女、男、女);//計算m=me mod n
big_to _bytes(0,c,szSerial,0);//轉換為字符串
返回lstrcmp( szlnputString,SZ serial);
}
3.強耦合關系的設計
如果圖3所示的流程僅用於序列號合法性檢測功能:
解密器可以使用以下方法進行攻擊:
(1)修改“判斷合法性子功能”的返回指令,使其始終返回正確的值,使軟件可以安裝/使用任意序列號。
(2)修改判斷後的跳轉指令,使程序永遠跳轉到正確的分支,效果與上壹條相同。
(3)在“判斷合法性子功能”前執行壹個跳轉指令,繞過判斷,直接跳轉到“正常執行”分支,這樣就可以不用輸入序列號就可以安裝/使用軟件。
為了防範上述攻擊手段,作者增加了序列號合法性檢測功能與程序其他部分“強耦合”的要求(即增強其與程序其他部分的相關性,成為整個程序不可分割的壹部分,壹旦被修改,程序將無法正常工作)(見圖1),並設置了“完整性檢測功能”來判斷相關代碼是否被修改。當然,出於同樣的原因,“完整性檢查功能”也必須與程序的其他部分有“強耦合”的關系。
強耦合關系通過以下方式建立:
隨機訪問程序其他部分的函數(例如函數A)需要“序列號有效性檢測函數”和“完整性檢測函數”的強耦合。調用時,隨機選擇壹個錯誤的序列號或用戶輸入的序列號,根據返回的結果,選擇A中的正常功能碼或出錯的功能碼。流程如圖4所示。
這次改進後,如果破解者通過修改代碼破解,程序會因為“完整性檢測”失敗而退出;如果用SMC等技術繞過“序列號合法性判斷功能”,在序列號正確時直接跳轉到執行入口,程序會因為後續操作中隨機耦合調用失敗而退出。要破解軟件,破解者將不得不跟蹤所有通過耦合調用過的函數,這顯然是壹項艱巨的任務。
4.完整性檢測功能的設計。
我們使用CRC算法計算需要完整性檢測的文件的校驗碼,使用RSA加密算法的公鑰(不同於序列號合法性檢測中的公鑰/私鑰對)對其進行加密,並存儲在特定的文件中。檢測時,我們先用CRC算法重新生成。
用私鑰解密完整性檢測文件的校驗碼,兩者進行比較,如果相等,則正常運行;否則退出。
5.程序加載器的設計
與編譯成機器碼的程序不同,Java程序只能由Java虛擬機解釋和執行,所以程序加載器的工作包括:初始化Java虛擬機;解密內存中當前運行的類文件;讓解密的c:class文件在虛擬機中運行。
如有必要,解密另壹個類文件。圖5是用於初始化JVM的代碼:
以上介紹了我們為Java軟件設計的加密保護方法,其中綜合運用了多種加密技術,防破解強度高;采用純軟件保護技術,成本低。經作者在Windows系列平臺上測試,運行穩定,效果良好。
在研發過程中,我們也總結了壹些加密保護軟件的經驗:
1.密鑰代碼和數據應該靜態加密,然後動態解密。防跟蹤/調試技術應結合具體工作平臺使用;
2.要充分利用系統的功能,比如在Windows下使用DLL文件或者驅動程序,得到最大的豐富和限制,可以充分利用系統的各種功能;
3.如果可能的話,關鍵代碼要存放在不能復制的地方;
4.序列號要和機器碼等用戶信息相關聯,防止食鹽再次流通;
5.加密過程的合理性比加密算法本身的強度更重要。