usr/Get-PeManifest.ps1

using namespace System.IO
using namespace System.Text

function Get-PeManifest {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory)]
    [ValidateScript({!!($script:file = Convert-Path -Path $_ -ErrorAction 0)})]
    [ValidateNotNullOrEmpty()]
    [String]$Path
  )

  begin {
    $Path = $file
    function private:Convert-RvaToRaw([UInt32]$rva, [UInt32]$align) {
      end {
        [ScriptBlock]$Aligner = {
          param([UInt32]$size)
          ($size -band ($align - 1)) ? (($size -band ($align * -1)) + $align) : $size
        }

        $sections.ForEach{
          if (($rva -ge $_.VirtualAddress) -and (
            $rva -lt ($_.VirtualAddress + (& $Aligner $_.VirtualSize))
          )) { return ($rva - ($_.VirtualAddress - $_.PointerToRawData)) }
        }
      }
    }
  }
  #process {}
  end {
    try {
      $br = [BinaryReader]::new(($fs = [File]::OpenRead($Path)))
      if ($br.ReadUInt16() -ne 0x5A4D) {
        throw [InvalidOperationException]::new('DOS signature has not been found.')
      }
      $fs.Position = 0x3C # e_lfanew
      $fs.Position = $br.ReadUInt32() # move to IMAGE_NT_HEADERS
      if ($br.ReadUInt32() -ne 0x4550) {
        throw [InvalidOperationException]::new('PE signature has not been found.')
      }
      $fs.Position += 0x02 # IMAGE_FILE_HEADER->Machine
      $NumberOfSections = $br.ReadUInt16()
      $fs.Position += 0x0C # IMAGE_FILE_HEADER->... till SizeOfOptionalHeader
      $SizeOfOptionalHeader, $offset = $br.ReadUInt16(), ($fs.Position + 0x02)
      $fs.Position += 0x26 # getting FileAlignment
      $FileAlignment = $br.ReadUInt32()
      $fs.Position = $offset + $SizeOfOptionalHeader
      if (!($PointerToRawData = ($sections = (1..$NumberOfSections).ForEach{
        [PSCustomObject]@{
          Name = [String]::new($br.ReadChars(0x08)).Trim("`0")
          VirtualSize = $br.ReadUInt32()
          VirtualAddress = $br.ReadUInt32()
          SizeOfRawData = $br.ReadUInt32()
          PointerToRawData = $br.ReadUInt32()
        }
        $fs.Position += 0x10 # move to the next section
      }).Where{$_.Name -eq '.rsrc'}.PointerToRawData)) {
        throw [InvalidOperationException]::new('It seems there are no resources.')
      }
      $fs.Position = $PointerToRawData + 0x0C # enumerate resources
      $entry = {
        param([UInt16]$name, [UInt16]$id)
        end {
          (1..($name + $id)).ForEach{
            [PSCustomObject]@{
              Name = $br.ReadUInt32()
              OffsetToData = $br.ReadUInt32()
            }
          }
        }
      }
      if (!($manifest = (& $entry $br.ReadUInt16() $br.ReadUInt16()
        ).Where{$_.Name -eq 24}.OffsetToData)) {
        throw [InvalidOperationException]::new('It seems there is no manifest.')
      }
      $fs.Position = $PointerToRawData + ($manifest -band 0x7FFFFFFF) + 0x0C # manifest directory
      $fs.Position = $PointerToRawData + (
        (& $entry ($n=$br.ReadUInt16()) ($i=$br.ReadUInt16())).OffsetToData -band 0x7FFFFFFF
      ) + 0x14 # LANGID
      "ResDir (MANIFEST) Entries:$($n+$i) (Named:$n, ID:$i)"
      $fs.Position = $PointerToRawData + $br.ReadUInt32() # IMAGE_RESOURCE_DATA_ENTRY
      $rva, $size = $br.ReadUInt32(), $br.ReadUInt32()
      "$(,([Char]32)*2)DataRVA: $($rva.ToString('X8')) DataSize: $($size.ToString('X'))`n"
      $fs.Position = Convert-RvaToRaw $rva $FileAlignment
      [Encoding]::UTF8.GetString($br.ReadBytes($size))
    }
    catch { Write-Verbose $_ }
    finally {
      ($br, $fs).ForEach{ if ($_) { $_.Dispose() } }
    }
  }
}

Export-ModuleMember -Function Get-PeManifest