Пользователь
0,0
рейтинг
9 декабря 2013 в 18:04

Разработка → Отчёт по событиям на Windows серверах домена

Всем привет!
Удобно получать в почту ежедневный отчёт о событиях на серверах домена за предыдущий день. Можно и за текущий, не суть важно. Когда такие отчёты собираются за длительный промежуток времени (за несколько лет, например), можно найти, кто завёл определённого пользователя, кто пользователя добавил/удалил из группы, кто поменял пользователю пароль (или когда он сам себе поменял), логины на серверы, неудачные логины и так далее. В принципе, каждый сам для себя определяет набор событий для отчётов. Главное принцип.
Нам, например, в почту приходит вот такой отчёт:

Кому нужно, под катом реализация.

Скрипт выполняется каждое утро в 4 часа. Для его работы на сервере надо установить LogParser и 7-ZIP (если файл отчёта больше 3 МБ, то он пакуется zip’ом).
На всякий случай ссылка на полезный документ по событиям 7-ки и 2008 сервера Vista_2008_Security_Event_Descriptions.xlsx.
У меня скрипт лежит на диске C в папке script. В папке script папка Tamplates для шаблонов. Плюс папки на F Logi_ForADReports для временных evt-файлов и Reports для html-файлов отчётов. В папке Reports также создаётся журнал работы скрипта.
Bat-файл запуска скрипта
net use Q: \\nas-srv\BACKUP
cscript //nologo "c:\script\LogParser_bat_4.vbs" %1 %2 %3
net use Q: /delete

Скрипт LogParser_bat_4.vbs
' Ежедневный отчет по событиям на серверах
' Автор Лужин Кирилл
' luzhin.kirill@yandex.ru

'On Error Resume Next

const gsReportFolder = "F:\Reports\"
const gsFrom = "admin1@domain.com"
const gsSubject = "send report"
const gsHelpFile = "c:\script\LogParser_bat.txt"
const gbDebugModeON = false

Dim oLogQuery
Dim oMyInputFormat
Dim oCSVOutputFormat 
Dim strQuery
Dim giErrorCode
Dim gsFileNameLog
Dim gsNormalDate
Dim gsTo
Dim gArrNumberOfFunctions
gArrNumberOfFunctions = Array ("1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")


gsTo = "admin1@domain.com"
gsEMail = "n"
' Это для отчета за сегодня (на всякий случай):
' gsNormalDate = fuNormalizeSystemDate(cStr(Date))
' Это для отчета за вчера (нормальный режим):
gsNormalDate = fuNormalizeSystemDate(cStr(DateAdd("d", -1, Date)))
gsDate = gsNormalDate
gsNumberOfFunctions = "all"


gsCheckDate = DateAdd("d", -1, Date)
gsLogFilename = fuGetFilename(gsCheckDate)


Set objFSO = CreateObject("Scripting.FileSystemObject")
gsFileNameLog = gsReportFolder & gsNormalDate & ".log"
Set objTextFileWriteLog = objFSO.OpenTextFile(gsFileNameLog, 8, True)


' отчет за последние 33 часа:
fuWritedown "* Дата отчета: " & Now, 4
gsPastDate = DateAdd("h", -33, Now) 
fuWritedown "* Отчеты создаются начиная с " & gsPastDate, 4
' отчет за последние 2 дня:
' gsPastDate = DateAdd("d", -2, Date)
	
if Wscript.Arguments.Count >= 1 then
	if lCase(Wscript.Arguments(0)) = "nothing" then
		gArrNumberOfFunctions = Array ("0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0")
		gsNumberOfFunctions = "nothing"
	elseif InStr(Wscript.Arguments(0), ",") then
		gArrNumberOfFunctions = split(Wscript.Arguments(0), ",")
		gsNumberOfFunctions = "different"
	elseif fuNeedHelp(lCase(Wscript.Arguments(0))) then
		fuTypeTextfile(gsHelpFile)
		WScript.Quit 0
	'else gArrNumberOfFunctions = Array ("1", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1")
	end if
	
	if Wscript.Arguments.Count >= 2 then
		if InStr(Wscript.Arguments(1), "@") then
			gsEMail = "y"
			gsTo = Wscript.Arguments(1)
		else
			gsEMail = lCase(Wscript.Arguments(1))
		end if
		
		if Wscript.Arguments.Count = 3 then
			gsDate = Wscript.Arguments(2)
		end if
	end if
end if


fuWritedown "* Имя файла журнала: " & gsFileNameLog, 2

gStartTime = fuStartTimer("")

if gsNumberOfFunctions <> "nothing" then
	gArrProcNamesList = Array (_
		"Процедура поиска логинов администраторов", _
		"Процедура AccauntManage", _
		"Процедура создания статистики неудачных логинов", _
		"Процедура управления группами", _
		"Процедура поиска неудачных логинов", _
		"Процедура управления паролями", _
		"Процедура управления компьютерами", _
		"Процедура аудита", _
		"Процедура статистики аудита", _
		"Процедура поиска логинов к RDP",_
		"Процедура слежения за действиями над объектами в AD")
		
	gArrReportfilesList = Array (_
		gsReportFolder & "logged_Administrator_" & gsNormalDate & ".html", _
		gsReportFolder & "new_AD_" & gsNormalDate & ".html", _
		gsReportFolder & "logonFailuresStats_" & gsNormalDate & ".html", _
		gsReportFolder & "group_Manage_" & gsNormalDate & ".html", _
		gsReportFolder & "logonFailure_" & gsNormalDate & ".html", _
		gsReportFolder & "change_password_" & gsNormalDate & ".html", _
		gsReportFolder & "new_Comp_AD_" & gsNormalDate & ".html", _
		gsReportFolder & "audit_" & gsNormalDate & ".html", _
		gsReportFolder & "auditStat_" & gsNormalDate & ".html", _
		gsReportFolder & "logged_Rdp_" & gsNormalDate & ".html", _
		gsReportFolder & "AD_objects_" & gsNormalDate & ".html")
		


	for gix = 0 to UBound(gArrNumberOfFunctions)
		gsFunctionName = gArrProcNamesList(gix)
		gsReportfile = gArrReportfilesList(gix)
		
		if gArrNumberOfFunctions(gix) = "1" then	
			startTime = fuStartTimer(gsFunctionName)
			gArrServerList = Array ("DC1", "DC2")
			Select Case gix
				Case 0: giErrorCode = fuLogonAdministrator(gArrServerList, gsReportfile)
				Case 1: giErrorCode = fuAccauntManage(gArrServerList, gsReportfile)
				Case 2: giErrorCode = fuLogonFailureStats(gArrServerList, gsReportfile)
				Case 3: gArrServerList = Array ("DC1","DC2","EXCH1","EXCH2")
						giErrorCode = fuGroupManage(gArrServerList, gsReportfile)
				Case 4: giErrorCode = fuLogonFailures(gArrServerList, gsReportfile)
				Case 5: giErrorCode = fuPasswordManage(gArrServerList, gsReportfile)
				Case 6: giErrorCode = fuCompManage(gArrServerList, gsReportfile)
				Case 7: gArrServerList = Array ("FILE-SRV1","FILE-SRV2")
						giErrorCode = fuAudit(gArrServerList, gsReportfile)
				Case 8: gArrServerList = Array ("FILE-SRV1","FILE-SRV2")
						giErrorCode = fuAuditStat(gArrServerList, gsReportfile)
				Case 9: gArrServerList = Array ("DC1","DC2","EXCH1","EXCH2")
						giErrorCode = fuLogonRdp(gArrServerList, gsReportfile, gsFunctionName)
				Case 10: giErrorCode = fuADObjects(gArrServerList, gsReportfile)
				Case else fuWritedown "* Некорректный индекс: " & gix, 4
			End Select
			fuCheckErrorCode giErrorCode, gArrServerList, gsReportfile, gsFunctionName, startTime
		else
			fuWritedown gsFunctionName & " пропущена", 4
		end if
	next
else
	fuWritedown "* Выбран вариант без создания отчетов", 4
end if

fuStopTimer(gStartTime)

if gsEMail = "y" then
	fuSendReportMail gsReportFolder & "*_" & gsDate & ".*", gsFrom, gsTo, gsSubject, gsDate
else
	fuWritedown "* Выбран вариант без отсылания отчетов", 4
end if

fuWritedown "* Журнал сохранен в Файл '" & gsFileNameLog & "'", 1
fuDeleteEvtxFiles "F:\Logi_ForADReports\*.evtx"
'MsgBox "Журнал сохранен в Файл '" & gsFileNameLog & "'", vbInformation, "Информация"
objTextFileWriteLog.Close

' Процедура поиска логинов администраторов
function fuLogonAdministrator(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		' Create Input Format object
		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		' Create Output Format object
		' Set oCSVOutputFormat = CreateObject("MSUtil.LogQuery.CSVOutputFormat")
		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		'oCSVOutputFormat.tabs = TRUE
		oTPLOutputFormat.tpl = "c:\script\Tamplates\logonAdministrator.tpl"

		' Создание текста запроса
		strQuery = "SELECT TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) as UserName, eventid, TimeGenerated, ComputerName as DC, " & _
			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) AS LogonName, " & _
			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,6,'|')) AS Domain, " & _
			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,13,'|')) AS LogonWKS, " & _
			"extract_token(trim(extract_token(Message, 18, ':' )), 0, ' ') as LogonIP, " & _
			"CASE TO_INT(EXTRACT_TOKEN(Strings,10,'|')) " & _
			"	WHEN 2 THEN  'Interactive - Intended for users who will be interactively using the machine, such as a user being logged on by a terminal server, remote shell, or similar process.'" & _
			"	WHEN 3 THEN  'Network - Intended for high performance servers to authenticate clear text passwords. LogonUser does not cache credentials for this logon type.'" & _
			"	WHEN 4 THEN  'Batch - Intended for batch servers, where processes may be executing on behalf of a user without their direct intervention; or for higher performance servers that process many clear-text authentication attempts at a time, such as mail or web servers. LogonUser does not cache credentials for this logon type.'" & _
			"	WHEN 5 THEN  'Service - Indicates a service-type logon. The account provided must have the service privilege enabled.'" & _
			"	WHEN 6 THEN  'Proxy - Indicates a proxy-type logon.'" & _
			"	WHEN 7 THEN  'Unlock - This logon type is intended for GINA DLLs logging on users who will be interactively using the machine. This logon type allows a unique audit record to be generated that shows when the workstation was unlocked.'" & _
			"	WHEN 8 THEN  'NetworkCleartext - Windows 2000; Windows XP and Windows Server 2003 family:  Preserves the name and password in the authentication packages, allowing the server to make connections to other network servers while impersonating the client. This allows a server to accept clear text credentials from a client, call LogonUser, verify that the user can access the system across the network, and still communicate with other servers.'" & _
			"	WHEN 9 THEN  'NewCredentials - Windows 2000; Windows XP and Windows Server 2003 family:  Allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identity, but uses different credentials for other network connections.'" & _
			"	WHEN 10 THEN 'RemoteInteractive - Terminal Server session that is both remote and interactive.'" & _
			"	WHEN 11 THEN 'CachedInteractive - Attempt cached credentials without accessing the network.'" & _
			"	WHEN 12 THEN 'CachedRemoteInteractive - Same as RemoteInteractive. This is used for internal auditing.'" & _
			"	WHEN 13 THEN 'CachedUnlock - Workstation logon'" & _
			"	ELSE EXTRACT_TOKEN(Strings,10,'|') " & _
			"END AS LogonType, " & _
			"extract_token(strings, 4, '|' ) as LogonProc, " & _
			"extract_token(strings, 11, '|' ) as ProcessID " & _
			"INTO " & lsReport & " " & _
			"FROM " & lsFROM & " " & _
			"WHERE EventID IN (4624;4636) " & _
			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
			"AND ((TO_LOWERCASE(LogonName) = TO_LOWERCASE('administrator')) " & _
			" OR  (TO_LOWERCASE(LogonName) = TO_LOWERCASE('администратор')) " & _
			" OR  (TO_LOWERCASE(LogonName) = TO_LOWERCASE('admin'))) "
		
		fuWritedown "* Запрос поиска логинов администраторов: '" & strQuery & "'", 4

		' Execute query
		oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		liErrorCode = 0
	else
		liErrorCode = 1
	end if

	fuLogonAdministrator = liErrorCode
end function

'Процедура AccauntManage
function fuAccauntManage(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)
	'lsFROM = "\\DC1\c$\WINDOWS\system32\winevt\Logs\Archive-Security-2010-08-03-09-34-11-527.evtx"
	'lsFROM = "\\DC1\security"

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\accauntManage.tpl"
			
		strQuery = "select extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, Message as EventCategoryName, " & _
			"extract_token(extract_token(Message, 0, ':' ),0,'.') as Description, EventID, ComputerName, " & _
			"extract_token(extract_token(Message, 8, ':' ), 1, ' ') as Name " & _
			"INTO " & lsReport & " " & _
			"FROM " & lsFROM & " " & _
			"WHERE EventID IN (4720;4722;4725;4726;4738;4740;4767;4780;4781;4782) " &_
			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss')"

		fuWritedown "* Запрос AccauntManage: '" & strQuery & "'", 4
		
		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		liErrorCode = 1
	end if

	fuAccauntManage = liErrorCode
end function

'Процедура создания статистики неудачных логинов
function fuLogonFailureStats(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\logonFailuresStats.tpl"

		strQuery = "SELECT TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) AS User, " & _
		"COUNT(*) AS Total " & _
		"INTO " & lsReport & " " & _
		"FROM " & lsFROM & " " & _
		"WHERE EventID IN (4625) " & _
		"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
		"GROUP BY User " & _
		"ORDER BY Total DESC"
			
		fuWritedown "* Запрос статистики неудачных логинов: '" & strQuery & "'", 4

		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		liErrorCode = 1
	end if
	
	fuLogonFailureStats = liErrorCode
end function

'Процедура управления группами
function fuGroupManage(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\groupManage.tpl"

		strQuery = "SELECT extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, EventCategoryName, " & _ 
			"extract_token(extract_token(Message, 0, ':' ), 0, '.') as EventIDName, " & _
			"COALESCE(extract_token(extract_token(strings, 0, ',' ), 1, '='), extract_token(strings, 0, '|' ), strings) as Name, " & _
			"COALESCE(extract_token(extract_token(strings, 0, ',' ), 1, '='), extract_token(strings, 1, '|' ), strings) as SIDName, " & _
			"extract_token(strings, 2, '|' ) as Name_Group, " & _
			"EventID, extract_token(ComputerName, 0, '.') " & _
			"INTO " & lsReport & " " & _
			"FROM " & lsFROM & " " & _
			"WHERE EventID IN (4727;4728;4729;4730;4731;4732;4733;4734;4735;4737;4744;4745;4746;4747;4748;4749;4750;4751;4752;4753;4754;4755;4756;4757;4758;4759;4760;4761;4762;4764;4783;4784;4785;4786;4787;4788;4789;4790) " & _
			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss')"
			
		fuWritedown "* Запрос управления группами: '" & strQuery & "'", 4

		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		liErrorCode = 1
	end if
	
	fuGroupManage = liErrorCode
end function

'Процедура поиска неудачных логинов
function fuLogonFailures(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\logonFailures.tpl"

		strQuery = "SELECT COUNT(EventID) AS TotalLogonFailures, " & _
			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) AS User, " & _
			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,6,'|')) AS Domain, " & _
			"TO_LOWERCASE(EXTRACT_TOKEN(Strings,13,'|')) AS WorkStation, " & _
			"CASE TO_INT(EXTRACT_TOKEN(Strings,10,'|')) " & _
			"	WHEN 2 THEN  'Interactive - Intended for users who will be interactively using the machine, such as a user being logged on by a terminal server, remote shell, or similar process.'" & _
			"	WHEN 3 THEN  'Network - Intended for high performance servers to authenticate clear text passwords. LogonUser does not cache credentials for this logon type.'" & _
			"	WHEN 4 THEN  'Batch - Intended for batch servers, where processes may be executing on behalf of a user without their direct intervention; or for higher performance servers that process many clear-text authentication attempts at a time, such as mail or web servers. LogonUser does not cache credentials for this logon type.'" & _
			"	WHEN 5 THEN  'Service - Indicates a service-type logon. The account provided must have the service privilege enabled.'" & _
			"	WHEN 6 THEN  'Proxy - Indicates a proxy-type logon.'" & _
			"	WHEN 7 THEN  'Unlock - This logon type is intended for GINA DLLs logging on users who will be interactively using the machine. This logon type allows a unique audit record to be generated that shows when the workstation was unlocked.'" & _
			"	WHEN 8 THEN  'NetworkCleartext - Windows 2000; Windows XP and Windows Server 2003 family:  Preserves the name and password in the authentication packages, allowing the server to make connections to other network servers while impersonating the client. This allows a server to accept clear text credentials from a client, call LogonUser, verify that the user can access the system across the network, and still communicate with other servers.'" & _
			"	WHEN 9 THEN  'NewCredentials - Windows 2000; Windows XP and Windows Server 2003 family:  Allows the caller to clone its current token and specify new credentials for outbound connections. The new logon session has the same local identity, but uses different credentials for other network connections.'" & _
			"	WHEN 10 THEN 'RemoteInteractive - Terminal Server session that is both remote and interactive.'" & _
			"	WHEN 11 THEN 'CachedInteractive - Attempt cached credentials without accessing the network.'" & _
			"	WHEN 12 THEN 'CachedRemoteInteractive - Same as RemoteInteractive. This is used for internal auditing.'" & _
			"	WHEN 13 THEN 'CachedUnlock - Workstation logon'" & _
			"	ELSE EXTRACT_TOKEN(Strings,10,'|') " & _
			"END AS Type " & _
			"INTO " & lsReport & " " & _
			"FROM " & lsFROM & " " & _
			"WHERE EventID IN (4625) " & _
			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
			"GROUP BY User,Domain,WorkStation,Type " & _
			"ORDER BY TotalLogonFailures DESC"
			
		fuWritedown "* Запрос создания статистики неудачных логинов: '" & strQuery & "'", 4

		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		liErrorCode = 1
	end if
	
	fuLogonFailures = liErrorCode
end function

'Процедура управления паролями
function fuPasswordManage(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\PasswordManage.tpl"
		
		strQuery = "SELECT extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, EventCategoryName, " & _
			"extract_token(extract_token(Message, 0, ':' ),0,'.') as Description, EventID, ComputerName, " & _
			"extract_token(extract_token(Message, 8, ':' ), 0, ' Account ') as Name " & _
			"INTO " & lsReport & " " & _
			"FROM " & lsFROM & " " & _
			"WHERE EventID IN (4723;4724;4782;4793) " & _
			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss')"
			
		fuWritedown "* Запрос управления паролями: '" & strQuery & "'", 4

		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		liErrorCode = 1
	end if
	
	fuPasswordManage = liErrorCode
end function

'Процедура управления компьютерами
function fuCompManage(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\compManage.tpl"
		
		strQuery = "SELECT extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, EventCategoryName, " & _
			"extract_token(extract_token(Message, 0, ':' ),0,'.') as Description, EventID, ComputerName, " & _
			"extract_token(extract_token(Message, 8, ':' ), 0, ' Account ') as Name " & _
			"INTO " & lsReport & " " & _
			"FROM " & lsFROM & " " & _
			"WHERE EventID in (4720;4742;4743) " & _
			"and Name like '%%$%%' " & _
			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss')"
			
		fuWritedown "* Запрос управления компьютерами: '" & strQuery & "'", 4

		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		liErrorCode = 1
	end if
	
	fuCompManage = liErrorCode
end function

'Процедура аудита
function fuAudit(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\audit.tpl"

		strQuery = "select TimeGenerated, EventID, " & _
			"extract_token(Strings, 0, '|' ) as UserSID,  " & _
			"extract_token(Strings, 6, '|' ) as ObjectName, " & _
			"extract_token(Strings, 1, '|' ) as User, " & _
			"extract_token(Strings, 2, '|' ) as Domain, " & _
			"extract_token(Strings, 5, '|' ) as ObjectType, " & _
			"extract_token(Strings, 11, '|' ) as ProgramName, " & _
			"extract_token(Message, 0, '.' ) as Event " & _
			"into " & lsReport & " " & _
			"from " & lsFROM & " " & _
			"where EventId in (4656;4659;4660;4661;4663;4691) " & _
			"and TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
			"and User <> 'NT AUTHORITY\SYSTEM' " & _
			"and extract_token(Strings, 6, '|' ) not like '%%HarddiskVolumeShadowCopy%%' " & _
			"and extract_token(Strings, 6, '|' ) not like '%%ShadowCopyVolume%%' " & _
			"and TO_LOWERCASE(extract_token(Strings, 6, '|' )) like '%%Top-Secret-Documents%%' " & _
			"and User <> 'FILE-SRV1$' " & _
			"and User <> 'FILE-SRV2$' " & _
			"order by Timegenerated"
			
		fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4

		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		lsFROM = fuCollectFileList(lArrServerList, true)
		
		if lsFROM <> "" then
			Set oLogQuery = CreateObject("MSUtil.LogQuery")

			Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
			oEVTInputFormat.direction = "BW"

			Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
			oTPLOutputFormat.tpl = "c:\script\Tamplates\audit.tpl"

			strQuery = "select TimeGenerated, EventID, " & _
				"extract_token(Strings, 0, '|' ) as UserSID,  " & _
				"extract_token(Strings, 6, '|' ) as ObjectName, " & _
				"extract_token(Strings, 1, '|' ) as User, " & _
				"extract_token(Strings, 2, '|' ) as Domain, " & _
				"extract_token(Strings, 5, '|' ) as ObjectType, " & _
				"extract_token(Strings, 11, '|' ) as ProgramName, " & _
				"extract_token(Message, 0, '.' ) as Event " & _
				"into " & lsReport & " " & _
				"from " & lsFROM & " " & _
				"where EventId in (4656;4659;4660;4661;4663;4691) " & _
				"and TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
				"and User <> 'NT AUTHORITY\SYSTEM' " & _
				"and extract_token(Strings, 6, '|' ) not like '%%HarddiskVolumeShadowCopy%%' " & _
				"and extract_token(Strings, 6, '|' ) not like '%%ShadowCopyVolume%%' " & _
				"and TO_LOWERCASE(extract_token(Strings, 6, '|' )) like '%%Top-Secret-Documents%%' " & _
				"and User <> 'FILE-SRV1$' " & _
				"and User <> 'FILE-SRV2$' " & _
				"order by Timegenerated"
				
			fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4

			if not gbDebugModeON then
				oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
			end if
		else
			fuWritedown "* Что-то с аудитом совсем плохо.", 4
		end if
		
		liErrorCode = 1
	end if
	
	fuAudit = liErrorCode
end function

'Процедура статистики аудита
function fuAuditStat(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\auditStat.tpl"

		strQuery = "select extract_token(Strings, 1, '|' ) as User, " & _
			"COUNT(*) as Qty, " & _
			"MAX(TimeGenerated) as MaxTime  " & _
			"into " & lsReport & " " & _
			"from " & lsFROM & " " & _
			"where EventId in (4656;4659;4660;4661;4663;4691) " & _
			"and TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
			"and User <> 'NT AUTHORITY\SYSTEM' " & _
			"and extract_token(Strings, 6, '|' ) not like '%%HarddiskVolumeShadowCopy%%' " & _
			"and extract_token(Strings, 6, '|' ) not like '%%ShadowCopyVolume%%' " & _
			"and TO_LOWERCASE(extract_token(Strings, 6, '|' )) like '%%Top-Secret-Documents%%' " & _
			"group by User " & _
			"order by User"
		
		fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4

		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		lsFROM = fuCollectFileList(lArrServerList, true)
		
		if lsFROM <> "" then
			Set oLogQuery = CreateObject("MSUtil.LogQuery")

			Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
			oEVTInputFormat.direction = "BW"

			Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
			oTPLOutputFormat.tpl = "c:\script\Tamplates\auditStat.tpl"

			strQuery = "select extract_token(Strings, 1, '|' ) as User, " & _
				"COUNT(*) as Qty, " & _
				"MAX(TimeGenerated) as MaxTime  " & _
				"into " & lsReport & " " & _
				"from " & lsFROM & " " & _
				"where EventId in (4656;4659;4660;4661;4663;4691) " & _
				"and TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
				"and User <> 'NT AUTHORITY\SYSTEM' " & _
				"and extract_token(Strings, 6, '|' ) not like '%%HarddiskVolumeShadowCopy%%' " & _
				"and extract_token(Strings, 6, '|' ) not like '%%ShadowCopyVolume%%' " & _
				"and TO_LOWERCASE(extract_token(Strings, 6, '|' )) like '%%Top-Secret-Documents%%' " & _
				"group by User " & _
				"order by User"
			
			fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4

			if not gbDebugModeON then
				oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
			end if
		else
			fuWritedown "* Что-то со статистикой аудитом совсем плохо.", 4
		end if
	
		liErrorCode = 1
	end if
	
	fuAuditStat = liErrorCode
end function

'Процедура поиска логинов к RDP
function fuLogonRdp(lArrServerList, lsReport, lsFunctionName)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\logonRdp.tpl"

		strQuery = "SELECT DISTINCT resolve_sid(SID) as UserName, eventid, TimeGenerated, extract_token(ComputerName, 0, '.') as NormComputerName, " & _
			"extract_token(strings, 5, '|' ) as LogonName, " & _
			"extract_token(strings, 13, '|' ) as LogonWKS, " & _
			"extract_token(strings, 18, '|' ) as LogonIP, " & _
			"case extract_token(strings, 8, '|' ) " & _
			" WHEN '2' THEN 'interactive' " & _
			" WHEN '3' THEN 'network' " & _
			" WHEN '4' THEN 'batch' " & _
			" WHEN '5' THEN 'service' " & _
			" WHEN '7' THEN 'unlocked workstation' " & _
			" WHEN '8' THEN 'network logon using a cleartext password' " & _
			" WHEN '9' THEN 'impersonated logons' " & _
			" WHEN '10' THEN 'remote access' " & _
			" ELSE extract_token(strings, 8, '|' ) " & _
			"end as LogonType, " & _
			"extract_token(strings, 17, '|' ) as LogonProc, " & _
			"extract_token(strings, 16, '|' ) as ProcessID " & _
			"INTO " & lsReport & " " & _
			"FROM " & lsFROM & " " & _
			"WHERE EventID IN (4624;4625;4648;4675) " & _
			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
			"AND LogonType = 'remote access' " & _
			"order by Timegenerated DESC"
			
		fuWritedown "* Запрос " & lsFunctionName & ": '" & strQuery & "'", 4

		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		liErrorCode = 1
	end if
	
	fuLogonRdp = liErrorCode
end function

function fuADObjects(lArrServerList, lsReport)
	liErrorCode = -1
	lsFROM = fuCollectFileList(lArrServerList, false)
	'lsFROM = "\\DC1\c$\WINDOWS\system32\winevt\Logs\Archive-Security-2010-12-09-09-55-23-631.evtx"
	'lsFROM = "\\DC1\security"

	if lsFROM <> "" then
		Set oLogQuery = CreateObject("MSUtil.LogQuery")

		Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
		oEVTInputFormat.direction = "BW"

		Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
		oTPLOutputFormat.tpl = "c:\script\Tamplates\adobjects.tpl"
			
		strQuery = "select extract_token(extract_token(Message, 3, ':' ), 0, ' Account ') as UserName, TimeGenerated, SourceName, Message as EventCategoryName, " & _
			"extract_token(extract_token(Message, 0, ':' ),0,'.') as Description, EventID, ComputerName, " & _
			"extract_token(extract_token(Message, 8, ':' ), 1, ' ') as Name " & _
			"INTO " & lsReport & " " & _
			"FROM " & lsFROM & " " & _
			"WHERE EventID IN (4928;4929;4930;4931;4934;4935;4936;4937;4662;5136;5137;" & _
			"5138;5139;5141;4932;4933) " & _
			"AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
			"AND UserName not like '%%RTCService%%' "

		fuWritedown "* Запрос ADObjects: '" & strQuery & "'", 4
		
		if not gbDebugModeON then
			oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
		end if
		
		liErrorCode = 0
	else
		liErrorCode = 1
	end if
	
	fuADObjects = liErrorCode
end function


' Служебные функции
function fuSendReportMail(lsFileMask, lsFrom, lsTo, lsSubject, lsDate)
	Set objEmail = CreateObject("CDO.Message")
	objEmail.From = lsFrom
	objEmail.To = lsTo
	objEmail.Subject = lsSubject
	objEmail.HTMLBody = "<span style='font-family:Tahoma,Arial,sans-serif;font-size:14pt;'>Отчёты за " & _
		lsDate & "</span>"

	fuCheckfileSizeAndZIP lsDate
	
	Set oLogQuery = CreateObject("MSUtil.LogQuery")
	Set oFormat = CreateObject("MSUtil.LogQuery.FileSystemInputFormat")
	Set oRecordSet = oLogQuery.Execute("SELECT * FROM " & lsFileMask, oFormat)

	i = 0
	While Not oRecordSet.atEnd
	    Set oRecord = oRecordSet.getRecord()
	    strValue = oRecord.getValue("Path")
	    objEmail.AddAttachment strValue
	    i = i + 1
	    oRecordSet.moveNext
	Wend
	oRecordSet.Close

	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserver")="MAIL-SRV" 
	objEmail.Configuration.Fields.Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
	objEmail.Configuration.Fields.Update
	objEmail.Send

	fuWritedown "* Отчеты отправлены от '" & lsFrom & "' на '" & lsTo & "'. Количество файлов-отчетов: " & i, 4
end function

function fuCheckErrorCode(liErrorCode, lArrServerList, lsReportfile, lsFunctionName, startTime)
	select case liErrorCode
		case -1:  fuWritedown "* " & lsFunctionName & " не выполнилась (функция не отработала корректно)", 4
		case 0:   fuWritedown "* " & lsFunctionName & " завершена", 4
				  fuCheckResultFile(lsReportfile)
		case 1:   fuWritedown "* Для серверов '" & Join(lArrServerList, ",") & "' не найдены архивные папки/файлы, по которым делать отчет (блок FROM пустой). Поиск выполняется по текущим журналам.", 4
				  fuCheckResultFile(lsReportfile)
		case else fuWritedown "* Непридвиденная ошибка в " & lsFunctionName & "!", 4
	end select
	
	fuStopTimer(startTime)
	fuWritedown "", 4
end function

function fuPing(NetworkDevice)
	lBoo = false
	set objPING = GetObject("winmgmts:{impersonationLevel=impersonate}")._
		ExecQuery ("select * from Win32_PingStatus where address ='" & NetworkDevice & "'")

	For Each PING In objPing
		if PING.StatusCode = 0 then
			lBoo = true
		end if
	next
	
	fuPing = lBoo
end function

function fuCollectFileList(lArrServerList, lbFindOnServer)
	' true для поиска в текущих журналах, false для поиска в архивных журналах:
	' lbFindOnServer = true
	' lbFindOnServer = false
	
	lsTmp = Join(lArrServerList, ",")
	fuWritedown "* Список компьютеров: " & lsTmp, 4
	lsList = ""
	lsListFiles = ""
	lsTmpPath = ""
	lbServerHaveArchive = false

	for lix = 0 to UBound(lArrServerList)
		lsServer = lArrServerList(lix)
		fuWritedown "* Компьютер '" & lsServer & "'", 4
		
		if lbFindOnServer then		
			if fuPing(lsServer) then
				fuWritedown "* Есть в сети", 4
				lsList = lsList & "\\" & lsServer & "\Security"
				
					if fuServerHaveArchive(lsServer, lsListFiles) then
						lbServerHaveArchive = true
						'lsList = lsList & "," & "\\" & lsServer & "\c$\WINDOWS\system32\config\archive-security-*.evtx"
						if len(lsListFiles) <> 0 then
							'lsList = lsList & "," & lsListFiles
						end if
					end if
				
				if lix < UBound(lArrServerList) then
					lsList = lsList & ","
				end if
			else
				fuWritedown "* Нет в сети", 4
			end if
		else
			lbServerHaveArchive = false
			
			if Len(lsListFiles) = 0 then
				lsListFiles = fuGetLogFolder(lsServer)
			else
				lsTmpPath = fuGetLogFolder(lsServer)
				if Len(lsTmpPath) <> 0 then
					lsListFiles = lsListFiles & "," & lsTmpPath
				end if
			end if
		end if
		
	next

	if Right(lsList, 2) = ", " then
		lsList = Left(lsList, Len(lsList)-2)
	end if
	
	'\\dc1\Security, \\dc1\c$\WINDOWS\system32\config\Archive-Security-*.evt, 
	'\\dc2\Security, \\dc2\c$\WINDOWS\system32\config\Archive-Security-*.evt
	
	if lbServerHaveArchive then
		lsList = lsList & "," & lsListFiles
	end if
	
	if not lbFindOnServer then
		lsList = lsListFiles
	end if
	
	fuWritedown "* Блок FROM из функции: '" & lsList & "'", 4
	fuCollectFileList = lsList
end function

function fuServerHaveArchive(lsServerName, lsListFiles_a)
	Const FILE_NAME = 0

	dim gbFoo
	dim gsFilename

	gbFoo = false

	Set objShell = CreateObject ("Shell.Application")
	Set objFolder = objShell.Namespace ("\\" & lsServerName & "\c$\Windows\System32\winevt\Logs")

	For Each strFileName in objFolder.Items
		gsFilename = trim(lCase(objFolder.GetDetailsOf (strFileName, FILE_NAME)))
		' fuWritedown "* gsFilename: " & gsFilename, 1
	    if ((InStr(gsFilename, "archive-security-")) and (Right(gsFilename, 4) = "evtx")) then
			fuWritedown "* Архив найден! \\" & lsServerName & "\c$\Windows\System32\winevt\Logs\"&gsFilename, 4
			if len(lsListFiles_a) = 0 then
				lsListFiles_a = "f:\Logi_ForADReports\" & gsFilename
			else 
				lsListFiles_a = lsListFiles_a & "," & "f:\Logi_ForADReports\" & gsFilename
			end if
			fuWritedown "* lsListFiles_a: " & lsListFiles_a, 2
			'fuConvertEvt2Evtx "\\" & lsServerName & "\c$\WINDOWS\system32\config\" & gsFilename, gsFilename
			fuCopyEvtx "\\" & lsServerName & "\c$\Windows\System32\winevt\Logs\" & gsFilename, gsFilename
			gbFoo = true
		end if
	Next

	if gbFoo then
		fuWritedown "* На компьютере '" & lsServerName & "' архивы журналов есть", 4
	else
		fuWritedown "* На компьютере '" & lsServerName & "' архивов журналов нет", 4
	end if 
	
	fuServerHaveArchive = gbFoo
end function

function fuConvertEvt2Evtx(lsFilenamePath, lsFilename)
	lbTmp = true
	
	if (fuIsFileExists("f:\Logi_ForADReports\" & lsFilename) and (fuIsFileExists("f:\Logi_ForADReports\" & lsFilename & "x"))) then
		fuWritedown "* Конвертация файла " & lsFilename & " не нужна, уже есть сконвертированный", 4
	else
		fuWritedown "* Конвертируем файл " & lsFilename & "...", 4

		Set WshShell = CreateObject("WScript.Shell")

		gsRunCmd = "c:\script\convert_evt_to_evtx.bat " & lsFilenamePath & " " & lsFilename

		fuWritedown "* Выполняется команда: '" & gsRunCmd & "'", 2
		WshShell.Run gsRunCmd
		
		WScript.Sleep 300000
	end if
	
	fuConvertEvt2Evtx = lbTmp
end function 

function fuCopyEvtx(lsFilenamePath, lsFilename)
	lbTmp = true

	if not fuIsFileExists("f:\Logi_ForADReports\" & lsFilename) then
		Set WshShell = CreateObject("WScript.Shell")
		
		gsRunCmd = "c:\script\copy_evtx.bat " & lsFilenamePath & " " & lsFilename
		fuWritedown "* Выполняется команда: '" & gsRunCmd & "'", 4
		WshShell.Run gsRunCmd
			
		WScript.Sleep 25000
	else
		fuWritedown "* Архивный журнал " & lsFilename & " копировать не нужно, уже есть скопированный", 4
	end if
	
	fuCopyEvtx = lbTmp
end function 

function fuDeleteEvtxFiles(lsFromList)
	fuWritedown "* Удаление временных файлов: " & lsFromList, 4
	lbTmp = true
	
	Set WshShell = CreateObject("WScript.Shell")
	
	if InStr(lsFromList, ",") then
		lArrFrom = Split(lsFromList, ",")
		
		for lix = 0 to uBound(lArrFrom)
			if InStr(lCase(lArrFrom(lix)), "archive-security-") then
				gsRunCmd = "c:\script\del_evtx.bat " & lArrFrom(lix)

				fuWritedown "* Выполняется команда: '" & gsRunCmd & "'", 4
				WshShell.Run gsRunCmd
			end if
		next
	else
		gsRunCmd = "c:\script\del_evtx.bat " & lsFromList

		fuWritedown "* Выполняется команда: '" & gsRunCmd & "'", 4
		WshShell.Run gsRunCmd
	end if
	
	WScript.Sleep 60000
	
	fuDeleteEvtxFiles = lbTmp
end function 


function fuIsFileExists(lsFilename)
	lBoo = false
	
	Set FSO = CreateObject("Scripting.FileSystemObject")
	if FSO.FileExists(lsFilename) then
		' Файл существует
		lBoo = true
	else 
		' Файла не существует
	end if
	Set FSO = nothing
	
	fuIsFileExists = lBoo
end function

function fuWritedown(lsToWrite, liCase)
	Select Case liCase
		Case 0: ' ничего не делать. Сообщение уходит в никуда.
		Case 1: WScript.Echo lsToWrite ' сообщение только на экран
		Case 2: objTextFileWriteLog.WriteLine lsToWrite ' сообщение только в журнал
		Case 4: WScript.Echo lsToWrite ' сообщение и на экран, и в журнал
				objTextFileWriteLog.WriteLine lsToWrite
		Case else WScript.Echo lsToWrite
	End Select
end function 

function fuNormalizeSystemDate(lsDate)
	lsNormalizeDate = lsDate
	
	if InStr(lsDate, ".") then
		lArrDate = Split(lsDate, ".")
		lsNormalizeDate = lArrDate(2) & "." & lArrDate(1) & "." & lArrDate(0)
	elseif InStr(lsDate, "/") then
		lArrDate = Split(lsDate, "/")
		lsNormalizeDate = fuCheckDatePart(lArrDate(2)) & "." & fuCheckDatePart(lArrDate(0)) & "." & fuCheckDatePart(lArrDate(1))
	end if
	
	fuNormalizeSystemDate = lsNormalizeDate
end function 

function fuNormalizeDate(lsDate)
	lsNormalizeDate = lsDate
	
	if InStr(lsDate, ".") then
		lArrDate = Split(lsDate, ".")
		lsNormalizeDate = lArrDate(2) & "." & lArrDate(1) & "." & lArrDate(0)
	end if
	
	fuNormalizeDate = lsNormalizeDate
end function 

function fuCheckDatePart(lsDate)
	lsNormalizeDate = lsDate
	
	if len(lsDate) <= 1 then
		lsNormalizeDate = "0" & lsDate
	end if
	
	fuCheckDatePart = lsNormalizeDate
end function 

function fuStartTimer(lsFunctionName)
	fuStartTimer = Now()
	if lsFunctionName <> "" then
		fuWritedown VBNewLine & lsFunctionName & " запущена", 4
	end if
end function 

function fuStopTimer(startTime)
	EndTime = Now()
	timeDiff = CDate(EndTime - startTime)
	fuWritedown "* Поиск выполнялся: " & timeDiff & " (" & startTime & "/" & EndTime & ").", 4
end function 

function fuCheckResultFile(lsReportfile)
	if objFSO.FileExists(lsReportfile) then
		fuWritedown "* Результат сохранен в файл '" & lsReportfile & "'", 4
	else 
		fuWritedown "* Файл результатов '" & lsReportfile & "' не был создан, так как поиск не дал результатов", 4
	end if
end function 

function fuTypeTextfile(lsTextfile)
	'fuWritedown "Распечатать файл помощи '" & lsTextfile & "'", 1
	Set objTextFileShowHelp = objFSO.OpenTextFile(lsTextfile, 1)
	Do Until objTextFileShowHelp.AtEndOfStream
		fuWritedown objTextFileShowHelp.Readline, 1
	Loop
	objTextFileShowHelp.Close
end function 

function fuNeedHelp(lsPar)
	lbFoo = false
	if  lsPar = "h" or lsPar = "help" or InStr(lsPar, "?") then
		lbFoo = true
	end if
	fuNeedHelp = lbFoo
end function 


function fuGetFilename(lsDate)
	lsTmp = "Archive-Security-2013-12-01-*.evtx"
	
	if InStr(lsDate, ".") then
		lArrDate = Split(lsDate, ".")
		lsTmp = "Archive-Security-" & fuCheckDatePart(lArrDate(2)) & "-" & fuCheckDatePart(lArrDate(1)) & "-" & fuCheckDatePart(lArrDate(0)) & "-*.evtx"
	elseif InStr(lsDate, "/") then
		lArrDate = Split(lsDate, "/")
		lsTmp = "Archive-Security-" & fuCheckDatePart(lArrDate(2)) & "-" & fuCheckDatePart(lArrDate(1)) & "-" & fuCheckDatePart(lArrDate(0)) & "-*.evtx"
	end if
	
	fuGetFilename = lsTmp
end function 

function fuGetLogFolder(lsServer)
	lsTmp = ""
	
	Select Case lsServer
		Case "DC1": lsTmp = "Q:\Logi_DC1\"
		Case "DC2": lsTmp = "Q:\Logi_DC2\"
		Case "FILE-SRV1": lsTmp = "Q:\Logi_FILE-SRV1\"
		Case "FILE-SRV2": lsTmp = "Q:\Logi_FILE-SRV2\"
		Case "EXCH1": lsTmp = "Q:\Logi_EXCH1\"
		Case "EXCH2": lsTmp = "Q:\Logi_EXCH2\"
		Case else 
			fuWritedown "* В скрипте папка с архивами сервера " & lsServer & " не указана. Пытаюсь папку угадать 'Q:\Logi_" & lsServer & "\'", 4
			lsTmp = "Q:\Logi_" & lsServer & "\"
	End Select
	
	
	lsPath = Left(lsTmp, Len(lsTmp)-1)
	'lsPath = lsTmp
	lsFile = gsLogFilename

	lsBoo = fuNASHaveArchive(lsServer, lsPath, lsFile)

	if lsBoo then
		lsTmp = lsTmp & gsLogFilename
	else
		lsTmp = ""
	end if
	
	fuGetLogFolder = lsTmp
end function 


function fuNASHaveArchive(Server, Path, File)
	wscript.echo Server & ", " & Path & ", " & File

	Const FILE_NAME = 0

	dim gbFoo
	dim gsFilename

	gbFoo = false
	
	lsF = lCase(Left(File, Len(File)-6))

	Set objShell = CreateObject("Shell.Application")
	Set objFolder = objShell.Namespace(Path)

	For Each strFileName in objFolder.Items
		gsFilename = trim(lCase(objFolder.GetDetailsOf (strFileName, FILE_NAME)))
		' wscript.echo  "* gsFilename: " & gsFilename
	    if InStr(gsFilename, lsF) then
			gbFoo = true
		end if
	Next

	fuNASHaveArchive = gbFoo
end function 

function fuCheckfileSizeAndZIP(lsDate)
	lsReportFolder = "F:\Reports\"
	lArrReportfilesList = Array (_
		lsReportFolder & "logged_Administrator_" & lsDate & ".html", _
		lsReportFolder & "new_AD_" & lsDate & ".html", _
		lsReportFolder & "logonFailuresStats_" & lsDate & ".html", _
		lsReportFolder & "group_Manage_" & lsDate & ".html", _
		lsReportFolder & "logonFailure_" & lsDate & ".html", _
		lsReportFolder & "change_password_" & lsDate & ".html", _
		lsReportFolder & "new_Comp_AD_" & lsDate & ".html", _
		lsReportFolder & "audit_" & lsDate & ".html", _
		lsReportFolder & "auditStat_" & lsDate & ".html", _
		lsReportFolder & "logged_Rdp_" & lsDate & ".html", _
		gsReportFolder & "AD_objects_" & gsNormalDate & ".html")

	for lix = 0 to UBound(lArrReportfilesList)
		lbTmp = false
		
		lsFilenamePath = lArrReportfilesList(lix)
		ArcName = Left(lsFilenamePath, Len(lsFilenamePath)-5) & ".zip"
		
		if fuIsFileExists(lsFilenamePath) then
			Set File = objFSO.GetFile(lsFilenamePath)

			 lsFilenameSize =  File.Size
			 if lsFilenameSize > 3000000 then
				fuWritedown "* Размер файла '" & lsFilenamePath & "' больше 3 МБ (размер " & lsFilenameSize & " байт), необходимо его заархивировать", 4
				fuWritedown "* Идет архивация...", 1
				'--[ Архивирование отчета ]-------------------------------------------------------------------
				Set Shell=CreateObject("WScript.Shell")
				Set Zip=Shell.Exec("C:\Program Files\7-Zip\7z.exe a " & ArcName & " " & lsFilenamePath)

				'Необходимо ожидание, пока архивирование не закончится
				While (Zip.Status = 0)
					WScript.Sleep 5000
				Wend

				Set Shell = Nothing

				fuWritedown "* Архивирование завершено! Имя архива '" & ArcName & "'", 4
				fuWritedown "* Удаляем файл отчета '" & lsFilenamePath & "'...", 4
				objFSO.DeleteFile lsFilenamePath, true
				fuWritedown "* Удаление завершено!", 1
				lbTmp = true
				'WScript.Sleep 2000
				'---------------------------------------------------------------------------------------------
			 end if
		else 
			' файла не существует, ничего не делаем.
		end if
	next
	
	fuCheckfileSizeAndZIP = lbTmp
end function 


Вспомагательные батники.
convert_evt_to_evtx.bat
copy %1 f:\Logi_ForADReports\%2
wevtutil epl f:\Logi_ForADReports\%2 f:\Logi_ForADReports\%2x /lf:true

copy_evtx.bat
copy %1 f:\Logi_ForADReports\%2

del_evtx.bat
del %1


Скрипт может запускаться без каких-либо ключей. В этом случае создаются все одиннадцать отчетов.
Скрипт может иметь три ключа.
Logparser_4.bat [список_отчетов] [адрес_электронной_почты] [дата]

[список_отчетов] — необязательный ключ. Перечень отчетов, которые надо выполнить. Указывается в формате: «1,1,0,0,1,0,1,0,1,0,0», то есть единица указывает, что отчет надо выполнить.
Можно сделать все отчеты, указав ключ all.
Выключает создание всех отчетов ключ nothing.

[адрес_электронной_почты] — необязательный ключ. Может принимать значения:
y — отправить отчеты на адрес по умолчанию (admin1@domain.com)
n — не отправлять отчеты на электронный ящик, а только сложить в папку отчетов f:\Reports.
адрес_электронной_почты — Указывает адрес электронной почты, на который будут отосланы отчеты.

[дата] — необязательный ключ. Указывает дату для работы с отчетами. Этот параметр указывается только тогда, когда надо отправить уже сделанные отчеты за дату в прошлом (Отчеты всегда выполняются за предыдущий день от даты запуска скрипта). Формат даты: YYYY.MM.DD

Отчеты:
1. Отчет поиска логинов администраторов
2. Отчет AccauntManage
3. Отчет создания статистики неудачных логинов
4. Отчет управления группами
5. Отчет поиска неудачных логинов
6. Отчет управления паролями
7. Отчет управления компьютерами
8. Отчет аудита по папке Top-Secret-Documents
9. Отчет статистики аудита по папке Top-Secret-Documents
10. Отчет поиска логинов к RDP
11. Отчет слежения за действиями над объектами в AD

Примеры.

Logparser_4.bat nothing "admin2@domain.com" 2013.01.01
Все уже сделанные отчеты за 1 января 2013 г. отсылает на «admin2@domain.com» (заново отчеты не выполняются).

Logparser_4.bat nothing y 2013.02.18
Все уже сделанные отчеты за 18 февраля 2013 г. отсылает на адрес по умолчанию (заново отчеты не выполняются).

Logparser_4.bat all "admin3@domain.com"
Создает все отчеты и отсылает на «admin3@domain.com».

Logparser_4.bat "1,0,0,0,0,0,0,0,0,1,0"
Создает только первый и последний отчёты и отсылает на адрес по умолчанию.

Logparser_4.bat "0,1,0,0,0,0,0,0,0,0,0" n
Создает только второй отчет, но никуда не отсылает, а складывет в папку f:\Reports.

Logparser_4.bat /?
Показывает помощь.

Примечание.
Сейчас скрипт ищет события в архивных журналах безопасности (те, что Archive-Security-*.evt) в централизованном хранилище. В скрипте это диск Q, подключенный в начале в батнике
net use Q: \\nas-srv\BACKUP
Но может искать в оперативных и архивных журналах на серверах. Для этого нужно в каждой их 11 функций изменить
lsFROM = fuCollectFileList(lArrServerList, false)
на
lsFROM = fuCollectFileList(lArrServerList, true)

Тут можно скачать архив со скриптом, батниками и шаблонами.
В принципе, не обязательно использовать скрипт дословно. Главное понять принцип, как логпарсер ищет события и выгружает их в html-файл, используя шаблон. И пользоваться.
Лужин Кирилл @luzhin_kirill
карма
9,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (15)

  • 0
    Способ интересный.
    Но есть один минус, названия созданных/удаленных/измененных групп не отображаются. Только SID.
    Если группу удалить, то мы никогда названия не узнаем:)
    Такую же вещь можно сделать встроенными средствами: например powershell. Только плюсом к этому вместо SID в именах группы писать диспейл_нейм группы (отдельным запросом доставать имя группы по SID).
    1 скрипт, может запускаться раз в 1/2/3.../24 часа или как угодно, парсить лог, доставать инфу, писать в базу к примеру, а там уже как хотите работаете с этой базой.
    Вот пример. Правда не в базу. Но прикрутить запрос к базе несложно.
    • 0
      названия созданных/удаленных/измененных групп не отображаются
      Отображаются. Только в поле «Имя пользователя». Микрософт в разных событиях в одни и те же поля вносит разную информацию. Поэтому надо было бы назвать поля моего отчёта более абстрактно, конечно.
  • +1
    А за документик с событиями — отдельное спасибо!
  • 0
    А вы события входа в домен пользователей не собираете?
    К примеру чтобы знать где какой пользователь и в какое время залогинился?
    • 0
      Это мы у SCCM’а спрашиваем.
      • +1
        Это можно делать анализируя события керберос, точнее запрос тикетов (4769 событие).
        На каждом КД анализировать лог на предмет этого события, и он со 100% вероятностью скажет кто залогинился, на какой машине и в какое время.
        Тем самым кстати можно посмотреть как делятся кеорберос-запросы между КД в домене.
        • 0
          IMHO, SCCM-ом хорошо потом анализировать, кто где «пасся», а «где именно сейчас этот бездельник», как Вы справедливо написали, через запрос событий керберос, и то с некоторой вероятностью, и если аудит успеха для Audit account logon events включен.

          Почему не 100%? Потому, что в случае если пользователь залогинился в отключенной от сети станции, то авторизация его шла по тикету на локальном хранилище. А после восстановления сетевого соединения не факт, что ему сразу резко потребуются какие-то сетевые ресурсы. Он может и локально долго некоторое время работать, хотя машинка уже в сети авторизуется.
          • 0
            Согласен, не 100%, не учел авторизацию без сетевого подключения к домену.
        • 0
          А я на контроллере домена ищу событие «4624, An account was successfully logged on».
          На каждом КД анализировать лог на предмет этого события
          Просмотр логов 2-х КД для 3 тсч. пользователей гораздо дольше, чем глянуть в отчёте SCCM’а.
  • 0
    Спасибо, очень интересно
  • 0
    И вручную коррелировать тонны строчек?)

    Это очень круто, что можно аккуратно собрать такое на коленке, но ведь есть очень удобные готовые решения, вроде GFI EventsManager (обращайтесь за ключиками, если вдруг), например, которые и централизуют, и выполняют резервное копирование, и фильтрацию, и автоматическую отчетность с графиками по любым срезам, попутно проверяя параметры систем, что выйдет дешевле уже исходя из пересчета на человеко-часы, а также исключит человеческий фактор.

    + как Вы будете это в отчет для ИБ включать?
    • 0
      За наводку на программу спасибо.
      + как Вы будете это в отчет для ИБ включать?
      Если я правильно понял вопрос, это уже и есть готовый отчёт для ИБ.
    • +1
      За «GFI EventsManager» редакция «Active Monitoring» вот такая сумма у меня вышла 3 312 000 р. :-) Тогда уж лучше какое-нибудь DLP-решение купить.
  • 0
    Не используйте Logparser на =<2008 серверах, используйте штатный wevtutil. Выгружает события намного быстрее.
    • 0
      Я запрос к Logparser’у делаю прямо в VBScript’е и результат выгружаю в html-файл.
      Set oLogQuery = CreateObject("MSUtil.LogQuery")
      
      Set oEVTInputFormat = CreateObject("MSUtil.LogQuery.EventLogInputFormat")
      oEVTInputFormat.direction = "BW"
      
      Set oTPLOutputFormat = CreateObject("MSUtil.LogQuery.TemplateOutputFormat")
      oTPLOutputFormat.tpl = "c:\script\Tamplates\logonFailuresStats.tpl"
      
      strQuery = "SELECT TO_LOWERCASE(EXTRACT_TOKEN(Strings,5,'|')) AS User, " & _
      	"COUNT(*) AS Total " & _
      "INTO " & lsReport & " " & _
      "FROM " & lsFROM & " " & _
      "WHERE EventID IN (4625) " & _
      "AND TimeGenerated >= TO_TIMESTAMP('" & gsPastDate & "','dd.MM.yyyy hh:mm:ss') " & _
      "GROUP BY User " & _
      "ORDER BY Total DESC"
          
      oLogQuery.ExecuteBatch strQuery, oEVTInputFormat, oTPLOutputFormat 
      
      И в скрипте же отсылаю результат.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.