Rapporter des données avec Frida

La fonctionnalité de rapport de données avec Frida est basée sur la fonctionnalité de persistance. Vous pouvez utiliser vos propres scripts Frida pour intercepter automatiquement les données des appels de méthode et les rapporter via notre méthode spécifique. La fonction de rapport de données vous permet de téléverser facilement les données interceptées par le script directement vers Redis ou une interface HTTP externe. Vous pouvez recevoir le contenu des données rapportées via Redis ou l'interface HTTP. En même temps, pour maximiser l'efficacité du réseau, le rapport de données compressées (zlib) est pris en charge.

Attention

À partir de la version 9.0, la version de Frida 17.x que nous utilisons en interne nécessitera d'empaqueter vous-même `frida-java-bridge` dans le script, sinon des erreurs liées à `Java not defined` apparaîtront. Ce changement est une modification officielle de Frida. Selon les notes de version officielles, vous devez créer un nouveau projet Node.js et y inclure le Java Bridge. Pour plus de détails, veuillez vous référer à https://github.com/oleavr/frida-agent-example ou utiliser notre outil `tools/frida_script_generate.py` pour encapsuler à nouveau votre script JS original.

Écrire le script de rapport

La première étape consiste à modifier votre script Frida. Habituellement, les scripts Frida disposent de fonctions telles que send et log pour envoyer des données à l'extérieur. Pour FIRERPA, vous devez utiliser une méthode spécifique pour envoyer les données. Voici une démonstration utilisant un de nos modèles de code pour l'interception du trafic OkHttp. Il s'agit simplement d'un script de démonstration, et il est possible que vous ne puissiez pas l'utiliser tel quel. Ce script n'est pas très différent d'un script classique, la seule différence étant l'utilisation d'une méthode emit, qui est une méthode intégrée à FIRERPA. Vous pouvez l'utiliser pour soumettre des données à l'extérieur de manière pratique et systématique.

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

La méthode emit a deux paramètres : emit(name, content). name représente le type de données. Si votre adresse de rapport est configurée sur Redis, ce nom représente le nom de la file d'attente Redis. Vous devriez le décrire aussi précisément que possible en anglais, par exemple product_info. content représente le contenu de ces données, et les types supportés sont uniquement les chaînes de caractères (string) et les tableaux d'octets (bytes). Dans l'exemple ci-dessus, nous l'avons converti en une chaîne JSON avant de le soumettre.

Maintenant que vous comprenez le format et les exigences d'appel pour l'écriture du script, c'est-à-dire comment soumettre les données interceptées (hookées) à l'extérieur, vous devez continuer à lire ci-dessous pour apprendre à configurer la destination du rapport de données.

Destination du rapport de données

La destination du rapport de données représente l'endroit où les données que vous émettez (emit) dans votre script doivent être envoyées. Les types de destinations pris en charge sont les interfaces HTTP, les files d'attente Redis et les files d'attente RabbitMQ. Il y a quelques légères différences entre eux.

En général, si vous n'avez pas besoin de vous soucier des informations sur la source des données, c'est-à-dire si vous n'avez pas besoin de faire correspondre les données avec la machine source, nous vous recommandons d'utiliser une file d'attente Redis. Dans le cas contraire, vous devriez utiliser HTTP ou MQTT (v5), car grâce aux caractéristiques de ces deux protocoles, nous pouvons transporter plus de métadonnées dans le protocole, ce qui vous permet d'effectuer une correspondance plus précise avec l'appareil.

Métadonnées rapportées

Pour les données rapportées à une interface HTTP ou à une destination RabbitMQ, en plus des données de rapport originales, vous pouvez également obtenir des métadonnées sur l'appareil et le script via la couche protocolaire. Les métadonnées incluses dans le protocole sont indiquées dans le tableau ci-dessous.

ChampDescription
applicationNom du package de l'application (ex: com.android.settings)
deviceID de l'appareil (ex: 67b2a3d7-5004-ea2a-0d44-194de6ede8de)
encodeEncodage des données (ex: none)
nameNom des données (ex: report_data)
scriptID du script (ex: 7c52530d)
sequenceSéquence du rapport (ex: 30)
timestampHeure du rapport (ex: 1740023596914)
userID de l'application multi-instance (ex: 0)

Attention

En raison des caractéristiques du protocole Redis, les données rapportées à une destination Redis ne contiennent aucune des métadonnées susmentionnées.

device est l'ID unique de l'appareil, que vous pouvez trouver dans la barre d'informations du bureau à distance. Cet ID est généralement unique et fixe. Vous pouvez l'utiliser pour marquer l'appareil et ainsi créer une correspondance. encode est l'encodage des données, qui prend en charge none et zlib. Si l'encodage est zlib, vous devrez utiliser zlib pour décompresser le corps des données. name est utilisé pour marquer le type de ces données rapportées ; c'est aussi le premier paramètre que vous utilisez avec la méthode emit. sequence représente l'index des données rapportées, commençant à 0 et s'incrémentant à chaque rapport. Vous pouvez utiliser ce champ pour trier les données ou vérifier s'il y a des pertes de rapports.

Paramètres supplémentaires pour l'URL de rapport

Lorsque vous construisez l'URL de rapport, vous pouvez y spécifier des paramètres d'ID dynamiques en utilisant des placeholders de variables au format ${name}. Vous pouvez les insérer à des endroits spécifiques de l'URL, par exemple sous la forme http://192.168.1.2/report/${device_id}. Les variables prises en charge sont les suivantes.

NomDescription du nom
device_idID unique de l'appareil
device_id_shortID unique de l'appareil (ID court encodé en BASE62)
android_idAndroid ID
serialnoro.serialno

Rapport vers HTTP

Vous devez écrire vous-même un service HTTP pour recevoir les données rapportées. L'interface de rapport HTTP doit implémenter la méthode POST. FIRERPA rapportera les données à l'interface via POST, et encodera également chaque champ des métadonnées comme un paramètre de requête HTTP, que vous pourrez extraire et traiter. Le corps des données est transporté dans le corps (body) de la requête POST. FIRERPA effectuera automatiquement 3 tentatives en cas de réception des codes de statut 502, 503 ou 504. Si votre backend a correctement reçu et traité les données rapportées, il doit retourner le texte brut OK ou SUCCESS et définir le code de statut à 200 pour indiquer que le traitement a réussi.

Attention

Les requêtes HTTP sont multithread, les messages reçus par le backend peuvent ne pas respecter l'ordre de rapport (séquence).
Exemple d'URL de rapport http://192.168.1.2/report/${device_id}?serialno=${serialno}
Si une authentification par mot de passe est requise http://user:password@192.168.1.2/report/${device_id}?serialno=${serialno}

Rapport vers MQTT

Pour les rapports vers MQTT, vous devez extraire les métadonnées rapportées de UserProperty. TLS, l'authentification par nom d'utilisateur/mot de passe et la validation de certificat unidirectionnelle (vérification du certificat serveur) sont pris en charge. La validation bidirectionnelle n'est pas supportée.

Exemple d'URL de rapport mqtt://test.mosquitto.org:1883/script/${device_id}/report
Si une authentification par mot de passe est requise mqtt://rw:readwrite@test.mosquitto.org:1884/script/${device_id}/report

Si une validation unidirectionnelle est requise mqtts://test.mosquitto.org:8883/script/${device_id}/report?verify=true&ca=LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVBekNDQXV1Z0F3SUJBZ0lVQlkxaGxDR3ZkajROaEJYa1ovdUxVWk5JTEF3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd2daQXhDekFKQmdOVkJBWVRBa2RDTVJjd0ZRWURWUVFJREE1VmJtbDBaV1FnUzJsdVoyUnZiVEVPTUF3RwpBMVVFQnd3RlJHVnlZbmt4RWpBUUJnTlZCQW9NQ1UxdmMzRjFhWFIwYnpFTE1Ba0dBMVVFQ3d3Q1EwRXhGakFVCkJnTlZCQU1NRFcxdmMzRjFhWFIwYnk1dmNtY3hIekFkQmdrcWhraUc5dzBCQ1FFV0VISnZaMlZ5UUdGMFkyaHYKYnk1dmNtY3dIaGNOTWpBd05qQTVNVEV3TmpNNVdoY05NekF3TmpBM01URXdOak01V2pDQmtERUxNQWtHQTFVRQpCaE1DUjBJeEZ6QVZCZ05WQkFnTURsVnVhWFJsWkNCTGFXNW5aRzl0TVE0d0RBWURWUVFIREFWRVpYSmllVEVTCk1CQUdBMVVFQ2d3SlRXOXpjWFZwZEhSdk1Rc3dDUVlEVlFRTERBSkRRVEVXTUJRR0ExVUVBd3dOYlc5emNYVnAKZEhSdkxtOXlaekVmTUIwR0NTcUdTSWIzRFFFSkFSWVFjbTluWlhKQVlYUmphRzl2TG05eVp6Q0NBU0l3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFNRTBIS21JemZUT3drS0xUM1RISGUrT2JkaXphbVBnClVabUQ2NFRmM3pKZE5lWUdZbjRDRVhieVA2ZnkzdFdjOFMyYm9XNmR6ckg4U2RGZjl1bzMyMEdKQTlCN1UxRlcKVGUzeGRhL0xtM0pGZmFIamtXdzdqQndjYXVRWmpwR0lOSGFwSFJscGlDWnNxdUF0aE9neFc5U2dEZ1lsR3pFQQpzMDZwa0VGaU13K3FEZkxvL3N4RktCNnZRbEZla01lQ3ltakxDYk53UEp5cXloRm1QV3dpby9QRE1ydUJUelBICjNjaW9CbnJKV0tYYzNPalhkTEdGSk9majdwUDBqL2RyMkxINzJlU3Z2M1BRUUZsOTBDWlBGaHJDVWNSSFNTeG8KRTZ5akdPZG56N2Y2UHZlTElCNTc0a1FPUnd0OGVQbjB5aWRyVEMxaWN0aWtFRDNuSFloTVVPVUNBd0VBQWFOVApNRkV3SFFZRFZSME9CQllFRlBWVjZ4QlVGUGlHS0R5bzVWMytIYmg0TjlZU01COEdBMVVkSXdRWU1CYUFGUFZWCjZ4QlVGUGlHS0R5bzVWMytIYmg0TjlZU01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUwKQlFBRGdnRUJBR2E5a1MyMU43MFRoTTYvSGo5RDdtYlZ4S0xCalZXZTJUUHNHZmJsM3JFRGZaK09LUloyajZBQwo2cjdqYjRUWk8zZHpGMnA2ZGdicmxVNzFZLzRLMFRkeklqUmozY1EzS1NtNDFKdlVRMGhaL2MwNGlHRGcveFdmCitwcDU4bmZQQVl3dWVycnVQTldtbFN0V0FYZjBVVHFSdGc0aFFEV0J1VUZESlR1V3V1QnZFWHVkejc0ZWgvd0sKc013ZnUxSEZ2ank1WjBpTURVOFBVRGVwalZvbE9DdWU5YXNobFM0RUI1SUVDZFNSMlRJdG5BSWlJd2lteDgzOQpMZFVkUnVkYWZNdTVUNVhtYTE4Mk9DMC91L3hSbEVtK3R2S0dHbWZGY04wcGlxVmw4T3JTUEJnSWxiKzFJS0pFCm0vWHJpV3IvQ3E0aC9KZkI3TlRzZXpWc2xna0Jhb1U9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K

Dans l'URL ci-dessus, les données correspondantes seront envoyées à script/${device_id}/report. Vous pouvez vous abonner à ces messages en utilisant une commande comme mosquitto_sub -L mqtt://test.mosquitto.org:1883/script/+/report.

Rapport vers Redis

Le rapport vers Redis est relativement simple. Comme il ne transporte aucune métadonnée, il n'est pas possible de distinguer directement la source des données. Vous devrez peut-être modifier dynamiquement le script injecté pour réaliser ce comportement. Pour le rapport vers Redis, FIRERPA poussera directement le corps des données dans la file d'attente via la commande LPUSH. Par exemple, dans le script d'exemple ci-dessus, les données rapportées seront poussées dans la file d'attente report_data.

Attention

Seuls les services Redis en mode autonome (standalone) sont pris en charge ; les clusters Redis ne sont pas supportés.

Attention

À l'exception du champ du mot de passe, n'ajoutez pas de placeholders de variables dans l'URL Redis. L'URL est une URL standard supportée par les bibliothèques Redis ; la modification d'autres parties pourrait entraîner des erreurs d'analyse.
Exemple d'URL de rapport redis://1.2.3.4/0
Si une authentification par mot de passe est requise redis://:password@1.2.3.4/0

TLS + mot de passe

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

Injecter le script de rapport

Bien sûr, obtenir une instance de l'application est la première étape. Vous pouvez utiliser l'appel suivant pour obtenir une variable app, qui représente l'instance de l'application dans laquelle vous souhaitez injecter. Ensuite, vous devrez l'utiliser pour continuer les opérations d'injection ou de détachement ci-dessous.

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

Utilisez l'interface suivante pour injecter le script de rapport ci-dessus dans l'application. Lorsque des données sont interceptées, elles seront soumises à la file d'attente report_data de Redis. Dans l'exemple, votre appareil doit pouvoir accéder directement au service correspondant sur 192.168.1.10, sinon le rapport de données échouera.

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

Vous pouvez également le configurer comme ceci, de sorte que vos données seront soumises à une interface HTTP au lieu de Redis (https supporté).

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

Lorsque les données que vous rapportez sont volumineuses, l'activation de la compression peut considérablement améliorer le débit de la transmission réseau. Vous pouvez utiliser encode pour activer la fonctionnalité de compression des données rapportées. Bien sûr, vous devrez ensuite décompresser les données rapportées.

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

Décompression des données rapportées

Par défaut, les données rapportées ne sont pas compressées. Si vous avez activé la compression des rapports, vous devrez également utiliser l'encodage zlib du côté du récepteur pour décompresser les données rapportées. Vous pouvez utiliser la méthode decompress de la bibliothèque standard Python zlib pour décompresser facilement les données rapportées.

zlib.decompress(data)

Retirer le script de rapport

Le processus de retrait du script de rapport est simple, l'utilisation est la même que pour les scripts persistants.

app.detach_script()