Steam端已经预加载并可下载资源了,除了data.unity3d的通用资源外,在Persistent/dat中的asset都无法直接提取
读取报错:
System.IO.IOException: Lz4 decompression error, write -1 bytes but expected 77876 bytes
- 手动查看文件头,Unity rev版本被改写成0.0.0, 改为正确Unity版本2022.3.18f1后仍报错
- 手动检查Unity文件格式,header正常 (lz4hc 压缩, flags 0x243),lz4 block info正常, 但解压lz4 block时异常
- 游戏目录下仅有GameAssembly,未找到global-metadata.dat
怀疑lz4 block的内容有点问题, 因为dump出来的部分完全没有可见的合理字符串,但没有啥思路。问问大佬们有没有思路,或者安卓包如果是Mono打包的,能不能直接BepInEx挂插件dump出来。
1 个赞
看了下手机端,global-metadata.dat的处理有几千行,简直不是人
改成dump, dump出来添上魔术也没法得到dump.cs
而且一用frida hook就闪退,等大佬来看看吧
更新一下:
看到GameAssembly里导入了Plugins/x86_64/libnative.dll里的LZ4_decompress_safe_ext和Z4_compress_default_ext, 应该是在LZ4加解密外加了一层。
把ghidra反汇编的代码扔给LLM看,AI认为解压之前有一个CFB模式的加解密。
但是这个o3的代码真是看着头秃… 不会key还是动态读的吧。用Ollydbg下断点也不行,应该有某种anti debugger
直接LoadLibraryA libnative.dll 对assetbundle 每个字节为起始跑,无法解压
写DLL Proxy,直接转发原dll,程序也会卡住
x64dbg+ScrytheHide倒是可以下断点,不过现在看不出啥,就有几个compress decompress的触发
这下没思路了
更新2:x64dbg+ScyllaHide 可以attach上进程加断点,成功用以下script dump出decompress函数的输入输出,但是每次都得敲完一遍script逐步执行,不知道有没有啥方法让他自动执行
memdumps.zip (918 字节)
不知道是没开服还是找的不对,现在dump出来的还是一些维护信息,看着跟直接json写的差不多…
x64dbg script:
decompress_addr = libnative.dll:LZ4_decompress_safe_ext
compress_addr = libnative.dll.LZ4_compress_default_ext
bp decompress_addr
bp compress_addr
SetBreakpointCommand decompress_addr, "scriptcmd call cb_decompress_entry"
SetBreakpointCommand compress_addr, "scriptcmd call cb_compress_entry"
erun
ret
cb_decompress_entry:
mov src_ptr, rcx
mov dst_ptr, rdx
mov src_len, r8
mov dst_cap, r9
rtr
mov dst_len, rax
savedata "memdumps/decompress_src_{breakpointcounter}.bin", src_ptr, src_len
savedata "memdumps/decompress_dst_{breakpointcounter}.bin", dst_ptr, dst_len
erun
ret
cb_compress_entry:
mov src_ptr, rcx
mov dst_ptr, rdx
mov src_len, r8
mov dst_cap, r9
rtr
mov dst_len, rax
savedata "memdumps/compress_src_{breakpointcounter}.bin", src_ptr, src_len
savedata "memdumps/compress_dst_{breakpointcounter}.bin", dst_ptr, dst_len
erun
ret
更新3: 上次那个hook只发服务端通讯,和asset没关系
暴力解法,在GameAssembly所有函数下断点。成功发现一些内存中解密完的AssetBundle。因为Unity LZ4 header有可能没被加密,通过CAB-标识符找到原文件
def extract_visible_node_name(path: Path) -> str:
with path.open('rb') as f:
sig = f.read(7)
if sig != "UnityFS".encode('ascii'):
return None
f.seek(8) # skip NUL byte at offset 7
format_ver = be_u32(f.read(4)) # uint32 BE
unity_ver = read_cstr(f)
engine_ver = read_cstr(f)
file_size = be_u64(f.read(8))
blocks_comp_size = be_u32(f.read(4))
blocks_raw_size = be_u32(f.read(4))
flags = be_u32(f.read(4))
align_stream(f, 16)
try:
blocks_info_comp = f.read(blocks_comp_size)
blocks_info = lz4.block.decompress(blocks_info_comp,
uncompressed_size=blocks_raw_size + 1000)
bi = io.BytesIO(blocks_info)
hash128 = bi.read(16) # usually unused
block_count = be_u32(bi.read(4))
blocks = []
for _ in range(block_count):
u_size = be_u32(bi.read(4))
c_size = be_u32(bi.read(4))
b_flags = be_u16(bi.read(2))
blocks.append((c_size, u_size, b_flags))
node_count = be_u32(bi.read(4))
nodes = []
names = []
for _ in range(node_count):
offset = be_u64(bi.read(8))
size = be_u64(bi.read(8))
nflags = be_u32(bi.read(4))
path_ = read_cstr(bi)
nodes.append((offset, size, nflags, path_))
names.append(path_)
return names
except Exception:
return None
def get_asset_map(root: Path):
asset_map = {}
for p in root.rglob('*'):
if p.is_file() and '.' not in p.name:
potential_asset = extract_visible_node_name(p)
if potential_asset is not None:
print(f"Add mapping {p} to {potential_asset}")
asset_map[str(p.name)] = potential_asset
return asset_map
对比了一下,算法很简单,就是保持前256个字节不动,后面xor一个88字节的key
path = Path('.')
op = path / 'f5.bin'
ep = path / 'f6.bin'
with open(op, 'rb') as f:
ot = f.read()
with open(ep, 'rb') as f:
et = f.read()
xor = xor_bytes(ot, et)
zero_part = xor[0:256]
encryption_starts = xor[256:]
invariant_text = ot[0:256]
key = encryption_starts[0:88]
print('Cycle key:', key.hex(), '\n')
但是这个key不是恒定的,暂时没有什么头绪,应该是从前256个字节里hash出来的88个byte。
附件里f1-f2,f3-f4,f5-f6是三对原文和密文,看看大佬有没有什么思路
_Artifact.zip (43.3 KB)
哥们我这里有人能改图出了个0.1教程,方便加我扣扣吗我发你,看看有没有参考必要,扣扣878819986
Airtnp
13
这个需要个人信息才能解包,而且具体的key看起来得需要抓一些HTTPS上的包?可能得用Fiddler抓一下,我没安卓测试端
可以直接dump的,不过dump出来貌似头部被替换了 
Wizard2AssetsUnpacker编译,使用方法在README.md
我dump出来的看起来只是前8字节被抹去了,但是有没有改别的东西就不清楚了…
Airtnp
21
请问AssetBundleAddress,CommonHeader, MD5Salt这些参数是怎么得到的啊?