Skip to content

Instantly share code, notes, and snippets.

@jdhitsolutions
Last active February 17, 2024 23:33
Show Gist options
  • Save jdhitsolutions/1b9dfb31fef91f34c54b344c6516c30b to your computer and use it in GitHub Desktop.
Save jdhitsolutions/1b9dfb31fef91f34c54b344c6516c30b to your computer and use it in GitHub Desktop.
This PowerShell function uses WMI via the Get-CimInstance command to query the state of installed anti-virus products.
#requires -version 5.1
Function Get-AVStatus {
<#
.Synopsis
Get anti-virus product information.
.Description
This command uses WMI via the Get-CimInstance command to query the state of installed anti-virus products. The default behavior is to only display enabled products, unless you use -All. You can query by computername or existing CIMSessions.
.Example
PS C:\> Get-AVStatus chi-win10
Displayname : ESET NOD32 Antivirus 9.0.386.0
ProductState : 266256
Enabled : True
UpToDate : True
Path : C:\Program Files\ESET\ESET NOD32 Antivirus\ecmd.exe
Timestamp : Thu, 21 Jul 2016 15:20:18 GMT
Computername : CHI-WIN10
.Example
PS C:\> import-csv s:\computers.csv | Get-AVStatus -All | Group Displayname | Select Name,Count | Sort Count,Name
Name Count
---- -----
ESET NOD32 Antivirus 9.0.386.0 12
ESET Endpoint Security 5.0 6
Windows Defender 4
360 Total Security 1
Import a CSV file which includes a Computername heading. The imported objects are piped to this command. The results are sent to Group-Object.
.Example
PS C:\> $cs | Get-AVStatus | where {-Not $_.UptoDate}
Displayname : ESET NOD32 Antivirus 9.0.386.0
ProductState : 266256
Enabled : True
UpToDate : False
Path : C:\Program Files\ESET\ESET NOD32 Antivirus\ecmd.exe
Timestamp : Wed, 20 Jul 2016 11:10:13 GMT
Computername : CHI-WIN11
Displayname : ESET NOD32 Antivirus 9.0.386.0
ProductState : 266256
Enabled : True
UpToDate : False
Path : C:\Program Files\ESET\ESET NOD32 Antivirus\ecmd.exe
Timestamp : Thu, 07 Jul 2016 15:15:26 GMT
Computername : CHI-WIN81
You can also pipe CIMSession objects. In this example, the output are enabled products that are not up to date.
.Notes
version: 1.1
Learn more about PowerShell:
http://jdhitsolutions.com/blog/essential-powershell-resources/
.Inputs
[string[]]
[Microsoft.Management.Infrastructure.CimSession[]]
.Outputs
[pscustomboject]
.Link
Get-CimInstance
#>
[cmdletbinding(DefaultParameterSetName = "computer")]
Param(
#The name of a computer to query.
[Parameter(
Position = 0,
ValueFromPipeline,
ValueFromPipelineByPropertyName,
ParameterSetName = "computer"
)]
[ValidateNotNullorEmpty()]
[string[]]$Computername = $env:COMPUTERNAME,
#An existing CIMsession.
[Parameter(ValueFromPipeline, ParameterSetName = "session")]
[Microsoft.Management.Infrastructure.CimSession[]]$CimSession,
#The default is enabled products only.
[switch]$All
)
Begin {
Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)"
Function ConvertTo-Hex {
Param([int]$Number)
'0x{0:x}' -f $Number
}
#initialize an hashtable of paramters to splat to Get-CimInstance
$cimParams = @{
Namespace = "root/SecurityCenter2"
ClassName = "Antivirusproduct"
ErrorAction = "Stop"
}
If ($All) {
Write-Verbose "[BEGIN ] Getting all AV products"
}
$results = @()
} #begin
Process {
#initialize an empty array to hold results
$AV = @()
Write-Verbose "[PROCESS] Using parameter set: $($pscmdlet.ParameterSetName)"
Write-Verbose "[PROCESS] PSBoundparameters: "
Write-Verbose ($PSBoundParameters | Out-String)
if ($pscmdlet.ParameterSetName -eq 'computer') {
foreach ($computer in $Computername) {
Write-Verbose "[PROCESS] Querying $($computer.ToUpper())"
$cimParams.ComputerName = $computer
Try {
$AV += Get-CimInstance @CimParams
}
Catch {
Write-Warning "[$($computer.ToUpper())] $($_.Exception.Message)"
$cimParams.ComputerName = $null
}
} #foreach computer
}
else {
foreach ($session in $CimSession) {
Write-Verbose "[PROCESS] Using session $($session.computername.toUpper())"
$cimParams.CimSession = $session
Try {
$AV += Get-CimInstance @CimParams
}
Catch {
Write-Warning "[$($session.computername.ToUpper())] $($_.Exception.Message)"
$cimParams.cimsession = $null
}
} #foreach computer
}
foreach ($item in $AV) {
Write-Verbose "[PROCESS] Found $($item.Displayname)"
$hx = ConvertTo-Hex $item.ProductState
$mid = $hx.Substring(3, 2)
if ($mid -match "00|01") {
$Enabled = $False
}
else {
$Enabled = $True
}
$end = $hx.Substring(5)
if ($end -eq "00") {
$UpToDate = $True
}
else {
$UpToDate = $False
}
$results += $item | Select-Object Displayname, ProductState,
@{Name = "Enabled"; Expression = { $Enabled } },
@{Name = "UpToDate"; Expression = { $UptoDate } },
@{Name = "Path"; Expression = { $_.pathToSignedProductExe } },
Timestamp,
@{Name = "Computername"; Expression = { $_.PSComputername.toUpper() } }
} #foreach
} #process
End {
If ($All) {
$results
}
else {
#filter for enabled only
($results).Where( { $_.enabled })
}
Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)"
} #end
} #end function
Copy link

ghost commented Jul 22, 2016

Are there other particular objects that support this function? Once I realized my WMI service wasn't running and stopped getting that error I ran this in administrator sessions in both the ISE and in a Powershell console. It ran but returns nothing even with the -All keyword.

@InfsecOu5
Copy link

when i tried to run it returned nothing even with the -All keyword.

@0xdevalias
Copy link

0xdevalias commented Jun 12, 2018

This gists code relates to the following blog post:

For anyone else that stumbles across this while trying to figure how to parse productState, I'm not sure that the logic is correct.

In particular, it says that if the last 2 chars after converting to hex are 00, then $UpToDate = $True. But applying the same logic to the productState I have of AV products that I know are out of date, it still says that they are 'UpToDate'.

Edit: It could just be that the productState values I have are outdated compared to the 'status' I am comparing them against.

There is a lot of discussion in the following thread, but this comment in particular about them being bit fields is particularly interesting/relevant:

The following bits are mentioned in that thread:

  • 19th bit = not so sure but, Av is turned on ( I wouldn't be sure it's enabled)
  • 13th bit = On Access Scanning (Memory Resident Scanning) is on, this tells you that the product is scanning every file that you open as opposed to just scanning at regular intervals.
  • 5th Bit = if this is true (==1) the virus scanner is out of date

When bit 5 is enabled, that is 00010000, which when converted to hex is 0x10, which matches the logic shown here.

@jdhitsolutions
Copy link
Author

This code will not work for Windows Server 2016 and later as it appears the referenced namespace no longer exists on these platforms and I can't find similar classes.

@jdhitsolutions
Copy link
Author

Use something like this for servers.
image

@Kf637
Copy link

Kf637 commented Jan 20, 2023

Code does not return anything when ran.

@jdhitsolutions
Copy link
Author

There are limitations to this solution. What operating system and PowerShell version are you running? It is also possible that the vendor for your AV product isn't populating the CIM database.

@Kf637
Copy link

Kf637 commented Jan 20, 2023

There are limitations to this solution. What operating system and PowerShell version are you running? It is also possible that the vendor for your AV product isn't populating the CIM database.

Windows 11 Pro
PSVersion 5.1.22621.963

@jdhitsolutions
Copy link
Author

Could be the AV vendor Could be CIM. Sometimes the CIM repository doesn't get properly populated. Did you try on a different desktop? It works for my Windows 11 desktop.

image

@Kf637
Copy link

Kf637 commented Jan 20, 2023

Found a scipt that worked on StackOverflow

StackOverflow Post

@jdhitsolutions
Copy link
Author

I can also get it to work querying a Windows 10 VM remotely.

image

I trust the code works. You could verify if the classes even exist.

image

If the classes exist, try querying the instances directly.

Get-Ciminstance -namespace root\securitycenter2 -ClassName antivirusproduct

If you get no result, either the vendor isn't populating the instance with information, or some policy might be preventing you from querying this information.

@jdhitsolutions
Copy link
Author

That's a legacy approach to what I am doing. The only other thing I can think of is that the WSMan ports are disabled, which would prevent the CIM cmdlets from querying remote computers. But it shouldn't have an effect if you run the command locally.

@paschott
Copy link

I can run this without issue - see Windows Defender and what seems to be the most recent definitions:
Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntivirusProduct

If I run the Get-AVStatus function as in the blog post, I get nothing. Running on Win11 Pro, using PS v5.1 ISE to load up the function/code.
Get-CimInstance | Get-AVStatus -all ## no results

@jdhitsolutions
Copy link
Author

You don't pipe Get-CimInstance. Just run Get-AVStatus -all

@paschott
Copy link

paschott commented Aug 21, 2023

Doing that, I get:

The client cannot connect to the destination specified in the request. Verify that the service on the destination is running and is accepting requests. Consult the logs and documentation for the WS-Management service running on the destination, most commonly IIS or WinRM. If the destination is the WinRM service, run the following command on the destination to analyze and configure the WinRM service: "winrm quickconfig".

I did manage to get WinRM enabled - got the results, but I think I still need to use the example you posted above for the virus definition dates and such.
Get-cimInstance -Namespace root/microsoft/protectionmanagement -class msft_mpcomputerstatus

We're trying to make sure things are enabled, but also reasonably up to date. I think this shows the app is up to date, but doesn't say anything about the definitions.

@jdhitsolutions
Copy link
Author

You definitely need PowerShell remoting enabled for this to work against a remote computer. My function is limited to what the CIM class can report. My function will tell you the state of the application. It isn't designed to tell you anything about signature files.

@paschott
Copy link

Hmm - that was against my local machine, which is why I thought it was odd to have to enable WinRM for it to work. It did work. I just need to run that additional script you provided for the signatures to make sure someone's not running with crazy old sigs. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment