Skip to content

Instantly share code, notes, and snippets.

@jsecurity101
Last active July 31, 2023 00:00
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save jsecurity101/6b9e87f5a428f31d41ffc8c1ee05a999 to your computer and use it in GitHub Desktop.
Save jsecurity101/6b9e87f5a428f31d41ffc8c1ee05a999 to your computer and use it in GitHub Desktop.
Powershell script that will pull whether a process or service is running as protected (PPL).
#Author: Jonthan Johnson (@jsecurity101)
if (-not ('ProtectedObjects.ProcessNativeMethods' -as [Type])) {
$TypeDef = @'
using System;
using System.Runtime.InteropServices;
namespace ProtectedObjects {
[Flags]
public enum ProcessAccess {
AllAccess = 0x001FFFFF,
Terminate = 0x00000001,
CreateThread = 0x00000002,
VirtualMemoryOperation = 0x00000008,
VirtualMemoryRead = 0x00000010,
VirtualMemoryWrite = 0x00000020,
DuplicateHandle = 0x00000040,
CreateProcess = 0x000000080,
SetQuota = 0x00000100,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
QueryLimitedInformation = 0x00001000,
Synchronize = 0x00100000
}
[Flags]
public enum PROCESSINFOCLASS
{
ProcessProtectionInformation = 0x3D,
}
[StructLayout(LayoutKind.Sequential)]
public struct _PsProtection
{
public PsProtectedType Type;
public PsProtectedSigner Signer;
public bool Audit;
}
[Flags]
public enum PsProtectedType
{
PsProtectedTypeNone = 0x0,
PsProtectedTypeProtectedLight = 0x1,
PsProtectedTypeProtected = 0x2,
PsProtectedTypeMax = 0x3,
}
[Flags]
public enum PsProtectedSigner
{
PsProtectedSignerNone = 0x0,
PsProtectedSignerAuthenticode = 0x1,
PsProtectedSignerCodeGen = 0x2,
PsProtectedSignerAntimalware = 0x3,
PsProtectedSignerLsa = 0x4,
PsProtectedSignerWindows = 0x5,
PsProtectedSignerWinTcb = 0x6,
PsProtectedSignerMax = 0x7,
}
public class ProcessNativeMethods {
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
ProcessAccess processAccess,
bool bInheritHandle,
int processId);
[DllImport("ntdll.dll", SetLastError=true)]
public static extern int NtQueryInformationProcess(
IntPtr processHandle,
PROCESSINFOCLASS processInformationClass,
ref _PsProtection processInformation,
int processInformationLength,
ref int returnLength);
[DllImport("kernel32.dll", SetLastError=true)]
public static extern bool CloseHandle(
IntPtr hHandle);
}
}
'@
Add-Type -TypeDefinition $TypeDef
}
function Get-ProtectedProcess {
<#
.SYNOPSIS
Performs an audit on processes to find their protection level.
.DESCRIPTION
Get-ProtectedProcess performs a protected process level audit on current running processes.
.PARAMETER ProcessId
Specifies process identifier of the desired process to be audited.
.PARAMETER ReturnOnlyProtected
Switch to only return processes where the protection level is greater than (0x0) PsProtectedTypeNone
.EXAMPLE
$Result = Get-ProtectedProcess -ReturnOnlyProtected
.EXAMPLE
$Result = Get-Process lsass | Get-ProtectedProcess
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipelineByPropertyName)]
[Int32]
[Alias('Id')]
$ProcessId,
[switch]
$ReturnOnlyProtected
)
if (($PSBoundParameters.ContainsKey('ProcessId')))
{
$Processes = Get-Process -Id $ProcessId
}
else{
$Processes = Get-Process
}
foreach ($p in $Processes)
{
$ProcessHandle = [ProtectedObjects.ProcessNativeMethods]::OpenProcess(
'QueryLimitedInformation',
$False,
$p.Id
);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
if($ProcessHandle -eq [IntPtr]::Zero){
Write-Warning $LastError
continue
}
$ppl = New-Object ProtectedObjects._PsProtection
$Size = [System.Runtime.InteropServices.Marshal]::SizeOf($ppl)
$returnLength = New-Object Int
[int]$Status = [ProtectedObjects.ProcessNativeMethods]::NtQueryInformationProcess(
$ProcessHandle,
'ProcessProtectionInformation',
[ref]$ppl,
$Size,
[ref]$returnLength
);$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
$null = [ProtectedObjects.ProcessNativeMethods]::CloseHandle($ProcessHandle)
if($Status -ne 0){
Write-Error $LastError
return
}
else{
$level = ('{0:x}' -f (([byte]$ppl.Type) -bor $ppl.Audit -bor ([int]$ppl.Signer -shl 4)))
switch($level)
{
0
{
$levelStr = "PsProtectedTypeNone"
}
11
{
$levelStr = "PsProtectedSignerAuthenticode-Light"
}
21
{
$levelStr ="PsProtectedSignerCodeGen-Light"
}
31
{
$levelStr = "PsProtectedSignerAntimalware-Light"
}
41
{
$levelStr = "PsProtectedSignerLsa-Light"
}
51
{
$levelStr = "PsProtectedSignerWindows-Light"
}
61
{
$levelStr = "PsProtectedSignerWinTcb-Light"
}
71
{
$levelStr = "PsProtectedSignerMax-Light"
}
12
{
$levelStr = "PsProtectedSignerAuthenticode"
}
22
{
$levelStr = "PsProtectedSignerCodeGen"
}
32
{
$levelStr = "PsProtectedSignerAntimalware"
}
42
{
$levelStr = "PsProtectedSignerLsa"
}
52
{
$levelStr = "PsProtectedSignerWindows"
}
62
{
$levelStr = "PsProtectedSignerWinTcb"
}
72
{
$levelStr = "PsProtectedSignerMax"
}
13
{
$levelStr = "PsProtectedSignerAuthenticode-Max"
}
23
{
$levelStr = "PsProtectedSignerCodeGen-Max"
}
33
{
$levelStr = "PsProtectedSignerAntimalware-Max"
}
43
{
$levelStr = "PsProtectedSignerLsa-Max"
}
53
{
$levelStr = "PsProtectedSignerWindows-Max"
}
63
{
$levelStr = "PsProtectedSignerWinTcb-Max"
}
73
{
$levelStr = "PsProtectedSignerMax-Max"
}
}
if ($ReturnOnlyProtected){
if ($levelStr -ne "PsProtectedTypeNone"){
[PSCustomObject] @{
ProcessName = $p.Name
ProcessId = $p.Id
ProtectedLevelHex = '0x'+$level
ProtectedLevel = $levelStr
}
}
else{
}
}
else{
[PSCustomObject] @{
ProcessName = $p.Name
ProcessId = $p.Id
ProtectedLevelHex = '0x'+$level
ProtectedLevel = $levelStr
}
}
}
}
}
function Get-ProtectedService {
<#
.SYNOPSIS
Performs an audit on services to find their protection level.
.DESCRIPTION
Get-ProtectedService performs an audit to see the protection level a service is launched as.
.PARAMETER ServiceName
Specifies the target service.
.EXAMPLE
$Result = Get-ProtectedService -ServiceName WinDefend
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipelineByPropertyName)]
[string]
$ServiceName
)
if (-not ($PSBoundParameters.ContainsKey('ServiceName'))){
$Services = Get-CimInstance Win32_Service -Filter "State = `"Running`""
}
else{
$Services = Get-CimInstance Win32_Service -Filter "Name = `"$ServiceName`""
}
foreach ($service in $Services.Name) {
$query = Get-ItemProperty Registry::HKLM\SYSTEM\CurrentControlSet\Services\$service
if ($query.LaunchProtected -ne $null){
switch($query.LaunchProtected){
0
{
$LaunchProtected = 'None'
}
1
{
$LaunchProtected = 'Windows'
}
2
{
$LaunchProtected = 'WindowsLight'
}
3
{
$LaunchProtected = 'AntiMalwareLight'
}
}
[PSCustomObject] @{
ServiceName = $service
ProtectedLevelHex = $query.LaunchProtected
ProtectedLevel = $LaunchProtected
}
}
}
}
function Get-ProtectedProperties {
$ProtectedProcess = Get-ProtectedProcess -ReturnOnlyProtected
$ProtectedService = Get-ProtectedService
[PSCustomObject] @{
Process = $ProtectedProcess
Service = $ProtectedService
}
}
@roelds
Copy link

roelds commented Jan 19, 2023

your post led me here
great script! an alternative is also using sc:

sc qprotection windefend | findstr ":"
SERVICE windefend PROTECTION LEVEL: ANTIMALWARE LIGHT.
sc qprotection sgrmbroker | findstr ":"
SERVICE sgrmbroker PROTECTION LEVEL: WINDOWS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment