Introduction au Reverse Engineering

Généralités
Définition
Le Reverse Engineering, ou rétro-ingénierie / ingénierie inverse en français, représente l'étude et l'analyse d'un système pour en déduire son fonctionnement interne, et se retrouve dans de nombreux domaines de l'ingénierie : génie civil, mécanique, ingénierie navale, aéronautique, etc...
Dans le domaine informatique, faire du reverse revient souvent à utiliser des outils d'analyse comme le désassembleur ou le décompilateur dans le cadre d'analyse en boîte blanche. Nous reviendrons par la suite sur les différents contextes d'analyse. On retrouve également des outils permettant d'analyser les entrées/sorties d'un programme en sniffant le réseau comme Wireshark, ainsi que de nombreux autres outils, sur lesquels nous reviendront par la suite.
Le reverse étant très lié au monde de la sécurité informatique, une distribution Linux apellée Kali Linux (anciennement Backtrack) spécialisée dans le domaine de la sécurité est un très bon point de départ pour se former au monde du reverse. S'y aventurer n'est pas chose facile, mais de nombreux sites webs, conférences (Nuit du Hack, Hack in Paris, DEFCON, etc...), livres, et autres supports vous aideront durant cette longue quête.
Il vous reste à choisir votre camp : Etes vous plutôt White Hat ou Black Hat? Expert en sécurité ou Hacker? C'est à vous de décider de quelle côté de la force vous êtes ....



No. Try not. Do... or do not. There is no try; Yoda, Star Wars, épisode V : L'Empire contre-attaque
Objectifs
Mais pourquoi faire du Reverse Engineering?
Dans le domaine de l'informatique, le reverse a de nombreuses utilités :
- Il peut être utilisé pour comprendre en détail le fonctionnement d'un logiciel,
- Améliorer la sécurité et qualité d'un logiciel (recherche de failles, étude d'un virus pour l'éradiquer, ...),
- Permettre de contourner les protections logiciels (numéro de licence, etc...),
- Reproduire le comportement d'un logiciel en outrepassant les DRM et restrictions des logiciels propriétaires.
Ainsi, il permet de passer sous licence libre de nombreux logiciels propriétaires :
On peut citer le désormais très populaire outil SaMBa, outil permettant de réaliser des partages réseaux de fichiers et d'imprimantes entre des systèmes Linux et Windows. Cette implémentation du protocole SMB/CIFS a été réalisée initialement par Andrew Tridgell par le biais d'une analyse réseau du protocole.
Dans le monde des drivers libres, Nouveau, projet de la fondation X.org produit des pilotes libres pour les cartes graphiques Nvidia, en utilisant le principe du Reverse : L'idée est d'analyser le comportement du pilote officiel de NVIDIA et ses interactions avec le matériel et le kernel Linux, pour ainsi en reproduire un totalement libre de droit.

Compétences
Pour pouvoir rentrer dans ce cercle de "reverseur", il convient de disposer d'un minimum de bagages, dont voici une liste non exhaustive :
- Savoir développer,
- Connaître les spécifications du processeur cible (CISC (Complex Instruction Set Computing): x86, RISC (Reduced Instruction Set Computing): ARM, etc...).
Un bon commencement consiste à connaître les techniques d'optimisation réalisées par un processeur, liées à la théorie de la compilation : déroulement de boucle (si le code généré par une boucle peut tenir dans une page de code (4Ko sur x86/x64), la boucle est supprimée et le code est copié autant de fois que le nombre d'itérations de la boucle), l'alignement, qui consiste à travailler avec la taille native des mots du processeur (ne pas travailler sur 1 octet sur un bus 32 bits, etc...), etc...,
- Avoir des bonnes bases concernant la compilation (Analyse lexicale, syntaxique, sémantique, parseur, génération de code, etc...),
- Savoir faire de la reconnaissance de formes, de pattern,
- Et bien sûr, pour pouvoir être un bon rétro-ingénieur, il faut savoir être ingénieux!!
Un peu de vocabulaire
Pour la suite ainsi que pour les exemples, il convient de comprendre ce qu'est un fichier binaire.
En voici une définition spécifique au sujet traité : c'est un ensemble d'octets, dont chaque octet est représenté par 2 caractères hexadécimaux (de 0 à F, un octet : OO, FA, FF, ...). Chaque ensemble d'octets représente un code en assembleur, spécifique à une architecture de processeur (x86, ARM, ...).
Voici 3 exemples d'instructions assembleur et leurs représentations en octets, qui seront utilisés dans l'exemple :
- Instruction JNZ MEMORY_ZONE (75XX): Signifiant Jump if not zero. Si le flag ZF vaut 0, la valeur du pointeur d'instruction (EIP) vaudra la zone MEMORY_ZONE et non pas l'instruction suivante. Cette instruction est précédée d'une opération artihmétique, placant le flag ZF à 0 ou 1 (test, cmp, ...). Ici, 75 représente le code hexadécimal de l'instruction JNZ, et XX la zone mémoire.
- Instruction JZ MEMORY_ZONE (74XX): Signifiant Jump if zero. Si le flag ZF vaut 1, la valeur du pointeur d'instruction (EIP) vaudra la zone MEMORY_ZONE et non pas l'instruction suivante. Cette instruction est précédée d'une opération artihmétique, placant le flag ZF à 0 ou 1 (test, cmp, ...). Ici, 74 représente le code hexadécimal de l'instruction JZ, et XX la zone mémoire.
- Instruction NOP (90): No Operation, signifiant littéralement pas d'opérations. Le programme poursuit son exécution, et le registre EIP prend pour valeur l'instruction suivante.
Le drapeau ZF est mis à 1 lorsque le résultat de la dernière opération arithmétique à renvoyer 0.
Pour avoir une vision un peu plus précise de l'assembleur, allez voir ici