MASTG-TOOL-0001 Frida for Android

Frida は Java API を通じて Android Java ランタイムとのインタラクションをサポートしています。プロセスとそのネイティブライブラリ内で Java とネイティブの両方の関数をフックして呼び出すことができます。JavaScript スニペットはメモリにフルアクセスでき、たとえば任意の構造化データを読み書きできます。

Frida API が提供するタスクのうち、Android に関連するものや Android 専用のものをいくつか紹介します。

  • Java オブジェクトをインスタンス化し、静的および非静的クラスメソッドを呼び出します (Java API)。

  • Java メソッド実装を置き換えます (Java API)。

  • Java ヒープをスキャンして、特定のクラスのライブインスタンスを列挙します (Java API)。

  • プロセスメモリをスキャンして文字列の存在を探します (Memory API)。

  • ネイティブ関数呼び出しをインターセプトして、関数の開始時と終了時に独自のコードを実行します (Interceptor API)。

Android では、Frida CLI (frida)、frida-psfrida-ls-devicesfrida-trace など、Frida のインストール時に提供されるビルトインツールも利用できることを心に留めてください。

Frida は Xposed とよく比較されますが、両フレームワークは異なる目標を念頭に設計されているため、この比較は公平とは言えません。アプリのセキュリティテスト担当者として、どのような状況でどちらのフレームワークを使うべきかを知るために、これを理解しておくことは重要です。

  • Frida はスタンドアロンであり、必要なのはターゲットの Android デバイスの既知の場所から frida-server バイナリを実行するだけです (下記の「Fridaのインストール」を参照)。つまり、Xposed とは対照的に、ターゲット OS に 深く インストールされるわけではありません。

  • アプリのリバースは反復的なプロセスです。前のポイントの結果として、フックを適用したり単純に更新するために (ソフト) リブートする必要がないため、テスト時のフィードバックループがより短くなります。そのため、より永続的なフックを実装する場合には Xposed を使用する方がいいかもしれません。

  • プロセスの実行中にいつでもオンザフライで Frida JavaScript コードを注入したり更新できます (iOS の Cycript と同様)。このようにして、Frida にアプリを生成させることで、いわゆる 早期計装 を実行できます。また、特定の状態にした実行中のアプリにアタッチすることも可能です。

  • Frida は Java とネイティブコード (JNI) の両方を処理でき、その両方を変更できます。これは残念ながらネイティブコードのサポートがない Xposed の制限です。

Xposed は 2019 年初頭の時点ではまだ Android 9 (API レベル 28) では動作しないことに注意してください。

Android に Frida をインストールする

Android デバイスに Frida をセットアップするには、以下のようにします。

  • デバイスがルート化されていない場合は、Frida を使用することもできます。Termux を参照してください。

  • ルート化済みデバイスをお持ちの場合は、公式の手順 に従うか、以下のヒントに従ってください。

ここでは特に断りがない限り、ルート化済みデバイスを想定しています。Frida リリースページ から frida-server バイナリをダウンロードします。Android デバイスまたはエミュレータのアーキテクチャ (x86, x86_64, arm, arm64) に適した frida-server バイナリをダウンロードしてください。サーバーバージョン (少なくともメジャーバージョン番号) がローカルの Frida インストールのバージョンと一致することを確認してください。通常 PyPI は最新の Frida をインストールします。どのバージョンがインストールされているかわからない場合は、Frida コマンドラインツールで確認できます。

frida --version

あるいは、以下のコマンドを実行して、Frida のバージョンを自動的に検出し、適切な frida-server バイナリをダウンロードできます。

wget https://github.com/frida/frida/releases/download/$(frida --version)/frida-server-$(frida --version)-android-arm.xz

frida-server をデバイスにコピーして実行します。

adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "su -c /data/local/tmp/frida-server &"

Android で Frida を使用する

frida-server が動作していれば、以下のコマンドで実行中のプロセスのリストを取得できるはずです (接続されている USB デバイスまたはエミュレータを使用するように Frida に指示するには -U オプションを使用します)。

$ frida-ps -U
  PID  Name
-----  --------------------------------------------------------------
  276  adbd
  956  android.process.media
  198  bridgemgrd
30692  com.android.chrome
30774  com.android.chrome:privileged_process0
30747  com.android.chrome:sandboxed
30834  com.android.chrome:sandboxed
 3059  com.android.nfc
 1526  com.android.phone
17104  com.android.settings
 1302  com.android.systemui
(...)

あるいは、-Uai フラグの組み合わせでリストを制限し、接続されている USB デバイス (-U) に現在インストールされている (-i) すべてのアプリ (-a) を取得します。

$ frida-ps -Uai
  PID  Name                                      Identifier
-----  ----------------------------------------  ------------------------------
  766  Android System                            android
30692  Chrome                                    com.android.chrome
 3520  Contacts Storage                          com.android.providers.contacts
    -  Uncrackable1                              sg.vantagepoint.uncrackable1
    -  drozer Agent                              com.mwr.dz

これは、すべての名前と識別子を表示します。現在実行中の場合には、それらの PID も表示します。リストでアプリを検索し、PID またはその名前/識別子を書き留めます。今後はそれらのいずれかを使用してアプリを参照します。PID はアプリを実行するたびに変化するため、識別子を使用することをお勧めします。たとえば com.android.chrome を取得してみましょう。この文字列は Frida CLI、frida-trace、Python スクリプトなど、すべての Frida ツールで使用できるようになりました。

frida-trace でのネイティブライブラリのトレース

特定の (低レベルの) ライブラリコールをトレースするには、frida-trace コマンドラインツールを使用できます。

frida-trace -U com.android.chrome -i "open"

これは __handlers__/libc.so/open.js に小さな JavaScript を生成し、Frida がプロセスに注入します。このスクリプトは libc.soopen 関数へのすべてのコールをトレースします。生成されたスクリプトは必要に応じて Frida JavaScript API で変更できます。

残念ながら Java クラスの高レベルメソッドのトレースはまだサポートしていません (しかし 将来的には するかもしれません)。

Frida CLI と Java API

Frida CLI ツール (frida) を使用して、Frida を対話的に操作します。プロセスにフックし、Frida の API へのコマンドラインインタフェースを提供します。

frida -U com.android.chrome

-l オプションで、Frida CLI を使用してスクリプトをロードすることもできます。たとえば myscript.js をロードします。

frida -U -l myscript.js com.android.chrome

Frida は Java API も提供しており、Android アプリを扱うのに特に役立ちます。これは Java クラスやオブジェクトを直接操作できます。Activity クラスの onResume 関数を上書きするスクリプトを以下に示します。

Java.perform(function () {
    var Activity = Java.use("android.app.Activity");
    Activity.onResume.implementation = function () {
        console.log("[*] onResume() got called!");
        this.onResume();
    };
});

上記のスクリプトは Java.perform をコールして、コードが Java VM のコンテキストで実行されるようにします。Java.use を介して android.app.Activity クラスのラッパーをインスタンス化し、onResume 関数を上書きします。新しい onResume 関数実装は、アプリでアクティビティが再開されるたびに、コンソールに通知を出力し、this.onResume を呼び出して、オリジナルの onResume メソッドをコールします。

jadx はグラフィカルコードブラウザを通じて Frida スニペットを生成できます。この機能を使用するには、jadx-gui で APK または DEX を開き、ターゲットのメソッドをブラウズしてメソッド名を右クリックし、"Copy as frida snippet (f)" を選択します。たとえば MASTG Android UnCrackable L1 を使用すると以下のようになります。

上記の手順により、以下の出力がペーストボードに配置され、JavaScript ファイルに貼り付けて frida -U -l に渡すことができます。

let a = Java.use("sg.vantagepoint.a.a");
a["a"].implementation = function (bArr, bArr2) {
    console.log('a is called' + ', ' + 'bArr: ' + bArr + ', ' + 'bArr2: ' + bArr2);
    let ret = this.a(bArr, bArr2);
    console.log('a ret value is ' + ret);
    return ret;
};

上記のコードは sg.vantagepoint.a.a クラスの a メソッドをフックし、その入力パラメータと戻り値をログ記録します。

Frida はヒープ上にあるインスタンス化されたオブジェクトを検索して操作することもできます。以下のスクリプトは android.view.View オブジェクトのインスタンスを検索して、その toString メソッドをコールします。結果はコンソールに出力されます。

setImmediate(function() {
    console.log("[*] Starting script");
    Java.perform(function () {
        Java.choose("android.view.View", {
             "onMatch":function(instance){
                  console.log("[*] Instance found: " + instance.toString());
             },
             "onComplete":function() {
                  console.log("[*] Finished heap search")
             }
        });
    });
});

出力は以下のようになります。

[*] Starting script
[*] Instance found: android.view.View{7ccea78 G.ED..... ......ID 0,0-0,0 #7f0c01fc app:id/action_bar_black_background}
[*] Instance found: android.view.View{2809551 V.ED..... ........ 0,1731-0,1731 #7f0c01ff app:id/menu_anchor_stub}
[*] Instance found: android.view.View{be471b6 G.ED..... ......I. 0,0-0,0 #7f0c01f5 app:id/location_bar_verbose_status_separator}
[*] Instance found: android.view.View{3ae0eb7 V.ED..... ........ 0,0-1080,63 #102002f android:id/statusBarBackground}
[*] Finished heap search

Java のリフレクション機能を使用することもできます。android.view.View クラスのパブリックメソッドをリストするには、Frida でこのクラスのラッパーを作成し、ラッパーの class プロパティから getMethods をコールします。

Java.perform(function () {
    var view = Java.use("android.view.View");
    var methods = view.class.getMethods();
    for(var i = 0; i < methods.length; i++) {
        console.log(methods[i].toString());
    }
});

これは非常に長いメソッドのリストをターミナルに出力します。

public boolean android.view.View.canResolveLayoutDirection()
public boolean android.view.View.canResolveTextAlignment()
public boolean android.view.View.canResolveTextDirection()
public boolean android.view.View.canScrollHorizontally(int)
public boolean android.view.View.canScrollVertically(int)
public final void android.view.View.cancelDragAndDrop()
public void android.view.View.cancelLongPress()
public final void android.view.View.cancelPendingInputEvents()
...

Last updated

Was this helpful?