Accueil de la Caverne Informatique - La mémoire virtuelle - Ecrire son O.S. - Sauver ses partitions

Les formats exécutables de Windows

  1. Introduction
  2. Format COM Ms-Dos
  3. Format exécutable Ms-Dos
  4. Format exécutable Windows
  5. Windows et la mémoire virtuelle
  6. Extensions
  7. Informations complémentaires

Introduction

Il existe de multiples formats de fichiers exécutables. Le plus ancien est le format COM, qui consiste en un "dump" de la mémoire. Bien que ce format ne soit plus utilisé, il est utile de le connaître, afin de mieux comprendre les difficultés liées l'adressage mémoire. Puis nous nous pencherons sur le format exécutable Ms-Dos, car tous les exécutables modernes maintiennent une compatibilité avec ce format antédiluvien. Enfin, nous regarderons la structure du format courant supporté par les Windows 32 bits (95, 98, Me, NT, 2000, XP, ...)

[haut de page]

Format COM Ms-Dos

Ce format existe depuis les origines du Dos et est conçu pour le mode réel. Les fichiers portent l'extension .com (par exemple command.com). Le format de ces exécutables est simplissime : il n'existe pas. Le fichier contient une copie conforme du code binaire tel qu'il doit être chargé en mémoire. L'inconvénient majeur de ce format est que le fichier DOIT être chargé à une adresse précise. En effet, il contient des références absolues à des zones mémoire. Par exemple, regardons le code assembleur qui lit le contenu d'une variable var1 et le place dans une variable var2 :

mov ax, var1
mov var2, ax

En décompilant le binaire obtenu, nous lirons quelque chose comme :

mov ax, [1A3Eh]
mov [1A40h], ax

Les adresses 0x1A3E et 0x1A40 sont absolues. L'instruction MOV va lire le contenu de cette zone mémoire, que la variable attendue y soit présente ou non. Si jamais le fichier est chargé dans une zone mémoire différente, toutes les références deviennent erronées. Il y aurait bien la possibilité d'analyser une à une chaque instruction et de modifier à la volée les adresses, mais ça réclamerait un temps considérable.

L'autre inconvénient du format COM est qu'il n'y a aucune séparation entre les données, la pile et le code. Tout est placé pêle-mêle dans la même zone mémoire. Un binaire de ce type ne peut donc pas profiter des avantages du mode protégé, qui définit des droits d'accès aux zones mémoire (par exemple, le code et les données constantes en lecture seule, la pile et les données variables en lecture / écriture).

A l'heure actuelle, ce format n'est absolument plus utilisé. Même les fichiers portant l'extension .com dans Windows (par exemple c:\windows\system32\edit.com) sont en réalité au format exécutable Ms-Dos ou Win32.

[haut de page]

Format exécutable Ms-Dos

Ce format existe depuis Ms-Dos 2.0. Le fichier est divisé en segments et est précédé d'un header. Cet en-tête contient beaucoup d'informations utiles, dont :

Les adresses sont toujours absolues, mais le fichier comprend désormais une table de "relocalisation". Ce tableau contient des pointeurs vers toutes les adresses mémoire à modifier si jamais le fichier exécutable n'était pas chargé à l'endroit attendu.

Avec l'apparition des Windows, de nouveaux formats exécutables ont été définis ; cependant Microsoft a tenu à conserver une compatibilité minimale avec le format initial Ms-Dos. C'est pour cela que n'importe quel programme Win16 et Win32 contient - dans son en-tête - un véritable programme Ms-Dos. Celui-ci affiche un message généralement d'avertissement :

This program cannot be run in DOS mode.

Cependant, ce système a été conçu pour permettre la cohabitation - dans un même fichier - de deux versions d'un même programme, l'une Ms-Dos, l'autre Windows. C'est le cas de ScanDisk pour Windows 95/98.

Pour permettre la cohabitation des deux formats (exécutables DOS et Windows), le header Ms-Dos a été incrémenté de quelques nouveaux champs, dont l'un contient un pointeur vers une seconde structure propre au nouveau format. La structure complète du header Ms-Dos, IMAGE_DOS_HEADER, est définie dans le fichier winnt.h :

    typedef struct _IMAGE_DOS_HEADER // DOS .EXE header
    {
 0h	WORD e_magic;		// Magic number
 2h	WORD e_cblp;		// Bytes on last page of file
 4h	WORD e_cp;  		// Pages in file
 6h	WORD e_crlc;		// Relocations
 8h	WORD e_cparhdr;		// Size of header in paragraphs
 Ah	WORD e_minalloc;	// Minimum extra paragraphs needed
 Ch	WORD e_maxalloc;	// Maximum extra paragraphs needed
 Eh	WORD e_ss;  		// Initial (relative) SS value
10h	WORD e_sp;  		// Initial SP value
12h	WORD e_csum;		// Checksum
14h	WORD e_ip;  		// Initial IP value
16h	WORD e_cs;  		// Initial (relative) CS value
18h	WORD e_lfarlc;		// File address of relocation table
1Ah	WORD e_ovno;		// Overlay number
1Ch	WORD e_res[4];		// Reserved words
24h	WORD e_oemid;		// OEM identifier (for e_oeminfo)
26h	WORD e_oeminfo;		// OEM information; e_oemid specific
28h	WORD e_res2[10];	// Reserved words
3Ch	LONG e_lfanew;		// File address of new exe header
    }
    IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
[haut de page]

Format exécutable Windows

Ce format se compose d'un en-tête Ms-Dos, d'un programme Ms-Dos, d'un second en-tête propre au format, suivi d'une ou plusieurs sections. Une section contient soit des données, soit du code. Elle peut se voir attribuer des droits d'exécution, de lecture et d'écriture.

A l'offset pointé par le champ e_lfanew de l'en-tête Ms-Dos, se trouve une seconde structure propre aux formats Windows. Il existe deux versions majeures de celle-ci :

Parce que le format NE n'est plus utilisé aujourd'hui, nous ne décrirons pas dans ce document la structure de son en-tête. La signature de ce format est 0x4E45, nombre qui forme la chaîne ascii NE.

L'en-tête PE est également définie dans winnt.h par la structure IMAGE_NT_HEADERS.

    typedef struct _IMAGE_NT_HEADERS
    {
 0h	DWORD Signature;
 4h	IMAGE_FILE_HEADER FileHeader;
18h	IMAGE_OPTIONAL_HEADER OptionalHeader;
    }
    IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;

Pour les exécutables PE, la signature est 0x00004550, ce qui en notation little endian donne la chaîne ascii "PE\0\0". Les deux sous-structures IMAGE_FILE_HEADER et IMAGE_OPTIONAL_HEADER sont définies comme suit :

    typedef struct _IMAGE_FILE_HEADER
    {
 0h	WORD  Machine;
 2h	WORD  NumberOfSections;
 4h	DWORD TimeDateStamp;
 8h	DWORD PointerToSymbolTable;
 Ch	DWORD NumberOfSymbols;
10h	WORD  SizeOfOptionalHeader;
12h	WORD  Characteristics;
    }
    IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

Malgré son nom, la structure IMAGE_OPTIONAL_HEADER n'est absolument pas optionnelle. Elle contient des informations très importantes.

    typedef struct _IMAGE_OPTIONAL_HEADER
    {
 0h	WORD   Magic;
 2h	BYTE   MajorLinkerVersion;
 3h	BYTE   MinorLinkerVersion;
 4h	DWORD  SizeOfCode;
 8h	DWORD  SizeOfInitializedData;
 Ch	DWORD  SizeOfUninitializedData;
10h	DWORD  AddressOfEntryPoint;
14h	DWORD  BaseOfCode;
18h	DWORD  BaseOfData;
1Ch	DWORD  ImageBase;
20h	DWORD  SectionAlignment;
24h	DWORD  FileAlignment;
28h	WORD   MajorOperatingSystemVersion;
2Ah	WORD   MinorOperatingSystemVersion;
2Ch	WORD   MajorImageVersion;
2Eh	WORD   MinorImageVersion;
30h	WORD   MajorSubsystemVersion;
32h	WORD   MinorSubsystemVersion;
34h	DWORD  Win32VersionValue;
38h	DWORD  SizeOfImage;
3Ch	DWORD  SizeOfHeaders;
40h	DWORD  CheckSum;
44h	WORD   Subsystem;
46h	WORD   DllCharacteristics;
48h	DWORD  SizeOfStackReserve;
4Ch	DWORD  SizeOfStackCommit;
50h	DWORD  SizeOfHeapReserve;
54h	DWORD  SizeOfHeapCommit;
58h	DWORD  LoaderFlags;
5Ch	DWORD  NumberOfRvaAndSizes;
60h	IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    }
    IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

Parmi les champs qui méritent d'être examinés de plus près, il y a ImageBase, qui contient l'adresse mémoire à laquelle le programme souhaite être chargé. Windows essaie de toujours charger un programme à l'adresse demandée : s'il est effectivement chargé à cette adresse, il n'y aura pas besoin de recalculer les adresses absolues à l'aide des tables de relocalisation. ImageBase est une adresse virtuelle. Nous verrons dans le chapitre suivant à quoi elle correspond réellement.

La plupart des autres références mémoire sont des adresses relatives à ImageBase. AddressOfEntryPoint, l'adresse du point d'entrée dans l'image, ou BaseOfCode, pointeur vers le début de la section de code, ces adresses expriment un offset relatif à ImageBase. Ces adresses sont appelées RVA (Relative Virtual Address).

Le tableau DataDirectory définit une liste de "répertoires" et leur offset dans le fichier. Le nombre d'entrées dans le tableau est donné par le champ NumberOfRvaAndSizes. Ces "répertoires" réunissent des données telles que :

Une description complète de la structure IMAGE_DATA_DIRECTORY se trouve dans la Msdn.

Directement suivant le header IMAGE_NT_HEADERS, on trouve une suite de structures IMAGE_SECTION_HEADER. Le nombre de ces structures est donné par le champ NumberOfSections de IMAGE_FILE_HEADER. L'offset de la première structure se calcule donc par :

DWORD offset = dos_header.e_lfanew + sizeof( IMAGE_NT_HEADERS );

Une structure décrit la taille et l'adresse de la section dans le fichier, la taille et l'adresse de la section en mémoire, les permissions de la section (exécution, lecture, écriture).

    typedef struct _IMAGE_SECTION_HEADER
    {
 0h	BYTE  Name[ IMAGE_SIZEOF_SHORT_NAME ];
 8h	union
	{
	    DWORD   PhysicalAddress;
	    DWORD   VirtualSize;
	} Misc;
 Ch	DWORD VirtualAddress;
10h	DWORD SizeOfRawData;
14h	DWORD PointerToRawData;
18h	DWORD PointerToRelocations;
1Ch	DWORD PointerToLinenumbers;
1Eh	WORD  NumberOfRelocations;
20h	WORD  NumberOfLinenumbers;
24h	DWORD Characteristics;
    }
IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

Toutes les références mémoire sont relatives, soit au début du fichier, soit à l'adresse de chargement ImageBase. Il est assez fréquent que les adresses et les tailles sur le disque et en mémoire soient différentes.

[haut de page]

Windows et la mémoire virtuelle

Bien que le format exécutable Windows prévoit un espace pour un tableau de relocalisation (relocation table), les dernières versions des compilateurs (à partir de Visual Studio 6) ne jugent plus utiles de les ajouter. En effet, dans les Windows 32-bits, tout programme s'exécute dans un espace d'adresses virtuelles. Cela signifie qu'un programme a virtuellement accès à 4 Go de RAM auquel il est seul à accéder (il n'y trouvera aucun autre programme).

Cet espace d'adresses virtuelles est créé au moment du chargement du programme, par conséquent l'espace est vierge, et l'adresse absolue à laquelle le programme a prévu de se charger est toujours disponible. C'est pour cela que désormais, seules les DLLs, qui peuvent être chargées n'importe où n'importe quand continuent d'include une relocation table.

Une description plus détaillée du fonctionnement de la mémoire virtuelle est disponible ici.

[haut de page]

Extensions

Il existe de nombreuses extensions différentes pour désigner des fichiers contenant du code exécutable. Les plus courantes sont :

Tous ces fichiers ont exactement le même format. Par exemple, la seule réelle différence entre un EXE et une DLL est un flag positionné ou non dans le champ Characteristics de la structure IMAGE_FILE_HEADER.

Cependant, seuls les EXE doivent définir obligatoirement un point d'entrée (qui correspond à la procédure main). Les DLL n'en définissent généralement pas.

[haut de page]

Informations complémentaires

MSDN

La documentation officielle de Microsoft. Vous trouverez (entre autre) la spécification complète du format Portable Executable PE.

winnt.h

Fichier include C (normalement fourni avec votre compilateur Windows) qui contient la définition des structures de données présentées.

[Haut de page]


Accueil de la Caverne Informatique - La mémoire virtuelle - Ecrire son O.S. - Sauver ses partitions

Si vous avez apprécié (ou détesté) cet article ou ce site, vous avez la possibilité de donner une note d'évaluation et indiquer les points forts, les points faibles de ce site. Ce service est offert par l'institut de statistiques Weborama.

Eric Minso - décembre 2003 - La Caverne Informatique - http://cavinfo.fr.st/