Android リバースエンジニアリング

こんにちは、freee でモバイルエンジニアを担当している @laprasDrum です。
この記事は [freee Engineers Advent Calendar 2015](http://qiita.com/advent-calendar/2015/freee) 20日目です。

-----

アプリ開発に行き詰まったとき、ググっても道が拓けないときなんてよくあることです。
そんなときは手元の端末に入ってるアプリに目を落とすと、意外なヒントが得られることもあったりなかったり。

ということで覚えていて損はない apk ファイルの解析について知見を共有します。

章立てとしては以下の3段です。

- apk を取り出す
- apk からアプリの設定をのぞく (AndroidManifest.xml)
- apk から Java コードをのぞく

今回は Evernote の中身をのぞいてみましょう。

# 目的の apk を端末から取り出す
[Android SDK Tools](https://developer.android.com/intl/ja/sdk/index.html#Other) をインストールして、Android 端末を接続しましょう。
まずは端末にインストールされたアプリをリスト出力します。
今回は Evernote の情報だけがほしいのでよしなに``grep``しましょう。

```shell-session
$ 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](https://evernote.com/intl/jp/skitch/) なので2つ目の``/data/app/com.evernote-1/base.apk``が目当ての apk となります。

では取り出しましょう。

```shell-session
$ 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台接続しているときはこんな感じです。

```shell-session
$ adb devices
List of devices attached
192.168.57.101:5555	device
ZX1G524VFK	device
```

もし2台目にのみコマンドを実行したい場合は``-s (端末ID = ZX1G524VFK)``オプションを加えましょう。

```shell-session
$ adb -s ZX1G524VFK shell pm list packages -f | grep evernote
```


# コンバート
apk ファイルを解凍しましょう。
拡張子を zip に変更して展開しても OK です。

```shell-session
$ 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](https://code.google.com/p/android4me/downloads/detail?name=AXMLPrinter2.jar&) でバイナリ XML をのぞきましょう。

```shell-session
$ cd evernote
$ java -jar AXMLPrinter2.jar AndroidManifest.xml > ConvertedManifest.xml
```

ざっと見るだけでもパーミッション多いですね。
``android.``から始まっていない``com.android.vending.BILLING``は In App Billing に必要なパーミッションです。

```xml:ConvertedManifest.xml
<?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](https://github.com/pxb1988/dex2jar) で .class ファイルを取り出します。

```shell-session
$ chmod +x dex2jar-2.0/*
$ ./dex2jar-2.0/d2j-dex2jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar
```

[JD-GUI](http://jd.benow.ca/) で jar ファイルを開くと、デコンパイルされた Java のコードが読めます。
難読化されているので若干読みにくいですね。

![jdgui.png](https://qiita-image-store.s3.amazonaws.com/0/7950/9c27679a-1619-c44f-c2a1-ccbcfb08d5f3.png)

試しに、最初に起動する画面のソースでも探してみましょう。
AndroidManifest.xml 上で最初に起動すべき画面 (activity) に``android.intent.action.MAIN``という属性を明記する必要があるので、それを頼りに探します。

```xml:ConvertedManifest.xml
<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``がそれらしいですね。
検索してみましょう。

![search.png](https://qiita-image-store.s3.amazonaws.com/0/7950/54fe6e8f-2e7c-bf6e-0019-3f0482acc510.png)

いましたね。
ここからコードを読んでヒントを探したり、次に起動する画面を辿ったりします。

気になるアプリがあれば是非試してみてください。

-----

freee には技術を熱く語り、体験設計やチームを大事にして、ビジネス向けアプリの未来を創っていこうとするエンジニアだらけです。
[そんなワクワクに飛び込んでみたいモバイルエンジニアの方々、是非一度遊びに来てください。](http://jobs.jobvite.com/freee/job/om741fwi)∩(･ω･)∩

明日は @ymrl や @joe-re と並んでフロントエンドに革命を起こす自称穏健派 ∧ freee ボルダリング部部長 ∧ ラーメンより蕎麦愛の強い @tohashi です。お楽しみに。