Отчёт о запуске программ на компьютерах пользователей

    Всем привет!
    Отчёт о том, что запускает пользователь на своём компьютере, крайне важен. С многих точек зрения. Особенно с точки зрения информационной безопасности.
    Информация о запуске программ на компьютерах пользователей храниться в журнале безопасности. Конечно, рассматривается среда Windows. В Инете готового решения не нашёл, поэтому сделал свою реализацию.
    Скрипт запускается на сервере. На выходе имеем набор файлов с отчётами о запуске программ.
    Картинка для привлечения внимания:


    Основная идея такая. Текущие события журнала безопасности сохраняются в evt-файле на клиентском компьютере. Файл копируется на сервер, где информация из него загружается на SQL Server. Затем SQL-запросом формируется отчёт и сохраняется в файл.
    Теперь, как это реализовано.
    Необходимо создать папки Log, Logs, CheckComps, Logi_ForReports и Computer. У меня папки на диске F. В папке Log создать файл list.txt со списком компьютеров, которые необходимо проверить. Каждое имя компьютера с новой строки. Я создал 2 файла list.txt и list7.txt для XP и семёрок соответственно. В папке Computer создать файл is_computer_online_listComps.vbs
    Содержимое файла is_computer_online_listComps.vbs:
    on error resume next
    
    dim gsFileName
    dim gsRunCmd
    dim gix
    dim giy
    dim giz
    
    if Wscript.Arguments.Count = 1 then
    	gsFileName = Wscript.Arguments(0)
    	gsOS = "XP"
    elseif Wscript.Arguments.Count = 2 then
    	gsFileName = Wscript.Arguments(0)
    	gsOS = Wscript.Arguments(1)
    else
    	gsFileName = InputBox("Файл со списком компьютеров", "Ввод", "F:\Log\list.txt")
    	gsOS = InputBox("Тип операционной системы:" & VBNewLine & "'XP' - для Windows 2000/XP" & VBNewLine & "'7' - для Windows 7", "Ввод", "XP")
    end if
    
    gsOS = uCase(gsOS)
    wscript.echo "gsOS: " & gsOS
    if inStr(gsOS, "XP") = 0 and inStr(gsOS, "7") = 0 then
    	MsgBox "Некорректно указан тип операционной системы!", vbInformation, "Внимание"
    	Wscript.Quit
    end if
    
    WScript.Echo "Файл со списком компьютеров: " & gsFileName
    
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFileOpen = objFSO.OpenTextFile(gsFileName, 1)
    
    gix = 0
    giy = 0
    
    Set WshShell = CreateObject("WScript.Shell")
    
    do until objTextFileOpen.AtEndOfStream
    	gsComputerName = objTextFileOpen.Readline
    	giy = giy + 1
    loop 
    
    objTextFileOpen.Close
    
    wscript.echo "Найдено компьютеров: " & giy
    
    Set objTextFileOpen = objFSO.OpenTextFile(gsFileName, 1)
    
    do until objTextFileOpen.AtEndOfStream
    	gsComputerName = objTextFileOpen.Readline
    	gix = gix + 1
    	giz = gix * 100
    	giz = giz / giy
    	giz = Round(giz, 1)
    	giOst = giy - gix
    	
    	if fuPing(gsComputerName) then
    		wscript.echo gsComputerName & VBTab & " осталось: " & giOst & ", готово: " & giz & "%"
    		
    		if inStr(gsOS, "XP") then
    			gsRunCmd = "f:\Computer\is_computer_online.bat " & gsComputerName & " y"
    		elseif inStr(gsOS, "7") then
    			gsRunCmd = "f:\Computer\is_computer_online7.bat " & gsComputerName & " y"
    		end if
    		WshShell.Run gsRunCmd
    		if giOst <> 0 then
    			WScript.Sleep 180000 ' Внимание! Вот это задержка в 180 секунд между компьютерами.
    		end if
    	else
    		wscript.echo gsComputerName & VBTab & " осталось: " & giOst & ", готово: " & giz & "%. Выключен."
    	end if
    loop 
    
    objTextFileOpen.Close
    
    WScript.Echo "Операция завершена!"
    
    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
    			'WScript.Echo "* Компьютер " & NetworkDevice & " в сети!" 
    			lBoo = true
    		else
    			'WScript.Echo "* Компьютера нет в сети."
    		end if
    	next
    	
    	fuPing = lBoo
    end function 
    


    Запускается процедура проверки bat-файлом. Ссылку на него можно сделать, например, на рабочем столе.
    bat-файл
    cscript //nologo "f:\Computer\is_computer_online_listComps.vbs" %1 %2
    


    Основной скрипт is_computer_online_listComps.vbs читает список компьютеров из текстового файла и для каждого запускает bat-файл формирования отчёта. Для XP — это файл is_computer_online.bat, для 7 — is_computer_online7.bat.
    Примечание.
    На сервере нужно установить logparser.
    Всё описанное должно заработать и на компьютере администратора. Только надо установить Microsoft SQL SERVER 2008 NATIVE CLIENT и Microsoft SQL Server 2008 Command Line Utilities. Но я не проверял.

    Блок работы с компьютерами XP


    Bat-файл:
    is_computer_online.bat
    cscript //nologo "f:\Computer\is_computer_online.vbs" %1 %2
    


    Bat-файл запускает скрипт. Скрипт выполняет сохранение событий журнала безопасности в evt-файл и запускает основной батник mo2csv.bat.
    is_computer_online.vbs
    on error resume next
    
    dim gsComputerName
    dim gsUseLogFile
    dim gsLogFilename
    dim gbFlag
    
    dim gsTableName
    dim gsCompName
    dim gsRunCmd
    
    if Wscript.Arguments.Count = 1 then
    	gsComputerName = Wscript.Arguments(0)
    	gsUseLogFile = "n"
    elseif Wscript.Arguments.Count = 2 then
    	gsComputerName = Wscript.Arguments(0)
    	gsUseLogFile = Wscript.Arguments(1)
    else
    	gsComputerName = InputBox("Имя компьютера", "Введите", "")
    	gsUseLogFile = InputBox("Использовать log-файл для проверки?" & VBNewline & "[y/n]", "Введите", "y")
    end if
    
    WScript.Echo "* Имя компьютера " & gsComputerName
    gsLogFilename = "f:\Log\" & gsComputerName & ".log"
    
    if lCase(gsUseLogFile) = "y" then
    	gbFlag = false
    	WScript.Echo "* Файл журнала " & gsLogFilename
    	set objFSO = CreateObject("Scripting.FileSystemObject")
    
    	if not objFSO.FileExists(gsLogFilename) then
    		WScript.Echo "* Файла журнала нет. Создается..."
    		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 8, True)
    		objTextFileWriteLog.writeLine "n"
    		objTextFileWriteLog.close
    		WScript.Echo "* Создан успешно."
    	end if
    
    	set objTextFileOpen = objFSO.OpenTextFile(gsLogFilename, 1)
    	do until objTextFileOpen.AtEndOfStream
    		record = trim(objTextFileOpen.Readline)
    		if record = "n" then
    			WScript.Echo "* Компьютер не проверялся ранее."
    			if fuPing(gsComputerName) then
    				gbFlag = true
    				
    				if fuBackup(gsComputerName) then
    					WScript.Sleep 15000 ' <- 15 секунд задержки для бекапа
    					fuUploadEvents gsComputerName
    					wscript.sleep 10000
    				end if
    			end if
    		elseif record = "y" then
    			WScript.Echo "* Информация с компьютера " & gsComputerName & " уже закачана на сервер."
    		else 
    			WScript.Echo "* Некорректная информация о компьютере " & gsComputerName & " в log-файле."
    		end if
    	loop
    
    	objTextFileOpen.close
    
    	if gbFlag then
    		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 2, True)
    		objTextFileWriteLog.writeLine "y"
    		objTextFileWriteLog.close
    		WScript.Echo "* Информация записана в журнал."
    	end if
    else
    	'if fuPing(gsComputerName) then
    		if fuBackup(gsComputerName) then
    			WScript.Sleep 15000 ' <- 15 секунд задержки для бекапа
    			fuUploadEvents gsComputerName
    			wscript.sleep 10000
    		end if
    	'end if
    end if
    
    wscript.sleep 1000
    
    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
    			WScript.Echo "* Компьютер " & NetworkDevice & " в сети!" 
    			lBoo = true
    		else
    			WScript.Echo "* Компьютера нет в сети."
    		end if
    	next
    	
    	fuPing = lBoo
    end function 
    
    function fuBackup(lsComputername)
    	lsEvtBackupFilename = "c:\" & lsComputername & ".evt"
    	lsEvtBackupFilenameRemote = "\\" & lsComputername & "\c$\" & lsComputername & ".evt"
    	lbFlag = false
    	
    	set lObjFSO = CreateObject("Scripting.FileSystemObject")
    	
    	if lObjFSO.FileExists(lsEvtBackupFilenameRemote) then
    		WScript.Echo "* Файл журнала уже есть. Используем существующий..."
    		lbFlag = true
    	else
    		Wscript.Echo "* Выполняется резервное копирование..."
    		Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate,(Backup)}!\\" & lsComputername & "\root\cimv2")
    		Set colLogFiles = objWMIService.ExecQuery ("Select * from Win32_NTEventLogFile where LogFileName='Security'")
    
    		For Each objLogfile in colLogFiles
    			errBackupLog = objLogFile.BackupEventLog(lsEvtBackupFilename)
    			If errBackupLog = 0 Then        
    				Wscript.Echo "* Резервное копирование выполнено успешно."
    				lbFlag = true
    			Else
    				Wscript.Echo "* Резервное копирование не выполнено."
    			End If
    		Next
    	end if
    	
    	fuBackup = lbFlag
    end function 
    
    function fuUploadEvents(lsComputername)
    	WScript.Echo "* Запущена закачка на сервер..."
    	gsCompName = lCase(lsComputername)
    
    	gsTableName = fuGetTableName(gsCompName)
    	gsTableName = uCase(gsTableName)
    	gsOutputFilename = "f:\Computer\" & gsCompName & ".csv"
    	gsOutputFilenameSQL = "f:\Computer\" & gsCompName & "_sql.csv"
    
    	Set WshShell = CreateObject("WScript.Shell")
    	gsRunCmd = "f:\Computer\mo2csv.bat " & gsCompName & " " & gsOutputFilename & " " & gsOutputFilenameSQL & " " & gsTableName
    
    	WScript.Echo "* Выполняется команда: '" & gsRunCmd & "'"
    	WshShell.Run gsRunCmd
    end function 
    
    function fuGetTableName(lsCompName)
    	lsTmp = lsCompName
    	
    	if InStr(lsTmp, "-") then
    		lsTmp = Replace(lsTmp, "-", "_")
    	end if
    	
    	fuGetTableName = lsTmp
    end function 
    


    mo2csv.bat делает следующее:
    • Забирает evt-файл с удалённого компьютера на сервер.
    • Преобразовывает evt-файл в evtx.
    • Выгружает только события запуска/остановки программ из evtx-файла в текстовый файл csv.
    • Информацию из текстового файла закачивает на SQL Server.
    • Бекапит оригинальный evt-файл в папку Logi_ForReports (вдруг пользователь сотрёт свой журнал, а у нас копия есть).
    • Удаляет временный evtx-файл.
    • Формирует и выполняет sql-запрос к SQL Server’у.
    • Удаляет временные файлы (в случае отладки или для изучения работы скрипта, этот раздел можно закомментировать).
    • Перемещает отчёты в папку CheckComps.

    mo2csv.bat
    @echo off
    
    @set WDate=%date:~-10%
    
    @echo * Журнал безопасности перемещается с удаленного компьютера %1 (Windows XP)...
    move \\%1\c$\%1.evt f:\Logs\
    @echo * Перемещение завершено.
    
    @echo * Выполняется конвертация evt журнала в evtx...
    wevtutil epl f:\Logs\%1.evt f:\Logs\%1.evtx /lf:true
    @echo * Конвертация завершена.
    
    @echo * Информация из журнала безопасности выгружается в текстовый файл. Источник: f:\Logs\%1.evtx, назначение: %2
    LogParser.exe file:"f:\Computer\get_info_from_log.sql"?source=f:\Logs\%1.evtx+output_file=%2 -i:EVT -o:TSV -headers:ON -oSeparator:tab -oTsFormat:"dd.MM.yyyy hh:mm:ss" -fileMode:1
    @echo * Выгрузка завершена.
    
    @echo * Исправляю текстовый файл %2. Получаю %3...
    cscript F:\Computer\update_csvFile_forSQLCheck.vbs %2 %3 //NoLogo
    @echo * Исправление завершено.
    
    
    @echo * Информация из текстового файла закачивается на SQL Server. Источник: %3, таблица %4...
    LogParser.exe file:"f:\Computer\get_info_from_log_2SQL.sql"?source=%3+output_file=%4 -i:TSV -headerRow:ON -iSeparator:tab -iTsFormat:"dd.MM.yyyy hh:mm:ss" -o:SQL -server:"SQL-SRV\SEC" -database:quickly -driver:"SQL Server" -createTable:ON
    @echo * Процесс завершен.
    
    
    @echo * Журнал безопасности перемещается в архив...
    move f:\Logs\%1.evt f:\Logi_ForReports\%1_%WDate%_sec.evt
    @echo * Перемещение завершено. Имя архивного файла 'f:\Logi_ForReports\%1_%WDate%_sec.evt'
    
    @echo * Удаление временного evtx журнала...
    del f:\Logs\%1.evtx
    @echo * Удаление завершено.
    
    
    @echo * Создание sql-запроса...
    cscript "F:\Computer\create_SQL_full.vbs" %1 1 //nologo
    @echo * Создание завершено.
    
    @echo * Выполнение sql-запроса...
    SQLCMD.EXE -S SQL-SRV\SEC -d quickly -E -i f:\Computer\%1-1.sql -o "f:\Computer\%1. Запуск программ.csv" -W -R -s ";" -w 4000
    @echo * Выполнение завершено.
    
    @echo * Исправляю результирующие файлы отчетов...
    cscript F:\Computer\update_result_file.vbs "f:\Computer\%1. Запуск программ.csv" //nologo
    @echo * Исправление завершено.
    
    @echo * Удаление временных файлов...
    del f:\Computer\%1-1.sql
    del %2
    del %3
    del f:\Computer\%1_dbg.txt
    del "f:\Computer\%1. Запуск программ.csv"
    @echo * Удаление завершено.
    
    @echo * Перемещение файлов-отчетов...
    move "f:\Computer\%1. Запуск программ.xls" "f:\CheckComps\%1. Запуск программ.xls"
    @echo * Перемещение завершено.
    
    @echo on
    
    Примечание
    Возможно, в батнике нужно будет SQLCMD.EXE заменить на «c:\Program Files\Microsoft SQL Server\100\Tools\Binn\SQLCMD.EXE», а LogParser.exe на «c:\Program Files (x86)\Log Parser 2.2\LogParser.exe» (или «c:\Program Files\Log Parser 2.2\LogParser.exe»).
    Имя сервера с SQL Server’ом SQL-SRV, имя экземпляра SEC и имя базы quickly. Заменить на свои.

    get_info_from_log.sql
    SELECT RecordNumber as id, 
    	eventid as eId, 
    	TimeGenerated as Tg, 
    	resolve_sid(sid) as UserName, 
    	computername as Computer, 
    	EXTRACT_TOKEN(Strings, 0, '|') as image_unique_id,
    	EXTRACT_TOKEN(Strings, 1, '|') as image
    into %output_file% 
    FROM %source%
    where ((EventID in (592; 593))
    and (TO_UPPERCASE(resolve_sid(sid)) <> 'NT AUTHORITY\NETWORK SERVICE')
    and (TO_UPPERCASE(resolve_sid(sid)) <> 'NT AUTHORITY\SYSTEM'))
    and TimeGenerated >= TO_TIMESTAMP('01.03.2011 00:00:00','dd.MM.yyyy hh:mm:ss')
    order by recordnumber asc
    


    get_info_from_log_2SQL.sql
    SELECT *
    into %output_file% 
    FROM %source%
    


    update_csvFile_forSQLCheck.vbs
    On Error Resume Next
    
    dim gsSimbolSplitFields
    dim sgSimbolSplitAdmin
    dim gbInsideBlock
    dim gIx
    dim gbDebug
    dim gbWriteString
    
    Dim gArrBlock_admin
    
    gsSimbolSplitFields = vbTab
    sgSimbolSplitAdmin = ";"
    gbInsideBlock = false
    gbIERuning = false
    gbIE = false
    giBlockPlus = 0
    giIEPlus = 0
    gsDateBlock = "01.01.2011 00:00:00"
    TgBlockStop = "01.01.2011 00:00:00"
    idBlockStop = ""
    gIx = 0
    gArrBlock_admin = Array (sgSimbolSplitAdmin & sgSimbolSplitAdmin, _
    	sgSimbolSplitAdmin & sgSimbolSplitAdmin, _
    	sgSimbolSplitAdmin & sgSimbolSplitAdmin, _
    	sgSimbolSplitAdmin & sgSimbolSplitAdmin)
    gbDebug = true
    'gbDebug = false
    
    if Wscript.Arguments.Count = 1 then
    	sgFilename = Wscript.Arguments(0)
    	sgFilenameOut = fuRemoveExtention(sgFilename) & "_sql.csv"
    	gsLogFilename = fuRemoveExtention(sgFilename) & "_dbg.txt"
    elseif Wscript.Arguments.Count = 2 then
    	sgFilename = Wscript.Arguments(0)
    	sgFilenameOut = Wscript.Arguments(1)
    	gsLogFilename = fuRemoveExtention(sgFilename) & "_dbg.txt"
    elseif Wscript.Arguments.Count = 3 then
    	sgFilename = Wscript.Arguments(0)
    	sgFilenameOut = Wscript.Arguments(1)
    	gsLogFilename = Wscript.Arguments(2)
    else
    	sgFilename = InputBox("Имя исходного файла", "Введите", "f:\comp-6475.csv")
    	sgFilenameOut = InputBox("Имя результирующего файла", "Введите", fuRemoveExtention(sgFilename) & "_sql.csv")
    	gsLogFilename = InputBox("Имя файла журнала", "Введите", fuRemoveExtention(sgFilename) & "_dbg.txt")
    end if
    
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    
    if not objFSO.FileExists(sgFilename) then
    	wscript.echo "Исходного файла для обновления нет, выхожу!"
    	Wscript.Quit
    end if
    
    Set objTextFileOpen = objFSO.OpenTextFile(sgFilename, 1)
    Set objTextFileWrite = objFSO.CreateTextFile(sgFilenameOut, True)
    
    if gbDebug then
    	if not objFSO.FileExists(gsLogFilename) then
    		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 8, True)
    	else
    		set objTextFileWriteLog = objFSO.CreateTextFile(gsLogFilename, True)
    	end if
    end if
    	
    Do Until objTextFileOpen.AtEndOfStream
    	record = trim(objTextFileOpen.Readline)
    	gIx = gIx + 1
    	gbWriteString = true
    	fuPrint gIx & ". '" & record & "'"
    	
    	if InStr(record, gsSimbolSplitFields) then
    		arr = Split(record, gsSimbolSplitFields)
    		id = arr(0)
    		eId = arr(1)
    		Tg = arr(2)
    		UserName = arr(3)
    		Computer = arr(4)
    		image_unique_id = arr(5)
    		image = arr(6)
    		
    		if InStr(lCase(image), "explorer.exe") then
    			if eId = "592" then
    				gbBlockBegin = true
    				gbBlockEnd = false
    				giBlockPlus = giBlockPlus + 1
    				fuPrint "explorer.exe старт"
    			else 
    				gbBlockBegin = false
    				gbBlockEnd = true
    				giBlockPlus = giBlockPlus - 1
    				if giBlockPlus < 0 then
    					giBlockPlus = 0
    				end if
    				fuPrint	"explorer.exe стоп"
    			end if
    		else
    			gbBlockBegin = false
    			gbBlockEnd = false
    		end if
    		
    		if InStr(lCase(image), "iexplore.exe") then
    			gbIE = true
    			fuPrint	"Строка с iexplore.exe"
    			
    			if eId = "592" then
    				fuPrint	"iexplore.exe старт"
    				giIEPlus = giIEPlus + 1
    				gbIERuning = true
    				
    				if giIEPlus = 1 then
    					image_unique_idIEStart = image_unique_id
    				end if
    			else 
    				fuPrint	"iexplore.exe стоп"
    				giIEPlus = giIEPlus - 1
    				gbIERuning = false
    			end if
    		else 
    			gbIE = false
    			fuPrint	"Строка без iexplore.exe"
    		end if
    		
    		if gIx = 1 then
    			objTextFileWrite.WriteLine record & gsSimbolSplitFields & "CompStart"
    			fuPrint "Первая строка, записываем"
    		elseif gIx = 2 then
    			fuPrint "вторая строка"
    			if gbBlockBegin then
    				fuPrint "Начало блока, записываем"
    				gbInsideBlock = true
    				gsDateBlock = Tg
    				gsUserNameBlockStart = UserName
    				image_unique_idBlockStart = image_unique_id
    				objTextFileWrite.WriteLine record & gsSimbolSplitFields & gsDateBlock
    			end if
    			
    			idPrev = id
    			eIdPrev = eId
    			TgPrev = Tg
    			UserNamePrev = UserName
    			ComputerPrev = Computer
    			image_unique_idPrev = image_unique_id
    			imagePrev = image	
    		else
    			'fuPrint "остальные строки"
    			
    			'-- Запуск explorer.exe
    			if gbBlockBegin then
    				fuPrint "Запуск explorer.exe (экземпляр № " & giBlockPlus & ")"
    				
    				if giBlockPlus = 1 then
    					giDiff = DateDiff("s", CDate(TgBlockStop), CDate(Tg))
    					if giDiff > 9 then
    						if Len(idBlockStop) > 0 then
    							fuPrint "Это не перезапуск explorer.exe. Записываем остановку предыдущего блока"
    							record_convert_prev = idBlockStop & gsSimbolSplitFields & _
    									eIdBlockStop & gsSimbolSplitFields & _
    									TgBlockStop & gsSimbolSplitFields & _
    									UserNameBlockStop & gsSimbolSplitFields & _
    									ComputerBlockStop & gsSimbolSplitFields & _
    									image_unique_idBlockStart & gsSimbolSplitFields & _
    									imageBlockStop & gsSimbolSplitFields & _
    									gsDateBlock
    							fuPrint record_convert_prev
    							objTextFileWrite.WriteLine record_convert_prev
    						end if
    						
    						gsDateBlock = Tg
    						fuPrint "Новая дата блока: '" & gsDateBlock & "'"
    						image_unique_idBlockStart = image_unique_id
    						fuPrint "Новый код блока: '" & image_unique_idBlockStart & "'"
    						gsUserNameBlockStart = UserName
    						fuPrint "Новый пользователь блока: '" & gsUserNameBlockStart & "'"
    					else
    						fuPrint "Это перезапуск explorer.exe! Не записываем остановку предыдущего блока и не записываем запуск этого."
    						gbWriteString = false
    					end if
    					
    					gbInsideBlock = true
    				else
    					if lCase(gsUserNameBlockStart) = lCase(UserName) then
    						fuPrint "gsUserNameBlockStart: '" & gsUserNameBlockStart & "', UserName: '" & UserName & "'"
    						fuPrint "Начало блока. Возможно, компьютер был аварийно выключен. Необходимо записать окончание предыдущего блока и сохранить новые параметры блока"
    						record_convert_prev = "999" & gsSimbolSplitFields & _
    								"593" & gsSimbolSplitFields & _
    								Tg & gsSimbolSplitFields & _
    								UserName & gsSimbolSplitFields & _
    								Computer & gsSimbolSplitFields & _
    								image_unique_idBlockStart & gsSimbolSplitFields & _
    								"C:\WINDOWS\explorer.exe" & gsSimbolSplitFields & _
    								gsDateBlock
    						fuPrint record_convert_prev
    						objTextFileWrite.WriteLine record_convert_prev
    						
    						giBlockPlus = 1
    						
    						gsDateBlock = Tg
    						fuPrint "Новая дата блока: '" & gsDateBlock & "'"
    						image_unique_idBlockStart = image_unique_id
    						fuPrint "Новый код блока: '" & image_unique_idBlockStart & "'"
    					else
    						'fuPrint "gsUserNameBlockStart: '" & gsUserNameBlockStart & "', UserName: '" & UserName & "'"
    						fuPrint "Не начало блока. Возможно, администратор запустил explorer. Записать текущую строку и сохранить параметры"
    						gArrBlock_admin(giBlockPlus-2) = image_unique_id & sgSimbolSplitAdmin & UserName & sgSimbolSplitAdmin & Tg
    						fuPrint gArrBlock_admin(giBlockPlus-2)
    						'gsDateBlock_admin = Tg
    						objTextFileWrite.WriteLine record & gsSimbolSplitFields & Tg
    						gbWriteString = false
    					end if
    				end if
    			end if
    			
    			'-- Остановка explorer.exe
    			if gbBlockEnd then
    				fuPrint "Остановлен explorer.exe (осталось экземпляров " & giBlockPlus & ")"
    				
    				if giBlockPlus = 0 then
    					fuPrint "Остановлен последний экземпляр, сохраняем его значения"
    					
    					idBlockStop = id
    					eIdBlockStop = eId
    					TgBlockStop = Tg
    					UserNameBlockStop = UserName
    					ComputerBlockStop = Computer
    					image_unique_idBlockStop = image_unique_id
    					imageBlockStop = image
    					
    					gbInsideBlock = false
    					giIEPlus = 0 ' <-- Добавил для обнуления количества копий IE
    				else
    					fuPrint "Остановлен не последний экземпляр, его значения не сохраняем, только записываем текущую строку"		
    					for giY = 0 to UBound(gArrBlock_admin)
    						arrA = Split(gArrBlock_admin(giY), sgSimbolSplitAdmin)
    						gsImage_unique_id_A = arrA(0)
    						gsUserName_A = arrA(1)
    						gsTg_A = arrA(2)
    						
    						if gsImage_unique_id_A = image_unique_id then
    							gsDateBlock_admin = gsTg_A
    						end if
    					next
    					objTextFileWrite.WriteLine record & gsSimbolSplitFields & gsDateBlock_admin
    					gbWriteString = false
    				end if
    			end if
    			
    			'-- Записать текущую строку 
    			if gbInsideBlock then
    				if gbIE then
    					if (((gbIERuning) and (giIEPlus = 1)) or ((not gbIERuning) and (giIEPlus = 0))) then
    						fuPrint "Записать IE строку"
    						record_convert_prev = id & gsSimbolSplitFields & _
    							eId & gsSimbolSplitFields & _
    							Tg & gsSimbolSplitFields & _
    							UserName & gsSimbolSplitFields & _
    							Computer & gsSimbolSplitFields & _
    							image_unique_idIEStart & gsSimbolSplitFields & _
    							image & gsSimbolSplitFields & _
    							gsDateBlock
    						fuPrint record_convert_prev
    						objTextFileWrite.WriteLine record_convert_prev
    					else
    						fuPrint "Вот хрень с IE! gbIERuning: " & gbIERuning & ", giIEPlus: " & giIEPlus
    						record_convert_prev = id & gsSimbolSplitFields & _
    							eId & gsSimbolSplitFields & _
    							Tg & gsSimbolSplitFields & _
    							UserName & gsSimbolSplitFields & _
    							Computer & gsSimbolSplitFields & _
    							image_unique_idIEStart & gsSimbolSplitFields & _
    							image & gsSimbolSplitFields & _
    							gsDateBlock
    						fuPrint record_convert_prev
    					end if
    				else
    					if gbWriteString then
    						fuPrint "Текущая строка в блоке и ее нужно записать"
    						fuPrint record & gsSimbolSplitFields & gsDateBlock
    						objTextFileWrite.WriteLine record & gsSimbolSplitFields & gsDateBlock
    					else
    						fuPrint "Текущая строка в блоке, но ее записывать не нужно"
    					end if
    				end if
    			else
    				fuPrint "Текущая строка не в блоке. Не записываем"
    			end if
    
    			'-- Сохранить текущие значения параметров для следующего прохода 
    			idPrev = id
    			eIdPrev = eId
    			TgPrev = Tg
    			UserNamePrev = UserName
    			ComputerPrev = Computer
    			image_unique_idPrev = image_unique_id
    			imagePrev = image
    		end if
    	end if
    	fuPrint "----------------------------------------------"
    Loop
    
    objTextFileWrite.Close
    objTextFileOpen.Close
    
    if gbDebug then
    	objTextFileWriteLog.close
    end if
    
    WScript.Echo ""
    WScript.Echo "* Операция успешно завершена." 
    
    
    function fuRemoveExtention(lsFilename)
    	lRes = lsFilename
    	if InStr(lsFilename, ".") then
    		lRes = Left(lsFilename, Len(lsFilename)-4)
    	end if
    	fuRemoveExtention = lRes
    end function 
    
    function fuGetDateFromFullDate(lsFullDate)
    	lRes = lsFullDate
    	if InStr(lsFullDate, " ") then
    		lArr = Split(lsFullDate, " ")
    		lsDate = lArr(0)
    		lsTime = lArr(1)
    		lRes = lsDate
    	end if
    	fuGetDateFromFullDate = lRes
    end function 
    
    function fuGetTimeFromFullDate(lsFullDate)
    	lRes = lsFullDate
    	if InStr(lsFullDate, " ") then
    		lArr = Split(lsFullDate, " ")
    		lsDate = lArr(0)
    		lsTime = lArr(1)
    		lRes = lsTime
    	end if
    	fuGetDateFromFullDate = lRes
    end function 
    
    function fuPrint(lsStr)
    	'if gbDebug then
    	'	wscript.echo lsStr
    	'end if
    	
    	if gbDebug then
    		objTextFileWriteLog.writeLine lsStr
    	end if
    	
    	fuPrint = true
    end function 
    


    create_SQL_full.vbs
    if Wscript.Arguments.Count = 1 then
    	gsComputerName = Wscript.Arguments(0)
    	gsSQLtype = "1"
    elseif Wscript.Arguments.Count = 2 then
    	gsComputerName = Wscript.Arguments(0)
    	gsSQLtype = Wscript.Arguments(1)
    else
    	gsComputerName = InputBox("Имя компьютера", "Введите", "")
    	gsSQLtype = InputBox("Тип sql-запроса?" & VBNewline & "[1 - короткий, 2 - полный, 3 - оба]", "Введите", "1")
    end if
    
    set objFSO = CreateObject("Scripting.FileSystemObject")
    
    if gsSQLtype = "1" then
    	fuCreateSQLFile gsComputerName, "1"
    elseif gsSQLtype = "2" then
    	fuCreateSQLFile gsComputerName, "2"
    elseif gsSQLtype = "3" then
    	fuCreateSQLFile gsComputerName, "1"
    	fuCreateSQLFile gsComputerName, "2"
    end if
    
    function fuGetTableName(lsCompName)
    	lsTmp = lsCompName
    	
    	if InStr(lsTmp, "-") then
    		lsTmp = Replace(lsTmp, "-", "_")
    	end if
    	
    	fuGetTableName = lsTmp
    end function 
    
    sub fuCreateSQLFile(lsComputerName, lsSQLtype)
    	if lsSQLtype = "1" then
    		lsTemplateFilename = "f:\Computer\template-short.sql"
    	elseif gsSQLtype = "2" then
    		lsTemplateFilename = "f:\Computer\template-full.sql"
    	end if
    	
    	lsLogFilename = "f:\Computer\" & lsComputerName & "-" & lsSQLtype & ".sql"
    	lsTableName = fuGetTableName(lsComputerName)
    
    	if not objFSO.FileExists(lsLogFilename) then
    		set objTextFileWriteLog = objFSO.OpenTextFile(lsLogFilename, 8, True)
    	else
    		set objTextFileWriteLog = objFSO.OpenTextFile(lsLogFilename, 2, True)
    	end if
    	
    	Set objTextFileOpen = objFSO.OpenTextFile(lsTemplateFilename, 1)
    	
    	do until objTextFileOpen.AtEndOfStream
    		record = objTextFileOpen.Readline
    		if InStr(record, "WARNING__TABLE_NAME_FOR_CHANGE") then
    			record = Replace(record, "WARNING__TABLE_NAME_FOR_CHANGE", lsTablename)
    		end if
    		objTextFileWriteLog.writeLine record
    	loop 
    
    	objTextFileOpen.Close
    	objTextFileWriteLog.Close
    end sub 
    


    template-short.sql
    SELECT     TOP (100) PERCENT Computer AS [Имя компьютера], UserName AS [Учетная запись], image AS Программа, start_time AS [Время запуска], 
                          stop_time AS [Время завершения], dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) / 3600)) 
                          + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 / 60)) 
                          + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 % 60)) AS Длительность
    FROM         (SELECT     r.Computer, r.UserName, r.image_unique_id, r.image, r.Tg AS start_time, MIN(s.Tg) AS stop_time
                           FROM          (SELECT     id, eId, Tg, UserName, Computer, image_unique_id, image
                                                   FROM          dbo.WARNING__TABLE_NAME_FOR_CHANGE
                                                   WHERE      (eId = 592) AND (Tg > CONVERT(DATETIME, '2013-01-01 00:00:00.000', 102))
                                                   ) AS r INNER JOIN
                                                      (SELECT     id, eId, Tg, UserName, Computer, image_unique_id, image
                                                        FROM          dbo.WARNING__TABLE_NAME_FOR_CHANGE
                                                        WHERE      (eId = 593)) AS s ON r.image_unique_id = s.image_unique_id AND r.image = s.image AND r.id < s.id AND r.Tg <= s.Tg
                           GROUP BY r.UserName, r.Computer, r.image_unique_id, r.image, r.Tg) AS DERIVEDTBL
    ORDER BY 'Время запуска' DESC
    


    update_result_file.vbs
    if Wscript.Arguments.Count = 1 then
    	gsFileName = Wscript.Arguments(0)
    	gsFileNameRes = fuRemoveExtention(gsFileName) & ".xls"
    elseif Wscript.Arguments.Count = 2 then
    	gsFileName = Wscript.Arguments(0)
    	gsFileNameRes = Wscript.Arguments(1)
    else
    	gsFileName = InputBox("Файл для обновления", "Ввод", "")
    	gsFileNameRes = InputBox("Файл результата", "Ввод", fuRemoveExtention(gsFileName) & ".xls")
    end if
    
    sgSimbolSplit = ";"
    gsSimbolSplitFields = vbTab
    
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFileOpen = objFSO.OpenTextFile(gsFileName, 1)
    
    if not objFSO.FileExists(gsFileName) then
    	wscript.echo "Исходного файла для обновления нет, выхожу!"
    	objTextFileOpen.Close
    	Wscript.Quit
    end if
    
    if not objFSO.FileExists(gsFileNameRes) then
    	set objTextFileWriteRes = objFSO.OpenTextFile(gsFileNameRes, 8, True)
    else
    	set objTextFileWriteRes = objFSO.CreateTextFile(gsFileNameRes, True)
    end if
    
    do until objTextFileOpen.AtEndOfStream
    	record = objTextFileOpen.Readline
    	
    	if ((InStr(record, "--------")) or (Len(record) = 0) or (InStr(record, "обработано строк")) or (InStr(record, "rows affected"))) then
    		'wscript.echo "пропускаю строку: '" & record & "'"
    	else
    		if InStr(record, sgSimbolSplit) then
    			recordRes = Replace(record, sgSimbolSplit, gsSimbolSplitFields)
    		else
    			recordRes = record
    		end if
    		
    		objTextFileWriteRes.writeLine recordRes
    	end if
    loop 
    
    objTextFileWriteRes.Close
    objTextFileOpen.Close
    
    WScript.Echo "Обновление завершено! Результирующий файл " & gsFileNameRes
    
    function fuRemoveExtention(lsFilename)
    	lRes = lsFilename
    	if InStr(lsFilename, ".") then
    		lRes = Left(lsFilename, Len(lsFilename)-4)
    	end if
    	fuRemoveExtention = lRes
    end function 
    



    Блок работы с компьютерами с семёркой


    Bat-файл:
    is_computer_online7.bat
    cscript //nologo "f:\Computer\is_computer_online7.vbs" %1 %2
    


    Bat-файл запускает скрипт. Скрипт выполняет сохранение событий журнала безопасности в evt-файл и запускает основной батник mo7.bat.
    is_computer_online7.vbs
    on error resume next
    
    dim gsComputerName
    dim gsUseLogFile
    dim gsLogFilename
    dim gbFlag
    
    dim gsTableName
    dim gsCompName
    dim gsRunCmd
    
    
    if Wscript.Arguments.Count = 1 then
    	gsComputerName = Wscript.Arguments(0)
    	gsUseLogFile = "n"
    elseif Wscript.Arguments.Count = 2 then
    	gsComputerName = Wscript.Arguments(0)
    	gsUseLogFile = Wscript.Arguments(1)
    else
    	gsComputerName = InputBox("Имя компьютера", "Введите", "")
    	gsUseLogFile = InputBox("Использовать log-файл для проверки?" & VBNewline & "[y/n]", "Введите", "y")
    end if
    
    WScript.Echo "* Имя компьютера " & gsComputerName
    
    if lCase(gsUseLogFile) = "y" then
    	gbFlag = false
    	
    	gsLogFilename = "f:\Log\" & gsComputerName & ".log"
    
    	WScript.Echo "* Файл журнала " & gsLogFilename
    
    	set objFSO = CreateObject("Scripting.FileSystemObject")
    
    	if not objFSO.FileExists(gsLogFilename) then
    		WScript.Echo "* Файла журнала нет. Создается..."
    		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 8, True)
    		objTextFileWriteLog.writeLine "n"
    		objTextFileWriteLog.close
    		WScript.Echo "* Создан успешно."
    	end if
    
    	set objTextFileOpen = objFSO.OpenTextFile(gsLogFilename, 1)
    
    	do until objTextFileOpen.AtEndOfStream
    		record = trim(objTextFileOpen.Readline)
    		if record = "n" then
    			WScript.Echo "* Компьютер не проверялся ранее."
    			if fuPing(gsComputerName) then
    				'fuListInstalledSoftware gsComputerName
    				gbFlag = true
    				
    				if fuBackup(gsComputerName) then
    					WScript.Sleep 15000 ' <- 15 секунд задержки для бекапа
    					fuUploadEvents gsComputerName
    					wscript.sleep 10000
    				end if
    			end if
    		elseif record = "y" then
    			WScript.Echo "* Информация с компьютера " & gsComputerName & " уже закачана на сервер."
    		else 
    			WScript.Echo "* Некорректная информация о компьютере " & gsComputerName & " в log-файле."
    		end if
    	loop
    
    	objTextFileOpen.close
    
    	if gbFlag then
    		set objTextFileWriteLog = objFSO.OpenTextFile(gsLogFilename, 2, True)
    		objTextFileWriteLog.writeLine "y"
    		objTextFileWriteLog.close
    		WScript.Echo "* Информация записана в журнал."
    		
    		'MsgBox "Компьютер " & gsComputerName & " в сети!", vbInformation, "Внимание"
    	end if
    else
    	'if fuPing(gsComputerName) then
    		if fuBackup(gsComputerName) then
    			WScript.Sleep 15000 ' <- 15 секунд задержки для бекапа
    			fuUploadEvents gsComputerName
    			wscript.sleep 60000
    		end if
    	'end if
    end if
    
    wscript.sleep 1000
    
    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
    			WScript.Echo "* Компьютер " & NetworkDevice & " в сети!" 
    			lBoo = true
    		else
    			WScript.Echo "* Компьютера нет в сети."
    		end if
    	next
    	
    	fuPing = lBoo
    end function 
    
    function fuBackup(lsComputername)
    	lsEvtBackupFilename = "c:\" & lsComputername & ".evt"
    	lsEvtBackupFilenameRemote = "\\" & lsComputername & "\c$\" & lsComputername & ".evt"
    	lbFlag = false
    	
    	'WScript.Echo "* lsEvtBackupFilename: " & lsEvtBackupFilename
    	'WScript.Echo "* lsEvtBackupFilenameRemote: " & lsEvtBackupFilenameRemote
    	
    	set lObjFSO = CreateObject("Scripting.FileSystemObject")
    	
    	if lObjFSO.FileExists(lsEvtBackupFilenameRemote) then
    		WScript.Echo "* Файл журнала уже есть. Используем существующий..."
    		lbFlag = true
    	else
    		Wscript.Echo "* Выполняется резервное копирование..."
    		
    		Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate,(Backup)}!\\" & lsComputername & "\root\cimv2")
    
    		Set colLogFiles = objWMIService.ExecQuery ("Select * from Win32_NTEventLogFile where LogFileName='Security'")
    
    		For Each objLogfile in colLogFiles
    			errBackupLog = objLogFile.BackupEventLog(lsEvtBackupFilename)
    			If errBackupLog = 0 Then        
    				Wscript.Echo "* Резервное копирование выполнено успешно."
    				lbFlag = true
    			Else
    				Wscript.Echo "* Резервное копирование не выполнено."
    			End If
    		Next
    	end if
    	
    	fuBackup = lbFlag
    end function 
    
    function fuUploadEvents(lsComputername)
    	WScript.Echo "* Запущена закачка на сервер..."
    	gsCompName = lCase(lsComputername)
    
    	gsTableName = fuGetTableName(gsCompName)
    	gsTableName = uCase(gsTableName)
    
    
    	Set WshShell = CreateObject("WScript.Shell")
    
    	gsRunCmd = "f:\Computer\mo7.bat " & gsCompName & " " & gsTableName
    
    	WScript.Echo "* Выполняется команда: '" & gsRunCmd & "'"
    	WshShell.Run gsRunCmd
    end function 
    
    function fuGetTableName(lsCompName)
    	lsTmp = lsCompName
    	
    	if InStr(lsTmp, "-") then
    		lsTmp = Replace(lsTmp, "-", "_")
    	end if
    	
    	fuGetTableName = lsTmp
    end function  
    


    mo7.bat делает следующее:
    • Забирает evt-файл с удалённого компьютера на сервер.
    • Преобразовывает evt-файл в evtx.
    • Информацию из evtx-файла закачивает на SQL Server.
    • Бекапит оригинальный evt-файл в папку Logi_ForReports (вдруг пользователь сотрёт свой журнал, а у нас копия есть).
    • Удаляет временный evtx-файл.
    • Формирует и выполняет sql-запрос к SQL Server’у.
    • Удаляет временные файлы (в случае отладки или для изучения работы скрипта, этот раздел можно закомментировать).
    • Перемещает отчёты в папку CheckComps.

    mo7.bat
    @echo off
    
    @set WDate=%date:~-10%
    
    @echo * Журнал безопасности перемещается с удаленного компьютера %1 (Windows 7)...
    move \\%1\c$\%1.evt f:\Logs\
    @echo * Перемещение завершено.
    
    @echo * Выполняется конвертация evt журнала в evtx...
    wevtutil epl f:\Logs\%1.evt f:\Logs\%1.evtx /lf:true
    @echo * Конвертация завершена.
    
    @echo * Информация из журнала безопасности закачивается на сервер. Источник: f:\Logs\%1.evtx
    LogParser.exe file:"f:\Computer\get_info_from_log7.sql"?source=f:\Logs\%1.evtx+output_file=%2 -i:EVT -o:SQL -server:"SQL-SRV\SEC" -database:quickly -driver:"SQL Server" -createTable:ON
    @echo * Процесс завершен.
    
    @echo * Журнал безопасности перемещается в архив...
    move f:\Logs\%1.evt f:\Logi_ForReports\%1_%WDate%_sec.evt
    @echo * Перемещение завершено. Имя архивного файла 'f:\Logi_ForReports\%1_%WDate%_sec.evtx'
    
    
    @echo * Создание sql-запроса...
    cscript "F:\Computer\create_SQL_full7.vbs" %1 1 //nologo
    @echo * Создание завершено.
    
    @echo * Выполнение sql-запроса...
    SQLCMD.EXE -S SQL-SRV\SEC -d quickly -E -i f:\Computer\%1-1.sql -o "f:\Computer\%1. Запуск программ.csv" -W -R -s ";" -w 4000
    @echo * Выполнение завершено.
    
    @echo * Исправляю результирующие файлы отчетов...
    cscript F:\Computer\update_result_file7.vbs "f:\Computer\%1. Запуск программ.csv" //nologo
    @echo * Исправление завершено.
    
    @echo * Удаление временных файлов...
    del f:\Logs\%1.evtx
    del f:\Computer\%1-1.sql
    del "f:\Computer\%1. Запуск программ.csv"
    @echo * Удаление временных файлов завершено.
    
    @echo * Перемещение файлов-отчетов...
    move "f:\Computer\%1. Запуск программ.xls" "f:\CheckComps\%1. Запуск программ.xls"
    @echo * Перемещение завершено.
    
    @echo on
    
    Примечание
    Возможно, в батнике нужно будет SQLCMD.EXE заменить на «c:\Program Files\Microsoft SQL Server\100\Tools\Binn\SQLCMD.EXE», а LogParser.exe на «c:\Program Files (x86)\Log Parser 2.2\LogParser.exe» (или «c:\Program Files\Log Parser 2.2\LogParser.exe»).
    Имя сервера с SQL Server’ом SQL-SRV, имя экземпляра SEC и имя базы quickly. Заменить на свои.

    get_info_from_log7.sql
    SELECT RecordNumber as id, 
    	eventid as eId, 
    	TimeGenerated as Tg, 
    	--resolve_sid(sid) as UserName, 
    	EXTRACT_TOKEN(Strings, 1, '|') as UserName,
    	computername as Computer, 
    	EXTRACT_TOKEN(Strings, 4, '|') as image_id,
    	EXTRACT_TOKEN(Strings, 5, '|') as image,
    	EXTRACT_TOKEN(Strings, 6, '|') as name
    into %output_file% 
    FROM %source%
    where (EventID in (4688;4689)) and (
    (TO_UPPERCASE(resolve_sid(sid)) <> 'NT AUTHORITY\NETWORK SERVICE')
    and (TO_UPPERCASE(resolve_sid(sid)) <> 'NT AUTHORITY\SYSTEM'))
    and TimeGenerated >= TO_TIMESTAMP('01.01.2013 00:00:00','dd.MM.yyyy hh:mm:ss')
    order by recordnumber desc
    


    create_SQL_full7.vbs
    'on error resume next
    
    if Wscript.Arguments.Count = 1 then
    	gsComputerName = Wscript.Arguments(0)
    	gsSQLtype = "1"
    elseif Wscript.Arguments.Count = 2 then
    	gsComputerName = Wscript.Arguments(0)
    	gsSQLtype = Wscript.Arguments(1)
    else
    	gsComputerName = InputBox("Имя компьютера", "Введите", "")
    	gsSQLtype = InputBox("Тип sql-запроса?" & VBNewline & "[1 - короткий, 2 - полный, 3 - оба]", "Введите", "1")
    end if
    
    set objFSO = CreateObject("Scripting.FileSystemObject")
    
    if gsSQLtype = "1" then
    	fuCreateSQLFile gsComputerName, "1"
    elseif gsSQLtype = "2" then
    	fuCreateSQLFile gsComputerName, "2"
    elseif gsSQLtype = "3" then
    	fuCreateSQLFile gsComputerName, "1"
    	fuCreateSQLFile gsComputerName, "2"
    end if
    
    function fuGetTableName(lsCompName)
    	lsTmp = lsCompName
    	
    	if InStr(lsTmp, "-") then
    		lsTmp = Replace(lsTmp, "-", "_")
    	end if
    	
    	fuGetTableName = lsTmp
    end function 
    
    sub fuCreateSQLFile(lsComputerName, lsSQLtype)
    	if lsSQLtype = "1" then
    		lsTemplateFilename = "f:\Computer\template-short7.sql"
    	elseif gsSQLtype = "2" then
    		lsTemplateFilename = "f:\Computer\template-full7.sql"
    	end if
    	
    	lsLogFilename = "f:\Computer\" & lsComputerName & "-" & lsSQLtype & ".sql"
    	lsTableName = fuGetTableName(lsComputerName)
    
    	'WScript.Echo "* Имя компьютера " & lsComputerName
    	'WScript.Echo "* Имя таблицы " & lsTableName
    	'WScript.Echo "* Имя файла sql-запроса " & lsLogFilename
    
    	if not objFSO.FileExists(lsLogFilename) then
    		set objTextFileWriteLog = objFSO.OpenTextFile(lsLogFilename, 8, True)
    	else
    		set objTextFileWriteLog = objFSO.OpenTextFile(lsLogFilename, 2, True)
    	end if
    	
    	Set objTextFileOpen = objFSO.OpenTextFile(lsTemplateFilename, 1)
    	
    	do until objTextFileOpen.AtEndOfStream
    		record = objTextFileOpen.Readline
    		if InStr(record, "WARNING__TABLE_NAME_FOR_CHANGE") then
    			record = Replace(record, "WARNING__TABLE_NAME_FOR_CHANGE", lsTablename)
    		end if
    		objTextFileWriteLog.writeLine record
    	loop 
    
    	objTextFileOpen.Close
    	objTextFileWriteLog.Close
    end sub 
    


    template-short7.sql
    SELECT TOP (100) PERCENT Computer AS [Имя компьютера], UserName AS [Учетная запись], program AS Программа, start_time AS [Время запуска], 
           stop_time AS [Время завершения], dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) / 3600)) 
           + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 / 60)) 
           + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 % 60)) AS Длительность
    FROM   (SELECT r.Computer, s.UserName, r.programID, r.id AS R_ID, MIN(s.id) AS S_ID, r.program, r.Tg AS start_time, MIN(s.Tg) AS stop_time
            FROM   (SELECT  id, eId, Tg, UserName, Computer, image_id AS programID, image AS program
    				FROM    dbo.WARNING__TABLE_NAME_FOR_CHANGE
    				WHERE   (eId = 4688)
    				AND     (Tg > CONVERT(DATETIME, '2013-01-01 00:00:00.000', 102))
    				AND     image not like '%.scr') AS r 
    				INNER JOIN
    				(SELECT  id, eId, Tg, UserName, Computer, image AS programID, name AS program
    				FROM    dbo.WARNING__TABLE_NAME_FOR_CHANGE
    				WHERE   (eId = 4689)
    				AND     name not like '%.scr') AS s 
    				ON r.programID = s.programID AND r.program = s.program AND r.UserName = s.UserName AND r.id <= s.id
             GROUP BY r.Computer, s.UserName, r.programID, r.id, r.program, r.Tg) AS DERIVEDTBL
    UNION ALL
    SELECT  TOP (100) PERCENT Computer AS [Имя компьютера], UserName AS [Учетная запись], program AS Программа, start_time AS [Время запуска], 
            stop_time AS [Время завершения], dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) / 3600)) 
            + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 / 60)) 
            + ':' + dbo.FU_GET_FULL_QTY_TEST(CONVERT(VARCHAR, DATEDIFF(SECOND, start_time, stop_time) % 3600 % 60)) AS Длительность
    FROM    (SELECT r.Computer, s.UserName, r.programID, r.id AS R_ID, MIN(s.id) AS S_ID, r.program, r.Tg AS start_time, MIN(s.Tg) AS stop_time
             FROM   (SELECT  id, eId, Tg, UserName, Computer, image_id AS programID, image AS program
    				 FROM    dbo.WARNING__TABLE_NAME_FOR_CHANGE
                     WHERE   (eId = 4688)
                     AND     (Tg > CONVERT(DATETIME, '2013-01-01 00:00:00.000', 102))
    				 AND     image like '%.scr') AS r 
    				 INNER JOIN
                    (SELECT  id, eId, Tg, UserName, Computer, image AS programID, name AS program
                     FROM    dbo.WARNING__TABLE_NAME_FOR_CHANGE
                     WHERE   (eId = 4689)
    				 AND     name like '%.scr') AS s 
    				 ON r.programID = s.programID AND r.program = s.program AND r.id <= s.id
             GROUP BY r.Computer, s.UserName, r.programID, r.id, r.program, r.Tg) AS DERIVEDTBL2
    ORDER BY 'Время запуска' DESC 
    


    update_result_file7.vbs
    'on error resume next
    
    if Wscript.Arguments.Count = 1 then
    	gsFileName = Wscript.Arguments(0)
    	gsFileNameRes = fuRemoveExtention(gsFileName) & ".xls"
    elseif Wscript.Arguments.Count = 2 then
    	gsFileName = Wscript.Arguments(0)
    	gsFileNameRes = Wscript.Arguments(1)
    else
    	gsFileName = InputBox("Файл для обновления", "Ввод", "")
    	gsFileNameRes = InputBox("Файл результата", "Ввод", fuRemoveExtention(gsFileName) & ".xls")
    end if
    
    sgSimbolSplit = ";"
    gsSimbolSplitFields = vbTab
    
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    Set objTextFileOpen = objFSO.OpenTextFile(gsFileName, 1)
    
    if not objFSO.FileExists(gsFileName) then
    	wscript.echo "Исходного файла для обновления нет, выхожу!"
    	objTextFileOpen.Close
    	Wscript.Quit
    end if
    
    if not objFSO.FileExists(gsFileNameRes) then
    	set objTextFileWriteRes = objFSO.OpenTextFile(gsFileNameRes, 8, True)
    else
    	set objTextFileWriteRes = objFSO.CreateTextFile(gsFileNameRes, True)
    end if
    
    do until objTextFileOpen.AtEndOfStream
    	record = objTextFileOpen.Readline
    	
    	if ((InStr(record, "--------")) or (Len(record) = 0) or (InStr(record, "обработано строк")) or (InStr(record, "rows affected"))) then
    		'wscript.echo "пропускаю строку: '" & record & "'"
    	else
    		if InStr(record, sgSimbolSplit) then
    			recordRes = Replace(record, sgSimbolSplit, gsSimbolSplitFields)
    		else
    			recordRes = record
    		end if
    		
    		objTextFileWriteRes.writeLine recordRes
    	end if
    loop 
    
    objTextFileWriteRes.Close
    objTextFileOpen.Close
    
    WScript.Echo "Обновление завершено! Результирующий файл " & gsFileNameRes
    
    function fuRemoveExtention(lsFilename)
    	lRes = lsFilename
    	if InStr(lsFilename, ".") then
    		lRes = Left(lsFilename, Len(lsFilename)-4)
    	end if
    	fuRemoveExtention = lRes
    end function 
    



    На SQL Server’е надо создать функцию FU_GET_FULL_QTY_TEST:
    FU_GET_FULL_QTY_TEST
    USE [quickly]
    GO
    /****** Object:  UserDefinedFunction [dbo].[FU_GET_FULL_QTY_TEST]    Script Date: 12/03/2013 13:03:43 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    
    CREATE FUNCTION [dbo].[FU_GET_FULL_QTY_TEST] (@short_qty varchar(255))
    RETURNS varchar(255)
    AS
    BEGIN
    DECLARE @retMsg varchar(255)
    set @retMsg = @short_qty
    if len(@short_qty) <= 1
    set @retMsg = '0' + @retMsg
    RETURN (@retMsg)
    END
    



    Архив со скриптами можно скачать тут.
    Знаю, кажется много батников и скриптов. Но достаточно один раз настроить и пользоваться потом.

    Update.

    Настройки журнала безопасности для сохранения событий запуска/остановки программ и номера событий


    Для XP

    EventID 592 для создания процесса, 593 для завершения.
    Настройки аудита.
    secedit /configure /cfg c:\XP\secsetup.inf /db secsetup.sdb /verbose /overwrite /quiet
    

    Кусок из файла secsetup.inf:
    [Event Audit]
    ; 0 - Выключено
    ; 1 - Только успех
    ; 2 - Только отказ
    ; 3 - Успех и отказ
    AuditSystemEvents = 3
    AuditLogonEvents = 3
    AuditObjectAccess = 3
    AuditPrivilegeUse = 3
    AuditPolicyChange = 3
    AuditAccountManage = 3
    AuditProcessTracking = 3
    AuditAccountLogon = 3
    


    Для 7

    EventID 4688 для создания процесса, 4689 для завершения.
    Настройки аудита.
    Для русской:
    auditpol.exe /set /category:"Подробное отслеживание" /subcategory:"Создание процесса" /success:enable /failure:enable
    auditpol.exe /set /category:"Подробное отслеживание" /subcategory:"Завершение процесса" /success:enable /failure:enable
    

    Для английской:
    auditpol.exe /set /category:"Detailed Tracking" /subcategory:"Process Creation" /success:enable /failure:enable
    auditpol.exe /set /category:"Detailed Tracking" /subcategory:"Process Termination" /success:enable /failure:enable
    


    Конечно, если есть домен, настройки аудита должны быть прописаны в доменных политиках.

    И кто как делает отчёт по запуску программ на компьютерах пользователей? Поделитесь.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 51

      +21
      Бедный ivanov-vi, пришёл на работу в 7 утра, за час до начала рабочего дня, 40 минут играл, а его за это теперь накажут, судя по красному цвету.
        +3
        Мне нравится как он каталоги называет. D:\Book\Игры\Games.
          +26
          А переназвал бы в D:\Work\Office\Excel97.exe и никаких вопросов от руководства.
        +1
        У нас как-то пол офиса залипло на очередном дудле от гугла(Тот, что на пятидесятилетие сериала «Доктор Кто»).
        Вот от этого никак не спастись.
          +1
          Этим надо переболеть.
            0
            … А потом придёт очередная эпидемия с очередным шикарным дудлом, который, например, вирусной рассылкой уложит «болеть» не одну компанию, а весь холдинг
          –28
          > windows
          > безопасность
          Меня одного удивляет попытка автора сочетать эти два понятия?
          • НЛО прилетело и опубликовало эту надпись здесь
              +1
              Да. (Эх, не успел обновить комментарии.)
              0
              Думаю что лучше писать в базу данных. Создать простенькую страничку на php, и смотреть базу в таблице, с возможностью выборки по пользователю к примеру. Это будет намного удобнее чем возиться с файлами.
              А вообще реализации зачет.
              Правда на Powershell вышло бы намного меньше кода:) Один минус — он должен быть на всех тачках.
                0
                Спасибо!
                Писать в базу и иметь веб-интерфейс — отличная идея. У меня так отчёты по инету реализованы.
                0
                На мак использую Timing.
                Для Windows использовал ManicTime — очень мощный инструмент.
                Есть еще http://www.yaware.com.ua.
                В свое время, эти инструменты очень помогли. Правда, использовал для контроля личного времени, а не времени сотрудников.
                  +2
                  Классное решение! Спасибо, пригодится.
                  Но если вы так переживаете за безопасность, то запретите им запуск ПО, кроме разрешенного. Например, только из папки Program Files, а установку ПО только ТП и только в эту папку.
                    0
                    Я конечно дилетант в подобных вопросах, но все же, если это настолько важно то может настроить домен?
                      0
                      Домен, конечно, есть.
                      +1
                      Выше уже давали ссылки на популярные инструменты для контроля за эффективностью использования компьютера. Мы, глядя на них, тоже написали свой полноценный сервис. Наша программа собирает вообще всю информацию об активности пользователя за компьютером. Помимо стандартных времени работы каждого приложения, сайтов и снятия снимков экрана, мы еще фиксируем активность периферийных устройств в запущенных программах. Т.е. мы знаем не только то, что человек запустил какую-либо программу, но и как активно он с ней работал (нажатия на клавиши, движения мышкой и т.д). Вся активность пользователя поступает в панель управления, а там уже ответственное лицо распределяет активность на «рабочую», «нейтральную» и «отдых». В результате мы имеем очень точные показатели эффективности использования компьютера по каждому сотруднику.

                      Все это дело мы всячески в панели управления визуализируем и упрощаем анализ. Вот, например, кусочек таймлайна по пользователю.
                      image

                      Квадратики сверху — это снимки экрана. Наводим мышку — получаем подробную информацию о каждой активности.

                      А вот так выглядит перераспределение полученной активности от пользователей, чтобы правильно красить таймлайн (мышкой перебрасываем туда-сюда):
                      image

                      Ну и т.д… пироги на общую дневную активность по пользователю, диаграммы, общая сводка за год, подробные отчеты и многое другое. Тема актуальная :)
                        0
                        А, например, 3 тсч. пользователей не уложат вашу систему? В какой базе храните, если не секрет?
                          0
                          база mysql. Данные от 3к пользователей мы спокойно примем, а вот вывести их в панели управления так, чтобы при этом сохранился текущий хорошо работающий интерфейс — та еще задача. На текущий момент наше решение к такому не готово. Пока мы его активно используем у себя внутри, а это от 5 до 50 сотрудников.
                            +1
                            Доработайте, чтобы решение держало хотя бы 15000 пользователей и поддерживало MS SQL — у Вас появятся покупатели ;)
                              +2
                              >а это от 5 до 50 сотрудников

                              Представил себе условного «директора», который периодически бегает по офису с распечаткой вашего отчёта, после чего количество сотрудников в течение недели падает с 50 до 5.
                                0
                                У Вас очень узкое представление о том, как можно применять подобный отчёт. На мой взгляд основной профит от него не столько в наказании нерадивых, сколько в изучении потребности пользователей в различном ПО:
                                Если 100 пользователей пользуются Excel раз в неделю не более часа имеет смысл не покупать им Excel, а пустить к Excel, установленному на терминальном сервере, например.
                                (Это грубый пример)
                                Ну или вообще вычислить места инсталляции платного ПО, которым не пользуются и перенести лицензии на другие ПК, где есть необходимость в данном ПО.
                            0
                            А как вы определяете какие вкладки в браузере просто открыты, а с какими ведется работа? Т.е., к примеру, у человека открыта вкладка с хабром, а сам он, на самом деле, в гугле искал что-то.
                              0
                              В определенный момент времени активной может быть только 1 вкладка. Для каждого браузера свой способ. Например, для FF, IE и старой оперы работает запрос текущей открытой страницы через DDE stackoverflow.com/questions/430614/get-firefox-url
                              +1
                              У вас оплата почасовая? Представить не могу в каких ещё случаях может требоваться такая степень контроля.
                                +5
                                да, это было сделано для контроля группы сотрудников с почасовой оплатой (специфика работы — очень много рутины. Не связано с программированием, творчеством и т.д.). Особенно было интересно посмотреть как идет овертайм работа по выходным дням, после окончания рабочего дня и т.д. (где оплата идет с коэффициентами). В процессе интеграции выяснилось, что больше 40% времени оплачивалось на просмотр социальных сетей и чтение развлекательных статей.
                                +6
                                пользователи в курсе такого шпионажа?
                                  +3
                                  Как вы посмели Хабр в отдых записать? :)
                                    0
                                    Личный планшет или ноутбук — и все эти отчеты выдадут совершенно левую информацию.
                                    К примеру ноутбук можно убрать в стол и подключить ко второму видеовходу монитора либо через квм — будет совершенно незаметно.
                                      0
                                      Нормальная политика безопасности в рабочей сети (например, привязка портов свитчей к MAC, запрет использования на рабочих компьютерах внешних носителей) и личный ноутбук из рабочего инструмента превращается в игрушку.
                                        0
                                        Я о личном ноуте говорил как о средстве посидеть в интернете незаметно для системы контроля — он не является рабочим инструментом и не входит в корпоративную сеть.
                                    +15
                                    >> информационной безопасности
                                    >> WinXP
                                    >> iexplore.exe

                                    FACEPA~1.BMP
                                      0
                                      Мы как-то отлавливали работу OpenGL и DirectX при запуске игр… ))
                                        +6
                                        Провести sql инъекцию от юзера, чьи данные собираются и дропнуть базу, вот будет исход :)
                                          0
                                          В Инете готового решения не нашёл, поэтому сделал свою реализацию.

                                          Плохо искали — они есть, как правило в составе других продуктов.

                                          Блок работы с компьютерами с семёркой

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

                                          А вообще вполне себе неплохой «велосипедик» для небольшой компании.
                                            0
                                            Небольшая компания — это сколько пользователей по-вашему?
                                            можно получать вообще только интересующие события, а не все подряд

                                            Я же ж ещё длительность считаю, а не просто факт запуска. Для нас это тоже важно.
                                              0
                                              Небольшая компания — это сколько пользователей по-вашему?

                                              До 100, полагаю…

                                              Я же ж ещё длительность считаю, а не просто факт запуска. Для нас это тоже важно.


                                              Я имел ввиду, что Вам же не нужны абсолютно все события из журнала безопасности, для определения длительности запуска приложения.
                                            +5
                                            У меня пара вопросов.

                                            Один — технический: В каком именно логе на машине пользователя хранится информация о времени запуска и завершения приложений? Вы пишете про журнал безопасности (Security, судя по «Select * from Win32_NTEventLogFile where LogFileName='Security'»), но у меня там только события аудита и только. Я не туда смотрю, или в Windows что-то подкрутить надо?

                                            Второй — правовой. С одной стороны если работник смотрит порнографию на рабочем месте — то это повод для увольнения, ну или строгого предупреждения как минимум. Но с другой стороны — такое наблюдение — суть вторжение в приватную сферу. Тут можно спорить о том, что частной жизни на рабочем месте быть не может, но если вы детально не расскажете сотрудникам о том, что ведётся наблюдение, а они про это узнают — то особо дотошные могут ведь и в суд подать. В Германии, к примеру, вы будете обязаны уведомить профком о такой штуке, и он моментально зарубит вашу инициативу на корню. Сами-то ваши сотрудники как к этому относятся?
                                              –1
                                              Вы немного искажаете.
                                              На рабочем месте работодатель имеет полное право хоть лично за вами поставить надсмотрщика, который будет за вами следить. Главное вы должны быть уведомлены об этом факте.
                                              Никакой частной жизни в принципе быть не может, вам платят деньги а вы работаете под условиями которые вам создали (главное чтобы они не противоречили конституции и не представляли вреда здоровью)
                                              Аналогом можно привести видеонаблюдение. Вы можете с полным правом вешать камеры в общественных местах, но обязаны повесить крупную хорошо различимую табличку «Ведётся видеонаблюдение», и правовых вопросов к вам не возникнет.
                                                +1
                                                Ну я возможно, невнятно выразился. Просто помнится, что-то такое уже рассматривалось в суде по правам человека лет пять-шесть назад и они постановили, что многодневное постоянное наблюдение за активностью работника — суть вмешательство в его приватную сферу. Опять же у нас на фирме хотели сделать что-то такое, но все воспротивились (но дело в Германии).

                                                Что касается видеонаблюдения — тут тоже не всё так просто. В Австрии, скажем, самовольно повешенная не то что в общественном месте, но даже на своём приватном участке камера (без разницы с табличкой или без), но различимая с улицы (ну или видеорегистратор хотя бы) обойдётся в 10000 (или около того) евро штрафа.

                                                Просто везде свои порядки.
                                                0
                                                В каком именно логе на машине пользователя хранится информация о времени запуска и завершения приложений?
                                                Да, это журнал безопасности (Security Log).
                                                Сами-то ваши сотрудники как к этому относятся?
                                                У нас приняты строгие, но справедливые документы (приказы/регламенты/инструкции/стандарты).
                                                  0
                                                  Да, это журнал безопасности (Security Log).

                                                  А можно осветить этот момент подробнее:
                                                  Какие именно события регистрируются при при запуске программы и при при её завершении?
                                                  Какие настройки аудита Вы выполнили, чтобы эти события регистрировались?

                                                  Полагаю это два самых интересных вопроса, ответы на которые не раскрыты…
                                                    +3
                                                    Для XP:
                                                    EventID 592 для создания процесса, 593 для завершения.
                                                    Настройки аудита.
                                                    secedit /configure /cfg c:\XP\secsetup.inf /db secsetup.sdb /verbose /overwrite /quiet
                                                    

                                                    Кусок из файла secsetup.inf:
                                                    [Event Audit]
                                                    ; 0 - Выключено
                                                    ; 1 - Только успех
                                                    ; 2 - Только отказ
                                                    ; 3 - Успех и отказ
                                                    AuditSystemEvents = 3
                                                    AuditLogonEvents = 3
                                                    AuditObjectAccess = 3
                                                    AuditPrivilegeUse = 3
                                                    AuditPolicyChange = 3
                                                    AuditAccountManage = 3
                                                    AuditProcessTracking = 3
                                                    AuditAccountLogon = 3
                                                    


                                                    Для 7:
                                                    EventID 4688 для создания процесса, 4689 для завершения.
                                                    Настройки аудита.
                                                    Для русской:
                                                    auditpol.exe /set /category:"Подробное отслеживание" /subcategory:"Создание процесса" /success:enable /failure:enable
                                                    auditpol.exe /set /category:"Подробное отслеживание" /subcategory:"Завершение процесса" /success:enable /failure:enable
                                                    

                                                    Для английской:
                                                    auditpol.exe /set /category:"Detailed Tracking" /subcategory:"Process Creation" /success:enable /failure:enable
                                                    auditpol.exe /set /category:"Detailed Tracking" /subcategory:"Process Termination" /success:enable /failure:enable
                                                    


                                                    Конечно, если есть домен, настройки аудита должны быть прописаны в доменных политиках.
                                                      0
                                                      Спасибо! То, что надо, работает.
                                                        0
                                                        А вот за эту информацию спасибо!
                                                        P.S. Если её в текст статьи включите — то вообще цены ей не будет! ;)
                                                          0
                                                          О, статью редактировать можно!
                                                            0
                                                            Добавил, спасибо.
                                                    0
                                                    «Конечно, рассматривается среда Windows» — ну конечно…
                                                      0
                                                      Никто не мешает сделать аналогичное решение и под линуксом. Разве что, не очень уверен, что можно будет мониторить что там в броузере конкретное открывается, и то, не факт, что нельзя
                                                        0
                                                        Для мониторинга кто что открывает в браузере уже давно придуманы прокси-сервера ;)
                                                      0
                                                      Что-то в статье не заметил про настройку аудита запуска процессов. Без настройки информации об этом в журналах не будет.
                                                      Также спорный вопрос, относится ли подсчет времени работы программ к информационной безопасности. Это скорее фактор трудовой дисциплины.
                                                      С точки зрения непосредственно ИБ интересно сам факт запуска exe 1. Из необычных мест 2. По ключевым словам названий. распространенных хак. утилит. Тогда все реализовывается гораздо проще.
                                                        0
                                                        А программу для периодического снятия скриншотов посоветуйте, по типу oDesk-клиента

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

                                                        Самое читаемое