TestHarnesses/T1543.003_WindowsService/DriverInstaller.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.
* 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.
* 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.
* 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'
                    ServiceName        = $ServiceWMIInstance.Name
                    ServiceDisplayName = $ServiceWMIInstance.Description
                    ServiceStartMode   = $ServiceWMIInstance.StartMode
                    ServiceState       = $ServiceWMIInstance.State
                    ServiceType        = $ServiceWMIInstance.ServiceType
                    ServiceRegistryKey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$ServiceName"
                    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'
                    ServiceName        = $ServiceWMIInstance.Name
                    ServiceDisplayName = $ServiceWMIInstance.Description
                    ServiceStartMode   = $ServiceWMIInstance.StartMode
                    ServiceState       = $ServiceWMIInstance.State
                    ServiceType        = $ServiceWMIInstance.ServiceType
                    ServiceRegistryKey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$ServiceName"
                    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'
                            ServiceName        = $MatchingServiceName
                            ServiceDisplayName = $ServiceWMIInstance.Description
                            ServiceStartMode   = $ServiceWMIInstance.StartMode
                            ServiceState       = $ServiceWMIInstance.State
                            ServiceType        = $ServiceWMIInstance.ServiceType
                            ServiceRegistryKey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\$MatchingServiceName"
                            DriverPathFormatted    = $ResolvedDriverPath
                            DriverPathUnformatted  = $_.Name
                            DriverFileHashSHA256   = $ResolvedDriverHash
                            LoadedImageBaseAddress = $_.ImageBaseAddress
                            LoadedImageSize        = $_.ImageSize
                            LoadCount              = $_.LoadCount
                        }
                    }
                } else {
                    # No corresponding service was found.
                    [PSCustomObject] @{
                        TechniqueID        = 'T1543.003'
                        ServiceName        = $null
                        ServiceDisplayName = $null
                        ServiceStartMode   = $null
                        ServiceState       = $null
                        ServiceType        = $null
                        ServiceRegistryKey = $null
                        DriverPathFormatted    = $ResolvedDriverPath
                        DriverPathUnformatted  = $_.Name
                        DriverFileHashSHA256   = $ResolvedDriverHash
                        LoadedImageBaseAddress = $_.ImageBaseAddress
                        LoadedImageSize        = $_.ImageSize
                        LoadCount              = $_.LoadCount
                    }
                }
            }
        }
    }
}

function Remove-ATHDriverService {
<#
.SYNOPSIS
 
Uninstall a driver service and optionally unload the corresponding loaded driver.
 
Technique ID: T1543.003 (Create or Modify System Process: Windows Service)
 
.DESCRIPTION
 
Remove-ATHDriverService uninstalls a driver service and optionally unloads the corresponding loaded driver.
 
.PARAMETER ServiceName
 
Specifies the service name of the driver service to uninstall.
 
.PARAMETER Unload
 
Explicitly unload the loaded driver by calling NtUnloadDriver.
 
.INPUTS
 
System.ServiceProcess.ServiceController
 
Remove-ATHDriverService accepts the output of Get-Service.
 
Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_SystemDriver
 
Remove-ATHDriverService 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-ATHDriverService -ServiceName TestDriverService
 
.EXAMPLE
 
Remove-ATHDriverService -ServiceName TestDriverService -Unload
#>


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

        [Switch]
        $Unload
    )

    $DriverServiceInstance = Get-CimInstance -ClassName Win32_SystemDriver -Filter "Name = '$ServiceName'"

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

        return
    }

    if ($Unload) {
        $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-ATHDriverService {
<#
.SYNOPSIS
 
Installs a driver as a service.
 
Technique ID: T1543.003 (Create or Modify System Process: Windows Service)
 
.DESCRIPTION
 
New-ATHDriverService installs a driver as a service and optionally loads 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 driver service should start. Supported options are: BootStart, SystemStart, AutoStart, DemandStart, Disabled. If -StartType is not specified, AutoStart is used as the default option.
 
.PARAMETER ServiceType
 
Specifies the type of driver service to install: KernelDriver or FileSystemDriver. If -ServiceType is not specified, KernelDriver 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.
 
.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.
* 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.
* 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.
* 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-ATHDriverService -ServiceName phymem -DisplayName 'Does driver stuff' -FilePath phymem64.sys -StartService
#>


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

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

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

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

        [Parameter(Mandatory)]
        [String]
        [ValidateScript({Test-Path -Path $_ -PathType Leaf})]
        $FilePath,

        [Switch]
        $StartService
    )

    $ServicesRegKey = 'HKLM:\SYSTEM\CurrentControlSet\Services'
    $NewServiceRegKey = Join-Path -Path $ServicesRegKey -ChildPath $ServiceName
    $ServiceRegistryKey = $null

    # Resolve the full service binary path
    $ServiceBinPath = Resolve-Path -Path $FilePath -ErrorAction Stop

    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 }
        'FileSystemDriver' { $ServiceTypeValue = 2 }
    }

    $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)",                                       # lpBinaryPathName
        $null,                                                          # lpLoadOrderGroup
        ([IntPtr]::Zero),                                               # lpdwTagId
        $null,                                                          # lpDependencies
        $null,                                                          # 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.
    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)

    Get-ATHDriverService -ServiceName $ServiceName
}