Skip to content

Instantly share code, notes, and snippets.

@swbbl
Last active April 9, 2023 10:17
Show Gist options
  • Save swbbl/3fa226f626abfc5b29b66c2d32c920d0 to your computer and use it in GitHub Desktop.
Save swbbl/3fa226f626abfc5b29b66c2d32c920d0 to your computer and use it in GitHub Desktop.
Gets all services and system/kernel drivers with all important details (more than Get-Service)
# usage:
# Get service 'DHCP':
# PS> Get-ServiceEx dhcp
#
# Get kernel driver 'ndis':
# PS> Get-ServiceEx ndis
#
# Get all services which are running via svchost:
# PS> Get-ServiceEx -ProcessName svchost
#
# Get all drivers (kernel, filesystem):
# PS> Get-ServiceEx -Type Driver
#
# Examples:
# https://gist.github.com/swbbl/81c5bee5cdd0df2d9903d5beac9ecb99
using namespace System.Collections.Generic
using namespace System.ServiceProcess
using namespace System.Diagnostics
using namespace System.Runtime.InteropServices
using namespace System.ComponentModel
enum ServiceControllerExType {
Driver = 0x1
Service = 0x2
}
class ServiceControllerEx {
[ServiceControllerExType] $Type
[ServiceType] $ServiceType
[string] $ServiceName
[string] $DisplayName
[string] $Description
[ServiceControllerStatus] $Status
[Nullable[ServiceStartMode]] $StartType
[Nullable[bool]] $DelayedAutoStart # Win32_Service
[string] $UserName
[ServiceControllerEx[]] $RequiredServices
[ServiceControllerEx[]] $DependentServices
hidden [ServiceController[]] $scRequiredServices
hidden [ServiceController[]] $scDependentServices
[string] $Path # Win32_Service/SystemDriver
[bool] $CanPauseAndContinue
[bool] $CanShutdown
[bool] $CanStop
[string] $ErrorControl # Win32_Service/SystemDriver
hidden [Nullable[uint32]] $ProcessId # Win32_Service
[Process] $Process
[SafeHandle] $ServiceHandle
[ISite] $Site
[IContainer] $Container
#Win32_SystemDriver:
[Nullable[datetime]] $InstallDate
[Nullable[bool]] $DesktopInteract
[Win32Exception] $ExitCode
[Nullable[UInt32]] $ServiceSpecificExitCode
[Nullable[UInt32]] $TagId
# addtiionally from Win32_Service
[Nullable[uint32]] $CheckPoint
[Nullable[uint32]] $WaitHint
# less important properties
[string] $MachineName
[string] $HostName
[string] $HostDomain
}
class ServiceControllerExService : ServiceControllerEx {}
class ServiceControllerExDriver : ServiceControllerEx {}
function Get-ServiceEx {
[CmdletBinding(DefaultParameterSetName='ServiceName')]
param(
# Parameter help description
[Parameter(ParameterSetName='ServiceName', Position=0)]
[Parameter(ParameterSetName='ProcessId', Position=0)]
[Parameter(ParameterSetName='ProcessName', Position=0)]
[SupportsWildcards()]
[string[]]
$Name,
# Parameter help description
[Parameter(ParameterSetName='ProcessId')]
[uint32[]]
$ProcessId,
# Parameter help description
[Parameter(ParameterSetName='ProcessName')]
[SupportsWildcards()]
[string[]]
$ProcessName,
# Parameter help description
[Parameter()]
[ServiceControllerExType[]]
$Type = ('Service', 'Driver'),
# Parameter help description
[Parameter()]
[switch]
$HasError
)
begin {
$cimComputerSystem = Get-CimInstance Win32_ComputerSystem -Property Domain, DNSHostName
$hostDomain = $cimComputerSystem.Domain
$hostDnsName = $cimComputerSystem.DNSHostName
$processDict = [Dictionary[UInt32, Process]]::new()
foreach ($process in (Get-Process)) {
$process.psobject.Methods.add(
[psscriptmethod]::new('ToString', { '{0} ({1})' -f $this.ProcessName, $this.ID })
)
$processDict[$process.ID] = $process
}
$servicesAndDriversDict = [Dictionary[string, object]]::new([System.StringComparer]::OrdinalIgnoreCase)
# non-device driver and OS services
foreach ($service in [ServiceController]::GetServices()) {
$serviceEx = [ServiceControllerExService]@{
Type = [ServiceControllerExType]::Service
HostName = $hostDnsName
HostDomain = $hostDomain
ServiceName = $service.ServiceName
DisplayName = $service.DisplayName
Status = $service.Status
scRequiredServices = $service.RequiredServices
scDependentServices = $service.DependentServices
CanPauseAndContinue = $service.CanPauseAndContinue
CanShutdown = $service.CanShutdown
CanStop = $service.CanStop
MachineName = $service.MachineName
StartType = $service.StartType
ServiceType = $service.ServiceType
ServiceHandle = $service.ServiceHandle
Site = $service.Site
Container = $service.Container
}
$servicesAndDriversDict.Add($serviceEx.ServiceName, $serviceEx)
}
# device-driver services
foreach ($driver in [ServiceController]::GetDevices()) {
$driverEx = [ServiceControllerExDriver]@{
Type = [ServiceControllerExType]::Driver
HostName = $hostDnsName
HostDomain = $hostDomain
ServiceName = $driver.ServiceName
DisplayName = $driver.DisplayName
Status = $driver.Status
scRequiredServices = $driver.RequiredServices
scDependentServices = $driver.DependentServices
CanPauseAndContinue = $driver.CanPauseAndContinue
CanShutdown = $driver.CanShutdown
CanStop = $driver.CanStop
MachineName = $driver.MachineName
StartType = $driver.StartType
ServiceType = $driver.ServiceType
ServiceHandle = $driver.ServiceHandle
Site = $driver.Site
Container = $driver.Container
}
$servicesAndDriversDict.Add($driverEx.ServiceName, $driverEx)
}
$win32Services = Get-CimInstance -ClassName Win32_Service
$win32SystemDrivers = Get-CimInstance -ClassName Win32_SystemDriver
foreach ($win32Service in $win32Services) {
$win32ServiceName = $win32Service.Name
$service = $servicesAndDriversDict[$win32ServiceName]
if ($null -ne $service) {
$service.InstallDate = $win32Service.InstallDate -as [datetime]
$service.DesktopInteract = $win32Service.DesktopInteract
$service.Description = $win32Service.Description
$service.ErrorControl = $win32Service.ErrorControl
$service.ExitCode = $win32Service.ExitCode -as [int]
$service.Path = $win32Service.PathName
$service.ServiceSpecificExitCode = $win32Service.ServiceSpecificExitCode
$service.UserName = $win32Service.StartName
$service.TagId = $win32Service.TagId
$service.CheckPoint = $win32Service.CheckPoint
$service.DelayedAutoStart = $win32Service.DelayedAutoStart
# with if clause to avoid assigning the idle process (0)
$service.ProcessId = if ($win32Service.ProcessId) { $win32Service.ProcessId };
$service.WaitHint = $win32Service.WaitHint
} else {
# this should never happen, but can be important to know.
Write-Warning "No service found for Win32_Service '$win32ServiceName'!"
}
}
foreach ($win32Device in $win32SystemDrivers) {
$win32DeviceName = $win32Device.Name
$service = $servicesAndDriversDict[$win32DeviceName]
if ($null -ne $service) {
$service.InstallDate = $win32Device.InstallDate
$service.DesktopInteract = $win32Device.DesktopInteract
$service.Description = $win32Device.Description
$service.ErrorControl = $win32Device.ErrorControl
$service.ExitCode = $win32Device.ExitCode -as [int]
$service.Path = $win32Device.PathName
$service.ServiceSpecificExitCode = $win32Device.ServiceSpecificExitCode
$service.UserName = $win32Device.StartName
$service.TagId = $win32Device.TagId
$service.CheckPoint = $win32Device.CheckPoint
$service.DelayedAutoStart = $win32Device.DelayedAutoStart
$service.ProcessId = $win32Device.ProcessId
$service.WaitHint = $win32Device.WaitHint
} else {
# this should never happen, but can be important to know.
Write-Warning "No service found for Win32_SystemDriver '$win32DeviceName'!"
}
}
foreach ($item in $servicesAndDriversDict.GetEnumerator()) {
$value = $item.Value
if ($value.scRequiredServices.Count) {
$value.RequiredServices = foreach ($scRequiredService in $value.scRequiredServices) {
$serviceName = [string] $scRequiredService.ServiceName
$servicesAndDriversDict[$serviceName]
}
}
if ($value.scDependentServices.Count) {
$value.DependentServices = foreach ($scDependentService in $value.scDependentServices) {
$serviceName = [string] $scDependentService.ServiceName
$servicesAndDriversDict[$serviceName]
}
}
if ($null -ne $value.ProcessId) {
$value.Process = $processDict[$value.ProcessId]
}
}
$servicesAndDrivers = [ServiceControllerEx[]]$servicesAndDriversDict.Values
}
process {
if ($Type.Count) {
$servicesAndDrivers = $servicesAndDrivers.Where{ $_.Type -in $Type }
}
if ($Name.Count) {
$servicesAndDrivers = foreach ($service in $servicesAndDrivers) {
foreach ($nameItem in $Name) {
if ($service.ServiceName -like $nameItem -or
$service.DisplayName -like $nameItem -or
$service.Description -like $nameItem -or
$service.Path -like $nameItem) {
$service
}
}
}
}
if ($HasError) {
# 0 = "Success" ; 1077 = "No attempts to start the service has been made since the last boot"
$servicesAndDrivers = $servicesAndDrivers.Where{ $_.ExitCode.NativeErrorCode -notin $null, 0, 1077 }
}
if ($ProcessId.Count) {
$servicesAndDrivers.Where{ $_.Process.Id -in $ProcessId }
} elseif ($ProcessName.Count) {
foreach ($service in $servicesAndDrivers) {
foreach ($nameItem in $ProcessName) {
if ($service.Process.ProcessName -like $nameItem) {
$service
}
}
}
} else {
$servicesAndDrivers
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment