列舉工作以來遇到的各種類型的軟件所采用的代碼保護技術,只講原理不涉及技術細節實現,以避免產生法律問題。有些朋友說直接把代碼放在Github開源下載,開源可以促進技術交流與進步,然而值錢的代碼都積壓在硬盤里面,即使很爛的代碼都賣了很多錢,贏得了許多客戶與市場。珍惜愛護自己寫的代碼,他們都是寶貴的財富。
以下保護技術主要測重于脫機驗證與保護,不涉及聯網(連接到許可證服務器)驗證。
1 程序集混淆 Asembly obfuscate
CLR代碼的運行是即時編譯執行的,.NET編譯器只是將源代碼文件編譯成中間語言,中間語言包含豐富的元數據。使用.NET Reflector/ILSpy/JustCode這類的反編譯器幾乎可以還原整個源代碼,所以用.NET開發軟件第一個要做的任務是混淆編譯后的中間語言代碼。這也是我見得最多的.NET代碼保護技術。
2 寫注冊表 Registry
雖然.NET時代強調XCOPY部屬,但很多很程序依然依賴于注冊表來做保護技術的基礎。可以把自己想要隱藏的信息放到很深的注冊表鍵中。
一些程序會將注冊表鍵做一個字符串處理,比如將字符串改成16進制值,或是做一個DES混淆處理,在不知道解密鍵(KEY)的情況下很難看出它的意思。
3 RSA 簽名 RSA Signature
RSA簽名技術可以生成二個密匙,私匙用于生成許可文件,公匙用于驗證許可。
string publicKey = "<BitStrength>1024</BitStrength><RSAKeyValue><Modulus>wDSWDT4ujpa+B7VkwdXGDvIEDc18O6qSnGtL38prVR18B7sjqbhR2Uq1C7hksADnF
DH+PAHv9BAWiqa1cWeaVgCwC2LNMClaJu4jZg0aFDIzhZUNHV56KS5aaVD+MHH+iEGmPok9XRz7Jd8hyjD38jHl439Uaf6oltxMRdKS5KU=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
string privateKey = "<BitStrength>1024</BitStrength><RSAKeyValue><Modulus>wDSWDT4ujpa+B7VkwdXGDvIEDc18O6qSnGtL38prVR18B7sjqbhR2Uq1C7hksADnFDH+PAHv9B
AWiqa1cWeaVgCwC2LNMClaJu4jZg0aFDIzhZUNHV56KS5aaVD+MHH+iEGmPok9XRz7Jd8hyjD38jHl439Uaf6oltxMRdKS5KU=</Modulus><Exponent>AQAB</Exponent><P>82nuszkXKLx
+NDtQLiruBlDGozAUaJ50PqHKq7jglLV740jz521+v2O6EVA+59gpGSwFrX6E7HnUrkKBQGbY/w==</P><Q>yiTO+1+xaNvmEcAyKdFboNRbP1/toUnakkREKyYCg/KLnjgpkb22gMtu
u5ocbfY2vIdyDZbYYeXmLYSJ9Io+Ww==</Q><DP>ds7Zx3iyKRv3rZ7Vv/MMQuDiU3yAOaA9tORbe/29AFiko4dUJT14hAo1I4Y7bgY/6R1nmAsM7i9486VaWQjaGQ==</DP><DQ>sRU
1zbiy7i1Vi09XopKpNmdR2F7tCVJti50KKtKNeZHNBbolkGslqgaM5wPGy/3ZTadKHuV6gaio0E8/m15P6Q==</DQ><InverseQ>YsZNcptrcalrlR5TS+rJ+m9G8+xUIJGGLSB3Czjw
q28rFgXa//avpx05E3FIN0tfAOKJhy84VKElqPNyQyacNA==</InverseQ><D>jaNy3Clxl6QgP3/90xWc0ZCpSh6eKT4GsnwjOrRpKh0DNJNEsaJhtpXmGs/0avsPToOUzVXEJP/iDKTTWtG
1GdkqZp+gaqKbp73RP21Nmd+lZ/S1WvmUTe3Ge5I5FP/7zf9KnTXBm1yLel/9N5UL6o5EncPNjqUt2+oKQ+q7vJU=</D></RSAKeyValue>";
因為私匙只存在于自己的電腦中,不會發布到程序中,只要私匙不泄露出去,這種加密方法是相當安全的。
破解RSA簽名的唯一方法是替換公匙,用自己生成的私匙生成許可文件。
4 參數保護 Parameter Protection
基于第3條RSA簽名技術,簽名之后產生的字符串是只讀的,在它的基礎上我們可以增加許多控制點,比如可使用的用戶數,過期時間,最大賬套數等。
在我的Enterprise Solution開發框架中,提供基于第3條和第4條為原理的許可保護技術的例子,在一定程度上保護產品不被未授權用戶使用。
5 機器硬件識別 Hardware Recognition
為了控制軟件不會被客戶隨意拷貝分發,一方面通過法律合同條文明確禁止,但通常這沒有什么卵用,另一方面則必須做硬件識別。在生成許可文件之前,要依賴于客戶的機器配置,將客戶的機器硬件信息完整的保存到許可文件中,運行時逐條檢測,發現有不一致的地方立刻退出應用程序。
.NET 提供了WMI接口用于讀取機器相關的信息,參數如下的代碼例子:
private static string GetDiskDriveSize()
{
return WmiHelper.GetWmiPropertyValue("Win32_DiskDrive", "Size");
}
private static string GetDiskDriveTotalSectors()
{
return WmiHelper.GetWmiPropertyValue("Win32_DiskDrive", "TotalSectors");
}
private static string GetDiskDriveTotalTracks()
{
return WmiHelper.GetWmiPropertyValue("Win32_BaseBoard", "Manufacturer");
}
三個方法分別讀取硬盤大小,硬盤分區,主板制造商,將這些信息組合起來放到一個字符串文件中,即是電腦的硬件信息,再對這個字符串做一個DES對稱加密或是RSA簽名處理,處理之后,客戶的硬件信息看起來是這樣的字符串。
lxocnnn7ofk2+Nheb7Qo+cBRDlCbVWHCfVSFG/XnyT3F7q/avyRL3Lj7gotPMtuo41A33AjSryt5a1ydvu7tTOnsOsizy8HJIrP13T5jHbgyHZgZaQk54eGLN3eft5zijKT3hyirZ5w2Mf6mWmv/9F
68jFxsnNh//OUUAVI3lx0=KMw2gRnZ8BqL4lNw4A5j+tuDlvbsj5gp3CGZNbAXlFlGW188uliAZexb6GhcvB0VSwTV37R6NUstT/VT5MJ7wyf/zyyHpa1uHGx4/UziLCAwawnap90BkQWlH1aVz
2lTX3I93iKKYOdAJF0y5TM9njlVvqToj2cBBWLUG+sYXxo=DUjIpSbrp5cl8L//WbIs7ErSX8RBTeVMFO7OCIYoTh7yogVzJNH3D3QZHYKnNfGmJDMSleJSgdLb0GdiWdEgiLTf0uv7lQsoA076
WM5VrtG7O/h0E3DvmisP7OkkuJIMEIpExaM+3GDlIHGvHYRK0nDfzBQM8AXM2k0ue8wj0xY=k5CRv+IkEdB6yZEy65iLXsU0lQT1FbHwSH7LawWBoOZHJt1TwIAajHuqH0AHAWZgRHYDbHCxnlJFv9t
OepsJdNGL6CvjUEnyuNA3RlhKNB6K51rOjIQ1ROKRiy9z+YsbHbJxdbsj3km/kITzfHvoDqQIg4zrTR0XfSn49j0rMkI=C4oBw14/k1OWPzMC1SUe+YxrJtyUqzL2k/cKz/feAhZ0DELbUiCUcbjhHg
+ryg+E7P5Bfy53n3kH5SBud4rVBpRUZGx4xZFPB8eXQ40SUCMxs74cbQRUPh7+zQo6qF/AtrQkXKbzoiZKHCz50n6h/afaa+jwuOplYUx191269w4=RGDYBw6j5VXJO7phILX1zLRcbO3+6SrBuCLEFwk
sitOk/z+OGtFEoIfHD5DZRp9t6GVYtpdI7jPVz7lTV4DL5xfCQ8woJjpqUdB++pYaIw3gx9EL6PD+ucRfLKtiNng6ujKe0r6A51y9eGJRic9p8uAPGSEj8OtNLmO0mVWcwhE=GJg9mdr/YdDMZCVe0WPb3
S8YGKfzdPYj3xJTQtRYdFLV9E6GHzQj8FFgIrxjdoD3n9FGthhF82MTpjS3kOdIyQEc7moienSbzqevcUPs4ZmDwmveH/F4DSynS7JAiPqCcnAj7N4EI/PkXZWAtdHSra2yWBkEBw5PlVhLTLbcLGE=FL
+JSw0yGUIpIZUgq8VRt27e5J+D0vRoEw9t5NKPROysnGw=YN1yhy2reEhQPs30aCnOpNDz2
He2E9re26QBwipNgivHM8Z/FcLk0hjALeiC8zY4Dcn+0PKag6DH7tmIBwTpBus2+ZqjLlgIx7uKMPB49+yPC5qOV8vsJGV/s81xVEA+prgNbh9Sa3TSsBV3qMSyZDEyvkHc+1QbBoLd/1h2WUI=p0/G
lIKMQ7XoRom9njDq5XPn3sj4V8P137v6eDDq7uTpRcTXD/qOWi/IZMcNX56LqVb1PJko0BCUYXa8h0zzEvsmTJNXUzggZlpkzk4EOzq4GekTwM6hXcn0Es/6iZ2J5Et1h5kSOOk1MLZS3cmUHeSRn0js
Ed0O/+L8+qBNpYapf6GL2iYf5j/Qfi+2djOwqCspHawZcBZ/e/WtK0b/0F4=lmHD5BVilXA7KIQk8yma7d4dP5mawdNhMEuYG68sN15SJOUMZlGiFT17rh0Y7Z
54itnjVmhaRVXBsc/LDKYPIwsD+TZBTGDbSOebmxvDe+Jdh6ZLsmGUIdREH4/L19y1713PyJ2r6HYi9v/8GobkWWg4chIKggBhSYUJFP8jj4Q=JHNsEDYacXEIwWhR
nDfiQoWZbrZjmCgF7lo9g4BCTYD0KbLLRKdrDERNPTQs+e3kLf0/fhc0x0BsE/yCuYcnj8rh9/iA08ZUxO+TZkz/p5rseWklWboc45o90q/ZXfuu5f+TCVPnfTXCxMjwdH3
F4I7EotPzweUS/P++YDvHHWA=d8l5s9fNa0fUX4eo9LoHVT4Wb8YbHKDvDW7o1P+wTDykIrOCLoCKklrSGdHjOi8EWw=LDrOGw3pq7rv4L7YoGLaZAmXfRdcw1wp2Zi/Hn2
這樣既避免了客戶隱私信息泄露,也可以解決當前軟件氛圍環境下軟件分發的困擾。
6 序列號/注冊碼 Serial Key
相當多的軟件還是采取這一傳統的序列號保護方法,比如Windows Server 2012的注冊碼:
NB4WH-BBBYV-3MPPC-9RCMV-46XCB
一組看似無序的數字與字母的組合,對軟件保護起了很重要的作用。如果是在軟件安裝時驗證序列號,則破解難度會更大。
序列上面的字母數字組合,可以包含很多信息,比如客戶名稱,過期時間,最大用戶數。將這樣的序列號分享給同行的其它客戶,一旦軟件公司追查起來很容易就能識別這個序列號是否合法。
在我的博客下載工具中用到的一個組件aspNetMHT,它就是采用這個方法來分發序列號的,序列號中包含過期時間。aspNetMHT的序列號看起來是這樣的:
SM48Z-FMXGZ-25P67-4ZJKF-GS211-AQYA4-7VHUX-KCF1C-RD4RC-RU7XS-XK8JY-6JT54-M9CX
7 強名稱 Strong Name
強名稱不是一種代碼保護技術,而是程序集標識技術。每個經過strong name簽名后的程序集都是唯一的,在運行時可以驗證這個文件的簽名是否被改動過。
驗證強名稱的關鍵地方在于調用CLR中的基礎方法:
[DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
public static extern bool StrongNameSignatureVerificationEx(string wszFilePath, bool fForceVerification, ref bool pfWasVerified);
所以開發.NET程序,不僅僅要給程序簽名,還需要在運行時驗證所給的簽名是否被改動過。
.NET CLR支持對程序集跳過強名稱驗證,不過這樣做的風險太大,直接修改了CLR的行為,未見有很多實例。
8 內存保護 Memory Protection
Delphi XE采取了這種方法,運行時會生成一段驗證代碼,驗證結束后代碼銷毀,因為驗證在內存中進行,不會在硬盤上留下痕跡,破解的難度相對困難了很多。
Win32支持動態加載和卸載程序集,所以Win32 API中有接口LoadLibrary和UnloadLibrary供調用,可實現內存保護。
.NET也支持動態加載程序集,但不支持卸載(unload)程序集,程序集的卸載由CLR控制,這一點給破解內存保護技術提供了方便。
在我的Enterprise Solution開發框架中,將第7條和第8條組合一下,簡單的解釋如下:創建一個類型定義文件,然后將這個文件用強名稱簽名,之后將這個簽名的程序集經過混淆,字符串加密等處理,附加到啟動程序的資源文件中去,運行時,我從資源文件中將此程序集解析釋放出來,同時強制驗證程序集的強名稱,運行類型定義文件中指定的驗證方法,實現簡單的內存保護技術。
破解這種保護需要知道簽名程序集的強名稱,字符串加密處理的全流程和相應的鍵,給破解增加了不少難度。要驗證什么和怎么驗證都是在我設計的類型定義文件中指定的,啟動程序只是一個殼,運用反射調用要驗證方法。而且這個經過簽名后的程序集是附加到主程序中去的,要替換這個資源文件,也需要一些方法和技巧。
9 源代碼混淆 Source code obfuscate
經過編譯混淆后的.NET程序集,在運行時依舊可以被調試器附加調試。如果我能將源代碼中的敏感字符串替換成亂七八糟的字符串,減少明文的出現,雖然降低了性能,但這樣可以增加安全性。比如,我運行時檢測找不到許可文件時,拋出以下異常:
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"license.lic");
if (!File.Exists(path))
throw new LicenseException("License file not found");
經過源代碼敏感字符串替換后的程序如下:
string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"license.lic");
if (!File.Exists(path))
throw new LicenseException(B("/jNloqd4U59LBT64eX1hb9eO8kYDD7tD"));
經過一個B方法的調用,無法看到異常的明文,增加了破解難度。如果代碼中這樣的B方法有很多,而且B方法的字符串處理方式各不相同,無疑提高了系統的安全性。
現在市場上有很多JavaScript源代碼混淆工具,就是把JavaScript代碼的格式打亂,讓它不容易讀取。
C#源代碼也可以采用此方法,不過,讀強大的Visual Studio有文檔格式化功能(Edit-Advanced- Format Document ,Ctrl + E,D) ,瞬間變為可讀的格式。
10 本機代碼 Native Code
即將發布的Visual Studio 2015提供.NET Native功能,可將.NET代碼編譯為本機代碼。然而經過近一年的等待,.NET Native目前也是僅僅限定于Windows應用程序商店項目,真正的Windows Forms/WPF/ASP.NET要實現編譯為本機代碼可能不現實。
這種保護方法的核心是許可驗證邏輯放在本機代碼中,直接編譯成機器碼。一般采用Visual C++/Delphi/VB6之類的工具,將重要的算法與驗證編譯成機器碼,這樣會增加破解難度。本機代碼難于處理的一個方面是.NET Any CPU的問題。當我們設定.NET編譯為Any CPU時,CLLRU會依據程序運行的平臺(x86,x64)編譯為本機代碼。
在x64的系統中加載32位的本機代碼,常常會拋出無效的程序集異常。破解這種保護的方法可直接修改.NET程序中的調用方法,可以忽略本機代碼的邏輯。
相對于前面的幾種技術,這種保護技術破解難度更大,要懂的知識點也非常多。
![]() |
不含病毒。www.avast.com |