ゲームアセットを難読化したら iOS で ITMS-90005 の エラーが出てハマった話

iOS

ずっと前の話になりますが、個人開発したゲームがやっと完成して iOS 審査に出そうとバイナリをアップロードしたところ、以下のエラーが出ました。

ERROR ITMS-90005: “This bundle is invalid. The icon file xxxx.png must be in .png format.”

当時はこの対処について相当困り、正直なところ「これ…リリース無理なんじゃないか?」と一時絶望しかけたほど悩みました。

無事に解決できてゲームをリリースでき、今に至るわけですが、上記のエラーがなぜ発生し、どのようにして解消したのかについて、紹介したいと思います。

ipa バイナリをアップロード時に ERROR ITMS-90005 のエラーが発生

ようやくゲームが完成し、ipa バイナリファイルを AppStore にアップロードしたときのことです。(2018年なので今から4年以上前の話になります。当時はまだ Transporer アプリなどはなく、Xcode でアップロードしていました)

初回のアップロードだったので沢山エラーが出ていました。それぞれ1つずつ潰していったのですが、以下だけが解消できませんでした。

ERROR ITMS-90005: “This bundle is invalid. The icon file Xxxx.app/res/image/IconChip.png must be in .png format.”

ゲームアセットなので、そのままの PNG でパッケージしてしまうと悪意のあるユーザなどにデータを抜き取られてしまうので、ファイルを暗号化(難読化)した形で格納しておき、ゲーム実行時に復元するようにしていました。利用している素材の利用規約にも「簡単に抜き出して再利用できるような状態でアプリに同梱するのは不可」のような記載があったので、対応が必須でした。

ipa のチェックなので、ゲームアセットの内容まで細々チェックしないと思っていたのですが、png format までチェックされており、ファイル内容が難読化した状態だったのでエラーになっていたようです。(が、正直ゲームアセット内のデータまでチェックする意味あるのか?と思いました)

cocos2d-x (当時は選択肢としては全然アリな時代)を使って開発しており、画面レイアウト作成ツール CocosStudio から参照される png ファイルだったのですが、以下のようにして難読化されていて問題ない状態で開発していました。

  • CocosStudio が参照する画像フォルダには難読化前の .png ファイルを配置
  • ゲームがロードする Resources 以下には難読化した .png ファイルを格納(専用のスクリプトで自動変換)
  • ゲーム実行時に難読化されたファイルを難読化前の状態に戻すよう、cocos2d-x 本体側の以下の各関数に処理を追加
    • cocos2d::FileUtils::getDataFromFile()
    • cocos2d::FileUtils::getStringFromFile()
    • cocos2d::FileUtils::getFileData()

iOS/Android ともにゲーム側の処理に問題はなく、Android もストアに問題なくアップロードできていたのですが、 iOS でストアにアップロードした際に前述のエラーが出てしました。

Appleサポートに連絡

以下のような内容で、Appleサポートに連絡しました。

XcodeでアプリをArchiveし、”Upload to App Store” ボタンを押して処理を進めていくと、最後に以下のエラーが出てしまいます。

ERROR ITMS-90005: “This bundle is invalid. The icon file Xxxx.app/res/image/IconChip.png must be in .png format.”

IconChip.png はゲームで使うアイコンチップを含む画像類で、クラッカーやハッカーによる攻撃でのデータ抜き出しなどを回避するため、データを難読化しており、.png の拡張子ですがファイル中身自体は「PNGフォーマット」にはなっていません。
しかし、ゲーム起動時のロード時にデコードして実際のPNGデータを復元しているため、ゲームのプレイには何ら問題はない状態です。
IconChip.png以外の大半のリソースファイルも同様に難読化しています。

アップロード時にゲームコンテンツのチェックをしているのはなぜなのでしょうか?
このエラーを回避するにはどのようにすればよいでしょうか?

丸一日以上待ってやっと返ってきた回答が以下。

Apple Developer Program Support にお問い合わせいただき、ありがとうございます。Xcode で、アプリケーションを Archive した後、Upload to App Store をクリックした後に出てくるエラーについてご質問を承っております。こちらにて少しでもお力になれないかと確認したのですが、今回ご連絡を頂いております問題につきましては、テクニカルな内容になりこちらにて詳細をお調べすることができないため、大変恐れ入りますが、以下のページよりご相談したいいただくことを検討していただけませんでしょうか。Apple Developer Forums では Apple のエンジニアやほかのデベロッパと開発関連の問題についてディスカッションを行っていただくことが可能となっております。
https://devforums.apple.com/
また、コードレベルの詳しいサポートが必要な場合、テクニカルサポートのリクエストを送信いただくことが可能となっております。Apple Developer Program には年2回まで無料でサポートを受けていただくことができる権利が含まれています。
https://developer.apple.com/jp/support/technical/
直接のサポートをさせていただくことができずに心苦しいのですが、何卒よろしくお願い致します。

年2回しか使えない「コードレベルのサポートが可能なテクニカルサポート」を使えとのことでした。残念。

テクニカルサポートに問い合わせ

以下の内容で問い合わせを送りました。英語です。

I archived the app by Xcode and clicked “Upload to App Store” button, but following error occurs.

ERROR ITMS-90005: “This bundle is invalid. The icon file Xxxx.app/res/image/IconChip.png must be in .png format.”

IconChip.png is the icon chip for game, and this file is obfuscated for avoiding attack by cracker or hacker.
So the file extension is ‘.png’, but its content is not exact PNG format.
But, applicaiton will decode this file and restore to PNG, so there is no problem about playing game.
Other resource files other than IconChip.png are also obfuscated.

Why Xcode does check game’s contents data for Upload to App Store? Is it needed?
How can I avoid this error?
I want to submit my application ASAP.

1日以内で回答が来ました。

How can I avoid this error?

The best thing to do here is to think about any of these resource files as your own file types, and using your own file extensions for them. For example, when working with the PNG file that produces this message, you would load a file named IconChip.your_file_extension from disk, deobfuscating it, and then converting the deobfuscating byte stream into an NSData object. Once you have a data object, you can make it an image again through an API like UIImage imageWithData.


Why Xcode does check game’s contents data for Upload to App Store? Is it needed?

The App Store applies a significant amount of processing to every submitted app to make it ready for delivery to customer devices. Once such reason for this pertaining to images is to apply app thinning, where unused image assets, such as 1x images on a 2x device, are removed from the app to make the overall app size smaller.

要約すると以下とのことです。

  • 独自タイプのファイルを使いたいなら拡張子を独自のものにしなさい
  • app thinning のために無駄な画像を減らすためにチェックしている

2つ目のは app thinning で iOS 仕様のアプリアイコンについてチェックしているなら分かるが、そもそもゲーム内で使う画像なので勝手に app thinning で削るのは不可能なはずで「?」という感じでした。

しかし、1つ目の指摘はごもっともではありました。

対応できるのなら対応したいところですが、Cocos Studio で参照される画像でもあるので勝手に画像の拡張子の変更はできないわけです。

画面のレイアウトファイルであるXML形式の .csd ファイルに png ファイルへのパスが記載されています。

CocosStudio で publish して生成されゲームでロードされるバイナリ形式の .csb ファイルにも png ファイルへのパスが埋め込まれています。

これらの事情から、以下のように返信しました。CocosStudio とか書いても伝わらないので UI layout tool という表現にしています。

In my game development environment, game’s UI layout file refers the image file by “.png” extension because UI layout tool uses non-obfuscated image files, and real file path is embedded to UI layout file.

So I can’t rename image file’s extension and I can’t adopt your approach.

By the way, I have something on my chest.
Nevertheless all .png files are not exact PNG format, ITSM-90005 error point out only IconChip.png.
I think this is hint for resolve this problem.

Perhaps png format check will be for only for images that base name is “Icon”?
If so, can I avoid this error by renaming “IconChip.png “to “ItemChip.png”?

後半に書いてるのは、「ひょっとして “Icon” というファイル名の画像は App thinning で削除する対処うになりうるからチェックしているのでは?」という想像から、ファイル名を変えてみると回避できないのか?というのを聞いてみた感じです。

その返答がこれです。これも数時間で回答が来ました。レスポンスの速さには感動した記憶があります。

Perhaps png format check will be for only for images that base name is “Icon”?
If so, can I avoid this error by renaming “IconChip.png “to “ItemChip.png”?

The validation logic changes over time, so making any assumptions about what files are validated and why is not a good idea. The best thing is to assume that the App Store expects all PNG files to actually be valid PNG format files.


In my game development environment, game’s UI layout file refers the image file by “.png” extension
because UI layout tool uses non-obfuscated image files, and real file path is embedded to UI layout file.
So I can’t rename image file’s extension and I can’t adopt your approach.

This is something you will need to discuss further with support channel for your game development environment. The best advice I can offer you is to either fully own the format you are using for these assets, as previously suggested, or to use a standard format without modification.

簡単にまとめると以下ということです。

  • 検証ロジックは時間が経てば変わるんだから、どのファイルが検証対象なのか等の仮定を元に回避しようとするのは良いアイデアはない
  • pngフォーマットは全部正しいと期待されてチェックされるんだから、その想定でいなさい
  • ゲーム開発環境の担当者と相談しなさい
  • 拡張子を独自のものにするか、拡張子にあった正しいフォーマットを使いなさい

要するに CocosStudio の変更について相談しろ、ということですね。

しかし、そんなのは無理ですね。「独自の難読化したフォーマットの画像使いたいからサポートしてくれ」なんて言って対応してくれるわけがありません。

CocosStudio はオープンソースではないので、自分で対応するというのは不可能です。

自前で CocosStudio 相当のツールを作成してしまうというのも、不可能ではないにせよ相当な無理があり現実的ではありません。

もうこれ以上テクニカルサポートに質問してもどうしようもなく、途方に暮れました。

このあたりでしばらくのあいだ絶望感に浸っていた記憶があります。

対応策についてのひらめき

ここまでの状況を整理すると以下でした。

  1. 画像は難読化が必須(素材の利用規約のため)
  2. 難読化した画像は .png の拡張子のままでは ipa アップロードでエラーになる
  3. CocosStudioは .png 以外扱えない

何か方法がないものかと数時間考えていたところ、以下を思いつきました。

  • 難読化した画像の拡張子は .png 以外(ここでは例として .img とする)にしておく
  • CocosStudioでは普通に .png を使ってレイアウトファイルの .csd を作成し、バイナリ形式 .csb の publish も行う
  • バイナリ形式の .csb 内に埋め込まれているファイルパス .png を .img に書き換える

3つ目の手順が可能かどうかは、バイナリファイル .csb のフォーマットを把握しなくてはならないと考えがちですが、ファイルの中身を見ると単純に文字列で埋め込まれているだけでした。

そのため、ファイルサイズが変わらないように、つまり .png と同じ3文字のままで .img に書き換えれば十分なのではないか?ということに気付きました。

ようするに、バイナリファイル内の

▓▓▓▓image/IconChip.png▓▓▓▓

のようになっている部分を

▓▓▓▓image/IconChip.img▓▓▓▓

と3バイトだけ書き換えてやればよいわけですね。

もし、csbのロード処理でチェックサム等でのファイル内容の検証などが行われていた場合、データ変更しているのでアウトでしたが、そのような処理は無さそうで問題なくロードができました。

そんなわけで、上記のバイナリファイル変換の処理をササっとRubyで書いて、独自拡張子を使うようにしてパッケージングする仕組みを整備しました。

その結果、Appleへの ipa アップロードでもエラーが出ることなく正常に処理されるようになりました。

めでたしめでたし。

まとめ

というわけで、.png ファイルを難読化していたため ITMS-90005 のエラーでハマった話について、詳細な経緯含めて事例を紹介しました。

「CoccosStudio のバイナリファイル csb ファイルのフォーマットが分からないため対応できない」とか考えがちなところを、柔軟なアイデアで対応することができたのはクリティカルだったなと思います。

似たような状況で困った人の参考になれば、幸いです。

コメント

タイトルとURLをコピーしました