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
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.
| Field | Description |
|---|---|
| application | The application's package name (e.g., com.android.settings) |
| device | The device ID (e.g., 67b2a3d7-5004-ea2a-0d44-194de6ede8de) |
| encode | Data encoding (e.g., none) |
| name | Data name (e.g., report_data) |
| script | The script ID (e.g., 7c52530d) |
| sequence | The reporting sequence number (e.g., 30) |
| timestamp | The reporting timestamp (e.g., 1740023596914) |
| user | The multi-instance app ID (e.g., 0) |
Attention
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.
| Name | Description |
|---|---|
| device_id | Unique device ID |
| device_id_short | Unique device ID (short device ID encoded in BASE62) |
| android_id | Android ID |
| serialno | ro.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
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
Attention
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()