Reporting Data with Frida

The feature for reporting data with Frida is based on the persistence feature. You can use your custom Frida scripts to automatically intercept data from method calls and report it using our specific methods. The data reporting feature allows you to easily upload data captured by your scripts directly to Redis or an external HTTP endpoint. You can then receive the reported data content through Redis or the HTTP endpoint. To maximize network performance, we also support reporting data after compression (zlib).

Attention

Starting from version 9.0, the built-in Frida 17.x requires you to package `frida-java-bridge` into your script yourself. Otherwise, you will encounter errors like "Java not defined". This is an official change from Frida. According to the official change notes, you need to create a new Node.js project and include the Java bridge. For details, please refer to https://github.com/oleavr/frida-agent-example or use our provided `tools/frida_script_generate.py` to re-package your original JS script.

Writing the Reporting Script

First, you need to modify your Frida script. Typically, Frida scripts have functions like send and log that allow you to send data externally. For FIRERPA, you need to use a specific method to send the data. Below is a template code for intercepting okhttp traffic. It is just a demonstration script and may not work for you as is. This script is not much different from a regular script; the only difference is the use of an emit method, which is a built-in method in FIRERPA. You can use it to conveniently and systematically submit data to an external destination.

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
        }
})

The emit method has two parameters: emit(name, content). Here, name represents the type of data. If your reporting destination is Redis, this name represents the Redis queue name. You should describe it as accurately as possible in English, for example, product_info. content represents the content of the data, and it only supports string and bytes types. In the example above, we converted it into a JSON string before submitting.

Now that you understand the required format and calling conventions for writing the script, i.e., how to submit hooked data to an external destination, you need to continue reading to learn how to configure the data reporting destination.

Data Reporting Destinations

The data reporting destination indicates where the data you emit in your script should be sent. Supported destination types include HTTP endpoints, Redis queues, and RabbitMQ queues, with slight differences between them.

Generally, if you don't need to care about information like the data source (i.e., you don't need to match data with its source machine), we recommend using a Redis queue. Otherwise, you should use HTTP or MQTT (v5), because due to the characteristics of these two protocols, we can carry more metadata in the protocol, allowing for more precise device matching.

Reported Metadata

For data reported to HTTP endpoints and RabbitMQ destinations, in addition to the original reported data, you can also obtain metadata about the device and script from the protocol layer. The metadata included in the protocol is shown in the table below.

FieldDescription
applicationThe application's package name (e.g., com.android.settings)
deviceThe device ID (e.g., 67b2a3d7-5004-ea2a-0d44-194de6ede8de)
encodeData encoding (e.g., none)
nameData name (e.g., report_data)
scriptThe script ID (e.g., 7c52530d)
sequenceThe reporting sequence number (e.g., 30)
timestampThe reporting timestamp (e.g., 1740023596914)
userThe multi-instance app ID (e.g., 0)

Attention

Due to the nature of the Redis protocol, data reported to a Redis destination does not contain any of the metadata mentioned above.

device is the unique ID of the device, which you can find in the information panel of the remote desktop. This ID is usually unique and fixed. You can use it to tag devices and establish a correspondence. encode is the data encoding, which supports none and zlib. If the encoding is zlib, you will need to use zlib to decompress the data body. name is used to mark the type of this reported data; it is also the first parameter you use in the emit method. sequence represents the index of the reported data, starting from 0 and incrementing with each report. You can use this field to sort the data or check for any lost reports.

Extra Parameters in the Reporting URL

When you construct the reporting URL, you can specify some dynamic ID parameters within the URL. You can insert them at specific parts of the URL using the variable placeholder format ${name}. For example, you can use it like http://192.168.1.2/report/${device_id}. The supported variables are as follows.

NameDescription
device_idUnique device ID
device_id_shortUnique device ID (short device ID encoded in BASE62)
android_idAndroid ID
serialnoro.serialno

Reporting to HTTP

You need to write an HTTP service yourself to receive the reported data. The HTTP reporting endpoint must implement the POST method. FIRERPA will report data to the endpoint via POST and will also encode each field from the metadata as HTTP query parameters, which you can extract and process. The data body itself is carried in the body of the POST request. FIRERPA will automatically retry 3 times upon receiving 502, 503, or 504 status codes. If your backend correctly receives and processes the reported data, it should return the plain text OK or SUCCESS and set the status code to 200 to indicate successful processing.

Attention

HTTP requests are multi-threaded, so the messages received by the backend may not follow the reporting order (sequence).
Example reporting URL: http://192.168.1.2/report/${device_id}?serialno=${serialno}
For password authentication: http://user:password@192.168.1.2/report/${device_id}?serialno=${serialno}

Reporting to MQTT

When reporting to MQTT, you should extract the metadata from UserProperty. TLS, username/password authentication, and one-way certificate validation (server certificate verification) are supported, but two-way validation is not.

Example reporting URL: mqtt://test.mosquitto.org:1883/script/${device_id}/report
For password authentication: mqtt://rw:readwrite@test.mosquitto.org:1884/script/${device_id}/report

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

In the URL above, the relevant data will be sent to script/${device_id}/report. You can subscribe to these messages using a command like mosquitto_sub -L mqtt://test.mosquitto.org:1883/script/+/report.

Reporting to Redis

Reporting to Redis is relatively simple. Since it doesn't carry any metadata, you cannot directly distinguish the data source. You might need to achieve this by dynamically modifying the injected script. For Redis reporting, FIRERPA will directly push the data body into a queue using LPUSH. For example, in the sample script above, the reported data will be pushed into the report_data queue.

Attention

Only standalone Redis services are supported; Redis Cluster is not.

Attention

Do not add variable placeholders in the Redis URL, except for the password field. The URL format is the one supported by standard Redis libraries; modifying other parts may lead to parsing errors.
Example reporting URL: redis://1.2.3.4/0
For password authentication: redis://:password@1.2.3.4/0

TLS + password

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

Injecting the Reporting Script

Of course, getting the app instance is the first step. You can use the following call to get an app variable, which represents the instance of the application you want to inject into. You will then use it to proceed with the injection or detachment operations below.

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

Use the following interface to inject the reporting script mentioned above into the application. When data is intercepted, it will be submitted to the report_data queue in Redis. In the example, your device needs to be able to directly access the relevant service on 192.168.1.10, otherwise data reporting will fail.

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

You can also set it up like this, so your data will be submitted to an HTTP endpoint instead of Redis (HTTPS is supported).

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

When the data you are reporting is large, enabling compression can significantly improve network transmission throughput. You can use encode to enable the data compression feature. Of course, you will then need to decompress the reported data.

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

Decompressing Reported Data

By default, reported data is not compressed. If you have enabled reporting compression, you will also need to use zlib encoding on the receiving end to decompress the reported data. You can conveniently decompress the data using the decompress method from Python's official zlib library.

zlib.decompress(data)

Removing the Reporting Script

Removing the reporting script is a simple process, the same as with persistent scripts.

app.detach_script()