I'm looking for comments/feedback on the following UDF which detects
the presence and configuration of several common Anti-Virus products.

It uses a common signature file to perform the tests, allowing a single
UDF to be extensible, supporting other products, and enhancements to
current products, without changing the UDF.

The script below contains some test code, the GetAVI UDF, and a
slightly modified version of fnWMIService. The modification to this
public UDF is simply to accept a pre-authenticated WMI object pointer.

To test, simply save the script and the AVSig.INI file (next post) to
a folder, and run KIX32 AVTEST.KIX. You can specify a remote computer
by adding $COMPUTER="computer" to the command line.

The signature file currently detects products from Symantec, McAfee,
Trend, Panda, and Microsoft. These signatures (other than McAfee) are
not validated, as I don't have access to computers with these products.
I'd like to hear about any issues, and the accuracy of the detections.
I would gladly update the signatures if anyone can provide me with
samples of the product registry settings, and file locations.

I have included a README in the second post for anyone that wishes to
experiment with the signature files.


;; KixGenerated: 2007/04/23 - 15:37:04 
 
Break On
 
; script to test the WMIGetAVI UDF 
; no strict coding standards defined at this stage 
 
Dim $, $A, $I, $J
 
$=SetOption('WrapAtEOL', 'On')
 
CLS
 
'This script tests the AntiVirus detection capabilities of the WMIGetAVI UDF.
It simply calls the UDF and displays the number of records and the data that
was returned. The number of records value validates that the AVSig.INI 
file was properly read. 

The data returned is
  0: boolean	product detected
  1: boolean	service running
  2: value	AV product name, from signature file
  3: value	name of section in AVSig.INI

Additional values will be returned only if the product is detected, and will
be in the format "Data Name","Data Value", with one value/data pair per
additional array elements. The total number of sub-array elements will vary
depending on the objects defined in the AVSig.INI file.

A computer name can be specified by invoking this script as
 KIX32 AVTest.kix $COMPUTER="remotehost"

Press a key to continue:'
 
Get $
 
? ?
 
$A = WMIGetAVI($COMPUTER)
@SERROR ?
 
UBound($A) + 1 ' Records' ?
 
For $I = 0 to UBound($A)
  ' 0: ' If $A[$I][0] 'Installed' Else 'Not Installed' EndIf ?
  ' 1: ' If $A[$I][1] 'Running' Else 'Not Running' EndIf ?
  ' 2: ' $A[$I][2] ?
  ' 3: ' $A[$I][3] ?
  For $J = 4 to UBound($A[$I])
    Right('  ' + $J, 2) ': ' $A[$I][$J] ?
  Next
  ?
Next
 
 
Exit 0
 
 
 
 
;; 
;;====================================================================== 
;; 
;;FUNCTION       WMIGetAVI() 
;; 
;;ACTION         Get AVI product info / status / detail 
;; 
;;AUTHOR         Glenn Barnas 
;; 
;;VERSION        0.0 / 2007/04/20 
;; 
;;SYNTAX         WMIGetAVI([Computer] [, WMIauth]) 
;; 
;;PARAMETERS     Computer - OPTIONAL, name of computer to check, default is local computer 
;; 
;;               WMIAuth  - OPTIONAL, preauthenticated WMI object pointer 
;; 
;;REMARKS        Checks for the presence of different AV products, returning an array of 
;;               product IDs, and status values representing the presence and running status 
;;               of each.  
;; 
;;               Part of the WMI suite because it makes WMI calls and accepts the WMIAuth pointer. 
;; 
;;RETURNS        array of arrays defining AV info - Each element represents one of the AV products 
;;               defined in the AVSig.ini file, while each sub-array contains the following information: 
;;                0: INSTALLED	Boolean	The product is installed 
;;                1: ACTIVE	Boolean	This product is actively running 
;;                2: NAME	String	Descriptive name of the product 
;;                3: PROD_ID	String	Short name of the product (INI section name) 
;;                4: DATA_PAIR	String	"Description","Value" pair 
;;                                      Represents one descriptive setting and it's value. There 
;;                                      can be many such elements returned - it is up to the user 
;;                                      to determine how many elements are returned and process them. 
;; 
;;                Only elements 0-3 are consistent, elements 4 and up will depend on the AV product 
;; 
;;DEPENDENCIES   AVSig.ini - AntiVirus signature database 
;;               WMIService -  
;; 
;;TESTED WITH    W2K, WXP, W2K3 
;; 
;;EXAMPLES        
; 
 
Function WMIGetAVI(OPTIONAL $_Computer, OPTIONAL $_pAuth)
 
  Dim $_, $_T1, $_T2, $_I, $_J, $_K	; temp & index vars 
  Dim $_WComputer			; computer name formatted for WMI queries 
  Dim $_SigFile				; path/name of signature file 
  Dim $_aProd, $_Prod			; array of product IDs enumerated from signature file 
  Dim $_aAVIData[0]			; array of AVI data detected 
  Dim $_aProdData[3]			; array of product-specific AVI data 
  Dim $_Action				; Flag to permit further action 
  Dim $_aTasks, $_Task			; array of tasks & enumerator var 
  Dim $_Services			; var holding service name 
 
  ; Insure computer name is either null or \\host\ format 
  If $_Computer
    $_Computer = '\\' + Join(Split($_Computer, '\', 3), '') + '\'
    $_WComputer = Join(Split($_Computer, '\', 3), '')
  Else
    $_WComputer = @WKSTA
  EndIf
 
  ; Locate the (preferred) Antivirus signature file 
  $_SigFile = ''
  If Exist(@SCRIPTDIR + '\AVSig.ini')
    $_SigFile = @SCRIPTDIR + '\AVSig.ini'	; default AVSig file location 
  EndIf
 
; ### 
  ; our environment defines the config file path in the S_CONFIG environment var 
  ; this will not affect general use, and can be removed if desired 
  If Exist('%S_CONFIG%\AVSig.ini')
    $_SigFile = '%S_CONFIG%\AVSig.ini'	; preferred AVSig file location, if present 
  EndIF
; ### 
  
  If Not $_SigFile
    Exit 2				; AVSig file not found - exit with status 2 
  EndIf
 
 
  ; Define the array of Product IDs. 
  ; enumerate the AVI products in the signature file 
  $_T1 = ReadProfileString($_SigFile, '', '')
  $_aProd = Split(Left($_T1,len($_T1)-1), Chr(10))
 
  ReDim $_aAVIData[UBound($_aProd)]
 
  For $_I = 0 to UBound($_aProd)
    $_Prod = $_aProd[$_I]		; working product name 
    ReDim $_aProdData[3]		; clear the array 
    $_aProdData[1] = 0			; default service state 
    $_aProdData[3] = $_Prod		; insert product ID 
 
    ; enumerate and process the individual signature tasks 
    $_T1 = ReadProfileString($_SigFile, $_Prod, '')
    $_aTasks = Split(Left($_T1,len($_T1)-1), Chr(10))
 
    $_Action = 1			; Allow data collection, assuming product is installed 
    $_J = 3
 
    For Each $_Task in $_aTasks
      Select
       ; Detect task - determine if the signature is present, quit scanning this product if it is not 
       Case $_Task = 'Detect'
        $_aProdData[0] = GetAVI_Detail($_Computer, ReadProfileString($_SigFile, $_Prod, $_Task))
        If Not $_aProdData[0]
          $_Action = 0
        EndIf
 
       ; determine if the service is running 
       Case $_Task = 'Service' And $_Action
        $_Services = Split(ReadProfileString($_SigFile, $_Prod, $_Task), '|')
        For $_K = 0 to UBound($_Services)
          $_ = WMIService($_Services[$_K], 'Name', $_WComputer, $_pAuth)
          If $_				; see if the service name is valid     
            $_ = WMIService($_Services[$_K], 'Started', $_WComputer, $_pAuth)
            If $_ And Not @ERROR
              $_aProdData[1] = Abs($_)
              $_K = 999
            EndIf	; found data 
          EndIf		; svc exists 
        Next
 
       ; Return the fixed descriptive name 
       Case $_Task = 'Name'
        $_aProdData[2] = ReadProfileString($_SigFile, $_Prod, $_Task)
 
       ; Iformational record - ignore 
       Case $_Task = 'Info' And $_Action
        ; This optional record contains information describing the author and date of the AV signature 
 
       ; process the AVI product-specific definitions 
       Case $_Action
        $_ = ReadProfileString($_SigFile, $_Prod, $_Task)
 
        ; is there a recursive lookup? Only one level of recursion is supported! 
        ; this permits the definition of a INSTALL PATH lookup, and reference 
        ; &INSTALL PATH&\file.ext for FILEDate or FILEVersion lookups 
        If InStr($_, '&')
          $_T1 = Split($_, '&')[1]		; get the embedded lookup id 
          $_T2 = GetAVI_Detail($_Computer, ReadProfileString($_SigFile, $_Prod, $_T1))
          $_ = Join(Split($_, '&' + $_T1 + '&'), $_T2)
        EndIf
 
        $_J = $_J + 1
        ReDim Preserve $_aProdData[$_J]
        $_aProdData[$_J] = $_Task + ';' + GetAVI_Detail($_Computer, $_)
 
      EndSelect
 
    Next
    $_aAVIData[$_I] = $_aProdData
 
  Next
 
  $WMIGetAVI = $_aAVIData			; return array of arrays 
  Exit 0
 
EndFunction
 
 
 
 
Function GetAVI_Detail($_Computer, $_Tasks)
 
  Dim $_, $_I, $_J, $_K					; temp & index vars 
  Dim $_Paths						; Paths to query 
  Dim $_Vals						; values to read 
  Dim $_Bool, $_BoolVal					; Bool result flag, True response value 
  Dim $_Result						; result string 
 
  ; array of METHOD, PATH(s), KEY, RESULT_TYPE 
  $_Tasks = Split($_Tasks + ';', ';')
 
  $_Bool = InStr($_Tasks[3], 'bool')			; Is this a BOOL or VALUE query? 
  $_BoolVal = IIf($_Bool <> 0, $_Tasks[3], '')		; Does the BOOL have an alternate TRUE response? 
  $_BoolVal = Split($_BoolVal, ':')[1]
  $_Result = IIf($_Bool, 0, '')				; set defaults for return to Bool False or empty string 
 
  Select
    Case $_Tasks[0] = 'REG'				; perform registry read 
      ; could have multiple keys to check 
      $_Paths = Split($_Tasks[1], '|')
      For $_I = 0 to UBound($_Paths)
        ; Verify that the key exists 
        If KeyExist($_Computer + $_Paths[$_I])
          $_Vals = Split($_Tasks[2], '|')
          For $_K = 0 to UBound($_Vals)
            $_ = ReadValue($_Computer + $_Paths[$_I], $_Vals[$_K])
            If $_
              $_J = 999					; got data, stop searching 
            EndIf
          Next
        EndIf	; Key Exists 
 
        If $_
          $_I = 999					; don't check other paths once a value is obtained 
          If $_Bool
            $_Result = IIf($_BoolVal <> '', $_BoolVal, 1)	; Return Bool True or Message String 
          Else
            $_Result = $_				; return the actual value 
          EndIf	; Bool/Value 
        EndIF	; $_ has data 
 
      Next	; $_I (Paths) 
 
    Case Left($_Tasks[0], 4) = 'File'			; perform file checks 
      ; could have multiple filepaths to check 
      $_Paths = Split($_Tasks[1], '|')
      For $_I = 0 to UBound($_Paths)
 
        ; if referencing a remote system, change the ":" to "$" in the path to use admin share 
        If $_Computer
          $_Paths[$_I] = Join(Split($_Paths[$_I], ':'), '$')
        EndIf
 
        ; Verify that the file exists 
        If Exist($_Computer + $_Paths[$_I])		; file exists 
 
          If Right($_Tasks[0], 1) = 'X'			; file exist check - return boolean 
            $_Result = IIf($_BoolVal <> '', $_BoolVal, 1)
            $_I = 999					; stop processing additional filepaths 
          EndIf
 
          If Right($_Tasks[0], 1) = 'V'			; file version check - return version string 
            $_ = ''
            If $_Tasks[2] $_ = $_Tasks[2] EndIf
            $_Result = GetFileVersion($_Computer + $_Paths[$_I], $_)
            $_I = 999					; stop processing additional filepaths 
          EndIf
 
          If Right($_Tasks[0], 1) = 'D'			; file date check - return timestamp 
            $_Result = GetFileTime($_Computer + $_Paths[$_I])
            $_I = 999					; stop processing additional filepaths 
          EndIf
 
        EndIf	; FielPath Exists 
 
      Next	; $_I (Paths) 
 
 
  EndSelect
 
  $GetAVI_Detail = $_Result
  Exit 0
 
EndFunction
 
 
 
 
 
;Function:	 fnWMIService() 
; 
;Author:	 Christopher Shilt 
;		 (christopher.shilt@relizon.com) 
; 
;Version:	 1.1 
; 
;Version History: 
; 2005-07-12   : NoVarsInStrings compliant 
; 2007-04-02   : GAB - permit use of pre-authenticated object pointer 
; 
;Action:	 Uses WMI to execute methods on services. 
; 
;Syntax:	 WMIService(SERVICE NAME, METHOD, Optional COMPUTER, Optional ObjPtr) 
; 
;Parameters: 
; SERVICE NAME : Required. Target service name to execute methods against. 
; 
; METHOD       : Required. See remarks for details. 
; 
; COMPUTER     : Optional. Local or Remote WMI enabled target computer. 
; 
;Remarks: 
; See WMI SDK on Win32_Service class for complete documentation:  
;     http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_service.asp 
; 
;Returns: 
; Queries on properties will return a value as indicated above. Method functions will return a return code  
; from the Win32_Service Method Return Value Table. 
; 
; Note: An error code will only be set if the WMI object is unable to be created.  Use the Return Code on  
;       methods to determine method success/failure. 
;  
;Dependencies: 
; KiX 4.02 (or higher) 
; WMI Enabled target computer. 
;  
; see associated .TXT file for detailed docs and examples 
; 
Function WMIService($sService, $sMethod, Optional $sComputer, OPTIONAL $pAuth)
 
  Dim $objWMI, $objSrvc, $nul
  If Not $sComputer $sComputer = @WKSTA EndIf
 
  ; Use pre-authenticated object pointer if provided, otherwise create a new object 
  If $pAuth
    $objWMI = $pAuth
  Else
    $objWMI = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" + $sComputer + "\root\cimv2")
    If @ERROR
      $WMIService = ''
      Exit VAL("&" + Right(DecToHex(@ERROR), 4))
    EndIf
  EndIf
 
  $objSrvc = $objWMI.ExecQuery('Select * from Win32_Service WHERE Name = "' + $sService + '"')
 
  For Each $objSrvc in $objSrvc 
    $nul = Execute(Chr(36) + "WMIService = " + Chr(36) + "objSrvc." + $sMethod)
  Next
 
EndFunction
 
 
 
_________________________
Actually I am a Rocket Scientist! \:D