Предисловие
Все началось с того, что купленное корпоративное решение для аудита систем не предоставляло нужную информацию по используемому антивирусному продукту, да и работала слишком уж долго, а панель управления, используемого компанией антивируса, оставляет желать лучшего. Решено было использовать «костыль» для сбора информации об антивирусах в домене.
Приведенный ниже сценарий обрабатывает статусы состояний только антивирусов, используемых в нашей корпоративной сети.
Что же собственно нужно и как это сделать?
Первым делом было установлено, что же нужно получать от антивируса и как это сделать в короткий промежуток времени со всего домена.
Получать нужно было:
1) Наименование установленного антивируса
2) Активен ли антивирус
3) Обновлены ли на нем базы
Собственно способ был найден моментально — использовать WMI (Windows Management Instrumentation).
Пришлось погрузиться в изучение самой структуры WMI на ПК, в этом мне помог замечательный набор утилит WMI Tools.
После чего нужно было решить как взаимодействовать с WMI, не долго думая решил написать сценарий на VBScript.
Разбор полетов
Сценарий обрабатывает 3500 хостов примерно за 1 час. Собственно сам код сценария:
WScript.Interactive = true
compid = 0
On Error Resume Next
Set objDomain = GetObject("LDAP://DOMAIN/OU=Workstations,DC=DOMAIN")
comps = Array()
for each objDomainItem in objDomain
if objDomainItem.objectClass = "Computer" then
idxLast = UBound (comps)
ReDim Preserve comps(idxLast + 1)
comps(idxLast + 1) = objDomainItem.dNSHostName
end if
next
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objNewFile = objFS.CreateTextFile("C:TEMPAV_CheckReportsAV_Status_Scan_is_Running.WAIT")
a = "<style>"
a = a& "@import ""TableFilter/filtergrid.css"";"
a = a& "BODY{background-color:Lavender ;}"
a = a& "TABLE{font-size: 10pt; font-family: arial;}"
a = a& "TH{background-color: buttonface; }"
a = a& "</style>"
objNewFile.WriteLine "<html>"
objNewFile.WriteLine "<head>"
objNewFile.WriteLine "<script language=""javascript"" type=""text/javascript"" src=""TableFilter/tablefilter.js""></script>"
objNewFile.WriteLine "<title>AntiVirus status information</title>"
objNewFile.WriteLine a & "</head><body>"
objNewFile.WriteLine "<h2>AVSI -- Date: " & Now() & "</h2>"
objNewFile.WriteLine "<script type=""text/javascript"">"
objNewFile.WriteLine "var tableToExcel = (function() {"
objNewFile.WriteLine " var uri = 'data:application/vnd.ms-excel;base64,'"
objNewFile.WriteLine " , template = '<html xmlns:o=""urn:schemas-microsoft-com:office:office"" xmlns:x=""urn:schemas-microsoft-com:office:excel""><head></head><body><table>{table}</table></body></html>'"
objNewFile.WriteLine " , base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) }"
objNewFile.WriteLine " , format = function(s, c) { return s.replace(/{(w+)}/g, function(m, p) { return c[p]; }) }"
objNewFile.WriteLine "return function(table, name) {"
objNewFile.WriteLine " if (!table.nodeType) table = document.getElementById(table)"
objNewFile.WriteLine " var ctx = {worksheet: name || 'Worksheet', table: table.innerHTML}"
objNewFile.WriteLine " window.location.href = uri + base64(format(template, ctx))"
objNewFile.WriteLine "}"
objNewFile.WriteLine "})()"
objNewFile.WriteLine "</script>"
objNewFile.WriteLine "<input type=""button"" onclick=""tableToExcel('table1', 'Export data')"" value=""Export data to Excel"">"
objNewFile.WriteLine "<table id=""table1"" BORDER=""1"" width=""100%"">"
objNewFile.WriteLine "<tr><th width=""2%"">id</th><th>Computer</th><th>AV Name</th><th>AV Status</th><th>AV Bases</th><th>Host Status</th></tr>"
for each comp in comps
compid = compid + 1
Set WshShell = WScript.CreateObject("WScript.Shell")
Ping = WshShell.Run("ping -n 1 " & comp, 0, True)
Select Case Ping
Case 0
On Error Resume next
Set oWMI = GetObject("winmgmts:\" & comp & "rootSecurityCenter2")
On Error Resume next
Set colAVItems = oWMI.ExecQuery("Select * from AntiVirusProduct")
If colAVItems.count = 0 Then
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th><font color=""red"">No AntiViruses found</font></th><th><font color=""red"">Disabled</font></th><th><font color=""red"">NOT Up to Date</font></th><th><font color=""green"">Online</font></th></tr>"
ElseIf colAVItems.count = 1 Then
For Each AntiVirus in colAVItems
If (AntiVirus.displayName) <> "Windows Defender" Then
AVStatus = hex(AntiVirus.ProductState)
If (AVStatus = "61000") _
OR (AVStatus = "51000") _
OR (AVStatus = "41000") Then
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th>" & AntiVirus.displayName & "</th><th><font color=""green"">Active</font></th><th><font color=""green"">Up to Date</font></th><th><font color=""green"">Online</font></th></tr>"
ElseIf (AVStatus = "41010") _
OR (AVStatus = "61010") _
OR (AVStatus = "51010") Then
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th>" & AntiVirus.displayName & "</th><th><font color=""green"">Active</font></th><th><font color=""red"">NOT Up to Date</font></th><th><font color=""green"">Online</font></th></tr>"
ElseIf (AVStatus = "60000") _
OR (AVStatus = "50000") _
OR (AVStatus = "40000") Then
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th>" & AntiVirus.displayName & "</th><th><font color=""red"">On Access scanning disabled!</font></th><th><font color=""green"">Up to Date</font></th><th><font color=""green"">Online</font></th></tr>"
Else
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th>" & AntiVirus.displayName & "</th><th><font color=""purple"">Unknown</font></th><th><font color=""purple"">Unknown</font></th><th><font color=""green"">Online</font></th></tr>"
End if
End If
Next
End If
Case 1
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th><font color=""purple"">Unknown</font></th><th><font color=""purple"">Unknown</font></th><th><font color=""purple"">Unknown</font></th><th><font color=""red"">Offline</font></th></tr>"
End Select
Next
objNewFile.WriteLine "</table>"
objNewFile.WriteLine "<script language=""javascript"" type=""text/javascript"">"
objNewFile.WriteLine "//<![CDATA["
objNewFile.WriteLine "var tableFilters = {"
objNewFile.WriteLine " sort_select: true,"
objNewFile.WriteLine " loader: true,"
objNewFile.WriteLine " rows_counter: true,"
objNewFile.WriteLine " col_2: ""select"","
objNewFile.WriteLine " col_3: ""select"","
objNewFile.WriteLine " col_4: ""select"","
objNewFile.WriteLine " col_5: ""select"","
objNewFile.WriteLine " on_change: true,"
objNewFile.WriteLine " display_all_text: "" [ Show all ] "","
objNewFile.WriteLine " alternate_rows: true,"
objNewFile.WriteLine "}"
objNewFile.WriteLine "setFilterGrid( ""table1"",tableFilters);"
objNewFile.WriteLine "//]]>"
objNewFile.WriteLine "</script>"
objNewFile.WriteLine "<h2>End of scan: " & Now() & "</h2>"
objNewFile.WriteLine "</body>"
objNewFile.WriteLine "</html>"
objNewFile.Close
objFS.MoveFile "C:TEMPAV_CheckReportsAV_Status_Scan_is_Running.WAIT", "C:TEMPAV_CheckReportsAV_Status_" & Date & "_" & Hour(Now()) & "." & Minute(Now()) & ".htm"
Что же собственно происходит? А происходит именно вот что:
1) Мы подключаемся к домену и заходим в каталог Workstations:
Set objDomain = GetObject("LDAP://DOMAIN/OU=Workstations,DC=DOMAIN")
2) Создаем пустой массив, в который будут добавляться ПК пользователей с соответствующим классом Computer:
for each objDomainItem in objDomain
if objDomainItem.objectClass = "Computer" then
idxLast = UBound (comps)
ReDim Preserve comps(idxLast + 1)
comps(idxLast + 1) = objDomainItem.dNSHostName
end if
next
3) Создаем файл отчета. Отчет было решено формировать в .htm формате и соответственно формируем страницу в самом сценарии:
Set objFS = CreateObject("Scripting.FileSystemObject")
Set objNewFile = objFS.CreateTextFile("C:TEMPAV_CheckReportsAV_Status_Scan_is_Running.WAIT")
a = "<style>"
a = a& "@import ""TableFilter/filtergrid.css"";"
a = a& "BODY{background-color:Lavender ;}"
a = a& "TABLE{font-size: 10pt; font-family: arial;}"
a = a& "TH{background-color: buttonface; }"
a = a& "</style>"
objNewFile.WriteLine "<html>"
objNewFile.WriteLine "<head>"
objNewFile.WriteLine "<script language=""javascript"" type=""text/javascript"" src=""TableFilter/tablefilter.js""></script>"
objNewFile.WriteLine "<title>AntiVirus status information</title>"
objNewFile.WriteLine a & "</head><body>"
objNewFile.WriteLine "<h2>AVSI -- Date: " & Now() & "</h2>"
objNewFile.WriteLine "<script type=""text/javascript"">"
objNewFile.WriteLine "var tableToExcel = (function() {"
objNewFile.WriteLine " var uri = 'data:application/vnd.ms-excel;base64,'"
objNewFile.WriteLine " , template = '<html xmlns:o=""urn:schemas-microsoft-com:office:office"" xmlns:x=""urn:schemas-microsoft-com:office:excel""><head></head><body><table>{table}</table></body></html>'"
objNewFile.WriteLine " , base64 = function(s) { return window.btoa(unescape(encodeURIComponent(s))) }"
objNewFile.WriteLine " , format = function(s, c) { return s.replace(/{(w+)}/g, function(m, p) { return c[p]; }) }"
objNewFile.WriteLine "return function(table, name) {"
objNewFile.WriteLine " if (!table.nodeType) table = document.getElementById(table)"
objNewFile.WriteLine " var ctx = {worksheet: name || 'Worksheet', table: table.innerHTML}"
objNewFile.WriteLine " window.location.href = uri + base64(format(template, ctx))"
objNewFile.WriteLine "}"
objNewFile.WriteLine "})()"
objNewFile.WriteLine "</script>"
objNewFile.WriteLine "<input type=""button"" onclick=""tableToExcel('table1', 'Export data')"" value=""Export data to Excel"">"
objNewFile.WriteLine "<table id=""table1"" BORDER=""1"" width=""100%"">"
objNewFile.WriteLine "<tr><th width=""2%"">id</th><th>Computer</th><th>AV Name</th><th>AV Status</th><th>AV Bases</th><th>Host Status</th></tr>"
{ ... }
objNewFile.WriteLine "</table>"
objNewFile.WriteLine "<script language=""javascript"" type=""text/javascript"">"
objNewFile.WriteLine "//<![CDATA["
objNewFile.WriteLine "var tableFilters = {"
objNewFile.WriteLine " sort_select: true,"
objNewFile.WriteLine " loader: true,"
objNewFile.WriteLine " rows_counter: true,"
objNewFile.WriteLine " col_2: ""select"","
objNewFile.WriteLine " col_3: ""select"","
objNewFile.WriteLine " col_4: ""select"","
objNewFile.WriteLine " col_5: ""select"","
objNewFile.WriteLine " on_change: true,"
objNewFile.WriteLine " display_all_text: "" [ Show all ] "","
objNewFile.WriteLine " alternate_rows: true,"
objNewFile.WriteLine "}"
objNewFile.WriteLine "setFilterGrid( ""table1"",tableFilters);"
objNewFile.WriteLine "//]]>"
objNewFile.WriteLine "</script>"
objNewFile.WriteLine "<h2>End of scan: " & Now() & "</h2>"
objNewFile.WriteLine "</body>"
objNewFile.WriteLine "</html>"
objNewFile.Close
Для удобства был использован HTML Table Filter Generator, который формирует фильтры по завершению выполнения сценария по сформированной таблице с полями id, Computer, AV Name, AV Status, AV Bases.
Также добавлена кнопка выгрузки таблицы в Excel, которую я нашел на каком-то форуме (честно сказать с ней я даже не возился и оставил как есть, работает криво и, как показала практика, только под FireFox).
4) Массив у нас сформирован и хранится в памяти, теперь рассмотрим заполнение таблицы отчета. Первым делом мы ставим счетчик для присваивания id каждому хосту из массива. После этого вызываем Shell скрипт для проверки доступности хоста путем отправки на него 1 icmp пакета. Создаем условия на обработку отклика, Case 0 — хост доступен и выполняем сценарий сбора данных для заполнения таблицы, Case 1 — выводим в таблицу данные о том, что хост не доступен:
for each comp in comps
compid = compid + 1
Set WshShell = WScript.CreateObject("WScript.Shell")
Ping = WshShell.Run("ping -n 1 " & comp, 0, True)
Select Case Ping
Case 0
{ ... }
Case 1
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th><font color=""purple"">Unknown</font></th><th><font color=""purple"">Unknown</font></th><th><font color=""purple"">Unknown</font></th><th><font color=""red"">Offline</font></th></tr>"
End Select
Next
5) Проверяем есть ли у нас ошибки, а если есть, то просто переходим к следующему хосту. Информация по Антивирусам, Антишпионам и Межсетевым экранам в WMI хранится в каталоге rootSecurityCenter2, по-этому подключаемся к нему и создаем WQL запрос, который собственно и вытаскивает нужную нам информацию по продукту. Проверяем есть ли вообще на хосте антивирус, а если есть, то проверяем не Windows Defender ли он, а если даже и он, то просто игнорируем его. Каждый антивирус имеет свой код состояния, об этом я узнал на форуме:
On Error Resume next
Set oWMI = GetObject("winmgmts:\" & comp & "rootSecurityCenter2")
On Error Resume next
Set colAVItems = oWMI.ExecQuery("Select * from AntiVirusProduct")
If colAVItems.count = 0 Then
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th><font color=""red"">No AntiViruses found</font></th><th><font color=""red"">Disabled</font></th><th><font color=""red"">NOT Up to Date</font></th><th><font color=""green"">Online</font></th></tr>"
ElseIf colAVItems.count = 1 Then
For Each AntiVirus in colAVItems
If (AntiVirus.displayName) <> "Windows Defender" Then
AVStatus = hex(AntiVirus.ProductState)
If (AVStatus = "61000") _
OR (AVStatus = "51000") _
OR (AVStatus = "41000") Then
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th>" & AntiVirus.displayName & "</th><th><font color=""green"">Active</font></th><th><font color=""green"">Up to Date</font></th><th><font color=""green"">Online</font></th></tr>"
ElseIf (AVStatus = "41010") _
OR (AVStatus = "61010") _
OR (AVStatus = "51010") Then
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th>" & AntiVirus.displayName & "</th><th><font color=""green"">Active</font></th><th><font color=""red"">NOT Up to Date</font></th><th><font color=""green"">Online</font></th></tr>"
ElseIf (AVStatus = "60000") _
OR (AVStatus = "50000") _
OR (AVStatus = "40000") Then
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th>" & AntiVirus.displayName & "</th><th><font color=""red"">On Access scanning disabled!</font></th><th><font color=""green"">Up to Date</font></th><th><font color=""green"">Online</font></th></tr>"
Else
objNewFile.WriteLine "<tr><th>" & compid & "</th><th>" & comp & "</th><th>" & AntiVirus.displayName & "</th><th><font color=""purple"">Unknown</font></th><th><font color=""purple"">Unknown</font></th><th><font color=""green"">Online</font></th></tr>"
End if
End If
Next
End If
Бонус
Ну и как бонус код на Python. Он создает текстовый файл, который можно использовать для выгрузки в Excel (в виде разделителя колонок — табуляция). К сожалению посредством LDAP выгружать список хостов у меня не получилось, а так же при использовании большого списка сценарий падает, возможно кто-нибудь доработает его для своих нужд:
import wmi
import codecs
import os
with open('Comp_list.txt','r') as list:
file = codecs.open('text.txt', 'w', 'utf-8')
file.write("Computer" + " AV Name" + " Host Status" + " AV Status" + " AV Basesn")
for comp in list:
response = os.system("ping -n 1 " + comp)
if response == 0:
path = '//%s/root/SecurityCenter2' % comp
c = wmi.WMI(moniker=path)
wql = "Select * from AntiVirusProduct"
wql = c.query(wql)
if wql == []:
file.write(comp + " no AntiVirus found" + " Online" + " Unknown" + " Unknownn")
else:
for AntiVirus in wql:
ProductState_in_hex = str(hex(AntiVirus.ProductState))
check_install = ProductState_in_hex[0:3]
check_state = ProductState_in_hex[3:5]
check_updates = ProductState_in_hex[5:7]
if check_state == "10" and check_updates == "00":
file.write(comp + " " + AntiVirus.displayName + " Online" + " Active" + " Up to Daten")
elif check_state == "10" and check_updates == "10":
file.write(comp + " " + AntiVirus.displayName + " Online" + " Active" + " NOT Up to Daten")
elif check_state == "00" and check_updates == "00":
file.write(comp + " " + AntiVirus.displayName + " Online" + " On Access scanning disabled!" + " Up to Daten")
elif check_state == "00" and check_updates == "00":
file.write(comp + " " + AntiVirus.displayName + " Online" + " On Access scanning disabled!" + " NOT Up to Daten")
else:
file.write(comp + " " + AntiVirus.displayName + " Online" + " Unknown staten" + " " + check_install)
else:
file.write(comp + " Unknown" + " Offline" + " Unknown" + " Unknownn")
file.close()
Автор: Valsek