pythonxmlpywin32

Converting Python win32evtlog objects to 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.

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:

I tried the convert using

LogObjectXML = win32evtlog.EvtRender(LogObject, 1)

This unfortunately returns

TypeError: The object is not a PyHANDLE object

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.

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.

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

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>

Solution

  • Listing [GitHub]: mhammond/pywin32 - Python for Windows (pywin32) Extensions which is a Python wrapper over WinAPIs. Documentation (WiP) can be found at [GitHub.MHammond]: Python for Win32 Extensions Help (or [ME.TimGolden]: Python for Win32 Extensions Help).

    ReadEventLog returns PyEventLogRecords (wrapper over [MS.Learn]: EVENTLOGRECORD structure (winnt.h)), while EvtRender expects (you need to work with) PyHANDLEs (PyEVT_HANDLEs (wrapper over EVT_HANDLE ([MS.Learn]: 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.

    code00.py:

    #!/usr/bin/env python
    
    import sys
    
    import pywintypes
    import win32evtlog
    
    
    INFINITE = 0xFFFFFFFF
    EVTLOG_READ_BUF_LEN_MAX = 0x7FFFF
    
    STANDARD_LOG_NAMES = ["Application", "System", "Security"]
    
    
    def get_record_data(eventlog_record):
        ret = {}
        for key in dir(eventlog_record):
            if "A" < key[0] < "Z":  # @TODO - cfati: Weak
                ret[key] = getattr(eventlog_record, key)
        return ret
    
    
    def get_eventlogs(source_name="Application", buf_size=EVTLOG_READ_BUF_LEN_MAX, backwards=True):
        ret = []
        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 = []
        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(*argv):
        log_names = STANDARD_LOG_NAMES[:]
        log_names.append("Windows Powershell")  # !!! @TODO - cfati: This works on my machine
        for log_name in log_names:
            print(log_name)
            logs = get_eventlogs(source_name=log_name)
            xmls = get_events_xmls(channel_name=log_name)
            #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} {:03d}bit on {:s}\n".format(
                " ".join(elem.strip() for elem in sys.version.split("\n")),
                64 if sys.maxsize > 0x100000000 else 32,
                sys.platform,
            )
        )
        rc = main(*sys.argv[1:])
        print("\nDone.\n")
        sys.exit(rc)
    

    Output:

    [cfati@CFATI-W10PC064:e:\Work\Dev\StackExchange\StackOverflow\q043911616]> "c:\Work\Dev\VEnvs\py_pc064_03.10_test0\Scripts\python.exe" ./code00.py
    Python 3.10.11 (tags/v3.10.11:7d4cc5a, Apr  5 2023, 00:38:17) [MSC v.1929 64 bit (AMD64)] 064bit on win32
    
    Application
    12068
    12068
    System
    10015
    10015
    Security
    22821
    22821
    Windows Powershell
    2038
    2038
    
    Done.
    

    Notes:

    Might also be interesting to read:



    Update #0

    According to [MS.Learn]: OpenEventLogW function (winbase.h):

    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.Learn]: 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.