Skip to content

Instantly share code, notes, and snippets.

@pa-0
Forked from delphidabbler/UExeType.pas
Created November 4, 2024 11:00
Show Gist options
  • Select an option

  • Save pa-0/051539782b76dc59f79dc73ed8887e40 to your computer and use it in GitHub Desktop.

Select an option

Save pa-0/051539782b76dc59f79dc73ed8887e40 to your computer and use it in GitHub Desktop.
Delphi Pascal function and type that can detect if a file is a DOS or Windows executable and determine its type.
// MIT License https://delphidabbler.mit-license.org/2022-
// Copyright (c) 2022, Peter D Johnson (https://gravatar.com/delphidabbler)
unit UExeType;
interface
type
TExeType = (
etUnknown, // unknown file kind: not an executable
etError, // error file kind: used for files that don't exist
etDOS, // DOS executable
etExe32, // 32 bit executable
etExe16, // 16 bit executable
etDLL32, // 32 bit DLL
etDLL16, // 16 bit DLL
etVXD, // virtual device driver
etExe64, // 64 bit executable
etDLL64, // 64 bit DLL
etROM // ROM image (PE format)
);
function ExeType(const FileName: string): TExeType;
implementation
uses
{$IF CompilerVersion >= 23.0} // Delphi XE2 and later
WinApi.Windows, System.SysUtils, System.Classes;
{$ELSE}
Windows, SysUtils, Classes,
{$ENDIF}
function ExeType(const FileName: string): TExeType;
const
cWinHeaderOffset = $3C; // offset of "pointer" to windows header in file
cNEAppTypeOffset = $0D; // offset in NE windows header app type field
cDOSMagic = $5A4D; // magic number of a DOS executable
cNEMagic = $454E; // magic number of a NE executable (Win 16)
cPEMagic = $4550; // magic nunber of a PE/PE+ executable (Win 32/64)
cLEMagic = $454C; // magic number of a Virtual Device Driver
cNEDLLFlag = $80; // flag in NE app type field indicating a DLL
cPEDLLFlag = $2000; // flag in PE Characteristics field indicating s DLL
cPE32Magic = $10B; // magic number of a 32 bit PE executable
cPE64Magic = $20B; // magic number of a 64 bit executable
cPEROMMagic = $107; // magic number of a ROM image
var
FS: TFileStream; // stream onto executable file
WinMagic: Word; // word that contains PE/NE/LE magic #
WinMagicEx: Word; // additional magic # for PE exec file
HdrOffset: LongWord; // offset of windows header in exec file
DOSHeader: IMAGE_DOS_HEADER; // DOS header record
PEFileHdr: IMAGE_FILE_HEADER; // PE file header record
PEOptHdrMagic: Word; // PE "optional" header magic #
AppFlagsNE: Byte; // byte defining DLLs in NE format
DOSFileSize: Integer; // size of DOS file
IsPEDLL: Boolean; // whether PE file is DLL
begin
try
// Open stream onto file: raises exception if can't be read
FS := TFileStream.Create(FileName, fmOpenRead + fmShareDenyNone);
try
// Assume unkown file type
Result := etUnknown;
// Any exec file is at least size of DOS header long
if FS.Read(DOSHeader, SizeOf(DOSHeader)) < SizeOf(DOSHeader) then
Exit;
// DOS files begin with "MZ"
if DOSHeader.e_magic <> cDOSMagic then
Exit;
// DOS files have length >= size indicated at by DOS header's
// e_cblp and e_cp fields. e_cblp stores the number of 512 byte
// pages in the file. e_cp stores the number of bytes used in the
// last page of the file.
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 know we have an executable file: assume its a DOS program
Result := etDOS;
// Try to find offset of Windows program header
if FS.Size <= cWinHeaderOffset + SizeOf(LongWord) 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 be NE, PE or LE per file type: check which
FS.ReadBuffer(WinMagic, SizeOf(Word));
case WinMagic of
cPEMagic:
begin
// 'PE' signature is followed by a zero word
if FS.Read(WinMagicEx, SizeOf(WinMagicEx)) < SizeOf(WinMagicEx) then
Exit;
if WinMagicEx <> 0 then
Exit;
// 32 or 64 bit Windows application: now check whether app or DLL
// by reading file header record and checking Characteristics field
if FS.Read(PEFileHdr, SizeOf(PEFileHdr)) < SizeOf(PEFileHdr) then
Exit;
IsPEDLL := (PEFileHdr.Characteristics and cPEDLLFlag)
= cPEDLLFlag;
// PE & PE+ format file must have "optional" header whose size is
// given by file header record's SizeOfOptionalHeader field, which
// must be non-zero & file must be large enough to accommodate it
if PEFileHdr.SizeOfOptionalHeader = 0 then
Exit;
if FS.Size < HdrOffset + SizeOf(WinMagic) + SizeOf(WinMagicEx)
+ PEFileHdr.SizeOfOptionalHeader then
Exit;
// check if 32 bit, 64 bit (or ROM) by reading Word value following
// file header (actually this is first field of "optional" PE header)
// read magic number at start of "optional" PE header that follows
if FS.Read(PEOptHdrMagic, SizeOf(PEOptHdrMagic))
< SizeOf(PEOptHdrMagic) then
Exit;
case PEOptHdrMagic of
cPE32Magic:
if IsPEDLL then
Result := etDLL32
else
Result := etExe32;
cPE64Magic:
if IsPEDLL then
Result := etDLL64
else
Result := etExe64;
cPEROMMagic:
Result := etROM;
else
Result := etUnknown; // unknown PE magic number
end;
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 := etDLL16
else
// app flags indicate program
Result := etExe16;
end;
cLEMagic:
// We have a Virtual Device Driver
Result := etVXD;
else
// DOS application
{Do nothing - DOS result already set};
end;
finally
FS.Free;
end;
except
// Exception raised in function => error result
Result := etError;
end;
end;
end.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment