解密与修改方法
在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左右。
版权归原作者 nxibjz 所有, 如有侵权,请联系我们删除。