Parsing PE Files in PowerShell

Using the header information from winnt.h in the Windows SDK we can parse Windows executable files. Due to use of enum this requires PowerShell v5. If using earlier versions of PowerShell those could be left out or use embedded C#.

Here is that information translated into PowerShell.

As PowerShell doesn’t have a built in mechanism for describing binary file structures as far as I know, I’m using an OrderedDictionary to describe the file content structure.

$filename = "C:\windows\explorer.exe"

[Uint16]$IMAGE_DOS_SIGNATURE = 0x5A4D
[Uint16]$IMAGE_NT_OPTIONAL_HDR32_MAGIC = 0x10b
[UInt16]$IMAGE_NT_OPTIONAL_HDR64_MAGIC = 0x20b
[UInt16]$IMAGE_ROM_OPTIONAL_HDR_MAGIC = 0x107
[Uint16]$IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16
[Uint16]$IMAGE_SIZEOF_SHORT_NAME = 8
[Uint16]$IMAGE_SIZEOF_SECTION_HEADER = 40

enum IMAGE_FILE_MACHINE {
    IMAGE_FILE_RELOCS_STRIPPED           = 0x0001  # Relocation info stripped from file.
    IMAGE_FILE_EXECUTABLE_IMAGE          = 0x0002  # File is executable  (i.e. no unresolved external references).
    IMAGE_FILE_LINE_NUMS_STRIPPED        = 0x0004  # Line nunbers stripped from file.
    IMAGE_FILE_LOCAL_SYMS_STRIPPED       = 0x0008  # Local symbols stripped from file.
    IMAGE_FILE_AGGRESIVE_WS_TRIM         = 0x0010  # Aggressively trim working set
    IMAGE_FILE_LARGE_ADDRESS_AWARE       = 0x0020  # App can handle >2gb addresses
    IMAGE_FILE_BYTES_REVERSED_LO         = 0x0080  # Bytes of machine word are reversed.
    IMAGE_FILE_32BIT_MACHINE             = 0x0100  # 32 bit word machine.
    IMAGE_FILE_DEBUG_STRIPPED            = 0x0200  # Debugging info stripped from file in .DBG file
    IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   = 0x0400  # If Image is on removable media, copy and run from the swap file.
    IMAGE_FILE_NET_RUN_FROM_SWAP         = 0x0800  # If Image is on Net, copy and run from the swap file.
    IMAGE_FILE_SYSTEM                    = 0x1000  # System File.
    IMAGE_FILE_DLL                       = 0x2000  # File is a DLL.
    IMAGE_FILE_UP_SYSTEM_ONLY            = 0x4000  # File should only be run on a UP machine
    IMAGE_FILE_BYTES_REVERSED_HI         = 0x8000  # Bytes of machine word are reversed.
    IMAGE_FILE_MACHINE_UNKNOWN           = 0x0
    IMAGE_FILE_MACHINE_TARGET_HOST       = 0x0001  # Useful for indicating we want to interact with the host and not a WoW guest.
    IMAGE_FILE_MACHINE_I386              = 0x014c  # Intel 386.
    IMAGE_FILE_MACHINE_R3000             = 0x0162  # MIPS little-endian, = 0x160 big-endian
    IMAGE_FILE_MACHINE_R4000             = 0x0166  # MIPS little-endian
    IMAGE_FILE_MACHINE_R10000            = 0x0168  # MIPS little-endian
    IMAGE_FILE_MACHINE_WCEMIPSV2         = 0x0169  # MIPS little-endian WCE v2
    IMAGE_FILE_MACHINE_ALPHA             = 0x0184  # Alpha_AXP
    IMAGE_FILE_MACHINE_SH3               = 0x01a2  # SH3 little-endian
    IMAGE_FILE_MACHINE_SH3DSP            = 0x01a3
    IMAGE_FILE_MACHINE_SH3E              = 0x01a4  # SH3E little-endian
    IMAGE_FILE_MACHINE_SH4               = 0x01a6  # SH4 little-endian
    IMAGE_FILE_MACHINE_SH5               = 0x01a8  # SH5
    IMAGE_FILE_MACHINE_ARM               = 0x01c0  # ARM Little-Endian
    IMAGE_FILE_MACHINE_THUMB             = 0x01c2  # ARM Thumb/Thumb-2 Little-Endian
    IMAGE_FILE_MACHINE_ARMNT             = 0x01c4  # ARM Thumb-2 Little-Endian
    IMAGE_FILE_MACHINE_AM33              = 0x01d3
    IMAGE_FILE_MACHINE_POWERPC           = 0x01F0  # IBM PowerPC Little-Endian
    IMAGE_FILE_MACHINE_POWERPCFP         = 0x01f1
    IMAGE_FILE_MACHINE_IA64              = 0x0200  # Intel 64
    IMAGE_FILE_MACHINE_MIPS16            = 0x0266  # MIPS
    IMAGE_FILE_MACHINE_ALPHA64           = 0x0284  # ALPHA64
    IMAGE_FILE_MACHINE_MIPSFPU           = 0x0366  # MIPS
    IMAGE_FILE_MACHINE_MIPSFPU16         = 0x0466  # MIPS
    IMAGE_FILE_MACHINE_AXP64             = 0x0284  # IMAGE_FILE_MACHINE_ALPHA64
    IMAGE_FILE_MACHINE_TRICORE           = 0x0520  # Infineon
    IMAGE_FILE_MACHINE_CEF               = 0x0CEF
    IMAGE_FILE_MACHINE_EBC               = 0x0EBC  # EFI Byte Code
    IMAGE_FILE_MACHINE_AMD64             = 0x8664  # AMD64 (K8)
    IMAGE_FILE_MACHINE_M32R              = 0x9041  # M32R little-endian
    IMAGE_FILE_MACHINE_ARM64             = 0xAA64  # ARM64 Little-Endian
    IMAGE_FILE_MACHINE_CEE               = 0xC0EE
}

[Flags()] enum IMAGE_SUBSYSTEM
{
    IMAGE_SUBSYSTEM_UNKNOWN              = 0   # Unknown subsystem.
    IMAGE_SUBSYSTEM_NATIVE               = 1   # Image doesnt require a subsystem.
    IMAGE_SUBSYSTEM_WINDOWS_GUI          = 2   # Image runs in the Windows GUI subsystem.
    IMAGE_SUBSYSTEM_WINDOWS_CUI          = 3   # Image runs in the Windows character subsystem.
    IMAGE_SUBSYSTEM_OS2_CUI              = 5   # image runs in the OS/2 character subsystem.
    IMAGE_SUBSYSTEM_POSIX_CUI            = 7   # image runs in the Posix character subsystem.
    IMAGE_SUBSYSTEM_NATIVE_WINDOWS       = 8   # image is a native Win9x driver.
    IMAGE_SUBSYSTEM_WINDOWS_CE_GUI       = 9   # Image runs in the Windows CE subsystem.
    IMAGE_SUBSYSTEM_EFI_APPLICATION      = 10  #
    IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11   #
    IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER   = 12  #
    IMAGE_SUBSYSTEM_EFI_ROM              = 13
    IMAGE_SUBSYSTEM_XBOX                 = 14
    IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16
    IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG    = 17
}

[Flags()] enum IMAGE_DLLCHARACTERISTICS {
    IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA = 0x0020  # Image can handle a high entropy 64-bit virtual address space.
    IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040     # DLL can move.
    IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY   = 0x0080     # Code Integrity Image
    IMAGE_DLLCHARACTERISTICS_NX_COMPAT    = 0x0100     # Image is NX compatible
    IMAGE_DLLCHARACTERISTICS_NO_ISOLATION = 0x0200     # Image understands isolation and doesn't want it
    IMAGE_DLLCHARACTERISTICS_NO_SEH       = 0x0400     # Image does not use SEH.  No SE handler may reside in this image
    IMAGE_DLLCHARACTERISTICS_NO_BIND      = 0x0800     # Do not bind this image.
    IMAGE_DLLCHARACTERISTICS_APPCONTAINER = 0x1000     # Image should execute in an AppContainer
    IMAGE_DLLCHARACTERISTICS_WDM_DRIVER   = 0x2000     # Driver uses WDM model
    IMAGE_DLLCHARACTERISTICS_GUARD_CF     = 0x4000     # Image supports Control Flow Guard.
    IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE     = 0x8000
}

enum IMAGE_DIRECTORY_ENTRY
{
    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_COPYRIGHT = 7   # (X86 usage)
    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
    IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14   # COM Runtime descriptor
}

[Flags()] enum IMAGE_SCN {
#       IMAGE_SCN_TYPE_REG                   0x00000000  #  Reserved.
#       IMAGE_SCN_TYPE_DSECT                 0x00000001  #  Reserved.
#       IMAGE_SCN_TYPE_NOLOAD                0x00000002  #  Reserved.
#       IMAGE_SCN_TYPE_GROUP                 0x00000004  #  Reserved.
 IMAGE_SCN_TYPE_NO_PAD               = 0x00000008  #  Reserved.
#       IMAGE_SCN_TYPE_COPY                  0x00000010  #  Reserved.

 IMAGE_SCN_CNT_CODE                  = 0x00000020  #  Section contains code.
 IMAGE_SCN_CNT_INITIALIZED_DATA      = 0x00000040  #  Section contains initialized data.
 IMAGE_SCN_CNT_UNINITIALIZED_DATA    = 0x00000080  #  Section contains uninitialized data.

 IMAGE_SCN_LNK_OTHER                =  0x00000100  #  Reserved.
 IMAGE_SCN_LNK_INFO                 = 0x00000200  #  Section contains comments or some other type of information.
#       IMAGE_SCN_TYPE_OVER                  0x00000400  #  Reserved.
 IMAGE_SCN_LNK_REMOVE                = 0x00000800  #  Section contents will not become part of image.
 IMAGE_SCN_LNK_COMDAT                = 0x00001000  #  Section contents comdat.
#                                            0x00002000  #  Reserved.
#       IMAGE_SCN_MEM_PROTECTED - Obsolete =  0x00004000
 IMAGE_SCN_NO_DEFER_SPEC_EXC         = 0x00004000  #  Reset speculative exceptions handling bits in the TLB entries for this section.
 IMAGE_SCN_GPREL                     = 0x00008000  #  Section content can be accessed relative to GP
 IMAGE_SCN_MEM_FARDATA               = 0x00008000
#       IMAGE_SCN_MEM_SYSHEAP  - Obsolete    0x00010000
 IMAGE_SCN_MEM_PURGEABLE             = 0x00020000
 IMAGE_SCN_MEM_16BIT                 = 0x00020000
 IMAGE_SCN_MEM_LOCKED                = 0x00040000
 IMAGE_SCN_MEM_PRELOAD              =  0x00080000

 IMAGE_SCN_ALIGN_1BYTES             =  0x00100000  # 
 IMAGE_SCN_ALIGN_2BYTES             =  0x00200000  # 
 IMAGE_SCN_ALIGN_4BYTES             =  0x00300000  # 
 IMAGE_SCN_ALIGN_8BYTES             =  0x00400000  # 
 IMAGE_SCN_ALIGN_16BYTES            =  0x00500000  #  Default alignment if no others are specified.
 IMAGE_SCN_ALIGN_32BYTES            =  0x00600000  # 
 IMAGE_SCN_ALIGN_64BYTES            =  0x00700000  # 
 IMAGE_SCN_ALIGN_128BYTES           =  0x00800000  # 
 IMAGE_SCN_ALIGN_256BYTES           =  0x00900000  # 
 IMAGE_SCN_ALIGN_512BYTES           =  0x00A00000  # 
 IMAGE_SCN_ALIGN_1024BYTES          =  0x00B00000  # 
 IMAGE_SCN_ALIGN_2048BYTES          =  0x00C00000  # 
 IMAGE_SCN_ALIGN_4096BYTES          =  0x00D00000  # 
 IMAGE_SCN_ALIGN_8192BYTES          =  0x00E00000  # 
#  Unused                                    0x00F00000
 IMAGE_SCN_ALIGN_MASK               =  0x00F00000

 IMAGE_SCN_LNK_NRELOC_OVFL          =  0x01000000  #  Section contains extended relocations.
 IMAGE_SCN_MEM_DISCARDABLE          =  0x02000000  #  Section can be discarded.
 IMAGE_SCN_MEM_NOT_CACHED           =  0x04000000  #  Section is not cachable.
 IMAGE_SCN_MEM_NOT_PAGED            =  0x08000000  #  Section is not pageable.
 IMAGE_SCN_MEM_SHARED               =  0x10000000  #  Section is shareable.
 IMAGE_SCN_MEM_EXECUTE              =  0x20000000  #  Section is executable.
 IMAGE_SCN_MEM_READ                 =  0x40000000  #  Section is readable.
 IMAGE_SCN_MEM_WRITE                =  0x80000000  #  Section is writeable.

# 
#  TLS Characteristic Flags
# 
 IMAGE_SCN_SCALE_INDEX              =  0x00000001  #  Tls index is scaled

}

Function Convert-EnumToString([Enum]$enum)
{
    $type = [Type]$enum
}
$IMAGE_DOS_HEADER = New-Object System.Collections.Specialized.OrderedDictionary

$IMAGE_DOS_HEADER.Add("e_magic",[Uint16]0)                     # magic number
$IMAGE_DOS_HEADER.Add("e_cblp",[UInt16]0)                      # Bytes on last page of file
$IMAGE_DOS_HEADER.Add("e_cp",[UInt16]0)                        # Pages in file
$IMAGE_DOS_HEADER.Add("e_crlc",[UInt16]0)                      # Relocations
$IMAGE_DOS_HEADER.Add("e_cparhdr",[UInt16]0)                   # Size of header in paragraphs
$IMAGE_DOS_HEADER.Add("e_minalloc",[UInt16]0)                  # Minimum extra paragraphs needed
$IMAGE_DOS_HEADER.Add("e_maxalloc",[UInt16]0)                  # Maximum extra paragraphs needed
$IMAGE_DOS_HEADER.Add("e_ss",[UInt16]0)                        # Initial (relative) SS value
$IMAGE_DOS_HEADER.Add("e_sp",[UInt16]0)                        # Initial SP value
$IMAGE_DOS_HEADER.Add("e_csum",[UInt16]0)                      # Checksum
$IMAGE_DOS_HEADER.Add("e_ip",[UInt16]0)                        # Initial IP value
$IMAGE_DOS_HEADER.Add("e_cs",[UInt16]0)                        # Initial (relative) CS value
$IMAGE_DOS_HEADER.Add("e_lfarlc",[UInt16]0)                    # File address of relocation table
$IMAGE_DOS_HEADER.Add("e_ovno",[UInt16]0)                      # Overlay number
$IMAGE_DOS_HEADER.Add("e_res",(New-Object UInt16[] 4))         # Reserved words
$IMAGE_DOS_HEADER.Add("e_oemid",[UInt16]0)                     # OEM identifier (for e_oeminfo)
$IMAGE_DOS_HEADER.Add("e_oeminfo",[UInt16]0)                   # OEM information,[UInt16]0) e_oemid specific
$IMAGE_DOS_HEADER.Add("e_res2[10]",(New-Object UInt16[] 10))   # Reserved words
$IMAGE_DOS_HEADER.Add("e_lfanew",[Uint32]0)                    # File address of new exe header

$IMAGE_FILE_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_FILE_HEADER.Add("Machine",[UInt16]0)
$IMAGE_FILE_HEADER.Add("NumberOfSections",[UInt16]0)
$IMAGE_FILE_HEADER.Add("TimeDateStamp",[UInt32]0)
$IMAGE_FILE_HEADER.Add("PointerToSymbolTable",[UInt32]0)
$IMAGE_FILE_HEADER.Add("NumberOfSymbols",[UInt32]0)
$IMAGE_FILE_HEADER.Add("SizeOfOptionalHeader",[UInt16]0)
$IMAGE_FILE_HEADER.Add("Characteristics",[UInt16]0)

$IMAGE_OPTIONAL_HEADER = New-Object System.Collections.Specialized.OrderedDictionary

$IMAGE_OPTIONAL_HEADER.Add("Magic",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfInitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfUninitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("AddressOfEntryPoint",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("BaseOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("BaseOfData",[Uint32]0)

# NT Additional Fields

$IMAGE_OPTIONAL_HEADER.Add("ImageBase",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SectionAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("FileAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MajorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("MinorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("Win32VersionValue",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfImage",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeaders",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("CheckSum",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("Subsystem",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("DllCharacteristics",[Uint16]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfStackReserve",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfStackCommit",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeapReserve",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("SizeOfHeapCommit",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("LoaderFlags",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("NumberOfRvaAndSizes",[Uint32]0)
$IMAGE_OPTIONAL_HEADER.Add("DataDirectory",(New-Object System.Collections.Specialized.OrderedDictionary[] $IMAGE_NUMBEROF_DIRECTORY_ENTRIES))
For ($i = 0; $i -lt $IMAGE_OPTIONAL_HEADER["DataDirectory"].Count;$i++)
{
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i] = New-Object System.Collections.Specialized.OrderedDictionary
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i].Add("VirtualAddress",[Uint32]0)
    $IMAGE_OPTIONAL_HEADER["DataDirectory"][$i].Add("Size",[UINt32]0)
}


$IMAGE_OPTIONAL_HEADER64 = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_OPTIONAL_HEADER64.Add("Magic",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorLinkerVersion",[Byte]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfInitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfUninitializedData",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("AddressOfEntryPoint",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("BaseOfCode",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("ImageBase",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SectionAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("FileAlignment",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorOperatingSystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorImageVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MajorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("MinorSubsystemVersion",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("Win32VersionValue",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfImage",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeaders",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("CheckSum",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("Subsystem",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("DllCharacteristics",[Uint16]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfStackReserve",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfStackCommit",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeapReserve",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("SizeOfHeapCommit",[Uint64]0)
$IMAGE_OPTIONAL_HEADER64.Add("LoaderFlags",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("NumberOfRvaAndSizes",[Uint32]0)
$IMAGE_OPTIONAL_HEADER64.Add("DataDirectory",(New-Object System.Collections.Specialized.OrderedDictionary[] $IMAGE_NUMBEROF_DIRECTORY_ENTRIES))
For ($i = 0; $i -lt $IMAGE_OPTIONAL_HEADER64["DataDirectory"].Count;$i++)
{
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i] = New-Object System.Collections.Specialized.OrderedDictionary
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i].Add("VirtualAddress",[Uint32]0)
    $IMAGE_OPTIONAL_HEADER64["DataDirectory"][$i].Add("Size",[UINt32]0)
}

$IMAGE_SECTION_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
$IMAGE_SECTION_HEADER.Add("Name",(New-Object Byte[] $IMAGE_SIZEOF_SHORT_NAME))
$IMAGE_SECTION_HEADER.Add("Misc.PhysicalAddress",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("Misc.VirtualSize",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("VirtualAddress",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("SizeOfRawData",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToRawData",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToRelocations",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("PointerToLinenumbers",[Uint32]0)
$IMAGE_SECTION_HEADER.Add("NumberOfRelocations",[Uint16]0)
$IMAGE_SECTION_HEADER.Add("NumberOfLinenumbers",[Uint16]0)
$IMAGE_SECTION_HEADER.Add("Characteristics",[Uint32]0)

Function Convert-EnumToString
{
    param($enum,$value)
    
    $builder = New-Object System.Text.StringBuilder
    $names = ($enum.DeclaredFields | Select-Object -Property Name | Where-Object { $_.Name -ne "value__" }).Name

    ForEach ($name in $names)
    {
        if (!($value -ne 0 -and [Enum]::Parse($enum,$name).value__ -eq 0))
        {
            if ((([Enum]::Parse($enum,$name).value__) -band $value) -eq ([Enum]::Parse($enum,$name).value__))
            {
                if ($builder.Length -gt 0)
                {
                    [void]$builder.Append(" | ")
                }
                [void]$builder.Append($name)
            }
        }
    }

    return $builder.ToString()
    
}
Function Read-BinaryFile
{
    param(
    [System.IO.BinaryReader][ref]$reader,
    [System.Collections.Specialized.OrderedDictionary][ref]$items)
   
   
    $keys = New-Object String[] $items.Count
    $items.Keys.CopyTo($keys,0)
  
    for ($i = 0;$i -lt $items.Count;$i++)
    {
        $item = $keys[$i]
       
        if ($items[$item] -is [Array])
        {
            if ($items[$item][0] -is [System.Collections.Specialized.OrderedDictionary])
            {
                ForEach ($subItem in $items[$item])
                {
                    Read-BinaryFile -reader ([ref]$reader) -items ([ref]$subItem)
                }
            }
            else
            {
                $currentItem = $items[$item][0]
                For ($j = 0;$j -lt $items[$item].Length;$j++)
                {
                    switch($currentItem.GetType().Name)
                    {
                        "Byte"
                        {
                            $items[$item][$j] = $reader.ReadByte()
                        }
                        "Uint16" 
                        { 
                            $items[$item][$j] = $Reader.ReadUint16()
                        }
                        "Uint32" 
                        {
                            $items[$item][$j] = $reader.ReadUInt32()
                        }
                        "Uint64"
                        { 
                            $items[$item][$j] = $Reader.ReadUInt64()
                        }
                        default  { "Unknown Type! $($currentItem.GetType().Name)" }
                    }
                }
            }
        }
        else
        {
            $currentItem = $items[$item]
            if ($currentItem -is [System.Collections.Specialized.OrderedDictionary])
            {
                Read-BinaryFile -reader ([ref]$reader) -items ([ref]$items[$item])
            }
            else
            {
                switch($currentItem.GetType().Name)
                {
                    "Byte"
                    {
                        $items[$item] = $reader.ReadByte()
                    }

                    "Uint16" { 
                        $items[$item] = $reader.ReadUint16()

                    }
                    "Uint32" 
                    { 
                         $items[$item] = $reader.ReadUint32()
                    }
                    "Uint64" 
                    {
                        $items[$item] = $reader.ReadUint64()               
                    }
                    default  { "Unknown Type! $($currentItem.GetType().Name)" }
                }
            }
        }
    }
}


$FileStream = [System.IO.File]::Open($filename, [System.IO.FileMode]::Open,[System.IO.FileAccess]::Read,[System.IO.FileShare]::Read)

$BinaryReader = New-Object System.IO.BinaryReader($FileStream)
[void]$BinaryReader.BaseStream.Seek(0,[System.IO.SeekOrigin]::Begin)

Read-BinaryFile -reader ([ref]$BinaryReader) -Items ([ref]$IMAGE_DOS_HEADER)

if ($IMAGE_DOS_HEADER["e_magic"] -eq $IMAGE_DOS_SIGNATURE)
{
    [void]$BinaryReader.BaseStream.Seek($IMAGE_DOS_HEADER["e_lfanew"] + 24,[System.IO.SeekOrigin]::Begin)
    $magic = $BinaryReader.ReadInt16()
    [void]$BinaryReader.BaseStream.Seek($IMAGE_DOS_HEADER["e_lfanew"],[System.IO.SeekOrigin]::Begin)

    $IMAGE_NT_HEADER = New-Object System.Collections.Specialized.OrderedDictionary
    $IMAGE_NT_HEADER.Add("Signature",[Uint32]0)
    $IMAGE_NT_HEADER.Add("FileHeader",$IMAGE_FILE_HEADER)

    switch ($magic)
    {
        $IMAGE_NT_OPTIONAL_HDR32_MAGIC 
        { 
            Write-Host "32-bit Executable"
            $IMAGE_NT_HEADER.Add("OptionalHeader",$IMAGE_OPTIONAL_HEADER) 
        }
        $IMAGE_NT_OPTIONAL_HDR64_MAGIC 
        {
            Write-Host "64-bit Executable" 
            $IMAGE_NT_HEADER.Add("OptionalHeader",$IMAGE_OPTIONAL_HEADER64)
        } 
        $IMAGE_ROM_OPTIONAL_HDR_MAGIC { "ROM File, not supported!" }
        default { "Unknown PE file type $($magic)!" } 
    }
    Read-BinaryFile -reader ([ref]$BinaryReader) -Items ([ref]$IMAGE_NT_HEADER)

}
else
{
    Write-Host "Not a valid PE file!"
}

$dllCharacteristics = Convert-EnumToString -enum ([IMAGE_DLLCHARACTERISTICS]) -value $IMAGE_NT_HEADER["OptionalHeader"]["DllCharacteristics"]
$subSystem = [Enum]::Parse([IMAGE_SUBSYSTEM],$IMAGE_NT_HEADER["OptionalHeader"]["Subsystem"])
$machine = [Enum]::Parse([IMAGE_FILE_MACHINE],$IMAGE_NT_HEADER["FileHeader"]["Machine"])
Write-Host "Image Version : $($IMAGE_NT_HEADER["OptionalHeader"]["MajorImageVersion"]).$($IMAGE_NT_HEADER["OptionalHeader"]["MinorImageVersion"])"
Write-Host "OS Version : $($IMAGE_NT_HEADER["OptionalHeader"]["MajorOperatingSystemVersion"]).$($IMAGE_NT_HEADER["OptionalHeader"]["MinorOperatingSystemVersion"])"
Write-Host "Subsystem Version $($IMAGE_NT_HEADER["OptionalHeader"]["MajorSubsystemVersion"]).$($IMAGE_NT_HEADER["OptionalHeader"]["MinorSubsystemVersion"])"
Write-Host "DLL Characteristics : $dllCharacteristics"
Write-Host "Subsystem : $subsystem"
Write-Host "Machine : $machine"
$BinaryReader.Close()
$FileStream.Close()


About chentiangemalc

specializes in end-user computing technologies. disclaimer 1) use at your own risk. test any solution in your environment. if you do not understand the impact/consequences of what you're doing please stop, and ask advice from somebody who does. 2) views are my own at the time of posting and do not necessarily represent my current view or the view of my employer and family members/relatives. 3) over the years Microsoft/Citrix/VMWare have given me a few free shirts, pens, paper notebooks/etc. despite these gifts i will try to remain unbiased.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a comment