Merrion
05-07-2003, 04:37 PM
Introduction
The Portable Executable Format is the data structure that describes how the various parts of a Win32
executable file are held together. It allows the operating system to load the executable and to locate the dynamically
linked libraries required to run that executable and to navigate the code,data and resource sections compiled into that
executable.
Getting over DOS
The PE Format was created for Windows but Microsoft had to make sure that running such an executable in DOS would
yield a meaningful error message and exit. To this end the very first bit of a windows executable file is actually a DOS
executable (sometimes known as the stub) which writes "This program requires Windows" or similar then exits.
The format of the DOS stub is:
Private Type IMAGE_DOS_HEADER
e_magic As Integer ''\\ Magic number
e_cblp As Integer ''\\ Bytes on last page of file
e_cp As Integer ''\\ Pages in file
e_crlc As Integer ''\\ Relocations
e_cparhdr As Integer ''\\ Size of header in paragraphs
e_minalloc As Integer ''\\ Minimum extra paragraphs needed
e_maxalloc As Integer ''\\ Maximum extra paragraphs needed
e_ss As Integer ''\\ Initial (relative) SS value
e_sp As Integer ''\\ Initial SP value
e_csum As Integer ''\\ Checksum
e_ip As Integer ''\\ Initial IP value
e_cs As Integer ''\\ Initial (relative) CS value
e_lfarlc As Integer ''\\ File address of relocation table
e_ovno As Integer ''\\ Overlay number
e_res(0 To 3) As Integer ''\\ Reserved words
e_oemid As Integer ''\\ OEM identifier (for e_oeminfo)
e_oeminfo As Integer ''\\ OEM information; e_oemid specific
e_res2(0 To 9) As Integer ''\\ Reserved words
e_lfanew As Long ''\\ File address of new exe header
End Type
The only field of this structure that is of interest to Windows is e_lfanew which is the file pointer to the new
Windows executable header. To skip over the DOS part of the program, set the file pointer to the value held in this field:
Private Sub SkipDOSStub(ByVal hfile As Long)
Dim BytesRead As Long
'\\ Go to start of file...
Call SetFilePointer(hfile, 0, 0, FILE_BEGIN)
If Err.LastDllError Then
Debug.Print LastSystemError
End If
Dim stub As IMAGE_DOS_HEADER
Call ReadFileLong(hfile, VarPtr(stub), Len(stub), BytesRead, ByVal 0&)
Call SetFilePointer(hfile, stub.e_lfanew, 0, FILE_BEGIN)
End Sub
The NT header
The NT header holds the information needed by the windows program loader to load the program. It consists of the PE File signature
followed by an IMAGE_FILE_HEADER and IMAGE_OPTIONAL_HEADER records.
For applications designed to run under Windows (i.e. not OS/2 or VxD files) the four bytes of the PE File signature should equal &h4550.
The other defined signatures are:
Public Enum ImageSignatureTypes
IMAGE_DOS_SIGNATURE = &H5A4D ''\\ MZ
IMAGE_OS2_SIGNATURE = &H454E ''\\ NE
IMAGE_OS2_SIGNATURE_LE = &H454C ''\\ LE
IMAGE_VXD_SIGNATURE = &H454C ''\\ LE
IMAGE_NT_SIGNATURE = &H4550 ''\\ PE00
End Enum
Following the PE file signature is the IMAGE_NT_HEADERS structure that stores information about the target environment of the executable.
The structure is:
Private Type IMAGE_FILE_HEADER
Machine As Integer
NumberOfSections As Integer
TimeDateStamp As Long
PointerToSymbolTable As Long
NumberOfSymbols As Long
SizeOfOptionalHeader As Integer
Characteristics As Integer
End Type
The Machine member describes what target CPU the executable was compiled for. It can be one of:
Public Enum ImageMachineTypes
IMAGE_FILE_MACHINE_I386 = &H14C ''\\ Intel 386.
IMAGE_FILE_MACHINE_R3000 = &H162 ''\\ MIPS little-endian,= &H160 big-endian
IMAGE_FILE_MACHINE_R4000 = &H166 ''\\ MIPS little-endian
IMAGE_FILE_MACHINE_R10000 = &H168 ''\\ MIPS little-endian
IMAGE_FILE_MACHINE_WCEMIPSV2 = &H169 ''\\ MIPS little-endian WCE v2
IMAGE_FILE_MACHINE_ALPHA = &H184 ''\\ Alpha_AXP
IMAGE_FILE_MACHINE_POWERPC = &H1F0 ''\\ IBM PowerPC Little-Endian
IMAGE_FILE_MACHINE_SH3 = &H1A2 ''\\ SH3 little-endian
IMAGE_FILE_MACHINE_SH3E = &H1A4 ''\\ SH3E little-endian
IMAGE_FILE_MACHINE_SH4 = &H1A6 ''\\ SH4 little-endian
IMAGE_FILE_MACHINE_ARM = &H1C0 ''\\ ARM Little-Endian
IMAGE_FILE_MACHINE_IA64 = &H200 ''\\ Intel 64
End Enum
The SizeOfOptionalHeader member indicates the size (in bytes) of the IMAGE_OPTIONAL_HEADER structure that immediatley follows it.
In practice this structure is not optional so that is a bit of a misnomer. This structure is defined as:
Private Type IMAGE_OPTIONAL_HEADER
Magic As Integer
MajorLinkerVersion As Byte
MinorLinkerVersion As Byte
SizeOfCode As Long
SizeOfInitializedData As Long
SizeOfUninitializedData As Long
AddressOfEntryPoint As Long
BaseOfCode As Long
BaseOfData As Long
End Type
and this in turn is immediately followed by the IMAGE_OPTIONAL_HEADER_NT structure:
Private Type IMAGE_OPTIONAL_HEADER_NT
ImageBase As Long
SectionAlignment As Long
FileAlignment As Long
MajorOperatingSystemVersion As Integer
MinorOperatingSystemVersion As Integer
MajorImageVersion As Integer
MinorImageVersion As Integer
MajorSubsystemVersion As Integer
MinorSubsystemVersion As Integer
Win32VersionValue As Long
SizeOfImage As Long
SizeOfHeaders As Long
CheckSum As Long
Subsystem As Integer
DllCharacteristics As Integer
SizeOfStackReserve As Long
SizeOfStackCommit As Long
SizeOfHeapReserve As Long
SizeOfHeapCommit As Long
LoaderFlags As Long
NumberOfRvaAndSizes As Long
DataDirectory(0 To 15) As IMAGE_DATA_DIRECTORY
End Type
The most useful field of this structure (to my purposes, anyhow) are the 16 IMAGE_DATA_DIRECTORY entries. These describe whereabouts
(if at all) the particular sections of the executable are located. The structure is defined thus:
Private Type IMAGE_DATA_DIRECTORY
VirtualAddress As Long
Size As Long
End Type
And the directories are held in order thus:
Public Enum ImageDataDirectoryIndexes
IMAGE_DIRECTORY_ENTRY_EXPORT = 0 ''\\ Export Directory
IMAGE_DIRECTORY_ENTRY_IMPORT = 1 ''\\ Import Directory
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 ''\\ Resource Directory
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 ''\\ Exception Directory
IMAGE_DIRECTORY_ENTRY_SECURITY = 4 ''\\ Security Directory
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 ''\\ Base Relocation Table
IMAGE_DIRECTORY_ENTRY_DEBUG = 6 ''\\ Debug Directory
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 ''\\ Architecture Specific Data
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 ''\\ RVA of GP
IMAGE_DIRECTORY_ENTRY_TLS = 9 ''\\ TLS Directory
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 ''\\ Load Configuration Directory
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 ''\\ Bound Import Directory in headers
IMAGE_DIRECTORY_ENTRY_IAT = 12 ''\\ Import Address Table
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 ''\\ Delay Load Import Descriptors
End Enum
Note that is an executable does not contain one of the sections (as is often the case) there will be an IMAGE_DATA_DIRECTORY for it but the
address and size will both be zero.
The Portable Executable Format is the data structure that describes how the various parts of a Win32
executable file are held together. It allows the operating system to load the executable and to locate the dynamically
linked libraries required to run that executable and to navigate the code,data and resource sections compiled into that
executable.
Getting over DOS
The PE Format was created for Windows but Microsoft had to make sure that running such an executable in DOS would
yield a meaningful error message and exit. To this end the very first bit of a windows executable file is actually a DOS
executable (sometimes known as the stub) which writes "This program requires Windows" or similar then exits.
The format of the DOS stub is:
Private Type IMAGE_DOS_HEADER
e_magic As Integer ''\\ Magic number
e_cblp As Integer ''\\ Bytes on last page of file
e_cp As Integer ''\\ Pages in file
e_crlc As Integer ''\\ Relocations
e_cparhdr As Integer ''\\ Size of header in paragraphs
e_minalloc As Integer ''\\ Minimum extra paragraphs needed
e_maxalloc As Integer ''\\ Maximum extra paragraphs needed
e_ss As Integer ''\\ Initial (relative) SS value
e_sp As Integer ''\\ Initial SP value
e_csum As Integer ''\\ Checksum
e_ip As Integer ''\\ Initial IP value
e_cs As Integer ''\\ Initial (relative) CS value
e_lfarlc As Integer ''\\ File address of relocation table
e_ovno As Integer ''\\ Overlay number
e_res(0 To 3) As Integer ''\\ Reserved words
e_oemid As Integer ''\\ OEM identifier (for e_oeminfo)
e_oeminfo As Integer ''\\ OEM information; e_oemid specific
e_res2(0 To 9) As Integer ''\\ Reserved words
e_lfanew As Long ''\\ File address of new exe header
End Type
The only field of this structure that is of interest to Windows is e_lfanew which is the file pointer to the new
Windows executable header. To skip over the DOS part of the program, set the file pointer to the value held in this field:
Private Sub SkipDOSStub(ByVal hfile As Long)
Dim BytesRead As Long
'\\ Go to start of file...
Call SetFilePointer(hfile, 0, 0, FILE_BEGIN)
If Err.LastDllError Then
Debug.Print LastSystemError
End If
Dim stub As IMAGE_DOS_HEADER
Call ReadFileLong(hfile, VarPtr(stub), Len(stub), BytesRead, ByVal 0&)
Call SetFilePointer(hfile, stub.e_lfanew, 0, FILE_BEGIN)
End Sub
The NT header
The NT header holds the information needed by the windows program loader to load the program. It consists of the PE File signature
followed by an IMAGE_FILE_HEADER and IMAGE_OPTIONAL_HEADER records.
For applications designed to run under Windows (i.e. not OS/2 or VxD files) the four bytes of the PE File signature should equal &h4550.
The other defined signatures are:
Public Enum ImageSignatureTypes
IMAGE_DOS_SIGNATURE = &H5A4D ''\\ MZ
IMAGE_OS2_SIGNATURE = &H454E ''\\ NE
IMAGE_OS2_SIGNATURE_LE = &H454C ''\\ LE
IMAGE_VXD_SIGNATURE = &H454C ''\\ LE
IMAGE_NT_SIGNATURE = &H4550 ''\\ PE00
End Enum
Following the PE file signature is the IMAGE_NT_HEADERS structure that stores information about the target environment of the executable.
The structure is:
Private Type IMAGE_FILE_HEADER
Machine As Integer
NumberOfSections As Integer
TimeDateStamp As Long
PointerToSymbolTable As Long
NumberOfSymbols As Long
SizeOfOptionalHeader As Integer
Characteristics As Integer
End Type
The Machine member describes what target CPU the executable was compiled for. It can be one of:
Public Enum ImageMachineTypes
IMAGE_FILE_MACHINE_I386 = &H14C ''\\ Intel 386.
IMAGE_FILE_MACHINE_R3000 = &H162 ''\\ MIPS little-endian,= &H160 big-endian
IMAGE_FILE_MACHINE_R4000 = &H166 ''\\ MIPS little-endian
IMAGE_FILE_MACHINE_R10000 = &H168 ''\\ MIPS little-endian
IMAGE_FILE_MACHINE_WCEMIPSV2 = &H169 ''\\ MIPS little-endian WCE v2
IMAGE_FILE_MACHINE_ALPHA = &H184 ''\\ Alpha_AXP
IMAGE_FILE_MACHINE_POWERPC = &H1F0 ''\\ IBM PowerPC Little-Endian
IMAGE_FILE_MACHINE_SH3 = &H1A2 ''\\ SH3 little-endian
IMAGE_FILE_MACHINE_SH3E = &H1A4 ''\\ SH3E little-endian
IMAGE_FILE_MACHINE_SH4 = &H1A6 ''\\ SH4 little-endian
IMAGE_FILE_MACHINE_ARM = &H1C0 ''\\ ARM Little-Endian
IMAGE_FILE_MACHINE_IA64 = &H200 ''\\ Intel 64
End Enum
The SizeOfOptionalHeader member indicates the size (in bytes) of the IMAGE_OPTIONAL_HEADER structure that immediatley follows it.
In practice this structure is not optional so that is a bit of a misnomer. This structure is defined as:
Private Type IMAGE_OPTIONAL_HEADER
Magic As Integer
MajorLinkerVersion As Byte
MinorLinkerVersion As Byte
SizeOfCode As Long
SizeOfInitializedData As Long
SizeOfUninitializedData As Long
AddressOfEntryPoint As Long
BaseOfCode As Long
BaseOfData As Long
End Type
and this in turn is immediately followed by the IMAGE_OPTIONAL_HEADER_NT structure:
Private Type IMAGE_OPTIONAL_HEADER_NT
ImageBase As Long
SectionAlignment As Long
FileAlignment As Long
MajorOperatingSystemVersion As Integer
MinorOperatingSystemVersion As Integer
MajorImageVersion As Integer
MinorImageVersion As Integer
MajorSubsystemVersion As Integer
MinorSubsystemVersion As Integer
Win32VersionValue As Long
SizeOfImage As Long
SizeOfHeaders As Long
CheckSum As Long
Subsystem As Integer
DllCharacteristics As Integer
SizeOfStackReserve As Long
SizeOfStackCommit As Long
SizeOfHeapReserve As Long
SizeOfHeapCommit As Long
LoaderFlags As Long
NumberOfRvaAndSizes As Long
DataDirectory(0 To 15) As IMAGE_DATA_DIRECTORY
End Type
The most useful field of this structure (to my purposes, anyhow) are the 16 IMAGE_DATA_DIRECTORY entries. These describe whereabouts
(if at all) the particular sections of the executable are located. The structure is defined thus:
Private Type IMAGE_DATA_DIRECTORY
VirtualAddress As Long
Size As Long
End Type
And the directories are held in order thus:
Public Enum ImageDataDirectoryIndexes
IMAGE_DIRECTORY_ENTRY_EXPORT = 0 ''\\ Export Directory
IMAGE_DIRECTORY_ENTRY_IMPORT = 1 ''\\ Import Directory
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 ''\\ Resource Directory
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3 ''\\ Exception Directory
IMAGE_DIRECTORY_ENTRY_SECURITY = 4 ''\\ Security Directory
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5 ''\\ Base Relocation Table
IMAGE_DIRECTORY_ENTRY_DEBUG = 6 ''\\ Debug Directory
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7 ''\\ Architecture Specific Data
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8 ''\\ RVA of GP
IMAGE_DIRECTORY_ENTRY_TLS = 9 ''\\ TLS Directory
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10 ''\\ Load Configuration Directory
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11 ''\\ Bound Import Directory in headers
IMAGE_DIRECTORY_ENTRY_IAT = 12 ''\\ Import Address Table
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13 ''\\ Delay Load Import Descriptors
End Enum
Note that is an executable does not contain one of the sections (as is often the case) there will be an IMAGE_DATA_DIRECTORY for it but the
address and size will both be zero.