Unity的游戲很容易被人反編譯出來(lái)七芭,然后再重新打包發(fā)布,把自己辛辛苦苦開發(fā)的游戲蔑赘,抄寫的一絲不掛狸驳。很多項(xiàng)目要求要做好資源加密,Unity中如何做好資源加密呢缩赛?本文給大家分享加密算法+資源打包整合思路:
(1) 游戲資源加密如何選擇加密算法;
(2) Assetsbundle資源包的加密與解密;
游戲資源包如何選擇加密算法
游戲資源包加密的第一件事情就是選一個(gè)什么樣的加密/解密算法,比較不容易被人破解耙箍。我們?nèi)绾蝸?lái)選擇呢?很遺憾的告訴大家酥馍,沒有任何一個(gè)保險(xiǎn)的加密解密算法是安全的辩昆。杠精第一時(shí)間就會(huì)來(lái)反駁,這怎么可能旨袒,我加密了他怎么破解汁针?游戲資源加密注定了加密算法不能用太耗時(shí)的加密算法。接下來(lái)我們來(lái)看下不同加密算法是如何被破解的砚尽。
(1) 使用標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)的加密解密算法,比如使用encrypt加密/解密算法施无。這里有個(gè)問題,我們會(huì)有一個(gè)密鑰用來(lái)解密必孤,密鑰一般游戲都寫死到代碼里猾骡。這種破解太簡(jiǎn)單了,密鑰寫死在代碼里敷搪,基本都是字符串等,靜態(tài)分析代碼找到密鑰對(duì)應(yīng)的字符串兴想,有了密鑰,你用的是標(biāo)準(zhǔn)庫(kù),一下加密資源就破解了赡勘,分分鐘你的資源就被破解了嫂便。
(2) 使用自己實(shí)現(xiàn)的加密解密算法,比如闸与,采用二進(jìn)制亦或來(lái)做加密解密顽悼,比如,加密定一個(gè)二進(jìn)制掩碼mask, 讓每個(gè)字節(jié)都異或這個(gè)mask,得到一個(gè)新的數(shù)據(jù)几迄,這樣資源就不能被直接識(shí)別出來(lái),使用資源得時(shí)候蔚龙,再把加密數(shù)據(jù)異或一次mask在解密回來(lái)到項(xiàng)目中使用。這種破解稍微蠻煩寫映胁,需要自己反編譯你的解密代碼木羹,同時(shí)還要找到你的解密密鑰key。但是本質(zhì)也比較容易破解。
加密/解密算法示意如下:
源數(shù)據(jù)1110 0001, 密鑰為 00101000;
加密數(shù)據(jù): 1110 0001 ^ 00101000 = 1100 1001
解密數(shù)據(jù): 1100 1001 ^ 00101000 = 1110 0001
所有從上面分析來(lái)看想要不被破解坑填,幾乎是很難抛人,就看破解的代價(jià)。你這個(gè)游戲是否值得脐瑰。一般我們的目標(biāo)就是不要讓人通過Unity反編譯工具就能直接讓我們的游戲直接反編譯出Unity項(xiàng)目工程妖枚,然后重新編譯打包發(fā)布就可以了。解密算法要快苍在,注定他不會(huì)太復(fù)雜绝页,密鑰單一的放客戶端或是網(wǎng)絡(luò),注定能獲取到寂恬。
Assetsbundle資源包的加密與解密
分析完加密解密算法以后续誉,接下來(lái)我們就來(lái)看下如何加密我們的資源,先來(lái)介紹第一種方案初肉,打資源包的時(shí)候酷鸦,加密單個(gè)的資源, 把加密后的數(shù)據(jù)打入到資源包。解密的時(shí)候,從ab包中讀取資源牙咏,然后再把資源包里的內(nèi)容解密開來(lái)臼隔。具體做法如下:
(1) 新建一個(gè)類,繼承自FileStream, 重寫Read/Write函數(shù)妄壶。代碼如下:
using System.IO;
public class MyStream : FileStream {
? ? const byte KEY = 40; // 密鑰mask: 0010 1000
? ? public MyStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) :
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? base(path, mode, access, share, bufferSize, useAsync) {
? ? }
? ? public MyStream(string path, FileMode mode) : base(path, mode) {
? ? }
? ? // 重載讀接口,一邊讀躬翁,一遍解密;
? ? public override int Read(byte[] array, int offset, int count) {
? ? ? ? var index = base.Read(array, offset, count);
? ? ? ? for (int i = 0 ; i < array.Length; i++) {
? ? ? ? ? ? array[i] ^= KEY;
? ? ? ? }
? ? ? ? return index;
? ? }
? ? public override void Write(byte[] array, int offset, int count) {
? ? ? ? // 重載寫接口,先加密再寫入;
? ? ? ? for (int i = 0; i < array.Length; i ++) {
? ? ? ? ? ? array[i] ^= KEY;
? ? ? ? }
? ? ? ? base.Write(array, offset, count);
? ? }
}
(2) 修改ab包打包工具腳本,再對(duì)生成的ab包資源加密一次,打包加密腳本如下:
[MenuItem("Tools/BuildAB")]
static void BuildAB()
{
? ? …
? ? // for循環(huán)遍歷里面的打包后的每個(gè)資源Ab包盯拱,然后調(diào)用進(jìn)行加密盒发。
? ? foreach (var name in manifest.GetAllAssetBundles())
? ? {
? ? ? ? var uniqueSalt = Encoding.UTF8.GetBytes(name);
? ? ? ? var data = File.ReadAllBytes(Path.Combine(Application.streamingAssetsPath, name));
? ? ? ? using (var myStream = new MyStream(Path.Combine(Application.streamingAssetsPath, "encypt_" + name),FileMode.Create))
? ? ? ? {
? ? ? ? ? ? myStream.Write(data, 0, data.Length); // 觸發(fā)MyStream中我們重寫的write函數(shù)的調(diào)用, 完成數(shù)據(jù)加密
? ? ? ? }
? ? }
? ? AssetDatabase.Refresh();
}
(3) 解密的時(shí)候,讀取ab包,使用函數(shù)AssetsBundle.LoadFromStream來(lái)加載狡逢,而這個(gè)會(huì)觸發(fā)Stream在Read讀數(shù)據(jù)接口調(diào)用宁舰,就可以進(jìn)入我們重載的Read接口,來(lái)做解密。代碼如下:
var fileStream = new MyStream(Application.streamingAssetsPath + "/encypt_myab.unity3d", FileMode.Open, FileAccess.Read, FileShare.None, 1024 * 4, false))
{
? ? var myLoadedAssetBundle = AssetBundle.LoadFromStream(fileStream); // 觸發(fā)MyStream中的Read調(diào)用奢浑,來(lái)解密數(shù)據(jù)蛮艰。
}
今天的分享就到這里了,可以進(jìn)入學(xué)習(xí)小組一起交流游戲開發(fā)