将Python win32evtlog对象转换为xml

时间:2017-05-11 09:31:46

标签: python xml pywin32

我有一个使用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处于正确的事件但是无法解决我是如何做到的。

这个问题与How retrieve from Python win32evtlog rest of info?非常相似,但是那里的解决方案没有回答我们如何将对象转换为XML的关键点。

- ==使用有关CristiFati的XML的信息编辑== -

以下是应用程序事件的示例,其中事件消息显示为: -

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 个答案:

答案 0 :(得分:2)

ReadEventLog 返回 PyEventLogRecord s([MS.Docs]: _EVENTLOGRECORD structure上的包装器),而 EvtRender 期望(您需要使用) PyHANDLE s( PyEVT_HANDLE s( EVT_HANDLE [MS.Docs]: Windows Event Log Data Types上的包装)更精确))。
因此,要获取 XML 数据,您需要使用适用于此类型的函数系列:例如 EvtQuery EvtNext

code.py

#!/usr/bin/env python3

import sys
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__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

备注

  • 2个列表的长度应相同。每个中的 n th 条目应引用相同的事件(只要两个函数都以相同的值调用向后参数(< em>在下面阅读))
  • get_events_xmls
    • 返回与事件相关联的 XML blob列表
    • 错误处理不是最好的,您可以在try / except子句中包含所有 API 调用(我没有遇到错误,所以我是不确定可以引发异常的情况)
    • 您可以使用[MS.Docs]: EvtNext function的参数( Timeout EventsSize )进行性能微调;对我来说, ~20k < / em>事件在 <10 秒内处理 - 其中文本打印和转换次数最多)
    • Python 3 中, XML bytes [Python 3.Docs]: Built-in Types - class bytes([source[, encoding[, errors]]]))而不是正常字符串(我必须对它们进行编码,因为某些字符串包含一些非 ASCII 字符,并且尝试打印它们会引发 UnicodeEncodeError
    • 可以进行事件过滤,检查[MS.Docs]: EvtQuery function的args(标志查询
    • 请注意向后参数,该参数允许以反向(按时间顺序)顺序遍历事件(默认设置为 True )。
  • get_record_data
    • 它只是一个便利功能,它将 PyEventLogRecord 对象转换为 Python 字典
    • 转换是基于以下事实:我们关心的字段以大写字母开头( E ventID < em> C omputerName T imeGenerated ,...),这是为什么不应该用于生产
    • 它不会转换实际值( TimeGenerated 的值为pywintypes.datetime(2017, 3, 11, 3, 46, 47)
  • get_eventlogs
    • 返回 PyEventLogRecord s
    • 的列表
    • get_events_xmls 的情况一样,请注意向后参数
    • 我必须坚持 buf_size 。正如[MS.Docs]: ReadEventLogW function所述,在获取事件时,可以使用max 512K 的缓冲区。现在(从 PyWin32 版本 220 开始),可以将其作为参数(最后一个)传递给win32evtlog.ReadEventLog。有关详细信息,请查看 [SourceForge.hg]: mhammond/pywin32 - Add buffer size parameter for ReadEventLog (patch #143 from cristi fati) 。默认情况下,存在一个限制,因此缓冲区大小被硬编码为 1K 。由于每个 ReadEventLog 都在访问磁盘,因此使用新的缓冲区大小我的速度提高了 10X (对于 ~180K 事件)
  • 由于我将所有数据存储在2个列表中(而不是现场数据处理),因此我选择速度超过内存消耗。对于 ~20K 事件,2个列表正在〜30MB RAM (现在我觉得它足够好了)

@ EDIT0 :我找不到使用 Evt * 函数系列获取所有必需信息的方法,所以我'从两个来源获取它(我增强了我之前发布的脚本):

@ EDIT1 :根据[MS.Docs]: OpenEventLogW function

  

如果您指定了自定义日志但无法找到,则事件日志记录服务会打开应用程序日志;但是,没有关联的消息或类别字符串文件。

[MS.Docs]: Eventlog Key列出了3个标准。所以,这就是它打开 Application 日志的原因。我对脚本做了一些小改动来测试源代码。我不知道 mmc 从哪里获取 Setup 事件。