Public/Get-HiddenServices.ps1

<#
    .SYNOPSIS
    Identifies hidden Windows services and provides detailed information about them.
 
    .DESCRIPTION
    The Get-HiddenServices function compares the list of services retrieved from the Windows Service Manager
    with the services listed in the registry. It identifies services that are present in the registry but
    not visible in the Service Manager. For each hidden service, it retrieves detailed information such as
    the display name, start type, image path, owner, description, and whether a corresponding process is running.
 
    .OUTPUTS
    PSCustomObject
        Outputs a custom object for each hidden service with the following properties:
        - Name: The name of the service.
        - DisplayName: The display name of the service.
        - StartType: The start type of the service in a human-readable format.
        - IsRunning: Indicates whether a process corresponding to the service is running.
        - ImagePath: The executable path of the service.
        - Owners: The owner(s) of the service.
        - Description: A description of the service.
 
    .EXAMPLE
    Get-HiddenServices
 
    This command retrieves and displays information about all hidden services on the system.
 
    .Link
    https://github.com/TheTaylorLee/AdminToolbox
#>


function Get-HiddenServices {

    [CmdletBinding()]
    param (
    )

    $reference = Get-Service |
        Select-Object -ExpandProperty Name |
        ForEach-Object { $_ -replace "_[0-9a-f]{2,8}$" }
    $difference = Get-ChildItem -Path hklm:\system\currentcontrolset\services |
        ForEach-Object { $_.Name -Replace "HKEY_LOCAL_MACHINE\\", "HKLM:\" } |
        Where-Object { Get-ItemProperty -Path "$_" -Name objectname -ErrorAction 'ignore' } |
        ForEach-Object { $_.substring(40) }

    $hiddenServices = Compare-Object -ReferenceObject $reference -DifferenceObject $difference -PassThru |
        Where-Object { $_.sideIndicator -eq "=>" }

    foreach ($service in $hiddenServices) {
        $servicePath = "HKLM:\system\currentcontrolset\services\$service"
        $serviceDetails = Get-ItemProperty -Path $servicePath -ErrorAction 'Ignore'

        # Translate StartType to human-readable format
        $startTypeReadable = switch ($serviceDetails.Start) {
            0 { "Boot" }
            1 { "System" }
            2 { "Automatic" }
            3 { "Manual" }
            4 { "Disabled" }
            Default { "Unknown" }
        }

        # Check if a process is running based on the ImagePath
        $imagePath = $serviceDetails.ImagePath -replace '"', '' # Remove quotes if present
        $isRunning = Get-Process | Where-Object { $_.Path -eq $imagePath } | ForEach-Object { $_.Name }

        [PSCustomObject]@{
            Name        = $service
            DisplayName = $serviceDetails.DisplayName
            StartType   = $startTypeReadable
            IsRunning   = if ($isRunning) { "Yes ($isRunning)" } else { "No" }
            ImagePath   = $serviceDetails.ImagePath
            Owners      = $serviceDetails.Owners
            Description = $serviceDetails.Description
        }
    }
}