diff options
author | bitform <matt@exploit-monday.com> | 2013-01-02 20:48:10 -0500 |
---|---|---|
committer | bitform <matt@exploit-monday.com> | 2013-01-02 20:48:10 -0500 |
commit | 7734cb5b347a3befe866308fe20df39c0398f47f (patch) | |
tree | e0e484d0717d01f181b154c55be2d8e331877154 | |
parent | d2d6ee1409f5b31de8aa7d44598baacb115a3214 (diff) | |
download | PowerSploit-7734cb5b347a3befe866308fe20df39c0398f47f.tar.gz PowerSploit-7734cb5b347a3befe866308fe20df39c0398f47f.zip |
Added Get-StructFromMemory
Marshals data from an unmanaged block of memory in an arbitrary process
to a newly allocated managed object of the specified type. In other
words, it will parse and return a structure at a known memory address in
any process.
-rw-r--r-- | README | 4 | ||||
-rw-r--r-- | RE_Tools/Get-StructFromMemory.ps1 | 201 |
2 files changed, 205 insertions, 0 deletions
@@ -64,6 +64,10 @@ Get-KernelModuleInfo: Returns loaded kernel module information.
+Get-StructFromMemory.ps1:
+
+ Marshals data from an unmanaged block of memory in an arbitrary process to a newly allocated managed object of the specified type.
+
Get-Member:
A proxy function used to extend the built-in Get-Member cmdlet. It adds the '-Private' parameter allowing you to display non-public .NET members
diff --git a/RE_Tools/Get-StructFromMemory.ps1 b/RE_Tools/Get-StructFromMemory.ps1 new file mode 100644 index 0000000..d4d710a --- /dev/null +++ b/RE_Tools/Get-StructFromMemory.ps1 @@ -0,0 +1,201 @@ +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 Module - Get-StructFromMemory
+Author: Matthew Graeber (@mattifestation)
+License: BSD 3-Clause
+
+.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($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($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, $StructType)
+
+ [Runtime.InteropServices.Marshal]::FreeHGlobal($LocalStructPtr)
+ $SafeHandle.Close()
+
+ Write-Output $ParsedStruct
+}
\ No newline at end of file |