Formats de fichier objet

Original: http://www.backerstreet.com/decompiler/object_formats.htm


Avant de démarrer la décompilation d’un fichier, que vous devez être capable de le lire.
Il y a 3 types possibles de formats de fichiers :
  1. Formats structurés (COFF, ELF)
  2. Formats de flux étiqueté (OMF, IEEE)
  3. Fichiers RAW (DOS, images rom, S-Archives etc..)
Chaque type de format a ses forces et ses faiblesses.
Les formats de deux fichier premiers transportent certains renseignements avec eux ce qui peut aider le décompilateur pour identifier les informations utiles. En revanche, le troisième type ne donne pas beaucoup d’informations, afin que l’information doit être fournie par l’utilisateur pour le décompilateur.
Chaque format de fichier nécessite son propre lecteur de Format de fichier.
Étape 1: identifier le type de fichier
La première étape après avoir ouvert le fichier est d’identifier son type. Flux structuré et étiquetés fichiers commencent par une séquence d’octets bien définie qui permet de les identifier. Voici quelques séquences d’octets pour les formats courants d’objet :
Contenu du fichier de format Offset
ELF 0 7F 45 4C 46
COFF 0 2-byte machine type (*)
IEEE
OMF

(*) Une caractéristique de COFF, c’est que les deux premiers octets identifier le format comme COFF et le processeur cible. Malheureusement, il n’y a pas de norme pour ces 2 octets, et également pour les processeurs qui prennent en charge les endiand big et little endian, les mêmes 2 octets peuvent apparaître dans les deux ordres, rendant difficile d’identifier le fichier comme un fichier au format COFF avec une certitude absolue.
Nous allons voir que même les fichiers au format COFF pour le même processeur cible peuvent avoir des structures de données différentes, car les différents compilateurs a choisi de ne pas suivre la norme (en général ils parfois utilisés champs 32 bits à la place de la définition originale de champ de 16 bits).
Si aucune des séquences ci-dessus est détectée, le fichier peut être une image raw ou un format de fichier inconnu. Dans ce cas, l’intervention manuelle est requise de l’utilisateur pour spécifier les informations le décompilateur a besoin.
Étape 2: identifier le type de processeur
Étant donné que nous allons avoir affaire avec les instructions de la machine que le décompilateur doit identifier la cible CPU, c’est le CPU en mesure d’exécuter les instructions dans le fichier d’entrée. Décompilation n’exige pas l’exécution effective des instructions, alors la cible CPU peut être différente de celui qui exécute le décompilateur (le CPU hôte). Autrement dit, décompilateurs doivent être croisés outils, en mesure d’accepter les fichiers binaires générés pour les architectures de processeur différentes.
En sélectionnant le CPU correct parfois définit les types de données que le programme cible va utiliser. Cela cependant n’est pas toujours vrai, puisque le programme original peut avoir été rédigé dans une variété de modèles. Les formats de fichiers structurés suivants fournissent des informations architecturales :
Contenu du fichier de format Offset
ELF 0x12 2-byte machine type
COFF 0 see previous table
IEEE
OMF


En revanche, seuls les formats raw ne fournissent qu’un minimum d’information (parfois il n’y aucune information du tout). Dans ces cas, un certain nombre d’heuristiques peut être épandu sur infèrent le type du fichier. Le décompilateur peut utiliser une base de données de séquences de code commun pour identifier la cible CPU. Cela peut être un long shot, mais elle est parfois réussie, si rien d’autre à faire une suggestion à l’utilisateur. Si aucune correspondance n’est trouvée, l’utilisateur doit fournir (via un fichier de projet ou par l’intermédiaire de l’interface utilisateur) la cible CPU qu’il souhaite utiliser avant de procéder à l’analyse du fichier objet.
Étape 3: identifier les zones de code, des données et des informations
Les formats de l’objet structuré transportent fois code et zones de données qui seront exécutés lorsque le programme est exécuté, et aussi zones d’appui qui sont utilisés par le système d’exploitation lors du chargement du fichier en mémoire (mais dont le contenu n’est pas réellement exécuté par le CPU) et aussi les zones qui sont utilisés par d’autres outils comme un débogueur.
Les formats ELF et COFF reposent sur le concept des sections. Une section est une zone dans le fichier qui contient des informations homogènes, comme tout le code, ou toutes données ou tous les symboles, etc..
Le décompilateur lit la table de la section et l’utilise pour convertir des offsets de fichier d’adresses et vice versa.
Il est également utilisé pour permettre à l’utilisateur d’inspecter le fichier par offset ou par adresse (par exemple quand vous faites un vidage hexadécimal du contenu du fichier).
Notez cependant que pas nécessairement une section marquée comme exécutable contiendra uniquement des instructions de la machine. Autres types de données en lecture seule peuvent aussi être mis dans une section exécutable, tel que lecture seule des chaînes (“const”) et les constantes en virgule flottante. Le compilateur ou l’éditeur de liens peut également ajouter du code supplémentaire qui n’était pas directement généré par le code source compilé. Tables de fonctions virtuelles, gestion des exceptions (try/catch/jet) en C++ et le Global Offset Table (GOT) et la procédure Linkage Table (PLT) pour soutenir la liaison dynamique des dll en est un exemple de code supplémentaire.
Il est donc important que le décompilateur identifie les données qui a été placées dans la section de code, afin qu’il ne pas démonter certaines zone de données. Si cela se produit, beaucoup des analyses successives utiliseront des données incorrectes, éventuellement invalider le processus entier de décompilation. Tous les formats de fichier devraient fournir à tout le moins l’offset de la première instruction exécutée après que le fichier est chargé en mémoire.
Les zones d’information peuvent aider énormément le processus de décompilation, parce que chaque élément d’information supplémentaire au-delà du code exécuté et les données est un élément d’information qui le décompilateur n’aura pas à deviner à l’aide de sa propre analyse.
Les fichiers exécutables non-dépouillé fournissent différents au niveau des informations symboliques :
  • adresses des étiquettes globales : ceux-ci pourraient être des points d’entrée de fonction et les variables de données globales. Notez cependant que la plupart du temps la taille de ces objets n’est pas fournie. Autrement dit, nous ne saurons pas commence une fonction, mais nous ne saurons pas elle se termine. Étant donné que les points d’entrée des fonctions statiques ne peuvent pas être stockés dans les fichiers, il n’est pas fiable pour simplement présumer qu’une fonction se termine au début du point d’entrée de la fonction suivante.
  • noms de bibliothèques (liés dynamiquement) importées et les adresses des points d’entrée de bibliothèques ou de trampoline code généré pour accéder à ces bibliothèques. Si le programme cible est une DLL proprement dite, une table d’exportation est stockée dans le fichier binaire. La table d’exportation fournit le point d’entrée des fonctions exportées par la DLL.
  • Si un tableau de réinstallation se trouve dans le fichier, le décompilateur pouvez utiliser les informations qui y sont à déduire quelles instructions fonctionnent sur des adresses par opposition à des constantes numériques. C’est très important en essayant d’identifier l’objet d’une notice de montage. Cela signifie également que le décompilateur doit relier pratiquement le fichier cible à une adresse mémoire fictif, qui peut être totalement différente de l’adresse réelle qui sera utilisé par le système d’exploitation, en particulier lorsque le décompiler un fichier transférable (.o, .obj ou .dll).
  • Si le fichier a été compilé avec les informations de débogage (-g sur les systèmes Unix), on trouvera beaucoup plus d’informations, telles que la liste des fichiers source et les numéros de ligne qui a été utilisé pour construire le programme de la cible, les types des variables et les deux mondiaux, module statique et fonction des variables locales avec leurs noms. C’est le meilleur des cas, donc un décompilateur doit profiter de cette mine d’informations.
La sortie du chargeur de format de fichier objet est un ensemble de tables qui permet les étapes suivantes de la decompiler est indépendant de n’importe quel format de fichier objet particulier.