こんにちは、freee でモバイルエンジニアを担当している @laprasDrum です。
この記事は freee Engineers Advent Calendar 2015 20日目です。
アプリ開発に行き詰まったとき、ググっても道が拓けないときなんてよくあることです。
そんなときは手元の端末に入ってるアプリに目を落とすと、意外なヒントが得られることもあったりなかったり。
ということで覚えていて損はない apk ファイルの解析について知見を共有します。
章立てとしては以下の3段です。
- apk を取り出す
- apk からアプリの設定をのぞく (AndroidManifest.xml)
- apk から Java コードをのぞく
今回は Evernote の中身をのぞいてみましょう。
目的の apk を端末から取り出す
Android SDK Tools をインストールして、Android 端末を接続しましょう。
まずは端末にインストールされたアプリをリスト出力します。
今回は Evernote の情報だけがほしいのでよしなにgrep
しましょう。
$ adb shell pm list packages -f | grep evernote
package:/data/app/com.evernote.skitch-1/base.apk=com.evernote.skitch
package:/data/app/com.evernote-1/base.apk=com.evernote
1つ目は skitch なので2つ目の/data/app/com.evernote-1/base.apk
が目当ての apk となります。
では取り出しましょう。
$ adb pull /data/app/com.evernote-1/base.apk
7655 KB/s (21830042 bytes in 2.784s)
$ ls -l | grep apk
-rw-r--r-- 1 laprasDrum staff 21830042 9 17 20:24 base.apk
複数の端末をPCに接続している場合
PC に 2台以上 (エミュレーター含む) 接続している場合、adb
コマンドを実行する端末を指定する必要があります。
実際に2台接続しているときはこんな感じです。
$ adb devices
List of devices attached
192.168.57.101:5555 device
ZX1G524VFK device
もし2台目にのみコマンドを実行したい場合は-s (端末ID = ZX1G524VFK)
オプションを加えましょう。
$ adb -s ZX1G524VFK shell pm list packages -f | grep evernote
コンバート
apk ファイルを解凍しましょう。
拡張子を zip に変更して展開しても OK です。
$ unzip -d evernote base.apk
$ ls -l evernote
total 30776
-rw-r--r-- 1 laprasDrum staff 94024 8 27 17:49 AndroidManifest.xml
drwxr-xr-x 6 laprasDrum staff 204 9 17 20:33 META-INF
-rw-r--r-- 1 laprasDrum staff 0 8 27 17:49 PullParser2.1.10_VERSION
drwxr-xr-x 23 laprasDrum staff 782 9 17 20:33 assets
-rw-r--r-- 1 laprasDrum staff 9232936 8 27 17:49 classes.dex
-rw-r--r-- 1 laprasDrum staff 4172 8 27 17:49 contact-note-template.enml
drwxr-xr-x 3 laprasDrum staff 102 9 17 20:33 dtds
drwxr-xr-x 3 laprasDrum staff 102 9 17 20:33 lib
drwxr-xr-x 3 laprasDrum staff 102 9 17 20:33 org
drwxr-xr-x 48 laprasDrum staff 1632 9 17 20:33 res
-rw-r--r-- 1 laprasDrum staff 6418108 8 27 17:46 resources.arsc
ズラッと出てきましたが、特に大事なのはAndroidManifest.xml
とclasses.dex
です。
AndroidManifest.xml
はその名の通りアプリのマニフェストで、アプリIDやパーミッション、開発 SDK のバージョン、画面名やサービス名が含まれます。
classes.dex
は Dalvik VM 上で起動する実行ファイルです。
XML をのぞく
AXMLPrinter2.jar でバイナリ XML をのぞきましょう。
$ cd evernote
$ java -jar AXMLPrinter2.jar AndroidManifest.xml > ConvertedManifest.xml
ざっと見るだけでもパーミッション多いですね。
android.
から始まっていないcom.android.vending.BILLING
は In App Billing に必要なパーミッションです。
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:amazon="http://schemas.amazon.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1071403"
android:versionName="7.1.4"
android:installLocation="0"
package="com.evernote"
platformBuildVersionCode="21"
platformBuildVersionName="5.0.1-1624448"
>
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="21"
>
</uses-sdk>
<uses-permission
android:name="com.android.vending.BILLING"
>
</uses-permission>
<uses-permission
android:name="android.permission.INTERNET"
>
</uses-permission>
<uses-permission
android:name="android.permission.RECORD_AUDIO"
>
</uses-permission>
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE"
>
</uses-permission>
<uses-permission
android:name="android.permission.ACCESS_COARSE_LOCATION"
>
</uses-permission>
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"
>
...
dex をのぞく
dex2jar で .class ファイルを取り出します。
$ chmod +x dex2jar-2.0/*
$ ./dex2jar-2.0/d2j-dex2jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar
JD-GUI で jar ファイルを開くと、デコンパイルされた Java のコードが読めます。
難読化されているので若干読みにくいですね。
試しに、最初に起動する画面のソースでも探してみましょう。
AndroidManifest.xml 上で最初に起動すべき画面 (activity) にandroid.intent.action.MAIN
という属性を明記する必要があるので、それを頼りに探します。
<application
android:label="@7F0E014F"
android:icon="@7F020302"
android:name="com.evernote.Evernote"
android:allowBackup="false"
android:hardwareAccelerated="true"
android:largeHeap="true"
>
...
<activity
android:theme="@7F0F0153"
android:name="com.evernote.ui.HomeActivity"
android:configChanges="0x000004A0"
android:alwaysRetainTaskState="true"
>
<intent-filter
>
<action
android:name="android.intent.action.MAIN"
>
</action>
<category
android:name="android.intent.category.LAUNCHER"
>
</category>
<category
android:name="android.intent.category.DEFAULT"
>
</category>
<category
android:name="android.intent.category.MULTIWINDOW_LAUNCHER"
>
</category>
</intent-filter>
</activity>
...
どうやらHomeActivity
がそれらしいですね。
検索してみましょう。
いましたね。
ここからコードを読んでヒントを探したり、次に起動する画面を辿ったりします。
気になるアプリがあれば是非試してみてください。
freee には技術を熱く語り、体験設計やチームを大事にして、ビジネス向けアプリの未来を創っていこうとするエンジニアだらけです。
そんなワクワクに飛び込んでみたいモバイルエンジニアの方々、是非一度遊びに来てください。∩(・ω・)∩
明日は @ymrl や @joe-re と並んでフロントエンドに革命を起こす自称穏健派 ∧ freee ボルダリング部部長 ∧ ラーメンより蕎麦愛の強い @tohashi です。お楽しみに。