App 製品が終着駅に到達したとき、何ができる思い出作り?
mitmproxy + Apple Configurator を使って、Appを下架前の状態に永久に保つ方法
はじめに

呪術廻戦
長時間働き、多くの製品に関わってきた中で、かつて参加した製品が終着駅(サービス終了)を迎えることも増えてきました。ゼロから製品を開発することは新しい命を育むようなもので、チーム全員で3〜4ヶ月かけて子供を産み出しました。後期は他の保育者(エンジニア)に引き継いで育ててもらいましたが、最近その製品がライフサイクルの終わりを迎えると聞くと、少し寂しい気持ちになります。
人生も同じで、明日太陽が先に昇るのか、それとも事故が先に起こるのかは誰にも分かりません。できることは今を大切にし、目の前のことをしっかりやることだけです。
追憶
歩んだ道には必ず痕跡が残ります。私たちは、製品が終着点に達する前に何かをして、皆が思い出を振り返れるように、また少なくとも存在していた証を残したいと考えています。以下の方法はすべて、App がまだオンラインであることが前提です。すでに下架されている場合は、本当に思い出だけが残ることになります。
非技術的な方法 — 録画
iPhone 内蔵の画面録画機能 を直接使う以外に、QuickTime Player を使って iPhone を Mac に接続し、パソコンで録画して動画をエクスポートすることもできます。
- Mac で QuickTime Player アプリを開く

- 左上のツールバーで「ファイル」->「新規ムービー録画」を選択してください

- 録画インターフェースを閉じた後、🔴の隣にある「v」をクリックし、画面とスピーカーで接続した携帯電話を選択します。

- この時、録画インターフェースにスマホの画面が表示されます

「🔴」をクリックして録画を開始し、録画したい内容をスマホで操作します。

録画中は現在の動画サイズが表示され、録画を終了したい場合はもう一度「🔴」を押すと停止します。

QuickTime Player のツールバーで簡単に動画をトリミングでき、最後に「Command」+「s」を押して動画を指定した場所に保存すれば、録画が完了します。
動画で残す利点は、将来の思い出が写真よりもつながりやすいことです。深く録画すればするほど、詳細に記録できます。画面を画像に変えたい場合も、直接スクリーンショットを撮ればよいので、とても便利です。
技術的な方法
App の技術的なバックアップは二つの方向に分かれます。「骨」は App 自体で、実は骨組みであり、「肉」は API レスポンスデータで構成され、App の内容データの核心です。
-
骨は App が App Store から削除されると同時に消えてしまいます。
-
肉は API サーバーの停止に伴い消失します。
ですので、バックアップは「骨」と「肉」の二つの技術的な方法に分けています。
声明
本記事は技術研究の共有を目的としており、いかなる技術を用いた違法または権利侵害行為を推奨するものではありません。
[骨] .ipa アプリインストールファイルのバックアップ
App がストアから削除された後も、ダウンロード済みの App はユーザーが手動で削除しない限り、その端末に残り続けます。新しい端末に機種変更する際も、データ移行で一緒に引き継がれます。
しかし、誤ってその App を削除したり、機種変更で移行しなかった場合は、本当に永遠にさよならすることになります。その際、手動でストアの .ipa ファイルをバックアップしていれば、再び命を繋ぐことができます。
ずっと前に 逆向きエンジニアリングの記事 で触れましたが、今回は単純に .ipa ファイルをバックアップするだけで、クラッキングは不要です。すべて Apple の公式ツールを使って行います。
1. Apple Configurator 2 をインストールする

まず Mac App Store から「Apple Configurator 2」をダウンロードしてインストールします。
2. iPhoneをMacに接続し、「このコンピュータを信頼」をタップする

接続に成功すると、iPhone のホーム画面が表示されます。
3. バックアップしたい .ipa ファイルのアプリがスマホにインストールされていることを確認する

Apple Configurator 2で置き換え画面をキャプチャして、一時保存された.ipaファイルを取得する必要があるため、まずは対象のAppがiPhoneにインストールされていることを確認してください。
4. Mac 上の Apple Configurator 2 に戻る
表示された iPhone のホーム画面をダブルタップして情報ページに入ります。

「App」タブに切り替え -> 右上の「+ 追加」-> 「App」
App Storeアカウントにログインすると、これまでに購入したAppの一覧を取得できます。

バックアップしたい対象のAppを検索し、選択して「追加」をタップします。

この時、待機ウィンドウが表示され、「XXX」にAppを追加中、「XXX」をダウンロード中となります。
5. .ipa ファイルの抽出
ダウンロードが完了すると、すでにインストールされているアプリを置き換えるかどうかの確認ウィンドウが表示されます。

この時は何も操作しないでください。この時は何も操作しないでください。この時は何も操作しないでください。
私たちは Finder を開きます:
左上のツールバーで「移動」-> 「フォルダへ移動」を選択してください。

以下のパスを貼り付けてください:
~/Library/Group Containers/K36BKF7T3D.group.com.apple.configurator/Library/Caches/Assets/TemporaryItems/MobileApps
そうすれば、ダウンロードしてインストール準備中の対象Appの.ipaファイルを見つけることができます:

それをコピーするだけで App の .ipa ファイルのバックアップが完了します。
ファイルのコピーが完了したら、Apple Configurator 2 に戻り、停止をクリックして操作を終了します。
[骨] .ipa アプリインストールファイルの復元
同様に、復元したいAppのiPhoneをMacに接続し、Apple Configurator 2を開いて、Appの追加画面に入ります。

復元する場合は、左下の「Macから選択…」を選んでください。

バックアップする App の .ipa ファイルを選択し、「追加」を押します。

送信とインストールが完了するのを待ち、スマホに戻って再度アプリを開けば復活成功!
[肉] 最後の API レスポンスデータのバックアップ
ここでは、以前のApp End-to-End Testing Local Snapshot API Mock Server の記事(詳細な原理はそちらを参照)で使った方法と当時作成したオープンソースプロジェクトを活用します:
以前に API リクエスト&レスポンスを録画して E2E テストを行った技術と同様に、App の下架や停止前の最後の API リクエスト&レスポンスデータを記録するためにも利用できます。
1. mitmproxy をインストールする
brew install mitmproxy
mitmproxy はオープンソースの中間者攻撃およびネットワークリクエストのスニッフィングツールです。
もし Mitmproxy の中間者攻撃の仕組みに詳しくない場合は、以前の記事「APPはHTTPSで通信しているのに、データが盗まれた。」やMitmproxy公式ドキュメント を先にご覧ください。
純粋なネットワークリクエストのスニッフィングに使う場合、mimproxy のインターフェースに慣れていなければ、「Proxyman」を使うこともできます。使い方は以前の別の記事を参考にしてください。
2. mitmproxy の証明書設定を完了する
HTTPSの暗号化接続に対しては、ルート証明書の差し替えによる中間者攻撃を行う必要があるため、初回使用時にはまず端末でルート証明書のダウンロードと有効化を完了してください。
もしあなたの App と API サーバーが SSL Pinning を実装している場合、Pinning 証明書も mitmproxy に追加する必要があります。
-
まず、iPhone と Mac が同じネットワーク環境に接続されていることを確認してください。
-
WiFi がなく、パソコンが有線ネットワークに接続されている場合でも、MacのWiFi共有機能をオンにすることで、スマホを Mac のネットワークに接続できます。
Terminal で mitmproxy または mitmweb(Web GUI版)を起動します。
mitmproxy

この画面が表示されている場合、mitmproxy サービスが起動しています。現在はトラフィックがないため空です。この画面のままターミナルを閉じないでください。
- Mac のネットワーク設定で Mac の IP アドレスを確認する
スマホのWiFi設定に戻り、「i」をタップして詳細設定に入り、一番下の「プロキシ設定」を見つけます:


-
サーバーに Mac コンピュータの IP アドレスを入力する
-
ポート番号に 8080 を入力する
-
保存する
携帯で Safari を開き、以下を入力してください: http://mitm.it/
もし以下のような表示が出たら:
もしこれが見えるなら、トラフィックは mitmproxy を通過していません。
代表スマホのネットワークプロキシサーバーが正しく設定されていないか、Macで mitmproxy が起動していないことを示します。
正常な場合は以下のように表示されます:

この時点では HTTP トラフィックのみが傍受可能で、HTTPS トラフィックはエラーになります。引き続き設定を進めます。
接続成功を示し、iOSセクションで「Get mimproxy-ca-cert.pem」をクリックします。


- 「許可する」をクリックしてください
ダウンロードが完了したら、スマートフォンの設定に入り、「ダウンロード済みのプロファイル」が表示されるので、タップして進みます。



- クリックして入り、右上の「インストール」を押し、携帯のパスワードを入力してインストールを完了します。
設定に戻り -> 「一般」->「情報」-> 一番下の「証明書信頼設定」-> 「mitmproxy」を有効にする。


- 「続行」をタップして有効にします。
これで中間者攻撃のすべての準備が完了しました。
現在、あなたのスマホのすべての通信はプロキシサーバーを経由してMacから送信されます。操作が終わったら必ずスマホのネットワーク設定でプロキシサーバーの設定をオフにしてください。そうしないと、スマホのWiFiが外部ネットワークに接続できなくなります。
Terminal の mitmproxy に戻り、スマホの App を操作すると、捕捉されたすべての API リクエスト記録が見られます。

すべてのリクエストで詳細な Request & Response の内容を確認できます:

以上は mitmproxy の基本設定と実際の作業です。
3. API の解析と理解
次に、mitmproxy の mitmdump サービスを使い、以前開発した mitmproxy-rodo addons と組み合わせて、リクエストの録画と再生を行います。
私の実現原理 はリクエストのパラメータをハッシュ値に変換し、再生時に再度リクエストのハッシュを計算して、ローカルに同じハッシュ値のバックアップレスポンスがあればそれを返します。同じハッシュ値のリクエストが複数ある場合は順番に保存&再生されます。
まずは上記の方法で App の API を解析(または Proxyman を使用)し、どのフィールドがハッシュマッピングに影響するかを確認します。それらを記録し、後の設定で除外します。例えば、ある API は常に ?ts パラメータを含みますが、これは返却内容に影響せず、ハッシュ値の計算に影響してローカルのバックアップが見つからなくなるため、後の設定で除外する必要があります。
4.設定 mitmproxy-rodo :
私が作成した録画・再生用のオープンソーススクリプトを使用する。
詳細なパラメータ設定については、該当のオープンソースプロジェクトの説明を参照してください。
git clone [email protected]:ZhgChgLi/mitmproxy-rodo.git
cd mitmproxy-rodo
上記の手順 3 で選んだパラメータを config.json 設定ファイルに入力します:
{
"ignored": {
"*": {
"*": {
"*": {
"iterable": false,
"enables": [
"query",
"formData"
],
"rules": {
"query": {
"parameters": [
"ts",
"connect_id",
"device_id",
"device_name",
]
},
"formData": {
"parameters": [
"aidck",
"device_id",
"ver_name",
]
}
}
}
}
}
}
}
以上のパラメータはハッシュ値の計算時にすべて除外され、個別のエンドポイントパスに対して特定の除外ルールを設定することも可能です。
5. 録画を有効にして、Terminalで次のコマンドを実行:
mitmdump -s rodo.py --set dumper_folder=zhgchgli --set config_file=config.json --set record=true "~d zhgchg.li"
-
終わりの
"~d zhgchg.li"は、*.zhgchg.li のトラフィックのみをキャプチャすることを意味します。 -
dumper_folder: 出力先のディレクトリ名
6. スマホで対象のAppを操作し、録画したい操作の流れを実行する
-
アプリを再起動し、再インストールして、最もクリーンな状態で操作を開始することをお勧めします。
-
録画を併用すると、再現手順がわかりやすくなります。

操作しながら、出力ディレクトリに多くの取得した API レスポンスデータが表示されます。これは Domain -> API パス -> HTTP メソッド -> ハッシュ値 -> Header-X / Content-X の順に保存されます(同じハッシュのリクエストが2回あった場合は、順番に保存されます)。
-
再録画は出力フォルダを削除して、再度キャプチャを行うことができます。
-
返却されるデータに個人情報が含まれる場合は、取得内容を匿名化するように調整してください。
[肉] キャプチャした API レスポンスデータの再生
録画が完了したら、必ず一度再生してデータが正常かどうかを確認してください。もしハッシュヒット率が低い(再生時に対応するレスポンスがほとんど見つからない)場合は、嗅探の手順を繰り返し、毎回実行時にハッシュ値に影響を与える不確定な変数を特定して除外してください。
再生の実行:
mitmdump -s rodo.py --set dumper_folder=zhgchgli --set config_file=config.json
「mitmdump」コマンドは、mitmproxy のコマンドラインインターフェースで、HTTP/HTTPS トラフィックをキャプチャおよび操作します。
この例では、「rodo.py」スクリプトを実行し、「dumper_folder」と「config_file」の設定を指定しています。
-
dumper_folder: 出力先ディレクトリ名 -
デフォルトでは、ローカルにハッシュマッピングされていないレスポンスデータは直接404を返し、アプリは空白になります。これにより、取得したデータが有効かどうかを確認できます。


-
録画した際に通過したルートページが、再生時に再表示される:OK!
-
録画時に通っていないパスページを再生すると、ネットワークエラーが表示されます:OK!
回想
ここまでで、私たちは骨と最後の肉を復元することで、当時のAppが終着駅に向かう最後の時間を再現できるようになりました。これにより、皆で心を合わせて作り上げたあの時代を偲ぶことができます。
この記事をもって、初めての職場のチームと、ウェブのバックエンド開発から独学でiOSアプリ開発へ転職し、3~4ヶ月という短期間でゼロから独立して作り上げたことを記念します。Android、デザイナー、PM、上司、バックエンドの同僚と共に無事に完成させた製品は、まもなくライフサイクルの終点を迎えますが、当時の苦楽や初めてリリースされて使われた感動は永遠に忘れません。
「ありがとうございます」
ご協力のお願い
もし同じような後悔があるなら、本記事があなたの助けになれば幸いです。なぜなら、mitmproxy-rodo は当初POC(概念実証)として開発されたツールだからです。ぜひ貢献やバグ報告、PRによる修正を歓迎します。
Post は ZMediumToMarkdown によって Medium から変換されました。



コメント