From 93bb9b65404a05ec9cbbf14c80dbea73c035f0c5 Mon Sep 17 00:00:00 2001 From: hakril Date: Tue, 5 Jun 2018 11:30:25 +0200 Subject: [PATCH] Add doc/sample/test for event log code --- docs/source/evtlog.rst | 92 ++++++ .../samples_output/event_log_eventlog.txt | 55 ++++ samples/event_log/eventlog.py | 65 +++++ tests/test_evtlog.py | 74 +++++ windows/winobject/{evtlog.py => event_log.py} | 269 ++++++++++++++---- windows/winobject/system.py | 8 + 6 files changed, 507 insertions(+), 56 deletions(-) create mode 100644 docs/source/evtlog.rst create mode 100644 docs/source/samples_output/event_log_eventlog.txt create mode 100644 samples/event_log/eventlog.py create mode 100644 tests/test_evtlog.py rename windows/winobject/{evtlog.py => event_log.py} (64%) diff --git a/docs/source/evtlog.rst b/docs/source/evtlog.rst new file mode 100644 index 0000000..739c730 --- /dev/null +++ b/docs/source/evtlog.rst @@ -0,0 +1,92 @@ +Event Log +========= + +.. module:: windows.winobject.event_log + + +Some part of the Event Log WINAPI are not straightforward. + +I have tried to offer some abstraction without completly hidding the some underlying subtilities (for now). + +The current API may need some works to provide simpler/highter level API in the future. + +For now, the best thing to do is look at the sample: + +.. note:: + + See sample :ref:`sample_event_log` + +.. warning:: + + This API have not been tested on real case yet and may be subject to changes. + + +EvtlogManager +""""""""""""" + +.. autoclass:: EvtlogManager + :special-members: __getitem__ + + +Channel +""""""" + +EvtChannel +'''''''''' + +.. autoclass:: EvtChannel + +ChannelConfig +''''''''''''' + +.. autoclass:: ChannelConfig + + +Publisher +""""""""" + +EvtPublisher +'''''''''''' + +.. autoclass:: EvtPublisher + +PublisherMetadata +''''''''''''''''' + +.. autoclass:: PublisherMetadata + + +EvtFile +""""""" + +.. autoclass:: EvtFile + + +Event +""""" + +EvtEvent +'''''''' + +.. autoclass:: EvtEvent + +EventMetadata +''''''''''''' + +.. autoclass:: EventMetadata + + +EvtQuery +'''''''' + +.. autoclass:: EvtQuery + + + +TODO +"""" + +.. autoclass:: PropertyArray + + + diff --git a/docs/source/samples_output/event_log_eventlog.txt b/docs/source/samples_output/event_log_eventlog.txt new file mode 100644 index 0000000..8db1868 --- /dev/null +++ b/docs/source/samples_output/event_log_eventlog.txt @@ -0,0 +1,55 @@ +(cmd) python event_log\eventlog.py +Event log Manager is: +They are <1155> channels +They are <1179> publishers +Openning channel +Channel is +The channel contains <1037> events + +Querying "Event/EventData[Data='C:\WINDOWS\System32\svchost.exe'] and Event/System[EventID=2006]"> +Query is +List contains 304 event + +First event is +System values: + * ID: 2006 + * version: 0 + * level: 4 + * opcode: 0 + * time_created: 131700673862101088 + * ID: 2006 +Event specific values: + * -> <108703760> + * -> + * -> + * -> <{318EF1CF-A3FA-4B04-8AAC-712276661117}> + +Event metadata is + * id : 2006 + * channel_id : 16 + * message_id : 2986346454 + * event_data : [u'RuleId', u'RuleName', u'ModifyingUser', u'ModifyingApplication'] + * EventData template : + + + +Exploring complex Evt types: +Channel is still +Channel config is +Channel publisher is +Channel publisher metadata is +Publisher's channels are: + * + * + * + * + * +Some publisher's event metadata are: + * : id=2000 + * : id=2001 + * : id=2002 diff --git a/samples/event_log/eventlog.py b/samples/event_log/eventlog.py new file mode 100644 index 0000000..b9ee778 --- /dev/null +++ b/samples/event_log/eventlog.py @@ -0,0 +1,65 @@ +import windows +import windows.generated_def as gdef + +evtlogmgr = windows.system.event_log +print("Event log Manager is: {0}".format(evtlogmgr)) +print("They are <{0}> channels".format(len(list(evtlogmgr.channels)))) +print("They are <{0}> publishers".format(len(list(evtlogmgr.publishers)))) + + +FIREWALL_CHANNEL = "Microsoft-Windows-Windows Firewall With Advanced Security/Firewall" +print("Openning channel <{0}>".format(FIREWALL_CHANNEL)) +evtchan = evtlogmgr[FIREWALL_CHANNEL] +print("Channel is {0}".format(evtchan)) +# Note that `evtchan.events` is an alias for `evtchan.query().all()` +print("The channel contains <{0}> events".format(len(evtchan.events))) + +print("") +EVT_QUERY = "Event/EventData[Data='C:\\WINDOWS\\System32\\svchost.exe'] and Event/System[EventID=2006]" +print("""Querying "{0}">""".format(EVT_QUERY)) +query = evtchan.query(EVT_QUERY) +print("Query is {0}".format(query)) +event_list = list(query) +print("List contains {0} event".format(len(event_list))) +event = event_list[0] + +print("") +print("First event is {0}".format(event)) +print("System values:") +print(" * ID: {0}".format(event.id)) +print(" * version: {0}".format(event.version)) +print(" * level: {0}".format(event.level)) +print(" * opcode: {0}".format(event.opcode)) +print(" * time_created: {0}".format(event.time_created)) +print(" * ID: {0}".format(event.id)) + +print("Event specific values:") +for name, value in event.data.items(): + print(" * <{0}> -> <{1}>".format(name, value)) + +print("") +evtmeta = event.metadata +print("Event metadata is {0}".format(evtmeta)) +print(" * id : {0}".format(evtmeta.id)) +print(" * channel_id : {0}".format(evtmeta.channel_id)) +print(" * message_id : {0}".format(evtmeta.message_id)) +print(" * event_data : {0}".format(evtmeta.event_data)) +print(" * EventData template :\n{0}".format(evtmeta.template.replace("\r\n", "\n"))) + + +print("") +print("Exploring complex Evt types:") + +print("Channel is still {0}".format(evtchan)) +print("Channel config is {0}".format(evtchan.config)) +publisher = evtchan.config.publisher +print("Channel publisher is {0}".format(publisher)) +print("Channel publisher metadata is {0}".format(publisher.metadata)) + +print("Publisher's channels are:") +for chan in publisher.metadata.channels: + print(" * {0}".format(chan)) + +print("Some publisher's event metadata are:") +for evtmeta in list(publisher.metadata.events_metadata)[:3]: + print(" * {0}: id={1}".format(evtmeta, evtmeta.id)) diff --git a/tests/test_evtlog.py b/tests/test_evtlog.py new file mode 100644 index 0000000..f8480a3 --- /dev/null +++ b/tests/test_evtlog.py @@ -0,0 +1,74 @@ +import pytest +import subprocess +import os.path + +import windows +import windows.generated_def as gdef + +import windows.winobject.event_log as evtl + + +CHANNEL_NAME = "Microsoft-Windows-Windows Firewall With Advanced Security/Firewall" +PUBLISHER_NAME = "Microsoft-Windows-Windows Firewall With Advanced Security" + +ALL_FIREWALL_CHAN = ["Microsoft-Windows-Windows Firewall With Advanced Security/Firewall", + "Microsoft-Windows-Windows Firewall With Advanced Security/ConnectionSecurity", + "Microsoft-Windows-Windows Firewall With Advanced Security/FirewallVerbose", + "Microsoft-Windows-Windows Firewall With Advanced Security/ConnectionSecurityVerbose", + "Network Isolation Operational"] + + +@pytest.mark.parametrize("name, publisher_name", [(CHANNEL_NAME, PUBLISHER_NAME)]) +def test_event_channel(name, publisher_name): + chan = windows.system.event_log[name] + assert isinstance(chan, evtl.EvtChannel) + assert chan.name == name + assert chan.config.publisher.name == publisher_name + assert not chan.config.classic + +@pytest.mark.parametrize("name, eventid", [(CHANNEL_NAME, 2004)]) +def test_event_channel_query(name, eventid): + chan = windows.system.event_log[name] + all_events = chan.events + assert len(all_events) # Should have some event to test | skip else ? + eventquery = chan.query(ids=2004) + assert isinstance(eventquery, evtl.EvtQuery) + all_id_events = eventquery.all() + assert len(all_id_events) + assert len(all_id_events) <= len(all_events) + assert all(evt.id == eventid for evt in all_id_events) + # Extract event metadata + event_data_names = chan.get_event_metadata(eventid).event_data + # Check all event data match event metadata description + for evt in all_id_events: + assert set(evt.data.keys()) == set(event_data_names) + + +@pytest.mark.parametrize("name, chans, eventid", [(PUBLISHER_NAME, ALL_FIREWALL_CHAN, 2004)]) +def test_event_publisher(name, chans, eventid): + publisher = windows.system.event_log[name] + assert isinstance(publisher, evtl.EvtPublisher) + assert publisher.name == name + pmetadata = publisher.metadata + assert set(chan.name for chan in pmetadata.channels) == set(chans) + assert eventid in [evtmedata.id for evtmedata in pmetadata.events_metadata] + +POWERSHELL_PATH = r"C:\Windows\System32\WindowsPowershell\v1.0\powershell.exe" +POWERSHELL_ARG = "PFW_TEST_STRING.NOTEXISTS" + +def test_new_event(): + chan = windows.system.event_log["Microsoft-Windows-PowerShell/Operational"] + pre_events = chan.events + p = windows.utils.create_process(POWERSHELL_PATH, ["PFW_TEST_STRING.NOTEXISTS"], show_windows=False) + p.wait() + import time; time.sleep(1) # It seems to take some time to log the event + post_events = chan.events + assert len(post_events) > len(pre_events) + nb_new_events = len(post_events) - len(pre_events) + new_events = post_events[-nb_new_events:] + # Check that some new event were triggered by our powershell + # TODO: should be nice to find simpler event log to trigger with controled data + assert any(evt.pid == p.pid for evt in new_events) + + + diff --git a/windows/winobject/evtlog.py b/windows/winobject/event_log.py similarity index 64% rename from windows/winobject/evtlog.py rename to windows/winobject/event_log.py index 1f645f4..32dc66e 100644 --- a/windows/winobject/evtlog.py +++ b/windows/winobject/event_log.py @@ -52,6 +52,7 @@ publishinfo = generate_query_function(winproxy.EvtGetPublisherMetadataProperty) # Class high-level API class EvtQuery(gdef.EVT_HANDLE): + """Represent an Event-log query""" TIMEOUT = 0x1000 def __init__(self, handle=0, channel=None): @@ -59,6 +60,7 @@ class EvtQuery(gdef.EVT_HANDLE): self.channel = channel def __next__(self): + """Return the next :class:`EvtEvent` matching the query""" try: event = EvtEvent(channel=self.channel) ret = gdef.DWORD() @@ -76,10 +78,15 @@ class EvtQuery(gdef.EVT_HANDLE): next = __next__ # Yep.. real name is 'next' in Py2 :D def all(self): # SqlAlchemy like :) + """Return a list with all the query results + + :rtype: [:class:`EvtEvent`] -- A list of Event + """ return list(self) class EvtEvent(gdef.EVT_HANDLE): + """An Event log""" def __init__(self, handle=0, channel=None): super(EvtEvent, self).__init__(handle) self.channel = channel @@ -111,6 +118,10 @@ class EvtEvent(gdef.EVT_HANDLE): return xml[:-1] def value(self, name, **kwargs): + """Retrieve a value from the event. + ``name`` is an XPath expressions that uniquely identify a node or attribute in the event. + (see https://msdn.microsoft.com/en-us/library/windows/desktop/aa385352(v=vs.85).aspx) + """ values = self.get_values((name,), **kwargs) assert len(values) == 1 return values[0] @@ -126,12 +137,12 @@ class EvtEvent(gdef.EVT_HANDLE): result = self.render(ctx, gdef.EvtRenderEventValues) return [r.value for r in result] - def system(self): # POC: use this for all @property based on system data ? + def system_values(self): # POC: use this for all @property based on system data ? ctx = windows.winproxy.EvtCreateRenderContext(0, None, gdef.EvtRenderContextSystem) result = self.render(ctx, gdef.EvtRenderEventValues) return [r.value for r in result] - def user(self): + def event_values(self): ctx = windows.winproxy.EvtCreateRenderContext(0, None, gdef.EvtRenderContextUser) result = self.render(ctx, gdef.EvtRenderEventValues) return [r.value for r in result] @@ -139,33 +150,55 @@ class EvtEvent(gdef.EVT_HANDLE): # Properties arround common Event/System values @property def id(self): + """The ID of the Event""" return self.value("Event/System/EventID") @property def version(self): + """The version of the Event""" return self.value("Event/System/Version") @property def level(self): + """The level of the Event""" return self.value("Event/System/Level") @property def opcode(self): + """The opcode of the Event""" return self.value("Event/System/Opcode") @property def time_created(self): + """The creation time of the Event""" return self.value("Event/System/TimeCreated/@SystemTime") + @property + def pid(self): + """The process ID of the Event""" + return self.value("Event/System/Execution/@ProcessID") + + @property + def tid(self): + """The process ID of the Event""" + return self.value("Event/System/Execution/@ThreadID") + @property def metadata(self): - "HEAVY CALL" + """The medata for the current Event + + :type: :class:`EventMetadata` + """ return self.channel.get_event_metadata(self.id) # Test @property def data(self): # user/event specifique data - return {k:v for k,v in zip(self.metadata.user_data, self.user())} + """A dict of EventData Name:Value for the current dict. + + :type: :class:`dict` + """ + return {k:v for k,v in zip(self.metadata.event_data, self.event_values())} def __repr__(self): @@ -216,43 +249,8 @@ class ImprovedEVT_VARIANT(gdef.EVT_VARIANT): return "<{0} of type={1}>".format(type(self).__name__, self.Type) -def channels(): - h = windows.winproxy.EvtOpenChannelEnum(None, 0) - size = 0x1000 - buffer = ctypes.create_unicode_buffer(size) - ressize = gdef.DWORD() - try: - while True: - try: - windows.winproxy.EvtNextChannelPath(h, size, buffer, ressize) - except WindowsError as e: - if e.winerror != gdef.ERROR_NO_MORE_ITEMS: - raise - return - assert buffer[ressize.value - 1] == "\x00" - yield buffer[:ressize.value - 1] - finally: # TODO: ctx manager - windows.winproxy.EvtClose(h) -def publishers(): - h = windows.winproxy.EvtOpenPublisherEnum(None, 0) - size = 0x1000 - buffer = ctypes.create_unicode_buffer(size) - ressize = gdef.DWORD() - try: - while True: - try: - windows.winproxy.EvtNextPublisherId(h, size, buffer, ressize) - except WindowsError as e: - if e.winerror != gdef.ERROR_NO_MORE_ITEMS: - raise - return - assert buffer[ressize.value - 1] == "\x00" - yield buffer[:ressize.value - 1] - finally: # TODO: ctx manager - windows.winproxy.EvtClose(h) - # x = windows.winproxy.EvtQuery(None, @@ -273,13 +271,33 @@ def publishers(): # list(channels()) class EvtChannel(object): + """An Event Log channel""" DEFAULT_QUERY_FLAGS = gdef.EvtQueryChannelPath + gdef.EvtQueryForwardDirection def __init__(self, name): self.name = name self.event_metadata_by_id = {} - def query(self, ids=None, filter=None): + def query(self, filter=None, ids=None): + """Query the event with the ``ids`` or perform a query with the raw query ``filter`` + + Both parameters are mutually exclusive. + + .. note:: Here are some query examples + + List all events with a event data attribute named 'RuleName': + ``Event/EventData/Data[@Name='RuleName']`` + + List all events with a event data value of 'C:\\\\WINDOWS\\\\System32\\\\svchost.exe': + ``Event/EventData[Data='C:\\WINDOWS\\System32\\svchost.exe']`` + + List all events with an EventID of 2006: + ``Event/System[EventID=2006]`` + + :rtype: :class:`EvtQuery` + """ + if ids and filter: + raise ValueError(" and are mutually exclusive") if ids is not None: if isinstance(ids, (long, int)): ids = (ids,) @@ -288,11 +306,27 @@ class EvtChannel(object): query_handle = winproxy.EvtQuery(None, self.name, filter, self.DEFAULT_QUERY_FLAGS) return EvtQuery(query_handle, self) + @property + def events(self): + """The list of all events in the channels, an alias for ``channel.query().all()`` + + :type: [:class:`EvtEvent`] -- A list of :class:`EvtEvent` + """ + return self.query().all() + @property def config(self): + """The configuration of the channel + + :type: :class:`ChannelConfig` + """ return ChannelConfig.from_channel_name(self.name) def get_event_metadata(self, id): + """Return the metadata for the event ID ``id`` + + :rtype: :class:`EventMetadata` + """ try: return self.event_metadata_by_id[id] except KeyError as e: @@ -308,36 +342,55 @@ class EvtChannel(object): class EvtFile(EvtChannel): + """Represent an Evtx file""" DEFAULT_QUERY_FLAGS = gdef.EvtQueryFilePath + gdef.EvtQueryForwardDirection @property def config(self): + """Not implemented for EvtFile + + :raise: :class:`NotImplementedError` + """ raise NotImplementedError("Cannot retrieve the configuration of an EvtFile") class ChannelConfig(gdef.EVT_HANDLE): + """The configuration of a event channel""" def __init__(self, handle, name=None): super(ChannelConfig, self).__init__(handle) self.name = name @classmethod - def from_channel_name(cls, channel_name): - return cls(winproxy.EvtOpenChannelConfig(None, channel_name, 0), channel_name) + def from_channel_name(cls, name): + """Return the :class:`ChannelConfig` for the channel ``name``""" + return cls(winproxy.EvtOpenChannelConfig(None, name, 0), name) @property def publisher(self): + """The :class:`EvtPublisher` for the channel""" return EvtPublisher(chaninfo(self, gdef.EvtChannelConfigOwningPublisher).value) + + @property + def classic(self): + """``True`` if the channel is a classic event channel (for example the Application or System log)""" + return bool(chaninfo(self, gdef.EvtChannelConfigClassicEventlog).value) + def __repr__(self): return '<{0} "{1}">'.format(type(self).__name__, self.name) class EvtPublisher(object): + """An Event provider""" def __init__(self, name): self.name = name @property def metadata(self): + """Return the metadata for this publisher + + :type: :class:`PublisherMetadata` + """ return PublisherMetadata.from_publisher_name(self.name) def __repr__(self): @@ -345,20 +398,30 @@ class EvtPublisher(object): class PublisherMetadata(gdef.EVT_HANDLE): + """The metadata about an event provider""" def __init__(self, handle, name=None): super(PublisherMetadata, self).__init__(handle) self.name = name @classmethod - def from_publisher_name(cls, publisher_name): - return cls(winproxy.EvtOpenPublisherMetadata(None, publisher_name, None, 0, 0), publisher_name) + def from_publisher_name(cls, name): + """The :class:`PublisherMetadata` for the publisher ``name``""" + return cls(winproxy.EvtOpenPublisherMetadata(None, name, None, 0, 0), name) @property def chanrefs(self): + """Identifies the channels child element of the provider. + + :type: :class:`PropertyArray` + """ return PropertyArray(publishinfo(self, gdef.EvtPublisherMetadataChannelReferences).value) @property def events_metadata(self): + """The :class:`EventMetadata` for each event this provider defines + + :yield: :class:`EventMetadata` + """ eh = winproxy.EvtOpenEventMetadataEnum(self, 0) with ClosingEvtHandle(eh): while True: @@ -370,16 +433,32 @@ class PublisherMetadata(gdef.EVT_HANDLE): raise break + @property def channel_name_by_id(self): + """The dict of channel defined by this provider by their id + + :type: :class:`dict` + """ chansref = self.chanrefs channame_by_value_id = {} for i in range(chansref.size): - value = chansref.propery(gdef.EvtPublisherMetadataChannelReferenceID, i) - name = chansref.propery(gdef.EvtPublisherMetadataChannelReferencePath, i) + value = chansref.property(gdef.EvtPublisherMetadataChannelReferenceID, i) + name = chansref.property(gdef.EvtPublisherMetadataChannelReferencePath, i) channame_by_value_id[value] = name return channame_by_value_id + @property + def channels(self): + """The list of :class:`EvtChannel` defined by this provider + + :type: [:class:`EvtChannel`] -- A list of :class:`EvtChannel` + """ + chansref = self.chanrefs + propertyid = gdef.EvtPublisherMetadataChannelReferencePath + return [EvtChannel(chansref.property(propertyid, i)) for i in range(chansref.size)] + def message(self, msgid): + "TODO" size = 0x1000 buffer = ctypes.c_buffer(size) sbuff = ctypes.cast(buffer, gdef.LPWSTR) @@ -396,65 +475,143 @@ class PublisherMetadata(gdef.EVT_HANDLE): class PropertyArray(gdef.EVT_OBJECT_ARRAY_PROPERTY_HANDLE): + "TODO" @property def size(self): array_size = gdef.DWORD() windows.winproxy.EvtGetObjectArraySize(self, array_size) return array_size.value - def propery(self, type, index): + def property(self, type, index): return arrayproperty(self, type, index).value class EventMetadata(gdef.EVT_HANDLE): + """The Metadata about a given Event type + + + see: https://msdn.microsoft.com/en-us/library/windows/desktop/aa385517(v=vs.85).aspx + """ + @property def id(self): + """The ID of the Event""" return eventinfo(self, gdef.EventMetadataEventID).value @property def channel_id(self): + """The the Channel attribute of the Event definition""" return eventinfo(self, gdef.EventMetadataEventChannel).value @property def message_id(self): + """Identifies the message attribute of the event definition.""" return eventinfo(self, gdef.EventMetadataEventMessageID).value @property def template(self): + """Identifies the template attribute of the event definition which is an XML string""" return eventinfo(self, gdef.EventMetadataEventTemplate).value @property - def user_data(self): + def event_data(self): + """The list of attribute specifique for this event. + Retrieved by parsing :data:`EventMetadata.template` + """ result = [] - xmltemplate = xml.dom.minidom.parseString(self.template) + template = self.template + if not template: + return {} + xmltemplate = xml.dom.minidom.parseString(template) for data in xmltemplate.getElementsByTagName("data"): result.append(data.attributes["name"].value) return result class EvtlogManager(object): - "TODO: doc -> the frontend API object" + """The main Evt class to open Evt channel/publisher and evtx file""" # https://msdn.microsoft.com/en-us/library/windows/desktop/aa385784(v=vs.85).aspx - def open_channel(self, channel_name): - chan = EvtChannel(channel_name) + def is_implemented(self): + """Return ``True`` if the new Evt-API is implemented on the current computer + + see: https://msdn.microsoft.com/en-us/library/windows/desktop/aa385784(v=vs.85).aspx + """ + return windows.winproxy.is_implemented(windows.winproxy.EvtQuery) + + @property + def channels(self): + h = windows.winproxy.EvtOpenChannelEnum(None, 0) + size = 0x1000 + buffer = ctypes.create_unicode_buffer(size) + ressize = gdef.DWORD() + with ClosingEvtHandle(h): + while True: + try: + windows.winproxy.EvtNextChannelPath(h, size, buffer, ressize) + except WindowsError as e: + if e.winerror != gdef.ERROR_NO_MORE_ITEMS: + raise + return + assert buffer[ressize.value - 1] == "\x00" + name = buffer[:ressize.value - 1] + chan = EvtChannel(name) + yield chan + + @property + def publishers(self): + h = windows.winproxy.EvtOpenPublisherEnum(None, 0) + size = 0x1000 + buffer = ctypes.create_unicode_buffer(size) + ressize = gdef.DWORD() + with ClosingEvtHandle(h): + while True: + try: + windows.winproxy.EvtNextPublisherId(h, size, buffer, ressize) + except WindowsError as e: + if e.winerror != gdef.ERROR_NO_MORE_ITEMS: + raise + return + assert buffer[ressize.value - 1] == "\x00" + name = buffer[:ressize.value - 1] + publisher = EvtPublisher(name) + yield publisher + + + + def open_channel(self, name): + """Open the Evt channel with ``name`` + + :rtype: :class:`EvtChannel` + """ + chan = EvtChannel(name) chan.config # Force to retrieve a handle (check channel exists) return chan def open_evtx_file(self, filename): - # We don't check if file exists because of 32->64 redirection - # Do a check with FSRedirection disabled ? + """Open the evtx file with ``filename`` + + :rtype: :class:`EvtFile` + """ with windows.utils.DisableWow64FsRedirection(): if not os.path.exists(filename): raise WindowsError(gdef.ERROR_FILE_NOT_FOUND, "Could not find file <{0}>".format(filename)) file = EvtFile(filename) return file - def open_publisher(self, publisher_name): - publisher = EvtPublisher(publisher_name) + def open_publisher(self, name): + """Open the Evt publisher with ``name`` + + :rtype: :class:`EvtPublisher` + """ + publisher = EvtPublisher(name) publisher.metadata # Force to retrieve a handle (check channel exists) return publisher def __getitem__(self, name): + """Open the Evt Channel/Publisher or Evtx file with ``name`` + + :rtype: :class:`EvtChannel` or :class:`EvtPublisher` or :class:`EvtFile` + """ try: return self.open_channel(name) except WindowsError as e: diff --git a/windows/winobject/system.py b/windows/winobject/system.py index 52a2709..2a5dd54 100644 --- a/windows/winobject/system.py +++ b/windows/winobject/system.py @@ -18,6 +18,7 @@ from windows.winobject import volume from windows.winobject import wmi from windows.winobject import kernobj from windows.winobject import handle +from windows.winobject import event_log from windows.winobject import task_scheduler from windows.generated_def.winstructs import * @@ -89,6 +90,11 @@ class System(object): return wmi.WmiManager() + @utils.fixedpropety + def event_log(self): + return event_log.EvtlogManager() + + @utils.fixedpropety def task_scheduler(self): """An object able to manage scheduled tasks on the local system @@ -98,6 +104,8 @@ class System(object): windows.com.init() clsid_task_scheduler = gdef.IID.from_string("0f87369f-a4e5-4cfc-bd3e-73e6154572dd") task_service = task_scheduler.TaskService() + # What is non-implemented (WinXP) + # Raise (NotImplementedError?) ? Return NotImplemented ? windows.com.create_instance(clsid_task_scheduler, task_service) task_service.connect() return task_service