大部分文件是没有加密的,但是有一部分CG文件被加密了。先看一下文件样本:
可以发现加密的文件都以10 00 00 00
结尾,而去掉这4字节都是整16字节倍数,合理猜测是AES之类的加密。
先尝试从LoadFromFileAsync
下手,但是走了一圈没有找到解密相关逻辑,再从LoadFromStreamAsync
入手,可以在UnityEngine_ResourceManagement_ResourceProviders_AssetBundleResource__LoadBundle_d__37__MoveNext
中看到如下逻辑
首先从文件末尾读取IV长度,然后根据IV长度从末端读取IV,剩下的部分就是被加密的数据,然后调用AES解密函数。这里有两个问题,-4 - iv_len + *(_DWORD *)(*(_QWORD *)&v69.fields._offset + 24LL)
对应的是ArraySegment
的Count
属性,这里有一个+24
是让人迷惑的,应该是反编译的错误。还有一个问题是这里没出现解密用的Key。
DecryptionUtil__DecryptAES_94228508
函数内部也没有直接出现Key的相关信息,但是在DecryptionUtil__DecryptAES_d__1__MoveNext
中,有如下代码
结合后面System_Security_Cryptography_ICryptoTransform_o
的构造,v4
应该就是Key数组。这里出现了一个叫Field__PrivateImplementationDetails__FC3F14D0A4F7ACB026C196667031B4DD35DA633748E76AD5B0FF643C4E947910
的东西。PrivateImplementationDetails
是一个编译时生成的类,对应一些静态变量。参考Perfare佬在这篇关于Il2CppDumper的更新的文章。
// Namespace:
[CompilerGenerated]
internal sealed class <PrivateImplementationDetails> // TypeDefIndex: 12932
{
// Fields
internal static readonly <PrivateImplementationDetails>.__StaticArrayInitTypeSize=5807 04F8EF9544695B3818934E7709AC87BAC4C8D733E9853D173CE137FA61F42E86 /*Metadata offset 0x614FA8*/; // 0x0
internal static readonly <PrivateImplementationDetails>.__StaticArrayInitTypeSize=3829 1A4635296267A11299FBF622134A6DED88DC7E554FD9BB6624EA57081739F4AC /*Metadata offset 0x616658*/; // 0x16AF
internal static readonly long 2D2025322643CE1497D8FB03FA789F27E833CF43545CA1003AFEFEA250D39313 = 3172232900852580628; // 0x25A8
internal static readonly <PrivateImplementationDetails>.__StaticArrayInitTypeSize=16 FC3F14D0A4F7ACB026C196667031B4DD35DA633748E76AD5B0FF643C4E947910 /*Metadata offset 0x617560*/; // 0x25B0
}
在global-metadata.dat
里对应偏移量找到16字节的Key
然后就可以写出解密脚本
from Crypto.Cipher import AES
import struct
import binascii
key = b'wiki is transfer'
def decrypt_aes(encrypted_file, output_file):
with open(encrypted_file, 'rb') as f:
file_content = f.read()
if file_content[:7] == b'UnityFS':
with open(output_file, 'wb') as f:
f.write(file_content)
return
iv_length = struct.unpack('<I', file_content[-4:])[0]
assert iv_length == 16
l = len(file_content)
data_end = l - 4 - iv_length
iv = file_content[data_end:l - 4]
encrypted_data = file_content[:data_end]
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted_data = cipher.decrypt(encrypted_data)
pad = decrypted_data[-1]
decrypted_data = decrypted_data[:-pad]
with open(output_file, 'wb') as f:
f.write(decrypted_data)
if __name__ == '__main__':
import sys
# Usage: python decrypt.py encrypted_dir output_dir
encrypted_dir = sys.argv[1]
output_dir = sys.argv[2]
import os
for root, dirs, files in os.walk(encrypted_dir):
for file in files:
encrypted_file = os.path.join(root, file)
output_file = os.path.join(output_dir, file)
decrypt_aes(encrypted_file, output_file)
解密出来的数据是这个样子的
可以看到Unity版本信息被抹去了,获取版本的方法有很多。我们可以在安装包的assets\bin\Data
路径下找到没有加密也没有抹去版本信息的data.unity3d
文件,可以看到版本是2022.3.32f1
。使用Raz版本的Studio,在Options->Specify Unity version
中输入2022.3.32f1
就能正常查看了。
Comments NOTHING