Преобразование Python win32evtlog объектов в XML

У меня есть приложение, которое использует win32evtlog для получения и отображения различных событий, и я хотел бы ограничить отображение событиями определенного уровня, но win32evtlog не возвращает это. Кажется, что вы можете преобразовать событие в XML и затем извлечь эту информацию, но я не могу понять, как вы переводите событие из цикла в XML.

Я могу перейти к следующему и использовать его для отображения данных, которые есть у LogObject, например, LogObject.TimeGenerated.

Log = win32evtlog.OpenEventLog('localhost', 'Application')
while 1:
    LogObjects = winev32tlog.ReadEventLog(Log, win32evtlog.EVENTLOG_BACKWARDS_READ|wine32vtlog.EVENTLOG_SEQUENTIAL_READ, 0)
    if not LogObjects:
        break
    for LogObject in LogObjects:

Я пробовал конвертировать используя

LogObjectXML = win32evtlog.EvtRender(LogObject, 1)

Это к сожалению возвращается

TypeError: The object is not a PyHANDLE object

Так что я знаю, что мне нужно получить некоторый объект-дескриптор, который я могу использовать, чтобы указать EvtRender на правильное событие, но не могу понять, как я это делаю.

Этот вопрос очень похож на Как получить из Python win32evtlog остальной информации? но решение там не ответило на критический момент того, как мы конвертируем объект в XML.

- == Отредактировано с информацией о XML для CristiFati ==-

Ниже приведен пример события приложения, в котором сообщение о событии гласит:

Updated Windows Defender status successfully to SECURITY_PRODUCT_STATE_ON.

XML для просмотра событий, как показано ниже

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="SecurityCenter" /> 
  <EventID Qualifiers="0">15</EventID> 
  <Level>4</Level> 
  <Task>0</Task> 
  <Keywords>0x80000000000000</Keywords> 
  <TimeCreated SystemTime="2017-05-23T07:36:27.627108000Z" /> 
  <EventRecordID>49419</EventRecordID> 
  <Channel>Application</Channel> 
  <Computer>Name.domain.here</Computer> 
  <Security /> 
  </System>
- <EventData>
  <Data>Windows Defender</Data> 
  <Data>SECURITY_PRODUCT_STATE_ON</Data> 
  </EventData>
  </Event>

1 ответ

Решение

ReadEventLog возвращается PyEventLogRecord s (оболочка над [MSDN]: EVENTLOGRECORD структура), в то время как EvtRender ожидает (вам нужно работать с) PyHANDLE с (PyEVT_HANDLE s (оболочка над [MSDN]: EVT_HANDLE) чтобы быть более точным).
Итак, для получения данных XML вам нужно использовать семейство функций, которое работает с этим типом: например, EvtQuery, EvtNext:

import pywintypes
import win32evtlog

INFINITE = 0xFFFFFFFF
EVTLOG_READ_BUF_LEN_MAX = 0x7FFFF


def get_record_data(eventlog_record):
    ret = dict()
    for key in dir(eventlog_record):
        if 'A' < key[0] < 'Z':
            ret[key] = getattr(eventlog_record, key)
    return ret


def get_eventlogs(source_name="Application", buf_size=EVTLOG_READ_BUF_LEN_MAX, backwards=True):
    ret = list()
    evt_log = win32evtlog.OpenEventLog(None, source_name)
    read_flags = win32evtlog.EVENTLOG_SEQUENTIAL_READ
    if backwards:
        read_flags |= win32evtlog.EVENTLOG_BACKWARDS_READ
    else:
        read_flags |= win32evtlog.EVENTLOG_FORWARDS_READ
    offset = 0
    eventlog_records = win32evtlog.ReadEventLog(evt_log, read_flags, offset, buf_size)
    while eventlog_records:
        ret.extend(eventlog_records)
        offset += len(eventlog_records)
        eventlog_records = win32evtlog.ReadEventLog(evt_log, read_flags, offset, buf_size)
    win32evtlog.CloseEventLog(evt_log)
    return ret


def get_events_xmls(channel_name="Application", events_batch_num=100, backwards=True):
    ret = list()
    flags = win32evtlog.EvtQueryChannelPath
    if backwards:
        flags |= win32evtlog.EvtQueryReverseDirection
    try:
        query_results = win32evtlog.EvtQuery(channel_name, flags, None, None)
    except pywintypes.error as e:
        print(e)
        return ret
    events = win32evtlog.EvtNext(query_results, events_batch_num, INFINITE, 0)
    while events:
        for event in events:
            ret.append(win32evtlog.EvtRender(event, win32evtlog.EvtRenderEventXml))
        events = win32evtlog.EvtNext(query_results, events_batch_num, INFINITE, 0)
    return ret


def main():
    import sys, os
    from collections import OrderedDict
    standard_log_names = ["Application", "System", "Security"]
    source_channel_dict = OrderedDict()

    for item in standard_log_names:
        source_channel_dict[item] = item

    for item in ["Windows Powershell"]: # !!! This works on my machine (96 events)
        source_channel_dict[item] = item

    for source, channel in source_channel_dict.items():
        print(source, channel)
        logs = get_eventlogs(source_name=source)
        xmls = get_events_xmls(channel_name=channel)
        #print("\n", get_record_data(logs[0]))
        #print(xmls[0])
        #print("\n", get_record_data(logs[-1]))
        #print(xmls[-1])
        print(len(logs))
        print(len(xmls))

if __name__ == "__main__":
    main()

Примечания:

  • 2 списка должны иметь одинаковую длину. N- я запись в каждом из них должна ссылаться на одно и то же событие (при условии, что обе функции вызываются с одинаковым значением для backwards аргумент (читайте ниже)
  • get_events_xmls:
    • Возвращает список блобов XML, связанных с событиями
    • Обработка ошибок не самая лучшая, вы можете обернуть все вызовы API в try/except пункты (я не сталкивался с ошибками, поэтому я не уверен, в каких ситуациях может возникнуть исключение)
    • Вы можете немного поиграть с [MSDN]: EvtNext аргументы функции (Timeout а также EventArraySize для тонкой настройки производительности; для меня ~20 тыс. событий были обработаны в течение <10 секунд - из которых печать текста и преобразования заняли больше всего)
    • В Py3 XML представляют собой [Python]: байты, а не обычные строки (мне пришлось их кодировать, потому что некоторые из них содержат символы, не входящие в ASCII, и попытка их напечатать приведет к UnicodeEncodeError)
    • Возможна фильтрация событий, проверьте [MSDN]: EvtQuery Аргументы функции (Flags а также Query)
    • Обратите внимание backwards аргумент, который позволяет проходить события в обратном (хронологическом) порядке (по умолчанию установлено True).
  • get_record_data:
    • Это просто удобная функция, она преобразует PyEventLogRecord объект в словарь Python
    • Преобразование основано на том факте, что поля, которые нам нужны, начинаются с заглавной буквы (EventID, ComputerName, TimeGenerated...), поэтому его нельзя использовать в производстве
    • Это не преобразует фактические значения (TimeGenerated значение pywintypes.datetime(2017, 3, 11, 3, 46, 47))
  • get_eventlogs:
    • Возвращает список PyEventLogRecord s
    • Как в get_events_xmls Обратите внимание на backwards аргумент
    • Я должен настаивать на buf_size, Как [MSDN]: ReadEventLog Состояния функций: при получении событий можно использовать буфер до 512 КБ. Теперь (начиная с pywin32 версии 220) его можно передать как аргумент (последний) win32evtlog.ReadEventLog, Я был тем, кто представил его в pywin32 ( http://pywin32.hg.sourceforge.net/hgweb/pywin32/pywin32/rev/955b7db466fe). По умолчанию было ограничение, так что размер буфера был жестко закодирован до 1K. Так как каждый ReadEventLog обращался к диску, с новым размером буфера я получил 10-кратное улучшение скорости (для событий ~180 КБ)
  • Поскольку я храню все данные в двух списках (вместо обработки данных на месте), я выбираю скорость, а не потребление памяти. Для событий ~20К 2 списка занимают ~30 МБ ОЗУ (что в настоящее время, я думаю, достаточно прилично)

@ EDIT0: я не мог найти способ получить всю необходимую информацию с Evt* Семейство функций, так что я получаю его из обоих источников (я улучшил сценарий, который я ранее опубликовал):

@ EDIT1: в соответствии с [MSDN]: функция OpenEventLog:

Если вы укажете собственный журнал, и он не найден, служба регистрации событий откроет журнал приложений; однако не будет никакого связанного сообщения или файла строки категории.

[MSDN]: в ключе журнала событий перечислены 3 стандартных. Вот почему он открывает журнал приложений. Я сделал несколько небольших изменений в сценарии, чтобы проверить источники. Я не знаю, откуда MMC получает события установки.

Другие вопросы по тегам