Project SEKAI 逆向(7):3D 模型提取

Project SEKAI 逆向(7):3D 模型 1. 文件结构 目前发现的 3D 模型基本都在 [ab cache]/live_pv/model/ 下 初步观察: (1) Body 即目标模型;当然,作为skinned mesh,而且带有blend shapes,处理细节会很多;后面继续讲 (2) 处的 MonoBehavior 就其名字猜测是碰撞盒 (3) 处的几个 Texture2D 则作为texture map 2. 模型收集 利用sssekai取得数据的流程在(5)中已有描述,这里不再多说 首先整理下根据mesh发现的数据需求 (1) Static Mesh pjsk发现的所有mesh在相应assetbundle中会有1个或更多GameObject的ref;对于这些ref,static mesh会出现在m_MeshRenderer之中 其他细节暂且不说;因为做 Skinned Mesh 导入时都是我们要处理的东西 (2) Skinned Mesh 不同于static mesh,这些ref会出现在m_SkinnedMeshRenderer之中 同时,我们也会需要骨骼结构的信息;bone weight以外,也需要bone path(后面会用来反向hash)和transform (3) Blend Shapes 这些可以出现在static/skinned mesh之中;如果存在,我们也会需要blend shape名字的hash,理由和bone path一致 加之,Unity存在aseetbundle中动画path也都是crc,blendshape不是例外 总结: (1) 所以对于static mesh,搜集对应GameObject即可 (2) 对于skinned mesh,同时也需要构造bone hierarchy(就是个单根有向无环图啦),并且整理vertex权重; 则需要收集的,反而只是bone的transform而已;transform有子/父节点信息,也有拥有transform的GameObject的ref (3) 的数据,在(1)(2)中都会有 3. 模型导入 当然,这里就不考虑将模型转化为中间格式了(i.e. FBX,GLTF)...

January 5, 2024 · 2 min · 237 words · mos9527

Project SEKAI 逆向(6):Live2D 资源

Project SEKAI 逆向(6):Live2D 资源 分析variant:世界計劃 2.6.1 (Google Play 台服) 1. Live2D 模型 所有live2d资源都可以在 [abcache]/live2d/下找到;包括模型及动画 ​ 首先,.moc3,.model3,.physics3资源都可以直接利用Live2D Cubism Editor直接打开 ​ 而模型材质需要额外更名;这些信息都在BuildModelData中 补全后即可导入,效果如图 2. 动画 Key 预处理 可惜动画并不是.motion3格式 封包中有的是Unity自己的Animation Clip 在提取资源时,所有的动画key只能读到对应key string的CRC32 hash;导出/操作必须知道string-hash关系 这些string在moc3以外的文件中未知:当然,碰撞出string也不现实;猜想string和Live2D参数有关 ​ 尝试搜索无果 幸运的是Live2D Unity SDK可以免费取得,而且附带样例 还记得前文处理BlendShape时,可以知道AnimationClip的源.anim会有path的源string,而不是crc 尝试加入前缀 可以定位;下面介绍如何构建CRC表,完成crc-string map 3. moc3 反序列化 + CRC打表 每次读取都从moc3文件构造应该可行;不过考虑到有导入纯动画的需求,显然一个常量map是需要的 故需要能读取moc3中所有参数名;参照https://raw.githubusercontent.com/OpenL2D/moc3ingbird/master/src/moc3.hexpat 在 ImHex 中可见: 提取参数名脚本如下: from typing import BinaryIO from struct import unpack # https://github.com/OpenL2D/moc3ingbird/blob/master/src/moc3.hexpat class moc3: Parameters : list Parts: list def __init__(self, file : BinaryIO) -> None: # Header: 64 bytes file....

January 2, 2024 · 2 min · 284 words · mos9527

Project SEKAI 逆向(5): AssetBundle 脱机 + USM 提取

Project SEKAI 逆向(5): AssetBundle 脱机 + USM 提取 分析variant:世界計劃 2.6.1 (Google Play 台服) 1. AssetBundleInfo 前文提及的AssetBundleInfo是从设备提取出来的;假设是已经加载过的所有资源的缓存的话: 在刚刚完成下载的设备上提取该文件时,该文件 4MB 但是在初始化后重现抓包时发现的该文件为 13MB curl -X GET 'https://184.26.43.87/obj/sf-game-alisg/gdl_app_5245/AssetBundle/2.6.0/Release/online/android21/AssetBundleInfo.json' -H 'Host: lf16-mkovscdn-sg.bytedgame.com' -H 'User-Agent: UnityPlayer/2020.3.32f1 (UnityWebRequest/1.0, libcurl/7.80.0-DEV)' -H 'Accept-Encoding: deflate, gzip' -H 'X-Unity-Version: 2020.3.32f1' 推测设备上文件为已缓存资源库,而这里的即为全量资源集合;尝试dump sssekai apidecrypt .\assetbundleinfo .\assetbundleinfo.json 查身体模型数看看吧 此外,这里的数据还会多几个field 新数据库: "live_pv/model/character/body/21/0001/ladies_s": { "bundleName": "live_pv/model/character/body/21/0001/ladies_s", "cacheFileName": "db0ad5ee5cc11c50613e7a9a1abc4c55", "cacheDirectoryName": "33a2", "hash": "28b258e96108e44578028d36ec1a1565", "category": "Live_pv", "crc": 2544770552, "fileSize": 588586, "dependencies": [ "android1/shader/live" ], "paths": null, "isBuiltin": false, "md5Hash": "f9ac19a16b2493fb3f6f0438ada7e269", "downloadPath": "android1/live_pv/model/character/body/21/0001/ladies_s" }, 设备数据库:...

January 1, 2024 · 1 min · 171 words · mos9527

Project SEKAI 逆向(4): pjsk AssetBundle 反混淆 + PV 动画导入

Project SEKAI 逆向(4): pjsk AssetBundle 反混淆 + PV 动画导入 分析variant:世界計劃 2.6.1 (Google Play 台服) 1. 数据提取 pjsk资源采用热更新模式;本体运行时之外,还会有3~4G左右的资源 (**注:**不定量,见下一篇) 尝试从本机提取资源 没有magic UnityFS,考虑ab文件有混淆 2. 加载流程分析 进dnSpy直接搜assetbundle找相关Class 进ida看impl,可以很轻松的找到加载ab的嫌疑流程 最后直接调用了unity的LoadFromStream,Sekai.AssetBundleStream实现了这样的Stream: 可以注意到 加载时根据 _isInverted flag 决定是否进行反混淆操作 如果有,则先跳过4bytes,之后5bytes按位取反 最后移交InvertedBytesAB继续处理 注意到n00应为128,v20为读取offset 这里考虑offset=0情况,那么仅前128字节需要处理 跟进InvertedBytesAB 可见,这里即跳过4bytes后,每 8bytes,取反前5bytes 综上,解密流程分析完毕;附脚本: import sys import os def decrypt(infile, outfile): with open(infile, 'rb') as fin: magic = fin.read(4) if magic == b'\x10\x00\x00\x00': with open(outfile,'wb') as fout: for _ in range(0,128,8): block = bytearray(fin.read(8)) for i in range(5): block[i] = ~block[i] & 0xff fout....

December 31, 2023 · 2 min · 223 words · mos9527

Project SEKAI 逆向(2): Frida动态调试及取API AES KEY/IV

Project SEKAI 逆向(2): Frida动态调试及取API AES KEY/IV 分析variant:世界計劃 2.6.1 (Google Play 台服) 0. 版本更新 毕竟美服的数据库滞后好几个版本号… 换到台服之后版本号(2.4.1->2.6.1)有变化,同时上一篇所说的metadata加密手段也换了 同样,拉取一份修复过的dump分析,可见: metadata加载部分不再进行全局加解密,观察二进制也可以发现: 2.4.0解密后metadata 2.6.1源metadata多出来8字节? 加载部分出入不大,尝试删掉8字节直接分析 卡在读string这一块;到Il2CppGlobalMetadataHeader.stringOffset检查一下 果然有混淆;这部分应该是明文,而unity的例子中开始应该是mscrolib 而so中il2cpp对metadata的使用却没加多余步骤;推测这部分在加载时进行了原地解密 1. metadata 动态提取 和上一篇记录的il2cpp.so提取步骤如出一辙,这里不再多说 比对apk的和dump出来的二进制: 果然string部分明朗了;继续dump 成功!带信息进ida;而非常戏剧化的事情是: 混淆没了。 果然是非常pro-customer的版本更新啊(确信) 2. frida-gadget 注入 WSA上注入各种碰壁,不过实体机上即使无Root也可以通过frida-gadget修改apk注入 之前用过改dex的方式尝试过,并不成功;挑一个运行时会加载的.so下手: 注: 后面目标lib换成了libFastAES.so,截图尚未更新 拿apktool打包后签名即可安装到真机 注: 我的frida跑在WSL上,拿 Windows 机器的adb做后端在 Win/Linux 机器配置分别如下 adb.exe -a -P 5555 nodaemon server export ADB_SERVER_SOCKET=tcp:192.168.0.2:5555 用 https://github.com/vfsfitvnm/frida-il2cpp-bridge 测试,未发现问题 3. IL2CPP 动态调用 runtime 接下来就可以直接调用runtime的东西了 回顾上文,貌似API业务逻辑只加了一套对称加密(AES128 CBC);这里同样如此 总结: APIManager为单例实例 APIManager.get_Crypt()取得Crypt,其aesAlgo即为.NET标准的AesManaged 综上,简单写一个脚本: import "frida-il2cpp-bridge"; Il2Cpp....

December 29, 2023 · 1 min · 158 words · mos9527