# encoding: ascii
# api: csharp
# title: PE
# description: Get-PEManifest and Get-PEHeader some fixes
# version: 0.1
# type: function
# author: greg zakharov
# license: CC0
# function: Get-PEManifest
# x-poshcode-id: 4782
# x-archived: 2014-01-15T19:12:07
# x-published: 2014-01-08T15:36:00
#
#
Set-Alias gpem Get-PEManifest
Set-Alias gpeh Get-PEHeader
#
# need for accessing PEStreams and SystemUtils
#
if (!([AppDomain]::CurrentDomain.GetAssemblies() | ? {
  $_.FullName.Split(',')[0] -eq 'System.Deployment'
})) {Add-Type -AssemblyName System.Deployment}
#
# common function(s)
#
function script:Get-Class([String]$Assembly, [String]$Class) {
  return ([AppDomain]::CurrentDomain.GetAssemblies() | ? {
    $_.FullName.Split(',')[0] -eq $Assembly
  }).GetType($Class)
}
#
# get PE manifest
#
function Get-PEManifest {
  [CmdletBinding(SupportsShouldProcess=$true)]
  param(
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [ValidateScript({Test-Path $_})]
    [String]$FileName
  )
  
  begin {
    $FileName = cvpa $FileName
    $asm = 'System.Deployment'
    $suT = Get-Class $asm $($asm + '.Application.Win32InterOp.SystemUtils')
    $suC = [Activator]::CreateInstance($suT)
  }
  process {
    if ($PSCmdlet.ShouldProcess($FileName, 'Dump manifest of')) {
      try {
        $res = -join [Char[]]$suC.GetType().InvokeMember('GetManifestFromPEResources', `
                                [Reflection.BindingFlags]280, $null, $suT, @($FileName))
      }
      catch [Management.Automation.RuntimeException] {
        $exp = [Boolean]$_.Exception
      }
    }
  }
  end {
    switch ($exp) {
      $true { Write-Host Invalid PE format.`n -fo Red }
      default { if ($res -ne '') { $res;"" } else {
        Write-Host File $($FileName) does not contain manifest.`n -fo Yellow }
      }
    }
  }
}
#
# get PE header
#
function Get-PEHeader {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
    [ValidateScript({Test-Path $_})]
    [String]$FileName
  )
  
  begin {
    function private:Get-Struct([Object]$Base, [String]$Name) {
      return [Activator]::CreateInstance(
        $Base.GetNestedType($Name, [Reflection.BindingFlags]32)
      )
    }
    
    function Trace-Binary([IO.BinaryReader]$Reader, [Object]$Data) {
      $bytes = $Reader.ReadBytes(
        [Runtime.InteropServices.Marshal]::SizeOf($Data)
      )
      $hndl = [Runtime.InteropServices.GCHandle]::Alloc(
        $bytes, [Runtime.InteropServices.GCHandleType]::Pinned
      )
      $struct = [Runtime.InteropServices.Marshal]::PtrToStructure(
        $hndl.AddrOfPinnedObject(), $Data.GetType()
      )
      $hndl.Free()
      
      return $struct
    }
    
    $FileName = cvpa $FileName
    $nul = '-' * 35 #optional
    
    $asm = 'System.Deployment' #it will be overwritten
    $IMAGE_DOS_HEADER = Get-Struct $(Get-Class $asm $($asm + '.Application.PEStream')) IMAGE_DOS_HEADER
    
    try { [void][PEUtils.Header] } catch [Management.Automation.RuntimeException] {
      $cscp = New-Object Microsoft.CSharp.CSharpCodeProvider
      
      $cp = New-Object CodeDom.Compiler.CompilerParameters
      $cp.GenerateExecutable = $false
      $cp.GenerateInMemory   = $true
      
      [void]$cscp.CompileAssemblyFromSource($cp, $library)
    }
    
    $asm = 'PEUtils.Header' -as [Type]
    $IMAGE_FILE_HEADER       = Get-Struct $asm IMAGE_FILE_HEADER
    $IMAGE_OPTIONAL_HEADER32 = Get-Struct $asm IMAGE_OPTIONAL_HEADER32
    $IMAGE_OPTIONAL_HEADER64 = Get-Struct $asm IMAGE_OPTIONAL_HEADER64
    $IMAGE_SECTION_HEADER    = Get-Struct $asm IMAGE_SECTION_HEADER
  }
  process {
    try {
      $fs = New-Object IO.FileStream($FileName, [IO.FileMode]::Open, [IO.FileAccess]::Read)
      $br = New-Object IO.BinaryReader($fs)
      
      $dos = Trace-Binary $br $IMAGE_DOS_HEADER
      [void]$fs.Seek($dos.e_lfanew, [IO.SeekOrigin]::Begin)
      $sig = $br.ReadUInt32()
      
      $fh = Trace-Binary $br $IMAGE_FILE_HEADER
      if ($fh.Characteristics.ToString().Split(', ') -contains 'Supports32Bit') {
        $pe = Trace-Binary $br $IMAGE_OPTIONAL_HEADER32
      }
      else {
        $pe = Trace-Binary $br $IMAGE_OPTIONAL_HEADER64
      }
      
      $sec = New-Object "$IMAGE_SECTION_HEADER[]" $fh.NumberOfSections
      for ($i = 0; $i -lt $sec.Length; ++$i) {
        $sec[$i] = Trace-Binary $br $IMAGE_SECTION_HEADER
      }
    }
    catch { $exp = [Boolean]$_.Exception }
    finally {
      if ($br -ne $null) { $br.Close() }
      if ($fs -ne $null) { $fs.Close() }
    }
  }
  end {
    switch ($exp) {
      $true { Write-Host Invalid PE format.`n -fo Red }
      default {
        "FILE HEADER VALUES`n$nul"
        $fh.PSObject.Properties | % {
          if ($_.Name -eq 'Machine') {
            '{0, 21}: {1}' -f $_.Name, [Reflection.ImageFileMachine]$_.Value
          }
          elseif ($_.Name -eq 'TimeDateStamp') {
            '{0, 21}: {1}' -f $_.Name, $([TimeZone]::CurrentTimeZone.ToLocalTime(
              ([DateTime]'1.1.1970').AddSeconds($_.Value))
            )
          }
          else {
            '{0, 21}: {1}' -f $_.Name, $_.Value
          }
        }
        "`n`nOPTIONAL HEDAER VALUES`n$nul"
        $pe.PSObject.Properties | % {
          if ($_.Name -eq 'Magic') {
            '{0, 28}: {1}' -f $_.Name, $(if ($_.Value -eq 0x10b) { 'PE32' } else { 'PE32+' })
          }
          elseif ($_.Name -ne 'Subsystem' -and $_.Name -ne `
            'DllCharacteristics' -and $_.Name -ne 'MajorLinkerVersion') {
            '{0, 28}: {1:X}' -f $_.Name, $_.Value
          }
          else {
            '{0, 28}: {1}' -f $_.Name, $_.Value
          }
        }
        ""
        $sec | select Name, VirtualSize, VirtualAddress, SizeOfRawData, PointerToRawData, `
        Characteristics | ft -a
      }
    }
  }
}
#
# additional library
#
$script:library = @'
using System;
using System.Reflection;
using System.Runtime.InteropServices;

[assembly: AssemblyVersion("2.0.0.0")]
[assembly: CLSCompliant(true)]
[assembly: ComVisible(false)]

namespace PEUtils {
  public sealed class Header {
    private Header() {}

    [Flags]
    internal enum IMAGE_FILE_CHARACTERISTICS : ushort { //UInt16
      RelocInfoStrippedFromFile     = 0x0001,
      Executable                    = 0x0002,
      LineNumbersStripped           = 0x0004,
      SymbolTableStripped           = 0x0008,
      AgressiveTrimWorkingSet       = 0x0010,
      LargeAddressAware             = 0x0020,
      Supports16Bit                 = 0x0040,
      ReservedBytesWo               = 0x0080,
      Supports32Bit                 = 0x0100,
      DebugInfoStripped             = 0x0200,
      RunFromSwapIfInRemovableMedia = 0x0400,
      RunFromSwapIfInNetworkMedia   = 0x0800,
      SystemFile                    = 0x1000,
      Dll                           = 0x2000,
      OnlyForSingleCoreProcessor    = 0x4000,
      BytesOfWordReserved           = 0x8000
    }
    
    [Flags]
    internal enum IMAGE_SUBSYSTEM : ushort { //UInt16
      UNKNOWN                  = 0,
      NATIVE                   = 1,
      WINDOWS_GUI              = 2,
      WINDOWS_CUI              = 3,
      OS2_CUI                  = 5,
      POSIX_CUI                = 7,
      NATIVE_WINDOWS           = 8,
      WINDOWS_CE_GUI           = 9,
      EFI_APPLICATION          = 10,
      EFI_BOOT_SERVICE_DRIVER  = 11,
      EFI_RUNTIME_DRIVER       = 12,
      EFI_ROM                  = 13,
      XBOX                     = 14,
      WINDOWS_BOOT_APPLICATION = 16
    }
    
    [Flags]
    internal enum IMAGE_DLL_CHARACTERISTICS : ushort { //UInt16
      RES_0                 = 0x0001,
      RES_1                 = 0x0002,
      RES_2                 = 0x0004,
      RES_3                 = 0x0008,
      DYNAMIC_BASE          = 0x0040,
      FORCE_INTEGRITY       = 0x0080,
      NX_COMPAT             = 0x0100,
      NO_ISOLATION          = 0x0200,
      NO_SEH                = 0x0400,
      NO_BIND               = 0x0800,
      RES_4                 = 0x1000,
      WDM_DRIVER            = 0x2000,
      TERMINAL_SERVER_AWARE = 0x8000
    }
    
    [Flags]
    internal enum IMAGE_SECTION_CHARACTERISTICS : uint { //UInt32
      TypeReg                  = 0x00000000,
      TypeDsect                = 0x00000001,
      TypeNoLoad               = 0x00000002,
      TypeGroup                = 0x00000004,  
      TypeNoPadded             = 0x00000008,
      TypeCopy                 = 0x00000010,
      ContentCode              = 0x00000020,
      ContentInitializedData   = 0x00000040,
      ContentUninitializedData = 0x00000080,
      LinkOther                = 0x00000100,
      LinkInfo                 = 0x00000200,
      TypeOver                 = 0x00000400,
      LinkRemove               = 0x00000800,
      LinkComDat               = 0x00001000,
      NoDeferSpecExceptions    = 0x00004000,
      RelativeGP               = 0x00008000,
      MemPurgeable             = 0x00020000,
      Memory16Bit              = 0x00020000,
      MemoryLocked             = 0x00040000,
      MemoryPreload            = 0x00080000,
      Align1Bytes              = 0x00100000,
      Align2Bytes              = 0x00200000,
      Align4Bytes              = 0x00300000,
      Align8Bytes              = 0x00400000,
      Align16Bytes             = 0x00500000,
      Align32Bytes             = 0x00600000,
      Align64Bytes             = 0x00700000,
      Align128Bytes            = 0x00800000,
      Align256Bytes            = 0x00900000,
      Align512Bytes            = 0x00A00000,
      Align1024Bytes           = 0x00B00000,
      Align2048Bytes           = 0x00C00000,
      Align4096Bytes           = 0x00D00000,
      Align8192Bytes           = 0x00E00000,
      AlignMask                = 0x00F00000,
      LinkExRelocationOverflow = 0x01000000,
      MemoryDiscardable        = 0x02000000,
      MemoryNotCached          = 0x04000000,
      MemoryNotPaged           = 0x08000000,
      MemoryShared             = 0x10000000,
      MemoryExecute            = 0x20000000,
      MemoryRead               = 0x40000000,
      MemoryWrite              = 0x80000000 
    }
    
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct IMAGE_DATA_DIRECTORY {
      public UInt32 VirtualAddress;
      public UInt32 Size;
    }
    
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct IMAGE_FILE_HEADER {
      public UInt16 Machine;
      public UInt16 NumberOfSections;
      public UInt32 TimeDateStamp;
      public UInt32 PointerToSymbolTable;
      public UInt32 NumberOfSymbols;
      public UInt16 SizeOfOptionalHeader;
      public IMAGE_FILE_CHARACTERISTICS Characteristics;
    }
    
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct IMAGE_OPTIONAL_HEADER32 {
      public UInt16 Magic;
      public Byte MajorLinkerVersion;
      public Byte MinorLinkerVersion;
      public UInt32 SizeOfCode;
      public UInt32 SizeOfInitializedData;
      public UInt32 SizeOfUninitializedData;
      public UInt32 AddressOfEntryPoint;
      public UInt32 BaseOfCode;
      public UInt32 BaseOfData;
      public UInt32 ImageBase;
      public UInt32 SectionAlignment;
      public UInt32 FileAlignment;
      public UInt16 MajorOperationSystemVersion;
      public UInt16 MinorOperationSystemVersion;
      public UInt16 MajorImageVersion;
      public UInt16 MinorImageVersion;
      public UInt16 MajorSubsystemVersion;
      public UInt16 MinorSybsystemVersion;
      public UInt32 Win32VersionValue;
      public UInt32 SizeOfImage;
      public UInt32 SizeOfHeaders;
      public UInt32 CheckSum;
      public IMAGE_SUBSYSTEM Subsystem;
      public IMAGE_DLL_CHARACTERISTICS DllCharacteristics;
      public UInt32 SizeOfStackReserve;
      public UInt32 SizeOfStackCommit;
      public UInt32 SizeOfHeapReserve;
      public UInt32 SizeOfHeapCommit;
      public UInt32 LoaderFlags;
      public UInt32 NumberOfRvaAndSizes;
      [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 16)]
      public IMAGE_DATA_DIRECTORY[] DataDirectory;
    }
    
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct IMAGE_OPTIONAL_HEADER64 {
      public UInt16 Magic;
      public Byte MajorLinkerVersion;
      public Byte MinorLinkerVersion;
      public UInt32 SizeOfCode;
      public UInt32 SizeOfInitializedData;
      public UInt32 SizeOfUninitializedData;
      public UInt32 AddressOfEntryPoint;
      public UInt32 BaseOfCode;
      public UInt64 ImageBase;
      public UInt32 SectionAlignment;
      public UInt32 FileAlignment;
      public UInt16 MajorOperatingSystemVersion;
      public UInt16 MinorOperatingSystemVersion;
      public UInt16 MajorImageVersion;
      public UInt16 MinorImageVersion;
      public UInt16 MajorSubsystemVersion;
      public UInt16 MinorSubsystemVersion;
      public UInt32 Win32VersionValue;
      public UInt32 SizeOfImage;
      public UInt32 SizeOfHeaders;
      public UInt32 CheckSum;
      public IMAGE_SUBSYSTEM Subsystem;
      public IMAGE_DLL_CHARACTERISTICS DllCharacteristics;
      public UInt64 SizeOfStackReserve;
      public UInt64 SizeOfStackCommit;
      public UInt64 SizeOfHeapReserve;
      public UInt64 SizeOfHeapCommit;
      public UInt32 LoaderFlags;
      public UInt32 NumberOfRvaAndSizes;
      [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=16)]
      public IMAGE_DATA_DIRECTORY[] DataDirectory;
    }
    
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    internal struct IMAGE_SECTION_HEADER {
      [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
      public String Name;
      public UInt32 VirtualSize;
      public UInt32 VirtualAddress;
      public UInt32 SizeOfRawData;
      public UInt32 PointerToRawData;
      public UInt32 PointerToRelocations;
      public UInt32 PointerToLinenumbers;
      public UInt16 NumberOfRelocations;
      public UInt16 NumberOfLinenumbers;
      public IMAGE_SECTION_CHARACTERISTICS Characteristics;
    }
  }
}
'@
