CORDEA blog

Android applications engineer

動的解析ツール Frida を Android に使う

Frida を使ってみたメモ

Frida

frida.re

自分の Script を Inject したり、値を取得したり、色々なことができる Toolkit です。
リバースエンジニアリングとかする時に使うみたいですね。
iOS / Android にも対応しており、今回は Android の話です。
ちなみに日本語の記事もいくつかあります。

もし試す場合は自分のアプリや許可されているものを使用して試してください。
ここからの手順やコマンド、コードによって何が起きても私は責任を取りません。

入れてみる

Rooted device のほうが簡単らしいのですが、持ってないので Without root で試します。
肝心の手順はここに全部書いており、写すことはしないのでこちらを参照してください。

koz.io

簡単に手順を書くと

1. apk を device から抜く (pm path とかしてから pull する)
2. apktool を用いて apk を decode する
3. 諸々いじった後、libfrida-gadget.so を lib/ に入れる
4. apktool で apk を build
5. jarsigner で署名 / 検証
6. zipalign で最適化

という感じです。

3 での諸々いじる工程もすべて上記の記事に書いてあります。

補足
  • AndroidManifest.xmlandroid:extractNativeLibs が false になっている場合は true にしないと Install 時に失敗します。
  • 最新の Frida は動かないという Issue が上がっており、実際に私も動かなかったので frida-gadget-12.7.26-android-*.so を使用しています。
  • frida-gadget を load するタイミングは onCreate のはじめとかでいいと思います

使ってみる

試したコードとアプリはここにあります。以降はこのデモ app を使用しています。

github.com

準備

Frida の install が終わっていない場合は

$ pip install frida-tools

さて、Frida は Python で書くのですが、Inject する Sctipt は JavaScript です。
ということでまずはベースとなる Python の Script が必要になります。

こちらをベースにして、

import sys
import frida


def on_message(message, data):
    print(message)


js = """
Java.perform(function() {
});
"""

process = frida.get_usb_device().attach('Gadget')

script = process.create_script(js)
script.on('message', on_message)
script.load()

sys.stdin.read()

こんな感じです。今回は Gadget を使っているので attach('Gadget') となります。
アプリが待ち受けている状態等で、

$ python example.py

を実行すると何も起きないはずです。
エラーが出る場合はおそらくどこか間違えています。

Detector の返す値を変更する

Repository を見てもらうと分かりますが jp.cordea.fridademo.Detector という Class があり、Detector#detect が false を返しています。
Detector#detect が true だと Button を click した際に toast が表示されます。ということで toast が表示されるようにします。

var detector = Java.use('jp.cordea.fridademo.Detector');
detector.detect.overload().implementation = function() {
    return true;
}

Button を click すると toast が表示されるはずです。

fab の click を上書きする

続いて fab の click を上書きします。まず fab を取得する必要があります。

var fabId = activity.findViewById(0x7f080069);
var fab = Java.cast(
    fabId.$handle,
    Java.use('com.google.android.material.floatingactionbutton.FloatingActionButton')
);

findViewById で fab を取得し、FloatingActionButton に cast しています。
なお、この ID はデモ app の場合は smali/jp/cordea/fridademo/R\$id.smali を検索することで取得できます。

Listener も必要です。

var listener = Java.use('android.view.View$OnClickListener');

そして Listener をセットします。

fab.setOnClickListener(Java.registerClass({
    name: 'jp.cordea.fridademo.OnClickListener',
    implements: [listener],
    methods: {
        onClick: function(v) {
        }
    }
}).$new());
TextView の count を上書きする

さて、先程 fab の click を上書きしたので今まで click 毎に +1 されていた TextView の count が動かなくなりました。
これを *2 するようにしてみましょう。

var textViewId = activity.findViewById(0x7f0800de);
var textView = Java.cast(
    textViewId.$handle,
    Java.use('android.widget.TextView')
);

そしてさきほどの onClick の中に *2 する実装を入れます

var count = 1;
fab.setOnClickListener(Java.registerClass({
    name: 'jp.cordea.fridademo.OnClickListener',
    implements: [listener],
    methods: {
        onClick: function(v) {
            count *= 2;
        }
    }
}).$new());

そして上書きしますが、java.lang.String としてセットする必要があります。
なので、以下のようにセットします。

var count = 1;
fab.setOnClickListener(Java.registerClass({
    name: 'jp.cordea.fridademo.OnClickListener',
    implements: [listener],
    methods: {
        onClick: function(v) {
            count *= 2;
            var string = Java.use('java.lang.String');
            textView.setText(string.$new(count.toString()));
        }
    }
}).$new());

これで fab を click すると +1 ではなく *2 で値が増えていく様子が見られると思います。
詳しくは Repository の example.py を見てください。

感想

使うためにはそれなりに知識が必要そうです。
Android の解析をするなら Android の知識、Frida の知識、Smali も軽く読める必要があります。
今回は難読化してないですが、難読化されている場合はそのあたりの知識も必要かもしれないですね。根気があればなんとかなりそうな気もしますが...

あと、ネイティブアプリエンジニアの観点から、これらにどう対処するかという話に興味がある場合
anti-frida や anti-reverse engineering で検索すると色々でてきますので見ると良いと思います。
中でも良かったものを 2 つ置いておきます