psdump.psm1
#requires -version 6 using namespace System.IO using namespace System.Text using namespace System.Reflection using namespace System.Reflection.Emit using namespace Microsoft.Win32.SafeHandles using namespace System.Runtime.InteropServices function Get-ProcAddress { [OutputType([Hashtable])] [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [String]$Module, [Parameter(Mandatory, Position=1)] [ValidateNotNullOrEmpty()] [String[]]$Function ) process { $kernel32 = @{} [Assembly]::LoadFile("$( [RuntimeEnvironment]::GetRuntimeDirectory() )Microsoft.Win32.SystemEvents.dll").GetType('Interop').GetNestedType( 'Kernel32', [BindingFlags]'NonPublic' ).GetMethods([BindingFlags]'NonPublic, Static, Public').Where{ $_.Name -cmatch '\AGet(Proc|Mod)' }.ForEach{$kernel32[$_.Name] = $_} if (( $mod = $kernel32.GetModuleHandle.Invoke($null, @($Module)) ) -eq [IntPtr]::Zero) { throw [DllNotFoundException]::new() } $funcs = @{} $Function.ForEach{ if (( $$ = $kernel32.GetProcAddress.Invoke($null, @($mod, $_)) ) -ne [IntPtr]::Zero) { $funcs.$_ = $$ } } $funcs } } function Set-Delegate { [OutputType([Type])] [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateScript({$_ -ne [IntPtr]::Zero})] [IntPtr]$ProcAddress, [Parameter(Mandatory, Position=1)] [ValidateNotNull()] [Type]$Prototype, [Parameter(Position=2)] [ValidateNotNullOrEmpty()] [CallingConvention]$CallingConvention = 'StdCall' ) process { $method = $Prototype.GetMethod('Invoke') $returntype, $paramtypes = $method.ReturnType, $method.GetParameters().ParameterType $paramtypes = ($paramtypes, $null)[!$paramtypes] $il, $sz = ($holder = [DynamicMethod]::new( 'Invoke', $returntype, $paramtypes, $Prototype )).GetILGenerator(), [IntPtr]::Size if ($paramtypes) { (0..($paramtypes.Length - 1)).ForEach{$il.Emit([OpCodes]::ldarg, $_)} } $il.Emit([OpCodes]::"ldc_i$sz", $ProcAddress."ToInt$((32, 64)[$sz / 4 - 1])"()) $il.EmitCalli([OpCodes]::calli, $CallingConvention, $returntype, $paramtypes) $il.Emit([OpCodes]::ret) $holder.CreateDelegate($Prototype) } } function New-Delegate { [OutputType([Hashtable])] [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateNotNullOrEmpty()] [String]$Module, [Parameter(Mandatory, Position=1)] [ValidateNotNull()] [Hashtable]$Signature ) process { $funcs, $addr = @{}, (Get-ProcAddress -Module $Module -Function $Signature.Keys) $addr.Keys.ForEach{ $funcs.$_ = Set-Delegate -ProcAddress $addr.$_ -Prototype $Signature.$_ } $funcs } } Set-Alias -Name psdump -Value Get-ProcessDump function Get-ProcessDump { [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [ValidateScript({!!($script:ps = Get-Process -Id $_ -ErrorAction 0)})] [Int32]$Id, [Parameter()] [ValidateNotNullOrEmpty()] [ValidateSet('MiniDump', 'FullDump')] [String]$DumpType = 'MiniDump', [Parameter()] [ValidateNotNullOrEmpty()] [ValidateScript({Test-Path $_})] [String]$SavePath = $pwd.Path ) begin { $kernel32, $dmp = (New-Delegate kernel32 -Signature @{ LoadLibraryW = [Func[[Byte[]], IntPtr]] FreeLibrary = [Func[IntPtr, Boolean]] }), "${SavePath}\$($ps.Name)_${Id}.dmp" } process {} end { try { if (!($dll = $kernel32.LoadLibraryW.Invoke( [Encoding]::Unicode.GetBytes('dbghelp.dll') ))) { throw [DllNotFoundException]::new() } $dbghelp, $fs, $numeric = (New-Delegate dbghelp -Signature @{ MiniDumpWriteDump = [Func[IntPtr, UInt32, SafeFileHandle, UInt32, IntPtr, IntPtr, IntPtr, Boolean]] }), [File]::Create($dmp), (6, 261)[$DumpType -eq 'MiniDump'] if (!$dbghelp.MiniDumpWriteDump.Invoke( $ps.Handle, ${Id}, $fs.SafeFileHandle, $numeric, [IntPtr]::Zero, [IntPtr]::Zero, [IntPtr]::Zero )) { $err = $true throw [InvalidOperationException]::new() } } catch { Write-Verbose $_ } finally { if ($fs) { $fs.Dispose() } if ($dll) { if (!$kernel32.FreeLibrary.Invoke($dll)) { Write-Verbose 'Could not release dbghelp.dll library.' } } if ($err) { Remove-Item $dmp -Force -ErrorAction 0 } } $ps.Dispose() } } Export-ModuleMember -Alias psdump -Function Get-ProcessDump |