aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Graeber <mattgraeber@gmail.com>2013-03-10 11:29:53 -0400
committerMatt Graeber <mattgraeber@gmail.com>2013-03-10 11:29:53 -0400
commit60c49734df3dbaf88a571e966495ee10ae3f4d3e (patch)
treeb362a2f56ca701d0e11d6e896500b20350a153d8
parent4475824be068180c252194629d8c22db594a79d5 (diff)
downloadPowerSploit-60c49734df3dbaf88a571e966495ee10ae3f4d3e.tar.gz
PowerSploit-60c49734df3dbaf88a571e966495ee10ae3f4d3e.zip
Added DownloadEXEFromMSSymbolServer method
After parsing a PE header with Get-PEHeader, you now have the option of downloading the original executable from Microsoft's symbol server for reference/comparision.
-rw-r--r--PETools/Get-PEHeader.ps11783
1 files changed, 902 insertions, 881 deletions
diff --git a/PETools/Get-PEHeader.ps1 b/PETools/Get-PEHeader.ps1
index f32239c..46ef75a 100644
--- a/PETools/Get-PEHeader.ps1
+++ b/PETools/Get-PEHeader.ps1
@@ -1,882 +1,903 @@
-function Get-PEHeader
-{
-<#
-.SYNOPSIS
-
-Parses and outputs the PE header of a process in memory or a PE file on disk.
-
-PowerSploit Function: Get-PEHeader
-Author: Matthew Graeber (@mattifestation)
-License: BSD 3-Clause
-Required Dependencies: None
-Optional Dependencies: PETools.format.ps1xml
-
-.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
-
-C:\PS> Get-Process cmd | Get-PEHeader
-
-Description
------------
-Returns the full PE headers of every loaded module in memory
-
-.EXAMPLE
-
-C:\PS> Get-ChildItem C:\Windows\*.exe | Get-PEHeader
-
-Description
------------
-Returns the full PE headers of every exe in C:\Windows\
-
-.EXAMPLE
-
-C:\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
-
-C:\PS> $Proc = Get-Process cmd
-C:\PS> $Kernel32Base = ($Proc.Modules | Where-Object {$_.ModuleName -eq 'kernel32.dll'}).BaseAddress
-C:\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 {
-
- 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()
- {
-
- if ($NTHeader.OptionalHeader.DataDirectory[0].VirtualAddress -eq 0) {
- Write-Verbose 'Module does not contain any exports'
- return
- }
-
- # 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()
- {
- if ($NTHeader.OptionalHeader.DataDirectory[1].VirtualAddress -eq 0) {
- Write-Verbose 'Module does not contain any imports'
- return
- }
-
- $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
-
-}
-
+function Get-PEHeader
+{
+<#
+.SYNOPSIS
+
+Parses and outputs the PE header of a process in memory or a PE file on disk.
+
+PowerSploit Function: Get-PEHeader
+Author: Matthew Graeber (@mattifestation)
+License: BSD 3-Clause
+Required Dependencies: None
+Optional Dependencies: PETools.format.ps1xml
+
+.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
+
+C:\PS> Get-Process cmd | Get-PEHeader
+
+Description
+-----------
+Returns the full PE headers of every loaded module in memory
+
+.EXAMPLE
+
+C:\PS> Get-ChildItem C:\Windows\*.exe | Get-PEHeader
+
+Description
+-----------
+Returns the full PE headers of every exe in C:\Windows\
+
+.EXAMPLE
+
+C:\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
+
+C:\PS> $Proc = Get-Process cmd
+C:\PS> $Kernel32Base = ($Proc.Modules | Where-Object {$_.ModuleName -eq 'kernel32.dll'}).BaseAddress
+C:\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 {
+
+ 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()
+ {
+
+ if ($NTHeader.OptionalHeader.DataDirectory[0].VirtualAddress -eq 0) {
+ Write-Verbose 'Module does not contain any exports'
+ return
+ }
+
+ # 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()
+ {
+ if ($NTHeader.OptionalHeader.DataDirectory[1].VirtualAddress -eq 0) {
+ Write-Verbose 'Module does not contain any imports'
+ return
+ }
+
+ $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')
+
+ $ScriptBlock = {
+ $SymServerURL = 'http://msdl.microsoft.com/download/symbols'
+ $FileName = $this.Module.Split('\')[-1]
+ $Request = "{0}/{1}/{2:X8}{3:X}/{1}" -f $SymServerURL, $FileName, $this.FileHeader.TimeDateStamp, $this.OptionalHeader.SizeOfImage
+ $Request = "$($Request.Substring(0, $Request.Length - 1))_"
+ $WebClient = New-Object Net.WebClient
+ $WebClient.Headers.Add('User-Agent', 'Microsoft-Symbol-Server/6.6.0007.5')
+ Write-Host "Downloading $FileName from the Microsoft symbol server..."
+ $CabBytes = $WebClient.DownloadData($Request)
+ $CabPath = "$PWD\$($FileName.Split('.')[0]).cab"
+ Write-Host "Download complete. Saving it to $("$(Split-Path $CabPath)\$FileName")."
+ [IO.File]::WriteAllBytes($CabPath, $CabBytes)
+ $Shell = New-Object -Comobject Shell.Application
+ $CabFile = $Shell.Namespace($CabPath).Items()
+ $Destination = $Shell.Namespace((Split-Path $CabPath))
+ $Destination.CopyHere($CabFile)
+ Remove-Item $CabPath -Force
+ }
+
+ $PEHeader = Add-Member -InputObject $PEHeader -MemberType ScriptMethod -Name DownloadEXEFromMSSymbolServer -Value $ScriptBlock -PassThru -Force
+
+ return $PEHeader
+
+}
+
} \ No newline at end of file