Voyons tout d'abord ce que nous avons appris jusqu'ici :
L'entête DOS MZ est appelé TImageDosHeader. Seuls deux de ses membres ont un intérêt pour nous :
e_magic qui contient le texte "MZ" et _lfanew qui contient l'offset de l'entête PE dans le fichier.
On utilise la valeur de e_magic pour vérifier que le fichier a un entête DOS valide, en la comparant à la
valeur de IMAGE_DOS_SIGNATURE. Si les deux valeurs correspondent, on considère que le fichier a un entête
DOS valide.
Pour aller à l'entête PE, il faut se déplacer dans le fichier à l'offset indiqué par la valeur de _lfanew.
Le premier DWord de l'entête PE doit contenir le texte "PE" suivi de deux zéros. La valeur de ce DWord est comparée
à la valeur IMAGE_NT_SIGNATURE. Si elles correspondent, alors on considère que l'entête PE est valide.
Nous en apprendrons plus sur l'entête PE dans ce tutoriel. Le nom de l'entête PE dans l'unité windows.pas est TImageNtHeaders.
Pour rappel, voici sa composition :
_IMAGE_NT_HEADERS = packed
record
Signature: DWORD;
FileHeader: TImageFileHeader;
OptionalHeader: TImageOptionalHeader;
end
;
TImageNtHeaders = _IMAGE_NT_HEADERS;
- Signature est la signature PE, "PE" suivi de deux zéros. Vous connaissez et utilisez déjà ce membre.
- FileHeader est une structure qui contient les informations relatives à l'organisation et des propriétés physiques générales du fichier PE.
- OptionalHeader est aussi une structure qui contient les informations sur l'organisation logique du fichier.
Les informations les plus intéressantes sont dans l'entête facultatif. Cependant, certains champs de l'entête de fichier ont également leur importance. Nous étudierons l'entête de fichier dans ce tutoriel, pour nous intéresser à l'entête facultatif dans le prochain tutoriel.
_IMAGE_FILE_HEADER = packed
record
Machine: Word
;
NumberOfSections: Word
;
TimeDateStamp: DWORD;
PointerToSymbolTable: DWORD;
NumberOfSymbols: DWORD;
SizeOfOptionalHeader: Word
;
Characteristics: Word
;
end
;
TImageFileHeader = _IMAGE_FILE_HEADER;
Champ | Contenu |
---|---|
Machine | Le processeur pour lequel le fichier est prévu. Pour une plateforme Intel, la valeur est IMAGE_FILE_MACHINE_I86 ($14C)(1). Ce champ est d'un rare intérêt pour nous, si ce n'est qu'il constitue une manière rapide d'empêcher un programme de s'exécuter sur une plateforme inadaptée. |
NumberOfSections | Le nombre de sections dans le fichier. Nous aurons besoin de modifier cette valeur lors de l'ajout ou de la suppression d'une section dans le fichier. |
TimeDateStamp | La date et l'heure auxquelles le fichier a été créé. Inutile pour nous. |
PointerToSymbolTable | Utilisé pour le débogage. Semble être toujours 0. |
NumberOfSymbols | Utilisé pour le débogage. Semble être toujours 0. |
SizeOfOptionalHeader | Taille de la structure OptionalHeader qui suit immédiatement l'entête fichier. Elle doit être initialisée avec une valeur correcte. |
Characteristics | Contient des flags pour le fichier, par exemple pour indiquer s'il s'agit d'un EXE ou d'une DLL. |
Pour résumer, seuls trois membres nous seront quelque peu utiles : Machine, NumberOfSections et
Characteristics. Vous n'aurez normalement aucune raison de modifier les valeurs de Machine ou de
Characteristics, mais il faudra utiliser NumberOfSections pour parcourir la table des sections.
Je prends un peu d'avance ici, mais pour illustrer l'utilisation de NumberOfSections, il faut dériver un peu
sur la table des sections elle-même.
La table des sections est un tableau de structures. Chaque structure contient les informations d'une section. Ainsi
s'il y a trois sections, il y aura trois membres dans cette table. La valeur de NumberOfSections est donc
nécessaire pour connaître le nombre de membres présents dans ce tableau. Vous songerez éventuellement que chercher
une structure contenant uniquement des zéros pourrait aider. Windows semble utiliser cette approche. Vous pouvez le
vérifier en changeant la valeur de NumberOfSections en un nombre supérieur à la valeur originale. Windows
lancera le fichier sans problème.
D'après mes observations, je pense que Windows lit la valeur de NumberOfSections et examine chaque structure
dans la table des sections. S'il trouve une structure qui ne contient que des zéros, il arrête sa recherche. Sinon
il continue jusqu'à avoir inspecté autant de structures qu'indiqué par NumberOfSections.
Pourquoi alors ne pas ignorer la valeur de NumberOfSections ? Il y a plusieurs raisons à cela. La
spécification du format PE n'indique pas que le tableau de la table des sections doit se terminer par une structure
ne contenant que des zéros. Ainsi on peut se trouver dans une situation où le dernier membre du tableau est contigu
à la première section, sans aucune forme d'espace libre. Une autre raison concerne les importations. La nouvelle
méthode de liaison insère les informations directement après le dernier membre du tableau de la table des sections.
Vous avez donc toujours besoin de NumberOfSections.
Liens
-
MSDN : Image Help Library
- MSDN : IMAGE_NT_HEADERS (TImageNtHeaders)
- MSDN : IMAGE_FILE_HEADER (TImageFileHeader)
Tutoriel suivant : Entête facultatif