unit PE;

interface

uses Windows, SysUtils, Classes;

type

  //Lecture de la table des sections////////////////////////////////////////////
  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;
  //////////////////////////////////////////////////////////////////////////////


resourcestring
  //Erreurs/////////////////////////////////////////////////////////////////////
  sFileDoesntExists = 'Le fichier "%s" n''existe pas !';
  sNotAValidPE      = '"%s" n''est pas un fichier PE valide !';
  //////////////////////////////////////////////////////////////////////////////

  //Caractéristiques d'une section//////////////////////////////////////////////
  sCntCode          = 'La section contient du code exécutable';
  sCntInitialized   = 'La section contient des données initialisées';
  sCntUnInitialized = 'La section contient des données non initialisées';
  sLnkComDat        = 'La section contient des données COMDAT';

  sAlign1Bytes      = 'Données alignées sur un octet';
  sAlign2Bytes      = 'Données alignées sur deux octets';
  sAlign4Bytes      = 'Données alignées sur quatre octets';
  sAlign8Bytes      = 'Données alignées sur huit octets';
  sAlign16Bytes     = 'Données alignées sur seize octets';
  sAlign32Bytes     = 'Données alignées sur 32 octets';
  sAlign64Bytes     = 'Données alignées sur 64 octets';
  sAlign128Bytes    = 'Données alignées sur 128 octets';
  sAlign256Bytes    = 'Données alignées sur 256 octets';
  sAlign512Bytes    = 'Données alignées sur 512 octets';
  sAlign1024Bytes   = 'Données alignées sur un kilo-octet';
  sAlign2048Bytes   = 'Données alignées sur deux kilo-octets';
  sAlign4096Bytes   = 'Données alignées sur quatre kilo-octets';
  sAlign8192Bytes   = 'Données alignées sur huit kilos-octets';

  sLnkNRelocOVFL    = 'La section contient des relocations étendues';
  
  sMemDiscardable   = 'La section peut être supprimée après utilisation';
  sMemNotCached     = 'La section ne peut être mise en cache';
  sMemNotPaged      = 'La section ne peut être placée en mémoire paginée';
  sMemShared        = 'La section peut être partagée en mémoire';
  sMemExecute       = 'La section peut être exécutée comme du code';
  sMemRead          = 'La section peut être lue';
  sMemWrite         = 'La section peut être écrite';
  //////////////////////////////////////////////////////////////////////////////


const
  //Déclaration de certaines constantes manquantes pour les caractéristiques d'une section (inutilisées selon windows.pas)
  IMAGE_SCN_ALIGN_128BYTES  = $00800000;
  IMAGE_SCN_ALIGN_256BYTES  = $00900000;
  IMAGE_SCN_ALIGN_512BYTES  = $00A00000;
  IMAGE_SCN_ALIGN_1024BYTES = $00B00000;
  IMAGE_SCN_ALIGN_2048BYTES = $00C00000;
  IMAGE_SCN_ALIGN_4096BYTES = $00D00000;
  IMAGE_SCN_ALIGN_8192BYTES = $00E00000;

  //Tableau des caractéristiques d'une section
  SectionCharacs: Array[0..25]Of Cardinal = (IMAGE_SCN_CNT_CODE, IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_CNT_UNINITIALIZED_DATA,
                                             IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_ALIGN_1BYTES, IMAGE_SCN_ALIGN_2BYTES, IMAGE_SCN_ALIGN_4BYTES,
                                             IMAGE_SCN_ALIGN_8BYTES, IMAGE_SCN_ALIGN_16BYTES, IMAGE_SCN_ALIGN_32BYTES, IMAGE_SCN_ALIGN_64BYTES,
                                             IMAGE_SCN_ALIGN_128BYTES, IMAGE_SCN_ALIGN_256BYTES, IMAGE_SCN_ALIGN_512BYTES, IMAGE_SCN_ALIGN_1024BYTES,
                                             IMAGE_SCN_ALIGN_2048BYTES, IMAGE_SCN_ALIGN_4096BYTES, IMAGE_SCN_ALIGN_8192BYTES, IMAGE_SCN_LNK_NRELOC_OVFL,
                                             IMAGE_SCN_MEM_DISCARDABLE, IMAGE_SCN_MEM_NOT_CACHED, IMAGE_SCN_MEM_NOT_PAGED, IMAGE_SCN_MEM_SHARED,
                                             IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE);
  //Tableau des strings correspondantes
  StrSectionCharacs: Array[0..25]Of String = (sCntCode, sCntInitialized, sCntUnInitialized, sLnkComDat, sAlign1Bytes, sAlign2Bytes, sAlign4Bytes,
                                              sAlign8Bytes, sAlign16Bytes, sAlign32Bytes, sAlign64Bytes, sAlign128Bytes, sAlign256Bytes, sAlign512Bytes,
                                              sAlign1024Bytes, sAlign2048Bytes, sAlign4096Bytes, sAlign8192Bytes, sLnkNRelocOVFL, sMemDiscardable,
                                              sMemNotCached, sMemNotPaged, sMemShared, sMemExecute, sMemRead, sMemWrite);


function  IsValidPE(Fichier: String): Boolean;

implementation

(*
  Fonction vérifiant que le fichier dont le chemin est passé en paramètre est bien
  un fichier au format PE.
  S'il y a une erreur ou que ce n'est pas un fichier PE, la fonction renvoie False
*)
function IsValidPE(Fichier: String): Boolean;
var
  EnteteDOS: TImageDosHeader;  //Structure pour l'entête DOS MZ
  EntetePE : TImageNtHeaders;  //Structure pour l'entête PE
  FStream  : TFileStream;      //Stream de lecture
begin

  Result := True;

  If not FileExists(Fichier) then
    raise Exception.Create(Format(sFileDoesntExists, [Fichier]));

  FStream := TFileStream.Create(Fichier, fmOpenRead);
  try
    try
      //Lecture de l'entête DOS MZ
      FStream.ReadBuffer(EnteteDOS, SizeOf(EnteteDOS));

      //Comparaison de la signature du fichier avec la signature DOS
      If EnteteDOS.e_magic <> IMAGE_DOS_SIGNATURE then
        Result := False
      else
      begin
        //Déplacement jusqu'à l'entête PE dont l'offset est indiqué par _lfanew
        FStream.Seek(EnteteDOS._lfanew, soFromBeginning);
        //Lecture de l'entête PE
        FStream.ReadBuffer(EntetePE, SizeOf(EntetePE));

        //Comparaison de la signature de l'entête avec la signature PE
        If EntetePE.Signature <> IMAGE_NT_SIGNATURE then
          Result := False;
      end;
    except
      //En cas de problème, on renvoie False
      On Exception do
        Result := False;
    end;
  finally
    FStream.Free;
  end;
end;



{ Classe TSectionTable }

constructor TSectionTable.Create(Fichier: String);
begin
  //Vérification de la validité du fichier
  If not IsValidPE(Fichier) then
    raise Exception.Create(Format(sNotAValidPE, [Fichier]));

  //Lecture et stockage des sections dans le tablea fSections
  LireSections(Fichier);
end;

function TSectionTable.GetSection(Index: Integer): TSection;
begin
  If (Index >= 0) and (Index < Length(fSections)) then
    Result := fSections[Index];
end;

procedure TSectionTable.SetSection(Index: Integer; const Value: TSection);
begin
  If (Index >= 0) and (Index < Length(fSections)) then
    fSections[Index] := Value;
end;

(*
  Le champ Name de TImageSectionHeader est un tableau d'octets où chaque octet
  représente un caractère du nom. La fonction GetName, getter de la propriété
  Name de la classe, permet de retourner directement ce nom sous forme de string 
*)
function  TSectionTable.GetName(Index: Integer): String;
var
  i: Integer;
begin
  Result := '';

  If (Index < 0) or (Index > Length(fSections)) then
    Exit;

  for i := 0 to Length(fSections[Index].Header.Name) - 1 do
    Result := Result + Chr(fSections[Index].Header.Name[i]);
end;

function TSectionTable.GetCount: Integer;
begin
  Result := Length(fSections);
end;

(*
  LireSections parcourt la table des sections du fichier PE fournit à la création
  de la classe et stocke dans le tableau fSections les informations de chaque
  section.
*)
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;

(*
  SectionHasCharacteristic renvoie True si la ième section de la table a la
  caractéristique Charac présente dans son champ Characteristics
*)
function TSectionTable.SectionHasCharacteristic(Index: Integer; Charac: Cardinal): Boolean;
begin
  Result := (fSections[Index].Header.Characteristics and Charac) <> 0;
end;

(*
  GetCharacteristics renvoie sous forme d'une string l'intégralité des caractéristiques
  de la ième section de la table.
*)
destructor TSectionTable.Destroy;
var
  i: Integer;
begin
  for i := 0 to Length(fSections) - 1 do
    FreeMem(fSections[i].Donnees, fSections[i].Header.SizeOfRawData);    

  inherited;
end;

function TSectionTable.GetCharacteristics(Index: Integer): String;
var
  i: Integer;
begin
  Result := '';

  for i := Low(SectionCharacs) to High(SectionCharacs) do
    if SectionHasCharacteristic(Index, SectionCharacs[i]) then
      Result := Result + StrSectionCharacs[i] + #13;
end;

end.
