余彥 萬毅
摘 要: 作為最常用的軟件保護方式之一,序列號保護面臨著最多的攻擊者的研究。破解者主要的攻擊過程分為黑盒階段,動態分析階段,靜態分析階段和編輯階段。動態分析可以理解成一個遵循“定位保護代碼-修改二進制代碼-測試”的破解循環。從該破解循環入手,提出一種新的程序架構,在校驗代碼之前加入某種規則決定是否進行校驗,并將其嵌入主程序的不同位置,形成的對主程序的校驗位置的組合有2N種,這可為軟件提供更有效的保護措施。
關鍵詞: 軟件保護; 序列號; 動態分析; 軟件破解
中圖分類號:TP311.1 文獻標志碼:A 文章編號:1006-8228(2015)05-22-04
Abstract: As one of the most commonly used method of software protection, serial number protection is facing up to the crackers' research. The main process of software attacking is divided into black box stage, dynamic analysis stage, static analysis stage and editing stage. Dynamic analysis stage can be understood as a follow "positioning the protection code-modify the binary code-test" of the work cycle. From the work cycle of dynamic analysis stage, we put forward a new program architecture, by adding certain rules before deciding whether to execute the code for checking serial number, and to embed them into the main program for n times, so there are 2n different kinds of combinations of the code for checking serial number to form. Thus the software can be protected more effectively.
Key words: software protection; serial number; dynamic analysis; software cracking
0 引言
在計算機軟件技術高速發展的今天,軟件產品早已作為知識產權受到法律保護??墒窃谖覈?,即使有各種各樣的法律,軟件被盜版問題依然很嚴重。根據商業軟件聯盟(Business Software Alliance)公布的調查報告[1]顯示,2013年的盜版軟件安裝率為74%,雖然較前幾年(2011:77%,2009:79%,2007:84%)有所下降,但依然處于非常高的比例,據該調查報告顯示,盜版軟件的商業價值達到87億美元。因此,軟件保護技術的研究依然是熱點課題。
軟件保護技術中最常見的是序列號保護技術。針對序列號保護,本文提出了一種新的程序架構,其關鍵是將校驗的代碼隨機多次嵌入主程序的不同位置,而校驗代碼之前加入某種規則決定是否進行授權校驗,使得程序可以根據不同運行狀態而進入不同位置的校驗代碼段,從而增加盜版者的破解難度。
1 序列號保護機制原理
作為最常見的軟件保護模型,序列號保護[2]的結構如圖1所示。每次程序開始運行時,需要先校驗保存在本地的序列號。如果校驗未通過,如序列號不正確或序列號有效期已過,則要求用戶輸入合法的序列號,否則不允許進入受保護的主程序。當校驗通過,程序便處于合法運行狀態,可以使用受保護的功能或算法等。
[啟動程序][用戶程序][校驗序列號] [進入主程序][程序結束][輸入序列號] [T][F]
圖1 序列號校驗流程
對于獲得了序列號的用戶來說,校驗過程是透明的,用戶只需要輸入從軟件廠商購買的序列號,校驗過程由本地程序自動完成[3]。校驗過程可用如下公式表示。
⑴ 軟件廠商獲得用戶信息后計算序列號:
序列號=F(用戶信息)
⑵ 用戶獲得序列號后在本地進行校驗:
用戶信息=G(序列號)
購買者如果隨意將用戶信息和對應的序列號非法分發給其他未授權使用者,軟件廠商無法禁止其使用。因此本文采取硬件信息作為用戶信息,如此,即使序列號被非法分發,也因為硬件不同而無法在不同的機器上使用,從而可以更好的保護軟件。
2 破解者的主要攻擊方法
整個破解的過程可以分為若干個階段,在最初的幾個階段中,破解者的任務主要是分析程序,試圖弄明白程序的內部結構及其行為模式。而在后面幾個階段,破解者則會使用前幾個階段中獲得的信息,按具體需要對程序進行修改。我們把破解過程大致分為四個階段[4]。
⑴ 黑盒階段
在黑盒分析階段,破解者并不了解程序的內部構造,對其而言,程序就是一個“黑盒”。破解者會試圖給程序輸入一些數據,并觀察程序的輸出,然后根據程序的行為做出某些推斷。
⑵ 動態分析階段
在動態分析階段,破解者開始分析程序的內部行為,雖然這時他仍然會執行程序,但是他這時會記錄在輸入不同的數據后,程序會執行哪些不同的部分。
⑶ 靜態分析階段
為了更好的理解程序的作用,破解者在動態分析過后會直接分析代碼本身,這就是靜態分析。
⑷ 編輯階段
前三個階段的工作使得破解者可以弄清楚他所關心的算法,找到了秘鑰所在。但如果破解者還需要對程序進行修改,例如刪除檢查軟件序列號部分的代碼,或者是抹除程序中用來標識用戶信息的指紋水印等,則需要驚醒這個步驟對原程序進行編輯。
動態分析中的破解循環,對于破解者來說,對一個程序進行動態分析是需要遵循“定位保護代碼-修改二進制代碼-測試”的破解循環來工作。如圖2所示,在定位保護代碼的階段,破解者需要找到程序的保護代碼(比如校驗序列號的代碼段)在程序中的位置,并且在接下來的修改階段對其進行一系列的修改使程序能按其方式來運行。對二進制代碼進行的修改包括去除舊的指令,增加新的指令以及對原有指令進行修改。最后,破解者還要測試經過修改后的程序是否能按照其意愿進行工作,否則將進行下一次破解循環。本文提出的程序結構就是從破解循環入手,增加其動態分析程序的阻力,通過增加程序運行時的不確定性讓程序在每次獨立運行時都會動態的選擇不同的執行路徑,讓破解者無法輕易找到序列號的校驗代碼所在。
圖3是采用objdump工具對作者的程序進行反匯編得到的結果。圖3左側是未經過修改的程序,MainWindow::core()是一個函數,用于代表程序的主體部分,其地址為0x00405040。圖片左側下方的內容是軟件的序列號校驗模塊其中的一段。進入程序主體部分之前,需要經過序列號的校驗。這段匯編代碼中有一條判斷語句:test %al,%al,其功能是如果用戶輸入的序列號通過了校驗,那么就進入主程序部分(地址0x00405040)繼續執行,否則就跳轉至程序的結束部分的地址(0x004059b0)??梢钥闯觯瑢浖谋Wo依賴于序列號的校驗是否成功。如果攻擊者對程序先進行反匯編,然后找到這段控制序列號校驗邏輯的代碼,將其的跳轉邏輯修改成無論序列號校驗是否成功都跳轉至程序的主體部分繼續執行,那么序列號的校驗模塊也就無法對軟件提供保護了。如圖3右下方代碼段所示,將匯編程序跳轉邏輯修改后,無論序列號校驗的結果如何,程序始終都會執行MainWindow::core()(在修改過的程序中該函數的地址為0x00404fd0)。也就是說,如果通過動態分析的手段找出了軟件的序列號校驗的代碼并對其功能進行修改,那么即使不知道序列號校驗的算法,也可以直接繞過序列號校驗的部分直接非法的使用該軟件。
3 一種新的抵抗動態分析的軟件模式
如前文所述,破解者在動態分析階段會試圖找出程序的序列號校驗模塊,并修改其邏輯已獲得非法使用程序的目的。動態分析主要是遵循“定位保護代碼-修改二進制代碼-測試”的破解循環來工作的,其中最重要的環節就是定位二進制代碼,如果破解者無法順利定位序列號校驗部分的代碼,也就無法針對其進行攻擊。傳統的商業軟件對于程序的保護一般是將程序的序列號校驗部分放在程序的最開始,這樣,攻擊者跟蹤程序的運行步驟不久后比較容易找出程序的校驗模塊。本系統的設計主要目的是提高破解者對代碼動態分析的難度,并且輔助以注冊碼的安全生成機制而提高軟件的安全性。
防止動態分析的手段,從動態分析階段入手,我們提出了以下更具復雜性和隨機性的機制來保護軟件。
⑴ 增加程序中校驗序列號的次數,也就是說,不僅僅在軟件安裝時校驗序列號,而在軟件的使用過程中可以嵌入多段校驗序列號的代碼。
⑵ 校驗序列號時不采用函數調用的方式,而是直接將代碼嵌入到需要校驗序列號的位置,這樣一來,攻擊者無法通過函數的調用頻率來分析該函數是否是校驗序列號的函數。
⑶ 在校驗的代碼段前增加一個開關,這個開關的狀態決定了是否進入校驗代碼段。如果開關的狀態可以隨機化,那么對于整個程序來說,不同位置的校驗狀態組合結果也就是隨機的。開關的狀態可以由開發人員自己決定,例如使用主程序的某個變量來決定,或者使用當前的時間,還可以將二者結合某個隨機值運算而成。加入這種機制后,如果在主程序代碼段內嵌入了N段校驗代碼,對于整個程序的校驗位置的組合來說理論上有2N種狀態。這種隨機性的加入使得代碼的破解難度呈指數級增加。
在設計開關時,我們提出三種方法。第一種是取系統的時間,根據其奇偶性作為開關的值;第二種是取系統中一個叫做State_Output_Counts的全局變量的值,State_Output_Counts是軟件中用于記錄程序運行狀態語句輸出次數的變量,根據其奇偶性作為開關的值;第三種是將系統當前時間和上述State_Output_Counts變量結合起來進行異或計算,根據得到的結果的奇偶性作為開關變量的值。每次決定具體使用哪一種方法之前取隨機數,根據隨機數的值對3求模,根據其結果決定使用哪一種開關。具體代碼如下:
r=rand();
seconds=time(NULL);
r=r%3;
switch (r)
{ case 0:Check_Switch=seconds;break;
case 1: Check_Switch=State_Output_counts; break;
case 2: Check_Switch=seconds^State_Output_Counts; bresk;
}
⑷ 將校驗序列號的代碼和程序的運算模塊結合起來,將校驗序列號的代碼段嵌入程序的運算模塊內,并且根據序列號校驗的結果決定運算模塊的結果是否正確,如果通過校驗代碼的驗證后,運算模塊可以正常使用。如果序列號校驗不通過,則可以通過校驗模塊傳遞給運算模塊一個錯誤的變量值使運算模塊失效或者輸出錯誤的結果。攻擊者很難發現運算模塊發生錯誤,而攻擊者如果采用修改二進制代碼的方式繞過校驗代碼部分,運算模塊也無法使用,也就起到了保護程序的作用。如果再結合第⑶步所描述的方法,運算模塊中的序列號校驗代碼不一定每次都會執行,因此在序列號不合法時運算模塊的功能有時會產生正確的結果而有時又會產生錯誤的結果,更加迷惑攻擊者。當序列號不正確時,程序并不會提示用戶序列號錯誤,而是直接給出錯誤的運算結果,因此攻擊者很難分析出序列號校驗代碼段的位置。
例如,原始代碼在主程序中完成一個加法功能的語句a+b,我們將其更改成為一個函數int Add(int a,int b),并且將序列號校驗的代碼嵌入其中,如果序列號校驗通過就正常的返回兩個參數的和,如果序列號校驗不通過,并不會有錯誤提示,但是返回值會是兩個參數的乘積,如此一來,攻擊者無法找到校驗的位置,也無法發現最終的運算結果是一個錯誤的答案。具體代碼如下:
int Add(int a,int b)
{ switch (Prog_State)
{ case true: return a+b;break;
case false: return a*b;break;
}
}
具體實現中,本文在主程序代碼中嵌入了5次校驗序列號的代碼,理論上應該有25種校驗狀態的組合。當校驗不通過時,計算結果產生了錯誤,也從另一個方面阻止了軟件的非法使用。
4 系統設計和實現
如圖4所示,該架構中客戶在程序開始時先獲得用戶ID,本系統中的用戶ID采用CPUID與MAC地址組合的方式生成。得到用戶ID后進入客戶端的序列號校驗模塊。校驗通過以后,可以進入主程序,序列號的校驗部分是由開發人員無規律隨機的分散在主程序代碼中的,校驗代碼段之前會有一個開關決定進入或是跳過校驗代碼,開關的狀態可以由多種變量的狀態決定。
對于服務器端,在用戶需要注冊時可以通過通信模塊接收用戶ID并通過序列號生成模塊計算出序列號并返回給用戶,并將用戶ID和生成的序列號保存在服務器本身的數據庫中。
4.1 序列號申請模塊
服務器計算出序列號的過程可用公式:序列號=F(用戶ID)來表示。其中用戶ID代表了用戶的個人信息,可以是電話號碼或者是銀行卡號等信息。但這些信息很容易被重復使用并且無法保證是否是用戶本人,本文采取提取用戶機器的硬件序列號(CPU ID和MAC地址)作為用戶ID來保證其惟一性和不可傳播性。由于用戶的硬件基本上不會隨便更換,因此可以保證用戶在使用軟件的過程中不會因此產生麻煩。公式中F代表的是根據用戶ID生成序列號所使用的算法,本文中使用的是MD5消息摘要算法。
序列號申請模塊主要分為生成用戶ID和序列號的生成與驗證兩個部分。
4.1.1 生成用戶ID的方式
本文采用了CPU ID和MAC地址共同作為用戶ID,但是在獲得了CPU ID和網卡MAC地址后需要構造一個可靠的用戶ID,若是簡單的將其連接必然會造成安全性上的欠缺,又因為通用的MD5算法是公開的,因此如果對用戶ID 的構成方式進行復雜化則可以迷惑攻擊者無法輕易的推測出用戶ID 的構造方式。本文通過采取將CPU ID和MAC地址交叉組合的方式,并在其后添加一個附加的自定義字串。例如CPU ID為abcdefgh而MAC地址為12345678則構成的用戶ID為a1b2c3d4e5f6g7h803914256。
4.1.2 序列號的生成與驗證
本文采用MD5消息摘要算法來生成序列號。MD5算法的特點[5]包括:易于從原始信息計算出MD5值,而從MD5值無法逆推算出原始數據,任意長的數據計算后得出的MD5值都是固定長度的。對于序列號的生成過程和校驗過程在計算上是同一個過程??蛻舳藱C器先在機器內計算出自己的用戶ID并將其發送給服務器端,當服務器端收到了用戶ID后根據收到的用戶ID計算其MD5值,并以此作為合法的序列號返回給用戶。如果用戶收到了序列號后,再在本地重復這一過程,根據同一個公式去計算本地的用戶ID是否與所得到的序列號相同。
4.2 程序運行結果
程序運行結果如圖5所示。程序左側的文本框采用獨立線程每隔三秒會顯示程序是否運行在合法狀態,即使破解者找到了注冊序列號的位置繞過了最初注冊時的驗證,但是每次運行程序時,隨機出現的校驗位置的狀態組合都不一樣,如果攻擊者并不知曉序列號而只是繞過了驗證部分,則在此處無法再次通過驗證因此程序提示用戶使用的是未注冊版本,進而可以保護主程序無法輕易的被破解和非法使用。程序下方還顯示了當前五個開關的狀態,可以看出,當前的開關狀態為“10110”,也就是說,主程序中的有五個位置需要進行序列號校驗,而在程序運行完一次后只有第一個、第三個和第四個位置發生了校驗,而第二個和第五個位置的校驗代碼并沒有運行。多次運行得到的開關狀態并不相同,具有隨機性。
5 結束語
本文提出了一種新的抵抗動態分析的軟件保護模型,并在LINXU平臺下采用QT和C++編程實現了該系統,經過實驗驗證能完整地提供序列號保護所需的功能,并較傳統序列號保護方式更具有復雜性和隨機性。該系統可以較好地抵御攻擊者對程序的動態分析過程。該程序結構簡單,卻能大大增加攻擊者的破解難度和破解代價,并且其結構的復雜度可以自己控制。因此,該系統具有較好的實用性和應用前景。本系統還有需要改進的地方,如太過于頻繁的驗證可能影響計算的性能,用戶如果更換硬件則需要更換序列號等問題,這些將是下一步需要研究的問題。
參考文獻:
[1] Business Software Alliance. The Compliance Gap BSA GLOBAL SOFTWARE SURVEY[R/OL].Washington, DC: Business Software Alliance, 2014. [2014-06].http://globalstudy.bsa.org/2013/downloads/studies/2013GlobalSurvey_Study_en.pdf
[2] 許旭,潘志剛.一種基于一機一碼的軟件激活序列號生成方案[J].浙江科技學院學報,2010.4.
[3] 龐啟寧.一種基于注冊碼的軟件加密算法[J].通信與廣播電視,2008.2.
[4] Christian Collberg,Jasvir Nagra,Surreption Software[M].人民郵電出版社,2012.
[5] 許琪.MD5加密算法的研究[J].福建電腦,2014.3.