一次解包 Unity 游戏应用的经历


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)。随便用它打开了一个资源文件,就可以查看内容了。

uabe.png

但是这用来批量解包并不方便,因为我只能操作它的命令行参数。

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分支安装的就可以正常解音频了。

但还遇到一个问题

[email protected]:/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

texture_format_34.png

我随后在UnityPack的代码里发现了相关的内容 (HearthSim/UnityPack/unitypack/engine/texture.py)。有意思。看起来是 ETC_RGB4 这种格式不被支持的原因。我只好寻找另外的解决方案

0x04 怎么解 ETC 格式的文件

在网上查阅各种资料后,找到了爱立信官方的ETC工具(Ericsson/ETCPACK)。

但是我又遇到了一个问题,不管在参数里用什么格式,都不能直接解包UABE里面导出的二进制数据。于是我用了张正常的图片,选择了ETC1算法转换成pkm格式,然后直接看数据。

pkm_bin.png

后来又拿了几张图转换,发现了最重要的就是前16个字节的描述信息。后来在网上又搜索到了具体的PKM header定义

于是我写上了PKM Header后,又从APK解压出的素材里导出的一个Texture2D binary里面复制进去了数据,这时候再使用 etcpack 转换就成功了。

pkm_header_with_texbin.png

还需要上下翻转一下,就是正确的图片了。

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


协议: 本文根据 Creative Commons Attribution-NonCommercial-ShareAlike 4.0 License 进行授权。

标签: unity 解包 解密


  • 有 6 条评论, 不如再加一个评论?
  • kk kk 2018-01-26 09:46

    Soha,最后的解密方式是啥?


    reply
    Soha Jin Soha Jin 2018-01-29 10:18

    BlowFish CBC。具体细节还是不透露了(想活命),有兴趣自己研究吧


    reply
    Ulysses Ulysses 2018-02-02 16:37

    LZ的钻研精神不错,但是从题目开始就很奇怪了。
    Unity并不依赖Xamarin。Unity发布到Android平台时,生成的项目是Java项目,当然游戏内的逻辑仍然是C井,只是可以用Java来写额外的Service或是Activity等。
    当然,事实上可以将Xamarin与Unity结合使用,但是恐怕除了我这样的C井主义者,绝大多数人并不会这么去做。
    不知道LZ所说的“Xamarin”是从哪里看出来的。


    reply
    Soha Jin Soha Jin 2018-02-02 16:46

    感谢指出。其实我对Unity的特点并不清楚,除了这次搞事,并没有太多和Unity的交集。我只是眉毛胡子一把抓地把Unity这个使用Mono做移动端跨平台的玩意儿认为是使用了Xamarin。刚才去查了一些资料,发现我的理解上的确出了点偏差。马上会对表述进行修正。


    reply
    言洛 言洛 2018-08-10 18:38

    大佬能续下来吗


    reply
    烟烟 烟烟 2018-08-16 06:13

    初学拆解小萌新直接在card_h这里翻船啦
    看来要先理解好一阵子了


    reply

撰写新评论

account_circle
mail
insert_link
mode_comment