軟件保護之註冊算法篇

一、前言

通常地,軟件保護可分爲三大環節:反調試、抗分析、防爆破

1、反調試,給你的軟件引入非常規的運行機制,例如加殼,來限制調試工具對你的軟件的掌控
   能力。如今調試工具越來越強大,但調試工具的設計目標畢竟是光明正大的,大多不會專門
   防備反調試技術。所以反調試技術還是相當重要的:它能夠大大減少你的潛在對手。畢竟,
   如果調試工具能夠很輕易地支配你的軟件,則任何一個程序員都可能成爲cracker,而普通程
   序員的隊伍何其龐大?所以,反調試是軟件保護的第一道防線。它已基本實現標準化:加殼
   工具已經滿天飛了。

2、抗分析,假如你不幸遇到對win32應用環境有足夠了解的對手,以至於你的軟件最終還是被兇
   悍的調試器任意蹂躪,你也還遠沒有被打敗,你還有第二道防線可守——抗分析。在這道防
   線裏,你有很多辦法可以限制cracker掌握你的註冊算法,從而阻止註冊機的出現:註冊機的
   出現意味着你被徹底打敗,所以第二道防線是你必須固守的。

3、防爆破,如果你的第二道防線足夠堅固,往往會導致你的對手放棄註冊,他們會試圖直接修
   改你的可執行文件,強行改變它的執行流程,比如讓它不註冊也能正常運行。所以你還需要
   部署第三道防線,教訓這些爆破者。防範爆破的手段很多,而且事實上你的防爆能力與你的
   第二道防線的架構直接相關,所以總體來說,第二道防線還是最重要的。

本文針對的是第二道防線,目的在於阻止cracker掌握你的註冊算法。

註冊算法的目的是隻向合法用戶提供完整的功能,通常採用註冊碼驗證的方式實現:

1、用戶向軟件作者提交用戶碼U,申請註冊。
2、軟件作者計算出註冊碼R=f(U),回覆給合法用戶。
3、用戶在軟件註冊界面輸入U和R。
4、軟件驗證F(U,R)=0來判定用戶的合法性。

其中一些常用術語說明如下:

1、用戶碼U:用於區別用戶身份。它可能僅僅是用戶自定義的一個用戶名,這種形式的用戶碼與
   用戶身份弱相關,假如一對合法的用戶碼、註冊碼被公開,則任何人都可以用來對軟件進行
   註冊;它也可能是用戶機器的硬件特徵碼,這種形式的用戶碼與用戶身份強相關,可以有效
   防止一次註冊,多人享用的局面出現,但對於合法用戶而言很不方便,一旦更換或升級機器,
   就必須重新申清註冊碼。

2、註冊碼R:用於驗證用戶身份。它可能與用戶碼具有惟一對應關係,也可能同一個用戶碼有若
   幹個註冊碼相對應,也可能同一個註冊碼有若干個用戶碼相對應。如果用戶碼和註冊碼的取
   值空間非常大,即使用戶碼與註冊碼不是惟一對應關係,非法用戶“碰巧”得到一對合法的
   用戶碼、註冊碼的概率也是微乎其微。

3、註冊機:我們把R=f(U)中的小f稱爲註冊機,掌握了註冊機就有能力針對任何用戶碼計算出
   相應的註冊碼。

4、驗證機:我們把F(U,R)中的大F稱爲驗證機,軟件使用驗證機驗證註冊碼的合法性,即,
   當且僅當R=f(U)成立時,F(U,R)=0。

在軟件註冊保護的“初級階段”,驗證機與註冊機沒有本質區別,即: F(U,R)=f(U)-R。所以
cracker甚至可以直接讓你的軟件擔當註冊機的作用,根本不需要關心算法過程,也就是所謂的
“內存註冊機”。

改進的做法是先求出f的反函數f',使:U=f'(R),然後令F(U,R)=f'(R)-U。這樣做安全了許多,
軟件本身不包含註冊機f,破解者必須在充分了解f'算法過程的墓礎上才能正確推導出註冊機f。

更進一步的做法是構造f、f',使得通過f'求f在計算上不可行,或構造F,使F與f、f'都不直接
相關。下面我們將一起來研究如何運用“堡壘戰術”、“游擊戰術”和“陷阱戰術”三大戰術來
實現以上手段。


二、堡壘戰術

事實上,在通信領域人們很早就開始了身份驗證的研究,並發展出了一系列優秀的密碼學算法,
其中MD5算法和RSA算法非常適合在軟件註冊算法中運用。

MD5散列算法的特點是:

1、任意長度的信息都可變換成一個固定長度(128位)的“信息摘要”。
2、不同信息造成相同信息摘要的情況稱爲“衝突”,衝突肯定存在,但鮮有實例發現。
3、由信息摘要無法逆推信息。

MD5算法並不適合直接被用來做註冊機,假如使用R=MD5(U)做註冊機,由於MD5不存在反函數,驗
證機將不得不包含註冊機。但是我們用以下辦法使用MD5算法:

1、設註冊機:R=f(U)
2、設MD5(a)=b
3、令驗證機爲:F(U,R)=MD5(f'(R)-U+a)-b

例如:U=f'(R)=R*5+19,a=7,則F(U,R)=MD5(R*5+26-U),a和f'融爲一體。由於MD5算法不可逆,
破解者無法通過b求出a,就無法求出f',更無法獲得註冊機。

實際上利用MD5算法還有很多方法可以構造F,讓破解者根本看不出F和f、f'的關係,相信讀者完
全能夠進行精彩的發揮。當然,M D5算法也有它的缺陷,正因爲MD5不可逆,所以a必須爲常數,
一旦破解者獲得了一對合法的U、R,他們就可以跟蹤到合法的a、b值,從而獲得f',並進一步推
導出注朋機f,但至少,他必須先獲得一對合法的U、R。

RSA非對稱算法的原理是:

1、選擇兩個互不相同的素數p、q,令n=p*q ,m=(p-1)*(q-1)
2、選擇素數e3、計算d滿足e^d mod m=1 
4、對於任意A
5、由e推算d的前提是因式分解n得到p、q,當n巨大,例如1024位時,分解n屬於世界級數學難題。

RSA 算法很容易在軟件保護中進行應用:

1、使用R=U^d mod n作爲註冊機
2、使用U=R^e mod n作爲驗證機

破解者即使跟蹤軟件運行的全過程,也得不到d,無法寫出註冊機。基本上使用RSA算法進行保護
的軟件可以說是建立了一座堅不可摧的堡壘,但是我們都知道馬其頓防線的故事,由於RSA算法衆
所周知,再加上RSA算法實現起來並不容易,使用者通常會採用公共函數庫,所以使用RSA算法也
存在一些風險:

1、RSA算法本身雖然足夠堅固,但使用者往往全盤採用第三方代碼。這些代碼可能含有漏洞,而
   全世界有大量的資深破解者在研究這些代碼的漏洞,一旦發現漏洞,軟件的安全性就可能成
   爲陪葬品,因爲每一種公開的固定代碼都必然包含一些特徵串,通過搜索特徵串可以輕鬆獲
   知哪些軟件使用了含有缺陷的代碼。

2、RSA算法的使用者往往並不瞭解算法細節,可能因錯誤使用RSA而在不知不覺之中遭遇非常規
   手段的攻擊,例如在不同的軟件作品中使用不同的e、d但使用相同的n而遭到“公共模組攻擊”,
   或在電子郵件等通信加密領域使用e、d、n而遭到“選擇密文攻擊”等等。

3、RSA算法源於通信領域,用於防止竊聽者在獲取B、e、n後推算出d從而使用A=B^d mod n獲取
   A,但實際在解密端通常並不使用A=B^d mod n解密,而會使用“中國剩餘定理”加速,而中
   國剩餘定理中包含對p、q的引用。因此用於通信領域的RSA函數庫並不適合軟件註冊算法,一
   旦軟件作者選擇了錯誤的RSA庫函數,在驗證函數中使用了中國剩餘定理,則會導致RSA 防線
   如同虛設。

雖然對軟件實施RSA保護存在一定的風險,但是畢竟RSA算法非常複雜,可以樂觀地估計,RSA足
以讓90%以上的破解者面對晦澀的彙編代碼好一陣子找不到北。事實上密碼學知識在軟件保護上
大有用武之地,各位如果潛心加以研究,一定還會發現“堡壘”其實還有很多種建造方法。


三、游擊戰術

游擊戰第一宗旨:化整爲零。對付強大的對手非常有效。而軟件保護中的游擊戰術就是將驗證函
數F“肢解”成多個互不相同的Fi ,然後將這些 Fi 儘可能藏到隱蔽的程序角落。通過任意一個
Fi的驗證都只是註冊碼合法的必要條件,而非充分條件,真正合法的註冊碼能夠通過所有的Fi的
驗證。破解者找到Fi其中的任一個或任意幾個,只要不能將所有的Fi一網打盡,他就無法一睹F的
全貌,無法得到註冊機。當然,將F分解成一系列必要非充分的Fi也不是一件容易的事情,需要較
專業的數學知識,但是我們至少可以使用分段函數來簡單地實現這一目標:

1、將R切分成多段Ri
2、構造不同的f算法使得:Ri=fi(U) 
3、令Fi=fi反函數fi'

這樣做雖然有點麻煩,但絕對是值得的。例如我們可以讓F1使用MD5算法,F2使用RSA算法,F3使
用自定義不知名算法,在用戶輸人註冊碼後僅僅使用F1進行驗證,如果驗證通過(不妨假定破解
者總是有辦法讓它通過的)就恭喜註冊成功。另外兩個驗證函數藏起來,只有使用者執行特定的
操作時才被調用,例如在用戶進行存檔操作或使用某些高級功能的時候將註冊碼讀出來再次驗證,
筆者甚至碰到過一個軟件在軟件被關閉時響應窗口Destory消息來調用一個驗證函數。一旦任一個
驗證函數發現註冊碼非法,就將軟件恢復爲未註冊狀態,甚至可以更極端地選擇“自殺”。


游擊戰第二宗旨:虛虛實實。對於破解者來說,遇到游擊戰術會非常被動,除非他找到的驗證函
數已經能夠將U 、R形成一對一的對應關係,否則永遠不能確定軟件中是否還埋藏着其他的驗證函
數,而事實上軟件作者根本沒有必要讓U、R形成一對一的對應關係,驗證函數個數的不確定性的
確很容易讓試圖製作註冊機的破解者懊惱不已。假如運用一點簡單的線性代數的知識,我們可以
將Ri的其中幾個(注意只是其中幾個,而不是全部)和 Fi關聯起來:例如設:

R1=3U
R2=5U
R3=7U

則:

F1=7R1+11R2+5R3-111U
F2=11R1+7R2+3R3-89U
F3=5R1+3R2+11R3-107U

這樣破解者找到F1、F2、F3中的任意一個,甚至無法求出R哪怕是小小的一段。一個更好的主意是
讓參與到線性方程組中的Ri的個數稍稍大於使用線性方程的驗證函數的個數,軟件作者手裏持有線
性方程的某一組特定解作爲註冊機,而破解者則無法瞭解驗證函數到底有幾個,就像鬼子永遠搞不
清楚八路軍到底有多少,以至於最後歇斯底里地見人就認爲是“八路的千活”。

如果將一對U、R作爲縱橫座標,看作平面上的一點,將註冊機f看作由合法U、R連成的一段平面曲
線,我們還可以構造多個空間曲面方程作爲驗證函數F,條件是f落在這些空間曲面之上,如果稍稍
瞭解空間解析幾何的知識,相信各位可以構造出無數個曲面方程作爲驗證函數,甚至還可以考慮使
用參數方程,這樣即使破解者獲得了所有的Fi,也要有精深的解幾功力才能求出f。

我們必須反覆強調數學知識的重要性,不管是數論、代數、線代、幾何、解幾,還是微積分(個人
認爲利用博立葉變換作驗證函數將會非常有趣)、概率論,都可以拿來作爲軟件保護的武器。請相
信,如果你其備了3成的數學功力,就足以將6成功力以下的對手摺磨致死,因爲你和他掌握着不對
稱的信息量。


游擊戰第三宗旨:戰略轉移。游擊隊也經常會被鬼子掃蕩,因爲游擊隊的行蹤經常會被漢奸告密。
我們的游擊戰術的致命弱點在於,每一個驗證函數都必須訪問註冊碼,而註冊碼的源頭只有一個:
用戶從註冊界面輸人的那一個。破解者會跟蹤程序從註冊界面讀人註冊碼的過程,並監控存放註冊
碼的內存地址,一旦驗證函數訪問這一地址就會泄漏行蹤,這樣註冊碼買際上成爲了破解者尋找驗
證函數的一把鑰匙。理論上他只要牢牢地抓着這把鑰匙不放,就一定會找到所有的驗證函數。應對
的辦法就是大規模的轉移,游擊戰嘛,就是要能“跑”,將鬼子拖死、累死,軟件必須不停的將注
冊碼“搬家”,搬家的方法要多樣化:

1、內存拷貝,這種常規做法容易被破解者的內存監控斷點識破。
2、寫入註冊表或文件,然後在另一處代碼中再讀入到另一個內存地址,這種辦法會被破解者的註冊
   表、文件監視工具識破。
3、一次將註冊碼拷貝到多個地址,讓破解者無法確定哪一個地址是註冊碼的新家,當然如果敵人堅
   忍不拔,個個都追,至少也可以消耗敵人大量的精力。
4、分批偷運,一次偷運一個字節,敵人難以察覺。
5、在反覆使用同一個函數搬家後突然使用另一個前半部分代碼相同而後半部分不同的函數進行搬家。
   這種方法很容易讓疲憊的敵人一不小心就將註冊碼跟丟。
6、將以上方法反覆使用,如果僅僅靠copy & paste就可以將對手逼成神經病,何樂而不爲呢?

事實上主動權永遠掌握在你的手裏,可能您會一不小心遇上一個精力過人孜孜不倦神勇無比的對手最
終將您的游擊隊一一肅清,但是請相信,只要您認真貫徹了游擊戰術的三大宗旨,靈活結合使用,一
定會讓90%以上的破解者像無頭蒼蠅一樣亂轉一氣之後惱羞成怒關機投降。


四、陷阱戰術

所謂“陷阱”,是要讓破解者誤人歧途,陷入困惑之中無法自拔,我們不應該一味試圖編制讓破解者
讀不懂的代碼,那不是一件容易的事情,而且抱着這樣的目標很容易犯自以爲是、低估對手的錯誤。
我們的任務最好是引誘對手犯自以爲是的錯誤,讓他在並沒有完全弄懂驗證過程時卻自以爲清楚了。
當然,將要介紹的“陷阱”,只是俺個人的一些研究思路,或許只能博高手一笑,但希望至少能夠對
各位有所啓發,起到拋磚引玉的作用。

第一個陷阱俺稱之爲“隨機陷阱”,原理是誰備多個驗證函數,程序每次運行時僅僅隨機調用其中的
某一個。其中隨機數的產生可以放在程序頭,由於應用程序通常會有大量的數據初始化工作,再加上
隨機數產生函數代碼本身也比較晦澀難懂,夾雜在其中生成一個隨機數應該是很隱蔽的。這樣破解者
在跟蹤程序之後通常會自以爲程序只有一個驗證函數,於是放心地發佈了註冊機,而其他人使用該注
冊機進行註冊時,由於程序生成了不同的隨機數,調用了不同的驗證函數,註冊自然失敗。不幸的是
您無法判斷您的對手的功力和習慣,有些功力一般的菜鳥(像俺這樣的)或者是某些有特殊習慣的高
手往往會將您的程序跟蹤好幾遍.甚至幾十遍,您的隨機陷阱就會被發現。所以筆者強烈建議您不妨
將其中一個驗證函數被隨機調用的概率控制在5%以下。這樣做的壞處是會出現一些並不完整的註冊機,
卻能夠讓您的軟件在註冊狀態下運行多次。好處是根據心理學家的研究,人類總是對自己關係越深卻
又無法控制的東西最感興趣,一旦某人千方百計找來一個不完整的註冊機註冊了您的軟件、他會認爲
“哈哈,現在這東西是我的了!”那種感覺當然會很好,他和您的軟件的關係在心理上的親密程度一
般絕不會比他掏腰包買來的軟件差。而一旦哪天他突然發現您的軟件識破了他的非法身份,他會很失
落,那感覺肯定會很不好,極有可能他會忍不住將“自己的東西”拿回來的衝動,哪怕是掏腰包。這
樣的話,恭喜您獲得了一個額外的註冊用戶。您還會獲得附帶的收穫:您的對手,破解者的聲譽受到
了打擊,事實上衆多的破解高手並不靠破解生活,他們在乎的其實就是所謂的聲譽,您的這一次打擊
很可能會讓他灰心喪氣,失去了繼續破解的興趣,這樣的話,恭喜您除掉了一個難纏的敵人。

第二個陷阱俺稱之爲“整數陷阱”,由於通常驗證函數都將用戶碼與註冊碼轉換成整數進行運算,我
們可以利用破解者的這一經驗作一些手腳。例如我們將用戶碼拆分成兩個整數U1、U2,註冊碼也拆
分成兩個整數R1、R2,註冊機爲:

R1=U1+115
R2=U2+351

驗證機爲:

F1:((R1-U1)^2+(R2-U2)^2)^(1/2)=369
F2:(R2-U2)/(R1-U1)=3

我們將 F1暴露,將F2隱藏。這樣破解者找到F1後首先會發現驗證過程包含了U、R的完整信息,而且F1
顯然是一個勾股方程,查表可得其唯一整數解爲:

R1-Ul=81
R2-U2=360

很容易自以爲是的認爲註冊機爲:

R1=U1+81
R2=U2+360

然而偏偏我們使用的不是F1的整數解,卻在計算機的計算下恰好滿足F1。其實可以將U看作平面座標系
上的一個點,滿足F1的是以U爲圓心,369爲半徑的圓;滿足F2的是經過U且斜率爲3的直線; F1和F2的
交點纔是合法的R。我們所學習的數學函數絕大多數都是連續函數,而計算機處理的卻是離散值,所以
這一類的陷阱很容易構造,但是破解者要識破,就不見得有那麼容易了。


五、結語

結合以上三大戰術,請相信,你完全有能力阻止註冊機的傳播。



原文地址:http://blog.csdn.net/fangle6688/article/details/1023699

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章