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)
Soha,最后的解密方式是啥?
reply
BlowFish CBC。具体细节还是不透露了(想活命),有兴趣自己研究吧
reply
你好,请教下,我看到他的CBC不需要iv这个参数。。。我用其他语言例如java或者python的话 cbc 加密都需要这个参数。加密出来结果都不一样,求指教
reply
因为这个调用了外部的库函数,那个参数可能放在了那个里面
reply
LZ的钻研精神不错,但是从题目开始就很奇怪了。
Unity并不依赖Xamarin。Unity发布到Android平台时,生成的项目是Java项目,当然游戏内的逻辑仍然是C井,只是可以用Java来写额外的Service或是Activity等。
当然,事实上可以将Xamarin与Unity结合使用,但是恐怕除了我这样的C井主义者,绝大多数人并不会这么去做。
不知道LZ所说的“Xamarin”是从哪里看出来的。
reply
感谢指出。其实我对Unity的特点并不清楚,除了这次搞事,并没有太多和Unity的交集。我只是眉毛胡子一把抓地把Unity这个使用Mono做移动端跨平台的玩意儿认为是使用了Xamarin。刚才去查了一些资料,发现我的理解上的确出了点偏差。马上会对表述进行修正。
reply
大佬能续下来吗
reply
初学拆解小萌新直接在card_h这里翻船啦
看来要先理解好一阵子了
reply
感谢博主的分享,该游戏现在已经采用了新的加密方式,请问有什么思路可以提供参考吗?谢谢~
reply