Rétro-ingénierie d’apps mobiles et de leur protocole
Une démarche d’analyse d’applications Android pour comprendre leurs protections : désassemblage, instrumentation dynamique et interception de trafic.
En bref
- Problème
- Une application mobile ne livre pas son code et chiffre son trafic, et ses API authentifient leurs clients et vérifient l’intégrité des requêtes ; comprendre ces protections demande de savoir les observer de l’extérieur.
- Ce que j’ai réalisé
- Une démarche d’analyse en trois temps, appliquée à des applications Android : désassemblage statique, instrumentation dynamique au runtime et cartographie du trafic HTTPS.
- Résultat
- Je sais lire une application obfusquée, observer son comportement en mémoire et cartographier ses échanges réseau, et j’en tire une lecture critique des protections mobiles.
- Rôle
- Recherche et analyse personnelles.
- Équipe
- Projet individuel
- Statut
- Terminé
Sommaire7 sections
Contexte
Travail de recherche personnel, hors cadre scolaire, mené pendant mon BUT. J’ai voulu comprendre comment des applications Android protègent leurs échanges avec leurs serveurs, et comment on analyse une application quand on n’a pas son code source. Je décris ici la démarche et les compétences : aucune application n’est nommée et aucun contournement n’est détaillé.
Le problème
Une application mobile n’expose jamais son code : il est compilé, souvent obfusqué, et son trafic est chiffré, parfois épinglé à un certificat. Ses API vérifient en plus les clients qui les appellent : protections côté client, signaux comportementaux, limites de débit. Pour comprendre ce qu’une protection arrête, je dois savoir l’observer de l’extérieur.
Démarche et méthode
J’ai travaillé en trois temps. En statique, je décompile l’APK avec jadx et apktool, je lis le smali et le Java reconstruit, et je remonte les points d’entrée et les références croisées malgré l’obfuscation. En dynamique, j’instrumente l’application au runtime avec Frida pour observer les valeurs calculées en mémoire et le comportement réel d’une fonction, là où la lecture statique s’arrête. En réseau, j’intercepte le trafic HTTPS pour cartographier la structure des requêtes : en-têtes, ordre, champs calculés. L’épinglage de certificat, quand l’application l’utilise, peut bloquer cette interception. Ghidra me sert quand une partie sensible passe par du code natif.
- Le point de départ. Une application Android livrée compilée, sans code source, et dont le trafic est chiffré : tout s’observe de l’extérieur.
- En statique. Je décompile l’APK avec jadx et apktool (Ghidra pour le code natif), je lis le smali et le Java reconstruit, et je remonte les points d’entrée et les références croisées.
- En dynamique. J’attache Frida au runtime pour observer une valeur calculée en mémoire et le comportement réel d’une fonction, là où la lecture statique s’arrête.
- En réseau. J’intercepte le trafic HTTPS pour cartographier la structure des requêtes : en-têtes, ordre, champs calculés. L’épinglage de certificat peut bloquer cette interception.
- La synthèse. Je relie ce que chaque voie révèle (le code possible, le comportement réel, les échanges envoyés) pour comprendre le fonctionnement de l’application.
Résultat
J’ai vérifié mes observations en les recoupant d’une couche à l’autre : ce que le code décompilé laissait prévoir, je l’ai confronté à ce que Frida montrait en mémoire, puis au trafic intercepté. La Fig. 2 montre ce recoupement sur une requête fictive.
Requête HTTPS fictive et générique
- lu à cette couche
- en partie : un nom, une origine ou une valeur seule
- invisible
Couche 1 : APK sans source
Je vois le paquet et ses chaînes figées : le domaine de l’API, le chemin, les noms d’en-têtes et de champs, le gabarit du User-Agent.
Je ne vois pas une seule valeur, ni l’ordre d’envoi : rien ne relie encore une chaîne au moment où elle part.
Couche 2 : Code décompilé
Je vois qui construit quoi : la fonction qui calcule la signature et ses entrées, l’assemblage de l’URL et du corps, l’appel aux protections côté client.
Je ne vois pas les valeurs : le jeton et la clé qui signe ne sont lus qu’à l’exécution. Le cookie m’échappe aussi : la bibliothèque HTTP le gère sans que ce code le nomme.
Couche 3 : Runtime instrumenté
Je vois les valeurs au moment où elles naissent : le jeton lu depuis le stockage, le nonce tiré, la signature calculée avec ses entrées, le corps en clair avant chiffrement.
Je ne vois pas la requête telle qu’elle part, avec l’ordre de ses en-têtes. L’intérieur du jeton de protection m’échappe aussi : il est produit hors de l’application.
Couche 4 : Trafic HTTPS
Je vois la requête exactement comme elle part : ordre et casse des en-têtes, cookie posé par le serveur.
Je ne vois pas d’où viennent les valeurs : la signature n’est plus qu’un nombre sans origine. L’épinglage de certificat peut aussi fermer cette fenêtre.
Les boutons ← → changent de couche ; dans la requête, les flèches haut et bas parcourent les lignes et Entrée ouvre la couche qui explique la ligne.
- Ligne de requête. La route : une chaîne du binaire, assemblée par le code ; le runtime et le fil la confirment.
- Host. Le domaine de l’API : lisible dès les chaînes du paquet, avant toute exécution.
- Authorization. Le jeton de session : le code montre d’où il est lu, seul le runtime en donne la valeur, le fil l’emporte tel quel.
- X-Signature. La signature : le code dit comment elle se calcule, le runtime donne sa valeur et ses entrées, le fil ne montre qu’un nombre.
- X-Client-Check. Un jeton de protection côté client, produit hors de l’application : aucune couche n’en lit le contenu.
- User-Agent. L’identité déclarée du client : un gabarit du binaire rempli par le code ; les protections côté client vérifient si elle est crédible.
- Cookie. Posé par le serveur et géré par la bibliothèque HTTP : absent du code de l’application, visible en mémoire et sur le fil.
- Champ action. Le champ fixe du corps : un nom dans les chaînes du paquet, une valeur assignée dans le code.
- Champ nonce. Tiré au hasard à chaque requête ; il n’existe qu’au runtime et sur le fil.
- Champ ts. L’horodatage, lu sur l’horloge à l’envoi et entrée de la signature : une valeur, donc une affaire de runtime.
Ce que les protections m’ont appris
Le versant défense m’a le plus appris. J’ai vu pourquoi une API signe ses requêtes, et comment elle authentifie ses clients. Les regarder de près m’apprend surtout à les concevoir.
Ce que j’ai appris, ce que je referais
J’ai appris une démarche d’analyse reproductible, et à documenter ce que j’observe avant de conclure. Ce travail est un projet personnel, mené sans mandat d’un éditeur : un exercice d’apprentissage. Il s’arrête à la compréhension des mécanismes. Je voudrais refaire ce type d’analyse dans le cadre d’un mandat ou d’un programme de bug bounty.
Compétences BUT mobilisées
| Compétence | Niveau | Ce que le projet montre, et preuve |
|---|---|---|
| RéaliserRéaliser un développement d’application | Niveau 2 | Mettre en œuvre une démarche de rétro-ingénierie logicielle.Preuve : Désassemblage statique (jadx, apktool, Ghidra) et instrumentation dynamique (Frida) d’applications Android. |
| AdministrerFaire évoluer et maintenir un système informatique communicant en conditions opérationnelles | Niveau 2 | Analyser les échanges réseau d’une application.Preuve : Interception et cartographie du trafic HTTPS, lecture de l’épinglage de certificat, de l’authentification des clients et de la signature des requêtes. |