0x00 为了更好的学(kuang)习(tian)研(lao)究(gong)，我尝试从这个游戏入手，解包一个 Unity 应用。 0x01 万能的 unzip 下载apk，然后直接unzip了它。然后我们想要的东西都在 assets 文件夹里。 ├─assets │ ├─bin │ │ └─Data │ │ ├─Managed │ │ └─Resources │ ├─common │ │ ├─animations │ │ │ ├─... │ │ ├─audios │ │ │ ├─... │ │ ├─effect │ │ │ ├─... │ │ ├─fonts │ │ ├─materials │ │ ├─shaders │ │ └─textures │ ├─i6 │ │ ├─... │ └─lua │ ├─data │ │ ├─... │ ├─lib │ ├─logic │ ├─module │ └─sys ├─lib │ ├─armeabi │ ├─armeabi-v7a │ └─x86 ├─META-INF └─res ├─anim ├─color ├─color-v11 ├─color-v23 ├─drawable ├─... 有个文件，路径是 /assets/bin/Data/unity default resources 很容易引起注意。 0x02 Unity Assets Bundle Extractor 我当即找了个解包Unity的资源文件的工具，这个软件是闭源的，但是你可以在GitHub上找到分发的软件包（DerPopo/UABE）。随便用它打开了一个资源文件，就可以查看内容了。 但是这用来批量解包并不方便，因为我只能操作它的命令行参数。 0x03 UnityPack 我又在GitHub找到了一个Python的工具，HearthSim/UnityPack，从它的README.md可以看出它既可以解音频又可以解图片。 依赖直接 pip install setup-tools decrunch pillow lz4 解音频需要 HearthSim/python-fsb5 建议使用 git clone 以后 python ./setup.py 来安装，pip里的版本太老，我在解的时候就遇到了header信息不全导致的失败。后来发现这个问题早就已经修复（HearthSim/python-fsb5 #4），使用master分支安装的就可以正常解音频了。 但还遇到一个问题 soha@SohaCompiling:/mnt/compiling/evol$ unityextract --all -o asset_extract/ gameAPK/assets/common/textures/black.png WARNING: Texture format not implemented. Skipping 'black.png'. Texture的格式未定义。我又拿出UABE看了看。TextureFormat=34。 我随后在UnityPack的代码里发现了相关的内容 （HearthSim/UnityPack/unitypack/engine/texture.py）。有意思。看起来是 ETC_RGB4 这种格式不被支持的原因。我只好寻找另外的解决方案 0x04 怎么解 ETC 格式的文件 在网上查阅各种资料后，找到了爱立信官方的ETC工具（Ericsson/ETCPACK）。 但是我又遇到了一个问题，不管在参数里用什么格式，都不能直接解包UABE里面导出的二进制数据。于是我用了张正常的图片，选择了ETC1算法转换成pkm格式，然后直接看数据。 后来又拿了几张图转换，发现了最重要的就是前16个字节的描述信息。后来在网上又搜索到了具体的PKM header定义。 于是我写上了PKM Header后，又从APK解压出的素材里导出的一个Texture2D binary里面复制进去了数据，这时候再使用 etcpack 转换就成功了。 还需要上下翻转一下，就是正确的图片了。 0x05 看来得自己造个解包轮子了 自己重新用Python造个parse ETC文件的轮子对于周末都不足24hr的高中生来说十分不现实。所以我就根据etcpack代码里的一些header的定义，写了段造PKM的代码。 def getPKMHeader(width,height,tformat): header=b"\x50\x4B\x4D\x20" version=b"20" if tformat==TextureFormat.ETC_RGB4: version=b"10" formatD=0 elif tformat==TextureFormat.ETC2_RGB: formatD=1 elif tformat==TextureFormat.ETC2_RGBA8: formatD=3 elif tformat==TextureFormat.ETC2_RGBA1: formatD=4 elif tformat==TextureFormat.EAC_R: formatD=5 elif tformat==TextureFormat.EAC_RG: formatD=6 elif tformat==TextureFormat.EAC_R_SIGNED: formatD=7 elif tformat==TextureFormat.EAC_RG_SIGNED: formatD=8 else: formatD=0 formatB=formatD.to_bytes(2,byteorder="big") widthB=width.to_bytes(2,byteorder="big") heightB=height.to_bytes(2,byteorder="big") return(header+version+formatB+widthB+heightB+widthB+heightB) 我造的解包轮子在解包到 Texture2D 的时候会判断 TextureFormat 是否在ETC系列里。如果在，那就通过构造pkm然后调用etcpack转换成ppm文件然后使用Pillow打开并且进行翻转，然后输出。 bindata=getPKMHeader(data.width,data.height,data.format)+data.image_data putFile('temp.pkm',currentWorkDir,bindata) subprocess.call(["./etcpack","./temp.pkm",currentWorkDir]) image=Image.open('./temp.ppm') 其他格式就丢给UnityPack操作了。 这些代码我放在了GitHub上（moesoha/unity-game-resource-unpacker）