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 を参照するか、当社提供の frida_script_generate.py を使用して JS スクリプトをバンドルしてください。

レポートスクリプトの作成

通常、Frida スクリプトにはデータを外部に送信する sendlog などの機能がありますが、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 メソッドには emit(name, content) という 2 つの引数があります。name はデータの種類を表します。レポート先として Redis を設定した場合、この名前は Redis キュー名になります。product_info のように、できるだけ正確な英語で記述してください。content はデータ内容で、文字列とバイト配列のみサポートします。例では JSON に変換して文字列として送信しています。

これで、スクリプト作成に必要な形式と呼び出し方法、つまり Hook したデータを外部に送信する方法を理解しました。次に、データレポートの送信先 の設定方法を読む必要があります。

データレポートの送信先

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

通常、データの送信元などの情報を気にする必要がない場合(つまり、データと送信元マシンを紐づける必要がない場合)は、Redis キューを使用することを推奨します。逆に、データと送信元を関連付ける必要がある場合は、HTTP または MQTT(v5)を使用してください。これらのプロトコルではメタデータをより多く含めることができ、より正確なデバイス照合が可能です。

レポートのメタデータ

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

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

注目

Redis プロトコルの特性上、Redis にレポートされたデータには上記のメタデータは一切含まれません。

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

レポートリンクの追加パラメータ

レポートリンク内に、${name} 形式の変数プレースホルダーを使用して動的な ID パラメータを指定できます。例: 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 リクエストのボディに含まれます。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...

レポートスクリプトの注入

もちろん、最初のステップは 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()