# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""
   Gather Windows Anti-Virus (AV) software information for:
   1. Symantec (Symantec/SEP)
   2. McAfee (Intel/Network Associates/McAfee)
   3. TrendMicro
   4. Microsoft Enpoint Protection, comprises:
         Microsoft Defender AS (MDAS)
         Microsoft Security Essentials (MSE)
         Microsoft Defender AV (MDAV)
"""

import os
import platform
import stat
import sys
import time, calendar
import datetime
import winreg as _winreg
import string
from sigma.util import ostypes
import logging
import operator

logger = logging.getLogger("gather.gather_winantivirus")

hmonthmap = {
    1: "January",
    2: "February",
    3: "March",
    4: "April",
    5: "May",
    6: "June",
    7: "July",
    8: "August",
    9: "September",
    10: "October",
    11: "November",
    12: "December",
    }

hshortmonthmap = {
    "Jan": 1,
    "Feb": 2,
    "Mar": 3,
    "Apr": 4,
    "May": 5,
    "Jun": 6,
    "Jul": 7,
    "Aug": 8,
    "Sep": 9,
    "Oct": 10,
    "Nov": 11,
    "Dec": 12,
    }
hexmap = {
    '0': 0,
    '1': 1,
    '2': 2,
    '3': 3,
    '4': 4,
    '5': 5,
    '6': 6,
    '7': 7,
    '8': 8,
    '9': 9,
    'A': 10,
    'B': 11,
    'C': 12,
    'D': 13,
    'E': 14,
    'F': 15,
    }

# Helper to invert a dict
def invert(d):
    i = {}
    for k, v in d.items():
        i[v] = k
    return i

inv_hmonthmap = invert(hmonthmap)

def Gather(collector):
    if os.name != "nt":
        raise RuntimeError("Can only gather Windows process info on Windows")
    table = collector.table("winantivirus", ["avname", "avversion", "avdefversion", "avdefdate", "avlastused"])
    rootkey = getattr(_winreg, "HKEY_LOCAL_MACHINE")

    na_av(table, rootkey)                       #McAfee(Network Associates - NetAssoc)
    na_av85(table, rootkey)                     #McAfee(NetAssoc) VirusScan Enterprise v8.5i (2006, ESP3.5); McAfee(Intel) v8.8 (2016, ESP3.7)
    symantec_av81(table, rootkey)
    symantec_av9(table, rootkey)
    symantec_av200x(table, rootkey, "norton antivirus 2004")
    symantec_av200x(table, rootkey, "norton antivirus 2005")
    symantec_SEP12_1_6(table,rootkey)                     #ESP3.7
    trend_micro_client(table, rootkey, "trend micro officescan client")
    trend_micro_wfbs9(table, rootkey)
    microsoft_EP(table, rootkey)      #Microsoft Endpoint Protection: Microsoft Defender & Security Essentials (from XP to ws2016)


"""get_LogFile_Info(logType, logfile)
   logType: 'AS', 'AM'
   logfile: 'C:\\Windows\\Temp\\MpSigStub.log'
   returns defdate, defversion, productversion
"""
def get_MS_Info(rootkey, ostype, logType, logfile):
    #Get the Windows Defender definition version
    defdate = 0
    defversion = ''
    productversion = ''

    if ostype == 'winxp':
        try:
            updatekey = _winreg.OpenKey(rootkey, r"SOFTWARE\Microsoft\Windows Defender\Signature Updates")
        except OSError as err:
            logger.exception('Microsoft Defender not found on Windows XP')
            return defdate, defversion, prductversion

        try:
            nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
            for i in range(nvalues):
                nm, val, typ = _winreg.EnumValue(updatekey, i)
                if nm.lower() =='assignatureversion':
                    defversion =  val
            _winreg.CloseKey(updatekey)
        except OSError as err:
            _winreg.CloseKey(updatekey)
            return

        return defdate, defversion, productversion


    try:
        lines = open(logfile).readlines()
        lines.reverse()
        for l in lines:
        #for line in reversed(open(logfile).readlines()):
            l= l.replace("\000","")
            if defdate == 0:
                if l.find('End time: ') != -1:
                    strTok = l.split('End time: ')
                    d1= strTok[1].strip()
                    if d1[len(d1)-1] == 'Z':
                        #Format: 2016-11-27T00:23:52Z
                        d1=d1.replace(" ", "T")
                        defdate = get_msFromUTC(d1)
                    else:
                        #Format: '2/26/2017 2:33 AM'
                        defdate = get_msFromDateTimeFormat2 (d1)
                    if productversion != '' and defversion != '':
                        break
                    else:
                        continue


            if defversion == '':
                if l.find('===')!= -1:
                    defdate=0
                    continue
                if logType == 'AV':
                    if l.find('AV delta VDM: ') != -1:
                        strTok = l.split('AV delta VDM: ')
                        defversion = strTok[1].split()[1]
                        if productversion != '' and defdate != 0:
                            break
                        else:
                            continue
                elif logType == 'AS':
                    if l.find('AS delta VDM: ') != -1:
                        strTok = l.split('AS delta VDM: ')
                        defversion = strTok[1].split()[1]
                        if productversion != '' and defdate != 0:
                            break
                        else:
                            continue


            if productversion == '':
                if l.find('Product:') != -1:
                    while '  ' in l:
                        l = l.replace('  ', ' ').strip()
                    strTok = l.split(' ')
                    productversion = strTok[1].strip()
                    if defdate != 0 and defversion != '':
                        break
                    else:
                        continue

        #lastused = getFileDate(scanLogFile)   #inaccurate: other processes write to this log
    except:
        logger.exception('Exception processing ' + logfile)
        return

    return defdate, defversion, productversion



"""get_Scan_Info(ostype, basePath, scanLogFile)
   returns ms since epoch of the timestamp of the date the scan was last run
"""
def get_MS_Scan_Info(ostype, basePath, productname, scanLogFile):
    path = ''
    scanPath = ''
    lastused = 0

    if ostype == ('winxp' or 'XP'):
        f = open(scanLogFile)
        prevLine = ''
        try:
            #for line in reversed(f.readlines()):
            lines = open(logfile).readlines()
            lines.reverse()
            for l in lines:
                l= l.replace("\000","")
                if l.find('Scan -RestrictPrivileges') != -1:
                    #Format:  Start Time: Fri Mar 03 2017 08:08:17
                    strTok = prevLine.split(' Start Time: ')
                    d1= strTok[1].strip()
                    d1=d1[4:]
                    lastused= get_msFromDateTimeFormat1(d1)
                    return lastused
                else:
                    prevLine = l
        except:
            logger.exception('Exception processing scan info')
            return 0

    #if ostype == 'win7' or '7':
        #scanPath = basePath + '\Windows Defender\Scans\History\Results\Quick'
    if productname == 'Microsoft Defender':
        scanPath = basePath + '\\Windows Defender\\Scans\\History\\Results\\System'
    elif productname == 'Microsoft Security Essentials':
        scanPath = basePath + '\\Microsoft Antimalware\\Scans\\History\\Results\\System'

    alist={}
    now = time.time()
    try:
        os.chdir(scanPath)
        latest = 0
        for file in os.listdir("."):
            if os.path.isfile(file):
                timestamp = os.path.getmtime( file )
                if timestamp > latest:
                    latest = timestamp
        lastused = latest
    except:
        logger.exception('Exception processing scan time')
        return 0

    return lastused



"""
   For platforms > w8.1, Microsoft Defender AV (MDAV) info is accessed through
   MI (Management Infrastructure) API
"""
def get_MDAV_Info(logfile):
    #import platform
    #import subprocess
    #import mi    

    defdate = 0
    lastused = 0
    defversion = ''
    productversion = ''

    """
    with mi.Application() as a:
        with a.create_session(protocol=mi.PROTOCOL_WMIDCOM) as s:
            with s.exec_query(u"Root\\Microsoft\\Windows\\Defender", u"select * from MSFT_MpComputerStatus") as q:
                i = q.get_next_instance()
                # Keep the values as native (Unicode) str -- the old
                # .encode('ascii','ignore') both dropped non-ASCII characters
                # AND, under Python 3, turned these into bytes that stringified
                # to "b'...'" downstream. str() guards against non-str COM types.
                defversion = str(i.AntivirusSignatureVersion)
                productversion = str(i.AMProductVersion)
                #defdate info seems to be incorrect
                #defdate = int(time.mktime(i.AntivirusSignatureLastUpdated.timetuple()))
                if i.FullScanStartTime != None:
                    lastused = int(time.mktime(i.FullScanStartTime.timetuple()))
                elif i.QuickScanStartTime != None:
                    lastused = int(time.mktime(i.QuickScanStartTime.timetuple()) *1000)

                temp = str(datetime.datetime.fromtimestamp(lastused))
                lastused = get_msFromUTC(temp)

    for line in reversed(open(logfile).readlines()):
        l= line.replace("\000","")
        if l.find('End time: ') != -1:
            strTok = l.split('End time: ')
            d1= strTok[1].strip()
            if d1[len(d1)-1] == 'Z':
                #Format: 2016-11-27T00:23:52Z
                d1=d1.replace(" ", "T")
                defdate = get_msFromUTC(d1)
            else:
                #Format: '2/26/2017 2:33 AM'
                defdate = get_msFromDateTimeFormat2 (d1)
            break
        """
    return defdate, lastused, defversion, productversion



""" Microsoft Endpoint Protection MEP:
    MDAS - Microsoft Defender Anti-Spyware
    MSE  - Microsoft Security Essentials
    MDAV - Microsoft Defender Anti-Virus
"""
def microsoft_EP(table, rootkey):
        productversion = ''
        productname = ''
        defversion = ''
        lastused = 0
        defdate = 0

        #Log files
        logfile = 'C:\\Windows\\Temp\\MpSigStub.log'
        scanLogFile = 'C:\\WINDOWS\\Temp\\MpCmdRun.log'
        basePath = 'C:\\ProgramData\\Microsoft'

        #Get the OS type
        osTypeDAS =''
        osTypeDAV =''
        tmpOS1 = ostypes.guess_ostype()     #winxp/win7/winvista/win8/windows
        tmpOS2 = platform.release()         #XP, 7, 8.1, 10, 2012ServerR2 /Windows /ws2016 shows as 10
        DAS_os = ['winxp', 'XP', 'winvista', 'win2003s', 'win2k8', 'win2k8r2', '7', 'win7']
        DAV_os = ['8', 'win8', '8.1', 'win8.1', '10', 'win10', 'win2012r2', 'win2016']

        if tmpOS1 in DAS_os:
            osTypeDAS = tmpOS1
        elif tmpOS1 in DAV_os:
            osTypeDAV = tmpOS1
        elif tmpOS2 in DAS_os:
            osTypeDAS = tmpOS2
        elif tmpOS2 in DAV_os:
            osTypeDAV = tmpOS2


        productname = get_MS_ProductName(rootkey, logfile)

        #if MSE (XP, Vista, 7)
        if productname == 'Microsoft Security Essentials':
            logType = "AV"
            defdate, defversion, productversion = get_MS_Info(rootkey, osTypeDAS, logType, logfile)
            lastused = get_MS_Scan_Info(osTypeDAS, basePath, productname, scanLogFile)
        elif productname == 'Microsoft Defender':
            #Check for DAS or DAV
            if osTypeDAV == "win8":               #MDAV: w8
                logType = "AV"
                defdate, defversion, productversion = get_MS_Info(rootkey, osTypeDAV, logType, logfile)
                lastused = get_MS_Scan_Info(osTypeDAV, basePath, productname, scanLogFile)
            elif osTypeDAS != '':                 #MDAS: XP, Vista, w7
                logType = "AS"
                defdate, defversion, productversion = get_MS_Info(rootkey, osTypeDAS, logType, logfile)
                lastused = get_MS_Scan_Info(osTypeDAS, basePath, productname, scanLogFile)
            elif osTypeDAV != '':                 #MDAV: w8.1, w10, ws2016
                if platform.python_version() == '2.3.4':
                    logType = "AV"
                    defdate, defversion, productversion = get_MS_Info(rootkey, osTypeDAV, logType, logfile)
                    lastused = get_MS_Scan_Info(osTypeDAV, basePath, productname, scanLogFile)

                #the "API" gatherer should work on any version of Python in the 2.7.x range
                elif platform.python_version()[0] == '2' and platform.python_version()[2] == '7':
                    defdate, lastused, defversion, productversion = get_MDAV_Info(logfile)

        logger.info("Endpoint Protection - Prod name: " + productname)
        logger.info("Endpoint Protection - Prod ver:  " + productversion)
        logger.info("Endpoint Protection - Def ver:   " + defversion)
        logger.info("Endpoint Protection - Last scan: " + str(datetime.datetime.fromtimestamp(lastused)))
        logger.info("Endpoint Protection - Def date:  " + str(datetime.datetime.fromtimestamp(defdate)))
        #print "Endpoint Protection - Last scan: " + str(datetime.datetime.fromtimestamp(lastused))
        #print "Endpoint Protection - Def date:  " + str(datetime.datetime.fromtimestamp(defdate))
        table.add((productname, productversion, defversion, defdate, lastused))


def get_MS_ProductName(rootkey,logfile):
    #Get the MSE product name
    #Inaccessible from registry; elevated access rights???
    r"""
    try:
        updatekey = _winreg.OpenKey(rootkey, r"SOFTWARE\Microsoft\Microsoft Antimalware\Signature Updates")
        productname = 'Microsoft Security Essentials'
        return productname
    except OSError as err:
        print('Microsoft Security Essentials is not installed')
        return


    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() =='displayname':
                if val.find('Windows Defender') != -1:
                    productname =  'Windows Defender'
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    """
    try:
        #f = open(logfile)
        log = open(logfile).readlines()
        revlog = log.reverse()
        for line in log:
        #for line in reversed(f.readlines()):      #2.7
            l= line.replace("\000","")
            #if defdate == 0:
            if l.find('Microsoft Security Essentials') != -1:
                return 'Microsoft Security Essentials'
        #endfor
    except:
            logger.exception('Microsoft Security Essentials not found')
            pass



    #Get the Windows Defender product name
    try:
        updatekey = _winreg.OpenKey(rootkey, r"SYSTEM\CurrentControlSet\Services\WinDefend")
        productname = 'Microsoft Defender'
        return productname
    except OSError as err:
        logger.exception('Microsoft Defender not found')
        return

    """
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() =='displayname':
                if val.find('Windows Defender') != -1:
                    productname =  'Windows Defender'
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return

    return productname
    """

"""get_msFromDateTimeFormat1(s)
   s format:   MMM DD YYYY hh:mm:ss
   e.g.:      'Jun 01 2005 13:59:01'
   returns ms since epoch (local time)
"""
def get_msFromDateTimeFormat1(s):
    try:
        assert len(s) == 20
        MM = hshortmonthmap.get(s[0:3])
        DD = int(s[4:6])
        YYYY = int(s[7:11])
        hh = int(s[12:14])
        mm = int(s[15:17])
        ss = int(s[18:20])
        #print "lastused", YYYY, MM, DD, hh, mm, ss
        t = (YYYY, MM, DD, hh, mm, ss, -1, -1, -1) # Assume it's local time
        return int(time.mktime(t))
    except:
        return 0


"""get_msFromDateFormat1(s)
   s format:  YYYYMMDD
   e.g.:     '20170127'
   returns ms since epoch (local time)
"""
def get_msFromDateFormat1(s):
    try:
        assert len(s) == 8
        #s = s.upper()
        YYYY = int(s[0:4])
        MM = int(s[4:6])
        DD = int(s[6:8])

        #    YYYY, MM, DD = map(int, [s[0:4], s[4:6], s[6:8]])
        t = (YYYY, MM, DD, 0, 0, 0, -1, -1, -1) # Assume it's local time
        return int(time.mktime(t))
    except:
        return 0


#TrendMicro Worry Free Business Security (WFBS) 2016, ESP3.7
def  trend_micro_wfbs9(table, rootkey):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    foldername = ""
    try:
        updatekey = _winreg.OpenKey(rootkey, r"SOFTWARE\Wow6432Node\TrendMicro\PC-cillinNTCorp\CurrentVersion\Misc.")
    except OSError as err:
        return
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() == 'tmlisten_ver':
                productversion = val
            elif nm.lower() == 'productname':
                productname = val
            elif nm.lower() == 'virusschedulescandatetime':
                lastused = get_msFromDateTimeFormat4(val)
            elif nm.lower() == 'lastupdatetime':
                defdate = val
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    #print "Product name: ", productname
    #print "Agent ver:    ", productversion
    #print "Def date:     ", time.ctime(defdate)
    #print "Last used:    ", time.ctime(lastused)

    #Get the definition version
    try:
        f = open("C:\\Program Files (x86)\\Trend Micro\\Security Agent\\Misc\\scan_operation.csv", "r")
        try:
            line = f.readline()
            l1 = 1
            while l1:
                temp = f.readline()
                if temp != '':
                    line = temp
                else:
                    l1 = 0

            part = line.split(",")
            defversion = part[12]
        finally:
            f.close()
    except OSError as err:
        #_winreg.CloseKey(updatekey)
        return

    #print "Def ver:      ", defversion
    table.add((productname, productversion, defversion, defdate, lastused))


def trend_micro_client(table, rootkey, prname):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    foldername = ""
    try:
        rootkey1 = _winreg.OpenKey(rootkey,
            r"Software\Microsoft\Windows\CurrentVersion\Uninstall")
    except OSError as err:
        return
    try:
        productname, productversion = walkregkey(table, rootkey1,
            r"Software\Microsoft\Windows\CurrentVersion\Uninstall",
            prname)
        if productname != "":
            try:
                rootkey2 = _winreg.OpenKey(rootkey, r"Software\TrendMicro\PC-cillinNTCorp\CurrentVersion")
            except OSError as err:
                return
            try:
                nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(rootkey2)
                for i in range(nvalues):
                    nm, val, typ = _winreg.EnumValue(rootkey2, i)
                    if nm.lower() == "application path":
                        foldername = os.path.join(val, "report")
            finally:
                _winreg.CloseKey(rootkey2)
            #print foldername
            try:
                rootkey2 = _winreg.OpenKey(rootkey, r"Software\TrendMicro\PC-cillinNTCorp\CurrentVersion\Misc.")
            except OSError as err:
                return
            try:
                nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(rootkey2)
                for i in range(nvalues):
                    nm, val, typ = _winreg.EnumValue(rootkey2, i)
                    if nm.lower() == "internalpatternver":
                        intpatternVer = str(val)
                    if nm.lower() == "patternver":
                        patternVer = str(val)
                    if nm.lower() == "patterndate":
                        patternDate = val
                    if nm.lower() == "programver":
                        productversion = val
            finally:
                _winreg.CloseKey(rootkey2)
            #print intpatternVer, patternVer, patternDate, productversion
            try:
                defdate = get_msFromDateFormat1(patternDate)
            except:
                logger.exception(patternDate)
            try:
                i = intpatternVer.find(patternVer)
                if i >= 0:
                    j = i + len(patternVer)
                    defversion = intpatternVer[0:i] + "." + patternVer + "." + intpatternVer[j:]
                else:
                    defversion = intpatternVer
            except:
                logger.exception(patternVer + " " + intpatternVer)

            #print "defversion", defversion
            try:
                lastFile = ""
                filelist = os.listdir(foldername)
                for aFile in filelist:
                    if not aFile.endswith("log"):
                        continue
                    filepath = os.path.join(foldername, aFile)
                    #print filepath
                    if os.path.isfile(filepath):
                        try:
                            f = open(filepath, "r")
                            try:
                                bytes = f.read()
                                i = bytes.find("Complete time : ")
                                if i >= 0:
                                    newdate = get_msFromFileDate(filepath)
                                    if lastused < newdate:
                                        lastused = newdate
                                        lastFile = filepath
                            finally:
                                f.close()
                        except:
                            continue
                try:
                    f = open(lastFile, "r")
                    try:
                        line = f.readline()
                        while line:
                            i = line.find("Complete time : ")
                            if i >= 0:
                                try:
                                    lastused = get_msFromDateTimeFormat1(line[20:40])
                                except:
                                    logger.exception(line)
                            line = f.readline()
                    finally:
                        f.close()
                except:
                    pass

            except:
                pass

            table.add((productname, productversion, defversion, defdate, lastused))
    finally:
        _winreg.CloseKey(rootkey1)


"""
def fixtime(s):
    # Input is of the form YYYYMMDD
    assert len(s) == 8
    YYYY, MM, DD = map(int, [s[0:4], s[4:6], s[6:8]])
##    if MM in hmonthmap:
##        s = str(DD) + " " + hmonthmap[MM] + " " + str(YYYY)
##    else:
##        s = str(DD) + "-" + str(MM) + "-" + str(YYYY)
##
##    return s

    t = (YYYY, MM, DD, 0, 0, 0, -1, -1, -1) # Assume it's local time
    return int(time.mktime(t))
"""


# ESP3.7 Symantec SEP 12.1.7061.6600 2016
def symantec_SEP12_1_6(table,rootkey):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    try:
        #registry for SEP12.1.6
        updatekey = _winreg.OpenKey(rootkey, r"SOFTWARE\Symantec\Symantec Endpoint Protection\CurrentVersion")
    except OSError as err:
        return
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() == 'productversion':
                productversion = val
            elif nm.lower() == 'productname':
                productname = val
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    #print "Product name: ", productname
    #print "Product ver:  ", productversion
    try:
        #registry for SEP12.1.6 AV
        updatekey = _winreg.OpenKey(rootkey, r"SOFTWARE\Symantec\Symantec Endpoint Protection\CurrentVersion\public-opstate")
    except OSError as err:
        return
    #second set of values
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() == 'lastsuccessfulscandatetime':
                lastused = val
            elif nm.lower() == 'latestvirusdefsdate':
                defdate = val
            elif nm.lower() == 'latestvirusdefsrevision':
                defversion = val
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    defdate = get_msFromDateFormat2(defdate)
    lastused = get_msFromUTC(lastused)
    #print "Def ver:      ", defversion
    #print "Def date:     ", time.ctime(defdate)
    #print "Last used:    ", time.ctime(lastused)
    table.add((productname, productversion, defversion, defdate, lastused))

def symantec_av200x(table, rootkey, prname):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    try:
        rootkey1 = _winreg.OpenKey(rootkey,
            r"Software\Microsoft\Windows\CurrentVersion\Uninstall")
    except OSError as err:
        return
    try:
        productname, productversion = walkregkey(table, rootkey1,
            r"Software\Microsoft\Windows\CurrentVersion\Uninstall",
            prname)
        if productname != "":
            try:
                rootkey2 = _winreg.OpenKey(rootkey, r"Software\Symantec\InstalledApps")
            except OSError as err:
                return
            foldername = ""
            lastusedpath = ""
            try:
                nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(rootkey2)
                for i in range(nvalues):
                    nm, val, typ = _winreg.EnumValue(rootkey2, i)
                    if nm.lower() == "avengedefs":
                        foldername = val
                    if nm.lower() == "navnt":
                        lastusedpath = val
            finally:
                _winreg.CloseKey(rootkey2)

            # get definitions date and version
            path = os.path.join(foldername, "definfo.dat")
            try:
                f = open(path, "r")
                try:
                    bytes = f.read()
                    i = bytes.lower().find("curdefs=")
                    if i >= 0:
                        j = bytes.lower().find(".", i + 8)
                        if j >= 0:
                            try:
                                defdate = get_msFromDateFormat1(bytes[i + 8:j])
                            except:
                                logger.exception(bytes[i:j])
                            k = bytes.lower().find("\n", j + 1)
                            if k >= 0:
                                defversion = bytes[j + 1:k]
                finally:
                    f.close()
            except:
                pass

            # get last scan date
            try:
                rootkey3 = _winreg.OpenKey(rootkey, r"Software\Symantec\Norton AntiVirus\LastScan")
                try:
                    nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(rootkey3)
                    for i in range(nvalues):
                        nm, val, typ = _winreg.EnumValue(rootkey3, i)
                        if nm.lower() == "systemtime":
                            if len(val) == 16:
                                try:
                                    year = ord(val[0]) + ord(val[1]) * 0x100
                                    month = ord(val[2]) + ord(val[3]) * 0x100
                                    day = ord(val[6]) + ord(val[7]) * 0x100
                                    hour = ord(val[8]) + ord(val[9]) * 0x100
                                    minute = ord(val[10]) + ord(val[11]) * 0x100
                                    sec = ord(val[12]) + ord(val[13]) * 0x100
                                    t = (year, month, day, hour, minute, sec, 0, 1, -1) # Assume it's local time
                                    lastused = time.mktime(t)
                                    ##print time.ctime(lastused)
                                except:
                                    logger.exception(val)
                            break
                finally:
                    _winreg.CloseKey(rootkey3)
            except OSError as err:
                pass

            """
            try:
                lastusedlist = []
                path = os.path.join(lastusedpath, "avapp.log")
                lastused1 = getFileDate(path)
                if lastused1 > 0:
                    lastusedlist.append(lastused1)
                path = os.path.join(lastusedpath, "averror.log")
                lastused2 = getFileDate(path)
                if lastused2 > 0:
                    lastusedlist.append(lastused2)
                path = os.path.join(lastusedpath, "avvirus.log")
                lastused3 = getFileDate(path)
                if lastused3 > 0:
                    lastusedlist.append(lastused3)
                lastused = min(lastusedlist)
            except:
                pass
            """
            table.add((productname, productversion, defversion, defdate, lastused))
    finally:
        _winreg.CloseKey(rootkey1)

def symantec_av9(table, rootkey):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    try:
        rootkey1 = _winreg.OpenKey(rootkey,
            r"Software\Microsoft\Windows\CurrentVersion\Uninstall")
    except OSError as err:
        return
    try:
        productname, productversion = walkregkey(table, rootkey1,
            r"Software\Microsoft\Windows\CurrentVersion\Uninstall",
            "symantec antivirus")
        if productname != "":
            # alternate way is to open key Software\Symantec\Shareddefs\Microdefs
            # read value data BinHubStamp
            try:
                rootkey2 = _winreg.OpenKey(rootkey, r"Software\Symantec\InstalledApps")
            except OSError as err:
                return
            foldername = ""
            lastusedpath = ""
            try:
                nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(rootkey2)
                for i in range(nvalues):
                    nm, val, typ = _winreg.EnumValue(rootkey2, i)
                    if nm.lower() == "avengedefs":
                        foldername = val
                    if nm.lower() == "common client data":
                        lastusedpath = os.path.join(val, "..\\Symantec AntiVirus Corporate Edition\\7.5\\logs")
            finally:
                _winreg.CloseKey(rootkey2)
            path = os.path.join(foldername, "definfo.dat")
            try:
                f = open(path, "r")
                try:
                    bytes = f.read()
                    i = bytes.lower().find("curdefs=")
                    if i >= 0:
                        j = bytes.lower().find(".", i + 8)
                        if j >= 0:
                            try:
                                defdate = get_msFromDateFormat1(bytes[i + 8:j])
                            except:
                                logger.exception(bytes[i:j])
                            k = bytes.lower().find("\n", j + 1)
                            if k >= 0:
                                defversion = bytes[j + 1:k]
                finally:
                    f.close()
            except:
                pass
            try:
                lastFile = ""
                filelist = os.listdir(lastusedpath)
                for aFile in filelist:
                    filepath = os.path.join(lastusedpath, aFile)
                    if os.path.isfile(filepath):
                        try:
                            f = open(filepath, "r")
                            try:
                                bytes = f.read()
                                i = bytes.find("Scan Complete:")
                                if i >= 0:
                                    newdate = get_msFromFileDate(filepath)
                                    if lastused < newdate:
                                        lastused = newdate
                                        lastFile = filepath
                            finally:
                                f.close()
                        except:
                            continue
                try:
                    f = open(lastFile, "r")
                    try:
                        line = f.readline()
                        while line:
                            i = line.find("Scan Complete:")
                            if i >= 0:
                                try:
                                    lastused = get_msFromDateTimeFormat3(line[0:12])
                                except:
                                    logger.exception(line)
                            line = f.readline()
                    finally:
                        f.close()
                except:
                    pass

            except:
                pass
            ##if lastused > 0: print time.ctime(lastused)
            table.add((productname, productversion, defversion, defdate, lastused))
    finally:
        _winreg.CloseKey(rootkey1)

def symantec_av81(table, rootkey):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    try:
        rootkey1 = _winreg.OpenKey(rootkey,
            r"Software\Microsoft\Windows\CurrentVersion\Uninstall")
    except OSError as err:
        return
    try:
        productname, productversion = walkregkey(table, rootkey1,
            r"Software\Microsoft\Windows\CurrentVersion\Uninstall",
            "symantec antivirus client")
        if productname != "":
            # alternate way is to open key Software\Symantec\Shareddefs\Microdefs
            # read value data BinHubStamp
            try:
                rootkey2 = _winreg.OpenKey(rootkey, r"Software\Symantec\InstalledApps")
            except OSError as err:
                return
            foldername = ""
            #lastusedpath = ""
            try:
                nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(rootkey2)
                for i in range(nvalues):
                    nm, val, typ = _winreg.EnumValue(rootkey2, i)
                    if nm.lower() == "avengedefs":
                        foldername = val
                    #if nm.lower() == "navnt":
                    #    lastusedpath = os.path.join(val, "vpdebug.log")
            finally:
                _winreg.CloseKey(rootkey2)
            path = os.path.join(foldername, "definfo.dat")
            try:
                f = open(path, "r")
                try:
                    bytes = f.read()
                    i = bytes.lower().find("curdefs=")
                    if i >= 0:
                        j = bytes.lower().find(".", i + 8)
                        if j >= 0:
                            try:
                                defdate = get_msFromDateFormat1(bytes[i + 8:j])
                            except:
                                logger.exception(bytes[i:j])
                            k = bytes.lower().find("\n", j + 1)
                            if k >= 0:
                                defversion = bytes[j + 1:k]
                finally:
                    f.close()
            except:
                pass
            try:
                """
                services = GetObject("WinMgMts://./root/cimv2").InstancesOf("Win32_Service")
                for service in services:
                    name = service.Properties_("Name").Value
                    if (name.lower() == "norton antivirus server"):
                        state = service.Properties_("State").Value
                        if state.lower() == "running":
                            lastused = int(time.time())
                        break
                if lastused == 0:
                    lastused = getFileDate(lastusedpath)
                """
                env = os.getenv("AllUsersProfile")
                if env is not None:
                    lastusedpath = os.path.join(env, "Application Data\\Symantec\\Norton AntiVirus Corporate Edition\\7.5\\logs")
                    filelist = os.listdir(lastusedpath)
                    lastFile = ""
                    for aFile in filelist:
                        filepath = os.path.join(lastusedpath, aFile)
                        if os.path.isfile(filepath):
                            try:
                                f = open(filepath, "r")
                                try:
                                    bytes = f.read()
                                    i = bytes.find("Scan Complete:")
                                    if i >= 0:
                                        newdate = get_msFromFileDate(filepath)
                                        if lastused < newdate:
                                            lastused = newdate
                                            lastFile = filepath
                                finally:
                                    f.close()
                            except:
                                continue
                    try:
                        f = open(lastFile, "r")
                        try:
                            line = f.readline()
                            while line:
                                i = line.find("Scan Complete:")
                                if i >= 0:
                                    try:
                                        lastused = get_msFromDateTimeFormat3(line[0:12])
                                    except:
                                        logger.exception(line)
                                line = f.readline()
                        finally:
                            f.close()
                    except:
                        pass
            except:
                pass
            #if lastused > 0: print time.ctime(lastused)
            table.add((productname, productversion, defversion, defdate, lastused))
    finally:
        _winreg.CloseKey(rootkey1)


""" get_msFromDateTimeFormat3(s)
    s format: 'YYMMDDhhmmss'
    e.g.:     '170127182422'
    returns ms since epoch (local time)
"""
def get_msFromDateTimeFormat3(s):
    assert len(s) == 12
    #s = s.upper()
    for aChar in s:
        if aChar not in string.hexdigits:
            return 0
    YYYY = hexmap.get(s[0:1]) * 16 + hexmap.get(s[1:2]) + 1970
    MM = hexmap.get(s[2:3]) * 16 + hexmap.get(s[3:4]) + 1
    DD = hexmap.get(s[4:5]) * 16 + hexmap.get(s[5:6])
    hh = hexmap.get(s[6:7]) * 16 + hexmap.get(s[7:8])
    mm = hexmap.get(s[8:9]) * 16 + hexmap.get(s[9:10])
    ss = hexmap.get(s[10:11]) * 16 + hexmap.get(s[11:12])
    t = (YYYY, MM, DD, hh, mm, ss, -1, -1, -1) # Assume it's local time
    return int(time.mktime(t))

def walkregkey(table, key, path, prname):
    nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(key)
    for i in range(nsubkeys):
        try:
            subkeyname = _winreg.EnumKey(key, i)
        except OSError as err:
            continue # Probably "More data is available" ?!?!?!
        subkeypath = path + "\\" + subkeyname
        try:
            subkey = _winreg.OpenKey(key, subkeyname)
        except OSError as err:
            ##print >>sys.stderr, path, subkeyname, err
            continue # Probably access denied
        try:
            productname, productversion = walkregval(table, subkey, prname)
            if productname != "":
                return (productname, productversion)
        finally:
            _winreg.CloseKey(subkey)
    return ("", "")



def walkregval(table, key, prname):
    nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(key)
    productname = ""
    productversion = ""
    for i in range(nvalues):
        nm, val, typ = _winreg.EnumValue(key, i)
        if isinstance(nm, str):
            nm = nm.encode("utf8")
        if isinstance(val, str):
            val = val.encode("utf8")
        if nm.lower() == "displayname":
            if val.lower() == prname:
                productname = val
        if nm.lower() == "displayversion":
            productversion = val
    return (productname, productversion)


""" get_msFromDateFormat3(s)
    defdate format: 'March 13 2006', or
                    '14 March 2006', or
                    '2017-01-27'
    returns ms since epoch (local time)
"""
def get_msFromDateFormat3(s):
    # date format: March 13 2006, 14 March 2006
    try:
        monthName, day, year = s.split()
        if monthName not in inv_hmonthmap:
            day, monthName, year = s.split()
            if monthName not in inv_hmonthmap:
                return 0
        MM = inv_hmonthmap.get(monthName)
        DD = int(day)
        YYYY = int(year)
        t = (YYYY, MM, DD, 0, 0, 0, -1, -1, -1) # Assume it's local time
        return int(time.mktime(t))
    except:
        return get_msFromDateFormat2(s)

        """
            try:
                # format 2007/06/23
                year, month, day = defdate.split('/')
                YYYY = int(year)
                MM = int(month)
                DD = int (day)
                if MM < 1 or MM > 12 or DD < 1 or DD > 31:
                    return 0
                t = (YYYY, MM, DD, 0, 0, 0, -1, -1, -1) # Assume it's local time
                return int(time.mktime(t))
            except:
                return 0
        """

"""utc2local(s)
   s format:  'YYYY-MM-DDThh:mm:ssZ (Zulu time)
   e.g.:      '2016-11-27T00:23:52Z, or
              '2016-11-27 00:23:52Z
   returns ms since epoch  (transforms UTC to local time)
"""
def get_msFromUTC(s):
    try:
        YYYY=int(s[0:4])
        MM=int(s[5:7])
        DD=int(s[8:10])
        h=int(s[11:13])
        m=int(s[14:16])
        s=int(s[17:19])

        d=datetime.datetime(YYYY,MM,DD,h,m,s)
        #print d.ctime()
        t1=time.gmtime()
        t2=time.localtime()
        offset=t1.tm_hour - t2.tm_hour
        h1=(h-offset)%24
        if offset > h:
            DD = DD-1

        if MM < 1 or MM > 12 or DD < 1 or DD > 31:
            return 0
        t = (YYYY, MM, DD, h1, m, s, -1, -1, -1)

        return int(time.mktime(t))    #Convert a time tuple in local time to seconds since the Epoch

    except:
        return 0


"""get_msFromDateTimeFormat2(s)
   s format:   '?M/DD/YYYY h:mm PM'
   e.g.:        ' 2/26/2017 2:33 PM'
   returns ms since epoch (local time)
"""
def get_msFromDateTimeFormat2(s):
    pm = False
    if s.find('PM') != -1:
        d = s.split('PM')[0].strip()
        pm = True
    elif s.find('AM') != -1:
        d = s.split('AM')[0].strip()
    else:
        d=s


    try:
        date, t1 = d.split(' ')

        if date.find('/') != -1:
            #format: '2/26/2017 2:33 PM'
            month, day, year = date.split('/')
        elif date.find('-') != -1:
            #format: '2-26-2017 2:33 PM'
            month, day, year = date.split('-')

        h, m = t1.split(':')
        secs = 0

        if pm == True:
            h = int(h) + 12

        YYYY = int(year)
        MM = int(month)
        DD = int (day)
        hh = int(h)
        mm = int(m)
        ss = int(secs)
        if MM < 1 or MM > 12 or DD < 1 or DD > 31:
            return 0
        t = (YYYY, MM, DD, hh, mm, ss, -1, -1, -1) # Assume it's local time

        #print datetime.datetime.fromtimestamp(time.mktime(t))
        #print d.ctime()
        return int(time.mktime(t))

    except:
        return 0


""" get_msFromDateTimeFormat4(s)
    s format: '2016/12/05 15:46:19', or
              '2016-12-05 15:46:19'
    return ms since epoch (local time)
"""
def get_msFromDateTimeFormat4(s):
    try:
        date, t1 = s.split(' ')
        h, m, s = t1.split(':')

        YYYY = int(date[0:4])
        MM = int(date[5:7])
        DD = int(date[8:10])
        hh = int(h)
        mm = int(m)
        ss = int(s)
        if MM < 1 or MM > 12 or DD < 1 or DD > 31:
            return 0
        t = (YYYY, MM, DD, hh, mm, ss, -1, -1, -1) # Assume it's local time
        return int(time.mktime(t))
        #return d.ctime()
    except:
        return 0


"""get_msFromDateFormat2(s)
   d format: 'YYYY-MM-DD', 'YYYY/MM/DD'
   e.g:      '2017-01-27', '2017/01/27
   returns ms since epoch (local time)
"""
def get_msFromDateFormat2(s):
    try:
        assert len(s) == 10
        YYYY = int(s[0:4])
        MM = int(s[5:7])
        DD = int(s[8:10])
        """
        year, month, day = d.split('-')
        #d=datetime.datetime(int(yr),int(mo),int(day))
        YYYY = int(year)
        MM = int(month)
        DD = int (day)
        """

        if MM < 1 or MM > 12 or DD < 1 or DD > 31:
            return 0
        t = (YYYY, MM, DD, 0, 0, 0, -1, -1, -1) # Assume it's local time
        return int(time.mktime(t))
        #return d.ctime()
    except:
        return 0


def na_av(table, rootkey):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    try:
        #registry for Network Associates AntiVirus app
        updatekey = _winreg.OpenKey(rootkey, r"Software\Network Associates\TVD\VirusScan Enterprise\CurrentVersion")
    except OSError as err:
        return
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() == 'szproductver':
                productversion = val
            elif nm.lower() == 'product':
                productname = val
            elif nm.lower() == 'szvirdefver':
                # format 4718, 4.0.4716
                defversion = val
            elif nm.lower() == 'szvirdefdate':
                try:
                    # format March 13 2006, 14 March 2006
                    defdate = get_msFromDateFormat3(val)
                except:
                    logger.exception(val)
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    try:
        #registry for the path of Network Associates AntiVirus log files
        updatekey = _winreg.OpenKey(rootkey, r"Software\Network Associates\TVD\Shared Components\Framework")
        try:
            nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
            for i in range(nvalues):
                nm, val, typ = _winreg.EnumValue(updatekey, i)
                if nm.lower() == 'data path':
                    dbpath = os.path.join(val, "db")
                    try:
                        file_list = os.listdir(dbpath)
                        for afile in file_list:
                            if afile.lower().startswith("agent_") and afile.lower().endswith(".xml"):
                                path = os.path.join(dbpath, afile)
                                newdate = get_msFromFileDate(path)
                                if lastused < newdate:
                                    lastused = newdate
                                ##print time.ctime(lastused)
                    except:
                        pass
                    break
        except OSError as err:
            pass
        _winreg.CloseKey(updatekey)
    except OSError as err:
        pass
    table.add((productname, productversion, defversion, defdate, lastused))

#McAfee(Network Associates) 2006  Enterprise Version 8.5i  (ESP3.5)
#McAfee(Network Associates) 200x? Enterprise Version 8.7i  (ESP3.5)
#McAfee(Intel)              2016  Enterprise Version 8.8   (ESP3.7)
def na_av85(table, rootkey):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    try:
        #registry for the AntiVirus app
        updatekey = _winreg.OpenKey(rootkey, r"Software\McAfee\DesktopProtection")
    except OSError as err:
        return
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() == 'szproductver':
                productversion = val
            elif nm.lower() == 'product':
                productname = val
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    #print productname, productversion
    try:
        #registry for Network Associates AntiVirus app
        updatekey = _winreg.OpenKey(rootkey, r"Software\McAfee\AVEngine")
    except OSError as err:
        return
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() == 'avdatversion':
                # format 4718, 4.0.4716
                defversion = val
            elif nm.lower() == 'avdatdate':
                try:
                    # format 2007/06/23, March 13 2006, 14 March 2006
                    defdate = get_msFromDateFormat3(val)
                except:
                    logger.exception(val)
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    #print defdate, defversion
    try:
        # path of Network Associates AntiVirus log files
        env = os.getenv("DEFLOGDIR")
        dbpath = os.path.join(env, "..", "Common Framework", "db")
        #print dbpath
        try:
            file_list = os.listdir(dbpath)
            for afile in file_list:
                if afile.lower().startswith("agent_") and afile.lower().endswith(".xml"):
                    #print afile
                    path = os.path.join(dbpath, afile)
                    newdate = get_msFromFileDate(path)
                    if lastused < newdate:
                        lastused = newdate
            #print time.ctime(lastused)
        except:
            pass
    except OSError as err:
        pass
    table.add((productname, productversion, defversion, defdate, lastused))


    r"""
def mcafee_virusscan_enterprise(table,rootkey):
    productversion = ""
    productname = ""
    defversion = ""
    defdate = 0
    lastused = 0
    try:
        #registry for McAfee(Intel) AntiVirus app
        updatekey = _winreg.OpenKey(rootkey, r"SOFTWARE\McAfee\DesktopProtection")
    except OSError as err:
        return
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() == 'szproductver':
                productversion = val
            elif nm.lower() == 'product':
                productname = val
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    print productname, productversion
    try:
        #registry for McAfee (Intel) AV app
        updatekey = _winreg.OpenKey(rootkey, r"Software\McAfee\AVEngine")
    except OSError as err:
        return
    try:
        nsubkeys, nvalues, mtime = _winreg.QueryInfoKey(updatekey)
        for i in range(nvalues):
            nm, val, typ = _winreg.EnumValue(updatekey, i)
            if nm.lower() == 'avdatversion':
                # format 4718, 4.0.4716
                defversion = val
            elif nm.lower() == 'avdatdate':
                try:
                    # format 2007/06/23, March 13 2006, 14 March 2006
                    defdate = convertDate(val)
                except:
                    logger.exception(val)
        _winreg.CloseKey(updatekey)
    except OSError as err:
        _winreg.CloseKey(updatekey)
        return
    #print defdate, defversion
    try:
        # path of Network Associates AntiVirus log files
        env = os.getenv("DEFLOGDIR")
        dbpath = os.path.join(env, "..", "Common Framework", "db")
        #print dbpath
        try:
            file_list = os.listdir(dbpath)
            for afile in file_list:
                if afile.lower().startswith("agent_") and afile.lower().endswith(".xml"):
                    print afile
                    path = os.path.join(dbpath, afile)
                    newdate = getFileDate(path)
                    if lastused < newdate:
                        lastused = newdate
            print time.ctime(lastused)
        except:
            pass
    except OSError as err:
        pass
    table.add((productname, productversion, defversion, defdate, lastused))
"""


"""get_msFromFileDate(filename)
   filename - 'C:\\PathToSomeFile'
   returns ms since epoch (local time)
"""
def get_msFromFileDate(filename):
    if os.path.exists(filename):
        stats = os.stat(filename)
        return stats[stat.ST_MTIME]
    else:
        return 0

def _test():
    from sigma.gather import gathering
    gathering.test(Gather)

if __name__ == "__main__":
    _test()
