Last active
July 10, 2018 14:14
-
-
Save powercode/e61747c36c030051965284d37572e0e4 to your computer and use it in GitHub Desktop.
PE Header reader for PowreShell
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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