mirror of
https://github.com/naksyn/ProcessStomping
synced 2026-06-06 16:14:38 +00:00
962 lines
46 KiB
PowerShell
962 lines
46 KiB
PowerShell
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.
|
|
|
|
.PARAMETER FilePath
|
|
|
|
Specifies the path to the portable executable file on disk
|
|
|
|
.PARAMETER ProcessID
|
|
|
|
Specifies the process ID.
|
|
|
|
.PARAMETER Module
|
|
|
|
The name of the module. This parameter is typically only used in pipeline expressions
|
|
|
|
.PARAMETER ModuleBaseAddress
|
|
|
|
The base address of the module
|
|
|
|
.PARAMETER GetSectionData
|
|
|
|
Retrieves raw section data.
|
|
|
|
.OUTPUTS
|
|
|
|
System.Object
|
|
|
|
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 (
|
|
[Parameter(Position = 0, Mandatory = $True, ParameterSetName = 'OnDisk', ValueFromPipelineByPropertyName = $True)] [Alias('FullName')] [String[]] $FilePath,
|
|
[Parameter(Position = 0, Mandatory = $True, ParameterSetName = 'InMemory', ValueFromPipelineByPropertyName = $True)] [Alias('Id')] [Int] $ProcessID,
|
|
[Parameter(Position = 2, ParameterSetName = 'InMemory', ValueFromPipelineByPropertyName = $True)] [Alias('MainModule')] [Alias('Modules')] [System.Diagnostics.ProcessModule[]] $Module,
|
|
[Parameter(Position = 1, ParameterSetName = 'InMemory')] [IntPtr] $ModuleBaseAddress,
|
|
[Parameter()] [Switch] $GetSectionData
|
|
)
|
|
|
|
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) {
|
|
$BaseAddr = $Mod.BaseAddress
|
|
Get-PEHeader -ProcessID $ProcessID -Module $Mod -ModuleBaseAddress $BaseAddr
|
|
}
|
|
}
|
|
|
|
if (-not $ModuleBaseAddress) { return }
|
|
|
|
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 = ''
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
try { [PE] | Out-Null } catch [Management.Automation.RuntimeException]
|
|
{
|
|
$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,
|
|
ARMNT = 0x01c4, // ARM Thumb-2 Little-Endian
|
|
AM33 = 0x01d3,
|
|
POWERPC = 0x01F0, // IBM PowerPC Little-Endian
|
|
POWERPCFP = 0x01f1,
|
|
IA64 = 0x0200, // Intel 64
|
|
MIPS16 = 0x0266, // MIPS
|
|
ALPHA64 = 0x0284, // ALPHA64
|
|
MIPSFPU = 0x0366, // MIPS
|
|
MIPSFPU16 = 0x0466, // MIPS
|
|
AXP64 = ALPHA64,
|
|
TRICORE = 0x0520, // Infineon
|
|
CEF = 0x0CEF,
|
|
EBC = 0x0EBC, // EFI public byte Code
|
|
AMD64 = 0x8664, // AMD64 (K8)
|
|
M32R = 0x9041, // M32R little-endian
|
|
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_DATA32
|
|
{
|
|
public Int32 AddressOfData; // PIMAGE_IMPORT_BY_NAME
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack=1)]
|
|
public struct _IMAGE_THUNK_DATA64
|
|
{
|
|
public Int64 AddressOfData; // PIMAGE_IMPORT_BY_NAME
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, Pack=1)]
|
|
public struct _IMAGE_IMPORT_BY_NAME
|
|
{
|
|
public ushort Hint;
|
|
public char Name;
|
|
}
|
|
}
|
|
"@
|
|
|
|
$compileParams = New-Object System.CodeDom.Compiler.CompilerParameters
|
|
$compileParams.ReferencedAssemblies.AddRange(@('System.dll', 'mscorlib.dll'))
|
|
$compileParams.GenerateInMemory = $True
|
|
Add-Type -TypeDefinition $code -CompilerParameters $compileParams -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')
|
|
#Fix For Windows 10 1803. Probably should check version , but meh.
|
|
$GetProcAddress = $UnsafeNativeMethods.GetMethod('GetProcAddress', [Type[]]@([System.Runtime.InteropServices.HandleRef], [String]))
|
|
#$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, [Type] $OpenProcessDelegate)
|
|
$ReadProcessMemoryAddr = Get-ProcAddress kernel32.dll ReadProcessMemory
|
|
$ReadProcessMemoryDelegate = Get-DelegateType @([IntPtr], [IntPtr], [IntPtr], [Int], [Int].MakeByRefType()) ([Bool])
|
|
$ReadProcessMemory = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($ReadProcessMemoryAddr, [Type] $ReadProcessMemoryDelegate)
|
|
$CloseHandleAddr = Get-ProcAddress kernel32.dll CloseHandle
|
|
$CloseHandleDelegate = Get-DelegateType @([IntPtr]) ([Bool])
|
|
$CloseHandle = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer($CloseHandleAddr, [Type] $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, [Type] [PE+_IMAGE_DOS_HEADER])
|
|
$PointerNtHeader = [IntPtr] ($PEBaseAddr.ToInt64() + $DosHeader.e_lfanew)
|
|
$NtHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PointerNtHeader, [Type] [PE+_IMAGE_NT_HEADERS32])
|
|
$Architecture = ($NtHeader.FileHeader.Machine).ToString()
|
|
|
|
$BinaryPtrWidth = 4
|
|
|
|
# Define relevant structure types depending upon whether the binary is 32 or 64-bit
|
|
if ($Architecture -eq 'AMD64') {
|
|
|
|
$BinaryPtrWidth = 8
|
|
|
|
$PEStruct = @{
|
|
IMAGE_OPTIONAL_HEADER = [PE+_IMAGE_OPTIONAL_HEADER64]
|
|
NT_HEADER = [PE+_IMAGE_NT_HEADERS64]
|
|
}
|
|
|
|
$ThunkDataStruct = [PE+_IMAGE_THUNK_DATA64]
|
|
|
|
Write-Verbose "Architecture: $Architecture"
|
|
Write-Verbose 'Proceeding with parsing a 64-bit binary.'
|
|
|
|
} elseif ($Architecture -eq 'I386' -or $Architecture -eq 'ARMNT' -or $Architecture -eq 'THUMB') {
|
|
|
|
$PEStruct = @{
|
|
IMAGE_OPTIONAL_HEADER = [PE+_IMAGE_OPTIONAL_HEADER32]
|
|
NT_HEADER = [PE+_IMAGE_NT_HEADERS32]
|
|
}
|
|
|
|
$ThunkDataStruct = [PE+_IMAGE_THUNK_DATA32]
|
|
|
|
Write-Verbose "Architecture: $Architecture"
|
|
Write-Verbose 'Proceeding with parsing a 32-bit binary.'
|
|
|
|
} else {
|
|
|
|
Write-Warning 'Get-PEHeader only supports binaries compiled for x86, AMD64, and ARM.'
|
|
return
|
|
|
|
}
|
|
|
|
# Need to get a new NT header in case the architecture changed
|
|
$NtHeader = [System.Runtime.InteropServices.Marshal]::PtrToStructure($PointerNtHeader, [Type] $PEStruct['NT_HEADER'])
|
|
# Display all section headers
|
|
$NumSections = $NtHeader.FileHeader.NumberOfSections
|
|
$NumRva = $NtHeader.OptionalHeader.NumberOfRvaAndSizes
|
|
$PointerSectionHeader = [IntPtr] ($PointerNtHeader.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf([Type] $PEStruct['NT_HEADER']))
|
|
$SectionHeaders = New-Object PSObject[]($NumSections)
|
|
foreach ($i in 0..($NumSections - 1))
|
|
{
|
|
$SectionHeaders[$i] = [System.Runtime.InteropServices.Marshal]::PtrToStructure(([IntPtr] ($PointerSectionHeader.ToInt64() + ($i * [System.Runtime.InteropServices.Marshal]::SizeOf([Type] [PE+_IMAGE_SECTION_HEADER])))), [Type] [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
|
|
|
|
}
|
|
|
|
if ($PSBoundParameters['GetSectionData'])
|
|
{
|
|
foreach ($i in 0..($NumSections - 1))
|
|
{
|
|
$RawBytes = $null
|
|
|
|
if ($OnDisk)
|
|
{
|
|
$RawBytes = New-Object Byte[]($SectionHeaders[$i].SizeOfRawData)
|
|
[Runtime.InteropServices.Marshal]::Copy([IntPtr] ($PEBaseAddr.ToInt64() + $SectionHeaders[$i].PointerToRawData), $RawBytes, 0, $SectionHeaders[$i].SizeOfRawData)
|
|
}
|
|
else
|
|
{
|
|
$RawBytes = New-Object Byte[]($SectionHeaders[$i].VirtualSize)
|
|
[Runtime.InteropServices.Marshal]::Copy([IntPtr] ($PEBaseAddr.ToInt64() + $SectionHeaders[$i].VirtualAddress), $RawBytes, 0, $SectionHeaders[$i].VirtualSize)
|
|
}
|
|
|
|
$SectionHeaders[$i] = Add-Member -InputObject ($SectionHeaders[$i]) -MemberType NoteProperty -Name RawData -Value $RawBytes -PassThru -Force
|
|
}
|
|
}
|
|
|
|
function Get-Exports()
|
|
{
|
|
|
|
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, [Type] [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'))"
|
|
$Result['RVA'] = "0x$($FunctionHashTable[$Key].ToString("X$($BinaryPtrWidth*2)"))"
|
|
#$Result['VA'] = "0x$(($FunctionHashTable[$Key] + $PEBaseAddr.ToInt64()).ToString("X$($BinaryPtrWidth*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([Type] [PE+_IMAGE_IMPORT_DESCRIPTOR])))
|
|
$ImportDescriptor = [System.Runtime.InteropServices.Marshal]::PtrToStructure($ImportDescriptorPtr, [Type] [PE+_IMAGE_IMPORT_DESCRIPTOR])
|
|
if ($ImportDescriptor.OriginalFirstThunk -eq 0) { break }
|
|
$DllNamePtr = [IntPtr] ($PEBaseAddr.ToInt64() + $ImportDescriptor.Name)
|
|
if ($OnDisk) { $DllNamePtr = Convert-RVAToFileOffset $DllNamePtr }
|
|
$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([Type] $ThunkDataStruct)))
|
|
$FuncAddr = [System.Runtime.InteropServices.Marshal]::PtrToStructure($FuncAddrPtr, [Type] $ThunkDataStruct)
|
|
$OFTPtr = [IntPtr] ($FirstOFTPtr.ToInt64() + ($j * [System.Runtime.InteropServices.Marshal]::SizeOf([Type] $ThunkDataStruct)))
|
|
$ThunkData = [System.Runtime.InteropServices.Marshal]::PtrToStructure($OFTPtr, [Type] $ThunkDataStruct)
|
|
$Result = @{ ModuleName = $DllName }
|
|
|
|
if (([System.Convert]::ToString($ThunkData.AddressOfData, 2)).PadLeft(32, '0')[0] -eq '1')
|
|
{
|
|
# Trim high order bit in order to get the ordinal value
|
|
$TempOrdinal = [System.Convert]::ToInt64(([System.Convert]::ToString($ThunkData.AddressOfData, 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['RVA'] = "0x$($FuncAddr.AddressOfData.ToString("X$($BinaryPtrWidth*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 DownloadFromMSSymbolServer -Value $ScriptBlock -PassThru -Force
|
|
|
|
return $PEHeader
|
|
|
|
}
|
|
|
|
} |