← Tous les projets

R&D défense · Reverse engineering

Taint analysis pour la rétro-conception de protocoles

Un outil de R&D conçu pour reconstruire automatiquement la structure de protocoles réseau propriétaires. Il suit la provenance des données pendant l'exécution d'un processus et réduit l'analyse de plusieurs heures à quelques minutes.

Période
2019-2022
Rôle
Architecte logiciel, lead R&D
Stack / Méthodes
C/C++, Valgrind, Intel PIN, analyse de teinte / taint analysis

Contexte

Au sein d'une équipe de R&D défense, nous devions analyser un volume croissant de protocoles réseau propriétaires afin d'en comprendre la structure, les champs et les données transportées. Un travail essentiel, mais très largement manuel.

Le problème

Réaliser manuellement la rétro-conception de chaque protocole était lent, répétitif et difficilement compatible avec le volume de nouvelles applications à analyser.

  • L'analyse manuelle des protocoles propriétaires était longue et répétitive.
  • Le volume de nouvelles applications rendait une analyse exhaustive impossible.
  • La complexité des protocoles, renforcée par le chiffrement et la compression, conduisait l'analyse manuelle à manquer certains champs ou relations importantes.

Objectifs

  • Réduire fortement le temps nécessaire à la rétro-conception d'un protocole.
  • Augmenter le nombre d'applications que l'équipe pouvait réellement analyser.
  • Identifier de manière fiable les champs et relations structurelles souvent manqués par l'analyse manuelle.

Mon approche

J'ai mené le projet comme une démarche de R&D progressive : valider l'hypothèse sur des cibles réelles avant d'étendre l'outil et d'en généraliser l'usage.

1. Cadrage R&D

Hypothèse de départ : observer une application pendant son exécution permettrait de comprendre comment chaque octet envoyé sur le réseau a été produit.

2. Preuve de concept

Développement d'une preuve de concept avec Valgrind et Intel PIN pour instrumenter les processus cibles et suivre les flux de données.

3. Test et validation

Tests de la preuve de concept sur des protocoles connus, puis comparaison des structures reconstruites avec des résultats de référence.

La solution technique

Observer une application pendant son exécution fournit de nombreuses informations sur les paquets réseau qu'elle émet. J'ai construit un moteur de taint analysis sur Valgrind qui :

  • Marque les fonctions et appels système pertinents : lectures de fichiers, entrées/sorties réseau, entrées utilisateur, routines de compression et de chiffrement.
  • Associe une étiquette de provenance aux données manipulées par ces fonctions, puis les suit tout au long de l'exécution du processus.
  • Lorsqu'un octet est écrit sur le réseau, reconstruit le chemin exact suivi depuis son origine.

Techniquement, Valgrind n'exécute pas directement le programme cible. Il traduit le code machine en VEX, une représentation intermédiaire de type RISC, l'instrumente superbloc par superbloc, puis recompile le code instrumenté. L'outil s'insère dans cette phase d'instrumentation. Il associe une étiquette de provenance à chaque registre invité, temporaire VEX (IRTemp) et octet de mémoire. Cette étiquette encode l'origine de la donnée.

La propagation suit le flux de données dans l'IR. Les opérations Get/Put déplacent les étiquettes entre registres invités et temporaires ; Load/Store les déplacent entre mémoire et temporaires. Les opérations arithmétiques ou logiques propagent ensuite l'union des étiquettes de leurs opérandes vers le résultat. Un octet ainsi marqué conserve son étiquette de provenance à travers les copies, les calculs et les frontières de buffers, dans les registres comme en mémoire.

Les étiquettes sont introduites au niveau des sources de données externes : read, recv, open, time, entrées d'environnement ou de terminal, ainsi que certaines routines choisies par l'utilisateur (exemple : zlib, OpenSSL, etc.). Elles sont ensuite relues au niveau des sorties réseau, comme send, sendto ou write sur une socket. La preuve de concept reposait sur deux backends, Valgrind/VEX et Intel PIN, afin de comparer les résultats et d'élargir la couverture d'instrumentation.

read() /tmp/report.bin time() syscall seq++ process deflate() zlib EVP_EncryptUpdate() libcrypto htonl() arpa/inet send() → paquet
Des sources indépendantes (fichier, temps, compteur) traversent des transformations et convergent vers send() : le paquet émis est l'union de toutes les origines suivies.

Exemple : pour une application qui lit un fichier, le compresse puis l'envoie sur le réseau, l'outil identifie les octets du paquet sortant issus du fichier et les transformations qu'ils ont traversées.

Paquet capturéTrace de provenance (origine des données)
0x0000Compteur · 4 octets00 00 04 2A
seq++état process htonl()arpa/inet
0x0004Horodatage · 4 octets65 9C 3F 80
time()syscall htonl()arpa/inet
0x0008Longueur · 4 octets00 00 12 0C
open("/tmp/report.bin")libc read()syscall deflate()zlib EVP_EncryptUpdate()libcrypto htonl()arpa/inet valeur = longueur en octets du pipeline payload (0x120C = 4620)
0x000CPayload · 4 620 octets9F 2C A8 E1 … 1D
open("/tmp/report.bin")libc read()syscall deflate()zlib EVP_EncryptUpdate()libcrypto send()syscall
Exemple de sortie : chaque champ d'un paquet inconnu (avec son offset), aux côtés de la chaîne exacte d'appels libc / zlib / OpenSSL qu'ont traversés ses octets.

Avec cette lecture, la structure du paquet devient directement visible. Le champ à 0x0008 est une longueur, égale à la taille de la payload produite par read()deflate()EVP_EncryptUpdate() ; le champ à 0x0004 vient directement du syscall time() ; et la payload à 0x000C est le contenu de /tmp/report.bin après compression zlib et chiffrement AES. Au lieu de déduire manuellement les frontières des champs à partir de dumps hexadécimaux, l'analyste obtient directement la structure et le rôle de chaque champ. Cette représentation exploitable accélère fortement la rétro-conception et peut générer automatiquement un dissecteur Wireshark.

Résultats

  • Une preuve de concept fonctionnelle, validée par l'équipe sur des cibles réelles.
  • Le temps d'analyse d'un protocole est passé d'environ cinq heures à une trentaine de minutes.
  • Une couverture d'analyse plus large, avec identification automatique de champs régulièrement manqués par l'inspection manuelle.
  • Les structures reconstruites peuvent être exportées directement en dissecteurs Wireshark, générés automatiquement au lieu d'être écrits à la main.

Un défi similaire ? Me contacter →