Edited at

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

More than 1 year has passed since last update.

こんにちは、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.xmlclasses.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 に必要なパーミッションです。


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 で .class ファイルを取り出します。

$ chmod +x dex2jar-2.0/*

$ ./dex2jar-2.0/d2j-dex2jar.sh classes.dex
dex2jar classes.dex -> ./classes-dex2jar.jar

JD-GUI で jar ファイルを開くと、デコンパイルされた Java のコードが読めます。

難読化されているので若干読みにくいですね。

jdgui.png

試しに、最初に起動する画面のソースでも探してみましょう。

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


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

いましたね。

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

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


freee には技術を熱く語り、体験設計やチームを大事にして、ビジネス向けアプリの未来を創っていこうとするエンジニアだらけです。

そんなワクワクに飛び込んでみたいモバイルエンジニアの方々、是非一度遊びに来てください。∩(・ω・)∩

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