Un meilleur job mieux payé ?

Deviens chef de projet, développeur, ingénieur, informaticien

Mets à jour ton profil pro

ça m'intéresse

Developpez.com - Delphi
X

Choisissez d'abord la catégorieensuite la rubrique :


PARTIE 5 : La table des sections

Date de publication : 08/10/2005

Par Iczelion (Iczelion's Win32 Assembly Homepage)
 Traduction et adaptation par Olivier Lance (Accueil)
 

Jusqu'à ce tutoriel, nous avons vu l'entête et l'entête . La table des sections est le sujet de ce nouveau tutoriel. Article original : Retour à Tutoriel précédent : Tutoriel suivant :



Théorie

La table des sections est en fait un tableau de structures suivant immédiatement l'entête PE. Le nombre de membres dans ce tableau est donné par le champ NumberOfSections dans l'entête fichier (TImageFileHeader). La structure en question est appelée TImageSectionHeader.

En voici la déclaration dans windows.pas :
_IMAGE_SECTION_HEADER = packed record
    Name: packed array[0..IMAGE_SIZEOF_SHORT_NAME-1] of Byte;
    Misc: TISHMisc;
    VirtualAddress: DWORD;
    SizeOfRawData: DWORD;
    PointerToRawData: DWORD;
    PointerToRelocations: DWORD;
    PointerToLinenumbers: DWORD;
    NumberOfRelocations: Word;
    NumberOfLinenumbers: Word;
    Characteristics: DWORD;
  end;
  
  TImageSectionHeader = _IMAGE_SECTION_HEADER;
Encore une fois, tous les champs ne sont pas utiles. Voici la description de ceux qui sont vraiment intéressants :

Champ Contenu
Name Ce champ contient le nom de la section. Notez que la taille maximale est de 8 octets (IMAGE_SIZEOF_SHORT_NAME = 8). Le nom n'est qu'une "étiquette", rien de plus. Vous pouvez utiliser n'importe quel nom, voire laisser ce champ vide. Notez également qu'il n'y a aucune mention de caractère terminal nul. Le nom n'est pas une chaîne à zéro terminal, donc ne vous attendez pas à trouver un 0 à sa fin.
VirtualAddress La RVA de la section. Le PE Loader vérifie et utilise la valeur de ce champ lorsqu'il place une section en mémoire. Ainsi si la valeur de ce champ est $1000 et que le fichier PE est chargé à l'adresse $400000, la section sera chargée à l'adresse $401000.
SizeOfRawData La taille des données de la section, arrondie au prochain multiple de FileAlignment (voir l'entête fichier). Le PE Loader vérifie ce champ pour savoir combien d'octets de la section il va devoir placer en mémoire.
PointerToRawData L'offset fichier du début de la section. Le PE Loader utilise la valeur de ce champ pour trouver les données de la section dans le fichier.
Characteristics Contient des drapeaux indiquant par exemple si la section contient du code exécutable, des données non initialisées, si elle dispose d'un accès en écriture ou en lecture.
Maintenant que nous connaissons la structure TImageSectionHeader, voyons comment on pourrait émuler le travail du PE Loader :

  1. Lire NumberOfSections dans l'entête fichier pour connaître le nombre de sections dans le fichier.
  2. Utiliser la valeur SizeOfHeaders comme l'offset fichier de la table des sections et y déplacer le pointeur de fichier.
  3. Parcourir le tableau de structures en examinant chaque membre.
  4. Pour chaque membre, récupérer la valeur de PointerToRawData et déplacer le pointeur de fichier à cet offset. Ensuite, lire la valeur de SizeOfRawData pour connaître le nombre d'octets à placer en mémoire. Lire la valeur VirtualAddress et y ajouter la valeur ImageBase pour obtenir l'adresse virtuelle où la section est censée débuter. A partir de là, on peut placer la section en mémoire et lui assigner les attributs spécifiés dans Characteristics.
  5. Parcourir le tableau jusqu'à avoir examiné toutes les sections.
Notez que nous n'avons pas utilisé le champ Name : il n'est pas vraiment nécessaire.


Exemple

L'exemple suivant permet d'ouvrir un fichier PE et d'afficher les informations des sections qu'il contient. Le programme est basé sur l'utilisation d'une classe TSectionTable qui permet de lire toutes les sections d'un fichier exécutable et d'y donner accès au moyen d'une propriété de type tableau. Nous ne détaillerons pas toute la classe mais donnerons seulement sa déclaration et la méthode qui nous intéresse le plus : LireSections.
Déclaration de TSectionTable
type
  TSection = record
    Header : TImageSectionHeader;
    Donnees: pChar;
  end;

  TSectionArray = Array of TSection;

  TSectionTable = class
  private
    FichierPE: TFileStream;
    fSections: TSectionArray;

    procedure LireSections(const Fichier: String);

    function  GetSection(Index: Integer): TSection;
    procedure SetSection(Index: Integer; const Value: TSection);
    function  GetName(Index: Integer): String;
    function  GetCount: Integer;
    function  GetCharacteristics(Index: Integer): String;
  public
    constructor Create(Fichier: String);
    destructor  Destroy; override;
    function    SectionHasCharacteristic(Index: Integer; Charac: Cardinal): Boolean;

    property Sections[Index: Integer]  : TSection read GetSection write SetSection; default;
    property Name    [Index: Integer]  : String              read GetName;
    property Count                     : Integer             read GetCount;
    property StrCharacs[Index: Integer]: String              read GetCharacteristics;
  end;
On remarque en premier lieu la déclaration du type TSection. Il associe simplement aux headers d'une section les données qui lui correspondent, sous forme d'un pChar pointant vers une zone mémoire qui sera allouée au besoin.

Les propriétés publiques de cette classe sont les suivantes :


La méthode LireSections est appelée dans le constructeur, auquel on passe le fichier à ouvrir. Le constructeur se charge de vérifier que le fichier fourni est bien un fichier PE en utilisant la fonction vue en deuxième partie puis exécute LireSections :
Méthode de lecture des sections
procedure TSectionTable.LireSections(const Fichier: String);
var
  EnteteDOS : TImageDosHeader;  //Structure pour l'entête DOS MZ
  EntetePE  : TImageNtHeaders;  //Structure pour l'entête PE
  SectionNbr: Integer;
  i         : Integer;
begin
  FichierPE := TFileStream.Create(Fichier, fmOpenRead);

  Try
    //Lecture de l'entête PE
    FichierPE.Read(EnteteDOS, SizeOf(EnteteDOS));
    FichierPE.Seek(EnteteDOS._lfanew, soFromBeginning);
    FichierPE.Read(EntetePE, SizeOf(EntetePE));

    //On récupère le nombre de sections dans le fichier
    SectionNbr := EntetePE.FileHeader.NumberOfSections;

    If SectionNbr <> 0 then
    begin
      SetLength(fSections, SectionNbr);

      //Les sections sont à la suite dans le fichier, on les lit une à une
      for i := 0 to SectionNbr - 1 do
        FichierPE.Read(fSections[i].Header, SizeOf(fSections[i].Header));

      //Lecture et stockage des données de chaque section
      for i := 0 to SectionNbr - 1 do
      begin
        FichierPE.Seek(fSections[i].Header.PointerToRawData, soFromBeginning);

        GetMem(fSections[i].Donnees, fSections[i].Header.SizeOfRawData);
        FichierPE.Read(fSections[i].Donnees^, fSections[i].Header.SizeOfRawData);
      end;

    end;       
  Finally
    FreeAndNil(FichierPE);
  end;
end;
Après s'être rendu à la table des sections grâce aux informations contenues dans les différents entêtes, la méthode ajoute chaque section au tableau fSections (qui sera accessible par la propriété Sections) puis parcourt le fichier pour retrouver les données de chacune et les enregistrer en mémoire.

Une fois la lecture terminée, le fichier est fermé et toutes les informations sont accessibles sans relecture ultérieure.
Il suffit alors d'accéder aux éléments de Sections pour obtenir toutes les informations voulues.


Téléchargements

Vous pouvez télécharger le projet utilisant cette classe ici :
miroir 1
miroir 2 (si le miroir 1 ne fonctionne pas)

Vous aurez également besoin du fichier PE.pas :
miroir 1
miroir 2 (si le miroir 1 ne fonctionne pas)

Placez PE.pas dans un répertoire de votre choix et dézippez l'application dans un sous-répertoire de celui-ci pour pouvoir compiler.


Liens

Tutoriel suivant : La table d'importation



Valid XHTML 1.1!Valid CSS!

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2005 Olivier Lance. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Responsables bénévoles de la rubrique Delphi : Gilles Vasseur - Alcatîz -