Androidソース解析の初歩を、CTFの過去問を使って学ぼう!

Android

こんにちは、まるやまです。
今回は、SECCON 2015 CTFの過去問を通して、Androidの解析について学びたいと思います。

SECCON とは

本題ではないので、詳細は公式ページをご確認ください。

過去問

githubにて、問題が公開されています。
https://github.com/SECCON/SECCON2015_online_CTF

今回は、Binary/100_Reverse-Engineering Android APK 1 を解きます。

問題内容

じゃんけんに1000回連続で勝ち続けよ

よっしゃ任せろ!!・・・

はい。

現実的には不可能なので、1000回勝てたら何が起こるのか、コードを見る必要があります。

apk -> dex

まずはソースを見るために、apkファイルを展開しましょう。
rps.apkをリネームし、rps.zipにします。

リネーム後、zipファイルを展開します。
展開したディレクトリ内は以下のようになっていました。

AndroidManifest.xml
META-INF/
classes.dex
lib/
res/
resources.arsc

dex -> jar

dexファイルをそのまま閲覧することは難しいので、jarファイルに変換します。
変換にはdex2jarを利用します。

リンクよりdex2jarをダウンロードし、任意のディレクトリへ展開してください。
今回はmac環境なので、ユーザーディレクトリ直下に配置しました。

shの実行権限を与えます。

$ cd ~/ユーザー名/dex2jar-2.0/
$ chmod +x *

これで、dex2jarの利用準備が完了しました。早速、classes.dexをjarに変換しましょう。

$ sh dex2jar-2.0/d2j-dex2jar.sh <classes.dexのパス>
dex2jar ../Downloads/SECCON2015_online_CTF-master/Binary/100_Reverse-Engineering Android APK 1/rps/classes.dex -> ./classes-dex2jar.jar

変換されたjarは、デフォルトではカレントディレクトリに配置されるようです。
特にエラーもないようなので、次のステップへ。

jarの展開

変換後のjarはclasses-dex2jar.jarとなっています。
zipへリネームするついでに、分かりやすいように名前をつけましょう。(zipにすれば何でも良いです。)

classes-dex2jar.jar -> question.zip

リネーム後、zipファイルを展開します。
展開したディレクトリ内は以下のようになっていました。

android/
com/

.classファイルの閲覧

展開したディレクトリの内部を辿ります。
com/example/seccon2015/rock_paper_scissors/

内部に、MainActivity.classがありました。
Android Studioにドラッグして閲覧してみましょう!

詳細は実際に見て頂くことにして、重要な部分を見ていきます。

private final Runnable showMessageTask = new Runnable() {
    public void run() {
        // 省略します。
        if(1000 == MainActivity.this.cnt) {
            var1.setText("SECCON{" + String.valueOf((MainActivity.this.cnt + MainActivity.this.calc()) * 107) + "}");
        }
        MainActivity.this.flag = 0;
    }
};

1000回目のカウントで何やらメッセージ表示しています。
SECCON{(cnt + calcの結果)*107} を出力しているようです。

cntは1000なので、calcの結果が分かれば、解答できそうですね!
では、calcメソッドを見てみましょう!

public native int calc();

WTF!?

さすがに、class展開しただけでは許してくれないようです。
nativeメソッドなので、どう頑張っても見られません。

NDKを使っているようなので、loadLibraryがないでしょうか・・・

static {
    System.loadLibrary("calc");
}

ありました:D
では、このcalcが何をやっているのか、見てみましょう。

calcの処理

apk -> dex で確認したディレクトリを再度開いてください。
lib/x86/libcalc.soを解析します。
逆アセンブルをする必要があるので、利用している環境に合わせて、お好きなツールをご利用ください。

今回は、Hopperを利用します。
フリー版には制限がありますが、特に問題ありません。(本格的に学ぶ方は購入すると良いでしょう。)

Hopperを開き、libcalc.soを解析します。

             Java_com_example_seccon2015_rock_1paper_1scissors_MainActivity_calc:
00000400         mov        eax, 0x7
00000405         ret
                        ; endp

ちょっとあっさりしすぎですが、7が返却されるようです。

解答

ということで、

SECCON{(1000 + 7)*107}より、

解答は SECCON{107749} となります。

Binary/100_Reverse-Engineering Android APK 1内にanswer.txtがあるので、確認してみてください。

おわりに

所謂、サービス問題のようですが、apkファイルの展開と、
NDKが利用されている場合に動作を見る方法、を確認できました。