lundi 7 décembre 2009

Divers: Analyse de code

Les microprocesseurs ont fait leur apparition dans les récepteurs professionnels dans les années 80. S’il est assez aisé de dépanner les matériels faisant appel à de la logique câblée, même en l’absence de documentation, ce n’est plus le cas lorsque l’équipement est piloté par un processeur.
Il n'y a rien de plus désespérant que de se retrouver face à un matériel affichant un simple code d’erreur au démarrage, ou après un autotest, sans disposer de la documentation permettant d’interpréter ce code. Et il est des équipements dont la seule certitude que l’on puisse avoir est qu’il a bien longtemps que cette documentation a disparu de la circulation.

Il faut alors employer une approche qui n’a plus grand-chose à voir avec l’électronique classique mais qui par contre est très couramment utilisée dans le domaine de la sécurité des systèmes d’information et de la lutte contre les codes malveillants: l’étude détaillée du logiciel de contrôle de l’équipement permettra d’identifier les grandes fonctionnalités de celui-ci et, avec un peu de chance et de méthode, de cerner l’origine du dysfonctionnement.

Cette étude suppose d’extraire le contenu des mémoires mortes puis de traiter fichier binaire ainsi obtenu pour remonter au code assembleur. Une opération appelée désassemblage, ou décompilation, assez strictement encadrée par la loi (Article L122-6-1 du Code de la Propriété Intellectuelle).

Dans la plupart des cas, l’équipement n’étant plus commercialisé, encore moins maintenu, l’équipementier ayant parfois même disparu, cette décompilation effectuée à titre personnelle, et sans communication des résultats, ne sera pas répréhensible. Encore faut-il disposer de l’outillage ad hoc, et d’un minimum d’informations sur la structure du microprocesseur et des périphériques associés.

L’outillage est le point le plus délicat. S’il est en effet aisé de trouver de fabuleux outils pour la famille des processeurs Intel x86 utilisés dans nos PC – désassembleur OllyDbg par exemple – peu d’outils, hors ceux proposés par le fondeur, sont disponibles pour les autres processeurs.

Il existe toutefois un fabuleux outil commercial capable de désassembler le code des processeurs les plus courants: l’outil IDA dans sa version professionnelle, hélas bien peu abordable pour l’amateur. Tous les processeurs n'y sont cependant pas traités à l’identique. Si pour les processeurs usuels – Motorola et Intel – l'analyse est irréprochable, il n’en va pas de même avec des processeurs complexes tels les processeurs de signaux et en particulier les processeurs Texas Instruments de la famille TMS320 pour lesquels le désassemblage est incomplet voir erroné. L’utilisateur devra alors s’atteler à la difficile tâche de modifier le code du générateur, code fort heureusement livré avec le logiciel.

Disposant du bon outil, il faudra ensuite configurer celui-ci pour que le code assembleur généré soit le plus exploitable possible.

Deux options s’offrent alors. Les adresses de tous les périphériques – E/S, RAM, PIO, PIA… - sont connues, il suffira alors de les déclarer symboliquement rendant le code généré bien plus lisible. Pour ma part et avec IDA Pro, je déclare chaque périphérique dans un segment dédié.


Dans l’hypothèse où ces adresses ne seraient pas connues – absence de toute schématique – il faudra déterminer celles-ci statiquement en étudiant le câblage, une opération longue et fastidieuse, ou bien dynamiquement ce qui sous-entend toutefois que l’équipement démarre partiellement. A cette fin, le bus d’adresse sera supervisé par un analyseur logique – j’utilise pour cela un vieux mais excellent Tektronix 1241 – et l’acquisition déclenchée par le signal de sélection du périphérique. Avec un peu d‘habitude, l’opération va très vite.

L’outil étant configuré, l’opération de désassemblage peut commencer, et avec elle un processus d’analyse itératif pouvant être très long.

Le dicton ‘cent fois sur le métier tu remettras ton ouvrage’ s’applique ici parfaitement car l’outil ne peut aller au-delà de ses limites et il faudra régulièrement intervenir pour corriger l’analyse, pour nommer les fonctions au fur et à mesure que l’on comprend leur rôle, pour assigner un nouveau nom à une variable…


Peu à peu les choses se dessinent, la logique apparaît, la méthode s’affine et l’on est capable d’identifier la structure du code. S’agissant d’un programme écrit en langage assembleur natif, le style du programmeur transparait permettant d’accélérer le travail d’analyse. Que le programme ait été écrit en langage évolué – langage C en général – et l’on sera rapidement à même d’identifier la transcription des instructions de contrôle complexes telles les switch, for ou do loop.

Et après quelques heures de travail, voire quelques week-ends, l’objectif est enfin atteint : toutes les informations requises pour interpréter le code d’erreur affiché, ou le cas échéant cibler le composant susceptible d’être à l’origine de la panne, sont en notre disposition.
Cette approche m’a ainsi permis de comprendre une fonctionnalité non documentée présente sur l’une des versions du programme équipant mon récepteur RACAL 1792 – processeur Mostek F8 - et de modifier ce code pour éliminer la limitation de la fréquence maximale.

Appliquée à un autre récepteur – processeur 6809 - affichant un comportement erratique, elle m’a permis d’identifier les routines de test puis les combinaisons d’un dip-switch permettant d’activer celles-ci. Le test de la mémoire vive ainsi identifié a permis de mettre en évidence un défaut aléatoire dans la RAM.

Aucun commentaire: