PuppyFriendlySpeculationControl.psm1

function Test-SpeculationControlSettings {
  <#
 
  .SYNOPSIS
  This function queries the speculation control settings for the system.
 
  .DESCRIPTION
  This function queries the speculation control settings for the system.
 
  Version 1.3.
   
  #>


  [CmdletBinding()]
  Param (

  )
    Begin {
        try {
            $ntdll = [Win32.ntdll]
        } catch {
            $NtQSIDefinition = "`n[DllImport(""ntdll.dll"")]`npublic static extern int NtQuerySystemInformation(uint systemInformationClass, IntPtr systemInformation, uint systemInformationLength, IntPtr returnLength);"
            $ntdll = Add-Type -MemberDefinition $NtQSIDefinition -Name 'ntdll' -Namespace 'Win32' -PassThru
        }

        [System.IntPtr]$systemInformationPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(4)
        [System.IntPtr]$returnLengthPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(4)

        $StatusObject = [Ordered]@{
            btiHardwarePresent = $false
            btiWindowsSupportPresent = $false
            btiWindowsSupportEnabled = $false
            btiDisabledBySystemPolicy = $false
            btiDisabledByNoHardwareSupport = $false
            kvaShadowRequired = $true
            kvaShadowPresent = $false
            kvaShadowEnabled = $false
            KvaShadowUserGlobal = $false
            kvaShadowPcidEnabled = $false
            KvaShadowInvpcid = $false
        }
        
    }
    Process {
        try {
            
            $TestParameters = @{
                StatusObject = ([ref]$StatusObject)
                systemInformationPtr = ([ref]$systemInformationPtr)
                returnLengthPtr = ([ref]$returnLengthPtr)
            }

            # Query branch target injection information.
            Test-BTI @TestParameters
            
            Write-Verbose ''

            # Query kernel VA shadow information.
            Test-KVA @TestParameters
            
            Write-Verbose ''
            
            # Provide guidance as appropriate.
            Get-SuggestedActions -StatusObject $StatusObject
        
            return $StatusObject

        }
        finally
        {
            if ($systemInformationPtr -ne [System.IntPtr]::Zero) {
                [System.Runtime.InteropServices.Marshal]::FreeHGlobal($systemInformationPtr)
            }

            if ($returnLengthPtr -ne [System.IntPtr]::Zero) {
                [System.Runtime.InteropServices.Marshal]::FreeHGlobal($returnLengthPtr)
            }
        }
    }
    End {}
}

function Get-ProcessorInfo {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [ref]$StatusObject
    )
    $cpu = Get-WmiObject Win32_Processor | select -first 1

    if ($cpu.Manufacturer -eq "AuthenticAMD") {
        $StatusObject.Value.kvaShadowRequired = $false
    }
    elseif ($cpu.Manufacturer -eq "GenuineIntel") {
        $regex = [regex]'Family (\d+) Model (\d+) Stepping (\d+)'
        $result = $regex.Match($cpu.Description)
            
        if ($result.Success) {
            $family = [System.UInt32]$result.Groups[1].Value
            $model = [System.UInt32]$result.Groups[2].Value
            $stepping = [System.UInt32]$result.Groups[3].Value
                
            if (($family -eq 0x6) -and ($model -in @(0x1c,0x26,0x27,0x36,0x35))) {
                $StatusObject.Value.kvaShadowRequired = $false
            }
        }
    }
    else {
        throw ("Unsupported processor manufacturer: {0}" -f $cpu.Manufacturer)
    }
}

function Get-SuggestedActions {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [hashtable]$StatusObject
    )
    
    [string[]]$actions = $(
        if (-not $StatusObject.btiHardwarePresent) {
            "Install BIOS/firmware update provided by your device OEM that enables hardware support for the branch target injection mitigation."
        }

        if ((-not $StatusObject.btiWindowsSupportPresent) -or (-not $StatusObject.kvaShadowPresent)) {
            "Install the latest available updates for Windows with support for speculation control mitigations."
        }

        if ($StatusObject.btiHardwarePresent -and (-not $StatusObject.btiWindowsSupportEnabled) -or ($StatusObject.kvaShadowRequired -and (-not $StatusObject.kvaShadowEnabled))) {
            $HostOSType = @(@('Server','4072698'),@('Client','4073119'))
            $os = Get-WmiObject Win32_OperatingSystem

            "Follow the guidance for enabling Windows {0} support for speculation control mitigations described in https://support.microsoft.com/help/{1}" -f $HostOSType[[int]($os.ProductType -eq 1)]
        }
    )

    if ($actions.Length -gt 0) {
        Write-Verbose "Suggested actions" -Verbose

        foreach ($action in $actions) {
            Write-Verbose " * $action" -Verbose
        }
    }
}

function Test-QueryReturn {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        $QueryResult,
        [Parameter(Mandatory=$true)]
        [string]$QueryTarget
    )

    if ($QueryResult -in @(0xc0000003,0xc0000002)) {
        $false
    }
    elseif ($QueryResult -ne 0) {
        throw (("Querying $QueryTarget information failed with error {0:X8}" -f $QueryResult))
    }
    else {
        $true
    }
}

function Test-BTI {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [ref]$StatusObject,
        [Parameter(Mandatory=$true)]
        [ref]$systemInformationPtr,
        [Parameter(Mandatory=$true)]
        [ref]$returnLengthPtr
    )
    Begin{
        $scf = @{
            BpbEnabled = [System.UInt32]0x01
            BpbDisabledSystemPolicy = [System.UInt32]0x02
            BpbDisabledNoHardwareSupport = [System.UInt32]0x04
            HwReg1Enumerated = [System.UInt32]0x08
            HwReg2Enumerated = [System.UInt32]0x10
            HwMode1Present = [System.UInt32]0x20
            HwMode2Present = [System.UInt32]0x40
            SmepPresent = [System.UInt32]0x80
        }
        [System.UInt32]$systemInformationClass = 201
        [System.UInt32]$systemInformationLength = 4
        Write-Verbose "Speculation control settings for CVE-2017-5715 [branch target injection]"
    }
    Process {
        $btiQuery = $ntdll::NtQuerySystemInformation($systemInformationClass, $systemInformationPtr.Value, $systemInformationLength, $returnLengthPtr.Value)

        if (Test-QueryReturn -QueryResult $btiQuery -QueryTarget 'branch target injection' -ErrorAction Stop){
            [System.UInt32]$flags = [System.UInt32][System.Runtime.InteropServices.Marshal]::ReadInt32($systemInformationPtr.Value)

            $StatusObject.Value.btiHardwarePresent = ((($flags -band $scf.HwReg1Enumerated) -ne 0) -or (($flags -band $scf.HwReg2Enumerated)))
            $StatusObject.Value.btiWindowsSupportPresent = $true
            $StatusObject.Value.btiWindowsSupportEnabled = (($flags -band $scf.BpbEnabled) -ne 0)

            if ($StatusObject.Value.btiWindowsSupportEnabled -eq $false) {
                $StatusObject.Value.btiDisabledBySystemPolicy = (($flags -band $scf.BpbDisabledSystemPolicy) -ne 0)
                $StatusObject.Value.btiDisabledByNoHardwareSupport = (($flags -band $scf.BpbDisabledNoHardwareSupport) -ne 0)
            }
            
            $scf.GetEnumerator() | %{
                Write-Verbose -Message " > $($_.Key) : $(($flags -band $_.Value) -ne 0)"
            }
        }
    }
    End{
        Write-Verbose " > Hardware support for branch target injection mitigation is present: $($StatusObject.Value.btiHardwarePresent)"
        Write-Verbose " > Windows OS support for branch target injection mitigation is present: $($StatusObject.Value.btiWindowsSupportPresent)"
        Write-Verbose " > Windows OS support for branch target injection mitigation is enabled: $($StatusObject.Value.btiWindowsSupportEnabled)"
  
        if ($StatusObject.Value.btiWindowsSupportPresent -and (-not $StatusObject.Value.btiWindowsSupportEnabled)) {
            Write-Verbose " > Windows OS support for branch target injection mitigation is disabled by system policy: $($StatusObject.Value.btiDisabledBySystemPolicy)"
            Write-Verbose " > Windows OS support for branch target injection mitigation is disabled by absence of hardware support: $($StatusObject.Value.btiDisabledByNoHardwareSupport)"
        }
    }

}

function Test-KVA {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [ref]$StatusObject,
        [Parameter(Mandatory=$true)]
        [ref]$systemInformationPtr,
        [Parameter(Mandatory=$true)]
        [ref]$returnLengthPtr
    )

    Begin{
        $kvaFlags = @{
            kvaShadowEnabled = [System.UInt32]0x01
            kvaShadowUserGlobal = [System.UInt32]0x02
            kvaShadowPcid = [System.UInt32]0x04
            kvaShadowInvpcid = [System.UInt32]0x08
            kvaShadowPresent = ''
        }
        [System.UInt32]$systemInformationClass = 196
        [System.UInt32]$systemInformationLength = 4
    }
    Process {
        
        Write-Verbose "Speculation control settings for CVE-2017-5754 [rogue data cache load]"

        Get-ProcessorInfo -StatusObject ([ref]($StatusObject.Value))
            
        $kvaQuery = $ntdll::NtQuerySystemInformation($systemInformationClass, $systemInformationPtr.Value, $systemInformationLength, $returnLengthPtr.Value)

        if (Test-QueryReturn -QueryResult $kvaQuery -QueryTarget 'kernel VA shadow' -ErrorAction Stop){
            [System.UInt32]$flags = [System.UInt32][System.Runtime.InteropServices.Marshal]::ReadInt32($systemInformationPtr.Value)

            [string[]]($StatusObject.Value.Keys | ?{$_ -match "^kva"}) | %{
                $StatusObject.Value[$_] = (($flags -band $kvaFlags[$_]) -ne 0)
            }

            $kvaFlags.kvaShadowPresent = $true

        }
    }
    End{
        Write-Verbose " > Hardware requires kernel VA shadowing: $($StatusObject.Value.kvaShadowRequired)"

        if ($StatusObject.Value.kvaShadowRequired) {

            Write-Verbose " > Windows OS support for kernel VA shadow is present: $($StatusObject.Value.kvaShadowPresent)"
            Write-Verbose " > Windows OS support for kernel VA shadow is enabled: $($StatusObject.Value.kvaShadowEnabled)"

            if ($StatusObject.Value.kvaShadowEnabled) {
                Write-Verbose "Windows OS support for PCID optimization is enabled: $($StatusObject.Value.kvaShadowPcidEnabled) [not required for security]"
            }
        }
    }

}