将 Python win32evtlog 对象转换为 xml [英] Converting Python win32evtlog objects to xml

查看:37
本文介绍了将 Python win32evtlog 对象转换为 xml的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用 win32evtlog 来获取和显示不同事件的应用程序,我想将显示限制为特定级别的事件,但 win32evtlog 不返回此内容.似乎您可以将事件转换为 XML,然后提取此信息,但我无法弄清楚如何将事件从循环获取到 XML.

I have a app that uses win32evtlog to get and display different events and I would like to limit the display to events of a specific level but win32evtlog doesn't return this. It seems that you can convert an event to XML and then pull this info but I can't work out how you get the event from a loop to XML.

我可以得到以下内容并使用它来显示 LogObject 具有的数据,例如 LogObject.TimeGenerated

I can get up to the following and use it to display data the LogObject has such as 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 指向正确的事件,但不知道我是如何做到的.

So I know I need to get some sort of handle object that I can use to point the EvtRender at the correct event but can't work out how I do that.

这个问题很像如何从Python中检索win32evtlog rest信息?但是那里的解决方案没有回答我们如何将对象转换为 XML 的关键部分.

This question is quite similar to How retrieve from Python win32evtlog rest of info? but the solution there didn't answer the critical bit of how we convert the object to XML.

--== 编辑了有关 CristiFati 的 XML 的信息 ==--

--== Edited with information about the XML for CristiFati ==--

以下是事件消息读取的应用程序事件示例:-

Below is an example of an Application event where the event message reads:-

Updated Windows Defender status successfully to SECURITY_PRODUCT_STATE_ON.

每个事件查看器的 XML 如下

The XML as per event viewer is as below

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

推荐答案

ReadEventLog 返回 PyEventLogRecords(封装在 [MS.Docs]: _EVENTLOGRECORD 结构),而 EvtRender 期望(您需要使用)PyHANDLE(PyEVT_HANDLE(EVT_HANDLE 上的包装器([MS.Docs]:Windows 事件日志数据类型)更精确)).
因此,要获取 XML 数据,您需要使用适用于此类型的函数系列:例如EvtQueryEvtNext.

ReadEventLog returns PyEventLogRecords (wrapper over [MS.Docs]: _EVENTLOGRECORD structure), while EvtRender expects (you need to work with) PyHANDLEs (PyEVT_HANDLEs (wrapper over EVT_HANDLE ([MS.Docs]: Windows Event Log Data Types) to be more precise)).
So, for getting XML data, you need to use the functions family that works with this type: e.g. 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 个列表的长度应该相同.每个函数中的 nth 条目应该引用相同的事件(只要两个函数的 backwards 参数(阅读下文))
  • get_events_xmls:
    • 返回与事件关联的 XML blob 列表
    • 错误处理不是最好的,您可以将所有 API 调用包装在 try/except 子句中(我没有遇到错误,所以我不确定在什么情况下可能会引发异常)
    • 您可以使用 [MS.Docs]:EvtNext 函数的参数(TimeoutEventsSize 用于性能微调;对我来说,~20k事件在 <10 秒内处理完毕 - 其中文本打印和转换花费最多)
    • Python 3中,XML字节([Python 3.Docs]:内置类型 - class bytes([source[, encoding[, errors]]])) 而不是普通字符串(我不得不对它们进行编码,因为有些包含一些非ASCII字符,并尝试打印它们会引发 UnicodeEncodeError)
    • 事件过滤是可能的,检查[MS.Docs]:EvtQuery 函数的参数(FlagsQuery)
    • 注意 backwards 参数,它允许以相反的(时间顺序)顺序遍历事件(默认设置为 True).
    • The 2 lists should have the same length. The nth entry in each of them should reference the same event (as long as both functions are called with same value for backwards argument (read below))
    • get_events_xmls:
      • Returns a list of XML blobs associated to the events
      • The error handling is not the best, you could wrap all API calls in try / except clauses (I didn't run into errors, so I'm not sure what are the situations where exception could be raised)
      • You can play a little bit with [MS.Docs]: EvtNext function's arguments (Timeout and EventsSize for performance fine tuning; for me, ~20k events were processed in a matter of <10 seconds - out of which text printing and conversions took the most)
      • In Python 3, the XMLs are bytes ([Python 3.Docs]: Built-in Types - class bytes([source[, encoding[, errors]]])) rather than normal strings (I had to encode them because some contain some non-ASCII chars, and attempting to print them would raise UnicodeEncodeError)
      • Event filtering is possible, check [MS.Docs]: EvtQuery function's args (Flags and Query)
      • Note the backwards argument which allows traversing the events in reversed (chronological) order (default set to True).
      • 这只是一个方便的函数,它将PyEventLogRecord 对象转换为Python 字典
      • 转换基于这样一个事实,即我们关心的字段以大写字母开头(EventIDComputerNameTimeGenerated、...),这就是为什么它不应该用于生产
      • 它不会转换实际值(TimeGenerated 的值为 pywintypes.datetime(2017, 3, 11, 3, 46, 47))
      • It's just a convenience function, it converts a PyEventLogRecord object into a Python dictionary
      • The conversion is based on the fact that fields that we care about start with a capital letter (EventID, ComputerName, TimeGenerated, ...), that's why it shouldn't be used in production
      • It doesn't convert the actual values (TimeGenerated's value is pywintypes.datetime(2017, 3, 11, 3, 46, 47))
      • Returns a list of PyEventLogRecords
      • As in get_events_xmls's case note the backwards argument
      • I must insist on buf_size. As [MS.Docs]: ReadEventLogW function states, when getting the events, a buffer of max 512K can be used. Now (starting with PyWin32 version 220), it's possible to pass it as an argument (the last one) to win32evtlog.ReadEventLog. Check [SourceForge.hg]: mhammond/pywin32 - Add buffer size parameter for ReadEventLog (patch #143 from cristi fati) for more details. By default, there was a limitation so that the buffer size was hardcoded to 1K. Since every ReadEventLog was accessing the disk, with the new buffer size I got a 10X speed improvement (for ~180K events)

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

      @EDIT0: I couldn't find a way to get all the required info with the Evt* functions family, so I'm getting it from both sources (I enhanced the script that I've previously posted):

      @EDIT1:根据 [MS.Docs]:OpenEventLogW 函数:

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

      If you specify a custom log and it cannot be found, the event logging service opens the Application log; however, there will be no associated message or category string file.

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

      [MS.Docs]: Eventlog Key lists the 3 standard ones. So, that's why it opens the Application log. I've done some small changes to the script to test the sources. I don't know where mmc gets the Setup events from.

      这篇关于将 Python win32evtlog 对象转换为 xml的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆