Начнем с закуски - уведомления на e-mail.
Напишем код класса по имени MailNotification. Для работы с электронной почтой используем набор библиотек CDO(Collaboration Data Objects).
'для примера используем сервер mail.ru Const Login = "mymyail@mail.ru" Const PW = "mypassword" Const SMTPSrv = "smtp.mail.ru" Set objMail = New MailNotification 'можно изменить порт, аутентификацию, таймаут подключения, 'кодировку и использовать SSL 'With objMail ' .Port = 465 'gmail ' .UseSSL = True 'End With 'можно добавить несколько вложений - вместо Nothing - какие-нибудь логи например 'arrLogs = Array("c:\temp\1.log","c:\temp\2.log") If objMail.Send(SMTPSrv, Login, PW, Login, "Заголовок", "Содержание", Nothing) Then MsgBox "Сообщение отправлено", vbInformation Else MsgBox "Не удалось отправить сообщение", vbCritical End If Set objMail = Nothing Class MailNotification Private m_Msg, m_Conf Private m_SMTPPort, m_SMTPAuth, m_SMTPUseSSL, m_SMTPTimeout, m_Charset Private Sub Class_Initialize() Set m_Msg = CreateObject("CDO.Message") Set m_Conf = CreateObject("CDO.Configuration") 'значения по умолчанию m_SMTPPort = 25 'порт m_SMTPAuth = 1 'базовая аутентификация m_SMTPUseSSL = False 'не использовать SSL m_SMTPTimeout = 60 'таймаут подключения m_Charset = "windows-1251" 'кодировка End Sub Private Sub Class_Terminate() Set m_Msg = Nothing Set m_Conf = Nothing End Sub Public Property Let Port(i) m_SMTPPort = i End Property Public Property Let Auth(i) m_SMTPAuth = i End Property Public Property Let UseSSL(b) m_SMTPUseSSL = b End Property Public Property Let Timeout(i) m_SMTPTimeout = i End Property Public Property Let Charset(s) m_Charset = s End Property Public Function Send(sSMTPSrv, sLogin, sPW, sTo, sSubject, sBody, arrAttachment) On Error Resume Next With m_Conf.Fields 'значение 1, которое используется по умолчанию – использовать каталог Pickup .Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2 '1 - базовая аутентификация, 0 – без аутентификации (анонимно), 2 – аутентификация NTLM .Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = m_SMTPAuth .Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = sSMTPSrv .Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = m_SMTPPort .Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = sLogin .Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = sPW 'использовать ssl .Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = m_SMTPUseSSL 'таймаут .Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = m_SMTPTimeout .Update End With With m_Msg .Configuration = m_Conf .From = sLogin .To = sTo .Subject = sSubject .TextBody = sBody .Bodypart.Charset = m_Charset ' выставляем кодировку If IsArray(arrAttachment) Then For i = 0 To UBound(arrAttachment) .AddAttachment arrAttachment(i) Next End If .Send End With If Err.Number = 0 Then Send = True End Function End ClassТаким образом мы можем не только отправить сообщение о наступлении события, например падения целевого сервиса, но и прикрепить к сообщению какие-нибудь логи, содержащие более детальную информацию.
Приступим к приготовлению основного блюда - SMS.
Писать код для работы с каждым sms-оператором - не айс :). Кроме того все sms-операторы для отправки sms через их сервисы используют капчу, что вполне объяснимо.
"Что же делать?", - спросите Вы меня. Ну-ка, спросите меня :).
Предлагаю использовать сервис Google Календарь, который позволяет создавать события и получать уведомления об их наступлении в виде сообщений электронной почты или SMS, для чего необходимо добавить номер мобильного телефона в настройки календаря.
На текущий момент поддерживаются следующие sms-операторы: Билайн, Мегафон, МТС и Скай Линк.
Авторизацию и общение с календарем реализуем с помошью объекта XMLHttpRequest.
Напишем код класса по имени SMSNotification и используем его единственный метод SendMessage по назначению.
Const Login = "mymail@gmail.com" Const PW = "mypassword" Set objSMS = New SMSNotification If objSMS.SendMessage(Login, PW, "Заголовок", "Содержание", "Место") Then MsgBox "Сообщение отправлено", vbInformation Else MsgBox "Не удалось отправить сообщение", vbCritical End If Set objSMS = Nothing Class SMSNotification Public Function SendMessage(sLogin, sPW, sTitle, sContent, sWhere) 'проверяем службу времени If Not W32TimeCheck Then Exit Function 'синхронизируем время If Not TimeSinc Then Exit Function Dim XMLHttp 'XMLHttpRequest Dim sAuthTokens Set XMLHttp = CreateObject("Microsoft.XMLHTTP") With XMLHttp 'авторизуемся .Open "POST", "https://www.google.com/accounts/ClientLogin", False .SetRequestHeader "Content-Type", "application/x-www-form-urlencoded" .Send "Email=" & sLogin & "&Passwd=" & sPW & "&service=cl&source=da440dil-GSMS-1.0" 'получаем строку аутентификации sAuthTokens = Right(.responseText, Len(.responseText)-InStr(.responseText, "Auth=")-4) If .Status <> 200 Then Exit Function 'ошибка 'работаем с календарем .Open "POST", "http://www.google.com/calendar/feeds/default/private/full", False .SetRequestHeader "Content-Type", "application/atom+xml" .SetRequestHeader "X-If-No-Redirect", "True" .SetRequestHeader "Authorization", "GoogleLogin auth=" & sAuthTokens .Send "<?xml version='1.0' ?><entry xmlns='http://www.w3.org/2005/Atom' " & _ "xmlns:gd='http://schemas.google.com/g/2005'>" & _ "<title type='text'>" & sTitle & "</title>" & _ "<content type='text'>" & sContent & "</content>" & _ "<gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'>" & _ "</gd:eventStatus>" & _ "<gd:where valueString='" & sWhere & "'></gd:where>" & _ "<gd:when startTime='" & MakeDateTime() & _ "' endTime='" & MakeDateTime() & "'>" & _ "<gd:reminder minutes='1' method='sms' /></gd:when></entry>" If .Status <> 201 Then Exit Function 'ошибка End With Set XMLHttp = Nothing SendMessage = True End Function 'собираем время в необходимом формате Private Function MakeDateTime() MakeDateTime = Year(Date()) & "-" & Right(0 & Month(Date()),2) & "-" & _ Right(0 & Day(Date),2) & "T" & Right(0 & DateAdd("s",60,Time()),8) End Function 'запуск службы времени Private Function W32TimeCheck() Set objShellApp = CreateObject("Shell.Application") With objShellApp If Not .IsServiceRunning("W32Time") Then If .ServiceStart("W32Time", True) = 0 Then Exit Function End If 'вместо WScript.Sleep, т.к. вероятно Shell.Application выполняет ServiceStart асинхронно W32TimeCheck = .IsServiceRunning("W32Time") End With Set objShellApp = Nothing End Function 'синхронизация времени с временем интернета Private Function TimeSinc() Set wshShell = CreateObject("WScript.Shell") With wshShell If .Run("w32tm /config /syncfromflags:manual /manualpeerlist:" & Chr(34) & _ "time.windows.com time.nist.gov, time-nw.nist.gov, time-a.nist.gov, time-a.nist.gov" & Chr(34) _ ,0,True) <> 0 Then Exit Function If .Run("w32tm /config /update",0,True) <> 0 Then Exit Function If .Run("w32Tm /resync /rediscover",0,True) <> 0 Then Exit Function End With Set wshShell = Nothing TimeSinc = True End Function End ClassС помощью календаря Google в случае необходимости можно даже организовать sms-рассылку, но здесь не об этом :).
Переходим к десерту - постоянной подписке на события WMI.
В предыдущей статье я изложил свой взгляд на временную подписку WMI, способы преодоления ее недостатков, и в заключении намекнул о существовании более "неубиваемых" способов мониторинга событий, под которыми подразумевал именно постоянную подписку.
В рамках настоящей статьи мы не сможем осветить эту тему подробно, поэтому сразу перейдем к стандартному потребителю событий CommandLineEventConsumer.
Для использования нашего потребителя необходимо скомпилировать файл wbemcons.mof, проживаюший по адресу %SystemRoot%\system32\Wbem\, в пространство имен root\cimv2 репозитория WMI. В Windows XP достаточно выполнить команду "mofcomp -N:root\cimv2 %SystemRoot%\system32\Wbem\wbemcons.mof", однако в Windows 7 такой способ не прокатит (возможно в Висте тоже - не пробовал). Все дело в строке "#pragma namespace autorecover" файла wbemcons.mof (в самом начале, первая после коммента), которая запрещает компиляцию в пространство имен, отличное от пространства имен по умолчанию. Если верить MSDN, единственный способ преодолеть это ограничение - редактировать mof-файл.
Не будем париться с редактированием файла в коде преодолевая параноидальную систему безопасности Windows 7 путем изменения DACL файла, смены владельца с TrustedInstaller на админский аккаунт и т.п. Просто скопируем файл wbemcons.mof в каталог скрипта, удалим эту злополучную строку руками и скомпилируем mof-файл из нового места проживания.
With CreateObject("Scripting.FileSystemObject") If CreateObject("WScript.Shell").Run("mofcomp -N:root\cimv2 " & Chr(34) & _ .BuildPath(.GetParentFolderName(WScript.ScriptFullName),"WBEMCons.mof") & Chr(34) _ ,0,True) = 0 Then MsgBox "Файл scrcons.mof скомпилирован", vbInformation Else MsgBox "Не удалось скопмилировать файл scrcons.mof", vbCritical End If End With
Далее напишем код создания модального окна, которое будем отображать каждый раз в момент отлова события.
Разумеется можно использовать написанные выше скрипты, но модальное окно по-моему нагляднее.
MsgBox "Вы запустили блокнот",vbInformation,"Achtung!"Назовем его Achtung.vbs.
Затем создадим подписку на событие запуска стандартного блокнота Notepad.
With CreateObject("Scripting.FileSystemObject") If CreateSubscription("MyFilter","MyConsumer","wscript.exe " & _ .BuildPath(.GetParentFolderName(WScript.ScriptFullName),"Achtung.vbs")) Then MsgBox "Создание подписки на событие завершено", vbInformation Else MsgBox "Не удалось создать подписку на событие", vbCritical End If End With 'функция создания интерактивной подписки на событие запуска блокнота Function CreateSubscription(sFilterName,sConsumerName,sScriptFilePath) On Error Resume Next Set objWMI = GetObject("winmgmts:\\.\Root\CIMV2") 'создание фильтра события With objWMI.Get("__EventFilter").SpawnInstance_() .Name = sFilterName .QueryLanguage = "WQL" .Query = "SELECT * FROM __InstanceCreationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_Process' " & _ "AND TargetInstance.Name = 'notepad.exe'" Set objFilterPath = .Put_() End With 'создание потребителя события With objWMI.Get("CommandLineEventConsumer").SpawnInstance_() .Name = sConsumerName .CommandLineTemplate = sScriptFilePath .RunInteractively = True Set objConsumerPath = .Put_() End With 'связка фильтра и потребителя With objWMI.Get("__FilterToConsumerBinding").SpawnInstance_() .Filter = objFilterPath .Consumer = objConsumerPath .Put_() End With Set objWMI = Nothing If Err.Number = 0 Then CreateSubscription = True End If End FunctionВыполняем скрипт. Запускаем блокнот... Кушать подано... :).
Теперь каждый раз при запуске блокнота мы будем видеть наше модальное окно.
"Что же делать?", - продублирую я цитату из одного популярного мультфильма по примеру одного известного режиссера :).
Пишем скрипт удаления постоянной подписки.
If DeleteSubscription("MyFilter","MyConsumer") Then MsgBox "Удаление подписки на событие завершено", vbInformation Else MsgBox "Не удалось удалить подписку на событие", vbCritical End If 'функция удаления подписки на событие Function DeleteSubscription(sFilterName,sConsumerName) On Error Resume Next Set objWMI = GetObject("winmgmts:\\.\Root\CIMV2") 'удаляем фильтр Set colFilters = objWMI.ExecQuery("SELECT * FROM __EventFilter WHERE Name='" & sFilterName & "'") If colFilters.Count Then For Each objFilter In colFilters objFilter.Delete_ Next End If Set colFilters = Nothing 'удаляем потребителя Set colConsumers = objWMI.ExecQuery("SELECT * FROM CommandLineEventConsumer WHERE Name='" & sConsumerName & "'") If colConsumers.Count Then For Each objConsumer In colConsumers objConsumer.Delete_ Next End If Set colConsumers = Nothing Set objWMI = Nothing If Err.Number = 0 Then DeleteSubscription = True End If End FunctionЗапускаем... Избавились.
В заключение еще пару слов о постоянной подписке.
С моей точки зрения ее преимущество в том, что она не создает никаких собственных процессов, служб и пр., таким образом оставляя гораздо более меньший по сравнению с временной подпиской "угол атаки". Правда у использованного в статье потребителя событий CommandLineEventConsumer есть один небольшой, на мой взгляд, недостаток - файл, прописанный в свойстве CommandLineTemplate можно удалить.
Но существуют иные стандартные потребители событий, обладающие своими преимуществами и недостатками, которые вы сможете использовать, если покопаете тему постоянной подписки на события WMI поглубже. Более того, углубившись в подписку на события WMI основательно, вы сможете написать код своего собственного потребителя событий. Но это уже совсем другая история...
Комментариев нет:
Отправить комментарий
Комментарий будет опубликован после модерации