コードのある部分がどうやって動いているか知る必要があるときや、誰かのバグに翻弄されているときは、ソースコードを見てみればよいのです…そこにソースコードがある限り。Conradがこの講演で紹介するたくさんのコンセプトやツールは、既存のアプリのリバースエンジニアリングだけではなく、いろいろなライブラリや自分のコードのデバッグにまで使えます。さらには、コードを注入したりネットワークトラフィックを調査したりして、LyftのiOSアプリをリバースエンジニアリングする技も披露します。このテクニックを使えば、App Storeにあるどんなアプリでも、その裏にひそむコードを明らかにすることができます。
イントロダクション (0:00)
Conrad Kramerです。WorkflowでiOS開発をしています。今から、iOSアプリのリバースエンジニアリングについて話したいと思います。ひとことで言えばリバースエンジニアリングとは、最終的な結果だけに基づいて仕組みを理解しようとすることです。iOSアプリで言えば、App Storeからダウンロードしてきた.appだけを手がかりに、ソースコードなしでそのアプリの仕組みを考えることです。
リバースエンジニアリングを行う際に困るポイントは2つあります。
-
オペレーティングツール自体があまりドキュメント化されておらず、極めてわかりづらいため、使うのが難しいことです。頻繁に動かなくなりますし、それを直せる人は誰もいません。したがってこれらのツールを使いこなせるようになるのも、そもそもどこで探せばいいのかわかるようになるのも難しいです。
-
これらのツールを使い出しても、調べなければいけないことは山ほどあり、他のアプリの中身のどの部分を見ればよいかを理解するのはとても大変です。さらに、リバースエンジニアリングする対象は、アプリだけではないのです。フレームワーク、例えばクローズドソースなAppleのフレームワークなどをリバースエンジニアリングしてもいいし、UIKitや誰かのアプリのバグを調べることもできるでしょう。
さて、リバースエンジニアリングにおいてまず最初に出てくるのは、「このバグの原因はなんだろう?UIのなかでどんなコンポーネントを使っているんだろう?」というような疑問です。例えば、このアプリはコレクションビューとテーブルビューのどっちを使っているのか知りたくなったら、実際にその答えを知ることができます。彼らのREST APIがどうなっているのか知りたくなったら、APIの仕組みも探ることができます。
Lyftのリバースエンジニアリング (1:44)
まず、実験台としてLyftのiOSアプリを使うことにしました。なぜなら、全てがSwiftで書かれているからです。最初にSwiftアプリについて知りたいのは何でしょうか。例えば、私はREST APIについて知りたい、Webや他のプラットフォームで自分のLyftクライアントを書きたいとします。APIがどうなっているのかを見て、そうすると「URLスキームはどうやって動作しているんだ?」という疑問が浮かびます。これは的を射た質問だと思います。というのも、みなさんの多くはiOSアプリを作っていて、Lyftへのディープリンクを張れるようになりたいと思っているでしょう。これにより、場所とライド(車)の種類を指定してリクエストしたり、というようなことができます。Workflowにとっては特に有用です。Workflowは自動化ツールで、多くの異なるアプリを統合しているからです。結果として、私はこういったことばかりやるようになりました。とりわけ、LyftのURLスキームはまだ公開されていませんからね。
Lyftを覗き見る (2:48)
Lyftアプリ自体はiTunesからダウンロードできる.IPAファイルで、本質的にはZIPファイルです。Info.plistなどのメタデータがたくさん含まれています。それに、アセットや画像、ローカライズ用の文字列、などなど。もちろん、実行可能な―面白いものが満載の―コンパイル済みコードも含まれています。それに加え、多くのフレームワークが入っています。なぜならLyftはSwiftで書かれており、Swiftはモジュールを推奨しているからです。
Lyftアプリは完全なブラックボックスです。中身を見ることはできませんが、ある角度から覗き見ることはできます。一つの方法としては、LyftアプリがLyftサーバーに送る全てのトラフィックを見ることです。これによってAPIとどのようにやり取りしているかのみならず、サーバーにどのような情報を送っているかを見ることができます。大抵は当たり障りのないアナリティクスなどです。
それから、コードを注入することもできます。これはとてもクールです。基本的にはアプリが動いている時にその生きているオブジェクトをいじることができます。そして動いていない時には、実行可能ファイルである.appがどのように構成されているかを確認するために、あるレンズを通してそのコードを見ることができます。
ネットワークトラフィックの調査 - Charlesのデモ (4:08)
Charlesは、トラフィックをインターネットに流れる前に傍受するHTTPSプロキシツールです。これを使って、Lyftアプリから出てくる全ての通信を確認できます。
LyftはSSLでトラフィックを暗号化していますが、Charlesの中間者攻撃と呼ばれるものを使えば、とても簡単に裏をかくことができます。Charlesは独自の証明書を発行するので、それをiOSデバイスで許可すれば、復号化を行い通常のHTTP通信として表示できます。
Lyftはアプリを使っている間、サーバーにあなたの位置情報を繰り返し送信します。Charlesでは、ライド(車)やキャンセルのリクエストも整形済みのJSON形式で表示され、レスポンスの中にはライドIDが入っています。Charlesを使えば、アプリがインターネットに接続して何をしているのかを全て見ることができます。
アプリにコードを注入する - Cycriptのデモ (7:44)
次のツールはとてもクールで、Cycriptといいます。他人のアプリにコードを注入するには、ジェイルブレイクを必要とします。Cycriptでは他の人のコードを調査することができますが、同様に自分自身のアプリにも使えます。これはObjective-CとJavaScriptのハイブリッドのツールで、ターミナルにObjective-Cのコードを入力するだけでそのアプリ内で実行することができます。LLDBよりも良いREPLを持っています。ブレークやブレークポイントの設定のようなことはできませんが、コードを実行することに関しては非常に素晴らしいです。こんなふうに、ほぼObjective-Cのようなコードをただ打ち込むだけで良いのです。
var application = [UIApplication sharedApplication];
[application openURL:[NSURL URLWithString:@"https://google.com"]];
ジェイルブレイクしたiPhone 6にSSHでログインした後、Lyftを起動し、Cycriptを動かします。すると、choose関数を使ってビューコントローラやアナリティクスのクラスなどのいかなるクラスのインスタンスでも取得することができます。また、開発者としては、APIキーへのアクセスを防ぐためにACLを設定するよう心がけてください。アプリがAPIキーを取得可能なら、Cycriptもまた取得可能だからです。
アプリ内のビューも変更可能です。例えば”Request Ride”ボタンを緑色に変えてみましょう。UIApp.keyWindow.recursiveDescriptionでボタンのメモリアドレスを見つけ、このように入力します。
var b = new Instance (ADDRESS)
b.backgroundColor = [UIColor greenColor]
CycriptのターミナルはTAB補完すら備えています。このような機能は、リバースエンジニアリングするのにクールなだけではなく、ちょっと新しい色を試してみたい、などで自分のアプリをデバッグするのにも使えます。
Q: Cycriptを使って自分のアプリをデバッグする場合でもデバイスがジェイルブレイクされている必要がありますか?
Conrad: 自分のアプリに対してならその必要はありません。ツールを自分のアプリに埋め込む方法はcycript.orgに書いてあり、今デモしたように接続してデバッグできます。
実行可能ファイルの復号化 - dumpdecrypted デモ (11:28)
次に、アプリが起動していない時の実際のコードを見てみましょう。これは少し複雑でジェイルブレイクを必要としますが、少し練習すれば簡単にできるようになります。各デバイス用にアプリなどを再署名可能なため、Appleはストアのアプリを暗号化し、他人と共有できないようにしています。しかし、ジェイルブレイクされたデバイスではアプリは復号化可能です。 これはdumpdecryptedというリポジトリをフォークしたものです。これを用いると、アプリをダンプすることができます。これはまた全てのフレームワークもサポートしているので便利です。というのも、今ではアプリはみんなフレームワークを持っているからです。
使い方は、クローンしてmake
し、ジェイルブレイクされたiPhoneで実行するアプリに対して実行するだけです。Lyftに対して実行すれば、何でも復号化してくれます。ファイルを見てみると、全てのフレームワークに.decrypted
という接尾詞がついているのがわかります。この中で興味深いのは、Lyft、LyftKit、LyftSDKですが、このアプリがSocketRocket、Stripe、Pusher、Mixpanelなどを使っているのもわかります。
実行可能ファイルの解析 - IDAデモ (13:47)
このダンプファイルを解析し実際のコードを見るには、IDAというツールを使います。これは、知っている方にはわかると思いますが、Hopperやclass-dumpに似たツールです。アプリをビルドする際、Xcodeは実行可能ファイルを作ります。これはアセンブリコードで構成されています。 IDAは実行可能ファイル内のアセンブリを整形して表示し、それらをつなげて、アプリケーションのアセンブリコードのグラフのように表示してくれます。IDAは高価ですが、フリー版でも必要なことはほとんどできます。ただし、64bitには対応していません。
Lyftが使うURLスキームを見つけるために、IDAの検索バーでopenURL
と打ち込んでみましょう。Objective-Cは、仕組みの透明性が高いため、この手のツールとかなり相性がいいです。Swiftは少しややこしく、ツールもまだあまり対応していません。
Swiftのアセンブリは冗長で、クラスの情報を抽出するのがより大変です。ですが、しばらくアセンブリを読む練習をすれば、探しているものを見つける方法がわかってくると思います。
IDAのグラフビューを使うことで _TZFV4Lyft15DeepLinkManager13handleOpenURLfMS0_FCSo5NSURLSb
を見つけることができます。これは見ての通りマングル化されていますが、このたくさんのでたらめな文字の中からLyftDeepLinkManager
、handleOpenURL
とNSURL
を読み取ることができます。このでたらめな文字はあまりドキュメント化されてはいませんが、興味深いことになかなか宣言的です。Mike AshのブログのFriday Q&Aで、これらのマングル化については全て説明されています。例えば_T
はSwiftのシンボルF
は関数を意味し4Lyft
はモジュール名になるなどです。
LyftのURLスキームを見つける (18:23)
このように、見える部分からURLスキームを見つけ出すには、開発者の立場で考えることが必要です。
URLスキームは次のように構成されます。
lyft://action?parameter=value
私たちは、action
が何なのか、また、取ることのできるparameter
の種類を見つけようとしています。そして、ディープリンクがどのように動作するのかを探って、SwiftのプロトコルのDeepLinkAble
を見つけました。ということは、これにくっついているものはリンクを表しています。DeepLinkAble
をIDAで検索すると、コンビニエントクラスクラスタの中に、ride
、help
、invite
、profile
など、いろいろな種類のリクエストオブジェクトが見つかります。
ライド画面を開く方法を知りたいので、どこかにあるLyftのDeepLinkToRide
クラスを見つけて、それがどう動いているのかを見てみましょう。これをするのに、アセンブリの知識は必要ありません。必要な情報のために、アセンブリを検索してスキャンする方法さえ知っていれば大丈夫です���フランス語を知らなくても、ずっとフランス語を見ていれば、最終的にはその中から何かを理解することができるでしょう。IDAが表示するもののほとんどは、まるで外国語のようです。Swiftでリバースエンジニアリングするのはこれがはじめてですが、それでも基本的なところはわかります。
DeepLinkToRide
のグラフをブラウズすると、”pickup”、”[latitude]”、”[longitude]”、”destination”などの文字列を特定することができます。さらに、”ridetype”というのもあります。いくつかの異なるURLを用いて試してみたところ、これがactionだとわかりました。また”ridetype”を調べてみて、”lyft”、”lyft_line”、”lyft_plus”、”access”というものも見つけました。これらはライドの種類です。
これらの異なったURLのパーツを組み立てていろんなリクエストで実験してみて、ライドをリクエストするためのスキームは次のとおりだとわかりました。
lyft://ridetype
?id=lyft_line
&pickup[latitude]=0
&pickup[longitude]=0
&destination[latitude]=0
&destination[longitude]=0
これでようやく仕組みがわかったので、Workflowに組み込むことができます。これがバイナリ解析のやり方です。ややこしくて手ごわそうに見えますが、開発者の立場で考えれば、とりあえずやってみてパターンマッチをしてみるのはそこまで大変ではありません。
Q&A (22:50)
Q: Certificate Pinningは中間者攻撃を防ぐのに役立ちますか?
Conrad: SSL Pinningは中間者攻撃を防ぐのに使えるテクニックです。これは実際のところ、ジェイルブレイクされていない世界中のiPhoneにはとてもよい方法だし、非常に効果的です。例えばTwitterはSSL Pinningを行っています。しかし、Cycriptでのリバースエンジニアリングでは、簡単にこれを迂回することができます。例えばAFNetworkingのSSL PinningにはSSLPinningMode
というプロパティがありますが、Cycriptを開いて、これをnone
にするだけです。もしiPhoneがジェイルブレイクされていて、誰かに乗っ取られた場合、トラフィックを見られるのを防ぐ方法はありません。もしジェイルブレイクされていなければ、SSL Pinningは非常に良い防御手段となります。
Q: cocoapods-keysなどのツールを使った文字列の難読化については、何をおすすめしますか?
Conrad: 文字列の難読化は次のようなことに有用です。
- AppleのフレームワークのプライベートAPIを使っている場合は、アプリのレビューの間、それを隠しておくのに便利です。レビュアーは自動化されたスキャンを行うからです。
- ジェイルブレイクされていないiPhoneを持っている人、または難読化を解除する方法を知らない人の場合、アプリ内の文字列を簡単には見つけることはできません。
とはいえ、文字列を永遠に隠しておくことはできません。例えばCycriptでは、swizzleにより難読化を回避できます。難読化は、保証された方法ではないですが、場合によっては良い対抗手段です。
Q: class-dumpはdumpdecrypedとどう違うのでしょうか?
Conrad: これらは現に別のツールです。dumpdecryptedは、App Storeによる暗号化バイナリを復号化するものです。それからclass-dumpは、IDAのように、暗号化されていないバイナリからObjective-Cのインターフェースファイルを取得するものです。残念ながら、Swiftに対しては使えません。
Q: これのようなAppleのフレームワークを明らかにするGitHubのライブラリも、class-dumpを使っているのでしょうか?
Conrad: はい。Appleのフレームワークは暗号化されていないため、容易にclass-dumpすることが可能です。多くの人がAppleのプライベートインターフェースをGitHubにアップロードしているので、中を見てみることができます。とても便利ですね。
Q: この手のリバースエンジニアリングには、法的問題はないのでしょうか?Lyftは彼らのプライベートなコードを盗み見られることに対して反対ではないでしょうか?
Conrad: 法的にはあまりにきびしい追求はないと思いますが、ケースバイケースで判断してください。URLスキームを調査していることは、パートナーに話したほうが良いです。これまで、私たちはそうしています。例えば、今日はLyftのURLスキームを発見したので、あとでLyftと話して、承認の上でWorkflowに統合します。他人のアプリの内部のものを明らかにすることに関しては、倫理的な懸念は確実にあります。ですが、リバースエンジニアリングは、私たち開発者がアプリを不正行為から守る助けになることもあります。例えば、この手のリバースエンジニアリングによって、Twitterが皆さんのデバイスにインストールされているアプリ一覧をサーバーに送っている、ということが明らかになりました。したがって良くも悪くも悪くもなるのです。リバースエンジニアリングは、適切に使われるべきであるひとつのツールにすぎません。
Q: Appleのフレームワークのディスアセンブルする工程はどのようになりますか?
Conrad: この工程は、私が行ったデモに近いですが、iPhoneをジェイルブレイクする必要はありません。iTunesは、iPhoneをコンピュータに繋いだ時に、デバイスからシンボルを取得しています。 Xcodeの中でディレクトリを見つけて(”device symbols”というような名前になっています)、AppleのフレームワークをIDAやclass-dumpで開けば、解析できます。これらは暗号化されていないので利用可能です。これは、私もやらざるを得なかったことがありますが、ベータ版のiOSのUIKitのバグを直したり、Swizzleしてパッチをあてるときに、ものすごく便利です。
���訳:岩谷 明 Akira Iwaya
校正・校閲:Yuko Honda Morita
About the content
This content has been published here with the express permission of the author.