0


将AddressablesCN加密保护的数据解密与修改

解密与修改方法

在dnSpy中搜索找到函数

UnityEngine.ResourceManagement.ResourceProviders.AssetBundleResource.LoadWithDataProc

如下

privatevoidLoadWithDataProc(string path,uint crc){FileStream fileStream =newFileStream(path, FileMode.Open, FileAccess.Read);Stream stream = m_dataProc.CreateReadStream(fileStream, m_ProvideHandle.Location.InternalId);if(stream.CanSeek){
        m_RequestOperation = AssetBundle.LoadFromStreamAsync(stream, crc);return;}MemoryStream memoryStream =newMemoryStream();
    stream.CopyTo(memoryStream);
    stream.Flush();
    stream.Dispose();
    fileStream.Dispose();
    memoryStream.Position =0L;
    m_RequestOperation = AssetBundle.LoadFromStreamAsync(memoryStream, crc);}

将其修改为如下

privatevoidLoadWithDataProc(string path,uint crc){//Debug.Log(path);FileStream fileStream;Stream stream;if(File.Exists(path +"uproc")){//Debug.Log("Load " + path + "uproc");
                fileStream =newFileStream(path +"uproc", FileMode.Open, FileAccess.Read);
                stream =newMemoryStream();
                fileStream.CopyTo(stream);}else{//Debug.Log("Load " + path);
                fileStream =newFileStream(path, FileMode.Open, FileAccess.Read);
                stream =this.m_dataProc.CreateReadStream(fileStream,this.m_ProvideHandle.Location.InternalId);FileStream fileStreamNu =newFileStream(path +"uproc", FileMode.CreateNew, FileAccess.Write);MemoryStream memoryStream2 =newMemoryStream();
                stream.CopyTo(memoryStream2);
                memoryStream2.Position =0L;
                memoryStream2.CopyTo(fileStreamNu);
                memoryStream2.Position =0L;
                stream.Flush();
                stream.Dispose();
                stream = memoryStream2;
                fileStreamNu.Flush();
                fileStreamNu.Dispose();}if(stream.CanSeek){this.m_RequestOperation = AssetBundle.LoadFromStreamAsync(stream, crc);return;}MemoryStream memoryStream3 =newMemoryStream();
            stream.CopyTo(memoryStream3);
            stream.Flush();
            stream.Dispose();
            fileStream.Dispose();
            memoryStream3.Position =0L;this.m_RequestOperation = AssetBundle.LoadFromStreamAsync(memoryStream3, crc);}

原.bundle文件在加载时将生成一个对应的已解密的.bundleuproc文件,为普通AB格式,可直接在AssetStudio查看,并且游戏受该已解密文件修改的影响,但原资源文件将失去作用

原理简介

上面代码中

this.m_dataProc

调用到的是在unity中设置的继承了

IDataConverter

的用于加密解密的流处理器类,可能是自定义的也可能不是,不过它是什么对于这种解密方法没有影响。这里看到FileStream可以确定是在最终步骤的直接对文件流进行的加解密,将解密后的文件流从新写到对应的解密后文件,直接增加后缀一一对应,并且如果存在的话直接读取解密后(可能进行修改)的资源进行加载。
不难看出,解密的功能是可以用独立程序完成的,只要可以调用到对应的解密函数,但是这种直接读取解密后的文件的方法可以节省大量繁琐步骤,更方便对资源的修改,事实上通过调用

this.m_dataProc.CreateWriteStream

即可将解密数据重新加密。

关于该流处理器类到底存放在什么地方,以及它是如何被引用的

可以追查到在函数

UnityEngine.ResourceManagement.ResourceProviders.AssetBundleProvider.Initialize

中有

SerializedType serializedType = JsonUtility.FromJson<SerializedType>(data);if(serializedType.Value !=null){
    DataStreamProcessor = Activator.CreateInstance(serializedType.Value)asIDataConverter;}

是该处理器类引用的来源。对data的追查则到了

UnityEngine.ResourceManagement.Util.ObjectInitializationData

结构中的

[FormerlySerializedAs("m_data")][SerializeField]privatestring m_Data;

<game>\<game>_Data\StreamingAssets\aa\catalog.json

文件中的内容通过搜索可以确定是

UnityEngine.AddressableAssets.ResourceLocators.ContentCatalogData

的序列化,其中的

m_ResourceProviderData

中的第一项

ObjectInitializationData

中的

m_Data

的值则是与运行过程中Debug输出到文件的data一模一样,可以确定这就是它最源头的来源(其它地方也没找到)。

曲折经历与思路

本来打算用AssetStudio找到然后修改某个MonoBehaviour的值(已经提前在ILspy里找到代码中使用MonoBehaviour上的值的内容了),结果找不到,就简单看了看好辨识的资源,结果明显有缺少,才发现一股脑拖进去的这些AB压根没识别。然后对相关资源的代码进行排查追踪,正着找了好半天果然还是太抽象了。决定搜一搜Addressables(aa)这个和AB看起来不太一样的东西,一顿搜索发现了CN版的加密。跟着它的说明文档的思路直接搜

AESStreamProcessor

就在游戏代码的程序集里找到了一个,这个是八九不离十。此时突发奇想,向其中安插

throw

,直接抓住调用栈,找到这个修改的函数。当然过程中并不一帆风顺,有很多这查一查那追一追的过程,我甚至为了方便获取信息向其中加入了一个用网络进行通信的debugmsg到一个控制台程序打印出来(尤其是前期要大量重复打印各种信息的时候十分的好用),总共用时大约9h左右。

标签: unity c# 安全

本文转载自: https://blog.csdn.net/weixin_47527928/article/details/128786835
版权归原作者 nxibjz 所有, 如有侵权,请联系我们删除。

“将AddressablesCN加密保护的数据解密与修改”的评论:

还没有评论