1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
|
function Get-StructFromMemory
{
<#
.SYNOPSIS
Marshals data from an unmanaged block of memory in an arbitrary process to a newly allocated managed object of the specified type.
PowerSploit Function: Get-StructFromMemory
Author: Matthew Graeber (@mattifestation)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
.DESCRIPTION
Get-StructFromMemory is similar to the Marshal.PtrToStructure method but will parse and return a structure from any process.
.PARAMETER Id
Process ID of the process whose virtual memory space you want to access.
.PARAMETER MemoryAddress
The address containing the structure to be parsed.
.PARAMETER StructType
The type (System.Type) of the desired structure to be parsed.
.EXAMPLE
C:\PS> Get-Process | ForEach-Object { Get-StructFromMemory -Id $_.Id -MemoryAddress $_.MainModule.BaseAddress -StructType ([PE+_IMAGE_DOS_HEADER]) }
Description
-----------
Parses the DOS headers of every loaded process. Note: In this example, this assumes that [PE+_IMAGE_DOS_HEADER] is defined. You can get the code to define [PE+_IMAGE_DOS_HEADER] here: http://www.exploit-monday.com/2012/07/structs-and-enums-using-reflection.html
.NOTES
Be sure to enclose the StructType parameter with parenthesis in order to force PowerShell to cast it as a Type object.
Get-StructFromMemory does a good job with error handling however it will crash if the structure contains fields that attempt to marshal pointers. For example, if a field has a custom attribute of UnmanagedType.LPStr, when the structure is parsed, it will attempt to dererence a string pointer for virtual memory in another process and access violate.
.LINK
http://www.exploit-monday.com
#>
[CmdletBinding()] Param (
[Parameter(Position = 0, Mandatory = $True)]
[Alias('ProcessId')]
[Alias('PID')]
[UInt16]
$Id,
[Parameter(Position = 1, Mandatory = $True)]
[IntPtr]
$MemoryAddress,
[Parameter(Position = 2, Mandatory = $True)]
[Alias('Type')]
[Type]
$StructType
)
Set-StrictMode -Version 2
$PROCESS_VM_READ = 0x0010 # The process permissions we'l ask for when getting a handle to the process
# Get a reference to the private GetProcessHandle method is System.Diagnostics.Process
$GetProcessHandle = [Diagnostics.Process].GetMethod('GetProcessHandle', [Reflection.BindingFlags] 'NonPublic, Instance', $null, @([Int]), $null)
try
{
# Make sure user didn't pass in a non-existent PID
$Process = Get-Process -Id $Id -ErrorVariable GetProcessError
# Get the default process handle
$Handle = $Process.Handle
}
catch [Exception]
{
throw $GetProcessError
}
if ($Handle -eq $null)
{
throw "Unable to obtain a handle for PID $Id. You will likely need to run this script elevated."
}
# Get a reference to MEMORY_BASIC_INFORMATION. I don't feel like making the structure myself
$mscorlib = [AppDomain]::CurrentDomain.GetAssemblies() | ? { $_.FullName.Split(',')[0].ToLower() -eq 'mscorlib' }
$Win32Native = $mscorlib.GetTypes() | ? { $_.FullName -eq 'Microsoft.Win32.Win32Native' }
$MEMORY_BASIC_INFORMATION = $Win32Native.GetNestedType('MEMORY_BASIC_INFORMATION', [Reflection.BindingFlags] 'NonPublic')
if ($MEMORY_BASIC_INFORMATION -eq $null)
{
throw 'Unable to get a reference to the MEMORY_BASIC_INFORMATION structure.'
}
# Get references to private fields in MEMORY_BASIC_INFORMATION
$ProtectField = $MEMORY_BASIC_INFORMATION.GetField('Protect', [Reflection.BindingFlags] 'NonPublic, Instance')
$AllocationBaseField = $MEMORY_BASIC_INFORMATION.GetField('BaseAddress', [Reflection.BindingFlags] 'NonPublic, Instance')
$RegionSizeField = $MEMORY_BASIC_INFORMATION.GetField('RegionSize', [Reflection.BindingFlags] 'NonPublic, Instance')
try { $NativeUtils = [NativeUtils] } catch [Management.Automation.RuntimeException] # Only build the assembly if it hasn't already been defined
{
# Build dynamic assembly in order to use P/Invoke for interacting with the following Win32 functions: ReadProcessMemory, VirtualQueryEx
$DynAssembly = New-Object Reflection.AssemblyName('MemHacker')
$AssemblyBuilder = [AppDomain]::CurrentDomain.DefineDynamicAssembly($DynAssembly, [Reflection.Emit.AssemblyBuilderAccess]::Run)
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('MemHacker', $False)
$Attributes = 'AutoLayout, AnsiClass, Class, Public, SequentialLayout, Sealed, BeforeFieldInit'
$TypeBuilder = $ModuleBuilder.DefineType('NativeUtils', $Attributes, [ValueType])
$TypeBuilder.DefinePInvokeMethod('ReadProcessMemory', 'kernel32.dll', [Reflection.MethodAttributes] 'Public, Static', [Reflection.CallingConventions]::Standard, [Bool], @([IntPtr], [IntPtr], [IntPtr], [UInt32], [UInt32].MakeByRefType()), [Runtime.InteropServices.CallingConvention]::Winapi, 'Auto') | Out-Null
$TypeBuilder.DefinePInvokeMethod('VirtualQueryEx', 'kernel32.dll', [Reflection.MethodAttributes] 'Public, Static', [Reflection.CallingConventions]::Standard, [UInt32], @([IntPtr], [IntPtr], $MEMORY_BASIC_INFORMATION.MakeByRefType(), [UInt32]), [Runtime.InteropServices.CallingConvention]::Winapi, 'Auto') | Out-Null
$NativeUtils = $TypeBuilder.CreateType()
}
# Request a handle to the process in interest
try
{
$SafeHandle = $GetProcessHandle.Invoke($Process, @($PROCESS_VM_READ))
$Handle = $SafeHandle.DangerousGetHandle()
}
catch
{
throw $Error[0]
}
# Create an instance of MEMORY_BASIC_INFORMATION
$MemoryBasicInformation = [Activator]::CreateInstance($MEMORY_BASIC_INFORMATION)
# Confirm you can actually read the address you're interested in
$NativeUtils::VirtualQueryEx($Handle, $MemoryAddress, [Ref] $MemoryBasicInformation, [Runtime.InteropServices.Marshal]::SizeOf([Type] $MEMORY_BASIC_INFORMATION)) | Out-Null
$PAGE_EXECUTE_READ = 0x20
$PAGE_EXECUTE_READWRITE = 0x40
$PAGE_READONLY = 2
$PAGE_READWRITE = 4
$Protection = $ProtectField.GetValue($MemoryBasicInformation)
$AllocationBaseOriginal = $AllocationBaseField.GetValue($MemoryBasicInformation)
$GetPointerValue = $AllocationBaseOriginal.GetType().GetMethod('GetPointerValue', [Reflection.BindingFlags] 'NonPublic, Instance')
$AllocationBase = $GetPointerValue.Invoke($AllocationBaseOriginal, $null).ToInt64()
$RegionSize = $RegionSizeField.GetValue($MemoryBasicInformation).ToUInt64()
Write-Verbose "Protection: $Protection"
Write-Verbose "AllocationBase: $AllocationBase"
Write-Verbose "RegionSize: $RegionSize"
if (($Protection -ne $PAGE_READONLY) -and ($Protection -ne $PAGE_READWRITE) -and ($Protection -ne $PAGE_EXECUTE_READ) -and ($Protection -ne $PAGE_EXECUTE_READWRITE))
{
$SafeHandle.Close()
throw 'The address specified does not have read access.'
}
$StructSize = [Runtime.InteropServices.Marshal]::SizeOf([Type] $StructType)
$EndOfAllocation = $AllocationBase + $RegionSize
$EndOfStruct = $MemoryAddress.ToInt64() + $StructSize
if ($EndOfStruct -gt $EndOfAllocation)
{
$SafeHandle.Close()
throw 'You are attempting to read beyond what was allocated.'
}
try
{
# Allocate unmanaged memory. This will be used to store the memory read from ReadProcessMemory
$LocalStructPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($StructSize)
}
catch [OutOfMemoryException]
{
throw Error[0]
}
Write-Verbose "Memory allocated at 0x$($LocalStructPtr.ToString("X$([IntPtr]::Size * 2)"))"
# Zero out the memory that was just allocated. According to MSDN documentation:
# "When AllocHGlobal calls LocalAlloc, it passes a LMEM_FIXED flag, which causes the allocated memory to be locked in place. Also, the allocated memory is not zero-filled."
# http://msdn.microsoft.com/en-us/library/s69bkh17.aspx
$ZeroBytes = New-Object Byte[]($StructSize)
[Runtime.InteropServices.Marshal]::Copy($ZeroBytes, 0, $LocalStructPtr, $StructSize)
$BytesRead = [UInt32] 0
if ($NativeUtils::ReadProcessMemory($Handle, $MemoryAddress, $LocalStructPtr, $StructSize, [Ref] $BytesRead))
{
$SafeHandle.Close()
[Runtime.InteropServices.Marshal]::FreeHGlobal($LocalStructPtr)
throw ([ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error())
}
Write-Verbose "Struct Size: $StructSize"
Write-Verbose "Bytes read: $BytesRead"
$ParsedStruct = [Runtime.InteropServices.Marshal]::PtrToStructure($LocalStructPtr, [Type] $StructType)
[Runtime.InteropServices.Marshal]::FreeHGlobal($LocalStructPtr)
$SafeHandle.Close()
Write-Output $ParsedStruct
}
|