Win32ServiceFunctions.ps1
# Copyright 2021 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. <# .SYNOPSIS Create a new Win32 service. .DESCRIPTION This cmdlet creates a new Win32 service. This is similar New-Service but it exposes all the options from the CreateService API and allows you to specify service users. .PARAMETER Name Specify the name of the service. .PARAMETER DisplayName Specify the display name for the service. .PARAMETER Type Specify the service type. .PARAMETER Start Specify the service start type. .PARAMETER Path Specify the path to the service binary. .PARAMETER LoadOrderGroup Specify the load order group. .PARAMETER Dependencies Specify the list of dependencies. .PARAMETER Username Specify the username for the service. .PARAMETER Password Specify the password for the username. .PARAMETER PassThru Specify to return information about the service. .PARAMETER MachineName Specify the target computer. .INPUTS None .OUTPUTS NtApiDotNet.Win32.Win32Service #> function New-Win32Service { [CmdletBinding()] param ( [parameter(Mandatory, Position = 0)] [string]$Name, [string]$DisplayName, [NtApiDotNet.Win32.ServiceType]$Type = "Win32OwnProcess", [NtApiDotNet.Win32.ServiceStartType]$Start = "Demand", [NtApiDotNet.Win32.ServiceErrorControl]$ErrorControl = 0, [parameter(Mandatory, Position = 1)] [string]$Path, [string]$LoadOrderGroup, [string[]]$Dependencies, [string]$Username, [NtObjectManager.Utils.PasswordHolder]$Password, [switch]$PassThru, [string]$MachineName ) $pwd = if ($null -ne $Password) { $Password.Password } $service = [NtApiDotNet.Win32.ServiceUtils]::CreateService($MachineName, $Name, $DisplayName, $Type, ` $Start, $ErrorControl, $Path, $LoadOrderGroup, $Dependencies, $Username, $pwd) if ($PassThru) { $service } } <# .SYNOPSIS Delete a Win32 service. .DESCRIPTION This cmdlet deletes a Win32 service. This is basically the same as Remove-Service but is available on PowerShell 5.1. Also directly supports specifying the machine name. .PARAMETER Name Specify the name of the service. .PARAMETER MachineName Specify the target computer. .INPUTS None .OUTPUTS None #> function Remove-Win32Service { [CmdletBinding()] param ( [parameter(Mandatory, Position = 0)] [string]$Name, [string]$MachineName ) [NtApiDotNet.Win32.ServiceUtils]::DeleteService($MachineName, $Name) } <# .SYNOPSIS Get the security descriptor for a service. .DESCRIPTION This cmdlet gets the security descriptor for a service or the SCM. .PARAMETER Name Specify the name of the service. .PARAMETER ServiceControlManager Specify to query the service control manager security descriptor. .PARAMETER SecurityInformation Specify the parts of the security descriptor to return. .PARAMETER MachineName Specify the target computer. .INPUTS None .OUTPUTS NtApiDotNet.SecurityDescriptor #> function Get-Win32ServiceSecurityDescriptor { [CmdletBinding(DefaultParameterSetName="FromName")] param ( [parameter(Mandatory, Position = 0, ParameterSetName="FromName")] [string]$Name, [parameter(Mandatory, Position = 0, ParameterSetName="FromScm")] [switch]$ServiceControlManager, [parameter(Position = 1)] [NtApiDotNet.SecurityInformation]$SecurityInformation = "Owner, Group, Dacl, Label", [string]$MachineName ) switch($PSCmdlet.ParameterSetName) { "FromName" { [NtApiDotNet.Win32.ServiceUtils]::GetServiceSecurityDescriptor($MachineName, $Name, $SecurityInformation) } "FromScm" { [NtApiDotNet.Win32.ServiceUtils]::GetScmSecurityDescriptor($MachineName, $SecurityInformation) } } } <# .SYNOPSIS Set the security descriptor for a service. .DESCRIPTION This cmdlet sets the security descriptor for a service or the SCM. .PARAMETER Name Specify the name of the service. .PARAMETER ServiceControlManager Specify to set the service control manager security descriptor. .PARAMETER SecurityInformation Specify the parts of the security descriptor to set. .PARAMETER SecurityDescriptor The security descriptor to set. .PARAMETER MachineName Specify the target computer. .INPUTS None .OUTPUTS None #> function Set-Win32ServiceSecurityDescriptor { [CmdletBinding(DefaultParameterSetName="FromName")] param ( [parameter(Mandatory, Position = 0, ParameterSetName="FromName")] [string]$Name, [parameter(Mandatory, ParameterSetName="FromScm")] [switch]$ServiceControlManager, [parameter(Mandatory, Position = 1)] [NtApiDotNet.SecurityDescriptor]$SecurityDescriptor, [parameter(Mandatory, Position = 2)] [NtApiDotNet.SecurityInformation]$SecurityInformation, [string]$MachineName ) switch($PSCmdlet.ParameterSetName) { "FromName" { [NtApiDotNet.Win32.ServiceUtils]::SetServiceSecurityDescriptor($MachineName, $Name, $SecurityDescriptor, $SecurityInformation) } "FromScm" { [NtApiDotNet.Win32.ServiceUtils]::SetScmSecurityDescriptor($MachineName, $SecurityDescriptor, $SecurityInformation) } } } <# .SYNOPSIS Start a Win32 service. .DESCRIPTION This cmdlet starts a Win32 service. This is basically the same as Start-Service but allows the user to specify the arguments to pass to the start callback. .PARAMETER Name Specify the name of the service. .PARAMETER ArgumentList Specify the list of arguments to the service. .PARAMETER PassThru Query for the service status after starting. .PARAMETER MachineName Specify the target computer. .PARAMETER NoWait Specify to not wait 30 seconds for the service to start. .PARAMETER Trigger Specify to try and use a service trigger to start the service. .INPUTS None .OUTPUTS NtApiDotNet.Win32.Win32Service #> function Start-Win32Service { [CmdletBinding(DefaultParameterSetName="FromStart")] param ( [parameter(Mandatory, Position = 0)] [string]$Name, [parameter(ParameterSetName="FromStart")] [string[]]$ArgumentList, [parameter(ParameterSetName="FromStart")] [string]$MachineName, [parameter(Mandatory, ParameterSetName="FromTrigger")] [switch]$Trigger, [switch]$PassThru, [switch]$NoWait ) try { switch ($PSCmdlet.ParameterSetName) { "FromStart" { [NtApiDotNet.Win32.ServiceUtils]::StartService($MachineName, $Name, $ArgumentList) } "FromTrigger" { $service_trigger = Get-Win32ServiceTrigger -Name $Name -Action Start | Select-Object -First 1 if ($null -eq $service_trigger) { throw "No service trigger available for $Name" } $service_trigger.Trigger() } } if (!$NoWait) { if (!(Wait-Win32Service -MachineName $MachineName -Name $Name -Status Running -TimeoutSec 30)) { Write-Error "Service didn't start in time." return } } if ($PassThru) { Get-Win32Service -Name $Name -MachineName $MachineName } } catch { Write-Error $_ } } <# .SYNOPSIS Tests a Win32 service state. .DESCRIPTION This cmdlet tests if a win32 service is in a fixed state. .PARAMETER Name Specify the name of the service. .PARAMETER Status Specify the status to test. .PARAMETER MachineName Specify the target computer. .INPUTS None .OUTPUTS Boolean #> function Test-Win32Service { [CmdletBinding()] param ( [parameter(Mandatory, Position = 0)] [string]$Name, [string]$MachineName, [parameter(Mandatory, Position = 1)] [NtApiDotNet.Win32.ServiceStatus]$Status ) try { $service = Get-Win32Service -Name $Name -MachineName $MachineName return $service.Status -eq $Status } catch { Write-Error $_ return $false } } <# .SYNOPSIS Restart a Win32 service. .DESCRIPTION This cmdlet restarts a Win32 service. .PARAMETER Name Specify the name of the service. .PARAMETER ArgumentList Specify the list of arguments to the service. .PARAMETER PassThru Query for the service status after starting. .PARAMETER MachineName Specify the target computer. .PARAMETER NoWait Specify to not wait 30 seconds for the service to start. .INPUTS None .OUTPUTS NtApiDotNet.Win32.Win32Service #> function Restart-Win32Service { [CmdletBinding(DefaultParameterSetName="FromStart")] param ( [parameter(Mandatory, Position = 0)] [string]$Name, [parameter(ParameterSetName="FromStart")] [string[]]$ArgumentList, [parameter(ParameterSetName="FromStart")] [string]$MachineName, [switch]$PassThru, [switch]$NoWait ) try { if (!(Test-Win32Service -Name $Name -MachineName $MachineName -Status Stopped)) { Send-Win32Service -Name $Name -MachineName $MachineName -Control Stop -ErrorAction Stop } Start-Win32Service -Name $Name -MachineName $MachineName -ArgumentList $ArgumentList -PassThru:$PassThru -NoWait:$NoWait } catch { Write-Error $_ } } <# .SYNOPSIS Send a control code to a Win32 service. .DESCRIPTION This cmdlet sends a control code to a Win32 service. .PARAMETER Name Specify the name of the service. .PARAMETER Control Specify the control code to send. .PARAMETER CustomControl Specify to send a custom control code. Typically in the range of 128 to 255. .PARAMETER PassThru Query for the service status after sending the code. .PARAMETER MachineName Specify the target computer. .PARAMETER NoWait Specify to not wait 30 seconds for the service control to be handled. .INPUTS None .OUTPUTS NtApiDotNet.Win32.Win32Service #> function Send-Win32Service { [CmdletBinding(DefaultParameterSetName="FromControl")] param ( [parameter(Mandatory, Position = 0)] [string]$Name, [parameter(Mandatory, Position = 1, ParameterSetName="FromControl")] [NtApiDotNet.Win32.ServiceControlCode]$Control, [parameter(Mandatory, Position = 1, ParameterSetName="FromCustomControl")] [int]$CustomControl, [switch]$PassThru, [string]$MachineName, [parameter(ParameterSetName="FromControl")] [switch]$NoWait ) try { $wait = switch($PSCmdlet.ParameterSetName) { "FromControl" { [NtApiDotNet.Win32.ServiceUtils]::ControlService($MachineName, $Name, $Control) !$NoWait } "FromCustomControl" { [NtApiDotNet.Win32.ServiceUtils]::ControlService($MachineName, $Name, $CustomControl) $false } } if ($wait) { $wait_state = switch($Control) { "Stop" { Wait-Win32Service -MachineName $MachineName -Name $Name -Status Stopped -TimeoutSec 30 } "Pause" { Wait-Win32Service -MachineName $MachineName -Name $Name -Status Paused -TimeoutSec 30 } "Continue" { Wait-Win32Service -MachineName $MachineName -Name $Name -Status Running -TimeoutSec 30 } default { # Anything else we just return success. $true } } if (!$wait_state) { Write-Error "Service didn't respond to control in time." return } } if ($PassThru) { Get-Win32Service -Name $Name -MachineName $MachineName } } catch { Write-Error $_ } } <# .SYNOPSIS Wait for a Win32 service status. .DESCRIPTION This cmdlet waits for a Win32 service to reach a certain status. Returns true if the status was reached. False if timed out or other error. .PARAMETER Name Specify the name of the service. .PARAMETER Status Specify the status to wait for. .PARAMETER MachineName Specify the target computer. .PARAMETER TimeoutSec Specify the timeout in seconds. .INPUTS None .OUTPUTS Boolean #> function Wait-Win32Service { [CmdletBinding()] param ( [parameter(Mandatory, Position = 0)] [string]$Name, [parameter(Mandatory, Position = 1)] [NtApiDotNet.Win32.ServiceStatus]$Status, [string]$MachineName, [int]$TimeoutSec = [int]::MaxValue ) try { if (Test-Win32Service -Name $Name -MachineName $MachineName -Status $Status) { return $true } if ($TimeoutSec -le 0) { return $false } $timeout_ms = $TimeoutSec * 1000 while ($timeout_ms -gt 0) { $service = Get-Win32Service -Name $Name -MachineName $MachineName if ($service.Status -eq $Status) { return $true } Start-Sleep -Milliseconds 250 $timeout_ms -= 250 } } catch { Write-Error $_ } return $false } <# .SYNOPSIS Get the configuration for a service or all services. .DESCRIPTION This cmdlet gets the configuration for a service or all services. .PARAMETER Name Specify the name of the service. .PARAMETER ServiceType Specify the types of services to return when querying all services. Defaults to all user services. .PARAMETER MachineName Specify the target computer. .INPUTS None .OUTPUTS NtApiDotNet.Win32.ServiceInformation[] #> function Get-Win32ServiceConfig { [CmdletBinding(DefaultParameterSetName="All")] param ( [parameter(Mandatory, Position = 0, ParameterSetName="FromName")] [string]$Name, [parameter(ParameterSetName = "All")] [NtApiDotNet.Win32.ServiceType]$ServiceType = [NtApiDotNet.Win32.ServiceUtils]::GetServiceTypes(), [string]$MachineName ) switch($PSCmdlet.ParameterSetName) { "FromName" { [NtApiDotNet.Win32.ServiceUtils]::GetServiceInformation($MachineName, $Name) } "All" { [NtApiDotNet.Win32.ServiceUtils]::GetServiceInformation($MachineName, $ServiceType) | Write-Output } } } <# .SYNOPSIS Get the configuration for a service or all services. .DESCRIPTION This cmdlet gets the configuration for a service or all services. .PARAMETER Name Specify the name of the service. .PARAMETER ServiceType Specify the types of services to return when querying all services. Defaults to all user services. .PARAMETER MachineName Specify the target computer. .INPUTS None .OUTPUTS NtApiDotNet.Win32.ServiceInformation[] #> function Get-Win32ServiceConfig { [CmdletBinding(DefaultParameterSetName="All")] param ( [parameter(Mandatory, Position = 0, ParameterSetName="FromName")] [string]$Name, [parameter(ParameterSetName = "All")] [NtApiDotNet.Win32.ServiceType]$ServiceType = [NtApiDotNet.Win32.ServiceUtils]::GetServiceTypes(), [string]$MachineName ) switch($PSCmdlet.ParameterSetName) { "FromName" { [NtApiDotNet.Win32.ServiceUtils]::GetServiceInformation($MachineName, $Name) } "All" { [NtApiDotNet.Win32.ServiceUtils]::GetServiceInformation($MachineName, $ServiceType) | Write-Output } } } <# .SYNOPSIS Get the service triggers for a service. .DESCRIPTION This cmdlet gets the service triggers for a service. .PARAMETER Name The name of the service. .PARAMETER MachineName Specify the target computer. .PARAMETER Action Specify an action to filter on. .PARAMETER Service Specify a service object. .INPUTS NtApiDotNet.Win32.Win32Service[] .OUTPUTS NtApiDotNet.Win32.ServiceTriggerInformation[] .EXAMPLE Get-Win32ServiceTrigger -Name "WebClient" Get the service triggers for the WebClient service. #> function Get-Win32ServiceTrigger { [CmdletBinding(DefaultParameterSetName="FromName")] param( [Parameter(Mandatory, Position = 0, ParameterSetName="FromName")] [string]$Name, [Parameter(Mandatory, Position = 0, ParameterSetName="FromService", ValueFromPipeline)] [NtApiDotNet.Win32.Win32Service]$Service, [NtApiDotNet.Win32.ServiceTriggerAction]$Action = 0, [string]$MachineName ) PROCESS { if ($PSCmdlet.ParameterSetName -eq "FromName") { $service = Get-Win32Service -MachineName $MachineName -Name $Name } if ($null -ne $service) { $triggers = $service.Triggers if ($Action -ne 0) { $triggers = $triggers | Where-Object Action -eq $Action } $triggers | Write-Output } } } <# .SYNOPSIS Gets a list of running services. .DESCRIPTION This cmdlet gets a list of running services. It can also include in the list non-active services. .PARAMETER IncludeNonActive Specify to return all services including non-active ones. .PARAMETER Driver Specify to include drivers. .PARAMETER State Specify the state of the services to get. .PARAMETER ServiceType Specify to filter the services to specific types only. .PARAMETER Name Specify names to lookup. .INPUTS None .OUTPUTS NtApiDotNet.Win32.Win32Service[] .EXAMPLE Get-RunningService Get all running services. .EXAMPLE Get-RunningService -IncludeNonActive Get all services including non-active services. .EXAMPLE Get-RunningService -Driver Get all running drivers. .EXAMPLE Get-RunningService -Name Fax Get the Fax running service. .EXAMPLE Get-RunningService -State All -ServiceType UserService Get all user services. #> function Get-RunningService { [CmdletBinding(DefaultParameterSetName = "All")] Param( [parameter(ParameterSetName = "All")] [switch]$IncludeNonActive, [parameter(ParameterSetName = "All")] [switch]$Driver, [parameter(ParameterSetName = "FromArgs")] [NtApiDotNet.Win32.ServiceState]$State = "Active", [parameter(Mandatory, ParameterSetName = "FromArgs")] [NtApiDotNet.Win32.ServiceType]$ServiceType = 0, [parameter(Mandatory, ParameterSetName = "FromName", Position = 0)] [string[]]$Name ) PROCESS { switch ($PSCmdlet.ParameterSetName) { "All" { $ServiceType = [NtApiDotNet.Win32.ServiceUtils]::GetServiceTypes() if ($Driver) { $ServiceType = [NtApiDotNet.Win32.ServiceUtils]::GetDriverTypes() } if ($IncludeNonActive) { $State = "All" } else { $State = "Active" } Get-Win32Service -State $State -Type $ServiceType } "FromArgs" { Get-Win32Service -State $State -Type $ServiceType } "FromName" { Get-Win32Service -Name $Name } } } } <# .SYNOPSIS Gets a list of win32 services. .DESCRIPTION This cmdlet gets a list of all win32 services. .PARAMETER State Specify the state of the services to get. .PARAMETER Type Specify to filter the services to specific types only. .PARAMETER Name Specify names to lookup. .PARAMETER MachineName Specify the target computer. .INPUTS None .OUTPUTS NtApiDotNet.Win32.Win32Service[] .EXAMPLE Get-Win32Service Get all services. .EXAMPLE Get-Win32Service -State Active Get all active services. .EXAMPLE Get-Win32Service -State All -Type UserService Get all user services. .EXAMPLE Get-Win32Service -ProcessId 1234 Get services running in PID 1234. .EXAMPLE Get-Win32Service -Name WebClient Get the WebClient service. #> function Get-Win32Service { [CmdletBinding(DefaultParameterSetName = "All")] Param( [parameter(ParameterSetName = "All")] [NtApiDotNet.Win32.ServiceState]$State = "All", [parameter(ParameterSetName = "All")] [NtApiDotNet.Win32.ServiceType]$Type = 0, [parameter(Mandatory, ParameterSetName = "FromName", Position = 0)] [string[]]$Name, [parameter(Mandatory, ParameterSetName = "FromPid", Position = 0)] [int[]]$ProcessId, [string]$MachineName ) PROCESS { switch ($PSCmdlet.ParameterSetName) { "All" { if ($Type -eq 0) { $Type = [NtApiDotNet.Win32.ServiceUtils]::GetServiceTypes() } [NtApiDotNet.Win32.ServiceUtils]::GetServices($MachineName, $State, $Type) | Write-Output } "FromName" { foreach ($n in $Name) { [NtApiDotNet.Win32.ServiceUtils]::GetService($MachineName, $n) | Write-Output } } "FromPid" { Get-Win32Service -State Active -MachineName $MachineName | Where-Object {$_.ProcessId -in $ProcessId} } } } } |