aboutsummaryrefslogtreecommitdiff
path: root/PETools
diff options
context:
space:
mode:
Diffstat (limited to 'PETools')
-rw-r--r--PETools/Get-LibSymbols.format.ps1xml31
-rw-r--r--PETools/Get-LibSymbols.ps1282
-rw-r--r--PETools/Get-ObjDump.format.ps1xml292
-rw-r--r--PETools/Get-ObjDump.ps1708
-rw-r--r--PETools/Get-PEArchitecture.ps194
-rw-r--r--PETools/Get-PEHeader.ps190
-rw-r--r--PETools/PETools.format.ps1xml2
-rw-r--r--PETools/PETools.psd19
-rw-r--r--PETools/PETools.psm12
9 files changed, 1383 insertions, 127 deletions
diff --git a/PETools/Get-LibSymbols.format.ps1xml b/PETools/Get-LibSymbols.format.ps1xml
new file mode 100644
index 0000000..5d4d848
--- /dev/null
+++ b/PETools/Get-LibSymbols.format.ps1xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<Configuration>
+ <ViewDefinitions>
+ <View>
+ <Name>SymbolTypeView</Name>
+ <ViewSelectedBy>
+ <TypeName>COFF.SymbolInfo</TypeName>
+ </ViewSelectedBy>
+ <ListControl>
+ <ListEntries>
+ <ListEntry>
+ <ListItems>
+ <ListItem>
+ <PropertyName>SymbolType</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>Module</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>DecoratedName</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>UndecoratedName</PropertyName>
+ </ListItem>
+ </ListItems>
+ </ListEntry>
+ </ListEntries>
+ </ListControl>
+ </View>
+ </ViewDefinitions>
+</Configuration>
diff --git a/PETools/Get-LibSymbols.ps1 b/PETools/Get-LibSymbols.ps1
new file mode 100644
index 0000000..9864676
--- /dev/null
+++ b/PETools/Get-LibSymbols.ps1
@@ -0,0 +1,282 @@
+function Get-LibSymbols
+{
+<#
+.SYNOPSIS
+
+ Displays symbolic information from Windows lib files.
+
+ PowerSploit Function: Get-LibSymbols
+ Author: Matthew Graeber (@mattifestation)
+ License: BSD 3-Clause
+ Required Dependencies: None
+ Optional Dependencies: None
+
+.DESCRIPTION
+
+ Get-LibSymbols parses and returns symbols in Windows .lib files
+ in both decorated and undecorated form (for C++ functions).
+
+.PARAMETER Path
+
+ Specifies a path to one or more lib file locations.
+
+.EXAMPLE
+
+ C:\PS>Get-LibSymbols -Path msvcrt.lib
+
+.EXAMPLE
+
+ C:\PS>ls *.lib | Get-LibSymbols
+
+.INPUTS
+
+ System.String[]
+
+ You can pipe a file system path (in quotation marks) to Get-LibSymbols.
+
+.OUTPUTS
+
+ COFF.SymbolInfo
+
+.LINK
+
+ http://www.exploit-monday.com/
+#>
+ [CmdletBinding()] Param (
+ [Parameter(Position = 0, Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
+ [ValidateScript({ Test-Path $_ })]
+ [Alias('FullName')]
+ [String[]]
+ $Path
+ )
+
+ BEGIN
+ {
+ $Code = @'
+ using System;
+ using System.IO;
+ using System.Text;
+ using System.Runtime.InteropServices;
+
+ namespace COFF
+ {
+ public class HEADER
+ {
+ public ushort Machine;
+ public ushort NumberOfSections;
+ public DateTime TimeDateStamp;
+ public uint PointerToSymbolTable;
+ public uint NumberOfSymbols;
+ public ushort SizeOfOptionalHeader;
+ public ushort Characteristics;
+
+ public HEADER(BinaryReader br)
+ {
+ this.Machine = br.ReadUInt16();
+ this.NumberOfSections = br.ReadUInt16();
+ this.TimeDateStamp = (new DateTime(1970, 1, 1, 0, 0, 0)).AddSeconds(br.ReadUInt32());
+ this.PointerToSymbolTable = br.ReadUInt32();
+ this.NumberOfSymbols = br.ReadUInt32();
+ this.SizeOfOptionalHeader = br.ReadUInt16();
+ this.Characteristics = br.ReadUInt16();
+ }
+ }
+
+ public class IMAGE_ARCHIVE_MEMBER_HEADER
+ {
+ public string Name;
+ public DateTime Date;
+ public ulong Size;
+ public string EndHeader;
+
+ public IMAGE_ARCHIVE_MEMBER_HEADER(BinaryReader br)
+ {
+ string tempName = Encoding.UTF8.GetString(br.ReadBytes(16));
+ DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);
+ this.Name = tempName.Substring(0, tempName.IndexOf((Char) 47));
+ this.Date = dt.AddSeconds(Convert.ToDouble(Encoding.UTF8.GetString(br.ReadBytes(12)).Split((Char) 20)[0]));
+ br.ReadBytes(20); // Skip over UserID, GroupID, and Mode. They are useless fields.
+ this.Size = Convert.ToUInt64(Encoding.UTF8.GetString(br.ReadBytes(10)).Split((Char) 20)[0]);
+ this.EndHeader = Encoding.UTF8.GetString(br.ReadBytes(2));
+ }
+ }
+
+ public class Functions
+ {
+ [DllImport("dbghelp.dll", SetLastError=true, PreserveSig=true)]
+ public static extern int UnDecorateSymbolName(
+ [In] [MarshalAs(UnmanagedType.LPStr)] string DecoratedName,
+ [Out] StringBuilder UnDecoratedName,
+ [In] [MarshalAs(UnmanagedType.U4)] uint UndecoratedLength,
+ [In] [MarshalAs(UnmanagedType.U4)] uint Flags);
+ }
+ }
+'@
+
+ Add-Type -TypeDefinition $Code
+
+ function Dispose-Objects
+ {
+ $BinaryReader.Close()
+ $FileStream.Dispose()
+ }
+ }
+
+ PROCESS
+ {
+ foreach ($File in $Path)
+ {
+ # Resolve the absolute path of the lib file. [IO.File]::OpenRead requires an absolute path.
+ $LibFilePath = Resolve-Path $File
+
+ # Pull out just the file name
+ $LibFileName = Split-Path $LibFilePath -Leaf
+
+ $IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR = 60
+ $IMAGE_ARCHIVE_START = "!<arch>`n" # Magic used for lib files
+ $IMAGE_SIZEOF_LIB_HDR = $IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR + $IMAGE_ARCHIVE_START.Length
+ $IMAGE_ARCHIVE_END = "```n" # Footer of an archive header
+ $SizeofCOFFFileHeader = 20
+
+ # Open the object file for reading
+ $FileStream = [IO.File]::OpenRead($LibFilePath)
+
+ $FileLength = $FileStream.Length
+
+ # Validate lib header size
+ if ($FileLength -lt $IMAGE_SIZEOF_LIB_HDR)
+ {
+ # You cannot parse the lib header if the file is not big enough to contain a lib header.
+ Write-Error "$($LibFileName) is too small to store a lib header."
+ $FileStream.Dispose()
+ return
+ }
+
+ # Open a BinaryReader object for the lib file
+ $BinaryReader = New-Object IO.BinaryReader($FileStream)
+
+ $ArchiveStart = [Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes(8))
+
+ if ($ArchiveStart -ne $IMAGE_ARCHIVE_START)
+ {
+ Write-Error "$($LibFileName) does not contain a valid lib header."
+ Dispose-Objects
+ return
+ }
+
+ # Parse the first archive header
+ $ArchiveHeader = New-Object COFF.IMAGE_ARCHIVE_MEMBER_HEADER($BinaryReader)
+
+ if ($ArchiveHeader.EndHeader -ne $IMAGE_ARCHIVE_END)
+ {
+ Write-Error "$($LibFileName) does not contain a valid lib header."
+ Dispose-Objects
+ return
+ }
+
+ # Check for the existence of symbols
+ if ($ArchiveHeader.Size -eq 0)
+ {
+ Write-Warning "$($LibFileName) contains no symbols."
+ Dispose-Objects
+ return
+ }
+
+ $NumberOfSymbols = $BinaryReader.ReadBytes(4)
+
+ # The offsets in the first archive header of a Microsoft lib file are stored in big-endian format
+ if ([BitConverter]::IsLittleEndian)
+ {
+ [Array]::Reverse($NumberOfSymbols)
+ }
+
+ $NumberOfSymbols = [BitConverter]::ToUInt32($NumberOfSymbols, 0)
+
+ $SymbolOffsets = New-Object UInt32[]($NumberOfSymbols)
+
+ foreach ($Offset in 0..($SymbolOffsets.Length - 1))
+ {
+ $SymbolOffset = $BinaryReader.ReadBytes(4)
+
+ if ([BitConverter]::IsLittleEndian)
+ {
+ [Array]::Reverse($SymbolOffset)
+ }
+
+ $SymbolOffsets[$Offset] = [BitConverter]::ToUInt32($SymbolOffset, 0)
+ }
+
+ $SymbolStringLength = $ArchiveHeader.Size + $IMAGE_SIZEOF_LIB_HDR - $FileStream.Position - 1
+ # $SymbolStrings = [Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes($SymbolStringLength)).Split([Char] 0)
+
+ # Write-Output $SymbolStrings
+
+ # There will be many duplicate offset entries. Remove them.
+ $SymbolOffsetsSorted = $SymbolOffsets | Sort-Object -Unique
+
+ $SymbolOffsetsSorted | ForEach-Object {
+ # Seek to the each repective offset in the file
+ $FileStream.Seek($_, 'Begin') | Out-Null
+
+ $ArchiveHeader = New-Object COFF.IMAGE_ARCHIVE_MEMBER_HEADER($BinaryReader)
+
+ # This is not a true COFF header. It's the same size and mostly resembles a standard COFF header
+ # but Microsoft placed a marker (0xFFFF) in the first WORD to indicate that the 'object file'
+ # consists solely of the module name and symbol.
+ $CoffHeader = New-Object COFF.HEADER($BinaryReader)
+
+ # Check for 0xFFFF flag value
+ if ($CoffHeader.NumberOfSections -eq [UInt16]::MaxValue)
+ {
+ # Get the total length of the module and symbol name
+ $SymbolStringLength = $CoffHeader.NumberOfSymbols
+ $Symbols = [Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes($SymbolStringLength)).Split([Char] 0)
+
+ $DecoratedSymbol = $Symbols[0]
+ $UndecoratedSymbol = ''
+
+ # Default to a 'C' type symbol unless it starts with a '?'
+ $SymbolType = 'C'
+
+ # Is the symbol a C++ type?
+ if ($DecoratedSymbol.StartsWith('?'))
+ {
+ $StrBuilder = New-Object Text.Stringbuilder(512)
+ # Magically undecorated the convoluted C++ symbol into a proper C++ function definition
+ [COFF.Functions]::UnDecorateSymbolName($DecoratedSymbol, $StrBuilder, $StrBuilder.Capacity, 0) | Out-Null
+ $UndecoratedSymbol = $StrBuilder.ToString()
+ $SymbolType = 'C++'
+ }
+ else
+ {
+ if ($DecoratedSymbol[0] -eq '_' -or $DecoratedSymbol[0] -eq '@')
+ {
+ $UndecoratedSymbol = $DecoratedSymbol.Substring(1).Split('@')[0]
+ }
+ else
+ {
+ $UndecoratedSymbol = $DecoratedSymbol.Split('@')[0]
+ }
+ }
+
+ $SymInfo = @{
+ DecoratedName = $DecoratedSymbol
+ UndecoratedName = $UndecoratedSymbol
+ Module = $Symbols[1]
+ SymbolType = $SymbolType
+ }
+
+ $ParsedSymbol = New-Object PSObject -Property $SymInfo
+ $ParsedSymbol.PSObject.TypeNames[0] = 'COFF.SymbolInfo'
+
+ Write-Output $ParsedSymbol
+ }
+ }
+
+ # Close file and binaryreader objects
+ Dispose-Objects
+ }
+ }
+
+ END {}
+}
diff --git a/PETools/Get-ObjDump.format.ps1xml b/PETools/Get-ObjDump.format.ps1xml
new file mode 100644
index 0000000..25e13b8
--- /dev/null
+++ b/PETools/Get-ObjDump.format.ps1xml
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<Configuration>
+ <ViewDefinitions>
+ <View>
+ <Name>ObjectFileView</Name>
+ <ViewSelectedBy>
+ <TypeName>COFF.OBJECT_FILE</TypeName>
+ </ViewSelectedBy>
+ <ListControl>
+ <ListEntries>
+ <ListEntry>
+ <ListItems>
+ <ListItem>
+ <PropertyName>COFFHeader</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>SectionHeaders</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>SymbolTable</PropertyName>
+ </ListItem>
+ </ListItems>
+ </ListEntry>
+ </ListEntries>
+ </ListControl>
+ </View>
+ <View>
+ <Name>COFFHeaderView</Name>
+ <ViewSelectedBy>
+ <TypeName>COFF.HEADER</TypeName>
+ </ViewSelectedBy>
+ <ListControl>
+ <ListEntries>
+ <ListEntry>
+ <ListItems>
+ <ListItem>
+ <PropertyName>Machine</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>NumberOfSections</PropertyName>
+ <FormatString>0x{0:X4}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>TimeDateStamp</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>PointerToSymbolTable</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>NumberOfSymbols</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>SizeOfOptionalHeader</PropertyName>
+ <FormatString>0x{0:X4}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>Characteristics</PropertyName>
+ </ListItem>
+ </ListItems>
+ </ListEntry>
+ </ListEntries>
+ </ListControl>
+ </View>
+ <View>
+ <Name>SectionHeaderView</Name>
+ <ViewSelectedBy>
+ <TypeName>COFF.SECTION_HEADER</TypeName>
+ </ViewSelectedBy>
+ <ListControl>
+ <ListEntries>
+ <ListEntry>
+ <ListItems>
+ <ListItem>
+ <PropertyName>Name</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>PhysicalAddress</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>VirtualSize</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>VirtualAddress</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>SizeOfRawData</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>PointerToRawData</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>PointerToRelocations</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>PointerToLinenumbers</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>NumberOfRelocations</PropertyName>
+ <FormatString>0x{0:X4}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>NumberOfLinenumbers</PropertyName>
+ <FormatString>0x{0:X4}</FormatString>
+ </ListItem>
+ <ListItem>
+ <PropertyName>Characteristics</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>RawData</PropertyName>
+ </ListItem>
+ <ListItem>
+ <PropertyName>Relocations</PropertyName>
+ </ListItem>
+ </ListItems>
+ </ListEntry>
+ </ListEntries>
+ </ListControl>
+ </View>
+ <View>
+ <Name>SymbolTableView</Name>
+ <ViewSelectedBy>
+ <TypeName>COFF.SYMBOL_TABLE</TypeName>
+ </ViewSelectedBy>
+ <TableControl>
+ <AutoSize/>
+ <TableHeaders>
+ <TableColumnHeader>
+ <Label>Name</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>Value</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>SectionNumber</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>Type</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>StorageClass</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>NumberOfAuxSymbols</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>AuxSymbols</Label>
+ </TableColumnHeader>
+ </TableHeaders>
+ <TableRowEntries>
+ <TableRowEntry>
+ <TableColumnItems>
+ <TableColumnItem>
+ <PropertyName>Name</PropertyName>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>Value</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>SectionNumber</PropertyName>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>Type</PropertyName>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>StorageClass</PropertyName>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>NumberOfAuxSymbols</PropertyName>
+ <FormatString>0x{0:X2}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>AuxSymbols</PropertyName>
+ </TableColumnItem>
+ </TableColumnItems>
+ </TableRowEntry>
+ </TableRowEntries>
+ </TableControl>
+ </View>
+ <View>
+ <Name>SectionDefinitionView</Name>
+ <ViewSelectedBy>
+ <TypeName>COFF.SECTION_DEFINITION</TypeName>
+ </ViewSelectedBy>
+ <TableControl>
+ <AutoSize/>
+ <TableHeaders>
+ <TableColumnHeader>
+ <Label>Length</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>NumberOfRelocations</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>NumberOfLinenumbers</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>CheckSum</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>Number</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>Selection</Label>
+ </TableColumnHeader>
+ </TableHeaders>
+ <TableRowEntries>
+ <TableRowEntry>
+ <TableColumnItems>
+ <TableColumnItem>
+ <PropertyName>Length</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>NumberOfRelocations</PropertyName>
+ <FormatString>0x{0:X4}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>NumberOfLinenumbers</PropertyName>
+ <FormatString>0x{0:X4}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>CheckSum</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>Number</PropertyName>
+ <FormatString>0x{0:X4}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>Selection</PropertyName>
+ <FormatString>0x{0:X2}</FormatString>
+ </TableColumnItem>
+ </TableColumnItems>
+ </TableRowEntry>
+ </TableRowEntries>
+ </TableControl>
+ </View>
+ <View>
+ <Name>RelocationView</Name>
+ <ViewSelectedBy>
+ <TypeName>COFF.RelocationEntry</TypeName>
+ </ViewSelectedBy>
+ <TableControl>
+ <AutoSize/>
+ <TableHeaders>
+ <TableColumnHeader>
+ <Label>VirtualAddress</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>SymbolTableIndex</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>Type</Label>
+ </TableColumnHeader>
+ <TableColumnHeader>
+ <Label>Name</Label>
+ </TableColumnHeader>
+ </TableHeaders>
+ <TableRowEntries>
+ <TableRowEntry>
+ <TableColumnItems>
+ <TableColumnItem>
+ <PropertyName>VirtualAddress</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>SymbolTableIndex</PropertyName>
+ <FormatString>0x{0:X8}</FormatString>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>Type</PropertyName>
+ </TableColumnItem>
+ <TableColumnItem>
+ <PropertyName>Name</PropertyName>
+ </TableColumnItem>
+ </TableColumnItems>
+ </TableRowEntry>
+ </TableRowEntries>
+ </TableControl>
+ </View>
+ </ViewDefinitions>
+</Configuration>
diff --git a/PETools/Get-ObjDump.ps1 b/PETools/Get-ObjDump.ps1
new file mode 100644
index 0000000..2a8ebcb
--- /dev/null
+++ b/PETools/Get-ObjDump.ps1
@@ -0,0 +1,708 @@
+function Get-ObjDump
+{
+<#
+.SYNOPSIS
+
+ Displays information about one or more Windows object files.
+
+ PowerSploit Function: Get-ObjDump
+ Author: Matthew Graeber (@mattifestation)
+ License: BSD 3-Clause
+ Required Dependencies: None
+ Optional Dependencies: None
+
+.DESCRIPTION
+
+ Get-ObjDump parses and returns nearly identical infomation as the dumpbin
+ utility. By nature of Get-ObjDump returning objects though, it lends itself
+ much better to manipulation since every field is an object.
+
+.PARAMETER Path
+
+ Specifies a path to one or more object file locations.
+
+.EXAMPLE
+
+ C:\PS>Get-ObjDump -Path main.obj
+
+.EXAMPLE
+
+ C:\PS>ls *.obj | Get-ObjDump
+
+.EXAMPLE
+
+ C:\PS>$ObjectFile = Get-ObjDump -Path shellcode.obj
+ C:\PS>$CodeBytes = $ObjectFile.SectionHeaders | ? {$_.Name -eq '.text'} | % {$_.RawData}
+
+ Description
+ -----------
+ Pulls the raw bytes out of the text section. Note that in this form,
+ no relocations have been fixed up.
+
+.INPUTS
+
+ System.String[]
+
+ You can pipe a file system path (in quotation marks) to Get-ObjDump.
+
+.OUTPUTS
+
+ COFF.OBJECT_FILE
+
+.LINK
+
+ http://www.exploit-monday.com/
+#>
+ [CmdletBinding()] Param (
+ [Parameter(Position = 0, Mandatory = $True, ValueFromPipeline = $True)]
+ [ValidateScript({ Test-Path $_ })]
+ [String[]]
+ $Path
+ )
+
+ BEGIN
+ {
+ $Code = @'
+ using System;
+ using System.IO;
+ using System.Text;
+
+ namespace COFF
+ {
+ public enum Machine : ushort
+ {
+ UNKNOWN = 0,
+ I386 = 0x014C, // Intel 386.
+ R3000 = 0x0162, // MIPS little-endian =0x160 big-endian
+ R4000 = 0x0166, // MIPS little-endian
+ R10000 = 0x0168, // MIPS little-endian
+ WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
+ ALPHA = 0x0184, // Alpha_AXP
+ SH3 = 0x01A2, // SH3 little-endian
+ SH3DSP = 0x01A3,
+ SH3E = 0x01A4, // SH3E little-endian
+ SH4 = 0x01A6, // SH4 little-endian
+ SH5 = 0x01A8, // SH5
+ ARM = 0x01C0, // ARM Little-Endian
+ THUMB = 0x01C2,
+ ARMV7 = 0x01C4, // ARM Thumb-2 Little-Endian
+ AM33 = 0x01D3,
+ POWERPC = 0x01F0, // IBM PowerPC Little-Endian
+ POWERPCFP = 0x01F1,
+ IA64 = 0x0200, // Intel 64
+ MIPS16 = 0x0266, // MIPS
+ ALPHA64 = 0x0284, // ALPHA64
+ MIPSFPU = 0x0366, // MIPS
+ MIPSFPU16 = 0x0466, // MIPS
+ AXP64 = ALPHA64,
+ TRICORE = 0x0520, // Infineon
+ CEF = 0x0CEF,
+ EBC = 0x0EBC, // EFI public byte Code
+ AMD64 = 0x8664, // AMD64 (K8)
+ M32R = 0x9041, // M32R little-endian
+ ARM64 = 0xAA64, // ARMv8 in 64-bit mode
+ CEE = 0xC0EE
+ }
+
+ [Flags]
+ public enum CoffHeaderCharacteristics : ushort
+ {
+ RELOCS_STRIPPED = 0x0001, // Relocation info stripped from file.
+ EXECUTABLE_IMAGE = 0x0002, // File is executable (i.e. no unresolved external references).
+ LINE_NUMS_STRIPPED = 0x0004, // Line nunbers stripped from file.
+ LOCAL_SYMS_STRIPPED = 0x0008, // Local symbols stripped from file.
+ AGGRESIVE_WS_TRIM = 0x0010, // Agressively trim working set
+ LARGE_ADDRESS_AWARE = 0x0020, // App can handle >2gb addresses
+ REVERSED_LO = 0x0080, // public bytes of machine public ushort are reversed.
+ BIT32_MACHINE = 0x0100, // 32 bit public ushort machine.
+ DEBUG_STRIPPED = 0x0200, // Debugging info stripped from file in .DBG file
+ REMOVABLE_RUN_FROM_SWAP = 0x0400, // If Image is on removable media =copy and run from the swap file.
+ NET_RUN_FROM_SWAP = 0x0800, // If Image is on Net =copy and run from the swap file.
+ SYSTEM = 0x1000, // System File.
+ DLL = 0x2000, // File is a DLL.
+ UP_SYSTEM_ONLY = 0x4000, // File should only be run on a UP machine
+ REVERSED_HI = 0x8000 // public bytes of machine public ushort are reversed.
+ }
+
+ public class HEADER
+ {
+ public Machine Machine;
+ public ushort NumberOfSections;
+ public DateTime TimeDateStamp;
+ public uint PointerToSymbolTable;
+ public uint NumberOfSymbols;
+ public ushort SizeOfOptionalHeader;
+ public CoffHeaderCharacteristics Characteristics;
+
+ public HEADER(BinaryReader br)
+ {
+ this.Machine = (Machine) br.ReadUInt16();
+ this.NumberOfSections = br.ReadUInt16();
+ this.TimeDateStamp = (new DateTime(1970, 1, 1, 0, 0, 0)).AddSeconds(br.ReadUInt32());
+ this.PointerToSymbolTable = br.ReadUInt32();
+ this.NumberOfSymbols = br.ReadUInt32();
+ this.SizeOfOptionalHeader = br.ReadUInt16();
+ this.Characteristics = (CoffHeaderCharacteristics) br.ReadUInt16();
+ }
+ }
+
+ [Flags]
+ public enum SectionHeaderCharacteristics : uint
+ {
+ TYPE_NO_PAD = 0x00000008, // Reserved.
+ CNT_CODE = 0x00000020, // Section contains code.
+ CNT_INITIALIZED_DATA = 0x00000040, // Section contains initialized data.
+ CNT_UNINITIALIZED_DATA = 0x00000080, // Section contains uninitialized data.
+ LNK_INFO = 0x00000200, // Section contains comments or some other type of information.
+ LNK_REMOVE = 0x00000800, // Section contents will not become part of image.
+ LNK_COMDAT = 0x00001000, // Section contents comdat.
+ NO_DEFER_SPEC_EXC = 0x00004000, // Reset speculative exceptions handling bits in the TLB entries for this section.
+ GPREL = 0x00008000, // Section content can be accessed relative to GP
+ MEM_FARDATA = 0x00008000,
+ MEM_PURGEABLE = 0x00020000,
+ MEM_16BIT = 0x00020000,
+ MEM_LOCKED = 0x00040000,
+ MEM_PRELOAD = 0x00080000,
+ ALIGN_1BYTES = 0x00100000,
+ ALIGN_2BYTES = 0x00200000,
+ ALIGN_4BYTES = 0x00300000,
+ ALIGN_8BYTES = 0x00400000,
+ ALIGN_16BYTES = 0x00500000, // Default alignment if no others are specified.
+ ALIGN_32BYTES = 0x00600000,
+ ALIGN_64BYTES = 0x00700000,
+ ALIGN_128BYTES = 0x00800000,
+ ALIGN_256BYTES = 0x00900000,
+ ALIGN_512BYTES = 0x00A00000,
+ ALIGN_1024BYTES = 0x00B00000,
+ ALIGN_2048BYTES = 0x00C00000,
+ ALIGN_4096BYTES = 0x00D00000,
+ ALIGN_8192BYTES = 0x00E00000,
+ ALIGN_MASK = 0x00F00000,
+ LNK_NRELOC_OVFL = 0x01000000, // Section contains extended relocations.
+ MEM_DISCARDABLE = 0x02000000, // Section can be discarded.
+ MEM_NOT_CACHED = 0x04000000, // Section is not cachable.
+ MEM_NOT_PAGED = 0x08000000, // Section is not pageable.
+ MEM_SHARED = 0x10000000, // Section is shareable.
+ MEM_EXECUTE = 0x20000000, // Section is executable.
+ MEM_READ = 0x40000000, // Section is readable.
+ MEM_WRITE = 0x80000000 // Section is writeable.
+ }
+
+ public enum AMD64RelocationType : ushort
+ {
+ ABSOLUTE,
+ ADDR64,
+ ADDR32,
+ ADDR32NB,
+ REL32,
+ REL32_1,
+ REL32_2,
+ REL32_3,
+ REL32_4,
+ REL32_5,
+ SECTION,
+ SECREL,
+ SECREL7,
+ TOKEN,
+ SREL32,
+ PAIR,
+ SSPAN32
+ }
+
+ public enum ARMRelocationType : ushort
+ {
+ ABSOLUTE,
+ ADDR32,
+ ADDR32NB,
+ BRANCH24,
+ BRANCH11,
+ TOKEN,
+ BLX24 = 0x08,
+ BLX11 = 0x09,
+ SECTION = 0x0E,
+ SECREL = 0x0F,
+ MOV32A = 0x10,
+ MOV32T = 0x11,
+ BRANCH20T = 0x12,
+ BRANCH24T = 0x14,
+ BLX23T = 0x15
+ }
+
+ public enum ARMv8RelocationType : ushort
+ {
+ ABSOLUTE,
+ ADDR32,
+ ADDR32NB,
+ BRANCH26,
+ PAGEBASE_REL21,
+ REL21,
+ PAGEOFFSET_12A,
+ PAGEOFFSET_12L,
+ SECREL,
+ SECREL_LOW12A,
+ SECREL_HIGH12A,
+ SECREL_LOW12L,
+ TOKEN,
+ SECTION,
+ ADDR64
+ }
+
+ public enum X86RelocationType : ushort
+ {
+ ABSOLUTE,
+ DIR16,
+ DIR32 = 0x06,
+ DIR32NB = 0x07,
+ SEG12 = 0x09,
+ SECTION = 0x0A,
+ SECREL = 0x0B,
+ TOKEN = 0x0C,
+ SECREL7 = 0x0D,
+ REL32 = 0x14
+ }
+
+ public class RelocationEntry
+ {
+ public uint VirtualAddress;
+ public uint SymbolTableIndex;
+ public Enum Type;
+ public string Name;
+
+ public RelocationEntry(BinaryReader br)
+ {
+ this.VirtualAddress = br.ReadUInt32();
+ this.SymbolTableIndex = br.ReadUInt32();
+ // Default to X86RelocationType. This will be changed once the processor type is determined
+ this.Type = (X86RelocationType) br.ReadUInt16();
+ }
+ }
+
+ public class SECTION_HEADER
+ {
+ public string Name;
+ public uint PhysicalAddress;
+ public uint VirtualSize;
+ public uint VirtualAddress;
+ public uint SizeOfRawData;
+ public uint PointerToRawData;
+ public uint PointerToRelocations;
+ public uint PointerToLinenumbers;
+ public ushort NumberOfRelocations;
+ public ushort NumberOfLinenumbers;
+ public SectionHeaderCharacteristics Characteristics;
+ public Byte[] RawData;
+ public RelocationEntry[] Relocations;
+
+ public SECTION_HEADER(BinaryReader br)
+ {
+ this.Name = Encoding.UTF8.GetString(br.ReadBytes(8)).Split((Char) 0)[0];
+ this.PhysicalAddress = br.ReadUInt32();
+ this.VirtualSize = this.PhysicalAddress;
+ this.VirtualAddress = br.ReadUInt32();
+ this.SizeOfRawData = br.ReadUInt32();
+ this.PointerToRawData = br.ReadUInt32();
+ this.PointerToRelocations = br.ReadUInt32();
+ this.PointerToLinenumbers = br.ReadUInt32();
+ this.NumberOfRelocations = br.ReadUInt16();
+ this.NumberOfLinenumbers = br.ReadUInt16();
+ this.Characteristics = (SectionHeaderCharacteristics) br.ReadUInt32();
+ }
+ }
+
+ public enum SectionNumber : short
+ {
+ UNDEFINED,
+ ABSOLUTE = -1,
+ DEBUG = -2
+ }
+
+ [Flags]
+ public enum TypeClass : short
+ {
+ TYPE_NULL,
+ TYPE_VOID,
+ TYPE_CHAR,
+ TYPE_SHORT,
+ TYPE_INT,
+ TYPE_LONG,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_STRUCT,
+ TYPE_UNION,
+ TYPE_ENUM,
+ TYPE_MOE,
+ TYPE_BYTE,
+ TYPE_WORD,
+ TYPE_UINT,
+ TYPE_DWORD,
+ DTYPE_POINTER = 0x100,
+ DTYPE_FUNCTION = 0x200,
+ DTYPE_ARRAY = 0x300,
+ DTYPE_NULL = 0x400 // Technically, this is defined as 0 in the MSB
+ }
+
+ public enum StorageClass : byte
+ {
+ NULL,
+ AUTOMATIC,
+ EXTERNAL,
+ STATIC,
+ REGISTER,
+ EXTERNAL_DEF,
+ LABEL,
+ UNDEFINED_LABEL,
+ MEMBER_OF_STRUCT,
+ ARGUMENT,
+ STRUCT_TAG,
+ MEMBER_OF_UNION,
+ UNION_TAG,
+ TYPE_DEFINITION,
+ ENUM_TAG,
+ MEMBER_OF_ENUM,
+ REGISTER_PARAM,
+ BIT_FIELD,
+ BLOCK = 0x64,
+ FUNCTION = 0x65,
+ END_OF_STRUCT = 0x66,
+ FILE = 0x67,
+ SECTION = 0x68,
+ WEAK_EXTERNAL = 0x69,
+ CLR_TOKEN = 0x6B,
+ END_OF_FUNCTION = 0xFF
+ }
+
+ public class SYMBOL_TABLE
+ {
+ public string Name;
+ public uint Value;
+ public SectionNumber SectionNumber;
+ public TypeClass Type;
+ public StorageClass StorageClass;
+ public byte NumberOfAuxSymbols;
+ public Object AuxSymbols;
+ private Byte[] NameArray;
+
+ public SYMBOL_TABLE(BinaryReader br)
+ {
+ this.NameArray = br.ReadBytes(8);
+
+ if (this.NameArray[0] == 0 && this.NameArray[1] == 0 &&this.NameArray[2] == 0 &&this.NameArray[3] == 0)
+ {
+ // Per specification, if the high DWORD is 0, then then low DWORD is an index into the string table
+ this.Name = "/" + BitConverter.ToInt32(NameArray, 4).ToString();
+ }
+ else
+ {
+ this.Name = Encoding.UTF8.GetString(NameArray).Trim(((char) 0));
+ }
+
+ this.Value = br.ReadUInt32();
+ this.SectionNumber = (SectionNumber) br.ReadInt16();
+ this.Type = (TypeClass) br.ReadInt16();
+ if ((((int) this.Type) & 0xff00) == 0) { this.Type = (TypeClass) Enum.Parse(typeof(TypeClass), ((int) this.Type | 0x400).ToString());}
+ this.StorageClass = (StorageClass) br.ReadByte();
+ this.NumberOfAuxSymbols = br.ReadByte();
+ }
+ }
+
+ public class SECTION_DEFINITION
+ {
+ public uint Length;
+ public ushort NumberOfRelocations;
+ public ushort NumberOfLinenumbers;
+ public uint CheckSum;
+ public ushort Number;
+ public byte Selection;
+
+ public SECTION_DEFINITION(BinaryReader br)
+ {
+ this.Length = br.ReadUInt32();
+ this.NumberOfRelocations = br.ReadUInt16();
+ this.NumberOfLinenumbers = br.ReadUInt16();
+ this.CheckSum = br.ReadUInt32();
+ this.Number = br.ReadUInt16();
+ this.Selection = br.ReadByte();
+ br.ReadBytes(3);
+ }
+ }
+ }
+'@
+
+ Add-Type -TypeDefinition $Code
+
+ function Dispose-Objects
+ {
+ $BinaryReader.Dispose()
+ $FileStream.Dispose()
+ }
+ }
+
+ PROCESS
+ {
+ foreach ($File in $Path) {
+
+ # Resolve the absolute path of the object file. [IO.File]::OpenRead requires an absolute path.
+ $ObjFilePath = Resolve-Path $File
+
+ # Pull out just the file name
+ $ObjFileName = Split-Path $ObjFilePath -Leaf
+
+ # Fixed structure sizes
+ $SizeofCOFFFileHeader = 20
+ $SizeofSectionHeader = 40
+ $SizeofSymbolTableEntry = 18
+ $SizeofRelocationEntry = 10
+
+ # Open the object file for reading
+ $FileStream = [IO.File]::OpenRead($ObjFilePath)
+
+ $FileLength = $FileStream.Length
+
+ if ($FileLength -lt $SizeofCOFFFileHeader)
+ {
+ # You cannot parse the COFF header if the file is not big enough to contain a COFF header.
+ Write-Error "$($ObjFileName) is too small to store a COFF header."
+ Dispose-Objects
+ return
+ }
+
+ # Open a BinaryReader object for the object file
+ $BinaryReader = New-Object IO.BinaryReader($FileStream)
+
+ # Parse the COFF header
+ $CoffHeader = New-Object COFF.HEADER($BinaryReader)
+
+ if ($CoffHeader.SizeOfOptionalHeader -ne 0)
+ {
+ # Per the PECOFF specification, an object file does not have an optional header
+ Write-Error "Coff header indicates the existence of an optional header. An object file cannot have an optional header."
+ Dispose-Objects
+ return
+ }
+
+ if ($CoffHeader.PointerToSymbolTable -eq 0)
+ {
+ Write-Error 'An object file is supposed to have a symbol table.'
+ Dispose-Objects
+ return
+ }
+
+ if ($FileLength -lt (($CoffHeader.NumberOfSections * $SizeofSectionHeader) + $SizeofCOFFFileHeader))
+ {
+ # The object file isn't big enough to store the number of sections present.
+ Write-Error "$($ObjFileName) is too small to store section header data."
+ Dispose-Objects
+ return
+ }
+
+ # A string collection used to store section header names. This collection is referenced while
+ # parsing the symbol table entries whose name is the same as the section header. In this case,
+ # the symbol entry will have a particular auxiliary symbol table entry.
+ $SectionHeaderNames = New-Object Collections.Specialized.StringCollection
+
+ # Correlate the processor type to the relocation type. There are more relocation type defined
+ # in the PECOFF specification, but I don't expect those to be present. In that case, relocation
+ # entries default to X86RelocationType.
+ $SectionHeaders = New-Object COFF.SECTION_HEADER[]($CoffHeader.NumberOfSections)
+ $MachineTypes = @{ [COFF.Machine]::I386 = [COFF.X86RelocationType]
+ [COFF.Machine]::AMD64 = [COFF.AMD64RelocationType]
+ [COFF.Machine]::ARMV7 = [COFF.ARMRelocationType]
+ [COFF.Machine]::ARM64 = [COFF.ARMv8RelocationType] }
+
+ # Parse section headers
+ for ($i = 0; $i -lt $CoffHeader.NumberOfSections; $i++)
+ {
+ $SectionHeaders[$i] = New-Object COFF.SECTION_HEADER($BinaryReader)
+
+ # Add the section name to the string collection. This will be referenced during symbol table parsing.
+ $SectionHeaderNames.Add($SectionHeaders[$i].Name) | Out-Null
+
+ # Save the current filestream position. We are about to jump out of place.
+ $SavedFilePosition = $FileStream.Position
+
+ # Check to see if the raw data points beyond the actual file size
+ if (($SectionHeaders[$i].PointerToRawData + $SectionHeaders[$i].SizeOfRawData) -gt $FileLength)
+ {
+ Write-Error "$($SectionHeaders[$i].Name) section header's raw data exceeds the size of the object file."
+ return
+ }
+ else
+ {
+ # Read the raw data into a byte array
+ $FileStream.Seek($SectionHeaders[$i].PointerToRawData, 'Begin') | Out-Null
+ $SectionHeaders[$i].RawData = $BinaryReader.ReadBytes($SectionHeaders[$i].SizeOfRawData)
+ }
+
+ # Check to see if the section has a relocation table
+ if ($SectionHeaders[$i].PointerToRelocations -and $SectionHeaders[$i].NumberOfRelocations)
+ {
+ # Check to see if the relocation entries point beyond the actual file size
+ if (($SectionHeaders[$i].PointerToRelocations + ($SizeofRelocationEntry * $SectionHeaders[$i].NumberOfRelocations)) -gt $FileLength)
+ {
+ Write-Error "$($SectionHeaders[$i].Name) section header's relocation entries exceeds the soze of the object file."
+ return
+ }
+
+ $FileStream.Seek($SectionHeaders[$i].PointerToRelocations, 'Begin') | Out-Null
+
+ $Relocations = New-Object COFF.RelocationEntry[]($SectionHeaders[$i].NumberOfRelocations)
+
+ for ($j = 0; $j -lt $SectionHeaders[$i].NumberOfRelocations; $j++)
+ {
+ $Relocations[$j] = New-Object COFF.RelocationEntry($BinaryReader)
+ # Cast the relocation as its respective type
+ $Relocations[$j].Type = ($Relocations[$j].Type.value__ -as $MachineTypes[$CoffHeader.Machine])
+ }
+
+ # Add the relocation table entry to the section header
+ $SectionHeaders[$i].Relocations = $Relocations
+ }
+
+ # Restore the original filestream pointer
+ $FileStream.Seek($SavedFilePosition, 'Begin') | Out-Null
+ }
+
+ # Retrieve the contents of the COFF string table
+ $SymTableSize = $CoffHeader.NumberOfSymbols * $SizeofSymbolTableEntry
+ $StringTableOffset = $CoffHeader.PointerToSymbolTable + $SymTableSize
+
+ if ($StringTableOffset -gt $FileLength)
+ {
+ Write-Error 'The string table points beyond the end of the file.'
+ Dispose-Objects
+ return
+ }
+
+ $FileStream.Seek($StringTableOffset, 'Begin') | Out-Null
+ $StringTableLength = $BinaryReader.ReadUInt32()
+
+ if ($StringTableLength -gt $FileLength)
+ {
+ Write-Error "The string table's length exceeds the length of the file."
+ Dispose-Objects
+ return
+ }
+
+ $StringTable = [Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes($StringTableLength))
+
+ $RawSymbolTable = New-Object COFF.SYMBOL_TABLE[]($CoffHeader.NumberOfSymbols)
+
+ # Retrieve the symbol table
+ if ($FileLength -lt $StringTableOffset)
+ {
+ "Symbol table is larger than the file size."
+ return
+ }
+
+ $FileStream.Seek($CoffHeader.PointerToSymbolTable, 'Begin') | Out-Null
+ $NumberofRegularSymbols = 0
+
+ <#
+ Go through each symbol table looking for auxiliary symbols to parse
+
+ Currently supported auxiliary symbol table entry formats:
+ 1) .file
+ 2) Entry names that match the name of a section header
+ #>
+ for ($i = 0; $i -lt $CoffHeader.NumberOfSymbols; $i++)
+ {
+ # Parse the symbol tables regardless of whether they are normal or auxiliary symbols
+ $RawSymbolTable[$i] = New-Object COFF.SYMBOL_TABLE($BinaryReader)
+
+ if ($RawSymbolTable[$i].NumberOfAuxSymbols -eq 0)
+ {
+ # This symbol table entry has no auxiliary symbols
+ $NumberofRegularSymbols++
+ }
+ elseif ($RawSymbolTable[$i].Name -eq '.file')
+ {
+ $TempPosition = $FileStream.Position # Save filestream position
+ # Retrieve the file name
+ $RawSymbolTable[$i].AuxSymbols = [Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes($RawSymbolTable[$i].NumberOfAuxSymbols * $SizeofSymbolTableEntry)).TrimEnd(([Char] 0))
+ $FileStream.Seek($TempPosition, 'Begin') | Out-Null # Restore filestream position
+ }
+ elseif ($SectionHeaderNames.Contains($RawSymbolTable[$i].Name))
+ {
+ $TempPosition = $FileStream.Position # Save filestream position
+ $RawSymbolTable[$i].AuxSymbols = New-Object COFF.SECTION_DEFINITION($BinaryReader)
+ $FileStream.Seek($TempPosition, 'Begin') | Out-Null # Restore filestream position
+ }
+ }
+
+ # Create an array of symbol table entries without auxiliary table entries
+ $SymbolTable = New-Object COFF.SYMBOL_TABLE[]($NumberofRegularSymbols)
+ $j = 0
+
+ for ($i = 0; $i -lt $CoffHeader.NumberOfSymbols; $i++)
+ {
+ $SymbolTable[$j] = $RawSymbolTable[$i] # FYI, the first symbol table entry will never be an aux symbol
+ $j++
+
+ # Skip over the auxiliary symbols
+ if ($RawSymbolTable[$i].NumberOfAuxSymbols -ne 0)
+ {
+ $i += $RawSymbolTable[$i].NumberOfAuxSymbols
+ }
+ }
+
+ # Dispose the binaryreader and filestream objects
+ Dispose-Objects
+
+ # Fix the section names if any of them point to the COFF string table
+ for ($i = 0; $i -lt $CoffHeader.NumberOfSections; $i++)
+ {
+ if ($SectionHeaders[$i].Name.IndexOf('/') -eq 0)
+ {
+ $StringTableIndex = $SectionHeaders[$i].Name.SubString(1)
+
+ if ($StringTableIndex -match '^[1-9][0-9]*$')
+ {
+ $StringTableIndex = ([Int] $StringTableIndex) - 4
+
+ if ($StringTableIndex -gt ($StringTableLength + 4))
+ {
+ Write-Error 'String table entry exceeds the bounds of the object file.'
+ }
+
+ $Length = $StringTable.IndexOf(([Char] 0), $StringTableIndex)
+ $SectionHeaders[$i].Name = $StringTable.Substring($StringTableIndex, $Length)
+ }
+ }
+ }
+
+ # Fix the symbol table names
+ for ($i = 0; $i -lt $SymbolTable.Length; $i++)
+ {
+ if ($SymbolTable[$i].Name.IndexOf('/') -eq 0)
+ {
+ $StringTableIndex = $SymbolTable[$i].Name.SubString(1)
+
+ if ($StringTableIndex -match '^[1-9][0-9]*$')
+ {
+ $StringTableIndex = ([Int] $StringTableIndex) - 4
+ $Length = $StringTable.IndexOf(([Char] 0), $StringTableIndex) - $StringTableIndex
+ $SymbolTable[$i].Name = $StringTable.Substring($StringTableIndex, $Length)
+ }
+ }
+ }
+
+ # Apply symbol names to the relocation entries
+ $SectionHeaders | Where-Object { $_.Relocations } | % {
+ $_.Relocations | % { $_.Name = $RawSymbolTable[$_.SymbolTableIndex].Name }
+ }
+
+ $Result = @{
+ COFFHeader = $CoffHeader
+ SectionHeaders = $SectionHeaders
+ SymbolTable = $SymbolTable
+ }
+
+ $ParsedObjectFile = New-Object PSObject -Property $Result
+ $ParsedObjectFile.PSObject.TypeNames[0] = 'COFF.OBJECT_FILE'
+ Write-Output $ParsedObjectFile
+
+ }
+ }
+
+ END {}
+}
diff --git a/PETools/Get-PEArchitecture.ps1 b/PETools/Get-PEArchitecture.ps1
deleted file mode 100644
index efc80be..0000000
--- a/PETools/Get-PEArchitecture.ps1
+++ /dev/null
@@ -1,94 +0,0 @@
-function Get-PEArchitecture
-{
-<#
-.SYNOPSIS
-
-Outputs the architecture for which a binary was compiled.
-
-PowerSploit Function: Get-PEArchitecture
-Author: Matthew Graeber (@mattifestation)
-License: BSD 3-Clause
-Required Dependencies: None
-Optional Dependencies: None
-
-.DESCRIPTION
-
-Get-PEArchitecture returns the architecture for which a Windows portable executable was compiled.
-
-.PARAMETER Path
-
-Path to the executable.
-
-.EXAMPLE
-
-C:\PS> Get-PEArchitecture C:\Windows\SysWOW64\calc.exe
-X86
-
-.EXAMPLE
-
-C:\PS> Get-PEArchitecture C:\Windows\System32\cmd.exe
-X64
-
-.LINK
-
-http://www.exploit-monday.com
-#>
-
- Param (
- [Parameter(Position = 0, Mandatory = $True)]
- [String]
- $Path
- )
-
- if (!(Test-Path $Path)) {
- Write-Warning 'Invalid path or file does not exist.'
- return
- }
-
- # Parse PE header to see if binary was compiled 32 or 64-bit
- $FileStream = New-Object System.IO.FileStream($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read)
-
- [Byte[]] $MZHeader = New-Object Byte[](2)
- $FileStream.Read($MZHeader,0,2) | Out-Null
-
- $Header = [System.Text.AsciiEncoding]::ASCII.GetString($MZHeader)
- if ($Header -ne 'MZ') {
- Write-Warning 'Invalid PE header.'
- $FileStream.Close()
- return
- }
-
- # Seek to 0x3c - IMAGE_DOS_HEADER.e_lfanew (i.e. Offset to PE Header)
- $FileStream.Seek(0x3c, [System.IO.SeekOrigin]::Begin) | Out-Null
-
- [Byte[]] $lfanew = New-Object Byte[](4)
-
- # Read offset to the PE Header (will be read in reverse)
- $FileStream.Read($lfanew,0,4) | Out-Null
- $PEOffset = [Int] ('0x{0}' -f (( $lfanew[-1..-4] | % { $_.ToString('X2') } ) -join ''))
-
- # Seek to IMAGE_FILE_HEADER.IMAGE_FILE_MACHINE
- $FileStream.Seek($PEOffset + 4, [System.IO.SeekOrigin]::Begin) | Out-Null
- [Byte[]] $IMAGE_FILE_MACHINE = New-Object Byte[](2)
-
- # Read compiled architecture
- $FileStream.Read($IMAGE_FILE_MACHINE,0,2) | Out-Null
- $Architecture = '{0}' -f (( $IMAGE_FILE_MACHINE[-1..-2] | % { $_.ToString('X2') } ) -join '')
- $FileStream.Close()
-
- if (($Architecture -ne '014C') -and ($Architecture -ne '8664') -and ($Architecture -ne '01C4')) {
- Write-Warning 'Invalid PE header or unsupported architecture.'
- return
- }
-
- if ($Architecture -eq '014C') {
- return 'X86'
- } elseif ($Architecture -eq '8664') {
- return 'X64'
- } elseif ($Architecture -eq '01C4') {
- return 'ARM'
- } else {
- return 'OTHER'
- }
-
-}
diff --git a/PETools/Get-PEHeader.ps1 b/PETools/Get-PEHeader.ps1
index 01a91f1..0021377 100644
--- a/PETools/Get-PEHeader.ps1
+++ b/PETools/Get-PEHeader.ps1
@@ -15,6 +15,26 @@ Optional Dependencies: PETools.format.ps1xml
Get-PEHeader retrieves PE headers including imports and exports from either a file on disk or a module in memory. Get-PEHeader will operate on single PE header but you can also feed it the output of Get-ChildItem or Get-Process! Get-PEHeader works on both 32 and 64-bit modules.
+.PARAMETER FilePath
+
+Specifies the path to the portable executable file on disk
+
+.PARAMETER ProcessID
+
+Specifies the process ID.
+
+.PARAMETER Module
+
+The name of the module. This parameter is typically only used in pipeline expressions
+
+.PARAMETER ModuleBaseAddress
+
+The base address of the module
+
+.PARAMETER GetSectionData
+
+Retrieves raw section data.
+
.OUTPUTS
System.Object
@@ -91,14 +111,11 @@ http://www.exploit-monday.com/2012/07/get-peheader.html
#>
[CmdletBinding(DefaultParameterSetName = 'OnDisk')] Param (
- # Path to the portable executable file on disk
[Parameter(Position = 0, Mandatory = $True, ParameterSetName = 'OnDisk', ValueFromPipelineByPropertyName = $True)] [Alias('FullName')] [String[]] $FilePath,
- # The process ID
[Parameter(Position = 0, Mandatory = $True, ParameterSetName = 'InMemory', ValueFromPipelineByPropertyName = $True)] [Alias('Id')] [Int] $ProcessID,
- # The name of the module. This parameter is typically only used in pipeline expressions
[Parameter(Position = 2, ParameterSetName = 'InMemory', ValueFromPipelineByPropertyName = $True)] [Alias('MainModule')] [Alias('Modules')] [System.Diagnostics.ProcessModule[]] $Module,
- # The base address of the module
- [Parameter(Position = 1, ParameterSetName = 'InMemory')] [IntPtr] $ModuleBaseAddress
+ [Parameter(Position = 1, ParameterSetName = 'InMemory')] [IntPtr] $ModuleBaseAddress,
+ [Parameter()] [Switch] $GetSectionData
)
PROCESS {
@@ -508,12 +525,10 @@ PROCESS {
}
"@
- $location = [PsObject].Assembly.Location
$compileParams = New-Object System.CodeDom.Compiler.CompilerParameters
- $assemblyRange = @("System.dll", $location)
- $compileParams.ReferencedAssemblies.AddRange($assemblyRange)
+ $compileParams.ReferencedAssemblies.AddRange(@('System.dll', 'mscorlib.dll'))
$compileParams.GenerateInMemory = $True
- Add-Type -TypeDefinition $code -passthru -WarningAction SilentlyContinue | Out-Null
+ Add-Type -TypeDefinition $code -CompilerParameters $compileParams -PassThru -WarningAction SilentlyContinue | Out-Null
}
function Get-DelegateType
@@ -565,13 +580,13 @@ PROCESS {
$OpenProcessAddr = Get-ProcAddress kernel32.dll OpenProcess
$OpenProcessDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr])
- $OpenProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessAddr, $OpenProcessDelegate)
+ $OpenProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessAddr, [Type] $OpenProcessDelegate)
$ReadProcessMemoryAddr = Get-ProcAddress kernel32.dll ReadProcessMemory
$ReadProcessMemoryDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [Int], [Int].MakeByRefType()) ([Bool])
- $ReadProcessMemory = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ReadProcessMemoryAddr, $ReadProcessMemoryDelegate)
+ $ReadProcessMemory = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ReadProcessMemoryAddr, [Type] $ReadProcessMemoryDelegate)
$CloseHandleAddr = Get-ProcAddress kernel32.dll CloseHandle
$CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool])
- $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate)
+ $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, [Type] $CloseHandleDelegate)
if ($OnDisk) {
@@ -606,9 +621,9 @@ PROCESS {
}
- $DosHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PEBaseAddr, [PE+_IMAGE_DOS_HEADER])
+ $DosHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PEBaseAddr, [Type] [PE+_IMAGE_DOS_HEADER])
$PointerNtHeader = [IntPtr] ($PEBaseAddr.ToInt64() + $DosHeader.e_lfanew)
- $NtHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PointerNtHeader, [PE+_IMAGE_NT_HEADERS32])
+ $NtHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PointerNtHeader, [Type] [PE+_IMAGE_NT_HEADERS32])
$Architecture = ($NtHeader.FileHeader.Machine).ToString()
$BinaryPtrWidth = 4
@@ -628,7 +643,7 @@ PROCESS {
Write-Verbose "Architecture: $Architecture"
Write-Verbose 'Proceeding with parsing a 64-bit binary.'
- } elseif ($Architecture -eq 'I386' -or $Architecture -eq 'ARMNT') {
+ } elseif ($Architecture -eq 'I386' -or $Architecture -eq 'ARMNT' -or $Architecture -eq 'THUMB') {
$PEStruct = @{
IMAGE_OPTIONAL_HEADER = [PE+_IMAGE_OPTIONAL_HEADER32]
@@ -648,15 +663,15 @@ PROCESS {
}
# Need to get a new NT header in case the architecture changed
- $NtHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PointerNtHeader, $PEStruct['NT_HEADER'])
+ $NtHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PointerNtHeader, [Type] $PEStruct['NT_HEADER'])
# Display all section headers
$NumSections = $NtHeader.FileHeader.NumberOfSections
$NumRva = $NtHeader.OptionalHeader.NumberOfRvaAndSizes
- $PointerSectionHeader = [IntPtr] ($PointerNtHeader.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf($PEStruct['NT_HEADER']))
- $SectionHeaders = New-Object PE+_IMAGE_SECTION_HEADER[]($NumSections)
+ $PointerSectionHeader = [IntPtr] ($PointerNtHeader.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf([Type] $PEStruct['NT_HEADER']))
+ $SectionHeaders = New-Object PSObject[]($NumSections)
foreach ($i in 0..($NumSections - 1))
{
- $SectionHeaders[$i] = [System.Runtime.InteropServices.Marshal]::PtrToStructure(([IntPtr] ($PointerSectionHeader.ToInt64() + ($i * [System.Runtime.InteropServices.Marshal]::SizeOf([PE+_IMAGE_SECTION_HEADER])))), [PE+_IMAGE_SECTION_HEADER])
+ $SectionHeaders[$i] = [System.Runtime.InteropServices.Marshal]::PtrToStructure(([IntPtr] ($PointerSectionHeader.ToInt64() + ($i * [System.Runtime.InteropServices.Marshal]::SizeOf([Type] [PE+_IMAGE_SECTION_HEADER])))), [Type] [PE+_IMAGE_SECTION_HEADER])
}
@@ -686,6 +701,27 @@ PROCESS {
$CloseHandle.Invoke($hProcess) | Out-Null
}
+
+ if ($PSBoundParameters['GetSectionData'])
+ {
+ foreach ($i in 0..($NumSections - 1))
+ {
+ $RawBytes = $null
+
+ if ($OnDisk)
+ {
+ $RawBytes = New-Object Byte[]($SectionHeaders[$i].SizeOfRawData)
+ [Runtime.InteropServices.Marshal]::Copy([IntPtr] ($PEBaseAddr.ToInt64() + $SectionHeaders[$i].PointerToRawData), $RawBytes, 0, $SectionHeaders[$i].SizeOfRawData)
+ }
+ else
+ {
+ $RawBytes = New-Object Byte[]($SectionHeaders[$i].VirtualSize)
+ [Runtime.InteropServices.Marshal]::Copy([IntPtr] ($PEBaseAddr.ToInt64() + $SectionHeaders[$i].VirtualAddress), $RawBytes, 0, $SectionHeaders[$i].VirtualSize)
+ }
+
+ $SectionHeaders[$i] = Add-Member -InputObject ($SectionHeaders[$i]) -MemberType NoteProperty -Name RawData -Value $RawBytes -PassThru -Force
+ }
+ }
function Get-Exports()
{
@@ -705,7 +741,7 @@ PROCESS {
$ExportDirHigh = $ExportDirLow.ToInt32() + $NtHeader.OptionalHeader.DataDirectory[0].Size
} else { $ExportDirHigh = $ExportDirLow + $NtHeader.OptionalHeader.DataDirectory[0].Size }
- $ExportDirectory = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ExportPointer, [PE+_IMAGE_EXPORT_DIRECTORY])
+ $ExportDirectory = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ExportPointer, [Type] [PE+_IMAGE_EXPORT_DIRECTORY])
$AddressOfNamePtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ExportDirectory.AddressOfNames)
$NameOrdinalAddrPtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ExportDirectory.AddressOfNameOrdinals)
$AddressOfFunctionsPtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ExportDirectory.AddressOfFunctions)
@@ -800,8 +836,8 @@ PROCESS {
# Get all imported modules
while ($true)
{
- $ImportDescriptorPtr = [IntPtr] ($FirstImageImportDescriptorPtr.ToInt64() + ($i * [System.Runtime.InteropServices.Marshal]::SizeOf([PE+_IMAGE_IMPORT_DESCRIPTOR])))
- $ImportDescriptor = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ImportDescriptorPtr, [PE+_IMAGE_IMPORT_DESCRIPTOR])
+ $ImportDescriptorPtr = [IntPtr] ($FirstImageImportDescriptorPtr.ToInt64() + ($i * [System.Runtime.InteropServices.Marshal]::SizeOf([Type] [PE+_IMAGE_IMPORT_DESCRIPTOR])))
+ $ImportDescriptor = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ImportDescriptorPtr, [Type] [PE+_IMAGE_IMPORT_DESCRIPTOR])
if ($ImportDescriptor.OriginalFirstThunk -eq 0) { break }
$DllNamePtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ImportDescriptor.Name)
if ($OnDisk) { $DllNamePtr = Convert-RVAToFileOffset $DllNamePtr }
@@ -815,10 +851,10 @@ PROCESS {
$j = 0
while ($true)
{
- $FuncAddrPtr = [IntPtr] ($FirstFuncAddrPtr.ToInt64() + ($j * [System.Runtime.InteropServices.Marshal]::SizeOf($ThunkDataStruct)))
- $FuncAddr = [System.Runtime.InteropServices.Marshal]::PtrToStructure($FuncAddrPtr, $ThunkDataStruct)
- $OFTPtr = [IntPtr] ($FirstOFTPtr.ToInt64() + ($j * [System.Runtime.InteropServices.Marshal]::SizeOf($ThunkDataStruct)))
- $ThunkData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($OFTPtr, $ThunkDataStruct)
+ $FuncAddrPtr = [IntPtr] ($FirstFuncAddrPtr.ToInt64() + ($j * [System.Runtime.InteropServices.Marshal]::SizeOf([Type] $ThunkDataStruct)))
+ $FuncAddr = [System.Runtime.InteropServices.Marshal]::PtrToStructure($FuncAddrPtr, [Type] $ThunkDataStruct)
+ $OFTPtr = [IntPtr] ($FirstOFTPtr.ToInt64() + ($j * [System.Runtime.InteropServices.Marshal]::SizeOf([Type] $ThunkDataStruct)))
+ $ThunkData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($OFTPtr, [Type] $ThunkDataStruct)
$Result = @{ ModuleName = $DllName }
if (([System.Convert]::ToString($ThunkData.AddressOfData, 2)).PadLeft(32, '0')[0] -eq '1')
@@ -921,4 +957,4 @@ PROCESS {
}
-} \ No newline at end of file
+}
diff --git a/PETools/PETools.format.ps1xml b/PETools/PETools.format.ps1xml
index 17d2c56..c510281 100644
--- a/PETools/PETools.format.ps1xml
+++ b/PETools/PETools.format.ps1xml
@@ -371,4 +371,4 @@
</TableControl>
</View>
</ViewDefinitions>
-</Configuration> \ No newline at end of file
+</Configuration>
diff --git a/PETools/PETools.psd1 b/PETools/PETools.psd1
index ef3db56..ef470a7 100644
--- a/PETools/PETools.psd1
+++ b/PETools/PETools.psd1
@@ -1,4 +1,4 @@
-@{
+@{
# Script module or binary module file associated with this manifest.
ModuleToProcess = 'PETools.psm1'
@@ -52,7 +52,7 @@ PowerShellVersion = '2.0'
# TypesToProcess = @()
# Format files (.ps1xml) to be loaded when importing this module
-FormatsToProcess = 'PETools.format.ps1xml'
+FormatsToProcess = 'PETools.format.ps1xml', 'Get-ObjDump.format.ps1xml', 'Get-LibSymbols.format.ps1xml'
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
# NestedModules = @()
@@ -74,7 +74,8 @@ ModuleList = @(@{ModuleName = 'PETools'; ModuleVersion = '1.0.0.0'; GUID = 'd150
# List of all files packaged with this module
FileList = 'PETools.psm1', 'PETools.psd1', 'PETools.format.ps1xml', 'Get-DllLoadPath.ps1',
- 'Get-PEArchitecture.ps1', 'Get-PEHeader.ps1', 'Usage.md'
+ 'Get-PEHeader.ps1', 'Get-ObjDump.ps1', 'Get-ObjDump.format.ps1xml', 'Get-LibSymbols.ps1',
+ 'Usage.md'
# Private data to pass to the module specified in RootModule/ModuleToProcess
# PrivateData = ''
@@ -85,4 +86,4 @@ FileList = 'PETools.psm1', 'PETools.psd1', 'PETools.format.ps1xml', 'Get-DllLoad
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''
-} \ No newline at end of file
+}
diff --git a/PETools/PETools.psm1 b/PETools/PETools.psm1
index e5234fb..81d3818 100644
--- a/PETools/PETools.psm1
+++ b/PETools/PETools.psm1
@@ -1 +1 @@
-Get-ChildItem (Join-Path $PSScriptRoot *.ps1) | % { . $_.FullName} \ No newline at end of file
+Get-ChildItem (Join-Path $PSScriptRoot *.ps1) | % { . $_.FullName}