Frida データ報告

Frida データ報告機能は永続化機能に基づいています。ユーザーが作成した Frida スクリプトでメソッド呼び出しのデータを自動的に取得し、当社の特定メソッドを通じてデータ報告できます。データ報告機能により、スクリプトが取得したデータを Redis、MQTT、または外部 HTTP インターフェースへ直接アップロードできます。Redis、MQTT、または HTTP インターフェースを通じて報告されたデータ内容を受信できます。また、ネットワーク効率を最大化するために、データ圧縮(zlib)後の報告もサポートしています。

注目

9.0 バージョン以降、内蔵 Frida 17.x では frida-java-bridge をスクリプトに自身でパッケージングする必要があります。パッケージングしないと Java not defined 関連のエラーが発生します。この変更は Frida 公式の変更です。公式の変更説明に従い、Node.js プロジェクトを新規作成して frida-java-bridge を導入する必要があります。詳細は https://github.com/oleavr/frida-agent-example を参照するか、当社提供の pack_frida_script.py を使用して js スクリプトをパッケージングしてください。

報告スクリプトの作成

通常、Frida スクリプトにはデータを外部へ送信できる send や log などの機能がありますが、FIRERPA では特定のメソッドを使用してデータを送信する必要があります。以下では、OkHttp トラフィックを取得するテンプレートコードを使用して説明します。これはデモ用スクリプトであり、そのままでは使用できない場合があります。以下のスクリプトは通常のスクリプトと大きな違いはありません。唯一の違いは emit メソッドを使用している点です。これは FIRERPA に組み込まれたメソッドで、これを使用することで簡単かつ体系的にデータを外部へ送信できます。

Java.perform(function() {
        Java.use("com.android.okhttp.internal.http.HttpEngine").getResponse.implementation = function() {
                var response = this.getResponse()
                var data = {}
                data["url"] = response._request.value._url.value._url.value
                data["body"] = response.body().string()
                emit("report_data", JSON.stringify(data))
                return response
        }
})

送信メソッドには emit(name, content) の 2 つの引数があります。name はデータの種類を表し、報告先を Redis に設定した場合、その名前は Redis キュー名を表します。product_info のように、できるだけ正確に英語で記述する必要があります。content はデータ内容を表し、型は文字列とバイト配列のみサポートされています。例では文字列に変換して送信しています。

ここまでで、スクリプト作成に必要な形式と呼び出し要件、つまりフックしたデータを外部へ送信する方法を理解できました。続いて データ報告先 の設定方法について説明します。

データ報告先

データ報告先は、スクリプト内で emit したデータをどこに送信するかを表します。報告先には HTTP インターフェース、Redis キュー、MQTT がサポートされており、それぞれにいくつかの相違点があります。

通常、データの送信元などの情報を考慮する必要がなく、データ と 送信元マシン を紐付ける必要がない場合は、Redis キューを使用することをお勧めします。逆に、データ と 送信元マシン を紐付ける必要がある場合は、HTTP または MQTT(v5)を使用してください。これら 2 つのプロトコルの特性により、プロトコル内により多くのメタデータを付与できるため、より正確なデバイスマッチングが可能です。

報告のメタデータ

HTTP インターフェースおよび MQTT 宛先へ報告されるデータには、元の報告データに加えて、プロトコル層を通じてデバイスおよびスクリプトに関するメタデータを取得できます。プロトコルに付随するメタデータは以下の表のとおりです。

フィールド説明
applicationアプリのパッケージ名(例: com.android.settings)
deviceデバイス ID(例: 67b2a3d7-5004-ea2a-0d44-194de6ede8de)
encodeデータエンコード(none
nameデータ名(例: report_data)
scriptスクリプト ID(例: 7c52530d)
sequence報告順序(例: 30)
timestamp報告時刻(例: 1740023596914)
userマルチアプリ ID(例: 0)

注目

Redis プロトコルの特性により、Redis 宛先へ報告されるデータには、上記のメタデータは一切含まれません。

device はデバイスの一意の ID で、リモートデスクトップの情報バーで確認できます。通常、この ID は一意かつ固定です。この ID でデバイスを識別し、対応関係を構築できます。encode はデータのエンコード方式で、none および zlib がサポートされています。エンコードが zlib の場合、データ本体を zlib で解凍する必要があります。name はこの報告データの種類を示し、emit メソッドの最初の引数でもあります。sequence は報告データのインデックスを表し、0 から始まり、報告のたびに増加します。このフィールドによりデータの並べ替えや、報告の欠落がないかの確認が可能です。

報告リンクの追加パラメータ

報告リンクでは、いくつかの動的な ID パラメータをリンク内に指定できます。変数プレースホルダ ${name} の形式でリンクの特定の場所に挿入します。例: http://192.168.1.2/report/${device_id}。サポートされている変数は以下の表のとおりです。

名称説明
device_idデバイス一意 ID
device_id_shortデバイス一意 ID(BASE62 エンコードの短縮デバイス ID)
android_idAndroid ID
serialnoro.serialno

HTTP への報告

報告データを受信する HTTP サービスを自分で作成する必要があります。HTTP 報告インターフェースは POST メソッドを実装する必要があります。FIRERPA は POST でインターフェースへデータを報告し、同時にメタデータの各フィールドを HTTP クエリパラメータとしてエンコードします。これらを抽出して処理できます。データ本体は POST リクエストの body に含まれます。FIRERPA は 502、503、504 ステータスコードを受信すると自動的に 3 回再試行します。バックエンドが報告データを正しく受信・処理した場合は、成功を示すためにプレーンテキストの OK または SUCCESS を返し、ステータスコードを 200 に設定してください。

注目

HTTP リクエストはマルチスレッドであるため、バックエンドが受信するメッセージは報告順序(sequence)に従わない場合があります。

報告リンクの例

http://192.168.1.2/report/${device_id}?serialno=${serialno}

標準 HTTP プロトコル認証が必要な場合

http://user:password@192.168.1.2/report/${device_id}?serialno=${serialno}

MQTT への報告

MQTT へ報告する場合、報告メタデータは UserProperty から抽出できます。TLS、ユーザー名・パスワード、片方向証明書検証(サーバー証明書検証)をサポートしています。

報告リンクの例

mqtt://test.mosquitto.org:1883/script/${device_id}/report

パスワード認証付き MQTT

mqtt://rw:readwrite@test.mosquitto.org:1884/script/${device_id}/report

サーバー証明書認証が必要な場合

mqtts://test.mosquitto.org:8883/script/${device_id}/report?verify=true&ca=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVBekNDQXV1Z0F3SUJBZ0lVQlkxaGxDR3ZkajROaEJYa1ovdUxVWk5JTEF3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2daQXhDekFKQmdOVkJBWVRBa2RDTVJjd0ZRWURWUVFJREE1VmJtbDBaV1FnUzJsdVoyUnZiVEVPTUF3RwpBMVVFQnd3RlJHVnlZbmt4RWpBUUJnTlZCQW9NQ1UxdmMzRjFhWFIwYnpFTE1Ba0dBMVVFQ3d3Q1EwRXhGakFVCkJnTlZCQU1NRFcxdmMzRjFhWFIwYnk1dmNtY3hIekFkQmdrcWhraUc5dzBCQ1FFV0VISnZaMlZ5UUdGMFkyaHYKYnk1dmNtY3dIaGNOTWpBd05qQTVNVEV3TmpNNVdoY05NekF3TmpBM01URXdOak01V2pDQmtERUxNQWtHQTFVRQpCaE1DUjBJeEZ6QVZCZ05WQkFnTURsVnVhWFJsWkNCTGFXNW5aRzl0TVE0d0RBWURWUVFIREFWRVpYSmllVEVTCk1CQUdBMVVFQ2d3SlRXOXpjWFZwZEhSdk1Rc3dDUVlEVlFRTERBSkRRVEVXTUJRR0ExVUVBd3dOYlc5emNYVnAKZEhSdkxtOXlaekVmTUIwR0NTcUdTSWIzRFFFSkFSWVFjbTluWlhKQVlYUmphRzl2TG05eVp6Q0NBU0l3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNRTBIS21JemZUT3drS0xUM1RISGUrT2JkaXphbVBnClVabUQ2NFRmM3pKZE5lWUdZbjRDRVhieVA2ZnkzdFdjOFMyYm9XNmR6ckg4U2RGZjl1bzMyMEdKQTlCN1UxRlcKVGUzeGRhL0xtM0pGZmFIamtXdzdqQndjYXVRWmpwR0lOSGFwSFJscGlDWnNxdUF0aE9neFc5U2dEZ1lsR3pFQQpzMDZwa0VGaU13K3FEZkxvL3N4RktCNnZRbEZla01lQ3ltakxDYk53UEp5cXloRm1QV3dpby9QRE1ydUJUelBICjNjaW9CbnJKV0tYYzNPalhkTEdGSk9majdwUDBqL2RyMkxINzJlU3Z2M1BRUUZsOTBDWlBGaHJDVWNSSFNTeG8KRTZ5akdPZG56N2Y2UHZlTElCNTc0a1FPUnd0OGVQbjB5aWRyVEMxaWN0aWtFRDNuSFloTVVPVUNBd0VBQWFOVApNRkV3SFFZRFZSME9CQllFRlBWVjZ4QlVGUGlHS0R5bzVWMytIYmg0TjlZU01COEdBMVVkSXdRWU1CYUFGUFZWCjZ4QlVGUGlHS0R5bzVWMytIYmg0TjlZU01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBRGdnRUJBR2E5a1MyMU43MFRoTTYvSGo5RDdtYlZ4S0xCalZXZTJUUHNHZmJsM3JFRGZaK09LUloyajZBQwo2cjdqYjRUWk8zZHpGMnA2ZGdicmxVNzFZLzRLMFRkeklqUmozY1EzS1NtNDFKdlVRMGhaL2MwNGlHRGcveFdmCitwcDU4bmZQQVl3dWVycnVQTldtbFN0V0FYZjBVVHFSdGc0aFFEV0J1VUZESlR1V3V1QnZFWHVkejc0ZWgvd0sKc013ZnUxSEZ2ank1WjBpTURVOFBVRGVwalZvbE9DdWU5YXNobFM0RUI1SUVDZFNSMlRJdG5BSWlJd2lteDgzOQpMZFVkUnVkYWZNdTVUNVhtYTE4Mk9DMC91L3hSbEVtK3R2S0dHbWZGY04wcGlxVmw4T3JTUEJnSWxiKzFJS0pFCm0vWHJpV3IvQ3E0aC9KZkI3TlRzZXpWc2xna0Jhb1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K

上記リンクでは、関連データは script/${device_id}/report チャネルに送信されます。mosquitto_sub -L mqtt://test.mosquitto.org:1883/script/+/report のようにしてこれらのメッセージをサブスクライブできます。

Redis への報告

Redis への報告は比較的シンプルです。メタデータを含まないため、データの送信元を直接区別できません。この機能を実現するには、注入スクリプトを動的に書き換える方法が必要になる場合があります。Redis への報告では、FIRERPA はデータ本体を LPUSH でキューに直接プッシュします。たとえば上記のサンプルスクリプトでは、報告データは report_data キューにプッシュされます。

注目

Redis サービスはスタンドアロンモードのみがサポートされ、Redis クラスターはサポートされていません。パスワードフィールド以外に、Redis リンクへ変数プレースホルダを追加しないでください。リンクは標準 Redis ライブラリがサポートする形式です。他の部分を変更すると正しく解析できなくなる可能性があります。

報告リンクの例

redis://1.2.3.4/0

パスワード認証が必要な Redis

redis://:password@1.2.3.4/0

TLS + パスワード認証の Redis

rediss://:password@1.2.3.4/0?ssl_ca_data=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURUVENDQWpXZ0F3SUJBZ0lVUFIvcmcxK0x2aU5tYzNsc0...

IPC への報告

IPC への報告は通常直接使用されません。この方式は Pigeon の動的スクリプトでのみ使用でき、タスクスクリプトが attach_script を使用している場合に、emit メソッドが発行するメッセージを外部 HTTP や Redis に直接送信せずに、直接リッスンして処理できるようにします。これにより、Python スクリプト内で js スクリプトが発行する情報を処理し、js と Python の連携を実現できます。

報告リンクの例

ipc://

報告データのリスニング

注目

以下のコードは Pigeon スクリプトでのみ使用でき、お使いのコンピュータやデバイス上で直接手動実行することはできません。

注入対象のアプリが com.android.settings であると仮定し、ipc:// を直接使用します。

app = d.application("com.android.settings")
app.attach_script(script, emit="ipc://")

Pigeon のタスクスクリプト内で、以下のようにリッスンしてスクリプトを出力します。タスククラスに統合してください。

from lamda.executor import InnerTaskThread

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

listener = HookScriptIPCListener("data", "com.android.settings", user=0,
                                              threadcls=InnerTaskThread)
listener.set_callback(on_message)
listener.start()

リスナーの停止

listener.stop()

注目

リスナーが起動する前に js フックスクリプトが送信したすべてのメッセージはリスナーに受信されません。

IPC+EVENT への報告

ipc+event は別の形式のリンクです。タスクスクリプトがメッセージを受信すると同時に、そのメッセージをイベントシステム(つまり firerpa デバイス側の状態・制御プロトコル。Pigeon ソースコードの状態/制御プロトコル部分を参照)へ同期報告できます。通常、この方式を直接使用することはありません。ただし、Pigeon を変更する場合や、デバイスの MQTT 制御プロトコルに直接接続して使用する場合を除きます。

この方式により、デバイスがタスクを実行していない場合でも、アプリ関連情報を MQTT バックエンドへ自動報告できます。たとえば js フックがメッセージを受信するインターフェースなどにより、アプリが受信したメッセージをリアルタイムでバックエンドへ送信して処理できます。

報告リンクの例

ここで event パラメータはフィルタであり、, で区切ります。リスト内にあるイベントのみがイベントシステムへ送信され、js スクリプト内の emit("nameA", ...) に対応します。

ipc+event://?event=nameA,nameB,nameC

報告データのリスニング

MQTT イベントシステムへ自分で接続する必要があります。イベントは script/emit タイプとして報告されます。Pigeon イベントシステムの接続および受信方法を参照してください。

報告スクリプトの注入

もちろん、最初のステップは app インスタンスの取得です。上記の呼び出しにより、注入対象のアプリインスタンスを表す app 変数を取得できます。その後、この変数を使用して注入またはデタッチ操作を実行します。

app = d.application("com.android.settings")

上記の報告スクリプトをアプリに注入します。データが取得されると、Redis の report_data キューに送信されます。この例では、デバイスが 192.168.1.10 上の関連サービスに直接アクセスできる必要があります。そうでない場合は受信できません。

app.attach_script(script, emit="redis://192.168.1.10/0")

このように呼び出すと、データは Redis ではなく HTTP インターフェース(HTTPS 対応)に送信されます。

app.attach_script(script, emit="http://192.168.1.10/dataReport")

報告データが大きい場合、圧縮を有効にするとネットワーク転送のスループット性能を大幅に向上できます。encode パラメータを使用して報告データの圧縮機能を有効にし、受信側で報告データを解凍する必要があります。

app.attach_script(..., encode=DataEncode.DATA_ENCODE_ZLIB)

報告データの解凍

デフォルトでは、報告データは圧縮されません。報告圧縮を設定した場合は、受信側で zlib エンコードを使用して報告データを解凍する必要があります。Python 標準ライブラリ zlib の decompress メソッドを使用すると、報告データを簡単に解凍できます。

zlib.decompress(data)

報告スクリプトの削除

報告スクリプトの削除は簡単で、永続化スクリプトと同じ使い方です。

app.detach_script()