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:

VB Code:
  1. Private Type IMAGE_DOS_HEADER
  2.     e_magic As Integer   ''\\ Magic number
  3.     e_cblp As Integer    ''\\ Bytes on last page of file
  4.     e_cp As Integer      ''\\ Pages in file
  5.     e_crlc As Integer    ''\\ Relocations
  6.     e_cparhdr As Integer ''\\ Size of header in paragraphs
  7.     e_minalloc As Integer ''\\ Minimum extra paragraphs needed
  8.     e_maxalloc As Integer ''\\ Maximum extra paragraphs needed
  9.     e_ss As Integer    ''\\ Initial (relative) SS value
  10.     e_sp As Integer    ''\\ Initial SP value
  11.     e_csum As Integer  ''\\ Checksum
  12.     e_ip As Integer  ''\\ Initial IP value
  13.     e_cs As Integer  ''\\ Initial (relative) CS value
  14.     e_lfarlc As Integer ''\\ File address of relocation table
  15.     e_ovno As Integer ''\\ Overlay number
  16.     e_res(0 To 3) As Integer ''\\ Reserved words
  17.     e_oemid As Integer ''\\ OEM identifier (for e_oeminfo)
  18.     e_oeminfo As Integer ''\\ OEM information; e_oemid specific
  19.     e_res2(0 To 9) As Integer ''\\ Reserved words
  20.     e_lfanew As Long ''\\ File address of new exe header
  21. 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:

VB Code:
  1. Private Sub SkipDOSStub(ByVal hfile As Long)
  2.  
  3. Dim BytesRead As Long
  4.  
  5. '\\ Go to start of file...
  6. Call SetFilePointer(hfile, 0, 0, FILE_BEGIN)
  7. If Err.LastDllError Then
  8.     Debug.Print LastSystemError
  9. End If
  10.  
  11. Dim stub As IMAGE_DOS_HEADER
  12. Call ReadFileLong(hfile, VarPtr(stub), Len(stub), BytesRead, ByVal 0&)
  13. Call SetFilePointer(hfile, stub.e_lfanew, 0, FILE_BEGIN)
  14.  
  15. 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:

VB Code:
  1. Public Enum ImageSignatureTypes
  2.     IMAGE_DOS_SIGNATURE = &H5A4D     ''\\ MZ
  3.     IMAGE_OS2_SIGNATURE = &H454E     ''\\ NE
  4.     IMAGE_OS2_SIGNATURE_LE = &H454C  ''\\ LE
  5.     IMAGE_VXD_SIGNATURE = &H454C     ''\\ LE
  6.     IMAGE_NT_SIGNATURE = &H4550      ''\\ PE00
  7. 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:

VB Code:
  1. Private Type IMAGE_FILE_HEADER
  2.     Machine As Integer
  3.     NumberOfSections As Integer
  4.     TimeDateStamp As Long
  5.     PointerToSymbolTable As Long
  6.     NumberOfSymbols As Long
  7.     SizeOfOptionalHeader As Integer
  8.     Characteristics As Integer
  9. End Type

The Machine member describes what target CPU the executable was compiled for. It can be one of:

VB Code:
  1. Public Enum ImageMachineTypes
  2.     IMAGE_FILE_MACHINE_I386 = &H14C   ''\\ Intel 386.
  3.     IMAGE_FILE_MACHINE_R3000 = &H162  ''\\ MIPS little-endian,= &H160 big-endian
  4.     IMAGE_FILE_MACHINE_R4000 = &H166  ''\\ MIPS little-endian
  5.     IMAGE_FILE_MACHINE_R10000 = &H168  ''\\ MIPS little-endian
  6.     IMAGE_FILE_MACHINE_WCEMIPSV2 = &H169  ''\\ MIPS little-endian WCE v2
  7.     IMAGE_FILE_MACHINE_ALPHA = &H184      ''\\ Alpha_AXP
  8.     IMAGE_FILE_MACHINE_POWERPC = &H1F0    ''\\ IBM PowerPC Little-Endian
  9.     IMAGE_FILE_MACHINE_SH3 = &H1A2   ''\\ SH3 little-endian
  10.     IMAGE_FILE_MACHINE_SH3E = &H1A4  ''\\ SH3E little-endian
  11.     IMAGE_FILE_MACHINE_SH4 = &H1A6   ''\\ SH4 little-endian
  12.     IMAGE_FILE_MACHINE_ARM = &H1C0   ''\\ ARM Little-Endian
  13.     IMAGE_FILE_MACHINE_IA64 = &b00  ''\\ Intel 64
  14. 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:

VB Code:
  1. Private Type IMAGE_OPTIONAL_HEADER
  2.     Magic As Integer
  3.     MajorLinkerVersion As Byte
  4.     MinorLinkerVersion As Byte
  5.     SizeOfCode As Long
  6.     SizeOfInitializedData As Long
  7.     SizeOfUninitializedData As Long
  8.     AddressOfEntryPoint As Long
  9.     BaseOfCode As Long
  10.     BaseOfData As Long
  11. End Type

and this in turn is immediately followed by the IMAGE_OPTIONAL_HEADER_NT structure:

VB Code:
  1. Private Type IMAGE_OPTIONAL_HEADER_NT
  2.     ImageBase As Long
  3.     SectionAlignment As Long
  4.     FileAlignment As Long
  5.     MajorOperatingSystemVersion As Integer
  6.     MinorOperatingSystemVersion As Integer
  7.     MajorImageVersion As Integer
  8.     MinorImageVersion As Integer
  9.     MajorSubsystemVersion As Integer
  10.     MinorSubsystemVersion As Integer
  11.     Win32VersionValue As Long
  12.     SizeOfImage As Long
  13.     SizeOfHeaders As Long
  14.     CheckSum As Long
  15.     Subsystem As Integer
  16.     DllCharacteristics As Integer
  17.     SizeOfStackReserve As Long
  18.     SizeOfStackCommit As Long
  19.     SizeOfHeapReserve As Long
  20.     SizeOfHeapCommit As Long
  21.     LoaderFlags As Long
  22.     NumberOfRvaAndSizes As Long
  23.     DataDirectory(0 To 15) As IMAGE_DATA_DIRECTORY
  24. 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:

VB Code:
  1. Private Type IMAGE_DATA_DIRECTORY
  2.     VirtualAddress As Long
  3.     Size As Long
  4. End Type

And the directories are held in order thus:

VB Code:
  1. Public Enum ImageDataDirectoryIndexes
  2.     IMAGE_DIRECTORY_ENTRY_EXPORT = 0  ''\\ Export Directory
  3.     IMAGE_DIRECTORY_ENTRY_IMPORT = 1  ''\\ Import Directory
  4.     IMAGE_DIRECTORY_ENTRY_RESOURCE = 2 ''\\ Resource Directory
  5.     IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3   ''\\ Exception Directory
  6.     IMAGE_DIRECTORY_ENTRY_SECURITY = 4   ''\\ Security Directory
  7.     IMAGE_DIRECTORY_ENTRY_BASERELOC = 5  ''\\ Base Relocation Table
  8.     IMAGE_DIRECTORY_ENTRY_DEBUG = 6   ''\\ Debug Directory
  9.     IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7   ''\\ Architecture Specific Data
  10.     IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8  ''\\ RVA of GP
  11.     IMAGE_DIRECTORY_ENTRY_TLS = 9  ''\\ TLS Directory
  12.     IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10    ''\\ Load Configuration Directory
  13.     IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11   ''\\ Bound Import Directory in headers
  14.     IMAGE_DIRECTORY_ENTRY_IAT = 12  ''\\ Import Address Table
  15.     IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13   ''\\ Delay Load Import Descriptors
  16. 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 image data directories