<< Back to Script Library

Show Unsigned Service Binaries

Query all services, both system and per-user, get the binary responsible for the service, which will be a dll when the service executable is svchost,exe and check its digital signature.
By default, only services whose binary is not validly signed will be shown but parameters are available to show any signing state and to show all services or just non-Microsoft ones
Version: 1.0.6
Created: 2023-02-23
Modified: 2023-02-23
Creator: Guy Leech
Downloads: 2
Tags: applications auditing code signing DLL exe security troubleshooting windows
The Script Copy Script Copied to clipboard
#requires -version 3

<#
.SYNOPSIS
    Get the exe or dll used in every service, show details and check signature

.PARAMETER showAllSigning
    Show services where the executable has any signing state.
    The default is to only show those where signing is not valid

.PARAMETER showNonMicrosoftOnly
    Show services not from Microsoft.
    The default is to show those from any vendor including Microsoft.
    Setting this to 'yes' and passing -showAllSigning 'yes' which show all services not from Microsoft

.NOTES
    Modification History:

    2023/19/01  @guyrleech  Initial release
#>

[CmdletBinding()]

Param
(
    [ValidateSet('Yes','No')]
    [string]$showAllSigning = 'No' ,
    [ValidateSet('Yes','No')]
    [string]$showNonMicrosoftOnly = 'No'
)

#Region ControlUp_Standards
$VerbosePreference = $(if( $PSBoundParameters[ 'verbose' ] ) { $VerbosePreference } else { 'SilentlyContinue' })
$DebugPreference = $(if( $PSBoundParameters[ 'debug' ] ) { $DebugPreference } else { 'SilentlyContinue' })
$ErrorActionPreference = $(if( $PSBoundParameters[ 'erroraction' ] ) { $ErrorActionPreference } else { 'Stop' })
$ProgressPreference = 'SilentlyContinue'

[int]$outputWidth = 400
if( ( $PSWindow = (Get-Host).UI.RawUI ) -and ( $WideDimensions = $PSWindow.BufferSize ) )
{
    try
    {
        $WideDimensions.Width = $outputWidth
        $PSWindow.BufferSize = $WideDimensions
    }
    catch
    {
        ## Nothing we can do but shouldn't cause script to end
    }
}
#EndRegion ControlUp_Standards

[array]$services = @( Get-Service )
[string]$baseServicesKey = 'HKLM:SYSTEM\CurrentControlSet\Services'

[array]$results = @( Get-CimInstance -ClassName win32_service | ForEach-Object `
{ 
    $service = $_
    $executableFile = $null
    [string]$serviceType = 'OwnProcess'
    [string]$serviceName = $service.Name
    ## win32_service doesn't have the service type that tells us whether it is a user service or not
    if( $userService = $services | Where-Object { $_.Name -eq $service.Name -and ($_.ServiceType -as [int]) -band 0xC0 }  )
    {
        $serviceName = $userService.name -replace '_([0-9a-f]+)$'
    }

    if( $service.pathname -match '\\svchost\.exe\b' )
    {
        $serviceType = 'SharedProcess'
        if( -Not ( $executableFile = Get-ItemProperty -Path "$BaseServicesKey\$($servicename)\Parameters" -name ServiceDll -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Servicedll ) `
            -and -Not ( $executableFile = Get-ItemProperty -Path "$BaseServicesKey\$($servicename)" -name ServiceDll -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Servicedll ))
        {
            Write-Warning -Message "Could not find a ServiceDll registry value for svchost based service $($service.Name) ($($service.DisplayName))"
        }
        elseif( $userservice )
        {
            $serviceType = 'UserSharedProcess'
        }
    }
    ## get exe in quotes, eg has spaces in path and then arguments, or no spaces in path but possibly space following if arguments
    elseif( $service.PathName -match  '^"([^"]+)"' -or $service.PathName -match '^([^\s]+)' )
    {
        $executableFile = $Matches[1]
        if( $userservice )
        {
            $serviceType = 'UserOwnProcess'
        }
    }
    else
    {
        Write-Warning -Message "Couldn't get executable from $($_.Pathname) for service ($_.Name) ($($_.DisplayName))"
    }

    $versionInfo = $null

    if( $executableFile )
    {
        $versionInfo = Get-ItemProperty -Path $executableFile -ErrorAction SilentlyContinue | Select-Object -ExpandProperty versioninfo -ErrorAction SilentlyContinue
    }

    Select-Object -InputObject $service -Property @(
        @{ n='Name'      ; e={$serviceName}} , 
        'Displayname' ,
        'StartMode' ,
        @{ n='Account'   ; e={$_.StartName }} ,
        'State' ,
        ##'Pathname' ,
        @{ n = 'Type'    ; e = { $serviceType }} ,
        @{ n = 'Binary'  ; e = { $executableFile }},
        ## some VMware executables don't populate companyname :-(
        @{ n = 'Vendor'  ; e = { if( $versionInfo -and [string]::IsNullOrEmpty( $versionInfo.CompanyName )) { $versionInfo.LegalCopyright } else { Select-Object -InputObject $versionInfo -ExpandProperty companyname -ErrorAction SilentlyContinue }}},
        @{ n = 'Signing' ; e = { Get-AuthenticodeSignature -FilePath $executableFile -ErrorAction SilentlyContinue | Select -ExpandProperty Status }}  ,
        @{ n = 'Delete Flag' ; e = { Get-ItemProperty -Path "$BaseServicesKey\$($servicename)" -name DeleteFlag -ErrorAction SilentlyContinue | Select-Object -ExpandProperty DeleteFlag }})
})

$results | Where-Object { ( $showAllSigning -ieq 'yes' -or $_.Signing -ine 'valid' ) -and ($showNonMicrosoftOnly -ieq 'no' -or ( $showNonMicrosoftOnly -ieq 'yes' -and ( -Not $_.Vendor -or$_.Vendor -notmatch 'Microsoft' ))) } | Sort-Object -property DisplayName | Format-Table -AutoSize