Skip to content

Commit 0f49dff

Browse files
committed
Added some docstrings
1 parent 9bf2e61 commit 0f49dff

6 files changed

Lines changed: 202 additions & 14 deletions

File tree

windows/security.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,17 @@ def to_filename(self, filename, flags=0):
890890
flags |= gdef.SACL_SECURITY_INFORMATION
891891
winproxy.SetNamedSecurityInfoW(filename, gdef.SE_FILE_OBJECT, flags, self.owner, self.group, self.dacl, self.sacl)
892892

893+
def to_handle(self, handle, flags=0):
894+
if not flags:
895+
if self.owner:
896+
flags |= gdef.OWNER_SECURITY_INFORMATION
897+
if self.group:
898+
flags |= gdef.GROUP_SECURITY_INFORMATION
899+
if self.dacl:
900+
flags |= gdef.DACL_SECURITY_INFORMATION
901+
if self.sacl:
902+
flags |= gdef.SACL_SECURITY_INFORMATION
903+
self._apply_to_handle_and_type(handle, gdef.SE_FILE_OBJECT, flags)
893904

894905
@classmethod
895906
def from_service(cls, filename, query_sacl=False, flags=SERVICE_SECURITY_INFORMATION):
@@ -978,11 +989,11 @@ def explain_mask(mask, sdtype):
978989
def explain_simple_ace(ace, sdtype):
979990
yield u"Type:"
980991
yield u" " + str(ace.Header.AceType)
981-
yield u"Flags:"
982-
yield u" {0:#x}".format(ace.Header.AceFlags)
992+
yield u"Flags: {0:#x}".format(ace.Header.AceFlags)
993+
yield u" {0}".format(ace.Header.flags)
983994
yield u"SID:"
984995
yield u" " + explain_sid(ace.sid)
985-
yield u"Mask:"
996+
yield u"Mask: {0:#x}".format(ace.Mask)
986997
mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype]
987998
yield u" " + str(list(mapper[x] for x in ace.mask))
988999

windows/winobject/event_log.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ def provider(self):
214214
"""The provider of the event"""
215215
return self.system_values()[gdef.EvtSystemProviderName]
216216

217+
@property
218+
def computer(self):
219+
"""The computer that generated the event"""
220+
return self.system_values()[gdef.EvtSystemComputer]
221+
217222
@property
218223
def id(self):
219224
"""The ID of the Event"""
@@ -249,6 +254,16 @@ def tid(self):
249254
"""The process ID of the Event"""
250255
return self.value("Event/System/Execution/@ThreadID")
251256

257+
@property
258+
def error_payload(self):
259+
raw = self.value("Event/ProcessingErrorData/EventPayload")
260+
return bytearray(raw) if raw is not None else None
261+
262+
@property
263+
def user(self):
264+
"""The User ID associated with the Event"""
265+
return self.system_values()[gdef.EvtSystemUserID]
266+
252267
@property
253268
def metadata(self):
254269
"""The medata for the current Event
@@ -276,6 +291,45 @@ def data(self): # user/event specifique data
276291
event_data_name = (i["name"] for i in self.metadata.event_data if i["type"] == "data")
277292
return {k:v for k,v in zip(event_data_name, self.event_values())}
278293

294+
def xml_data(self):
295+
xmlevt = xml.dom.minidom.parseString(self.render_xml())
296+
res = {}
297+
298+
eventdata = xmlevt.getElementsByTagName("EventData")
299+
if eventdata:
300+
# <Data Name='FIELD_NAME'>FIELD_VALUE</Data>
301+
for i, datanode in enumerate(xmlevt.getElementsByTagName("Data")):
302+
name = datanode.getAttribute("Name")
303+
if not name:
304+
# Some Data in old EVTX have no name (Windows Powershell)
305+
# Do the best we can by using the position of the event
306+
name = str(i)
307+
308+
if datanode.hasChildNodes():
309+
value = datanode.firstChild.nodeValue
310+
else:
311+
value = ""
312+
if not (name not in res):
313+
import pdb;pdb.set_trace()
314+
res[name] = value
315+
userdata = xmlevt.getElementsByTagName("UserData")
316+
if userdata:
317+
# <UserData>
318+
# <EventXML xmlns="Event_NS">
319+
# <FIELD_NAME>FIELD_VALUE</FIELD_NAME>
320+
# </EventXML>
321+
# </UserData>
322+
for datanode in userdata[0].firstChild.childNodes:
323+
name = datanode.tagName
324+
if datanode.hasChildNodes():
325+
value = datanode.firstChild.nodeValue
326+
else:
327+
value = ""
328+
assert name not in res
329+
res[name] = value
330+
return res
331+
332+
279333
@property
280334
def date(self):
281335
"""``Event.time_created`` as a :class:``datetime``"""

windows/winobject/event_trace.py

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ def level(self):
5151

5252
@property
5353
def context(self):
54+
if self.UserContext is None:
55+
return None
5456
return ctypes.py_object.from_address(self.UserContext).value
5557

5658
@property
@@ -74,12 +76,17 @@ def __repr__(self):
7476
PEventRecord = ctypes.POINTER(EventRecord)
7577

7678
class EventTraceProperties(gdef.EVENT_TRACE_PROPERTIES):
79+
"""Represent an Event Trace session that may exist or now. (https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-event_trace_properties)
80+
81+
This class is widly used by :class:`EtwTrace`
82+
"""
7783
# Test: ascii / Use Wchar ?
7884
FULL_SIZE = ctypes.sizeof(gdef.EVENT_TRACE_PROPERTIES) + MAX_SESSION_NAME_LEN_W + MAX_LOGFILE_PATH_LEN_W
7985

8086
# def alloc(cls, size) ?
8187
@classmethod
8288
def create(cls):
89+
"""Initialize a new :class:`EventTraceProperties`"""
8390
buff = windows.utils.BUFFER(cls)(size=cls.FULL_SIZE)
8491
# ctypes.memset(buff, "\x00", cls.FULL_SIZE)
8592
self = buff[0]
@@ -92,14 +99,13 @@ def get_logfilename(self):
9299
assert self.LogFileNameOffset
93100
return windows.current_process.read_string(ctypes.addressof(self) + self.LogFileNameOffset)
94101

95-
96102
def set_logfilename(self, filename):
97103
assert self.LogFileNameOffset
98104
if not filename.endswith("\x00"):
99105
filename += "\x00"
100106
return windows.current_process.write_memory(ctypes.addressof(self) + self.LogFileNameOffset, filename)
101107

102-
logfile = property(get_logfilename, set_logfilename)
108+
logfile = property(get_logfilename, set_logfilename) #: The logfile associated with the session
103109

104110
def get_logger_name(self):
105111
assert self.LoggerNameOffset
@@ -112,17 +118,23 @@ def set_logfilename(self, filename):
112118
filename += "\x00"
113119
return windows.current_process.write_memory(ctypes.addressof(self) + self.LoggerNameOffset, filename)
114120

115-
name = property(get_logger_name, set_logfilename)
121+
name = property(get_logger_name, set_logfilename) #: The name of the session
116122

117123
@property
118124
def guid(self):
125+
"""The GUID of the Event Trace session (see ``Wnode.Guid``)"""
119126
return self.Wnode.Guid
120127

121128
@property
122129
def id(self):
123-
"""LoggerId"""
130+
"""The LoggerId if the session (see ``Wnode.HistoricalContext``)"""
124131
return self.Wnode.HistoricalContext
125132

133+
134+
def __repr__(self):
135+
return """<{0} name="{1}" guid={2}>""".format(type(self).__name__, self.name, self.guid)
136+
137+
126138
# GUID setter ?
127139

128140
class CtxProcess(object):
@@ -154,15 +166,17 @@ def __exit__(self, exc_type, exc_value, traceback):
154166

155167

156168
class EtwTrace(object):
169+
"""Represent an ETW Trace for tracing/processing events"""
157170
def __init__(self, name, logfile=None, guid=None):
158-
self.name = windows.pycompat.raw_encode(name)
159-
self.logfile = logfile
171+
self.name = windows.pycompat.raw_encode(name) #: The name of the trace
172+
self.logfile = logfile #: The logging file of the trace (``None`` means real time trace)
160173
if guid and isinstance(guid, basestring):
161174
guid = gdef.GUID.from_string(guid)
162-
self.guid = guid
175+
self.guid = guid #: The guid of the trace
163176
self.handle = 0
164177

165178
def exists(self):
179+
"""Return ``True`` if the trace already exist (based on its name)"""
166180
prop = EventTraceProperties.create()
167181
try:
168182
windows.winproxy.ControlTraceA(self.handle, self.name, prop, gdef.EVENT_TRACE_CONTROL_QUERY)
@@ -173,6 +187,7 @@ def exists(self):
173187
return True
174188

175189
def start(self, flags=0):
190+
"""Start the tracing"""
176191
prop = EventTraceProperties.create()
177192
prop.NumberOfBuffers = 42
178193
prop.EnableFlags = flags
@@ -189,6 +204,11 @@ def start(self, flags=0):
189204
self.handle = handle
190205

191206
def stop(self, soft=False): # Change name
207+
"""stop the tracing.
208+
209+
``soft`` will allow to stop a non-existing trace that do not exists/run.
210+
This allow for simpler script that stop/start some EtwTrace.
211+
"""
192212
prop = EventTraceProperties.create()
193213
try:
194214
windows.winproxy.ControlTraceA(0, self.name, prop, gdef.EVENT_TRACE_CONTROL_STOP)
@@ -199,16 +219,19 @@ def stop(self, soft=False): # Change name
199219
return True
200220

201221
def flush(self):
222+
"""Flush the trace"""
202223
prop = EventTraceProperties.create()
203224
windows.winproxy.ControlTraceA(0, self.name, prop, gdef.EVENT_TRACE_CONTROL_FLUSH)
204225

205226

206227
def enable(self, guid, flags=0xff, level=0xff):
228+
"""Enable the specified event trace provider."""
207229
if isinstance(guid, basestring):
208230
guid = gdef.GUID.from_string(guid)
209231
return windows.winproxy.EnableTrace(1, flags, level, guid, self.handle) # EnableTraceEx ?
210232

211233
def enable_ex(self, guid, flags=0xff, level=0xff, any_keyword = 0xffffffff, all_keyword=0x00):
234+
"""Enable the specified event trace provider."""
212235
if isinstance(guid, basestring):
213236
guid = gdef.GUID.from_string(guid)
214237

@@ -223,6 +246,17 @@ def enable_ex(self, guid, flags=0xff, level=0xff, any_keyword = 0xffffffff, all_
223246

224247

225248
def process(self, callback, begin=None, end=None, context=None):
249+
"""Process the event retrieved by the trace.
250+
This function will call ``callback`` with any :class:`EventRecord` in the trace.
251+
``begin/end`` allow to filter and only process events in a given timeframe.
252+
253+
.. warning::
254+
255+
If the trace if ``REALTIME`` (no logfile) this function will hang/process new event until the trace is stopped.
256+
257+
Using ``logman -ets stop TRACE_NAME`` for exemple.
258+
259+
"""
226260
if end == "now":
227261
end = gdef.FILETIME()
228262
windows.winproxy.GetSystemTimeAsFileTime(end)
@@ -272,11 +306,19 @@ def __repr__(self):
272306
return """<{0} name={1!r} logfile={2!r}>""".format(type(self).__name__, self.name, self.logfile)
273307

274308
class TraceProvider(object):
309+
"""Represent a ETW provider, which is just a GUID.
310+
Corresponding name for a provider may be available trhought WMI.
311+
"""
275312
def __init__(self, guid):
276313
self.guid = guid
277314

278315
@property
279316
def infos(self):
317+
"""The :class:`TraceGuidInfo` associated with the provider.
318+
Main use is to retrieve the instances of the provider (directly available with ``instances``)
319+
320+
:type: :class:`TraceGuidInfo`
321+
"""
280322
size = gdef.DWORD()
281323
info_buffer = ctypes.c_buffer(0x1000)
282324
try:
@@ -293,13 +335,21 @@ def infos(self):
293335
# Our trace providers should be able to directly returns its instances
294336
@property
295337
def instances(self):
338+
"""The instances of the provider.
339+
340+
:type: [:class:`TraceProviderInstanceInfo`] -- A list of :class:`TraceProviderInstanceInfo`
341+
"""
296342
return self.infos.instances
297343

298344
def __repr__(self):
299345
return """<{0} for "{1}">""".format(type(self).__name__, self.guid)
300346

301347

302348
class TraceGuidInfo(gdef.TRACE_GUID_INFO):
349+
"""Defines the header to the list of sessions that enabled the provider
350+
(see https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_guid_info)
351+
"""
352+
303353
@classmethod
304354
def from_raw_buffer(cls, buffer):
305355
self = cls.from_buffer(buffer)
@@ -317,13 +367,20 @@ def _instance_generator(self):
317367

318368
@property
319369
def instances(self):
370+
"""The instances of the provider.
371+
372+
:type: [:class:`TraceProviderInstanceInfo`] -- A list of :class:`TraceProviderInstanceInfo`
373+
"""
320374
return [x for x in self._instance_generator()]
321375

322376
def __repr__(self):
323377
return "<{0} InstanceCount={1} Reserved={2}>".format(type(self).__name__, self.InstanceCount, self.Reserved)
324378

325379

326380
class TraceProviderInstanceInfo(gdef.TRACE_PROVIDER_INSTANCE_INFO):
381+
"""Defines an instance of the provider
382+
(see https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_provider_instance_info)
383+
"""
327384
@classmethod
328385
def from_raw_buffer(cls, buffer, offset):
329386
self = cls.from_buffer(buffer, offset)
@@ -340,15 +397,25 @@ def _instance_generator(self):
340397

341398
@property
342399
def sessions(self):
400+
"""The sessions for the instance
401+
402+
:type: [:class:`~windows.generated_def.winstructs.TRACE_ENABLE_INFO`] -- A list of session
403+
"""
343404
return [x for x in self._instance_generator()]
344405

345406
def __repr__(self):
346407
return "<{0} Pid={1} EnableCount={2}>".format(type(self).__name__, self.Pid, self.EnableCount)
347408

348409

349410
class EtwManager(object):
411+
"""An object to query ETW session/providers and open new trace"""
412+
350413
@property
351414
def sessions(self):
415+
"""The list of currently active ETW session.
416+
417+
:type: [:class:`EventTraceProperties`] -- A list of :class:`EventTraceProperties`
418+
"""
352419
# Create a tuple of MAX_ETW_SESSIONS EventTraceProperties ptr
353420
t = [EventTraceProperties.create() for _ in range(MAX_ETW_SESSIONS)]
354421
# Put this in a ctypes array
@@ -362,12 +429,20 @@ def sessions(self):
362429

363430
@property
364431
def providers(self):
432+
"""The list of currently existing ETW providers.
433+
434+
:type: [:class:`TraceProvider`] -- A list of ETW providers
435+
"""
365436
buffer = windows.utils.BUFFER(gdef.GUID, 0x1000)()
366437
size = gdef.DWORD()
367438
windows.winproxy.EnumerateTraceGuidsEx(gdef.TraceGuidQueryList, None, 0, buffer, buffer.real_size, size)
368-
return [TraceProvider(g) for g in buffer[:size.value / ctypes.sizeof(gdef.GUID)]]
439+
return [TraceProvider(g) for g in buffer[:size.value // ctypes.sizeof(gdef.GUID)]]
369440

370441

371442
# Temp name / API ?
372443
def open_trace(self, name=None, logfile=None, guid=None):
444+
"""Open a new ETW Trace
445+
446+
:return: :class:`EtwTrace`
447+
"""
373448
return EtwTrace(name, logfile, guid)

windows/winobject/system.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,18 @@ def wmi(self):
107107

108108
@utils.fixedpropety
109109
def event_log(self):
110+
"""An object to open Event channel/publisher and evtx file
111+
112+
:type: :class:`~windows.winobject.event_log.EvtlogManager`
113+
"""
110114
return event_log.EvtlogManager()
111115

112116
@utils.fixedpropety
113117
def etw(self):
118+
"""An object to interact with ETW (Event Tracing for Windows)
119+
120+
:type: :class:`~windows.winobject.event_trace.EtwManager`
121+
"""
114122
return event_trace.EtwManager()
115123

116124

0 commit comments

Comments
 (0)