Check for non-Microsoft signed Windows Services

I have a lot of Windows Services on my Windows 11 laptop, mainly from Microsoft itself or third-party, but Microsoft signed are safe(r). In this blog post, I will show you how you can scan your system for non-Microsoft signed Windows Services and create a report on those.

What are Microsoft signed Windows Services?

A Window Service is a task/process that runs automatically or manually on your system as a background process. Most Windows Services are built-in, but other software can add Windows Services to your system. Inside the properties of a Windows Service, you will find the executable being started. C:\WINDOWS\system32\svchost.exe, for example, is the one that Windows Services uses to start the built-in/required services.

Inside the properties of the executable, there is a Digital Signatures tab with a signature list containing the name of the signer(s). Microsoft Services are signed by a certificate that has been issued to Microsoft Corporation:

What does the script do?

The script checks all the Windows Services on your (or a remote) system and reports which ones are not signed by Microsoft by using the Get-AuthenticodeSignature cmdlet. These Windows Services are third-party and could potentially be dangerous. It is worth checking, in my opinion. Some third-party Windows Services are signed by, for example, Lenovo but also by Microsoft. These will not be reported, for example:

I had some ‘fun’ with RegEx in the script, too. I must master that skill in the future. 🙁

Running the script

Scanning one system

You can start the script without any parameter, and it will then connect to your local system and show the results in an Out-GridView pane. It will show you how many services it will scan and a progress bar. For example:

.\Get-Non_Microsoft_Signed_Services.ps1

If there are no results found, it will report that on your screen:

Scanning multiple systems

You can also run the script with the -ComputerName parameter to specify multiple systems separated by a comma. In the example below, I ran it on my test Domain Controller to scan that server and the w2k22member server. The W2K22DC server had no results, and the W2K22Member server did for Google Chrome.

.\Get-Non_Microsoft_Signed_Services.ps1 -ComputerName w2k22dc, w2k22member
Retrieving information for 228 services on w2k22dc
Retrieving information for 196 services on w2k22member

Note: Your account must have permissions on the remote system. It will not prompt you for it.

Saving the results to an Excel file

To get the results in a Microsoft Excel file, you can use the -Filename parameter. Results will not be shown in an Out-GridView pane anymore but will be saved in the specified Excel file. If the specified Excel file exists, it will add a Services tab to the file with a time stamp and insert the results there.

Note: The Excel filename must end with .xlsx, and the path must be accessible. The script will check for that and install the ImportExcel PowerShell module if it is not already present on your system.

For example:

.\Get-Non_Microsoft_Signed_Services.ps1' -Filename C:\Data\Services.xlsx
Specified C:\Data\Services.xlsx filename is correct, and the path is accessible, continuing...
The ImportExcel module was found on the system, continuing...
Retrieving information for 310 services on NEXXT-592
Exported Non-Microsoft Signed Services to C:\Data\Services.xlsx

The Services.xlsx Excel File will look like this:

The script

Below are the contents of the script. Save it to c:\scripts\Get-Non_Microsoft_Signed_Services.ps1, for example.

[CmdletBinding()]
param (
    [Parameter(Mandatory = $false)][String[]]$ComputerName = $env:COMPUTERNAME,
    [parameter(Mandatory = $false)][string]$Filename
)

#Validate output $filename
if ($Filename) {
    if (-not ($Filename.EndsWith('.xlsx'))) {
        Write-Warning ("Specified {0} filename does not end with .xlsx, exiting..." -f $Filename)
        return
    }

    #Check access to the path, and if the file already exists, append if it does or test the creation of a new one
    if (-not (Test-Path -Path $Filename)) {
        try {
            New-Item -Path $Filename -ItemType File -Force:$true -Confirm:$false -ErrorAction Stop | Out-Null
            Remove-Item -Path $Filename -Force:$true -Confirm:$false | Out-Null
            Write-Host ("Specified {0} filename is correct, and the path is accessible, continuing..." -f $Filename) -ForegroundColor Green
        }
        catch {
            Write-Warning ("Path to specified {0} filename is not accessible, correct or file is in use, exiting..." -f $Filename)
            return
        }
    }
    else {
        Write-Warning ("Specified file {0} already exists, appending data to it..." -f $Filename)
    }

    #Check if the ImportExcel module is installed. Install it if not
    if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
        Write-Warning ("The ImportExcel module was not found on the system, installing now...")
        try {
            Install-Module -Name ImportExcel -SkipPublisherCheck -Force:$true -Confirm:$false -Scope CurrentUser -ErrorAction Stop
            Import-Module -Name ImportExcel -Scope Local -ErrorAction Stop
            Write-Host ("Successfully installed the ImportExcel module, continuing..") -ForegroundColor Green
        }
        catch {
            Write-Warning ("Could not install the ImportExcel module, exiting...")
            return
        }
    }
    else {
        Write-Host ("The ImportExcel module was found on the system, continuing...") -ForegroundColor Green
    }
}

#Set $total to $null for when running script multiple times in one session
$total = $null

#Retrieve services on system and check if they are signed using a Microsoft signature (Trusted)
$total = foreach ($Computer in $ComputerName) {
    try {
        $count = 0
        $services = Get-WmiObject -Class Win32_Service -ComputerName $Computer -ErrorAction Stop | Select-Object Displayname, Name, PathName, State, StartName, StartMode | Sort-Object DisplayName
        Write-Host ("Retrieving information for {0} services on {1}" -f $services.count, $($Computer)) -ForegroundColor Green
        foreach ($service in $services) {
            $count++
            Write-Progress ("Checking service {0} on {1}" -f $service.Displayname, $($Computer)) -PercentComplete (($count * 100) / $services.count) -Status "$(([math]::Round((($count)/$services.count * 100),0))) %"
            if ($null -ne $service.PathName) {
                $servicepath = $service.PathName -replace '^(?:"(.+?)"|([^ ]+)).*', '$1$2'
                $servicepath = "\\$($computer)\$($servicepath.Substring(0,1))$" + "$($servicepath.Substring(2))"
                if (Test-Path -Path $servicepath) {
                    if (-not ((Get-AuthenticodeSignature $($servicepath)).SignerCertificate.Subject -match 'O=Microsoft Corporation')) {
                        [PSCustomObject]@{
                            ComputerName = $Computer
                            DisplayName  = $service.DisplayName
                            Name         = $service.Name
                            'Log on as'  = $service.StartName
                            StartMode    = $service.StartMode
                            State        = $service.State
                            Path         = $service.PathName
                        }
                    }
                }
            }
        }
    }
    catch {
        Write-Warning ("Error connecting {0}, skipping..." -f $Computer)
    }
}

#Output to GridView or Excel is results were found
if ($Total.Count -gt 0) {
    if (-not $Filename) {
        $Total | Sort-Object ComputerName, DisplayName | Out-GridView -Title 'Non-Microsoft Signed Services'
    }
    else {
        $Date = Get-Date -Format 'dd-MM-yyyy HH-mm'
        $Total | Export-Excel -Path $Filename -WorksheetName "Services_$($Date)" -AutoFilter -AutoSize -Append
        Write-Host ("Exported Non-Microsoft Signed Services to {0}" -f $Filename) -ForegroundColor Green
    } 
}
else {
    Write-Host ("No Non-Microsoft Signed Services found on {0}..." -f $ComputerName)
}

Download the script(s) from GitHub here

7 thoughts on “Check for non-Microsoft signed Windows Services

  1. Hi Harm,

    PowerShell beginner here. I’m trying to scan local device, i.e. run script (from an elevated PowerShell console) with no parameters, exactly as per first paragraph of the article.

    I just see:

    “WARNING: Error connecting DESKTOP-O6AC836, skipping…
    No Non-Microsoft Signed Services found on DESKTOP-O5AC738…”

    The device actually has 14 non-MS services installed.

    So… 2 issues. One is that the services are not being retrieved and the other is that the script doesn’t halt at the this error but continues to the next (output) stage.

    Test device is Windows 10 Pro 1809 running PS 5.1. I’ve also tried it on another test device running Windows 10 22H2/PS 5.1 with the same error results.

    Any ideas?

    Regards,

    • I tested it on my Windows 11 machine (PS5 and 7), Windows Sandbox and two Windows Servers 2022 (PS5) and on all of them I was an admin… Just tested it on a Windows 10 machine too (PS5) just now, same issue there. Seems that it doesn’t like Get-CimInstance. Let me check some things….

    • Changed it to Get-WmiObject and it now also works on my Windows 10 VM. Also added a bugfix, I had one service on my Windows 10 VM that had no executable in the Service properties. (It will skip services without it now)

      Could you try the updated version?

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.