Tutorial 1 : Vue d'ensemble sur le
Format d'un PE File

C'est une complète réécriture d'un vieux Tutorial PE que je considérais comme le plus mauvais Tutorial que j'avais écrit. Donc j'ai décidé de le remplacer par ce nouveau.

PE veut dire 'Portable Executable'. C'est le format de base des fichiers Win32. Sa spécification provient en partie du format COFF d'Unix (format de fichier objet commun) ou (common object file format). La particularité du "Portable Executable" c'est que c'est un format de fichier universel à travers la plate-forme win32 : le PE Loader de chaque plate-forme win32 reconnaît et emploie ce format de fichier même lorsque Windows tourne sur des plates-formes CPU autres qu'Intel. Ça ne signifie pas que vos Programmes executables (au format PE) soient capables de supporter d'autres plates-formes CPU sans modification. Chaque Win32 exécutable (sauf les VxDs et les Dlls 16 bits) utilisent le format du PE File. Même les Drivers NT utilisent le format du PE File. C'est pourquoi le fait d'étudier le format du PE File vous fait bien comprendre la structure de Windows.

Plongeons dans la structure générale du format du PE File sans plus attendre.

DOS MZ header
DOS stub
PE header
Section table
Section 1
(ex: .data)
Section 2
Section ...
Section n
(ex: .code)

L'image ci-dessus représente la structure générale d'un PE File. Tous les PE File (même les DLLs 32 bits) doivent commencer par un simple entête DOS 'MZ'. Habituellement, nous ne sommes pas beaucoup intéressés par cette structure. Elle est fournit au cas où le programme tourne sous DOS, ainsi le DOS peut la reconnaître en tant qu'exécutable valide et peut ainsi lancer le 'DOS Stub' qui est stocké à la suite de l'entête MZ. Le 'DOS Stub' est en réalité un EXE valide qui est exécuté au cas où le système d'exploitation ne reconnaît pas le format du PE File. Ça sert simplement à afficher une chaîne de caractères comme " This program requires Windows" ou bien ça peut aussi faire apparaître un programme(fenêtre) DOS suivant l'intention du programmeur. Nous ne sommes pas non plus vraiment intéressés par le 'DOS Stub' : Habituellement, l'assembleur/compilateur le fournit de lui-même. Dans la plupart des cas, il emploie simplement l' int 21h, service 9 pour afficher une chaîne de caractères disant " This program cannot run in DOS mode".

Après le DOS stub, arrive le PE header (l'entête PE). Le PE header est un terme général pour la structure relative au PE nommée IMAGE_NT_HEADERS. Cette structure contient la plupart des paramètres essentiels pour le PE Loader. Par la suite, nous serons tout à fait familiers avec elle quand vous en saurez un peu plus à propos du format du PE File. Dans le cas où le programme est exécuté dans un système d'exploitation qui reconnaît le format du PE File, le PE Loader peut retrouver l'offset de départ du PE Header à partir du MZ header du DOS. De cette manière, il peut sauter le DOS Stub et aller directement au PE header qui est le véritable header du fichier. (son entête)

Le contenu réel du PE File est divisé en plusieurs blocs appelés sections. Une section n'est rien plus qu'un bloc de données avec des attributs communs comme le sont, le code en lecture et les données en écriture etc. Vous pouvez vous représenter le PE File comme un disque logique. Le PE header est le secteur de boot (la racine (comme C:)) et les sections sont les fichiers du disque. Les fichiers peuvent avoir des attributs différents comme lecture-seule, système, fichier caché, archives et cetera. Je souhaite éclaircir ce point dès à présent, toutes les données appartenant à un même groupe dans une section, ont pour base les mêmes attributs en communs : ce n'est pas le cas dans une base logique. Ça n'a pas d'importance la façon dont le code ou les données sont employés. Si les données et le code dans le PE File ont les mêmes attributs, on peut les regrouper dans une même section. Dans ce cas là, une section n'obéit plus au schéma classique des concepts logiques, comme le font les sections "data" et "code": Ici les sections peuvent contenir à la fois du code et des données à condition qu'ils aient les mêmes attributs. Si vous avez un bloc de données que vous souhaitez n'utiliser qu'en lecture-seule, vous pouvez placer ces données dans la section qui est affectée aux lectures-seules. Quand le PE Loader mappe des sections en mémoire, il examine les attributs des sections puis renvoie le bloc de mémoire qu'occupent maintenant ces sections.

Si nous nous représentons le format du PE File comme un disque logique, le PE header comme le secteur de boot et les sections comme des fichiers, parfois il nous manque des informations pour découvrir où les fichiers sont placés sur le disque. En fait, nous n'avons pas encore débattus sur ce qui serait l'équivalent des répertoires dans le cas du format des PE File. Immédiatement après le PE header vient La table des sections qui en fait est un tableau contenant plusieurs structures. Chaque structure contient les informations sur chaque section du PE File, comme par exemple ses attributs, l'offset du fichier, l'offset virtuel. S'il y a 5 sections dans le PE File, il y aura exactement 5 membres dans ce tableau de structures. Nous pouvons alors voir la table des sections un peu comme le 'root directory' (le répertoire principal) du disque logique. Chaque membre du tableau est l'équivalent d'un sous-répertoire du répertoire Principal.

C'est tout ce qu'il y a à savoir sur la disposition physique du format du PE File. Je récapitule les étapes principales pour charger un PE File en mémoire :

  1. Quand le PE File est lancé, le PE Loader examine le MZ header (du DOS) pour trouver l'offset du PE header. S'il le trouve, il saute au PE header.
  2. Le PE Loader vérifie si le PE header est valide. S'il l'est, il va à la fin de PE header.
  3. Immédiatement après le PE header on arrive à la table des sections. Le PE header lit les informations à propos des sections puis mappe ces sections en mémoire en employant le procédé de 'File Mapping' (on fait une copie en mémoire, d'un fichier qui lui est bien réel au départ). Il délègue également leurs attributs à chaque section comme c'est indiqué dans la table des sections.
  4. Après que le PE File ait été mappé en mémoire, le PE Loader se comporte lui-même comme une partie logique du PE File, en tant que table d'importation.

Les étapes ci-dessus sont d'une excessive simplification et sont basés sur mes propres observations. Il peut y avoir quelques inexactitudes mais ça doit vous éclairer le processus.

Vous devez télécharger La description du format du PE File de LUEVELSMEYER. Il est extrêmement détaillé et vous devez le considérer comme une véritable référence.


[Iczelion's Win32 Assembly Homepage]



Traduit par Morgatte