Skip to content

Instantly share code, notes, and snippets.

@powercode
Last active July 10, 2018 14:14
Show Gist options
  • Save powercode/e61747c36c030051965284d37572e0e4 to your computer and use it in GitHub Desktop.
Save powercode/e61747c36c030051965284d37572e0e4 to your computer and use it in GitHub Desktop.
PE Header reader for PowreShell
using namespace System.Management.Automation
using namespace System.IO
using namespace System.Collections.Generic
class PEHeaders {
[String] $Path
[CoffHeader] $Coffheader
[PEHeader] $PEHeader
[SectionHeader[]] $SectionHeaders
[DebugDirectoryEntry[]] $DebugDirectoryEntires
PEHeaders([string] $path) {
$f = [file]::OpenRead($path)
$r = [BinaryReader]::new($f)
$mz = $r.ReadUInt16()
if ($mz -ne 0x5A4D) {
# 'M', 'Z'
throw [Error]::CreateNotAnExecutable($path)
}
$f.Position = 0x3C
$f.Position = $r.ReadInt32()
$coffsig = $r.ReadInt32()
if ($coffsig -ne 0x4550) {
throw [Error]::CreateInvalidCoffSignature($path)
}
try {
$this.Path = $Path
$this.COFFHeader = [CoffHeader]::new($r)
$this.PEHeader = [PEHeader]::new($r)
$this.ReadSectionHeaders($r)
$this.DebugDirectoryEntires = $this.ReadDebugDirectory($r)
}
catch [System.Management.Automation.SetValueInvocationException] {
throw [Error]::SetValueException($_, $path)
}
catch [System.Management.Automation.PSInvalidCastException] {
throw [Error]::SetValueException($_, $path)
}
catch [Exception] {
throw [Error]::CreateUnknownError($_.Exception, $path)
}
finally {
$r.Dispose()
}
}
[void] ReadSectionHeaders([IO.BinaryReader] $reader) {
$secCount = $this.Coffheader.NumberOfSections
$headers = [SectionHeader[]]::new($secCount)
for ($i = 0; $i -lt $secCount; $i++) {
$headers[$i] = [SectionHeader]::new($reader)
}
$this.SectionHeaders = $headers
}
[List[DebugDirectoryEntry]] ReadDebugDirectory([IO.BinaryReader] $reader) {
$debugDirectory = $this.PEHeader.DebugTableDirectory
$res = [List[DebugDirectoryEntry]]::new()
if ($debugDirectory.Size -eq 0) {
return $res
}
$position = ''
if (!$this.TryGetDirectoryOffset($debugDirectory, [ref] $position)) {
throw "BadImageFormat: Invalid directory RVA"
}
if ($debugDirectory.Size % 28 -ne 0) {
throw "BadImageFormat: Invalid directory Size"
}
$count = $debugDirectory.Size / 28
$reader.BaseStream.Position = $position
return [PEHeaders]::ReadDebugDirectoryEntries($reader, $count)
}
static [List[DebugDirectoryEntry]] ReadDebugDirectoryEntries([io.BinaryReader] $reader, [int] $count) {
$res = [List[DebugDirectoryEntry]]::new($count)
for ($i = 0; $i -lt $count; $i++) {
$characteristics = $reader.ReadInt32()
if ($characteristics -ne 0) {
throw "BadImageFormatException: InvalidDebugDirectoryEntryCharacteristics"
}
$stamp = $reader.ReadUInt32()
$majorVersion = $reader.ReadUInt16()
$minorVersion = $reader.ReadUInt16()
$type = [DebugDirectoryEntryType] $reader.ReadInt32()
$dataSize = $reader.ReadInt32()
$dataRva = $reader.ReadInt32()
$dataPointer = $reader.ReadInt32()
$dde = [DebugDirectoryEntry]::new($stamp, $majorVersion, $minorVersion, $type, $dataSize, $dataRva, $dataPointer)
$res.Add($dde)
}
return $res
}
[bool] TryGetDirectoryOffset([DirectoryEntry] $entry, [ref] $offset) {
$index = $this.GetContainingSectionIndex($entry.VirtualRVA)
if ($index -lt 0) {
$offset = -1
return $false
}
$header = $this.SectionHeaders[$index]
$relativeOffset = $entry.VirtualRVA - $header.VirtualAddress
$offset.Value = $header.PointerToRawData + $relativeOffset
return $true
}
[int] GetContainingSectionIndex([int] $relativeVirtualAddress) {
for ($i = 0; $i -lt $this.sectionHeaders.Length; $i++) {
$h = $this.SectionHeaders[$i]
if ($h.VirtualAddress -le $relativeVirtualAddress -and
$relativeVirtualAddress -lt $this.sectionHeaders[$i].VirtualAddress + $this.sectionHeaders[$i].VirtualSize) {
return $i;
}
}
return -1;
}
}
class Error {
static [ErrorRecord] CreateNotAnExecutable([string] $path) {
$x = [Exception]::new("Invalid PE signatue: $path")
return [ErrorRecord]::new($x, "NotAPEExecutable", [ErrorCategory]::InvalidData, $path)
}
static [ErrorRecord] CreateUnknownError([Exception] $inner, [string] $path) {
return [ErrorRecord]::new($inner, "UnknownPEError", [ErrorCategory]::NotSpecified, $path)
}
static [ErrorRecord] SetValueException([Exception] $inner, [string] $path) {
return [ErrorRecord]::new($inner, "UnknownConversion", [ErrorCategory]::NotImplemented, $path)
}
static [ErrorRecord] CreateInvalidImage([string] $detail) {
return [ErrorRecord]::new([Exception]::new($detail), "BadImageFormat", [ErrorCategory]::InvalidData)
}
static [ErrorRecord] CreateInvalidCoffSignature([string] $path) {
return [ErrorRecord]::new([Exception]::new("Invalid Coff signature"), "BadImageFormatCoff", [ErrorCategory]::InvalidData, $path)
}
}
class CoffHeader {
# The type of target machine.
[Machine] $Machine
# The number of sections. This indicates the size of the section table which immediately follows the headers.
[int16] $NumberOfSections
# The low 32 bits of the number of seconds since 00:00 January 1 1970 that indicates when the file was created.
[int] $TimeDateStamp
# The file pointer to the COFF symbol table or zero if no COFF symbol table is present.
# This value should be zero for a PE image.
[int] $PointerToSymbolTable
# The number of entries in the symbol table. This data can be used to locate the string table
# which immediately follows the symbol table. This value should be zero for a PE image.
[int] $NumberOfSymbols
# The size of the optional header which is required for executable files but not for object files.
# This value should be zero for an object file.
[int16] $SizeOfOptionalHeader
# The flags that indicate the attributes of the file.
[Characteristics] $Characteristics
CoffHeader([io.BinaryReader] $reader) {
$this.Machine = $reader.ReadUInt16()
$this.NumberOfSections = $reader.ReadInt16()
$this.TimeDateStamp = $reader.ReadInt32()
$this.PointerToSymbolTable = $reader.ReadInt32()
$this.NumberOfSymbols = $reader.ReadInt32()
$this.SizeOfOptionalHeader = $reader.ReadInt16()
$this.Characteristics = $reader.ReadUInt16()
}
}
class PEHeader {
[PEMagic] $Magic
# The linker major version number.
[byte] $MajorLinkerVersion
# The linker minor version number.
[byte] $MinorLinkerVersion
# The size of the code (text) section or the sum of all code sections if there are multiple sections.
[int] $SizeOfCode
# The size of the initialized data section or the sum of all such sections if there are multiple data sections.
[int] $SizeOfInitializedData
# The size of the uninitialized data section (BSS) or the sum of all such sections if there are multiple BSS sections.
[int] $SizeOfUninitializedData
# The address of the entry point relative to the image base when the PE file is loaded into memory.
# For program images this is the starting address. For device drivers this is the address of the initialization function.
# An entry point is optional for DLLs. When no entry point is present this field must be zero.
[int] $AddressOfEntryPoint
# The address that is relative to the image base of the beginning-of-code section when it is loaded into memory.
[int] $BaseOfCode
# The address that is relative to the image base of the beginning-of-data section when it is loaded into memory.
[int] $BaseOfData
# The preferred address of the first byte of image when loaded into memory
# must be a multiple of 64K.
[uint64] $ImageBase
# The alignment (in bytes) of sections when they are loaded into memory. It must be greater than or equal to <see cref="FileAlignment"/>.
# The default is the page size for the architecture.
[int] $SectionAlignment
# The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
# The value should be a power of 2 between 512 and 64K inclusive. The default is 512.
# If the <see cref="SectionAlignment"/> is less than the architecture's page size
# then <see cref="FileAlignment"/> must match <see cref="SectionAlignment"/>.
[int] $FileAlignment
# The major version number of the required operating system.
[uint16] $MajorOperatingSystemVersion
# The minor version number of the required operating system.
[uint16] $MinorOperatingSystemVersion
# The major version number of the image.
[uint16] $MajorImageVersion
# The minor version number of the image.
[uint16] $MinorImageVersion
# The major version number of the subsystem.
[uint16] $MajorSubsystemVersion
# The minor version number of the subsystem.
[uint16] $MinorSubsystemVersion
# The size (in bytes) of the image including all headers as the image is loaded in memory.
# It must be a multiple of <see cref="SectionAlignment"/>.
[int] $SizeOfImage
# The combined size of an MS DOS stub PE header and section headers rounded up to a multiple of FileAlignment.
[int] $SizeOfHeaders
# The image file checksum.
[uint32] $CheckSum
# The subsystem that is required to run this image.
[Subsystem] $Subsystem
[DllCharacteristics] $DllCharacteristics
# The size of the stack to reserve. Only <see cref="SizeOfStackCommit"/> is committed
# the rest is made available one page at a time until the reserve size is reached.
[uint64] $SizeOfStackReserve
# The size of the stack to commit.
[uint64] $SizeOfStackCommit
# The size of the local heap space to reserve. Only <see cref="SizeOfHeapCommit"/> is committed
# the rest is made available one page at a time until the reserve size is reached.
[uint64] $SizeOfHeapReserve
# The size of the local heap space to commit.
[uint64] $SizeOfHeapCommit
# The number of data-directory entries in the remainder of the <see cref="PEHeader"/>. Each describes a location and size.
[int] $NumberOfRvaAndSizes
#region Directory Entries
# Aka IMAGE_DIRECTORY_ENTRY_EXPORT.
[DirectoryEntry] $ExportTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_IMPORT.
[DirectoryEntry] $ImportTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_RESOURCE.
[DirectoryEntry] $ResourceTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_EXCEPTION.
[DirectoryEntry] $ExceptionTableDirectory
# The Certificate Table entry points to a table of attribute certificates.
# These certificates are not loaded into memory as part of the image.
# As such the first field of this entry which is normally an RVA is a file pointer instead.
#
# Aka IMAGE_DIRECTORY_ENTRY_SECURITY.
[DirectoryEntry] $CertificateTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_BASERELOC.
[DirectoryEntry] $BaseRelocationTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_DEBUG.
[DirectoryEntry] $DebugTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_COPYRIGHT or IMAGE_DIRECTORY_ENTRY_ARCHITECTURE.
[DirectoryEntry] $CopyrightTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_GLOBALPTR.
[DirectoryEntry] $GlobalPointerTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_TLS.
[DirectoryEntry] $ThreadLocalStorageTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG.
[DirectoryEntry] $LoadConfigTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT.
[DirectoryEntry] $BoundImportTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_IAT.
[DirectoryEntry] $ImportAddressTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT.
[DirectoryEntry] $DelayImportTableDirectory
# Aka IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR.
[DirectoryEntry] $CorHeaderTableDirectory
#endregion
PEHeader([IO.BinaryReader] $reader) {
[PEMagic] $local:magic = $reader.ReadUInt16()
$this.Magic = $magic
if ($magic -ne [PEMagic]::PE32 -and $magic -ne [PEMagic]::PE32Plus) {
throw new BadImageFormatException(SR.UnknownPEMagicValue)
}
$this.Magic = $magic
$this.MajorLinkerVersion = $reader.ReadByte()
$this.MinorLinkerVersion = $reader.ReadByte()
$this.SizeOfCode = $reader.ReadInt32()
$this.SizeOfInitializedData = $reader.ReadInt32()
$this.SizeOfUninitializedData = $reader.ReadInt32()
$this.AddressOfEntryPoint = $reader.ReadInt32()
$this.BaseOfCode = $reader.ReadInt32()
if ($magic -eq [PEMagic]::PE32Plus) {
$this.BaseOfData = 0 # not present
}
else {
$this.BaseOfData = $reader.ReadInt32()
}
if ($magic -eq [PEMagic]::PE32Plus) {
$this.ImageBase = $reader.ReadUInt64()
}
else {
$this.ImageBase = $reader.ReadUInt32()
}
# NT additional fields:
$this.SectionAlignment = $reader.ReadInt32()
$this.FileAlignment = $reader.ReadInt32()
$this.MajorOperatingSystemVersion = $reader.ReadUInt16()
$this.MinorOperatingSystemVersion = $reader.ReadUInt16()
$this.MajorImageVersion = $reader.ReadUInt16()
$this.MinorImageVersion = $reader.ReadUInt16()
$this.MajorSubsystemVersion = $reader.ReadUInt16()
$this.MinorSubsystemVersion = $reader.ReadUInt16()
# Win32VersionValue (reserved should be 0)
$reader.ReadUInt32()
$this.SizeOfImage = $reader.ReadInt32()
$this.SizeOfHeaders = $reader.ReadInt32()
$this.CheckSum = $reader.ReadUInt32()
$this.Subsystem = $reader.ReadUInt16()
$this.DllCharacteristics = $reader.ReadUInt16()
if ($magic -eq [PEMagic]::PE32Plus) {
$this.SizeOfStackReserve = $reader.ReadUInt64()
$this.SizeOfStackCommit = $reader.ReadUInt64()
$this.SizeOfHeapReserve = $reader.ReadUInt64()
$this.SizeOfHeapCommit = $reader.ReadUInt64()
}
else {
$this.SizeOfStackReserve = $reader.ReadUInt32()
$this.SizeOfStackCommit = $reader.ReadUInt32()
$this.SizeOfHeapReserve = $reader.ReadUInt32()
$this.SizeOfHeapCommit = $reader.ReadUInt32()
}
# loader flags
$reader.ReadUInt32()
$this.NumberOfRvaAndSizes = $reader.ReadInt32()
# directory entries:
$this.ExportTableDirectory = [DirectoryEntry]::new($reader)
$this.ImportTableDirectory = [DirectoryEntry]::new($reader)
$this.ResourceTableDirectory = [DirectoryEntry]::new($reader)
$this.ExceptionTableDirectory = [DirectoryEntry]::new($reader)
$this.CertificateTableDirectory = [DirectoryEntry]::new($reader)
$this.BaseRelocationTableDirectory = [DirectoryEntry]::new($reader)
$this.DebugTableDirectory = [DirectoryEntry]::new($reader)
$this.CopyrightTableDirectory = [DirectoryEntry]::new($reader)
$this.GlobalPointerTableDirectory = [DirectoryEntry]::new($reader)
$this.ThreadLocalStorageTableDirectory = [DirectoryEntry]::new($reader)
$this.LoadConfigTableDirectory = [DirectoryEntry]::new($reader)
$this.BoundImportTableDirectory = [DirectoryEntry]::new($reader)
$this.ImportAddressTableDirectory = [DirectoryEntry]::new($reader)
$this.DelayImportTableDirectory = [DirectoryEntry]::new($reader)
$this.CorHeaderTableDirectory = [DirectoryEntry]::new($reader)
# ReservedDirectory (should be 0 0)
[DirectoryEntry]::new($reader)
}
}
class SectionHeader {
[string] $Name
[int] $VirtualSize
[int] $VirtualAddress
[int] $SizeOfRawData
[int] $PointerToRawData
[int] $PointerToRelocations
[int] $PointerToLineNumbers
[uint16] $NumberOfRelocations
[uint16] $NumberOfLineNumbers
[SectionCharacteristics] $SectionCharacteristics
SectionHeader([IO.BinaryReader] $reader) {
$nBytes = $reader.ReadBytes(8)
for($i = 0; $i -lt 8; $i++){
if ($nBytes[$i] -eq 0){
break
}
}
$this.Name = [Text.Encoding]::UTF8.GetString($nBytes, 0, $i)
$this.VirtualSize = $reader.ReadInt32()
$this.VirtualAddress = $reader.ReadInt32()
$this.SizeOfRawData = $reader.ReadInt32()
$this.PointerToRawData = $reader.ReadInt32()
$this.PointerToRelocations = $reader.ReadInt32()
$this.PointerToLineNumbers = $reader.ReadInt32()
$this.NumberOfRelocations = $reader.ReadUInt16()
$this.NumberOfLineNumbers = $reader.ReadUInt16()
$this.SectionCharacteristics = $reader.ReadInt32()
}
}
class DirectoryEntry {
[uint32] $VirtualRVA
[uint32] $Size
DirectoryEntry([IO.BinaryReader] $reader) {
$this.VirtualRVA = $reader.ReadUInt32()
$this.Size = $reader.ReadUInt32()
}
[string] ToString() {return 'rva: {0,6:x} size: {1,6:x}' -f $this.VirtualRVA, $this.VirtualSize}
}
enum DebugDirectoryEntryType {
# An unknown value that is ignored by all tools.
Unknown = 0
# The COFF debug information (line numbers, symbol table, and string table).
# This type of debug information is also pointed to by fields in the file headers.
Coff = 1
# Associated PDB file description.
CodeView = 2
# unknown
Unknown1 = 10
Unknown2 = 11
Feat = 12
CoffGroup = 13
Unknown4 = 14
Unknown5 = 15
# Presence of this entry indicates deterministic PE/COFF file.
# The tool that produced the deterministic PE/COFF file guarantees that the entire content of the file
# is based solely on documented inputs given to the tool (such as source files, resource files, compiler options, etc.)
# rather than ambient environment variables (such as the current time, the operating system,
# the bitness of the process running the tool, etc.).
# The value of field TimeDateStamp in COFF File Header of a deterministic PE/COFF file
# does not indicate the date and time when the file was produced and should not be interpreted that way.
# Instead the value of the field is derived from a hash of the file content. The algorithm to calculate
# this value is an implementation detail of the tool that produced the file.
# The debug directory entry of type <see cref="Reproducible"/> must have all fields, except for Type zeroed.
Reproducible = 16
# The entry points to a blob containing Embedded Portable PDB.
# The Embedded Portable PDB blob has the following format:
# blob ::= uncompressed-size data
# Data spans the remainder of the blob and contains a Deflate-compressed Portable PDB.
EmbeddedPortablePdb = 17
}
[Flags()]
enum Characteristics {
RelocsStripped = 0x0001 # Relocation info stripped from file.
ExecutableImage = 0x0002 # File is executable (i.e. no unresolved external references).
LineNumsStripped = 0x0004 # Line numbers stripped from file.
LocalSymsStripped = 0x0008 # Local symbols stripped from file.
AggressiveWSTrim = 0x0010 # Aggressively trim working set
LargeAddressAware = 0x0020 # App can handle >2gb addresses
BytesReversedLo = 0x0080 # Bytes of machine word are reversed.
Bit32Machine = 0x0100 # 32 bit word machine.
DebugStripped = 0x0200 # Debugging info stripped from file in .DBG file
RemovableRunFromSwap = 0x0400 # If Image is on removable media copy and run from the swap file.
NetRunFromSwap = 0x0800 # If Image is on Net copy and run from the swap file.
System = 0x1000 # System File.
Dll = 0x2000 # File is a DLL.
UpSystemOnly = 0x4000 # File should only be run on a UP machine
BytesReversedHi = 0x8000 # Bytes of machine word are reversed.
}
enum PEMagic {
PE32 = 0x010B
PE32Plus = 0x020B
}
enum Subsystem {
Unknown = 0 # Unknown subsystem.
Native = 1 # Image doesn't require a subsystem.
WindowsGui = 2 # Image runs in the Windows GUI subsystem.
WindowsCui = 3 # Image runs in the Windows character subsystem.
OS2Cui = 5 # image runs in the OS/2 character subsystem.
PosixCui = 7 # image runs in the Posix character subsystem.
NativeWindows = 8 # image is a native Win9x driver.
WindowsCEGui = 9 # Image runs in the Windows CE subsystem.
EfiApplication = 10 # Extensible Firmware Interface (EFI) application.
EfiBootServiceDriver = 11 # EFI driver with boot services.
EfiRuntimeDriver = 12 # EFI driver with run-time services.
EfiRom = 13 # EFI ROM image.
Xbox = 14 # XBox system.
WindowsBootApplication = 16 # Boot application.
}
[Flags()]
enum DllCharacteristics {
ProcessInit = 0x0001
ProcessTerm = 0x0002
ThreadInit = 0x0004
ThreadTerm = 0x0008
HighEntropyVirtualAddressSpace = 0x0020
DynamicBase = 0x0040
CheckIntegrity = 0x0080
NxCompatible = 0x0100
NoIsolation = 0x0200
NoSeh = 0x0400
NoBind = 0x0800
AppContainer = 0x1000
WdmDriver = 0x2000
ControlFlowGuard = 0x4000
TerminalServerAware = 0x8000
}
[Flags()]
enum SectionCharacteristics {
TypeReg = 0x00000000 # Reserved.
TypeDSect = 0x00000001 # Reserved.
TypeNoLoad = 0x00000002 # Reserved.
TypeGroup = 0x00000004 # Reserved.
TypeNoPad = 0x00000008 # Reserved.
TypeCopy = 0x00000010 # Reserved.
ContainsCode = 0x00000020 # Section contains code.
ContainsInitializedData = 0x00000040 # Section contains initialized data.
ContainsUninitializedData = 0x00000080 # Section contains uninitialized data.
LinkerOther = 0x00000100 # Reserved.
LinkerInfo = 0x00000200 # Section contains comments or some other type of information.
TypeOver = 0x00000400 # Reserved.
LinkerRemove = 0x00000800 # Section contents will not become part of image.
LinkerComdat = 0x00001000 # Section contents comdat.
# 0x00002000 # Reserved.
MemProtected = 0x00004000
NoDeferSpecExc = 0x00004000 # Reset speculative exceptions handling bits in the TLB entries for this section.
GPRel = 0x00008000 # Section content can be accessed relative to GP
MemFardata = 0x00008000
MemSysheap = 0x00010000
MemPurgeable = 0x00020000
Mem16Bit = 0x00020000
MemLocked = 0x00040000
MemPreload = 0x00080000
Align1Bytes = 0x00100000 #
Align2Bytes = 0x00200000 #
Align4Bytes = 0x00300000 #
Align8Bytes = 0x00400000 #
Align16Bytes = 0x00500000 # Default alignment if no others are specified.
Align32Bytes = 0x00600000 #
Align64Bytes = 0x00700000 #
Align128Bytes = 0x00800000 #
Align256Bytes = 0x00900000 #
Align512Bytes = 0x00A00000 #
Align1024Bytes = 0x00B00000 #
Align2048Bytes = 0x00C00000 #
Align4096Bytes = 0x00D00000 #
Align8192Bytes = 0x00E00000 #
# Unused 0x00F00000
AlignMask = 0x00F00000
LinkerNRelocOvfl = 0x01000000 # Section contains extended relocations.
MemDiscardable = 0x02000000 # Section can be discarded.
MemNotCached = 0x04000000 # Section is not cachable.
MemNotPaged = 0x08000000 # Section is not pageable.
MemShared = 0x10000000 # Section is shareable.
MemExecute = 0x20000000 # Section is executable.
MemRead = 0x40000000 # Section is readable.
MemWrite = 0x80000000 # Section is writable.
}
enum Machine {
# The target CPU is unknown or not specified.
Unknown = 0x0000
# Intel 386.
I386 = 0x014C
# MIPS little-endian WCE v2
WceMipsV2 = 0x0169
# Alpha
Alpha = 0x0184
# Hitachi SH3 little endian
SH3 = 0x01a2
# Hitachi SH3 DSP.
SH3Dsp = 0x01a3
# Hitachi SH3 little endian.
SH3E = 0x01a4
# Hitachi SH4 little endian.
SH4 = 0x01a6
# Hitachi SH5.
SH5 = 0x01a8
# ARM little endian
Arm = 0x01c0
# arm 64
Arm64 = 0xaa64
# Thumb.
Thumb = 0x01c2
# ARM Thumb-2 little endian.
ArmThumb2 = 0x01c4
# Matsushita AM33.
AM33 = 0x01d3
# IBM PowerPC little endian.
PowerPC = 0x01F0
# PowerPCFP
PowerPCFP = 0x01f1
# Intel 64
IA64 = 0x0200
# MIPS
MIPS16 = 0x0266
# ALPHA64
Alpha64 = 0x0284
# MIPS with FPU.
MipsFpu = 0x0366
# MIPS16 with FPU.
MipsFpu16 = 0x0466
# Infineon
Tricore = 0x0520
# EFI Byte Code
Ebc = 0x0EBC
# AMD64 (K8)
Amd64 = 0x8664
# M32R little-endian
M32R = 0x9041
}
# Identifies the location, size and format of a block of debug information.
class DebugDirectoryEntry {
# The time and date that the debug data was created if the PE/COFF file is not deterministic,
# otherwise a value based on the hash of the content.
# The algorithm used to calculate this value is an implementation
# detail of the tool that produced the file.
[uint32] $Stamp
# The major version number of the debug data format.
[uint16] $MajorVersion
# The minor version number of the debug data format.
[uint16] $MinorVersion
# The format of debugging information.
[DebugDirectoryEntryType] $Type
# The size of the debug data (not including the debug directory itself).
[int] $DataSize
# The address of the debug data when loaded, relative to the image base.
[int] $DataRelativeVirtualAddress
# The file pointer to the debug data.
[int] $DataPointer
# True if the entry is a <see cref="DebugDirectoryEntryType.CodeView"/> entry pointing to a Portable PDB.
DebugDirectoryEntry([uint32] $stamp, [uint16] $majorVersion, [uint16] $minorVersion, [DebugDirectoryEntryType] $type,
[int] $dataSize, [int] $dataRelativeVirtualAddress, [int] $dataPointer) {
$this.Stamp = $stamp
$this.MajorVersion = $majorVersion
$this.MinorVersion = $minorVersion
$this.Type = $type
$this.DataSize = $dataSize
$this.DataRelativeVirtualAddress = $dataRelativeVirtualAddress
$this.DataPointer = $dataPointer
}
[string] ToString() { return $this.Type.ToString() }
static [CodeViewDebugDirectoryData] CodeViewDebugDirectoryData([BinaryReader] $reader, [int] $size) {
if ($reader.ReadByte() -ne [char]'R' -or
$reader.ReadByte() -ne [char]'S' -or
$reader.ReadByte() -ne [char]'D' -or
$reader.ReadByte() -ne [char]'S') {
throw [BadImageFormatException]::new('UnexpectedCodeViewDataSignature');
}
$guidBytes = $reader.ReadBytes(16)
$guid = [Guid]::new($guidBytes)
$age = $reader.ReadInt32();
$strLen = $size - 20
$bytes = $reader.ReadBytes($strLen)
for ($i = 0; $i -lt $bytes.Length; $i++) {
if ($bytes[$i] -eq 0) {
break
}
}
$path = [Text.Encoding]::UTF8.GetString($bytes, 0, $i)
return [CodeViewDebugDirectoryData]::new($guid, $age, $path);
}
}
class CodeViewDebugDirectoryData {
[Guid] $Guid
[int] $Age
[string] $Path
CodeViewDebugDirectoryData($guid, $age, $path) {
$this.Guid = $guid
$this.Age = $age
$this.Path = $path
}
}
function Get-PEHeader {
[CmdletBinding(DefaultParameterSetName = 'Path')]
[OutputType([PEHeaders])]
param(
[Parameter(Mandatory, ParameterSetName = 'Path', Position = 0)]
[string[]] $Path
,
[Alias('PSPath')]
[Parameter(ValueFromPipelineByPropertyName, Mandatory, ParameterSetName = 'LiteralPath')]
[string[]] $LiteralPath
)
process {
if ($PSCmdlet.ParameterSetName -eq 'LiteralPath') {
foreach ($lp in $LiteralPath) {
$p = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($lp)
try {
[PEHeaders]::new($p)
}
catch {
$pscmdlet.WriteError($_)
}
}
}
else {
$provider = $null
foreach ($pp in $Path) {
$resolved = $PSCmdlet.GetResolvedProviderPathFromPSPath($pp, [ref] $provider)
foreach ($r in $resolved) {
try {
[PEHeaders]::new($r)
}
catch {
$pscmdlet.WriteError($_)
}
}
}
}
}
}
function Remove-PEPDBPath {
[CmdletBinding(DefaultParameterSetName = 'Path')]
param(
[Parameter(Mandatory, ParameterSetName = 'Path', Position = 0)]
[string[]] $Path
,
[Alias('PSPath')]
[Parameter(ValueFromPipelineByPropertyName, Mandatory, ParameterSetName = 'LiteralPath')]
[string[]] $LiteralPath
)
process {
[PEHeaders[]] $pes = Get-PEHeader @PSBoundParameters
foreach ($pe in $pes) {
$dd = $pe.DebugDirectoryEntires.Where{$_.Type -eq 'CodeView'}
if (!$dd) {
Write-Error "Not a CodeView file" -TargetObject $pe
}
$s = [File]::OpenRead($pe.Path)
$r = [BinaryReader]::new($s)
$cve = $null
try {
$r.BaseStream.Position = $dd.DataPointer
$cve = [DebugDirectoryEntry]::CodeViewDebugDirectoryData($r, $dd.DataSize)
}
finally {
$r.Dispose()
}
$fs = [FileStream]::new($pe.Path, [FileMode]::Open, [FileAccess]::Write, [FileShare]::Read)
try {
$name = [io.Path]::GetFileName($cve.Path)
$nameBytes = [Text.Encoding]::UTF8.GetBytes($name)
$bytes = [byte[]]::new($dd.Datasize - 24)
[Array]::Copy($nameBytes, $bytes, $nameBytes.Length)
$fs.Position = $dd.DataPointer + 24
$fs.Write($bytes, 0, $bytes.Length)
$fs.Flush()
}
finally {
$fs.Dispose()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment