Unity AssetBundle高效加密案例分享

这是侑虎科技第585篇文章,感谢作者江鱼供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

作者主页:https://www.zhihu.com/people/yu-jiang-3-65/,作者也是U Sparkle活动参与者,UWA欢迎更多开发朋友加入U Sparkle开发者计划,这个舞台有你更精彩!


随着AssetStudio的普及,Unity项目中使用的AssetBundle资源可以被各种小白用户解包提取,由此AssetBundle资源包的安全问题不得不引起我们重视。

过去,通过官方文档的了解,我们主要可以通过,AssetBundle.LoadFromMemory(Async)的方案来实现资源包加密,官方文档对这个方法是这样描述的:

Use this method to create an AssetBundle from an array of bytes. This is useful when you have downloaded the data with encryption and need to create the AssetBundle from the unencrypted bytes.

Compared to LoadFromMemoryAsync, this version is synchronous and will not return until it is done creating the AssetBundle object.

下面是官方文档中的示例代码:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class ExampleClass : MonoBehaviour
{
    byte[] MyDecription(byte[] binary)
    {
        byte[] decrypted = new byte[1024];
        return decrypted;
    }

    IEnumerator Start()
    {
        var uwr = UnityWebRequest.Get("http://myserver/myBundle.unity3d");
        yield return uwr.SendWebRequest();
        byte[] decryptedBytes = MyDecription(uwr.downloadHandler.data);
        AssetBundle.LoadFromMemory(decryptedBytes);
    }
}

需要注意的是,对于AssetBundle.LoadFromMemory(Async)这个方法,在官方的AssetBundle foudamentals一文中,官方又非常明确的指出:

Unity's recommendation is not to use this API.

AssetBundle.LoadFromMemoryAsync loads an AssetBundle from a managed-code byte array (byte[] in C#). It will always copy the source data from the managed-code byte array into a newly-allocated, contiguous block of native memory. If the AssetBundle is LZMA compressed, it will decompress the AssetBundle while copying. Uncompressed and LZ4-compressed AssetBundles will be copied verbatim.

The peak amount of memory consumed by this API will be at least twice the size of the AssetBundle: one copy in native memory created by the API, and one copy in the managed byte array passed to the API. Assets loaded from an AssetBundle created via this API will therefore be duplicated three times in memory: once in the managed-code byte array, once in the native-memory copy of the AssetBundle and a third time in GPU or system memory for the asset itself.

Prior to Unity 5.3.3, this API was known as AssetBundle.CreateFromMemory. Its functionality has not changed.

从官方的解释中,我们可以看到AssetBundle.LoadFromMemory(Async)的使用成本非常高昂,不被推荐是自然而然的事情。但是,有没有更高效便捷的方式去对AssetBundle进行加密处理,防止被小白用户利用AssetStudio之类的工具轻易地提取到AssetBundle的资源呢?

在查看Unity API的时候发现LoadFromFile末尾有一个offset参数,那么这个参数有什么用呢?是否可以起到防止AssetBundle资源直接被AssetStudio提取呢?先看官方文档的接口说明:

public static AssetBundle LoadFromFile(string path, uint crc, along offset);

Parameters

Returns
AssetBundle Loaded AssetBundle object or null if failed.

Description
Synchronously loads an AssetBundle from a file on disk.

The function supports bundles of any compression type. In case of lzma compression, the data will be decompressed to the memory. Uncompressed and chunk-compressed bundles can be read directly from disk.

Compared to LoadFromFileAsync, this version is synchronous and will not return until it is done creating the AssetBundle object.

This is the fastest way to load an AssetBundle.

官方文档的代码示例并没有,提供offest参数的演示,所以在这里就不搬运了,接下来我会用自己写的测试代码来做演示。

首先,我们需将XAsset生成好的AssetBundle文件内容进行偏移处理,待Unity打包完成后遍历所有AssetBundle文件,并对文件添加offset后进行覆盖,代码如下:

foreach (string bundleName in bundleNames)
{
    string filepath = outputPath + "/" + bundleName;
    // 利用 hashcode 做偏移 
    string hashcode = manifest.GetAssetBundleHash(bundleName).ToString();
    ulong offset = Utility.GetOffset(hashcode);
    if ( offset > 0)
    {
        byte[] filedata = File.ReadAllBytes(filepath);
        int filelen = ((int)offset + filedata.Length);
        byte[] buffer = new byte[filelen];
        copyHead(filedata, buffer, (uint)offset);
        copyTo(filedata, buffer, (uint)offset);
        FileStream fs = File.OpenWrite(filepath);
        fs.Write(buffer, 0, filelen);
        fs.Close();
        offsets  += filepath + " offset:" + offset + "\n";
    }
    WriteItem(stream, bundleName, filepath, hashcode);
}

然后,我们再进行加载测试,我们分别使用offset参数加载AssetBundle,和模拟解密文件后从内存中加载AssetBundle然后读取其中的一个Texture用于显示,可以参考以下代码:

// 基于offset加载AssetBundle
async void onLoadWithOffsetClicked()
{
    if (offsetBundle)
        offsetBundle.Unload(true);

    var current_memory = Profiler.GetTotalAllocatedMemoryLong();
    display_image.texture = null;
    var path = System.IO.Path.Combine(Application.streamingAssetsPath, "assets_previews_offset");
    var assetBundleRequest = AssetBundle.LoadFromFileAsync(path, 0, 294);
    await assetBundleRequest;
    var texture = assetBundleRequest.assetBundle.LoadAsset<Texture2D>("download.jpg");
    display_image.texture = texture;
    offsetBundle = assetBundleRequest.assetBundle;

    Debug.Log("Offset Load Complete:" + (Profiler.GetTotalAllocatedMemoryLong() - current_memory));
}

// 基于Menmory加载AssetBundle
async void onLoadWithMemoryClicked()
{
    if (memoryBundle)
        memoryBundle.Unload(true);

    var current_memory = Profiler.GetTotalAllocatedMemoryLong();
    display_image.texture = null;
    var path = System.IO.Path.Combine(Application.streamingAssetsPath, "assets_previews");
    WWW www = new WWW("file://" + path);
    await www;
    var request = AssetBundle.LoadFromMemoryAsync( www.bytes);
    await request;
    var texture = request.assetBundle.LoadAsset<Texture2D>("download.jpg");
    display_image.texture = texture;

    memoryBundle = request.assetBundle;
    www.Dispose();

    Debug.Log("Memory Load Complete:"+ (Profiler.GetTotalAllocatedMemoryLong() - current_memory));
}

接下来,我们再看看对以上两个函数执行的Profiler数据分析采样的结果:

测试场景截图

 

使用offset参数加载之前的内存情况

 

使用offset参数加载AssetBundle之后的内存情况

 

使用LoadFromMemory加载之前的内存情况

 

使用LoadFromMemory加载之后的内存情况

 

 

通过对比发现,使用LoadFromMemory内存明显发生了增长,并且在加载过程中还出现了一个内存高峰。

由于我们对AssetBundle的资源进行了偏移,势必在理论上,AssetStudio无法直接解析出我们Unity工程中的AssetBundle,接下来我们再来看下,我们的理论是否经得起实践的考验。

经测试,没加offset的时候可以轻易地用AssetStudio预览AssetBundle中的资源,请参考下图(因为用的是公司项目的资源所以需要打码处理):

 

带offset的资源,发现和我们的理论推测结果一致,请参考:

 

 

在测试过程中发现,有些老版本的AssetStudio,在解析带offest的资源的时候甚至会直接奔溃。其实,对于资源加密,我们大多数时候能做到的是防小白不防专家,不管你是采用简单的或者复杂的,在反编译高手手里都有点苍白,我亲眼所见一个大佬用IDA把人家的通信加密算法反出来了,所以这里就不做更深入的分析了。


文末,再次感谢江鱼的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

也欢迎大家来积极参与U Sparkle开发者计划,简称“US”,代表你和我,代表UWA和开发者在一起!

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Unity AssetBundle BrowserUnity引擎的一款插件,它可以帮助游戏开发者在编辑器内浏览和管理AssetBundle资源。AssetBundle资源是游戏制作过程中必需的一种资源,它可以将一些预制件、贴图、声音和代码等打包成二进制文件,并在运行时通过代码加载到游戏中,从而实现动态加载资源和减少加载时间的效果。 Unity AssetBundle Browser插件提供了一个可视化的资源管理界面,用户可以通过它对资源进行打包、解包、浏览、删除等操作。在使用AssetBundle资源时,由于资源文件的二进制格式与编辑器内资源格式不同,开发者需要将资源文件打包成AssetBundle进行加载。Unity AssetBundle Browser将这个过程简化了,用户只需选择需要打包的资源,设置相关参数,并点击打包按钮,就可以轻松地创建AssetBundle文件,并将其加载到游戏中。 此外,Unity AssetBundle Browser还支持实时预览AssetBundle资源,开发者可以通过它快速地浏览预制件、纹理、材质球等资源,从而提高开发效率。总的来说,Unity AssetBundle Browser是一款十分实用的插件,它为开发者提供了一种便捷的方式去管理和使用AssetBundle资源,使游戏制作变得更加高效和便捷。 ### 回答2: Unity AssetBundle Browser是一个Unity扩展工具,用于在IDE中浏览和管理AssetBundle。AssetBundle是一种Unity资源包,可以使开发者轻松创建和管理资源,如纹理、声音和模型等。 Unity AssetBundle Browser可以让开发者直观地查看AssetBundle文件的内容,包括资源列表、依赖关系和预览功能。此外,它还提供了打包、解包、压缩和解压缩AssetBundle文件的功能,使开发者可以更灵活地使用AssetBundle,以满足项目需求。 使用Unity AssetBundle Browser,开发者可以快速地浏览和编辑AssetBundle,节省了大量的时间和精力。它还可以帮助开发者更好地理解AssetBundle的工作原理,为开发者深入学习Unity提供了一个重要的工具。 总之,Unity AssetBundle Browser是一个非常有用的工具,可以帮助Unity开发者更高效地管理和使用AssetBundle,提高项目开发效率和质量。 ### 回答3: Unity AssetBundle BrowserUnity游戏引擎中的一个工具,可以用于浏览和导出AssetBundle资源包。AssetBundle是一种Unity的资源打包方式,可以将游戏中的资源打包成一个文件,方便后续的版本管理和部署。 使用AssetBundle Browser可以很方便地管理和处理AssetBundle资源包,包括在游戏中动态加载和卸载资源,减小游戏的内存占用和优化游戏性能。通过AssetBundle Browser,我们可以查看和编辑资源包中的资源,例如模型、贴图、音频等,并提供了一套完整的编辑工具,支持资源导入、复制、删改等操作。 在游戏开发中,AssetBundle Browser可以用于优化游戏的资源处理方式,提高游戏性能,同时也可以帮助开发者更加高效地管理和打包游戏资源。值得注意的是,AssetBundle Browser只适用于Unity游戏引擎,需要Unity开发环境的支持。因此,开发者在使用AssetBundle Browser时,需要提前了解Unity的相关知识和技术,否则会影响到开发效率和质量。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值