Are my computers patched against Meltdown and Spectre?

Following the discovery of the Meltdown and Spectre vulnerabilities, we have had a number of enquiries from customers about tools that can identify affected machines in their environment.

Microsoft has prepared a PowerShell script that can run on individual machines, letting users know if they are affected. The script checks if the Windows OS has been patched, and if the BIOS is updated.

The script is fine to run on a per-machine-basis, but what if I have 100, 1000, or 10,000 machines to query? How do I get a report that tells me if I’m 100% protected, or what I can do to protect my organization?

There is good news for Specops Desktop Management customers, or those who have purchased Specops Command. You already have the technology that enables you to produce such a report, and this blog post will take you through the steps.


You will need to meet the following requirements:

  • All client computers running the Specops Command Client Side Extension. This is an MSI that can be deployed using Group Policy, or any other MSI deployment tool.
  • Specops Command installed on a member server in your domain.
  • Specops Reporting installed on a server to view the report.
  • Specops Command Admin tools installed on a machine that has Group Policy Management console installed.

For more information related to requirements, and installation, click here.

The Process

With the requirements in place, we can create a Specops Command Group Policy that will:

  • Distribute the PowerShell script
  • Run the PowerShell script
  • Gather the results of the script, and send it back to the reporting server

From there, you can create a custom report that will display how you can protect each computer in the environment.

On the machine where the Specops Command Admin tools is installed:

  1. Create a new GPO, or edit an existing Specops Command GPO, and link it to the base OU that contains the machines that you want to scan e.g. Windows workstations, servers etc.
  2. Expand Computer Configuration, Policies, Systems Management, Specops Command, and click New Script Assignment.
  3. Give the Script Assignment a name, in this example I’ve named it Meltdown and Spectre Check.
  4. Copy and paste the following script in the script tab:
    function Get-SpeculationControlSettings {
        param (  
        process {
            $NtQSIDefinition = @' 
        public 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)
            $object = New-Object -TypeName PSObject
            try {
                $btiHardwarePresent = $false
                $btiWindowsSupportPresent = $false
                $btiWindowsSupportEnabled = $false
                $btiDisabledBySystemPolicy = $false
                $btiDisabledByNoHardwareSupport = $false
                [System.UInt32]$systemInformationClass = 201
                [System.UInt32]$systemInformationLength = 4
                $retval = $ntdll::NtQuerySystemInformation($systemInformationClass, $systemInformationPtr, $systemInformationLength, $returnLengthPtr)
                if ($retval -eq 0xc0000003 -or $retval -eq 0xc0000002) {
                    # fallthrough
                elseif ($retval -ne 0) {
                    throw (("Querying branch target injection information failed with error {0:X8}" -f $retval))
                else {
                    [System.UInt32]$scfBpbEnabled = 0x01
                    [System.UInt32]$scfBpbDisabledSystemPolicy = 0x02
                    [System.UInt32]$scfBpbDisabledNoHardwareSupport = 0x04
                    [System.UInt32]$scfHwReg1Enumerated = 0x08
                    [System.UInt32]$scfHwReg2Enumerated = 0x10
                    [System.UInt32]$scfHwMode1Present = 0x20
                    [System.UInt32]$scfHwMode2Present = 0x40
                    [System.UInt32]$scfSmepPresent = 0x80
                    [System.UInt32]$flags = [System.UInt32][System.Runtime.InteropServices.Marshal]::ReadInt32($systemInformationPtr)
                    $btiHardwarePresent = ((($flags -band $scfHwReg1Enumerated) -ne 0) -or (($flags -band $scfHwReg2Enumerated)))
                    $btiWindowsSupportPresent = $true
                    $btiWindowsSupportEnabled = (($flags -band $scfBpbEnabled) -ne 0)
                    if ($btiWindowsSupportEnabled -eq $false) {
                        $btiDisabledBySystemPolicy = (($flags -band $scfBpbDisabledSystemPolicy) -ne 0)
                        $btiDisabledByNoHardwareSupport = (($flags -band $scfBpbDisabledNoHardwareSupport) -ne 0)
                $object | Add-Member -MemberType NoteProperty -Name BTIHardwarePresent -Value $btiHardwarePresent
                $object | Add-Member -MemberType NoteProperty -Name BTIWindowsSupportPresent -Value $btiWindowsSupportPresent
                $object | Add-Member -MemberType NoteProperty -Name BTIWindowsSupportEnabled -Value $btiWindowsSupportEnabled
                $object | Add-Member -MemberType NoteProperty -Name BTIDisabledBySystemPolicy -Value $btiDisabledBySystemPolicy
                $object | Add-Member -MemberType NoteProperty -Name BTIDisabledByNoHardwareSupport -Value $btiDisabledByNoHardwareSupport
                $kvaShadowRequired = $true
                $kvaShadowPresent = $false
                $kvaShadowEnabled = $false
                $kvaShadowPcidEnabled = $false
                $cpu = Get-WmiObject Win32_Processor
            if ($cpu -is [array]) {
                $cpu = $cpu[0]
            $manufacturer = $cpu.Manufacturer
            if ($manufacturer -eq "AuthenticAMD") {
                    $kvaShadowRequired = $false
            elseif ($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 -eq 0x1c) -or
                                ($model -eq 0x26) -or
                                ($model -eq 0x27) -or
                                ($model -eq 0x36) -or
                                ($model -eq 0x35))) {
                            $kvaShadowRequired = $false
                else {
            throw ("Unsupported processor manufacturer: {0}" -f $manufacturer)
                [System.UInt32]$systemInformationClass = 196
                [System.UInt32]$systemInformationLength = 4
                $retval = $ntdll::NtQuerySystemInformation($systemInformationClass, $systemInformationPtr, $systemInformationLength, $returnLengthPtr)
                if ($retval -eq 0xc0000003 -or $retval -eq 0xc0000002) {
                elseif ($retval -ne 0) {
                    throw (("Querying kernel VA shadow information failed with error {0:X8}" -f $retval))
                else {
                    [System.UInt32]$kvaShadowEnabledFlag = 0x01
                    [System.UInt32]$kvaShadowUserGlobalFlag = 0x02
                    [System.UInt32]$kvaShadowPcidFlag = 0x04
                    [System.UInt32]$kvaShadowInvpcidFlag = 0x08
                    [System.UInt32]$flags = [System.UInt32][System.Runtime.InteropServices.Marshal]::ReadInt32($systemInformationPtr)
                    $kvaShadowPresent = $true
                    $kvaShadowEnabled = (($flags -band $kvaShadowEnabledFlag) -ne 0)
                    $kvaShadowPcidEnabled = ((($flags -band $kvaShadowPcidFlag) -ne 0) -and (($flags -band $kvaShadowInvpcidFlag) -ne 0))
                $object | Add-Member -MemberType NoteProperty -Name KVAShadowRequired -Value $kvaShadowRequired
                $object | Add-Member -MemberType NoteProperty -Name KVAShadowWindowsSupportPresent -Value $kvaShadowPresent
                $object | Add-Member -MemberType NoteProperty -Name KVAShadowWindowsSupportEnabled -Value $kvaShadowEnabled
                $object | Add-Member -MemberType NoteProperty -Name KVAShadowPcidEnabled -Value $kvaShadowPcidEnabled
                $actions = @()
                if ($btiHardwarePresent -eq $false) {
                    $actions += "Install BIOS/firmware update provided by your device OEM that enables hardware support for the branch target injection mitigation."
                if ($btiWindowsSupportPresent -eq $false -or $kvaShadowPresent -eq $false) {
                    $actions += "Install the latest available updates for Windows with support for speculation control mitigations."
                if (($btiHardwarePresent -eq $true -and $btiWindowsSupportEnabled -eq $false) -or ($kvaShadowRequired -eq $true -and $kvaShadowEnabled -eq $false)) {
                    $guidanceUri = ""
                    $guidanceType = ""
                    $os = Get-WmiObject Win32_OperatingSystem
                    if ($os.ProductType -eq 1) {
                        # Workstation
                        $guidanceUri = ""
                        $guidanceType = "Client"
                    else {
                        # Server/DC
                        $guidanceUri = ""
                        $guidanceType = "Server"
                    $actions += "Follow the guidance for enabling Windows $guidanceType support for speculation control mitigations described in $guidanceUri"
                if ($actions.Length -eq 0) {
                    $object | Add-Member -MemberType NoteProperty -Name IsFullyPatched -Value $true
                else {
                    $object | Add-Member -MemberType NoteProperty -Name IsFullyPatched -Value $false
                $object | Add-Member -MemberType NoteProperty -Name SuggestedActions -Value $actions
                return $object
            finally {
                if ($systemInformationPtr -ne [System.IntPtr]::Zero) {
                if ($returnLengthPtr -ne [System.IntPtr]::Zero) {
    $result = Get-SpeculationControlSettings
    if ($result.IsFullyPatched) {
        send-feedback "IsFullyPatched"
    else {
        $result.SuggestedActions | ForEach-Object {
            send-feedback $_
  5. Set the Script language to PowerShell.
  6. Set the Send Feedback to Always.
  7. The Undo Script and Target tabs can be left blank.
  8. On the Schedule tab, I recommend selecting Every group policy. This ensures that your reports are always be up-to-date.
  9. Set the GPO processing mode to Both, so that it runs at every refresh no matter if it’s in foreground or background mode.
  10. Click OK to save the Script Assignment.
  11. Now that the GPO has been configured, you will want to run it as soon as possible against all the windows machines in your domain. You can use Specops Gpupdate (Free) to do this. Install Gpupdate on a machine with AD Users and Computers (ADUC) installed.
  12. From ADUC, right-click on the OU containing your computers, and click Specops Gpupdate. I am using the PRO version, but the free one works just as well for this exercise.
  13. Click Gpupdate. If needed, tick Recurse target container to execute on machines in sub-OU’s.
  14. Tick Force to get a full GP refresh (you don’t need to ping the machines before trying).
  15. Click Next, then click Execute.
  16. All machines in that OU will then run a Gpupdate, process the Specops Command GPO, execute the script, and report back the results to the reporting server.
  17. To create the report, connect to the Specops Reporting website: http://servername/specopsreporting
  18. Click Create New Report, name it, and choose where the report should reside (Specops Command> –Computer) is a logical place.
  19. In the Columns tab specify what items you want the report to contain i.e. the computer name, and feedback from the report. The make and model of the PC might also be useful if you need to download new BIOS or Firmware updates.
  20. Under the Filters tab specify what GPO you are using, and filter out the words IsFullyPatched.
  21. You can also create another filter that will tell you when machines are fully patched.
  22. We are now in a position to run our report, simply click the View tab to display it.

In the CustomFeedback column, we can see what issues each computer has, whether they need a MS patch, a hardware update, or both to mitigate the Meltdown/Spectre vulnerabilities.

Remember this is a live report, and will update every time a computer refreshes group policy, so be sure to keep checking it.

Once you have this information, you can scope the remedial work that you need to undertake. The Windows Patching is usually handled by Windows Update or WSUS, but the firmware patching might take some further consideration. You may want to push out that type of update automatically using a deployment tool, such as Specops Deploy, instead of manually installing the BIOS on each machine.

(Last updated on August 9, 2023)

Tags: ,

darren james

Written by

Darren James

Darren James is a Senior Product Manager at Specops Software, an Outpost24 company. Darren is a seasoned cybersecurity professional with more than 20 years of experience in the IT industry. He has worked as a consultant across various organizations and sectors, including central and local governments, retail and energy. His areas of specialization include identity and access management, Active Directory, and Azure AD. Darren has been with Specops Software for more than 12 years and brings his expertise to the support and development of world-class password security and authentication solutions. 

Back to Blog

Related Articles