From 65ebaea880b1470718f609e1946f950e7fff0d81 Mon Sep 17 00:00:00 2001 From: bitform Date: Sun, 22 Jul 2012 15:16:22 -0400 Subject: Added Get-PEHeader. PETools is now a module. Get-PEHeader is a 32 and 64-bit in-memory and on-disk PE parsing utility. PETools is now a PowerShell module that can be loaded with `Import-Module PETools` --- PETools/Get-DllLoadPath.ps1 | 181 +++++++++ PETools/Get-PEArchitecture.ps1 | 83 ++++ PETools/Get-PEHeader.ps1 | 870 +++++++++++++++++++++++++++++++++++++++++ PETools/PETools.format.ps1xml | 374 ++++++++++++++++++ PETools/PETools.psm1 | 4 + PETools/Usage.txt | 12 + 6 files changed, 1524 insertions(+) create mode 100644 PETools/Get-DllLoadPath.ps1 create mode 100644 PETools/Get-PEArchitecture.ps1 create mode 100644 PETools/Get-PEHeader.ps1 create mode 100644 PETools/PETools.format.ps1xml create mode 100644 PETools/PETools.psm1 create mode 100644 PETools/Usage.txt (limited to 'PETools') diff --git a/PETools/Get-DllLoadPath.ps1 b/PETools/Get-DllLoadPath.ps1 new file mode 100644 index 0000000..687f9e9 --- /dev/null +++ b/PETools/Get-DllLoadPath.ps1 @@ -0,0 +1,181 @@ +function Get-DllLoadPath { +<# +.Synopsis + + PowerSploit Module - Get-DllLoadPath + Author: Matthew Graeber (@mattifestation) + License: BSD 3-Clause + +.Description + + Get-DllLoadPath returns the path from which Windows will load a Dll for the given executable. + +.Parameter ExecutablePath + + Path to the executable from which the Dll would be loaded. + +.Parameter DllName + + Name of the Dll in the form 'dllname.dll'. + +.Example + + PS> Get-DllLoadPath C:\Windows\System32\cmd.exe kernel32.dll + + Path + ---- + C:\Windows\system32\kernel32.dll + +.Example + + PS> Get-DllLoadPath C:\Windows\SysWOW64\calc.exe Comctl32.dll + + Path + ---- + C:\Windows\SysWOW64\Comctl32.dll + +.Outputs + + None or System.Management.Automation.PathInfo + +.Notes + + This script will not detect if the executable provided intentionally alters the Dll search path via + LoadLibraryEx, SetDllDirectory, or AddDllDirectory. + +.Link + + My blog: http://www.exploit-monday.com + Dll Search Order Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586%28v=vs.85%29.aspx +#> + + Param ( + [Parameter(Position = 0, Mandatory = $True)] [String] $ExecutablePath, + [Parameter(Position = 1, Mandatory = $True)] [String] $DllName + ) + + if (!(Test-Path $ExecutablePath)) { + Write-Warning 'Invalid path or file does not exist.' + return + } else { + $ExecutablePath = Resolve-Path $ExecutablePath + $ExecutableDirectory = Split-Path $ExecutablePath + } + + if ($DllName.Contains('.dll')) { + $DllNameShort = $DllName.Split('.')[0] + } else { + Write-Warning 'You must provide a proper dll name (i.e. kernel32.dll)' + return + } + + function Get-PEArchitecture { + + Param ( [Parameter(Position = 0, Mandatory = $True)] [String] $Path ) + + # 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')) { + Write-Warning 'Invalid PE header or unsupported architecture.' + return + } + + if ($Architecture -eq '014C') { + return 'X86' + } elseif ($Architecture -eq '8664') { + return 'X64' + } else { + return 'OTHER' + } + + } + + # Check if SafeDllSearch is disabled. Note: The logic of this check will fail in XP SP0/1 + $UnsafeSearch = $False + $SearchMode = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager').SafeDllSearchMode + if ($SearchMode -eq 0) { $UnsafeSearch = $True } + + $OSArch = (Get-WmiObject Win32_OperatingSystem -Property OSArchitecture).OSArchitecture + $PEArch = Get-PEArchitecture $ExecutablePath + $KnownDlls = Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs' + + if ($OSArch -eq '32-bit') { + $DllDirectory = Resolve-Path $KnownDlls.DllDirectory + } else { + if ($PEArch -eq 'X86') { + $DllDirectory = Resolve-Path $KnownDlls.DllDirectory32 + } else { + $DllDirectory = Resolve-Path $KnownDlls.DllDirectory + } + } + + + if ($KnownDlls | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -eq $DllNameShort }) { + $Expression = '$KnownDlls.' + "$DllNameShort" + $Filename = Invoke-Expression $Expression + return Resolve-Path (Join-Path $DllDirectory $Filename) + } + + $FoundInAppDirectory = Get-ChildItem (Join-Path $ExecutableDirectory $DllName) -ErrorAction SilentlyContinue + if ($FoundInAppDirectory) { return Resolve-Path $FoundInAppDirectory.FullName } + + if ($UnsafeSearch) { + $FoundInWorkingDirectory = Get-ChildItem (Join-Path (Get-Location) $DllName) -ErrorAction SilentlyContinue + if ($FoundInWorkingDirectory) { return Resolve-Path $FoundInWorkingDirectory.FullName } + } + + $FoundInSystemDirectory = Get-ChildItem (Join-Path $DllDirectory $DllName) -ErrorAction SilentlyContinue + if ($FoundInSystemDirectory) { return Resolve-Path $FoundInSystemDirectory.FullName } + + $FoundIn16BitSystemDir = Get-ChildItem "$($Env:windir)\System\$DllName" -ErrorAction SilentlyContinue + if ($FoundIn16BitSystemDir) { return Resolve-Path $FoundIn16BitSystemDir.FullName } + + $FoundInWindowsDirectory = Get-ChildItem "$($Env:windir)\$DllName" -ErrorAction SilentlyContinue + if ($FoundInWindowsDirectory) { return Resolve-Path $FoundInWindowsDirectory.FullName } + + if (!$UnsafeSearch) { + $FoundInWorkingDirectory = Get-ChildItem (Join-Path (Get-Location) $DllName) -ErrorAction SilentlyContinue + if ($FoundInWorkingDirectory) { return Resolve-Path $FoundInWorkingDirectory.FullName } + } + + $Env:Path.Split(';') | ForEach-Object { + if ($_ -match '%(.{1,})%') { + $TempPath = $_.Replace($Matches[0], [Environment]::GetEnvironmentVariable($Matches[1])) + } else { + $TempPath = $_ + } + + $FoundInPathEnvVar = Get-ChildItem (Join-Path $TempPath $DllName) -ErrorAction SilentlyContinue + if ($FoundInPathEnvVar) { return Resolve-Path $FoundInPathEnvVar.FullName } + } + +} diff --git a/PETools/Get-PEArchitecture.ps1 b/PETools/Get-PEArchitecture.ps1 new file mode 100644 index 0000000..e53c5ff --- /dev/null +++ b/PETools/Get-PEArchitecture.ps1 @@ -0,0 +1,83 @@ +function Get-PEArchitecture { +<# +.Synopsis + + PowerSploit Module - Get-PEArchitecture + Author: Matthew Graeber (@mattifestation) + License: BSD 3-Clause + +.Description + + Get-PEArchitecture returns the architecture for which + a Windows portable executable was compiled. + +.Parameter Path + + Path to the executable. + +.Example + + PS> Get-PEArchitecture C:\Windows\SysWOW64\calc.exe + X86 + +.Example + + PS> Get-PEArchitecture C:\Windows\System32\cmd.exe + X64 + +.Link + + My blog: 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')) { + Write-Warning 'Invalid PE header or unsupported architecture.' + return + } + + if ($Architecture -eq '014C') { + return 'X86' + } elseif ($Architecture -eq '8664') { + return 'X64' + } else { + return 'OTHER' + } + +} diff --git a/PETools/Get-PEHeader.ps1 b/PETools/Get-PEHeader.ps1 new file mode 100644 index 0000000..315f397 --- /dev/null +++ b/PETools/Get-PEHeader.ps1 @@ -0,0 +1,870 @@ +function Get-PEHeader { +<# +.SYNOPSIS +PowerSploit Module - Get-PEHeader +Author: Matthew Graeber (@mattifestation) +License: BSD 3-Clause + +.DESCRIPTION +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. + +.OUTPUTS +System.Object. Returns a custom object consisting of the following: compile time, +section headers, module name, DOS header, imports, exports, file header, +optional header, and PE signature + +.EXAMPLE +PS > Get-Process cmd | Get-PEHeader +Description +----------- +Returns the full PE headers of every loaded module in memory + +PS > Get-ChildItem C:\Windows\*.exe | Get-PEHeader +Description +----------- +Returns the full PE headers of every exe in C:\Windows\ + +.EXAMPLE +PS > Get-PEHeader C:\Windows\System32\kernel32.dll + +Module : C:\Windows\System32\kernel32.dll +DOSHeader : PE+_IMAGE_DOS_HEADER +FileHeader : PE+_IMAGE_FILE_HEADER +OptionalHeader : PE+_IMAGE_OPTIONAL_HEADER32 +SectionHeaders : {.text, .data, .rsrc, .reloc} +Imports : {@{Ordinal=; FunctionName=RtlUnwind; ModuleName=API-MS-Win-Core-RtlSupport-L1-1-0. + dll; VA=0x000CB630}, @{Ordinal=; FunctionName=RtlCaptureContext; ModuleName=API-MS + -Win-Core-RtlSupport-L1-1-0.dll; VA=0x000CB63C}, @{Ordinal=; FunctionName=RtlCaptu + reStackBackTrace; ModuleName=API-MS-Win-Core-RtlSupport-L1-1-0.dll; VA=0x000CB650} + , @{Ordinal=; FunctionName=NtCreateEvent; ModuleName=ntdll.dll; VA=0x000CB66C}...} +Exports : {@{ForwardedName=; FunctionName=lstrlenW; Ordinal=0x0552; VA=0x0F022708}, @{Forwar + dedName=; FunctionName=lstrlenA; Ordinal=0x0551; VA=0x0F026A23}, @{ForwardedName=; + FunctionName=lstrlen; Ordinal=0x0550; VA=0x0F026A23}, @{ForwardedName=; FunctionN + ame=lstrcpynW; Ordinal=0x054F; VA=0x0F04E54E}...} + +.EXAMPLE +PS > $Proc = Get-Process cmd +PS > $Kernel32Base = ($Proc.Modules | Where-Object {$_.ModuleName -eq 'kernel32.dll'}).BaseAddress +PS > Get-PEHeader -ProcessId $Proc.Id -ModuleBaseAddress $Kernel32Base + +Module : +DOSHeader : PE+_IMAGE_DOS_HEADER +FileHeader : PE+_IMAGE_FILE_HEADER +OptionalHeader : PE+_IMAGE_OPTIONAL_HEADER32 +SectionHeaders : {.text, .data, .rsrc, .reloc} +Imports : {@{Ordinal=; FunctionName=RtlUnwind; ModuleName=API-MS-Win-Core-RtlSupport-L1-1-0. + dll; VA=0x77B8B6D9}, @{Ordinal=; FunctionName=RtlCaptureContext; ModuleName=API-MS + -Win-Core-RtlSupport-L1-1-0.dll; VA=0x77B8B4CB}, @{Ordinal=; FunctionName=RtlCaptu + reStackBackTrace; ModuleName=API-MS-Win-Core-RtlSupport-L1-1-0.dll; VA=0x77B95277} + , @{Ordinal=; FunctionName=NtCreateEvent; ModuleName=ntdll.dll; VA=0x77B4FF54}...} +Exports : {@{ForwardedName=; FunctionName=lstrlenW; Ordinal=0x0552; VA=0x08221720}, @{Forwar + dedName=; FunctionName=lstrlenA; Ordinal=0x0551; VA=0x08225A3B}, @{ForwardedName=; + FunctionName=lstrlen; Ordinal=0x0550; VA=0x08225A3B}, @{ForwardedName=; FunctionN + ame=lstrcpynW; Ordinal=0x054F; VA=0x0824D566}...} + +Description +----------- +A PE header is returned upon providing the module's base address. This technique would be useful +for dumping the PE header of a rogue module that is invisible to Windows - e.g. a reflectively +loaded meterpreter binary (metsrv.dll). + +.NOTES +Be careful if you decide to specify a module base address. Get-PEHeader does not check for the +existence of an MZ header. An MZ header is not a prerequisite for reflectively loading a module +in memory. If you provide an address that is not an actual PE header, you could crash the process. + +.LINK +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 + ) + +PROCESS { + + # Apply custom view to the PE header format only if file exists and has not yet been applied + $FormatFilePath = Join-Path $PsScriptRoot 'PETools.Format.ps1xml' + + if ((Test-Path $FormatFilePath) -and !(Get-FormatData PEHeader)) { + Update-FormatData -PrependPath $FormatFilePath + } + + switch ($PsCmdlet.ParameterSetName) { + 'OnDisk' { + + if ($FilePath.Length -gt 1) { + foreach ($Path in $FilePath) { Get-PEHeader $Path } + } + + if (!(Test-Path $FilePath)) { + Write-Warning 'Invalid path or file does not exist.' + return + } + + $FilePath = Resolve-Path $FilePath + + if ($FilePath.GetType() -eq [System.Array]) { + $ModuleName = $FilePath[0] + } else { + $ModuleName = $FilePath + } + + } + 'InMemory' { + + if ($Module.Length -gt 1) { + foreach ($Mod in $Module) { + $ModuleBaseAddress = $Mod.BaseAddress + Get-PEHeader -ProcessID $ProcessID -Module $Mod -ModuleBaseAddress $ModuleBaseAddress + } + } + + if ($ProcessID -eq $PID) { + Write-Warning 'You cannot parse the PE header of the current process. Open another instance of PowerShell.' + return + } + + if ($Module) { + $ModuleName = $Module[0].FileName + } else { + $ModuleName = '' + } + + } + } + +$code = @" + using System; + using System.Runtime.InteropServices; + + public class PE + { + [Flags] + public enum IMAGE_DOS_SIGNATURE : ushort + { + DOS_SIGNATURE = 0x5A4D, // MZ + OS2_SIGNATURE = 0x454E, // NE + OS2_SIGNATURE_LE = 0x454C, // LE + VXD_SIGNATURE = 0x454C, // LE + } + + [Flags] + public enum IMAGE_NT_SIGNATURE : uint + { + VALID_PE_SIGNATURE = 0x00004550 // PE00 + } + + [Flags] + public enum IMAGE_FILE_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, + 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 + CEE = 0xC0EE + } + + [Flags] + public enum IMAGE_FILE_CHARACTERISTICS : ushort + { + IMAGE_RELOCS_STRIPPED = 0x0001, // Relocation info stripped from file. + IMAGE_EXECUTABLE_IMAGE = 0x0002, // File is executable (i.e. no unresolved external references). + IMAGE_LINE_NUMS_STRIPPED = 0x0004, // Line nunbers stripped from file. + IMAGE_LOCAL_SYMS_STRIPPED = 0x0008, // Local symbols stripped from file. + IMAGE_AGGRESIVE_WS_TRIM = 0x0010, // Agressively trim working set + IMAGE_LARGE_ADDRESS_AWARE = 0x0020, // App can handle >2gb addresses + IMAGE_REVERSED_LO = 0x0080, // public bytes of machine public ushort are reversed. + IMAGE_32BIT_MACHINE = 0x0100, // 32 bit public ushort machine. + IMAGE_DEBUG_STRIPPED = 0x0200, // Debugging info stripped from file in .DBG file + IMAGE_REMOVABLE_RUN_FROM_SWAP = 0x0400, // If Image is on removable media =copy and run from the swap file. + IMAGE_NET_RUN_FROM_SWAP = 0x0800, // If Image is on Net =copy and run from the swap file. + IMAGE_SYSTEM = 0x1000, // System File. + IMAGE_DLL = 0x2000, // File is a DLL. + IMAGE_UP_SYSTEM_ONLY = 0x4000, // File should only be run on a UP machine + IMAGE_REVERSED_HI = 0x8000 // public bytes of machine public ushort are reversed. + } + + [Flags] + public enum IMAGE_NT_OPTIONAL_HDR_MAGIC : ushort + { + PE32 = 0x10b, + PE64 = 0x20b + } + + [Flags] + public enum IMAGE_SUBSYSTEM : ushort + { + UNKNOWN = 0, // Unknown subsystem. + NATIVE = 1, // Image doesn't require a subsystem. + WINDOWS_GUI = 2, // Image runs in the Windows GUI subsystem. + WINDOWS_CUI = 3, // Image runs in the Windows character subsystem. + OS2_CUI = 5, // image runs in the OS/2 character subsystem. + POSIX_CUI = 7, // image runs in the Posix character subsystem. + NATIVE_WINDOWS = 8, // image is a native Win9x driver. + WINDOWS_CE_GUI = 9, // Image runs in the Windows CE subsystem. + EFI_APPLICATION = 10, + EFI_BOOT_SERVICE_DRIVER = 11, + EFI_RUNTIME_DRIVER = 12, + EFI_ROM = 13, + XBOX = 14, + WINDOWS_BOOT_APPLICATION = 16 + } + + [Flags] + public enum IMAGE_DLLCHARACTERISTICS : ushort + { + DYNAMIC_BASE = 0x0040, // DLL can move. + FORCE_INTEGRITY = 0x0080, // Code Integrity Image + NX_COMPAT = 0x0100, // Image is NX compatible + NO_ISOLATION = 0x0200, // Image understands isolation and doesn't want it + NO_SEH = 0x0400, // Image does not use SEH. No SE handler may reside in this image + NO_BIND = 0x0800, // Do not bind this image. + WDM_DRIVER = 0x2000, // Driver uses WDM model + TERMINAL_SERVER_AWARE = 0x8000 + } + + [Flags] + public enum IMAGE_SCN : 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. + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_DOS_HEADER + { + public IMAGE_DOS_SIGNATURE e_magic; // Magic number + public ushort e_cblp; // public bytes on last page of file + public ushort e_cp; // Pages in file + public ushort e_crlc; // Relocations + public ushort e_cparhdr; // Size of header in paragraphs + public ushort e_minalloc; // Minimum extra paragraphs needed + public ushort e_maxalloc; // Maximum extra paragraphs needed + public ushort e_ss; // Initial (relative) SS value + public ushort e_sp; // Initial SP value + public ushort e_csum; // Checksum + public ushort e_ip; // Initial IP value + public ushort e_cs; // Initial (relative) CS value + public ushort e_lfarlc; // File address of relocation table + public ushort e_ovno; // Overlay number + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] + public string e_res; // This will contain 'Detours!' if patched in memory + public ushort e_oemid; // OEM identifier (for e_oeminfo) + public ushort e_oeminfo; // OEM information; e_oemid specific + [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=10)] // , ArraySubType=UnmanagedType.U4 + public ushort[] e_res2; // Reserved public ushorts + public int e_lfanew; // File address of new exe header + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_FILE_HEADER + { + public IMAGE_FILE_MACHINE Machine; + public ushort NumberOfSections; + public uint TimeDateStamp; + public uint PointerToSymbolTable; + public uint NumberOfSymbols; + public ushort SizeOfOptionalHeader; + public IMAGE_FILE_CHARACTERISTICS Characteristics; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_NT_HEADERS32 + { + public IMAGE_NT_SIGNATURE Signature; + public _IMAGE_FILE_HEADER FileHeader; + public _IMAGE_OPTIONAL_HEADER32 OptionalHeader; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_NT_HEADERS64 + { + public IMAGE_NT_SIGNATURE Signature; + public _IMAGE_FILE_HEADER FileHeader; + public _IMAGE_OPTIONAL_HEADER64 OptionalHeader; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_OPTIONAL_HEADER32 + { + public IMAGE_NT_OPTIONAL_HDR_MAGIC Magic; + public byte MajorLinkerVersion; + public byte MinorLinkerVersion; + public uint SizeOfCode; + public uint SizeOfInitializedData; + public uint SizeOfUninitializedData; + public uint AddressOfEntryPoint; + public uint BaseOfCode; + public uint BaseOfData; + public uint ImageBase; + public uint SectionAlignment; + public uint FileAlignment; + public ushort MajorOperatingSystemVersion; + public ushort MinorOperatingSystemVersion; + public ushort MajorImageVersion; + public ushort MinorImageVersion; + public ushort MajorSubsystemVersion; + public ushort MinorSubsystemVersion; + public uint Win32VersionValue; + public uint SizeOfImage; + public uint SizeOfHeaders; + public uint CheckSum; + public IMAGE_SUBSYSTEM Subsystem; + public IMAGE_DLLCHARACTERISTICS DllCharacteristics; + public uint SizeOfStackReserve; + public uint SizeOfStackCommit; + public uint SizeOfHeapReserve; + public uint SizeOfHeapCommit; + public uint LoaderFlags; + public uint NumberOfRvaAndSizes; + [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=16)] + public _IMAGE_DATA_DIRECTORY[] DataDirectory; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_OPTIONAL_HEADER64 + { + public IMAGE_NT_OPTIONAL_HDR_MAGIC Magic; + public byte MajorLinkerVersion; + public byte MinorLinkerVersion; + public uint SizeOfCode; + public uint SizeOfInitializedData; + public uint SizeOfUninitializedData; + public uint AddressOfEntryPoint; + public uint BaseOfCode; + public ulong ImageBase; + public uint SectionAlignment; + public uint FileAlignment; + public ushort MajorOperatingSystemVersion; + public ushort MinorOperatingSystemVersion; + public ushort MajorImageVersion; + public ushort MinorImageVersion; + public ushort MajorSubsystemVersion; + public ushort MinorSubsystemVersion; + public uint Win32VersionValue; + public uint SizeOfImage; + public uint SizeOfHeaders; + public uint CheckSum; + public IMAGE_SUBSYSTEM Subsystem; + public IMAGE_DLLCHARACTERISTICS DllCharacteristics; + public ulong SizeOfStackReserve; + public ulong SizeOfStackCommit; + public ulong SizeOfHeapReserve; + public ulong SizeOfHeapCommit; + public uint LoaderFlags; + public uint NumberOfRvaAndSizes; + [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=16)] + public _IMAGE_DATA_DIRECTORY[] DataDirectory; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_DATA_DIRECTORY + { + public uint VirtualAddress; + public uint Size; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_EXPORT_DIRECTORY + { + public uint Characteristics; + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public uint Name; + public uint Base; + public uint NumberOfFunctions; + public uint NumberOfNames; + public uint AddressOfFunctions; // RVA from base of image + public uint AddressOfNames; // RVA from base of image + public uint AddressOfNameOrdinals; // RVA from base of image + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_SECTION_HEADER + { + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] + public string Name; + 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 IMAGE_SCN Characteristics; + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_IMPORT_DESCRIPTOR + { + public uint OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) + public uint TimeDateStamp; // 0 if not bound, + // -1 if bound, and real date/time stamp + // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) + // O.W. date/time stamp of DLL bound to (Old BIND) + public uint ForwarderChain; // -1 if no forwarders + public uint Name; + public uint FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_THUNK_DATA + { + public IntPtr AddressOfData; // PIMAGE_IMPORT_BY_NAME + } + + [StructLayout(LayoutKind.Sequential, Pack=1)] + public struct _IMAGE_IMPORT_BY_NAME + { + public ushort Hint; + public char Name; + } + } +"@ + + $location = [PsObject].Assembly.Location + $compileParams = New-Object System.CodeDom.Compiler.CompilerParameters + $assemblyRange = @("System.dll", $location) + $compileParams.ReferencedAssemblies.AddRange($assemblyRange) + $compileParams.GenerateInMemory = $True + Add-Type -TypeDefinition $code -passthru -WarningAction SilentlyContinue | Out-Null + + function Get-DelegateType + { + Param ( + [Parameter(Position = 0, Mandatory = $True)] [Type[]] $Parameters, + [Parameter(Position = 1)] [Type] $ReturnType = [Void] + ) + + $Domain = [AppDomain]::CurrentDomain + $DynAssembly = New-Object System.Reflection.AssemblyName('ReflectedDelegate') + $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) + $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('InMemoryModule', $false) + $TypeBuilder = $ModuleBuilder.DefineType('MyDelegateType', 'Class, Public, Sealed, AnsiClass, AutoClass', [System.MulticastDelegate]) + $ConstructorBuilder = $TypeBuilder.DefineConstructor('RTSpecialName, HideBySig, Public', [System.Reflection.CallingConventions]::Standard, $Parameters) + $ConstructorBuilder.SetImplementationFlags('Runtime, Managed') + $MethodBuilder = $TypeBuilder.DefineMethod('Invoke', 'Public, HideBySig, NewSlot, Virtual', $ReturnType, $Parameters) + $MethodBuilder.SetImplementationFlags('Runtime, Managed') + + return $TypeBuilder.CreateType() + } + + function Get-ProcAddress + { + Param ( + [Parameter(Position = 0, Mandatory = $True)] [String] $Module, + [Parameter(Position = 1, Mandatory = $True)] [String] $Procedure + ) + + # Get a reference to System.dll in the GAC + $SystemAssembly = [AppDomain]::CurrentDomain.GetAssemblies() | + Where-Object { $_.GlobalAssemblyCache -And $_.Location.Split('\\')[-1].Equals('System.dll') } + $UnsafeNativeMethods = $SystemAssembly.GetType('Microsoft.Win32.UnsafeNativeMethods') + # Get a reference to the GetModuleHandle and GetProcAddress methods + $GetModuleHandle = $UnsafeNativeMethods.GetMethod('GetModuleHandle') + $GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress') + # Get a handle to the module specified + $Kern32Handle = $GetModuleHandle.Invoke($null, @($Module)) + $tmpPtr = New-Object IntPtr + $HandleRef = New-Object System.Runtime.InteropServices.HandleRef($tmpPtr, $Kern32Handle) + # Return the address of the function + + return $GetProcAddress.Invoke($null, @([System.Runtime.InteropServices.HandleRef]$HandleRef, $Procedure)) + } + + $OnDisk = $True + if ($PsCmdlet.ParameterSetName -eq 'InMemory') { $OnDisk = $False } + + + $OpenProcessAddr = Get-ProcAddress kernel32.dll OpenProcess + $OpenProcessDelegate = Get-DelegateType @([UInt32], [Bool], [UInt32]) ([IntPtr]) + $OpenProcess = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($OpenProcessAddr, $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) + $CloseHandleAddr = Get-ProcAddress kernel32.dll CloseHandle + $CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool]) + $CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, $CloseHandleDelegate) + + if ($OnDisk) { + + $FileStream = New-Object System.IO.FileStream($FilePath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read) + $FileByteArray = New-Object Byte[]($FileStream.Length) + $FileStream.Read($FileByteArray, 0, $FileStream.Length) | Out-Null + $FileStream.Close() + $Handle = [System.Runtime.InteropServices.GCHandle]::Alloc($FileByteArray, 'Pinned') + $PEBaseAddr = $Handle.AddrOfPinnedObject() + + } else { + + # Size of the memory page allocated for the PE header + $HeaderSize = 0x1000 + # Allocate space for when the PE header is read from the remote process + $PEBaseAddr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($HeaderSize + 1) + # Get handle to the process + $hProcess = $OpenProcess.Invoke(0x10, $false, $ProcessID) # PROCESS_VM_READ (0x00000010) + + # Read PE header from remote process + if (!$ReadProcessMemory.Invoke($hProcess, $ModuleBaseAddress, $PEBaseAddr, $HeaderSize, [Ref] 0)) { + if ($ModuleName) { + Write-Warning "Failed to read PE header of $ModuleName" + } else { + Write-Warning "Failed to read PE header of process ID: $ProcessID" + } + + Write-Warning "Error code: 0x$([System.Runtime.InteropServices.Marshal]::GetLastWin32Error().ToString('X8'))" + $CloseHandle.Invoke($hProcess) | Out-Null + return + } + + } + + $DosHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PEBaseAddr, [PE+_IMAGE_DOS_HEADER]) + $PointerNtHeader = [IntPtr] ($PEBaseAddr.ToInt64() + $DosHeader.e_lfanew) + $NtHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PointerNtHeader, [PE+_IMAGE_NT_HEADERS32]) + $Architecture = ($NtHeader.FileHeader.Machine).ToString() + + # Define relevant structure types depending upon whether the binary is 32 or 64-bit + if ($Architecture -eq 'AMD64') { + + $PEStruct = @{ + IMAGE_OPTIONAL_HEADER = [PE+_IMAGE_OPTIONAL_HEADER64] + NT_HEADER = [PE+_IMAGE_NT_HEADERS64] + } + Write-Verbose "Architecture: $Architecture" + Write-Verbose 'Proceeding with parsing a 64-bit binary.' + + } elseif ($Architecture -eq 'I386') { + + $PEStruct = @{ + IMAGE_OPTIONAL_HEADER = [PE+_IMAGE_OPTIONAL_HEADER32] + NT_HEADER = [PE+_IMAGE_NT_HEADERS32] + } + Write-Verbose "Architecture: $Architecture" + Write-Verbose 'Proceeding with parsing a 32-bit binary.' + + } else { + + Write-Warning 'This parser only supports binaries compiled for x86 or AMD64.' + return + + } + + # Need to get a new NT header in case the architecture changed + $NtHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PointerNtHeader, $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) + 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]) + } + + + if (!$OnDisk) { + + $ReadSize = $NtHeader.OptionalHeader.SizeOfImage + # Free memory allocated for the PE header + [System.Runtime.InteropServices.Marshal]::FreeHGlobal($PEBaseAddr) + $PEBaseAddr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($ReadSize + 1) + + # Read process memory of each section header + foreach ($SectionHeader in $SectionHeaders) { + if (!$ReadProcessMemory.Invoke($hProcess, [IntPtr] ($ModuleBaseAddress.ToInt64() + $SectionHeader.VirtualAddress), [IntPtr] ($PEBaseAddr.ToInt64() + $SectionHeader.VirtualAddress), $SectionHeader.VirtualSize, [Ref] 0)) { + if ($ModuleName) { + Write-Warning "Failed to read $($SectionHeader.Name) section of $ModuleName" + } else { + Write-Warning "Failed to read $($SectionHeader.Name) section of process ID: $ProcessID" + } + + Write-Warning "Error code: 0x$([System.Runtime.InteropServices.Marshal]::GetLastWin32Error().ToString('X8'))" + $CloseHandle.Invoke($hProcess) | Out-Null + return + } + } + + # Close handle to the remote process since we no longer need to access the process. + $CloseHandle.Invoke($hProcess) | Out-Null + + } + + function Get-Exports() + { + + # List all function Rvas in the export table + $ExportPointer = [IntPtr] ($PEBaseAddr.ToInt64() + $NtHeader.OptionalHeader.DataDirectory[0].VirtualAddress) + # This range will be used to test for the existence of forwarded functions + $ExportDirLow = $NtHeader.OptionalHeader.DataDirectory[0].VirtualAddress + if ($OnDisk) { + $ExportPointer = Convert-RVAToFileOffset $ExportPointer + $ExportDirLow = Convert-RVAToFileOffset $ExportDirLow + $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]) + $AddressOfNamePtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ExportDirectory.AddressOfNames) + $NameOrdinalAddrPtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ExportDirectory.AddressOfNameOrdinals) + $AddressOfFunctionsPtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ExportDirectory.AddressOfFunctions) + $NumNamesFuncs = $ExportDirectory.NumberOfFunctions - $ExportDirectory.NumberOfNames + $NumNames = $ExportDirectory.NumberOfNames + $NumFunctions = $ExportDirectory.NumberOfFunctions + $Base = $ExportDirectory.Base + + # Recalculate file offsets based upon relative virtual addresses + if ($OnDisk) { + $AddressOfNamePtr = Convert-RVAToFileOffset $AddressOfNamePtr + $NameOrdinalAddrPtr = Convert-RVAToFileOffset $NameOrdinalAddrPtr + $AddressOfFunctionsPtr = Convert-RVAToFileOffset $AddressOfFunctionsPtr + } + + if ($NumFunctions -gt 0) { + + # Create an empty hash table that will contain indices to exported functions and their RVAs + $FunctionHashTable = @{} + + foreach ($i in 0..($NumFunctions - 1)) + { + + $RvaFunction = [System.Runtime.InteropServices.Marshal]::ReadInt32($AddressOfFunctionsPtr.ToInt64() + ($i * 4)) + # Function is exported by ordinal if $RvaFunction -ne 0. I.E. NumberOfFunction != the number of actual, exported functions. + if ($RvaFunction) { $FunctionHashTable[[Int]$i] = $RvaFunction } + + } + + # Create an empty hash table that will contain indices into RVA array and the function's name + $NameHashTable = @{} + + foreach ($i in 0..($NumNames - 1)) + { + + $RvaName = [System.Runtime.InteropServices.Marshal]::ReadInt32($AddressOfNamePtr.ToInt64() + ($i * 4)) + $FuncNameAddr = [IntPtr] ($PEBaseAddr.ToInt64() + $RvaName) + if ($OnDisk) { $FuncNameAddr= Convert-RVAToFileOffset $FuncNameAddr } + $FuncName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($FuncNameAddr) + $NameOrdinal = [Int][System.Runtime.InteropServices.Marshal]::ReadInt16($NameOrdinalAddrPtr.ToInt64() + ($i * 2)) + $NameHashTable[$NameOrdinal] = $FuncName + + } + + foreach ($Key in $FunctionHashTable.Keys) + { + $Result = @{} + + if ($NameHashTable[$Key]) { + $Result['FunctionName'] = $NameHashTable[$Key] + } else { + $Result['FunctionName'] = '' + } + + if (($FunctionHashTable[$Key] -ge $ExportDirLow) -and ($FunctionHashTable[$Key] -lt $ExportDirHigh)) { + $ForwardedNameAddr = [IntPtr] ($PEBaseAddr.ToInt64() + $FunctionHashTable[$Key]) + if ($OnDisk) { $ForwardedNameAddr = Convert-RVAToFileOffset $ForwardedNameAddr } + $ForwardedName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($ForwardedNameAddr) + # This script does not attempt to resolve the virtual addresses of forwarded functions + $Result['ForwardedName'] = $ForwardedName + } else { + $Result['ForwardedName'] = '' + } + + $Result['Ordinal'] = "0x$(($Key + $Base).ToString('X4'))" + # Uncomment this after I somehow manage to implement the RVA for the imports + # $Result['RVA'] = "0x$($FunctionHashTable[$Key].ToString('X8'))" + $Result['VA'] = "0x$(($FunctionHashTable[$Key] + $PEBaseAddr).ToString("X$([IntPtr]::Size*2)"))" + + $Export = New-Object PSObject -Property $Result + $Export.PSObject.TypeNames.Insert(0, 'Export') + + $Export + + } + + } else { Write-Verbose 'Module does not export any functions.' } + + } + + function Get-Imports() + { + $FirstImageImportDescriptorPtr = [IntPtr] ($PEBaseAddr.ToInt64() + $NtHeader.OptionalHeader.DataDirectory[1].VirtualAddress) + if ($OnDisk) { $FirstImageImportDescriptorPtr = Convert-RVAToFileOffset $FirstImageImportDescriptorPtr } + $ImportDescriptorPtr = $FirstImageImportDescriptorPtr + + $i = 0 + # 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]) + if ($ImportDescriptor.OriginalFirstThunk -eq 0) { break } + $DllNamePtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ImportDescriptor.Name) + if ($OnDisk) { $DllNamePtr = Convert-RVAToFileOffset $DllNamePtr } + $DllName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($DllNamePtr) + $FirstFuncAddrPtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ImportDescriptor.FirstThunk) + if ($OnDisk) { $FirstFuncAddrPtr = Convert-RVAToFileOffset $FirstFuncAddrPtr } + $FuncAddrPtr = $FirstFuncAddrPtr + $FirstOFTPtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ImportDescriptor.OriginalFirstThunk) + if ($OnDisk) { $FirstOFTPtr = Convert-RVAToFileOffset $FirstOFTPtr } + $OFTPtr = $FirstOFTPtr + $j = 0 + while ($true) + { + $FuncAddrPtr = [IntPtr] ($FirstFuncAddrPtr.ToInt64() + ($j * [System.Runtime.InteropServices.Marshal]::SizeOf([PE+_IMAGE_THUNK_DATA]))) + $FuncAddr = [System.Runtime.InteropServices.Marshal]::PtrToStructure($FuncAddrPtr, [PE+_IMAGE_THUNK_DATA]) + $OFTPtr = [IntPtr] ($FirstOFTPtr.ToInt64() + ($j * [System.Runtime.InteropServices.Marshal]::SizeOf([PE+_IMAGE_THUNK_DATA]))) + $ThunkData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($OFTPtr, [PE+_IMAGE_THUNK_DATA]) + $Result = @{ ModuleName = $DllName } + + if (([System.Convert]::ToString($ThunkData.AddressOfData.ToInt64(),2)).PadLeft(32, '0')[0] -eq '1') + { + # Trim high order bit in order to get the ordinal value + $TempOrdinal = [System.Convert]::ToInt64(([System.Convert]::ToString($ThunkData.AddressOfData.ToInt64(),2))[1..63] -join '', 2) + $TempOrdinal = $TempOrdinal.ToString('X16')[-1..-4] + [Array]::Reverse($TempOrdinal) + $Ordinal = '' + $TempOrdinal | ForEach-Object { $Ordinal += $_ } + $Result['Ordinal'] = "0x$Ordinal" + $Result['FunctionName'] = '' + } + else + { + $ImportByNamePtr = [IntPtr] ($PEBaseAddr.ToInt64() + [Int64]$ThunkData.AddressOfData + 2) + if ($OnDisk) { $ImportByNamePtr = Convert-RVAToFileOffset $ImportByNamePtr } + $FuncName = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($ImportByNamePtr) + $Result['Ordinal'] = '' + $Result['FunctionName'] = $FuncName + } + + $Result['VA'] = "0x$($FuncAddr.AddressOfData.ToString("X$([IntPtr]::Size*2)"))" + + if ($FuncAddr.AddressOfData -eq 0) { break } + if ($OFTPtr -eq 0) { break } + + $Import = New-Object PSObject -Property $Result + $Import.PSObject.TypeNames.Insert(0, 'Import') + + $Import + + $j++ + + } + + $i++ + + } + + } + + function Convert-RVAToFileOffset([IntPtr] $Rva) + { + + foreach ($Section in $SectionHeaders) { + if ((($Rva.ToInt64() - $PEBaseAddr.ToInt64()) -ge $Section.VirtualAddress) -and (($Rva.ToInt64() - $PEBaseAddr.ToInt64()) -lt ($Section.VirtualAddress + $Section.VirtualSize))) { + return [IntPtr] ($Rva.ToInt64() - ($Section.VirtualAddress - $Section.PointerToRawData)) + } + } + + # Pointer did not fall in the address ranges of the section headers + return $Rva + + } + + $PEFields = @{ + Module = $ModuleName + DOSHeader = $DosHeader + PESignature = $NTHeader.Signature + FileHeader = $NTHeader.FileHeader + OptionalHeader = $NTHeader.OptionalHeader + SectionHeaders = $SectionHeaders + Imports = Get-Imports + Exports = Get-Exports + } + + if ($Ondisk) { + $Handle.Free() + } else { + # Free memory allocated for the PE header + [System.Runtime.InteropServices.Marshal]::FreeHGlobal($PEBaseAddr) + } + + $PEHeader = New-Object PSObject -Property $PEFields + $PEHeader.PSObject.TypeNames.Insert(0, 'PEHeader') + + return $PEHeader + +} + +} \ No newline at end of file diff --git a/PETools/PETools.format.ps1xml b/PETools/PETools.format.ps1xml new file mode 100644 index 0000000..2a1cbdb --- /dev/null +++ b/PETools/PETools.format.ps1xml @@ -0,0 +1,374 @@ + + + + + OptionHeaderTypes + + PE+_IMAGE_OPTIONAL_HEADER32 + PE+_IMAGE_OPTIONAL_HEADER64 + + + + + + PEView + + PEHeader + + + + + + + Module + + + DOSHeader + + + FileHeader + + + OptionalHeader + + + SectionHeaders + + + Imports + + + Exports + + + + + + + + OptionalHeaderView + + OptionHeaderTypes + + + + + + + Magic + + + MajorLinkerVersion + + + MinorLinkerVersion + + + + "0x$($_.SizeOfCode.ToString('X8'))" + + + + "0x$($_.SizeOfInitializedData.ToString('X8'))" + + + + "0x$($_.SizeOfUninitializedData.ToString('X8'))" + + + + "0x$($_.AddressOfEntryPoint.ToString('X8'))" + + + + "0x$($_.BaseOfCode.ToString('X8'))" + + + + "0x$($_.BaseOfData.ToString('X8'))" + + + + if ($_.Magic.ToString() -eq 'PE32') { "0x$($_.ImageBase.ToString('X8'))" } else { "0x$($_.ImageBase.ToString('X16'))" } + + + + "0x$($_.SectionAlignment.ToString('X8'))" + + + + "0x$($_.FileAlignment.ToString('X8'))" + + + MajorOperatingSystemVersion + + + MinorOperatingSystemVersion + + + MajorSubsystemVersion + + + MinorSubsystemVersion + + + Win32VersionValue + + + + "0x$($_.SizeOfImage.ToString('X8'))" + + + + "0x$($_.SizeOfHeaders.ToString('X8'))" + + + + "0x$($_.CheckSum.ToString('X8'))" + + + Subsystem + + + DllCharacteristics + + + + if ($_.Magic.ToString() -eq 'PE32') { "0x$($_.ImageBase.ToString('X8'))" } else { "0x$($_.ImageBase.ToString('X16'))" } + + + + if ($_.Magic.ToString() -eq 'PE32') { "0x$($_.ImageBase.ToString('X8'))" } else { "0x$($_.ImageBase.ToString('X16'))" } + + + + if ($_.Magic.ToString() -eq 'PE32') { "0x$($_.ImageBase.ToString('X8'))" } else { "0x$($_.ImageBase.ToString('X16'))" } + + + + if ($_.Magic.ToString() -eq 'PE32') { "0x$($_.ImageBase.ToString('X8'))" } else { "0x$($_.ImageBase.ToString('X16'))" } + + + LoaderFlags + + + NumberOfRvaAndSizes + + + DataDirectory + + + + + + + + SectionHeaderView + + PE+_IMAGE_SECTION_HEADER + + + + + + + Right + + + + + + + + + + + + + + + + Left + + + + + + + Right + Name + + + "0x$($_.VirtualSize.ToString('X8'))" + + + "0x$($_.VirtualAddress.ToString('X8'))" + + + "0x$($_.SizeOfRawData.ToString('X8'))" + + + "0x$($_.PointerToRawData.ToString('X8'))" + + + Characteristics + + + + + + + + FileHeaderView + + PE+_IMAGE_FILE_HEADER + + + + + + + Machine + + + NumberOfSections + + + + + (New-Object DateTime(1970, 1, 1, 0, 0, 0)).AddSeconds($_.TimeDateStamp) + + + + + PointerToSymbolTable + + + NumberOfSymbols + + + SizeOfOptionalHeader + + + Characteristics + + + + + + + + DataDirectoryView + + PE+_IMAGE_DATA_DIRECTORY + + + + + + + + + + + + + + + + "0x$($_.VirtualAddress.ToString('X8'))" + + + "0x$($_.Size.ToString('X8'))" + + + + + + + + ImportView + + Import + + + + + + + Right + + + + + + + + + + + + + + + + Right + ModuleName + + + VA + + + Ordinal + + + FunctionName + + + + + + + + ExportView + + Export + + + + + + + + + + + + + + + + + + + + + + VA + + + Ordinal + + + FunctionName + + + ForwardedName + + + + + + + + \ No newline at end of file diff --git a/PETools/PETools.psm1 b/PETools/PETools.psm1 new file mode 100644 index 0000000..7f16c2f --- /dev/null +++ b/PETools/PETools.psm1 @@ -0,0 +1,4 @@ +# Pull in all of the PE Tools +. (Join-Path $PSScriptRoot Get-PEHeader.ps1) +. (Join-Path $PSScriptRoot Get-DllLoadPath.ps1) +. (Join-Path $PSScriptRoot Get-PEArchitecture.ps1) \ No newline at end of file diff --git a/PETools/Usage.txt b/PETools/Usage.txt new file mode 100644 index 0000000..6c96b10 --- /dev/null +++ b/PETools/Usage.txt @@ -0,0 +1,12 @@ +To install this module, drop the entire PETools folder into one of your module directories. The default PowerShell module paths are listed in the $Env:PSModulePath environment variable. + +The default per-user module path is: "$Env:HomeDrive$Env:HOMEPATH\Documents\WindowsPowerShell\Modules" +The default computer-level module path is: "$Env:windir\System32\WindowsPowerShell\v1.0\Modules" + +To use the module, type `Import-Module PETools` + +To see the commands imported, type `Get-Command -Module PETools` + +For help on each individual command, Get-Help is your friend. + +Note: The tools contained within this module were all designed such that they can be run individually. Including them in a module simply lends itself to increased portability. \ No newline at end of file -- cgit v1.2.3