Home > Delphi, Disassembly, Tech > Display PE headers for a file

Display PE headers for a file

I was interested in retrieving some information about a particular file, and learning a bit more about the portable executable file format.

I found some code on Torry written by Peter Below that did everything I needed. I converted the code to work in a console application, and to allow for any number of files to be passed in on the command line and analyzed.

This utility will dump mounds of information contained in the PE header, which is useful when disassembling.

Click ‘Read More’ for my modifications of Peter’s original source code.

NOTE: My source code uses a non-standard unit called CmdLineHelper.pas, which is available from About.com’s Delphi blog.

program PeShow;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Windows,
  Classes,
  CmdLineHelper;

procedure DumpDOSHeader(const h: IMAGE_DOS_HEADER; var Lines: TStringList);
begin
  Lines.Add('  [Dump of DOS file header]');
  Lines.Add('        -------------');
  Lines.Add(Format('  Magic number (File Signature): %d', [h.e_magic]));
  Lines.Add(Format('  Bytes on last page of file: %d', [h.e_cblp]));
  Lines.Add(Format('  Pages in file: %d', [h.e_cp]));
  Lines.Add(Format('  Relocations: %d', [h.e_crlc]));
  Lines.Add(Format('  Size of header in paragraphs: %d', [h.e_cparhdr]));
  Lines.Add(Format('  Minimum extra paragraphs needed: %d', [h.e_minalloc]));
  Lines.Add(Format('  Maximum extra paragraphs needed: %d', [h.e_maxalloc]));
  Lines.Add(Format('  Initial (relative) SS value: %d', [h.e_ss]));
  Lines.Add(Format('  Initial SP value: %d', [h.e_sp]));
  Lines.Add(Format('  Checksum: %d', [h.e_csum]));
  Lines.Add(Format('  Initial IP value: %d', [h.e_ip]));
  Lines.Add(Format('  Initial (relative) CS value: %d', [h.e_cs]));
  Lines.Add(Format('  File address of relocation table: %d', [h.e_lfarlc]));
  Lines.Add(Format('  Overlay number: %d', [h.e_ovno]));
  Lines.Add(Format('  OEM identifier (for e_oeminfo): %d', [h.e_oemid]));
  Lines.Add(Format('  OEM information; e_oemid specific: %d', [h.e_oeminfo]));
  Lines.Add(Format('  File address of new exe header: %d', [h._lfanew]));
  Lines.Add('');
end;

procedure DumpPEHeader(const h: IMAGE_FILE_HEADER; var Lines: TStringList);
var
  dt: TDateTime;
begin
  Lines.Add('  [Dump of PE file header]');
  Lines.Add('        -------------');
  Lines.Add(Format('  Machine: %4x', [h.Machine]));
  case h.Machine of
    IMAGE_FILE_MACHINE_UNKNOWN: Lines.Add('   MACHINE_UNKNOWN ');
    IMAGE_FILE_MACHINE_I386: Lines.Add('   Intel 386. ');
    IMAGE_FILE_MACHINE_R3000: Lines.Add('   MIPS little-endian, 0x160 big-endian ');
    IMAGE_FILE_MACHINE_R4000: Lines.Add('   MIPS little-endian ');
    IMAGE_FILE_MACHINE_R10000: Lines.Add('   MIPS little-endian ');
    IMAGE_FILE_MACHINE_ALPHA: Lines.Add('   Alpha_AXP ');
    IMAGE_FILE_MACHINE_POWERPC: Lines.Add('   IBM PowerPC Little-Endian ');
    // some values no longer defined in winnt.h
    $14D: Lines.Add('   Intel i860');
    $268: Lines.Add('   Motorola 68000');
    $290: Lines.Add('   PA RISC');
  else
    Lines.Add(' unknown machine type');
  end; { Case }
  Lines.Add(Format('  NumberOfSections: %d', [h.NumberOfSections]));
  Lines.Add(Format('  TimeDateStamp: %d', [h.TimeDateStamp]));
  dt := EncodeDate(1970, 1, 1) + h.Timedatestamp / SecsPerDay;
  Lines.Add(FormatDateTime('   c', dt));

  Lines.Add(Format('  PointerToSymbolTable: %d', [h.PointerToSymbolTable]));
  Lines.Add(Format('  NumberOfSymbols: %d', [h.NumberOfSymbols]));
  Lines.Add(Format('  SizeOfOptionalHeader: %d', [h.SizeOfOptionalHeader]));
  Lines.Add(Format('  Characteristics: %d', [h.Characteristics]));
  if (IMAGE_FILE_DLL and h.Characteristics) <> 0 then
    Lines.Add('   file is a DLL')
  else if (IMAGE_FILE_EXECUTABLE_IMAGE and h.Characteristics) <> 0 then
    Lines.Add('   file is a program');
  Lines.Add('');
end;

procedure DumpOptionalHeader(const h: IMAGE_OPTIONAL_HEADER; var Lines: TStringList);
begin
  Lines.Add('  [Dump of PE optional file header]');
  Lines.Add('        -------------');
  Lines.Add(Format('  Magic (File Signature): %d', [h.Magic]));
  case h.Magic of
    $107: Lines.Add('   ROM image');
    $10B: Lines.Add('   executable image');
  else
    Lines.Add('   unknown image type');
  end; { If }
  Lines.Add(Format('  MajorLinkerVersion: %d', [h.MajorLinkerVersion]));
  Lines.Add(Format('  MinorLinkerVersion: %d', [h.MinorLinkerVersion]));
  Lines.Add(Format('  SizeOfCode: %d', [h.SizeOfCode]));
  Lines.Add(Format('  SizeOfInitializedData: %d', [h.SizeOfInitializedData]));
  Lines.Add(Format('  SizeOfUninitializedData: %d', [h.SizeOfUninitializedData]));
  Lines.Add(Format('  AddressOfEntryPoint: %d', [h.AddressOfEntryPoint]));
  Lines.Add(Format('  BaseOfCode: %d', [h.BaseOfCode]));
  Lines.Add(Format('  BaseOfData: %d', [h.BaseOfData]));
  Lines.Add(Format('  ImageBase: %d', [h.ImageBase]));
  Lines.Add(Format('  SectionAlignment: %d', [h.SectionAlignment]));
  Lines.Add(Format('  FileAlignment: %d', [h.FileAlignment]));
  Lines.Add(Format('  MajorOperatingSystemVersion: %d', [h.MajorOperatingSystemVersion]));
  Lines.Add(Format('  MinorOperatingSystemVersion: %d', [h.MinorOperatingSystemVersion]));
  Lines.Add(Format('  MajorImageVersion: %d', [h.MajorImageVersion]));
  Lines.Add(Format('  MinorImageVersion: %d', [h.MinorImageVersion]));
  Lines.Add(Format('  MajorSubsystemVersion: %d', [h.MajorSubsystemVersion]));
  Lines.Add(Format('  MinorSubsystemVersion: %d', [h.MinorSubsystemVersion]));
  Lines.Add(Format('  Win32VersionValue: %d', [h.Win32VersionValue]));
  Lines.Add(Format('  SizeOfImage: %d', [h.SizeOfImage]));
  Lines.Add(Format('  SizeOfHeaders: %d', [h.SizeOfHeaders]));
  Lines.Add(Format('  CheckSum: %d', [h.CheckSum]));
  Lines.Add(Format('  Subsystem: %d', [h.Subsystem]));
  case h.Subsystem of
    IMAGE_SUBSYSTEM_NATIVE:
      Lines.Add('   Image doesn''t require a subsystem. ');
    IMAGE_SUBSYSTEM_WINDOWS_GUI:
      Lines.Add('   Image runs in the Windows GUI subsystem. ');
    IMAGE_SUBSYSTEM_WINDOWS_CUI:
      Lines.Add('   Image runs in the Windows character subsystem. ');
    IMAGE_SUBSYSTEM_OS2_CUI:
      Lines.Add('   image runs in the OS/2 character subsystem. ');
    IMAGE_SUBSYSTEM_POSIX_CUI:
      Lines.Add('   image run in the Posix character subsystem. ');
  else
    Lines.Add(' unknown subsystem')
  end; { Case }
  Lines.Add(Format('  DllCharacteristics: %d', [h.DllCharacteristics]));
  Lines.Add(Format('  SizeOfStackReserve: %d', [h.SizeOfStackReserve]));
  Lines.Add(Format('  SizeOfStackCommit: %d', [h.SizeOfStackCommit]));
  Lines.Add(Format('  SizeOfHeapReserve: %d', [h.SizeOfHeapReserve]));
  Lines.Add(Format('  SizeOfHeapCommit: %d', [h.SizeOfHeapCommit]));
  Lines.Add(Format('  LoaderFlags: %d', [h.LoaderFlags]));
  Lines.Add(Format('  NumberOfRvaAndSizes: %d', [h.NumberOfRvaAndSizes]));
end;

var
  fs: TFilestream;
  signature: DWORD;
  dos_header: IMAGE_DOS_HEADER;
  pe_header: IMAGE_FILE_HEADER;
  opt_header: IMAGE_OPTIONAL_HEADER;
  slInfo: TStringList;
  sCurInfo: string;
  iCurFileCount: integer;
  FileName: string;
begin
  try
    if CmdLineHelper.GetParamCount < 1 then
    begin
      Writeln('You must enter the path to a file to analyze');
      Exit;
    end;

    for iCurFileCount := 1 to CmdLineHelper.GetParamCount do
    begin
      FileName := CmdLineHelper.GetParamStr(iCurFileCount);

      if not FileExists(FileName) then
      begin
        Writeln(#13#10#13#10 + '*** Error: <' + FileName + '> does not exist!');
        Continue;
      end;

      Writeln(#13#10 + '========================================');
      Writeln('=  Analyzing: ' + FileName);
      Writeln('========================================');
      fs := TFilestream.Create(FileName, fmOpenread or fmShareDenyNone);
      slInfo := TStringList.Create;
      try
        fs.read(dos_header, SizeOf(dos_header));
        if dos_header.e_magic <> IMAGE_DOS_SIGNATURE then
        begin
          WriteLn(#13#10#13#10 + '*** Error: Invalid DOS file header');
          Continue;
        end;

        DumpDOSHeader(dos_header, slInfo);

        fs.seek(dos_header._lfanew, soFromBeginning);
        fs.read(signature, SizeOf(signature));
        if signature <> IMAGE_NT_SIGNATURE then
        begin
          WriteLn(#13#10#13#10 + '*** Error: Invalid PE header');
          Continue;
        end;

        fs.read(pe_header, SizeOf(pe_header));
        DumpPEHeader(pe_header, slInfo);

        if pe_header.SizeOfOptionalHeader > 0 then
        begin
          fs.read(opt_header, SizeOf(opt_header));
          DumpOptionalHeader(opt_header, slInfo);
        end;

        if slInfo.Count > 0 then
        begin
          for sCurInfo in slInfo do
          begin
            Writeln(sCurInfo);
          end;
        end
        else
        begin
          Writeln('No information was retrieved from this file');
        end;
      finally
        fs.Free;
        FreeAndNil(slInfo);
      end; { finally }
    end;
  except
    on E: Exception do
    begin
      WriteLn(#13#10#13#10 + '*** Error: ' + E.Message);
    end;
  end;
end.
Advertisements
Categories: Delphi, Disassembly, Tech
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s