做过Unity bundle资源的同学都知道AssetStudio可以轻易的的破解bundle获取到项目里的原始资源,为了防止自己的项目资源被人使用AssetStudio轻易的获取到,上网研究了下可行的Bundle加密方案,总结有以下两种方式:
1.使用位的异或运算bundle的字节码去加解密,普及下为什么位的异或运算可以实现加解密,因为1 ^ 0 = 1;0 ^ 0 = 0;所以x ^ 0 = x 可实现数据的保持, 位运算中的异或运算满足交换律,所以B ^ A ^ A = B ^ 0 = B,所以在bundle资源打完之后读取bundle的字节数据去异或你的密钥生成加密过后的文件,在加载bundle的时候先读取加密过后的文件的字节数据再去异或一次你的密钥得到原始的bundle字节数据,再使用 AssetBundle.LoadFromMemory(byte[] binary)加载出对应的bundle,此方案是可以实现bundle的加解密,但是上线的项目要考虑性能的问题,该方案在内存性能方面不能满足要求,所以实际线上项目一般不使用该方案。
2.还是读取打完bundle的字节数据,然后重新创建对应的文件在头部先写入一段加密的字节数据,再加入bundle的字节数据,在加载bundle的时候使用AssetBundle.LoadFromFile(string path, uint crc, ulong offset) 接口传入你加密的字节数据的长度为offset,也可以加载出对应的bundle,该方案在性能方面基本上没啥另外的消耗,也是本人最终使用的方案。下面贴出对应的加密代码,有兴趣的可以参考借鉴。
[MenuItem("Tools/Encode Bundles", false, 901)]
public static void EncodeBundles()
{
string bundleDir = Application.dataPath + "/StreamingAssets/xxxx"; //你的bundle文件夹所在路径
DirectoryInfo dirInfo = new DirectoryInfo(bundleDir);
string currentDir = dirInfo.Name;
string encodeDir = Path.Combine(dirInfo.Parent.FullName, currentDir + "_encode");
if (Directory.Exists(encodeDir))
{
Directory.Delete(encodeDir, true);
}
Directory.CreateDirectory(encodeDir);
FileInfo[] abs = dirInfo.GetFiles("*", SearchOption.AllDirectories);
for (int i = 0; i < abs.Length; i++)
{
FileInfo bundleFile = abs[i];
if (bundleFile.Extension.Equals(".meta") || bundleFile.Extension.Equals(".manifest"))
{
continue;
}
string newDir = bundleFile.DirectoryName.Replace("\\", "/").Replace(bundleDir, encodeDir);
if (!Directory.Exists(newDir))
{
Directory.CreateDirectory(newDir);
}
byte[] salt = "密钥字符串".StringToBytes(); //可通过其它方式生成不同的salt,这里只是简单例子
byte[] src = File.ReadAllBytes(Path.Combine(bundleFile.DirectoryName, bundleFile.Name));
byte[] buffer = new byte[src.Length + salt.Length];
Array.Copy(salt, buffer, salt.Length);
Array.Copy(src, 0, buffer, salt.Length, src.Length);
string newFile = Path.Combine(newDir, bundleFile.Name);
FileStream fs = new FileStream(newFile, FileMode.CreateNew);
fs.Write(buffer, 0, buffer.Length);
fs.Close();
}
AssetDatabase.Refresh();
}
把bundleDir修改为自己项目的bundle所在路径,处理完会在同级目录下生成对应的同名_encode文件夹,存放加过密的bundle, 其中salt可以替换成另外的生成方式,这里只是简单处理。最后再调用AssetBundle.LoadFromFile(path, 0, salt长度)来加载对应的bundle资源。
注意以下问题:
1.考虑到出包以及热更新,所以不要把原始的bundle目录打在StreamingAssets目录下,而是把加密过后的文件夹放到StreamingAssets目录下打包用,这样别人解压你的包即使拿到bundle文件,也无法轻易用AssetStudio获取到原始资源。
2.如果使用bundle的MD5码记录与对比来实现热更新资源,这时候就应该使用的是加密过后的bundle文件MD5码,而不是原始bundle, 每次热更增量打包后都进行加密处理,保证你的密钥salt不变。
3.试过把salt加在末尾而不是头部是行不通,AssetStudio还是可以解析出原始资源,所以salt还是只能放置头部。