【问题标题】:Converting Python win32evtlog objects to xml将 Python win32evtlog 对象转换为 xml
【发布时间】:2017-05-11 09:31:45
【问题描述】:

我有一个使用 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>

【问题讨论】:

    标签: python xml pywin32


    【解决方案1】:

    ReadEventLog 返回 PyEventLogRecords([MS.Docs]: _EVENTLOGRECORD structure 上的包装器),而 EvtRender 期望(您需要使用)PyHANDLE s(PyEVT_HANDLEs(更准确地说是 EVT_HANDLE ([MS.Docs]: Windows Event Log Data Types) 的包装))。
    因此,为了获取 XML 数据,您需要使用适用于此类型的函数系列:例如EvtQueryEvtNext.

    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()
    

    注意事项

    • 这两个列表的长度应该相同。它们每个中的 nth 条目应该引用相同的事件(只要调用两个函数的 backwards 参数值相同(阅读下文))
    • get_events_xmls
      • 返回与事件关联的 XML blob 列表
      • 错误处理不是最好的,您可以将所有 API 调用包装在 try / except 子句中(我没有遇到错误,所以我不确定是什么可能引发异常的情况)
      • 您可以使用[MS.Docs]: EvtNext function 的参数(TimeoutEventsSize 进行性能微调;对我来说,~20k 事件在 秒内处理完毕 - 其中文本打印和转换花费最多)
      • Python 3 中,XMLbytes ([Python 3.Docs]: Built-in Types - class bytes([source[, encoding[, errors]]])) 而不是普通字符串 (我必须对它们进行编码,因为有些包含一些非ASCII 字符,并且尝试打印它们会引发 UnicodeEncodeError)
      • 事件过滤是可能的,检查[MS.Docs]: EvtQuery function的参数(标志查询
      • 注意 backwards 参数,该参数允许以相反的(时间顺序)顺序遍历事件(默认设置为 True)。
    • get_record_data
      • 只是一个方便的函数,它将一个PyEventLogRecord对象转换成一个Python字典
      • 转换是基于我们关心的字段以大写字母开头(EventID、C omputerName, TimeGenerated, ...),这就是为什么它不应在生产中使用
      • 它不会转换实际值(TimeGenerated 的值是pywintypes.datetime(2017, 3, 11, 3, 46, 47)
    • get_eventlogs
    • 由于我将所有数据存储在 2 个列表中(而不是就地数据处理),因此我选择的是速度而不是内存消耗。对于 ~20K 事件,这两个列表占用了 ~30MBRAM(现在我认为这已经足够了)

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

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

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

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

    【讨论】:

    • 感谢它真的很有帮助!当我收到事件时,我还需要能够显示解释事件内容的文本,而我看不到从 win32evtlog.EvtQuery 方法中获取该事件的方法。有没有办法用这种方法得到这些细节?
    • 不是所有数据都包含在 XML blob 中吗?有没有遗漏的部分?当然,必须解析 blob 才能获取数据。下面是一个解析 XML 的例子:[SO]: Print all xml child node using python
    • 我无法将其放入评论中,因此使用来自 XML 的信息和描述编辑了原始内容,我从事件查看器中提取了数据,因为给出示例比使用 python 更快。如您所见,XML 不包含“友好”描述。
    • 你有机会测试吗?
    • 让我知道结果如何 :)
    猜你喜欢
    • 2010-09-29
    • 1970-01-01
    • 2011-03-11
    • 2013-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多