delphi3000.com - the free delphi knowledge platform
delphi3000.com - the free delphi knowledge platform
494 Users Online NOW
Have a look at your member-status

connecting people's knowledge


  - Recent ArticlesRSS feed for Recent Articles on delphi3000.com
  - List of All Articles
  - Top Viewed Articles
  - Articles (+Attachem.)
  - Articles Of Interest
  - Categories
  - Top Uploader
  - Search
  - Index

  - My Home
  - Submit an Article
  - My Articles
  - My Personal Data
  - My Bookmarks
  - Activities
  - Login/Logout

  - Sign Up
  - Why Sign Up
  - Newsletter

  - Press
  - Advertise

  - Contact
  - Feedback





Community
Borland
ClubeDelphi
Dr. Bob
UK-BUG
Delphi Meetings
Planeta Delphi



Loremo - the 1.5 liter car coming in 2009




Startblatt.de






Share this article with friendsShare this article with friends
Rate this articleRate this article - to keep the quality of delphi3000.com !
Comment this article or read through previous comments (2)


Check for exe files and DLLsComponent available for this articleFormat this article printer-friendly!Bookmark function is only available for registered users!
How to detect the types of executable files and DLLs
Product:
Delphi 3.x (or higher)
Category:
System
Skill Level:
Scoring:
Last Update:
02/22/2004
Search Keys:
delphi delphi3000 article borland vcl code-snippet executable program NE PE PE-file-format NE-file-format DLL DOS exe-file file-type IMAGE_DOS_HEADER IMAGE_FILE_HEADER TImageDosHeader TImageFileHeader
Times Scored:
4
Visits:
4858
Uploader: Peter Johnson
Company:
Reference: delphidabbler.com
Component Download: http://www.delphidabbler.com/download.php?file=exetypedemo.zip
 
Question/Problem/Abstract:
This article looks at how we examine a file to check if it is a DOS or Windows executable and, if so, whether it is a program file or a DLL.
Answer:



Abstract


In article #3496 "Getting an exe file type", Lutfi Baran showed us how to find out if a file is a 16 or 32 bit Windows or a DOS executable. But what if we need to know if the file is an application or a DLL?


This articles adds to Lutfi's work by adding the ability to check for DLLs. Since this code was developed independently of the earlier article, any errors are mine!


Thanks to to Flurin Honegger (see comment below) for suggesting some of the "reasonableness" checks on the DOS header to verify a valid MS-DOS file that are included in this revised article.


This is an abbreviated version of the original article,
published on my website.



Outline Design


Before we start coding, let's look at how we're going to accomplish this task. Our approach will be to scan through the file, looking for markers to indicate its file type. We use the following information:



  • All DOS program files (and therefore Windows executables)
    begin with a header record whose first element is a "magic number"; the word
    value $5A4D ("MZ" in ASCII).

  • The DOS header defines the expected length of the file and the offset of
    a "relocation table". We can check the length of the file being checked is greater than or equal to the expected length and that the offset of the DOS relocation table lies within the file.

  • Windows executables have a header record whose offset in the file is given by the LongWord at offset $3C.

  • The Windows header begins with the "magic number" $454E (NE file format - 16bit) or $4550 (PE file format - 32bit).

  • PE executables have an "image header" immediately following
    the $4550 magic number. This header structure has a Characteristics field which is a bit mask If the bit mask contains the flag IMAGE_FILE_DLL then the file is a DLL.

  • NE executables have a byte sized field at offset $0D from the start of the header which is a bit mask that contains the flag $80 when the file is a DLL.



Coding the Function


Our function will return a value that indicates the type of file whose name is passed to it as a parameter. The type of the return value is defined as:


type

  TExeFileKind = (
    fkUnknown,  // unknown file kind: not an executable
    fkError,    // error file kind: used for files that don't exist
    fkDOS,      // DOS executable
    fkExe32,    // 32 bit executable
    fkExe16,    // 16 bit executable
    fkDLL32,    // 32 bit DLL
    fkDLL16     // 16 bit DLL
  );

The implementation of the function requires structures for the PE and DOS file headers. The PE file header (type IMAGE_FILE_HEADER) is defined in the Windows unit. The DOS file header is not defined there, so we need to defined it as follows (copied from the Delphi Resxplor demo program):


type

  IMAGE_DOS_HEADER = packed record // DOS .exe header
    e_magic   : Word;                         // Magic number ("MZ")
    e_cblp    : Word;                         // Bytes on last page of file
    e_cp      : Word;                         // Pages in file
    e_crlc    : Word;                         // Relocations
    e_cparhdr : Word;                         // Size of header in paragraphs
    e_minalloc: Word;                         // Minimum extra paragraphs needed
    e_maxalloc: Word;                         // Maximum extra paragraphs needed
    e_ss      : Word;                         // Initial (relative) SS value
    e_sp      : Word;                         // Initial SP value
    e_csum    : Word;                         // Checksum
    e_ip      : Word;                         // Initial IP value
    e_cs      : Word;                         // Initial (relative) CS value
    e_lfarlc  : Word;                         // Address of relocation table
    e_ovno    : Word;                         // Overlay number
    e_res     : packed array [0..3] of Word;  // Reserved words
    e_oemid   : Word;                         // OEM identifier (for e_oeminfo)
    e_oeminfo : Word;                         // OEM info; e_oemid specific
    e_res2    : packed array [0..9] of Word;  // Reserved words
    e_lfanew  : Longint;                      // File address of new exe header
  end;

We are now ready to code the function:


function ExeType(const FileName: string): TExeFileKind;

  {Examines given file and returns a code that indicates the type of executable
  file it is (or if it isn't an executable)}

const
  cDOSRelocOffset = $18;  // offset of "pointer" to DOS relocation table
  cWinHeaderOffset = $3C; // offset of "pointer" to windows header in file
  cNEAppTypeOffset = $0D; // offset in NE windows header of app type field
  cDOSMagic = $5A4D;      // magic number identifying a DOS executable
  cNEMagic = $454E;       // magic number identifying a NE executable (Win 16)
  cPEMagic = $4550;       // magic nunber identifying a PE executable (Win 32)
  cNEDLLFlag = $80        // flag in NE app type field indicating a DLL
var
  FS: TFileStream;              // stream to executable file
  WinMagic: Word;               // word that contains PE or NE magic numbers
  HdrOffset: LongInt;           // offset of windows header in exec file
  ImgHdrPE: IMAGE_FILE_HEADER;  // PE file header record
  DOSHeader: IMAGE_DOS_HEADER;  // DOS header
  AppFlagsNE: Byte;             // byte defining DLLs in NE format
  DOSFileSize: Integer;         // size of DOS file
begin
  try
    // Open stream onto file: raises exception if can't be read
    FS := TFileStream.Create(FileName, fmOpenRead + fmShareDenyNone);
    try
      // Assume unkown file
      Result := fkUnknown;
      // Any exec file is at least size of DOS header long
      if FS.Size < SizeOf(DOSHeader) then
        Exit;
      FS.ReadBuffer(DOSHeader, SizeOf(DOSHeader));
      // DOS files begin with "MZ"
      if DOSHeader.e_magic <> cDOSMagic then
        Exit;
      // DOS files have length >= size indicated at offset $02 and $04
      // (offset $02 indicates length of file mod 512 and offset $04 indicates
      // no. of 512 pages in file)

      if (DOSHeader.e_cblp = 0) then
        DOSFileSize := DOSHeader.e_cp * 512
      else
        DOSFileSize := (DOSHeader.e_cp - 1) * 512 + DOSHeader.e_cblp;
      DOSFileSize := (DOSHeader.e_cp - 1) * 512 + DOSHeader.e_cblp;
      if FS.Size <  DOSFileSize then
        Exit;
      // DOS file relocation offset must be within DOS file size.
      if DOSHeader.e_lfarlc > DOSFileSize then
        Exit;
      // We assume we have an executable file: assume its a DOS program
      Result := fkDOS;
      // Try to find offset of Windows program header
      if FS.Size <= cWinHeaderOffset + SizeOf(LongInt) then
        // file too small for windows header "pointer": it's a DOS file
        Exit;
      // read it
      FS.Position := cWinHeaderOffset;
      FS.ReadBuffer(HdrOffset, SizeOf(LongInt));
      // Now try to read first word of Windows program header
      if FS.Size <= HdrOffset + SizeOf(Word) then
        // file too small to contain header: it's a DOS file
        Exit;
      FS.Position := HdrOffset;
      // This word should identify either a NE or PE format file: check which
      FS.ReadBuffer(WinMagic, SizeOf(Word));
      case WinMagic of
        cPEMagic:
        begin
          // 32 bit Windows application: now check whether app or DLL
          if FS.Size < HdrOffset + SizeOf(LongWord) + SizeOf(ImgHdrPE) then
            // file not large enough for image header: assume DOS
            Exit;
          // read Windows image header
          FS.Position := HdrOffset + SizeOf(LongWord);
          FS.ReadBuffer(ImgHdrPE, SizeOf(ImgHdrPE));
          if (ImgHdrPE.Characteristics and IMAGE_FILE_DLL) = IMAGE_FILE_DLL then
            // characteristics indicate a 32 bit DLL
            Result := fkDLL32
          else
            // characteristics indicate a 32 bit application
            Result := fkExe32;
        end;
        cNEMagic:
        begin
          // We have 16 bit Windows executable: check whether app or DLL
          if FS.Size <= HdrOffset + cNEAppTypeOffset + SizeOf(AppFlagsNE) then
            // app flags field would be beyond EOF: assume DOS
            Exit;
          // read app flags byte
          FS.Position := HdrOffset + cNEAppTypeOffset;
          FS.ReadBuffer(AppFlagsNE, SizeOf(AppFlagsNE));
          if (AppFlagsNE and cNEDLLFlag) = cNEDLLFlag then
            // app flags indicate DLL
            Result := fkDLL16
          else
            // app flags indicate program
            Result := fkExe16;
        end;
        else
          // DOS application
          {Do nothing - DOS result already set};
      end;
    finally
      FS.Free;
    end;
  except
    // Exception raised in function => error result
    Result := fkError;
  end;
end;

Conclusion


So there we have it -- a function to return the file type of an executable file. If you have any suggestions then please contact me or leave a comment to this article.


Worked Example


You can download a worked example from my website that includes the ExeType function, along with a Delphi 4 project that exercises it.






Please rate this article!
Skill level:
BeginnerExpert

Useful:
No!Very!

Overall rating:
PoorExcellent



Comments to this article
Write a new comment
MZ header verification
    Flurin Honegger (Sep 2 2003 7:13PM)

By examining the structures

TImageDosHeader
TImageFileHeader
TImageNtHeaders

defined in the windows unit, you can make some plausability checks.

for example

e_cp,e_cblp:

The file size reported in the DosHeader ((e_cp-1)*512+e_cblp) must be smaller or equal to the file size

e_cparhdr:
  the header size (SizeOf(TImageDosHeader) in paragraphs (4)

e_lfrlc:
  the pointer to the dos relocations must be within the dos file size


If you parse the ImageFileHeaders you must arrive at a end point be smaller or equal to the file size. (Remark: You can always add some data to the end of an exe. The end point allows you to get the entry point to this data)

etc.

Best wishes

Flurin

Respond

RE: MZ header verification
Peter Johnson (Sep 10 2003 12:22AM)

Thanks for this info - I've updated the article and revised the function to check the DOS header. I've used the file size reported by the e_cp and e_cblp fields and the pointer to DOS relocations e_lfrlc as suggested to check the file size.

I've checked the size returned by e_cparhdr in different programs and, while it always seems to be 4 for PE executables, several NE files and MSDOS apps return a different size (32 or 33 for some DOS programs), so I've not used this in checks.

Thanks again for the info.


Respond














 
Sign up to consume product discounts for Bronze memberships !

read more


  Visit our Sponsor

 

  Community Ad of
D. Wischnewski
 
   














 







     
  Copyright © 2000 - 2007 delphi3000.com - All rights reserved. Terms of use. || Privacy
delphi3000.com is a service by bluestep.com IT-Services GmbH (Vienna)