psuspend.psm1
#requires -version 6 using namespace System.Linq using namespace System.Reflection using namespace System.ComponentModel using namespace System.Reflection.Emit 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 psrsm -Value Resume-Process function Resume-Process { [CmdletBinding(DefaultParameterSetName='Name')] param( [Parameter(Mandatory, ParameterSetName='Name', Position=0)] [ValidateScript({!!($script:ps = Get-Process $_ -ErrorAction 0)})] [ValidateNotNullOrEmpty()] [String]$Name, [Parameter(Mandatory, ParameterSetName='Id', Position=0)] [ValidateScript({!!($script:ps = Get-Process -Id $_ -ErrorAction 0)})] [Int32]$Id ) process { $ntdll = New-Delegate ntdll -Signature @{ NtResumeProcess = [Func[IntPtr, Int32]] RtlNtStatusToDosError = [Func[Int32, Int32]] } $ps.ForEach{ if ([Enumerable]::Sum([Int32[]]( Select-Object -InputObject $_.Threads[0] -Property ThreadState, WaitReason ).PSObject.Properties.Value.ForEach{$_ -eq 5}) -eq 2) { if (($nts = $ntdll.NtResumeProcess.Invoke($_.Handle)) -ne 0) { Write-Verbose "$([Win32Exception]::new( $ntdll.RtlNtStatusToDosError.Invoke($nts) ).Message)" } else { Write-Verbose "Process $($_.Id) is resumed." } } else { Write-Verbose "Process $($_.Id) is already active." } $_.Dispose() } } } Set-Alias -Name pspnd -Value Suspend-Process function Suspend-Process { [CmdletBinding(DefaultParameterSetName='Name')] param( [Parameter(Mandatory, ParameterSetName='Name', Position=0)] [ValidateScript({!!($script:ps = Get-Process $_ -ErrorAction 0)})] [ValidateNotNullOrEmpty()] [String]$Name, [Parameter(Mandatory, ParameterSetName='Id', Position=0)] [ValidateScript({!!($script:ps = Get-Process -Id $_ -ErrorAction 0)})] [Int32]$Id ) process { $ntdll = New-Delegate ntdll -Signature @{ NtSuspendProcess = [Func[IntPtr, Int32]] RtlNtStatusToDosError = [Func[Int32, Int32]] } $ps.ForEach{ if ([Enumerable]::Sum([Int32[]]( Select-Object -InputObject $_.Threads[0] -Property ThreadState, WaitReason ).PSObject.Properties.Value.ForEach{$_ -eq 5}) -ne 2) { if (($nts = $ntdll.NtSuspendProcess.Invoke($_.Handle)) -ne 0) { Write-Verbose "$([Win32Exception]::new( $ntdll.RtlNtStatusToDosError.Invoke($nts) ).Message)" } else { Write-Verbose "Process $($_.Id) is suspended." } } else { Write-Verbose "Process $($_.Id) is already suspended." } $_.Dispose() } } } Export-ModuleMember -Alias psrsm, pspnd -Function Resume-Process, Suspend-Process |