Category Archives: Antivirus Administration

Repair a Broken Symantec Management Client Service

As mentioned in a previous post, a SEP client can have a broken SMC service and you may very well never know that there is anything wrong (unless you use the script found in that post :-) ).  This is the Powershell script that will allow you to remotely repair most of those broken clients.  Again, as with the audit script, you must have admin rights to the computer that you are running this script on.  Since it uses WMI to run a remote process, WMI must also be working properly on the remote computer.

The script will repair one client at a time using the “-compname” parameter.  However, you can modify the script to read a text file by adding another “ForEach” and a “Get-Content” to the script.

#################################################################
#							        #
#- Antivirus SMC service repair for clients with corrupt        #
#     security policies.                                        #
#- Requires a commandline parm of -compname          		#
# - Parms can be changed to accept a list for multiple PCs	#
#								#
#- Written by Jim Melton					#
#								#
#################################################################
 
## Accept computer name from the commandline with -compname
param ([string]$CompName = $(throw "Text Input is Required"))
Write-Host $CompName
 
## Load the System.ServiceProcess .net class, set the Error Action to
## Sliently Continue, and initialize some variables.
[System.Reflection.Assembly]::LoadWithPartialName('system.serviceprocess')
$ErrorActionPreference = "Continue"
$SEPreg = "SOFTWARE\Symantec\Symantec Endpoint Protection\AV"
$SEPhome = "Home Directory"
$SEPinstall = ""
$DefPaths = "SOFTWARE\Symantec\SharedDefs"
$SMCkey = "SOFTWARE\Symantec\Symantec Endpoint Protection\SMC"
$SMCsvc = "Symantec Management Client"
$SEPnetwork = ""
 
## Create a new Pint object to test to see if the remote computer
## is online so the script doesn't bomb, assign -compname to a
## variable and ping the computer.
$ping = New-Object System.Net.NetworkInformation.Ping
$computer = $CompName
$reply = $ping.send($computer)
 
## If the computer replies, open a remote process on the broken computer,
## open the registry (HKLM in this case), and count the SEP registry entries
## to make sure SEP is actually installed.
If ($reply.status -eq 'Success'){
	$remoteProcess = [WmiClass]"\\$computer\ROOT\CIMV2:Win32_Process"
	$OpenRegistry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine,$computer)
	$DefsKey = $OpenRegistry.OpenSubKey($DefPaths,$true)
	[int]$ValueCount = $DefsKey.ValueCount
 
	## If SEP is installed and has valid registry entries to get paths from,
	## find the SEP install path, write it to the screen, rename the existing
	## serdef.dat, copy a new serdef.dat, and restart the SMC service using
	## our remote WMI process.
	If ([int]$ValueCount -gt 0){
 
		If ($DefsKey.GetValueNames() -ne $null){
			$SEPpath = $OpenRegistry.OpenSubKey($SEPreg,$true)
			$SEPinstall = $SEPpath.GetValue("$SEPhome")
			$SEPtemp = $SEPinstall.length
			[int]$SEPtemp = [int]$SEPtemp - 3
			$SEPinstall = $SEPinstall.substring($SEPinstall.length - [int]$SEPtemp)
			Write-Host "Symantec Endpoint Protection is installed at $SEPinstall."
			Copy-Item "\\$computer\c$\$SEPinstall\serdef.dat" "\\$computer\c$\$SEPinstall\serdef.old" -Recurse
			$remoteProcess.Create("c:\$SEPinstall\smc.exe -start")
			Write-Host "Starting SMC service."
 
			Write-Host "Finished"
			}
		Else{
			Write-Host "SEP Install Not Found"
			}
	}
	Else{
		Write-Host "Not installed"
	}
}
 
Else{
	Write-Host "Computer is not responding to a ping"
#	}
}

Audit Symantec Endpoint Protection Clients

If you’ve ever administered clients running Symantec Endpoint Protection 11 (SEP), you know how woefully inadequate the SEP management interface is for determining the health of your clients.  If a computer get’s a corrupt policy, chances are the Symantec Management Client (SMC) service will stop and refuse to restart.  When this happens, the component of SEP responsible for updating definitions and reporting out of date definitions stops working.  The real-time scanning component – the Symantec Endpoint Protection service – will continue to run, but it will never update.  Since the service that tells the SEP Manager that a client is out of date doesn’t work, chances are you will never know about it unless one (or more) of your end users happens to report it.

So, in order to find the broken SEP clients in my company – about 14,000 computers in various locations around the country – I turned to Powershell and the .NET Framework to do the heavy lifting.  The results were both very surprising and disconcerting.  Hundreds of our computers were out of date and not reporting this to the SEP Manager.  This information allowed us to update our computers using a combination of some automation and the good ol’ fashioned Sneaker Net.

This script assumes a few things:

  1. You must have admin privileges on the computers you are auditing.
  2. File and Print Sharing is enabled on the computers.
  3. You have a pretty solid list of computer names to run the Powershell script against (I used an SCCM list of all discovered Active Directory computer objects).
#################################################################
#
#
#	- Antivirus Health Check Script
#	- Audit computer objects from a text file specified at
#		runtime.  Checks for AV version, stopped/running
#		services, and definition dates.
#
#	- Written by Jim Melton
#        ** Feel free to use and modify this script as needed.
#            Just please give credit if you do.
#
#################################################################
 
## Setup command line parameter to allow user to specify content
## file at runtime.  Parameter is -FileName
param ([string]$FileName = $(throw "Text Input is Required"))
Write-Host $FileName
 
## Load assembly used to interrogate services on the remote machine.
[System.Reflection.Assembly]::LoadWithPartialName('system.serviceprocess')
$erroractionpreference = "Continue"
 
## Set variables
$SMCsvc = "Symantec Management Client"
$SMCstatus = ""
$SEPsvc = "Symantec Endpoint Protection"
$SAVsvc = "Symantec Antivirus"
$SAV8svc = "Symantec Antivirus Client"
$Path = "c$\Program Files\Common Files\Symantec Shared\VirusDefs"
$SavePath = "\\SERVER\SHARE\"
 
## Instantiate Excel ComObject and write headings on spreadsheet.
$excel = New-Object -comobject Excel.Application
$excel.visible = $true
 
$workbook = $excel.Workbooks.Add()
$worksheet = $workbook.Worksheets.Item(1)
 
$worksheet.Cells.Item(1,1) = "Machine Name"
$worksheet.Cells.Item(1,2) = "IP Address"
$worksheet.Cells.Item(1,3) = "AV Version"
$worksheet.Cells.Item(1,4) = "Virus Definition"
$worksheet.Cells.Item(1,5) = "Status"
 
$range = $worksheet.UsedRange
$range.Interior.ColorIndex = 19
$range.Font.ColorIndex = 11
$range.Font.Bold = $True
 
$intRow = 2
 
## Create Ping object
$ping = New-Object System.Net.NetworkInformation.Ping
 
## Get data from user specified file
$colComputers = get-content $FileName 
 
foreach ($computer in $colComputers){
 
	## Zero variables at the top of the loop and ping the next machin
	## in the list.
	$IPaddress = ""
	$AVservice = ""
	[string]$service = ""
	$VirDate = ""
	$reply = $ping.send($computer)
	$IPaddress = [string]$reply.Address
 
	## Check ping reply.  If it's a success, test the Symantec Shared files path to see if
	## it exists.  If the path is valid, get the content of definfo.dat and parse the
	## definition date and rev.
	If ($reply.status -eq 'Success'){
		$ValidPath = Test-Path "\\$computer\$path"
		If ($ValidPath -eq "True"){
		     $DefInfo = Get-Content "\\$computer\$Path\definfo.dat"
		     $DefLine = $DefInfo[1]
		     $DefYear = $DefLine.substring(8,4)
		     $DefMonth = $DefLine.substring(12,2)
		     $DefDay = $DefLine.substring(14,2)
		     $Revision = $DefLine.substring(17,3)
		     [int]$DefDate = $DefLine.Substring(8,8)
		     [string]$DefDate = "$DefMonth" + "-" + "$DefDay" + "-" + "$DefYear"
		     $VirDate = [datetime]$DefDate
		}
 
		## If the path or definfo.dat doesn't exist, bail out of the if statement.
		Else{
		     $VirDate = $null
		     Continue
		}
	## Query the remote machine for the SEP, SAV, or SAV 8x RTVScan services.  Also,
	## query the SMC service for SEP.  These are separate in order to make sure
	## that both the SEP and SMC services are running on a machine.
	$services = [System.ServiceProcess.ServiceController]::GetServices($computer) | `
			where { (($_.displayname -eq $SEPsvc) -or ($_.displayname -eq $SAVsvc) `
			                                      -or ($_.displayname -eq $SAV8svc))}
	$SMCservice = [System.ServiceProcess.ServiceController]::GetServices($computer) | `
			where { ($_.displayname -eq $SMCsvc)}
 
	## Check to see what services are present.  If there are no services on the machine, write that
	## into the spreadsheet and continue on to the next computer.
	foreach ($service in $services){
		If ($service.displayname -eq $null){
			$worksheet.Cells.Item($intRow,1) = $computer
			$worksheet.Cells.Item($intRow,2) = $IPaddress
			$worksheet.Cells.Item($intRow,5) = "Services are not present on $computer."
			$intRow = $intRow + 1
		}
 
		## Otherwise, if it's SAV or SAV 8, insert SAV into $AVService.
		Else{
			If (($service.DisplayName -eq $SAVsvc) -or ($service.DisplayName -eq $SAV8svc)){
				$AVservice = "Symantec Antivirus"
			}
 
			## If not Null or SAV, then it must be SEP.  Insert SEP into $AVService.  Also check to see if
			## the SMC service is stopped.  If so, set $SMCstatus to Stopped.
			Else {
				If ($service.DisplayName -eq $SEPsvc){
				     $AVservice = "Symantec Endpoint Protection"
				     If ($SMCservice.DisplayName -eq $SMCsvc){
		        	          If ([string]$SMCservice.status -eq 'Stopped'){
			        		$SMCstatus = "Stopped"
					  }
				     }
				}
			}
 
		## If computer is online and has AV installed, insert its information into the spreadsheet.
		## If the SMC service is stopped, append that info to the Status block showing the SEP
		## RTVScan status.  Otherwise,just insert the status of RTVScan.
		$worksheet.Cells.Item($intRow,1) = $computer
		$worksheet.Cells.Item($intRow,2) = $IPaddress
		$worksheet.Cells.Item($intRow,3) = $AVservice
		$worksheet.Cells.Item($intRow,4) = "$DefDate" + " rev " + "$Revision"
		If ($SMCstatus -eq "Stopped"){
			$worksheet.Cells.Item($intRow,5) = [string]$service.Status + ", `
				however, the SMC Service is stopped."
		}
		Else{
			$worksheet.Cells.Item($intRow,5) = [string]$service.Status
		}
	}
 
	## Increment the spreadsheet row and reset $SMCstatus to blank.
	$intRow = $intRow + 1
	$SMCstatus = ""
		}
	}
 
	## If the machine doesn't respond to a ping, insert that in the spreadsheet.
	Else{
		If ($reply.status -eq "TimedOut"){
			$worksheet.Cells.Item($intRow,1) = $computer
			$worksheet.Cells.Item($intRow,3) = $IPaddress
			$worksheet.Cells.Item($intRow,5) = "Did not respond to a ping."
			$intRow = $intRow + 1
		}
		## If the machine doesn't respond to a ping and the IP is not valid, the computer name
		## must not bevalid any longer.
		Else{
			$IPaddress = "Computer name is not valid."
			$worksheet.Cells.Item($intRow,1) = $computer
			$worksheet.Cells.Item($intRow,3) = $IPaddress
			$worksheet.Cells.Item($intRow,5) = "Did not respond to a ping."
			$intRow = $intRow + 1
		}
	}
 
## Once the data is completely written to the spreadsheet, auto-fit all columns to the data width.
$range.EntireColumn.AutoFit() | Out-Null
}
 
## Switch statement using a regular expression switch to determine location being audited based on the
## input file filename.  If you need an Adhoc report, use computers.txt as the input file.
Switch -regex ($FileName){
 
	"loc01comps.txt" {$Location = "Location1"}
	"loc02comps.txt" {$Location = "Location2"}
	"loc03comps.txt" {$Location = "Location3"}
	"loc04comps.txt" {$Location = "Location4"}
	"loc05comps.txt" {$Location = "Location5"}
	"loc06comps.txt" {$Location = "Location6"}
	"loc07comps.txt" {$Location = "Location7"}
	"loc08comps.txt" {$Location = "Location8"}
	"loc09comps.txt" {$Location = "Location9"}
	"loc10comps.txt" {$Location = "Location10"}
	"loc11comps.txt" {$Location = "Location11"}
	"computers.txt"    {$Location = "Adhoc Report"}
	default {$Location = "Other"}
}
 
## Save the spreadsheet to the path in the variable using the name determined from the above Switch.
$SpreadSheetName = $SavePath+$Location+".xls"
$workbook.Worksheets.item(1).Name = $Location
 
## Check to see if the spreadsheet exists.  If it does, delete it and recreate it, and save it.
## Attempt to close Excel and end the ComObject.  This doesn't seem to work well with Excel 2K7,
## so we are killing processes called Excel.  This will close all Excel processes.
If(Test-Path $SpreadSheetName){
	Remove-Item $SpreadSheetName
	Write-host $SpreadSheetName
	$excel.ActiveWorkbook.SaveAs($SpreadSheetName)
	$excel.Quit()
	[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
	Remove-Variable excel
	Get-Process -Name Excel | Kill
}
Else{
	$excel.ActiveWorkbook.SaveAs($SpreadSheetName)
	$excel.Quit()
	[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
	Remove-Variable excel
	Get-Process -Name Excel | Kill
}