ps/Modules/Cole.PowerShell.Developer/Public/Get-AllServices.ps1

198 lines
9.4 KiB
PowerShell
Raw Normal View History

2023-05-30 22:51:22 -07:00
function Get-AllServices {
<#
.SYNOPSIS
Used to find all services on a system but with all the details that we might use
Queries the registry to find all services that have at least an ImagePath, but filters out a majority of Windows Services (not all)
.PARAMETER All
Returns everything that wasn't filtered out for other reasons
.PARAMETER Name
Used to match names
.PARAMETER Path
Used to match paths
.PARAMETER Fragment
Used to search for any occurrence on path or name
.PARAMETER Exact
Used to specify matching an exact path or name
#>
[CmdletBinding(DefaultParameterSetName = 'All')]
param (
[Parameter(ParameterSetName = 'All')]
[switch]$All,
[Parameter(Mandatory = $true, ParameterSetName = 'Name')]
[Alias('NameLike')]
[Alias('NamePartial')]
[Alias('NameFragment')]
[string]$Name,
[Parameter(Mandatory = $true, ParameterSetName = 'Path')]
[Alias('PathLike')]
[Alias('PathPartial')]
[Alias('PathFragment')]
[string]$Path,
[Parameter(Mandatory = $true, ParameterSetName = 'Fragment')]
[Alias('Query')]
[string]$Fragment,
[Parameter(Mandatory = $false, ParameterSetName = 'Name')]
[Parameter(Mandatory = $false, ParameterSetName = 'Path')]
[switch]$Exact,
[switch]$RefreshCache
)
begin {
$logLead = (Get-LogLeadName)
# Define the defaults for the ServiceController response to add some custom display information
$defaultTypeName = 'ServiceObject'
$defaultKeys = @('Name')
$defaultDisplaySet = @('Name', 'RunAs', 'StartMode', 'ExePath')
$defaultDisplayPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultDisplayPropertySet',[string[]]$defaultDisplaySet)
$defaultKeyPropertySet = New-Object System.Management.Automation.PSPropertySet('DefaultKeyPropertySet',[string[]]$defaultKeys)
$PSStandardMembers = [System.Management.Automation.PSMemberInfo[]]@($defaultDisplayPropertySet,$defaultKeyPropertySet)
}
process {
$contains = $Name.IndexOf('*') -or $Path.IndexOf('*') -or $Fragment.IndexOf('*')
if ($contains -and $Exact) {
throw "$logLead : Can not search for an Exact match AND use wildcards"
}
$checkPath = ![string]::IsNullOrWhiteSpace($Path)
$checkName = ![string]::IsNullOrWhiteSpace($Name)
if (![string]::IsNullOrWhiteSpace($Fragment)) {
$checkPath = $true
$checkName = $true
$Name = $Fragment
$Path = $Fragment
}
$Name = $Name -Replace '\*',''
$Path = $Path -Replace '\*',''
if ($RefreshCache) {
$global:get_allservices_servicesWithPaths = $null
}
if ($null -ne $global:get_allservices_servicesWithPaths) {
$servicesWithPaths = $global:get_allservices_servicesWithPaths
} else {
$allServices = Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Services\
# If there is no path property, we really don't care for the purposes of this function
$servicesWithAnyPath = $allServices.Where({$_.Property -Contains 'ImagePath'})
$servicesWithPaths = $servicesWithAnyPath.Where({!$_.GetValue('ImagePath').StartsWith("\SystemRoot")})
$global:get_allservices_servicesWithPaths = $servicesWithPaths
}
# https://docs.microsoft.com/en-us/windows/win32/msi/serviceinstall-table
$serviceType = @{0x1 = 'KernelDriver';0x2 = 'FileSystemDriver';0x4 = 'Adapter';0x8 = 'RecognizerDriver';0x10 = 'Win32OwnProcess';0x20 = 'Win32ShareProcess';0x100 = 'InteractiveProcess';}
$startMode = ('Boot','System','Automatic','Manual','Disabled')
$errorControl = ('Ignore','Normal','Severe','Critical')
$services = @()
foreach ($regService in $servicesWithPaths) {
$serviceName = (Split-Path -Path $regService -Leaf)
if ($checkName) {
$checkMatch = $checkName -and ($serviceName.IndexOf($Name, [StringComparison]::OrdinalIgnoreCase) -gt -1)
if ($checkName) {
# Write-Host "$logLead : $checkMatch | $serviceName | $Name"
}
$exactMatch = $Exact -and ($serviceName -eq $Name)
if ($Exact) {
# Write-Host "$logLead : $exactMatch | $serviceName | $Name"
}
if ($checkMatch -or $exactMatch) {
# We can continue, we found a matching name
# Write-Host "$logLead : Found $serviceName matched"
} else {
continue
}
}
$displayName = $regService.GetValue("DisplayName")
if (![string]::IsNullOrWhiteSpace($displayName)) { if ($displayName.IndexOf('@') -eq 0) {
if ($displayName.IndexOf(';') -gt -1) {
$displayName = $displayName.Substring($displayName.LastIndexOf(';'))
}
}} else {
$displayName = $serviceName
}
if ($displayName.IndexOf('@') -eq 0) {
# Neat trick: Alkami doesn't care about these services _at all_
continue
}
$description = $regService.GetValue("Description")
if (![string]::IsNullOrWhiteSpace($description)) { if ($description.IndexOf('@') -eq 0) {
# Neat trick: Alkami _also_ doesn't care about these services _at all_
continue
}}
$imagePath = $regService.GetValue("ImagePath")
$exePath = $imagePath
if (![string]::IsNullOrWhiteSpace($imagePath)) { if ($imagePath.IndexOf('"') -gt -1) {
$exePath = ($imagePath.Remove($imagePath.LastIndexOf(".exe",[StringComparison]::OrdinalIgnoreCase)) + ".exe").Replace('"','')
}}
$loginName = ($regService.GetValue("ObjectName"),'LocalSystem' -ne $null)[0]
if ($checkPath) {
$checkMatch = $checkPath -and ($exePath.IndexOf($Path, [StringComparison]::OrdinalIgnoreCase) -gt -1)
if ($checkPath) {
# Write-Host "$logLead : $checkMatch | $exePath | $Path"
}
$exactMatch = $Exact -and ($exePath -eq $Path)
if ($Exact) {
# Write-Host "$logLead : $exactMatch | $exePath | $Path"
}
if ($checkMatch -or $exactMatch) {
# We can continue, we found a matching path
# Write-Host "$logLead : Found $exePath matched"
} else {
continue
}
}
$startModeValue = $regService.GetValue("Start")
$obj = @{
Name = $serviceName
StartMode = $startMode[$startModeValue]
StartType = <#[System.ServiceProcess.ServiceStartMode]#>$startModeValue
ErrorControl = $errorControl[$regService.GetValue("ErrorControl")]
ImagePath = $imagePath
ExePath = $exePath
RunAs = $loginName
DisplayName = $displayName
Description = $description
Dependencies = $regService.GetValue("Dependencies")
ManagedServiceAccount = [System.BitConverter]::ToInt32(($regService.GetValue("ServiceAccountManaged"),@(0,0,0,0) -ne $null)[0],0) -eq 1
FailureActions = ConvertFrom-FailureActions -FailureActions $regService.GetValue("FailureActions")
# FailureActionsOnNonCrashFailures https://docs.microsoft.com/en-us/windows/win32/api/winsvc/ns-winsvc-service_failure_actions_flag
# If this member is TRUE and the service has configured failure actions, the failure actions are queued if the service process terminates without reporting a status of SERVICE_STOPPED or if it enters the SERVICE_STOPPED state but the dwWin32ExitCode member of the SERVICE_STATUS structure is not ERROR_SUCCESS (0).
# If this member is FALSE and the service has configured failure actions, the failure actions are queued only if the service terminates without reporting a status of SERVICE_STOPPED.
FailureActionsOnNonCrashFailures = $regService.GetValue("FailureActionsOnNonCrashFailures") -eq 1
FailureCommand = $regService.GetValue("FailureCommand")
DelayedAutostart = $regService.GetValue("DelayedAutostart")
AutoRun = $regService.GetValue("AutoRun")
AutoRunAlwaysDisable = $regService.GetValue("AutoRunAlwaysDisable")
AutorunsDisabled = $regService.GetValue("AutorunsDisabled")
StateFlags = $regService.GetValue("StateFlags")
Type = $serviceType[$regService.GetValue("Type")]
# Where we find this key
# Key = $regService
# The properties this object has so we can get the data later for confirming we got everything we want
# Property = $regService.Property
}
$serviceController = New-Object PSCustomObject -Property $obj
$serviceController.PSObject.TypeNames.Insert(0,$defaultTypeName)
$serviceController | Add-Member -MemberType MemberSet -Name PSStandardMembers -Value $PSStandardMembers
$services += $serviceController
}
return $services
}
}