Windows/TestHarnesses/T1543.003_WindowsService/ServiceInstaller.ps1

if (-not ('AtomicTestHarnesses_T1543_003.ProcessNativeMethods' -as [Type])) {
    $TypeDef = @'
using System;
using System.Runtime.InteropServices;

namespace AtomicTestHarnesses_T1543_003 {
    public struct UNICODE_STRING
    {
        public short Length;
        public short MaximumLength;
        public IntPtr Buffer;
    }

    [Flags]
    public enum SC_MANAGER {
        AllAccess = 0xF003F,
        Connect = 0x0001,
        CreateService = 0x0002,
        EnumerateService = 0x0004,
        Lock = 0x0008,
        QueryLockStatus = 0x0010,
        ModifyBootConfig = 0x0020
    }

    [Flags]
    public enum SERVICE {
        AllAccess = 0xF01FF,
        Delete = 0x10000,
        QueryConfig = 0x0001,
        ChangeConfig = 0x0002,
        QueryStatus = 0x0004,
        EnumerateDependents = 0x0008,
        Start = 0x0010,
        Stop = 0x0020,
        PauseContinue = 0x0040,
        Interrogate = 0x0080,
        UserDefinedControl = 0x0100
    }

    [Flags]
    public enum SERVICE_START {
        BootStart = 0x0000,
        SystemStart = 0x0001,
        AutoStart = 0x0002,
        DemandStart = 0x0003,
        Disabled = 0x0004
    }

    [StructLayout(LayoutKind.Sequential, Pack = 2, CharSet = CharSet.Ansi)]
    public struct SYSTEM_MODULE {
        public uint Reserved1;
        public uint Reserved2;
        public UInt64 ImageBaseAddress;
        public uint ImageSize;
        public uint Flags;
        public ushort Index;
        public ushort Rank;
        public ushort LoadCount;
        public ushort NameOffset;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string Name;
    }

    public class ProcessNativeMethods {
        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr CreateService(IntPtr hService, string serviceName, string displayName, int access, int serviceType, int startType, int errorControl, string binaryPath, string loadOrderGroup, IntPtr pTagId, string dependencies, string servicesStartName, string password);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr OpenSCManager(string machineName, string databaseName, int access);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool CloseServiceHandle(IntPtr hService);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool DeleteService(IntPtr hService);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern IntPtr OpenService(IntPtr hService, string serviceName, int access);

        [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
        public static extern bool StartService(IntPtr hService, int dwNumServiceArgs, IntPtr lpServiceArgVectors);

        [DllImport("ntdll.dll", CharSet = CharSet.Unicode)]
        public static extern void RtlInitUnicodeString(ref UNICODE_STRING DestinationString, String SourceString);

        [DllImport("ntdll.dll")]
        public static extern int NtQuerySystemInformation(int SystemInformationClass, IntPtr SystemInformation, uint SystemInformationLength, ref uint ReturnLength);

        [DllImport("ntdll.dll")]
        public static extern int NtUnloadDriver(ref UNICODE_STRING DriverServiceName);
    }
}
'@


    Add-Type -TypeDefinition $TypeDef -ErrorAction Stop
}

# Helper function. Do not export.
function New-UnicodeString {
    param (
        [Parameter(Mandatory, Position = 0)]
        [ValidateNotNull()]
        $String
    )

    $UnicodeString = New-Object -TypeName AtomicTestHarnesses_T1543_003.UNICODE_STRING

    [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::RtlInitUnicodeString([Ref] $UnicodeString, $String)

    return $UnicodeString
}

# Helper function. Do not export.
# The purpose of this helper function is to normalize the inconsistent driver path strings.
# There may be some corner cases that were not considered here in which case, they can be easily added.
filter ConvertFrom-UnformattedDriverPath {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [String]
        [ValidateNotNullOrEmpty()]
        $DriverPathString
    )

    switch -Regex ($DriverPathString) {
        '^System32\\' {
            $FormattedPath = "$($Env:windir)\$DriverPathString"
        }

        '^\\\?\?\\[A-Z]{1}:\\' {
            $FormattedPath = $DriverPathString.Substring(4)
        }

        '^\\SystemRoot\\' {
            $FormattedPath = "$($env:SystemRoot)\$($DriverPathString.Substring(12))"
        }

        default {
            # Consider the path as-is. This may represent an unaccounted for corner case or a standard path - i.e. in the form of "drive-letter:\path\to\driver.sys"
            $FormattedPath = $DriverPathString
        }
    }

    $FormattedPath
}

# Helper function. Do not export.
# This function is a wrapper for NtQuerySystemInformation to retrieve loaded driver information.
function Get-LoadedDriver {
    [CmdletBinding()]
    Param ()

    if ([IntPtr]::Size -eq 4) {
        # 32-bit could be supported but this test harness does not require it.
        Write-Error 'Enumerating loaded drivers is only supported in a 64-bit process.'

        return
    }

    $ModuleInfo64BitStructSize = 296

    $SystemModuleInformation = 0x0B

    [UInt32] $ReturnLength = 0

    $Result = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::NtQuerySystemInformation($SystemModuleInformation, [IntPtr]::Zero, 0, [ref] $ReturnLength)

    if ($Result -ne 0xC0000004) { # STATUS_INFO_LENGTH_MISMATCH
        Write-Error "NtQuerySystemInformation encountered an unexpected error. Error code: 0x$($Result.ToString('X8'))"

        return
    }

    $SysModuleInfoPtr = [Runtime.InteropServices.Marshal]::AllocHGlobal($ReturnLength)

    [UInt32] $ReturnLength2 = 0

    $Result = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::NtQuerySystemInformation($SystemModuleInformation, $SysModuleInfoPtr, $ReturnLength, [ref] $ReturnLength2)

    if ($Result -ne 0) {
        [Runtime.InteropServices.Marshal]::FreeHGlobal($SysModuleInfoPtr)

        Write-Error "NtQuerySystemInformation encountered an unexpected error. Error code: 0x$($Result.ToString('X8'))"

        return
    }

    $ModuleCount = [Runtime.InteropServices.Marshal]::ReadInt32($SysModuleInfoPtr)

    $ModuleInfoPtr = [IntPtr]::Add($SysModuleInfoPtr, 0x10)

    $ModuleInformation = 1..$ModuleCount | ForEach-Object {
        [Runtime.InteropServices.Marshal]::PtrToStructure($ModuleInfoPtr, [Type][AtomicTestHarnesses_T1543_003.SYSTEM_MODULE])
        $ModuleInfoPtr = [IntPtr]::Add($ModuleInfoPtr, $ModuleInfo64BitStructSize)
    }

    [Runtime.InteropServices.Marshal]::FreeHGlobal($SysModuleInfoPtr)

    $ModuleInformation
}

filter Get-ATHDriverService {
<#
.SYNOPSIS

Retrieves information about an installed and/or loaded driver service.

Technique ID: T1543.003 (Create or Modify System Process: Windows Service)

.DESCRIPTION

Get-ATHDriverService retrieves information about a registered driver service with optional loaded driver context. This function can be used to validate the installation and loaded status of existing services or validate the installation of a new driver service.

Get-ATHDriverService permits a user to specify either a specific service name to query or the filename of a driver that it suspected to be loaded.

Get-ATHDriverService does not comprise an attack technique so it has no need to implement the standardized TestSuccess and TestGuid output fields.

.PARAMETER ServiceName

Specifies a service name to query that corresponds to a driver-specific service.

.PARAMETER LoadedDriverFileName

Specifies the filename of a driver that is suspected to be loaded. Get-ATHDriverService will return all loaded drivers that match the specified filename and attempt to identify its corresponding installed service.

.INPUTS

System.ServiceProcess.ServiceController

Get-ATHDriverService accepts the output of Get-Service.

Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_SystemDriver

Get-ATHDriverService accepts the output of a Win32_SystemDriver WMI object via Get-CimInstance.

.OUTPUTS

PSObject

Outputs an object consisting of relevant execution details. The following object properties may be populated:

* TechniqueID - Specifies the relevant MITRE ATT&CK Technique ID.
* TestSuccess - Specifies if the service creation was successful.
* ServiceName - Specifies the name of the installed service. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceDisplayName - Specifies the display name of the installed service. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceStartMode - Specifies the name of the installed service. The following start modes may be returned: Boot, System, Auto, Manual, Disabled. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceState - Specifies the current state of the installed service. The following states may be returned: Stopped, Start Pending, Stop Pending, Running, Continue Pending, Pause Pending, Paused, Unknown. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceType - Specifies the type of installed service. The following types may be returned: Kernel Driver, File System Driver, Unknown. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceRegistryKey - Specifies the registry key path of the installed service. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceImagePath - Binary that the service will launch.
* DriverPathFormatted - The full path to the driver formatted as driver-letter:\path\to\driver.sys
* DriverPathUnformatted - The full, unformatted driver path. Driver paths are not standardized and can be interpreted in several ways.
* DriverFileHashSHA256 -
* LoadedImageBaseAddress - The loaded kernel virtual base address of the driver. This field may not be populated if the driver is not currently loaded.
* LoadedImageSize - The image size of the driver mapped in memory. This field may not be populated if the driver is not currently loaded.
* LoadCount - The number of times the driver has been loaded in kernel memory. This field may not be populated if the driver is not currently loaded.

.EXAMPLE

Get-ATHDriverService -ServiceName Beep

.EXAMPLE

Get-ATHDriverService -LoadedDriverFileName cdrom.sys
#>

    [CmdletBinding(DefaultParameterSetName = 'ServiceName')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ServiceName', ValueFromPipelineByPropertyName)]
        [String]
        [ValidateLength(0,256)]
        [Alias('Name')]
        $ServiceName,

        [Parameter(Mandatory, ParameterSetName = 'FileName')]
        [String]
        [ValidateNotNullOrEmpty()]
        $LoadedDriverFileName
    )
    $LoadedDrivers = Get-LoadedDriver

    $DriverServiceHashtable = @{}

    # Prepopulate a hashtable consisting of driver service names with their corresponding image paths.
    (Get-Item HKLM:\SYSTEM\CurrentControlSet\Services\).GetSubKeyNames() | ForEach-Object {
        $ServiceInformation = try { [PSCustomObject] @{
            Type = (Get-ItemPropertyValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$_" -Name Type)
            ImagePath = (Get-ItemPropertyValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$_" -Name ImagePath)
        } } catch {}

        if ($ServiceInformation -and (($ServiceInformation.Type -eq 1) -or ($ServiceInformation.Type -eq 2))) {
            $DriverServiceHashtable[$_] = ConvertFrom-UnformattedDriverPath -DriverPathString $ServiceInformation.ImagePath
        }
    }

    switch ($PSCmdlet.ParameterSetName) {
        'ServiceName' {
            $ServiceWMIInstance = Get-CimInstance Win32_SystemDriver -Filter "Name = '$ServiceName'" | Select-Object -First 1

            if (-not $ServiceWMIInstance) {
                # Attempt to resolve the driver path directly via the registry.
                $ServiceType = Get-ItemPropertyValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Name Type -ErrorAction Stop

                if (-not (($ServiceType -eq 1) -or ($ServiceType -eq 2))) {
                    Write-Error "The specified service is not a driver service. Service name: $ServiceName"

                    return
                }

                $ImagePath = Get-ItemPropertyValue -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Name ImagePath -ErrorAction Stop

                $UnformattedDriverPath = $ImagePath
                $ResolvedDriverPath = ConvertFrom-UnformattedDriverPath -DriverPathString $ImagePath
            } else {
                $UnformattedDriverPath = $ServiceWMIInstance.PathName
                $ResolvedDriverPath = ConvertFrom-UnformattedDriverPath -DriverPathString $ServiceWMIInstance.PathName
            }

            $ResolvedDriverHash = Get-FileHash -Path $ResolvedDriverPath -Algorithm SHA256 | Select-Object -ExpandProperty Hash

            # Attempt to find the first matching loaded driver based on the resolved service image path
            $MatchingLoadedDriver = $LoadedDrivers | ForEach-Object {
                if ($ResolvedDriverPath -eq (ConvertFrom-UnformattedDriverPath -DriverPathString $_.Name)) { $_ }
            } | Select-Object -First 1

            if ($MatchingLoadedDriver) {
                [PSCustomObject] @{
                    TechniqueID            = 'T1543.003'
                    TestSuccess               = $true
                    ServiceName            = $ServiceWMIInstance.Name
                    ServiceDisplayName     = $ServiceWMIInstance.Description
                    ServiceStartMode       = $ServiceWMIInstance.StartMode
                    ServiceState           = $ServiceWMIInstance.State
                    ServiceType            = $ServiceWMIInstance.ServiceType
                    ServiceRegistryKey     = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$ServiceName"
                    ServiceImagePath       = $ResolvedDriverPath
                    DriverPathFormatted    = $ResolvedDriverPath
                    DriverPathUnformatted  = $MatchingLoadedDriver.Name
                    DriverFileHashSHA256   = $ResolvedDriverHash
                    LoadedImageBaseAddress = $MatchingLoadedDriver.ImageBaseAddress
                    LoadedImageSize        = $MatchingLoadedDriver.ImageSize
                    LoadCount              = $MatchingLoadedDriver.LoadCount
                }
            } else {
                # No corresponding loaded driver was found
                [PSCustomObject] @{
                    TechniqueID            = 'T1543.003'
                    TestSuccess               = $false
                    ServiceName            = $ServiceWMIInstance.Name
                    ServiceDisplayName     = $ServiceWMIInstance.Description
                    ServiceStartMode       = $ServiceWMIInstance.StartMode
                    ServiceState           = $ServiceWMIInstance.State
                    ServiceType            = $ServiceWMIInstance.ServiceType
                    ServiceRegistryKey     = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$ServiceName"
                    ServiceImagePath       = $ResolvedDriverPath
                    DriverPathFormatted    = $ResolvedDriverPath
                    DriverPathUnformatted  = $UnformattedDriverPath
                    DriverFileHashSHA256   = $ResolvedDriverHash
                    LoadedImageBaseAddress = $null
                    LoadedImageSize        = $null
                    LoadCount              = $null
                }
            }
        }

        'FileName' {
            $LoadedDrivers | Where-Object { $_.Name.ToLower().EndsWith($LoadedDriverFileName.ToLower()) } | ForEach-Object {
                $ResolvedDriverPath = ConvertFrom-UnformattedDriverPath -DriverPathString $_.Name

                $ResolvedDriverHash = Get-FileHash -Path $ResolvedDriverPath -Algorithm SHA256 | Select-Object -ExpandProperty Hash

                # For each loaded driver, attempt to resolve any services that correspond to the driver image path
                $MatchingServices = foreach ($ServiceName in $DriverServiceHashtable.Keys) {
                    if ($DriverServiceHashtable[$ServiceName] -eq $ResolvedDriverPath) {
                        $ServiceName
                    }
                }

                if ($MatchingServices) {
                    foreach ($MatchingServiceName in $MatchingServices) {
                        $ServiceWMIInstance = Get-CimInstance -ClassName Win32_SystemDriver -Filter "Name = '$MatchingServiceName'"

                        [PSCustomObject] @{
                            TechniqueID            = 'T1543.003'
                            TestSuccess               = $true
                            ServiceName            = $ServiceWMIInstance.Name
                            ServiceDisplayName     = $ServiceWMIInstance.Description
                            ServiceStartMode       = $ServiceWMIInstance.StartMode
                            ServiceState           = $ServiceWMIInstance.State
                            ServiceType            = $ServiceWMIInstance.ServiceType
                            ServiceRegistryKey     = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$ServiceName"
                            ServiceImagePath       = $ImagePath
                            DriverPathFormatted    = $ResolvedDriverPath
                            DriverPathUnformatted  = $_.Name
                            DriverFileHashSHA256   = $ResolvedDriverHash
                            LoadedImageBaseAddress = $_.ImageBaseAddress
                            LoadedImageSize        = $_.ImageSize
                            LoadCount              = $_.LoadCount
                        }
                    }
                } else {
                    # No corresponding service was found.
                    [PSCustomObject] @{
                        TechniqueID            = 'T1543.003'
                        TestSuccess            = $false
                        ServiceName            = $null
                        ServiceDisplayName     = $null
                        ServiceStartMode       = $null
                        ServiceState           = $null
                        ServiceType            = $null
                        ServiceRegistryKey     = $null
                        ServiceImagePath       = $null
                        DriverPathFormatted    = $ResolvedDriverPath
                        DriverPathUnformatted  = $_.Name
                        DriverFileHashSHA256   = $ResolvedDriverHash
                        LoadedImageBaseAddress = $_.ImageBaseAddress
                        LoadedImageSize        = $_.ImageSize
                        LoadCount              = $_.LoadCount
                    }
                }
            }
        }
    }
}

function Remove-ATHService {
<#
.SYNOPSIS

Uninstall a service and optionally unloads the corresponding loaded driver.

Technique ID: T1543.003 (Create or Modify System Process: Windows Service)

.DESCRIPTION

Remove-ATHService uninstalls a service and optionally unloads the corresponding loaded driver.

.PARAMETER ServiceName

Specifies the service to uninstall.

.PARAMETER DriverUnload

Explicitly unload the loaded driver by calling NtUnloadDriver.

.PARAMETER RegistryRemove

Remove the service via Registry Deletion.

.INPUTS

System.ServiceProcess.ServiceController

Remove-ATHService accepts the output of Get-Service.

Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_SystemDriver

Remove-ATHService accepts the output of a Win32_SystemDriver WMI object via Get-CimInstance.

.OUTPUTS

PSObject

Outputs an object consisting of relevant uninstallation details. The following object properties may be populated:

* TechniqueID - Specifies the relevant MITRE ATT&CK Technique ID.
* ServiceRemoved - Indicates True if the service was successfully removed.
* ServiceName - Specifies the name of the uninstalled service.

.EXAMPLE

Remove-ATHService -ServiceName TestDriverService

.EXAMPLE

Remove-ATHService -ServiceName TestDriverService -DriverUnload

.EXAMPLE

Remove-ATHService -ServiceName TestDriverService -RegistryRemove
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [String]
        [ValidateLength(0,256)]
        [Alias('Name')]
        $ServiceName,

        [Switch]
        $DriverUnload, 

        [Switch]
        $RegistryRemove
    )

    
    if($RegistryRemove){

        $null = Remove-Item HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName

        [PSCustomObject] @{
            TechniqueID = 'T1543.003'
            ServiceRemoved = $True
            ServiceName = $ServiceName
        }
    }
    else{
        if ($DriverUnload) {
            $DriverServiceInstance = Get-CimInstance -ClassName Win32_SystemDriver -Filter "Name = '$ServiceName'"

            if (-not $DriverServiceInstance) {
                Write-Error "The `"$ServiceName`" service is not a registered driver service."

                return
            }
            $ServiceNameString = New-UnicodeString -String $ServiceName

            $UnloadResult = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::NtUnloadDriver([Ref] $ServiceNameString)

            if ($UnloadResult) {
                Write-Error "The driver corresponding to the `"$ServiceName`" service failed to unload. ErrorCode: 0x$($UnloadResult.ToString('X8'))"
            }
        }

        Write-Verbose 'Requesting service control manager handle with SC_MANAGER_CONNECT access.'

        # Get a handle to the service control manager requesting the minimum possible access to create a service: SC_MANAGER_CONNECT (0x0001)
        $SCHandle = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::OpenSCManager(
            $null,                                              # lpMachineName
            'ServicesActive',                                   # lpDatabaseName
            [AtomicTestHarnesses_T1543_003.SC_MANAGER]::Connect # dwDesiredAccess
        );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

        if ($SCHandle -eq ([IntPtr]::Zero)) {
            # Failed to open a handle to the service control manager

            Write-Error "Failed to obtain a service control manager handle with SC_MANAGER_CONNECT access. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"

            return
        }

        Write-Verbose 'Requesting service handle with DELETE access.'

        $ServiceHandle = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::OpenService(
            $SCHandle,                                        # hSCManager
            $ServiceName,                                     # lpServiceName
            ([AtomicTestHarnesses_T1543_003.SERVICE]::Delete) # dwDesiredAccess
        );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

        if ($ServiceHandle -eq ([IntPtr]::Zero)) {
            # Close the service control manager handle
            $null = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CloseServiceHandle($SCHandle)

            Write-Error "Failed to obtain a service handle with DELETE access. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"

            return
        }

        Write-Verbose 'Deleting the service.'

        $DeletionResult = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::DeleteService(
            $ServiceHandle
        );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

        if ($False -eq $DeletionResult) {
            # Close the service handle
            $null = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CloseServiceHandle($ServiceHandle)

            # Close the service control manager handle
            $null = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CloseServiceHandle($SCHandle)

            Write-Error "Failed to delete the service. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"

            return
        }

        Write-Verbose 'Service was successfully deleted.'

        [PSCustomObject] @{
            TechniqueID = 'T1543.003'
            ServiceRemoved = $True
            ServiceName = $ServiceName
        }
    }
}


function New-ATHService {
<#
.SYNOPSIS

Installs a service.

Technique ID: T1543.003 (Create or Modify System Process: Windows Service)

.DESCRIPTION

New-ATHService installs a service and optionally starts it.

.PARAMETER ServiceName

Specifies the name of the service to be created.

.PARAMETER DisplayName

Specifies the description of the service to be created.

.PARAMETER StartType

Specifies how the service should start. Supported options are: BootStart, SystemStart, AutoStart, DemandStart, Disabled. If -StartType is not specified, DemandStart is used as the default option.

.PARAMETER ServiceType

Specifies the type of service to install: KernelDriver, FileSystemDriver, Win32OwnProcess, Win32ShareProcess. If -ServiceType is not specified, Win32OwnProcess is used as the default option.

.PARAMETER FilePath

Specifies the path to the service binary.

.PARAMETER StartService

Indicates that the service is to be started immediately after installation.

.PARAMETER Variant

Chooses how the service creation will be performed. This can be one of the following - Win32 APIs, WMI, sc.exe, or Registry. Default is sc.exe.

.OUTPUTS

PSObject

Outputs an object consisting of relevant execution details. The following object properties may be populated:

* TechniqueID - Specifies the relevant MITRE ATT&CK Technique ID.
* TestSuccess - Specifies if the service creation was successful.
* ServiceName - Specifies the name of the installed service. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceDisplayName - Specifies the display name of the installed service. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceStartMode - Specifies the name of the installed service. The following start modes may be returned: Boot, System, Auto, Manual, Disabled. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceState - Specifies the current state of the installed service. The following states may be returned: Stopped, Start Pending, Stop Pending, Running, Continue Pending, Pause Pending, Paused, Unknown. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceType - Specifies the type of installed service. The following types may be returned: Kernel Driver, File System Driver, Unknown. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceRegistryKey - Specifies the registry key path of the installed service. This field may not be populated if a driver is loaded that does not have a corresponding service.
* ServiceImagePath - Binary that the service will launch.
* DriverPathFormatted - The full path to the driver formatted as driver-letter:\path\to\driver.sys
* DriverPathUnformatted - The full, unformatted driver path. Driver paths are not standardized and can be interpreted in several ways.
* DriverFileHashSHA256 -
* LoadedImageBaseAddress - The loaded kernel virtual base address of the driver. This field may not be populated if the driver is not currently loaded.
* LoadedImageSize - The image size of the driver mapped in memory. This field may not be populated if the driver is not currently loaded.
* LoadCount - The number of times the driver has been loaded in kernel memory. This field may not be populated if the driver is not currently loaded.

.EXAMPLE

New-ATHService -ServiceName phymem -DisplayName 'Does driver stuff' -ServiceType KernelDriver -FilePath phymem64.sys -StartService

.EXAMPLE

New-ATHService -ServiceName TestService -DisplayName TestService

.EXAMPLE

New-ATHService -ServiceName TestService -DisplayName TestService -FilePath filename.exe -StartService

.EXAMPLE

New-ATHService -ServiceName TestService -DisplayName TestService -FilePath filename.exe -Variant Win32 -StartType DemandStart -StartService

.EXAMPLE

New-ATHService -ServiceName TestService -DisplayName TestService -FilePath filename.exe -Variant sc.exe -StartType AutoStart -ServiceType Win32OwnProcess -StartService
#>


    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [String]
        [ValidateLength(0,256)]
        $ServiceName,

        [Parameter(Mandatory)]
        [String]
        [ValidateLength(0,256)]
        $DisplayName,

        [string]
        [ValidateSet('sc.exe', 'WMI', 'Win32', 'Registry')]
        $Variant = 'sc.exe',

        [String]
        [ValidateSet('BootStart', 'SystemStart', 'AutoStart', 'DemandStart', 'Disabled')]
        $StartType = 'DemandStart',

        [String]
        [ValidateSet('KernelDriver', 'FileSystemDriver', 'Win32OwnProcess', 'Win32ShareProcess')]
        $ServiceType = 'Win32OwnProcess',

        [String]
        $FilePath,

        [Switch]
        $DeleteServiceBinary,

        [Switch]
        $StartService
    )

    $ServicesRegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services'
    $NewServiceRegKey = Join-Path -Path $ServicesRegKey -ChildPath $ServiceName
    $ServiceRegistryKey = $null
    if(([string]::IsNullOrEmpty($FilePath)) -and (($ServiceType -eq 'Win32OwnProcess') -or ($ServiceType -eq 'Win32ShareProcess'))){
        Add-Type -OutputAssembly ATHService.exe -TypeDefinition @'
                        public class Test {
                            public static void Main() {
                                System.Threading.Thread.Sleep(3000);
                            }
                        }
'@

    
        $ServiceBinPath = Resolve-Path ATHService.exe -ErrorAction Stop
        }
    else{
        # Resolve the full service binary path
        $ServiceBinPath = Resolve-Path -Path $FilePath -ErrorAction Stop
    }

    switch($Variant) {
       'sc.exe'{
        $ServiceInfo = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName\ -ErrorAction Ignore
        if([string]::IsNullOrEmpty($ServiceInfo)){
        #Updating values to match: https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/sc-create
           switch($ServiceType){
                'KernelDriver' {$service='kernel'}
                'FileSystemDriver'{$service='filesys'}
                'Win32OwnProcess'{$service='own'}
                'Win32ShareProcess'{$service='share'}
                }
            switch($StartType){
                'AutoStart'{$start = 'auto'}
                'BootStart'{$start = 'boot'}
                'SystemStart'{$start = 'system'}
                'DemandStart'{$start = 'demand'}
                'Disabled'{$start = 'disabled'}
            }
        $ProcessStartup = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly
        $ProcessStartupInstance = Get-CimInstance -InputObject $ProcessStartup
        $ProcessStartupInstance.ShowWindow = [UInt16] 0
        $ProcStartResult = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = "sc.exe create $ServiceName binpath= $ServiceBinPath.Path start= $start displayname= $DisplayName type= $service"; ProcessStartupInformation = $ProcessStartupInstance }
        if($ProcStartResult.ReturnValue -ne 0){
            Write-Error "sc.exe creation failed"
            return
        }
        if ($StartService) {
            $ProcessStartup = New-CimInstance -ClassName Win32_ProcessStartup -ClientOnly
            $ProcessStartupInstance = Get-CimInstance -InputObject $ProcessStartup
            $ProcessStartupInstance.ShowWindow = [UInt16] 0
            $ProcStartResult = Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = "sc.exe start $ServiceName"; ProcessStartupInformation = $ProcessStartupInstance }
            }
        }
        else{
            Write-Error "Service already exists"
            return
        }

       }

       'WMI'{
        #Updating values to match: https://learn.microsoft.com/en-us/windows/win32/cimwin32prov/create-method-in-class-win32-service
        switch ($ServiceType) {
                'KernelDriver'      { $ServiceTypeValue = 1}
                'FileSystemDriver'  { $ServiceTypeValue = 2}
                'Win32OwnProcess'   { $ServiceTypeValue = 16}
                'Win32ShareProcess' { $ServiceTypeValue = 32}
            }
            switch($StartType){
                'AutoStart'{$start = 'Automatic'}
                'BootStart'{$start = 'Boot'}
                'SystemStart'{$start = 'System'}
                'DemandStart'{$start = 'Manual'}
                'Disabled'{$start = 'Disabled'}
            }
       $ResultWMI = Invoke-CimMethod -ClassName Win32_Service -MethodName Create -Arguments @{Name= $ServiceName; DisplayName= $DisplayName; PathName= $ServiceBinPath.Path; StartMode= $start; ServiceType = ([Byte] $ServiceTypeValue)}
       if($ResultWMI.ReturnValue -ne 0){
        Write-Error "Service failed to create via WMI. Error code: $($ResultWMI.ReturnValue)"
        return
       }
       if ($StartService) {
        $Service = (Get-WmiObject -Class Win32_Service -Filter "Name='$($ServiceName)'").InvokeMethod("StartService",$null)
        }
    }

       'Registry'{
        $ServiceInfo = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName\ -ErrorAction Ignore
        if([string]::IsNullOrEmpty($ServiceInfo)){
            switch ($ServiceType) {
                'KernelDriver'      { $ServiceTypeValue = 1}
                'FileSystemDriver'  { $ServiceTypeValue = 2}
                'Win32OwnProcess'   { $ServiceTypeValue = 16}
                'Win32ShareProcess' { $ServiceTypeValue = 32}
            }
            switch($StartType){
                'AutoStart'{$start = 2}
                'BootStart'{$start = 0}
                'SystemStart'{$start = 1}
                'DemandStart'{$start = 3}
                'Disabled'{$start = 4}
            }
            
            
            $RegCreatKey = New-Item $NewServiceRegKey
            $RegSetValue = New-ItemProperty -Path $NewServiceRegKey -Name ImagePath -Value $ServiceBinPath.Path -Type ExpandString 
            $RegSetValue = New-ItemProperty -Path $NewServiceRegKey -Name ErrorControl -Value 1 -Type DWord 
            $RegSetValue = New-ItemProperty -Path $NewServiceRegKey -Name Type -Value $ServiceTypeValue -Type DWord 
            $RegSetValue = New-ItemProperty -Path $NewServiceRegKey -Name Start -Value $start -Type DWord 
            $RegSetValue = New-ItemProperty -Path $NewServiceRegKey -Name DisplayName -Value $DisplayName -Type String
            if (($ServiceType -eq  'KernelDriver') -or ($ServiceType -eq 'FileSystemDriver')){
                #Bypassing ObjectName as that will throw an error for the driver to start
            }
            else{
                $RegSetValue = New-ItemProperty -Path $NewServiceRegKey -Name ObjectName -Value 'LocalSystem' -Type String
            }
            if ($StartService) {
                Write-Error "Can't start service until computer is rebooted"
            }
        }
        else{
            Write-Error "Service already exists"
            return
        }
    }   
        
       'Win32' { 
            Write-Verbose "Requesting service control manager handle with SC_MANAGER_CONNECT access."
            # Get a handle to the service control manager requesting the minimum possible access to create a service: SC_MANAGER_CREATE_SERVICE (0x0002)
            $SCHandle = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::OpenSCManager(
                $null,                                                    # lpMachineName
                'ServicesActive',                                         # lpDatabaseName
                [AtomicTestHarnesses_T1543_003.SC_MANAGER] 'CreateService, Connect' # dwDesiredAccess
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if ($SCHandle -eq ([IntPtr]::Zero)) {
                # Failed to open a handle to the service control manager

                Write-Error "Failed to obtain a service control manager handle with SC_MANAGER_CREATE_SERVICE access. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"

                return
            }

            Write-Verbose 'Creating service.'

            switch ($ServiceType) {
                'KernelDriver'      { $ServiceTypeValue = 1; $lpServiceStartName = $null }
                'FileSystemDriver'  { $ServiceTypeValue = 2; $lpServiceStartName = $null }
                'Win32OwnProcess'   { $ServiceTypeValue = 16; $lpServiceStartName = 'NT AUTHORITY\SYSTEM' }
                'Win32ShareProcess' { $ServiceTypeValue = 32; $lpServiceStartName = 'NT AUTHORITY\SYSTEM' }
            }


            $ServiceHandle = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CreateService(
                $SCHandle,                                                      # hSCManager
                $ServiceName,                                                   # lpServiceName
                $DisplayName,                                                   # lpDisplayName
                ([AtomicTestHarnesses_T1543_003.SERVICE]::AllAccess),           # dwDesiredAccess
                $ServiceTypeValue,                                              # dwServiceType
                ($StartType -as [AtomicTestHarnesses_T1543_003.SERVICE_START]), # dwStartType
                0x0001,                                                         # dwErrorControl - SERVICE_ERROR_NORMAL
                $ServiceBinPath.Path,                                                # lpBinaryPathName
                $null,                                                          # lpLoadOrderGroup
                ([IntPtr]::Zero),                                               # lpdwTagId
                $null,                                                          # lpDependencies
                $lpServiceStartName,                                            # lpServiceStartName
                $null                                                           # lpPassword
            );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

            if ($ServiceHandle -eq ([IntPtr]::Zero)) {
                # Close the service control manager handle
                $null = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CloseServiceHandle($SCHandle)

                Write-Error "Failed to create service. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"

                return
            }

            Write-Verbose 'Successfully created the service.'

            # Only supply registry key context in the case that the service was successfully created.
            if (Test-Path -Path $NewServiceRegKey -PathType Container) {
                $ServiceRegistryKey = $NewServiceRegKey
            }

            # Calling CreateService will create an empty "ObjectName" value which will cause StartService to fail with an error code of 0x4db (WIN32: 1243 ERROR_SERVICE_NOT_FOUND) - "The specified service does not exist."
            # Explicitly deleting the "ObjectName" value will resolve this issue.
            if($null -eq $lpServiceStartName){
                Remove-ItemProperty -Path $ServiceRegistryKey -Name ObjectName -ErrorAction Ignore 
            }

            if ($StartService) {
                Write-Verbose 'Attempting to start the service'

                $StartServiceResult = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::StartService(
                    $ServiceHandle, # hService
                    0,              # dwNumServiceArgs
                    [IntPtr]::Zero  # lpServiceArgVectors
                );$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()

                if ($StartServiceResult -eq $False) {
                    # Close the service handle
                    $null = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CloseServiceHandle($ServiceHandle)

                    # Close the service control manager handle
                    $null = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CloseServiceHandle($SCHandle)

                    Write-Error "Failed to start service. Reason: $($LastError.Message) (ErrorCode: 0x$($LastError.NativeErrorCode.ToString('X8')))"

                    return
                }
            }
             # Close the service handle
            $null = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CloseServiceHandle($ServiceHandle)

            # Close the service control manager handle
            $null = [AtomicTestHarnesses_T1543_003.ProcessNativeMethods]::CloseServiceHandle($SCHandle)
           }
    }
    if($DeleteServiceBinary) {
        $null = Remove-Item $ServiceBinPath -Force -ErrorAction SilentlyContinue
    }
    switch ($ServiceType){

        'Win32OwnProcess'{
            Start-Sleep 1
            $ServiceInfo = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName\ -ErrorAction Ignore
            if ($null -ne $ServiceInfo){
                $TestSuccess = $true
                $ServiceWMIInstance = Get-CimInstance Win32_Service -Filter "Name = '$ServiceName'" | Select-Object -First 1
                if($null -eq $ServiceWMIInstance){
                    $ServiceState = 'Stopped'
                }
                else{
                    $ServiceState = $ServiceWMIInstance.State
                }
            }
            else { 
                $TestSuccess = $false
            }
            
            [PSCustomObject] @{
            TechniqueID             = 'T1543.003'
            TestSuccess            = $TestSuccess
            ServiceName            = $ServiceName
            ServiceDisplayName     = $ServiceInfo.DisplayName
            ServiceStartMode       = $ServiceInfo.Start
            ServiceState           = $ServiceState
            ServiceType            = $ServiceInfo.Type
            ServiceRegistryKey    = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$ServiceName"
            ServiceImagePath       = $ServiceInfo.ImagePath
            DriverPathFormatted    = $null
            DriverPathUnformatted  = $null
            DriverFileHashSHA256   = $null
            LoadedImageBaseAddress = $null
            LoadedImageSize        = $null
            LoadCount              = $null
            }
        }
    
         'Win32ShareProcess'{
            Start-Sleep 1
            $ServiceInfo = Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName\ -ErrorAction Ignore
            if ($null -ne $ServiceInfo){
                $TestSuccess = $true
                $ServiceWMIInstance = Get-CimInstance Win32_Service -Filter "Name = '$ServiceName'" | Select-Object -First 1
                if($null -eq $ServiceWMIInstance){
                    $ServiceState = 'Stopped'
                }
                else{
                    $ServiceState = $ServiceWMIInstance.State
                }
            }
            else { $TestSuccess = $false}
            
            [PSCustomObject] @{
            TechniqueID             = 'T1543.003'
            TestSuccess            = $TestSuccess
            ServiceName            = $ServiceName
            ServiceDisplayName     = $ServiceInfo.DisplayName
            ServiceStartMode       = $ServiceInfo.Start
            ServiceState           = $ServiceState
            ServiceType            = $ServiceInfo.Type
            ServiceRegistryKey    = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$ServiceName"
            ServiceImagePath       = $ServiceInfo.ImagePath
            DriverPathFormatted    = $null
            DriverPathUnformatted  = $null
            DriverFileHashSHA256   = $null
            LoadedImageBaseAddress = $null
            LoadedImageSize        = $null
            LoadCount              = $null
                }
            }
    
        'KernelDriver'{
            Get-ATHDriverService -ServiceName $ServiceName
        }
        'FileSystemDriver'{
            Get-ATHDriverService -ServiceName $ServiceName
            }
        
        }
    }