Detect the state of your Windows Defender antivirus.

Currently we help companies protecting their devices. Some of them are willing to shift to other AV products like Microsoft Windows Defender.

The challenge in these operations is to monitor the state of Antivirus products while and after reconfiguring. Together with the help of the great support team of Defender I could build a solution to this problem. To help others and working consistently I have putted the findings in a new PowerShell Module that is available for anyone from PowerShell Gallery and or GitHub.

Let me explain first why we need this Modules and or PowerShell cmdlets.

For PowerShell and Windows Defender there is the perfectly fine Get-MPComputerStatus.  And while the description of this cmdlet states: “This command gets the status of antimalware protection software installed on the computer.” to my finding it doesn’t provide that a generic status.

If I run this cmdlet while a Non-Microsoft antivirus product is installed and active the output looks as:

So can we check for the current state of any Antivirus product installed using a different approach?

From a user perspective we can monitor the current state via opening the custom protocol handler that is implemented by Windows Defender
windows windowsdefender://providers/ this is an easy way to open the correct dialog directly.

 

For the sake of this demonstration I installed 2 additional Antivirus Products. In this dialog you can see the current security products that are installed and monitored by Windows Security Center. Their respectively status is shown.

My goal is to gather this information by using PowerShell.

Specifically for antivirus products we can execute the following command to gather the information:

Get-CimInstance Namespace root/SecurityCenter2 ClassName AntivirusProduct

If you don’t have access to the CimInstance or WMI you could also try:

Get-ChildItem Path "HKLM:\SOFTWARE\Microsoft\Security Center\Provider\Av" | Get-ItemProperty

view raw
Get-AvProviders.ps1
hosted with ❤ by GitHub

In the returned objects you will find the following fields

displayName : Windows Defender
instanceGuid : {D68DDC3A-831F-4fae-9E44-DA132C1ACF46}
pathToSignedProductExe : windowsdefender://
pathToSignedReportingExe : %ProgramFiles%\Windows Defender\MsMpeng.exe
productState : 393472
timestamp : Wed, 01 Apr 2020 16:56:49 GMT
PSComputerName :

view raw
.txt
hosted with ❤ by GitHub

For registry this looks as:

Among others there is this productState (or STATE) value.

This value contains information about the statuses of the product.
By using the right equations you could also determine other statuses, for example if it is up-to-date.

On docs pages there is some documentation about the interface and actual api that exposes this information.

We can find out that the api exposes information about:

Enumerations

 
Title Description
WSC_SECURITY_PRODUCT_STATE Defines the current state of the security product that is made available to Windows Security Center.
WSC_SECURITY_SIGNATURE_STATUS Reports the current version status of the security product to Windows Security Center.

I couldn’t find any publicly available information about these Enumerations from Microsoft but there are others that gained some information from the W10 SDK.

From MS support we received confirmation that the productState bitmask flag is 0x0000F000 and the value for Enable should be 0x1000.

The value is set using Bit Fields, this is an object member which contains multiple members that stores the information in just a couple of bits.
More information about this: https://docs.microsoft.com/en-us/cpp/cpp/cpp-bit-fields

Using bitwise operators and some help from Microsoft Support we could figure out how we can determine the product status by using one equation.

I have made some PowerShell Enumerators to help creating readable code.

[Flags()] enum ProductState
{
Off = 0x0000
On = 0x1000
Snoozed = 0x2000
Expired = 0x3000
}
[Flags()] enum SignatureStatus
{
UpToDate = 0x00
OutOfDate = 0x10
}
[Flags()] enum ProductOwner
{
NonMs = 0x000
Windows = 0x100
}
[Flags()] enum ProductFlags
{
SignatureStatus = 0x000000F0
ProductOwner = 0x00000F00
ProductState = 0x0000F000
}

view raw
Enumerators.ps1
hosted with ❤ by GitHub

Using this information I wrote this little function that will return true of false.

function Test-IsProductStateOn {
<#
.SYNOPSIS
Tests if given product state has product state flag to On
.DESCRIPTION
Registry, WMI and other properties may contain a DWORD value or data object that represents the state of the corresponding product.
Specific state of the product is set to a bit in this DWORD, these states can be optained using bitwise operations.
This function will return true if the flag for product state is set to on, meaning this product is enabled.
.PARAMETER ProductState
The value (DWORD) containing the bitflags.
.EXAMPLE
PS C:\> Test-IsProductStateOn -ProductState 393472
False
This example shows basic functionality
.OUTPUTS
Bool
.NOTES
This function was build to resolve the state of a Antivirus Provider registered in Security Center.
Using this function it is possible to read which product is set to On or not.
Other states are Off, Snoozed and Expired which can be resolved by using the enums provided in this module.
Example: Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntivirusProduct | Where-Object {($_.productState -band [ProductFlags]::ProductState) -eq [ProductState]::Off}
Will list all products that are disabled.
Use Add-ProductStates to return the actual state or cast the value using the stateflag
$prod = Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntivirusProduct
[SignatureStatus]($prod[0].productState -band [ProductFlags]::SignatureStatus)
#>
[CmdletBinding()]
param (
# Product State contains a value (DWORD) that contains multiple bitflags and we use the productState flag (0000F000)
[Parameter(Mandatory, Position = 0, ValueFromPipelineByPropertyName, HelpMessage = "The value (DWORD) containing the bitflags.")]
[Alias("STATE")]
[UInt32]$ProductState
)
try
{
if( $([ProductState]::On -and $($ProductState -band [ProductFlags]::ProductState) ) )
{
return $true
}
else
{
return $false
}
}
catch
{
return $false
}
}

As you can see in the Enumerators list, Enable isn’t the only status these products can have, also Snooze is a state. In that case we will return false too ofcourse.

It is possible to return the actual state that a given product currently is in. Bear in mind productState has multiple members. That is why we need to set a Bitmask to retrieve the correct state.

Below a couple of examples:

To help out others and to work consistent  among different scripts and projects I added some functions and enumerators to the PowerShell Module.
In 1 line of PowerShell code you can make use of this function.

Install-Module wrt.helpers Scope CurrentUser Force
Test-IsProductStateOn 39756

TIP: Have a look at the function Add-ProductStates, this accepts array of products and adds members state and signatureStatus

 

I you have any requests or issues for this Module please let me know in this post or post an issue on the GitHub page.