diff options
author | Matt Graeber <mattgraeber@gmail.com> | 2013-07-09 20:17:01 -0400 |
---|---|---|
committer | Matt Graeber <mattgraeber@gmail.com> | 2013-07-09 20:17:01 -0400 |
commit | 55a6dbd019624d4e8de0a0c77c042a3a5963d32a (patch) | |
tree | d9aed28869bddf20a815f8b9a27b2603b417dfc5 | |
parent | 030fc3b43bf853ebdc10a9dbe8ff2439faf7146c (diff) | |
download | PowerSploit-55a6dbd019624d4e8de0a0c77c042a3a5963d32a.tar.gz PowerSploit-55a6dbd019624d4e8de0a0c77c042a3a5963d32a.zip |
Added Get-ObjDump
Get-ObjDump parses and return information about one or more Windows
object files. It is similar to dumpbin but it returns objects!
-rw-r--r-- | PETools/Get-ObjDump.format.ps1xml | 292 | ||||
-rw-r--r-- | PETools/Get-ObjDump.ps1 | 708 | ||||
-rw-r--r-- | PETools/PETools.psd1 | 5 | ||||
-rw-r--r-- | README.md | 4 |
4 files changed, 1007 insertions, 2 deletions
diff --git a/PETools/Get-ObjDump.format.ps1xml b/PETools/Get-ObjDump.format.ps1xml new file mode 100644 index 0000000..d44ade0 --- /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>
\ No newline at end of file diff --git a/PETools/Get-ObjDump.ps1 b/PETools/Get-ObjDump.ps1 new file mode 100644 index 0000000..3df8235 --- /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 {} +}
\ No newline at end of file diff --git a/PETools/PETools.psd1 b/PETools/PETools.psd1 index ef3db56..696eb57 100644 --- a/PETools/PETools.psd1 +++ b/PETools/PETools.psd1 @@ -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'
# 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-PEArchitecture.ps1', 'Get-PEHeader.ps1', 'Get-ObjDump.ps1', 'Get-ObjDump.format.ps1xml',
+ 'Usage.md'
# Private data to pass to the module specified in RootModule/ModuleToProcess
# PrivateData = ''
@@ -68,6 +68,10 @@ Add persistence capabilities to a script. An in-memory and on-disk PE parsing utility. +#### `Get-ObjDump` + +Displays information about one or more Windows object files. + #### `Get-PEArchitecture` Returns the architecture for which an executable was compiled. |