Added some docstrings

This commit is contained in:
hakril
2020-07-16 23:33:27 +02:00
parent 9bf2e61431
commit 0f49dff128
6 changed files with 202 additions and 14 deletions
+14 -3
View File
@@ -890,6 +890,17 @@ class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR):
flags |= gdef.SACL_SECURITY_INFORMATION
winproxy.SetNamedSecurityInfoW(filename, gdef.SE_FILE_OBJECT, flags, self.owner, self.group, self.dacl, self.sacl)
def to_handle(self, handle, flags=0):
if not flags:
if self.owner:
flags |= gdef.OWNER_SECURITY_INFORMATION
if self.group:
flags |= gdef.GROUP_SECURITY_INFORMATION
if self.dacl:
flags |= gdef.DACL_SECURITY_INFORMATION
if self.sacl:
flags |= gdef.SACL_SECURITY_INFORMATION
self._apply_to_handle_and_type(handle, gdef.SE_FILE_OBJECT, flags)
@classmethod
def from_service(cls, filename, query_sacl=False, flags=SERVICE_SECURITY_INFORMATION):
@@ -978,11 +989,11 @@ def explain_mask(mask, sdtype):
def explain_simple_ace(ace, sdtype):
yield u"Type:"
yield u" " + str(ace.Header.AceType)
yield u"Flags:"
yield u" {0:#x}".format(ace.Header.AceFlags)
yield u"Flags: {0:#x}".format(ace.Header.AceFlags)
yield u" {0}".format(ace.Header.flags)
yield u"SID:"
yield u" " + explain_sid(ace.sid)
yield u"Mask:"
yield u"Mask: {0:#x}".format(ace.Mask)
mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype]
yield u" " + str(list(mapper[x] for x in ace.mask))
+54
View File
@@ -214,6 +214,11 @@ class EvtEvent(EvtHandle):
"""The provider of the event"""
return self.system_values()[gdef.EvtSystemProviderName]
@property
def computer(self):
"""The computer that generated the event"""
return self.system_values()[gdef.EvtSystemComputer]
@property
def id(self):
"""The ID of the Event"""
@@ -249,6 +254,16 @@ class EvtEvent(EvtHandle):
"""The process ID of the Event"""
return self.value("Event/System/Execution/@ThreadID")
@property
def error_payload(self):
raw = self.value("Event/ProcessingErrorData/EventPayload")
return bytearray(raw) if raw is not None else None
@property
def user(self):
"""The User ID associated with the Event"""
return self.system_values()[gdef.EvtSystemUserID]
@property
def metadata(self):
"""The medata for the current Event
@@ -276,6 +291,45 @@ class EvtEvent(EvtHandle):
event_data_name = (i["name"] for i in self.metadata.event_data if i["type"] == "data")
return {k:v for k,v in zip(event_data_name, self.event_values())}
def xml_data(self):
xmlevt = xml.dom.minidom.parseString(self.render_xml())
res = {}
eventdata = xmlevt.getElementsByTagName("EventData")
if eventdata:
# <Data Name='FIELD_NAME'>FIELD_VALUE</Data>
for i, datanode in enumerate(xmlevt.getElementsByTagName("Data")):
name = datanode.getAttribute("Name")
if not name:
# Some Data in old EVTX have no name (Windows Powershell)
# Do the best we can by using the position of the event
name = str(i)
if datanode.hasChildNodes():
value = datanode.firstChild.nodeValue
else:
value = ""
if not (name not in res):
import pdb;pdb.set_trace()
res[name] = value
userdata = xmlevt.getElementsByTagName("UserData")
if userdata:
# <UserData>
# <EventXML xmlns="Event_NS">
# <FIELD_NAME>FIELD_VALUE</FIELD_NAME>
# </EventXML>
# </UserData>
for datanode in userdata[0].firstChild.childNodes:
name = datanode.tagName
if datanode.hasChildNodes():
value = datanode.firstChild.nodeValue
else:
value = ""
assert name not in res
res[name] = value
return res
@property
def date(self):
"""``Event.time_created`` as a :class:``datetime``"""
+83 -8
View File
@@ -51,6 +51,8 @@ class EventRecord(gdef.EVENT_RECORD):
@property
def context(self):
if self.UserContext is None:
return None
return ctypes.py_object.from_address(self.UserContext).value
@property
@@ -74,12 +76,17 @@ class EventRecord(gdef.EVENT_RECORD):
PEventRecord = ctypes.POINTER(EventRecord)
class EventTraceProperties(gdef.EVENT_TRACE_PROPERTIES):
"""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)
This class is widly used by :class:`EtwTrace`
"""
# Test: ascii / Use Wchar ?
FULL_SIZE = ctypes.sizeof(gdef.EVENT_TRACE_PROPERTIES) + MAX_SESSION_NAME_LEN_W + MAX_LOGFILE_PATH_LEN_W
# def alloc(cls, size) ?
@classmethod
def create(cls):
"""Initialize a new :class:`EventTraceProperties`"""
buff = windows.utils.BUFFER(cls)(size=cls.FULL_SIZE)
# ctypes.memset(buff, "\x00", cls.FULL_SIZE)
self = buff[0]
@@ -92,14 +99,13 @@ class EventTraceProperties(gdef.EVENT_TRACE_PROPERTIES):
assert self.LogFileNameOffset
return windows.current_process.read_string(ctypes.addressof(self) + self.LogFileNameOffset)
def set_logfilename(self, filename):
assert self.LogFileNameOffset
if not filename.endswith("\x00"):
filename += "\x00"
return windows.current_process.write_memory(ctypes.addressof(self) + self.LogFileNameOffset, filename)
logfile = property(get_logfilename, set_logfilename)
logfile = property(get_logfilename, set_logfilename) #: The logfile associated with the session
def get_logger_name(self):
assert self.LoggerNameOffset
@@ -112,17 +118,23 @@ class EventTraceProperties(gdef.EVENT_TRACE_PROPERTIES):
filename += "\x00"
return windows.current_process.write_memory(ctypes.addressof(self) + self.LoggerNameOffset, filename)
name = property(get_logger_name, set_logfilename)
name = property(get_logger_name, set_logfilename) #: The name of the session
@property
def guid(self):
"""The GUID of the Event Trace session (see ``Wnode.Guid``)"""
return self.Wnode.Guid
@property
def id(self):
"""LoggerId"""
"""The LoggerId if the session (see ``Wnode.HistoricalContext``)"""
return self.Wnode.HistoricalContext
def __repr__(self):
return """<{0} name="{1}" guid={2}>""".format(type(self).__name__, self.name, self.guid)
# GUID setter ?
class CtxProcess(object):
@@ -154,15 +166,17 @@ class CtxProcess(object):
class EtwTrace(object):
"""Represent an ETW Trace for tracing/processing events"""
def __init__(self, name, logfile=None, guid=None):
self.name = windows.pycompat.raw_encode(name)
self.logfile = logfile
self.name = windows.pycompat.raw_encode(name) #: The name of the trace
self.logfile = logfile #: The logging file of the trace (``None`` means real time trace)
if guid and isinstance(guid, basestring):
guid = gdef.GUID.from_string(guid)
self.guid = guid
self.guid = guid #: The guid of the trace
self.handle = 0
def exists(self):
"""Return ``True`` if the trace already exist (based on its name)"""
prop = EventTraceProperties.create()
try:
windows.winproxy.ControlTraceA(self.handle, self.name, prop, gdef.EVENT_TRACE_CONTROL_QUERY)
@@ -173,6 +187,7 @@ class EtwTrace(object):
return True
def start(self, flags=0):
"""Start the tracing"""
prop = EventTraceProperties.create()
prop.NumberOfBuffers = 42
prop.EnableFlags = flags
@@ -189,6 +204,11 @@ class EtwTrace(object):
self.handle = handle
def stop(self, soft=False): # Change name
"""stop the tracing.
``soft`` will allow to stop a non-existing trace that do not exists/run.
This allow for simpler script that stop/start some EtwTrace.
"""
prop = EventTraceProperties.create()
try:
windows.winproxy.ControlTraceA(0, self.name, prop, gdef.EVENT_TRACE_CONTROL_STOP)
@@ -199,16 +219,19 @@ class EtwTrace(object):
return True
def flush(self):
"""Flush the trace"""
prop = EventTraceProperties.create()
windows.winproxy.ControlTraceA(0, self.name, prop, gdef.EVENT_TRACE_CONTROL_FLUSH)
def enable(self, guid, flags=0xff, level=0xff):
"""Enable the specified event trace provider."""
if isinstance(guid, basestring):
guid = gdef.GUID.from_string(guid)
return windows.winproxy.EnableTrace(1, flags, level, guid, self.handle) # EnableTraceEx ?
def enable_ex(self, guid, flags=0xff, level=0xff, any_keyword = 0xffffffff, all_keyword=0x00):
"""Enable the specified event trace provider."""
if isinstance(guid, basestring):
guid = gdef.GUID.from_string(guid)
@@ -223,6 +246,17 @@ class EtwTrace(object):
def process(self, callback, begin=None, end=None, context=None):
"""Process the event retrieved by the trace.
This function will call ``callback`` with any :class:`EventRecord` in the trace.
``begin/end`` allow to filter and only process events in a given timeframe.
.. warning::
If the trace if ``REALTIME`` (no logfile) this function will hang/process new event until the trace is stopped.
Using ``logman -ets stop TRACE_NAME`` for exemple.
"""
if end == "now":
end = gdef.FILETIME()
windows.winproxy.GetSystemTimeAsFileTime(end)
@@ -272,11 +306,19 @@ class EtwTrace(object):
return """<{0} name={1!r} logfile={2!r}>""".format(type(self).__name__, self.name, self.logfile)
class TraceProvider(object):
"""Represent a ETW provider, which is just a GUID.
Corresponding name for a provider may be available trhought WMI.
"""
def __init__(self, guid):
self.guid = guid
@property
def infos(self):
"""The :class:`TraceGuidInfo` associated with the provider.
Main use is to retrieve the instances of the provider (directly available with ``instances``)
:type: :class:`TraceGuidInfo`
"""
size = gdef.DWORD()
info_buffer = ctypes.c_buffer(0x1000)
try:
@@ -293,6 +335,10 @@ class TraceProvider(object):
# Our trace providers should be able to directly returns its instances
@property
def instances(self):
"""The instances of the provider.
:type: [:class:`TraceProviderInstanceInfo`] -- A list of :class:`TraceProviderInstanceInfo`
"""
return self.infos.instances
def __repr__(self):
@@ -300,6 +346,10 @@ class TraceProvider(object):
class TraceGuidInfo(gdef.TRACE_GUID_INFO):
"""Defines the header to the list of sessions that enabled the provider
(see https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_guid_info)
"""
@classmethod
def from_raw_buffer(cls, buffer):
self = cls.from_buffer(buffer)
@@ -317,6 +367,10 @@ class TraceGuidInfo(gdef.TRACE_GUID_INFO):
@property
def instances(self):
"""The instances of the provider.
:type: [:class:`TraceProviderInstanceInfo`] -- A list of :class:`TraceProviderInstanceInfo`
"""
return [x for x in self._instance_generator()]
def __repr__(self):
@@ -324,6 +378,9 @@ class TraceGuidInfo(gdef.TRACE_GUID_INFO):
class TraceProviderInstanceInfo(gdef.TRACE_PROVIDER_INSTANCE_INFO):
"""Defines an instance of the provider
(see https://docs.microsoft.com/en-us/windows/win32/api/evntrace/ns-evntrace-trace_provider_instance_info)
"""
@classmethod
def from_raw_buffer(cls, buffer, offset):
self = cls.from_buffer(buffer, offset)
@@ -340,6 +397,10 @@ class TraceProviderInstanceInfo(gdef.TRACE_PROVIDER_INSTANCE_INFO):
@property
def sessions(self):
"""The sessions for the instance
:type: [:class:`~windows.generated_def.winstructs.TRACE_ENABLE_INFO`] -- A list of session
"""
return [x for x in self._instance_generator()]
def __repr__(self):
@@ -347,8 +408,14 @@ class TraceProviderInstanceInfo(gdef.TRACE_PROVIDER_INSTANCE_INFO):
class EtwManager(object):
"""An object to query ETW session/providers and open new trace"""
@property
def sessions(self):
"""The list of currently active ETW session.
:type: [:class:`EventTraceProperties`] -- A list of :class:`EventTraceProperties`
"""
# Create a tuple of MAX_ETW_SESSIONS EventTraceProperties ptr
t = [EventTraceProperties.create() for _ in range(MAX_ETW_SESSIONS)]
# Put this in a ctypes array
@@ -362,12 +429,20 @@ class EtwManager(object):
@property
def providers(self):
"""The list of currently existing ETW providers.
:type: [:class:`TraceProvider`] -- A list of ETW providers
"""
buffer = windows.utils.BUFFER(gdef.GUID, 0x1000)()
size = gdef.DWORD()
windows.winproxy.EnumerateTraceGuidsEx(gdef.TraceGuidQueryList, None, 0, buffer, buffer.real_size, size)
return [TraceProvider(g) for g in buffer[:size.value / ctypes.sizeof(gdef.GUID)]]
return [TraceProvider(g) for g in buffer[:size.value // ctypes.sizeof(gdef.GUID)]]
# Temp name / API ?
def open_trace(self, name=None, logfile=None, guid=None):
"""Open a new ETW Trace
:return: :class:`EtwTrace`
"""
return EtwTrace(name, logfile, guid)
+8
View File
@@ -107,10 +107,18 @@ class System(object):
@utils.fixedpropety
def event_log(self):
"""An object to open Event channel/publisher and evtx file
:type: :class:`~windows.winobject.event_log.EvtlogManager`
"""
return event_log.EvtlogManager()
@utils.fixedpropety
def etw(self):
"""An object to interact with ETW (Event Tracing for Windows)
:type: :class:`~windows.winobject.event_trace.EtwManager`
"""
return event_trace.EtwManager()
+4 -1
View File
@@ -5,6 +5,7 @@ import windows
from windows import utils
from windows import winproxy
import windows.generated_def as gdef
# import windows.security # at the end of this file (loop import)
bltn_type = type
@@ -308,7 +309,6 @@ class Token(utils.AutoHandle):
:type: :class:`windows.security.Acl`
"""
import window.security # Beuk move token.py & in a security/ directory ?
return self.get_token_infomations(gdef.TokenDefaultDacl, windows.security.PAcl)[0]
# def source(self): (tok.TokenSource) ??
@@ -618,3 +618,6 @@ class Token(utils.AutoHandle):
if toktype == gdef.TokenPrimary:
return "<{0} TokenId={1:#x} Type={2}>".format(type(self).__name__, tid_int, flag_repr(toktype))
return "<{0} TokenId={1:#x} Type={2} ImpersonationLevel={3}>".format(type(self).__name__, tid_int, flag_repr(toktype), flag_repr(self.impersonation_level))
import windows.security
+39 -2
View File
@@ -2,7 +2,7 @@ import ctypes
import windows.generated_def as gdef
from ..apiproxy import ApiProxy, NeededParameter
from ..error import fail_on_zero, succeed_on_zero, result_is_error_code, result_is_handle, no_error_check
from ..error import fail_on_zero, succeed_on_zero, result_is_error_code, result_is_handle, no_error_check, result_is_ntstatus
class Advapi32Proxy(ApiProxy):
APIDLL = "advapi32"
@@ -588,4 +588,41 @@ def TraceEvent(SessionHandle, EventTrace):
@Advapi32Proxy(error_check=result_is_handle)
def GetTraceLoggerHandle(Buffer):
return GetTraceLoggerHandle.ctypes_function(Buffer)
return GetTraceLoggerHandle.ctypes_function(Buffer)
# Lsa APIs
@Advapi32Proxy(error_check=result_is_ntstatus)
def LsaOpenPolicy(SystemName=None, ObjectAttributes=None, DesiredAccess=NeededParameter, PolicyHandle=NeededParameter):
if ObjectAttributes is None:
ObjectAttributes = gdef.LSA_OBJECT_ATTRIBUTES()
return LsaOpenPolicy.ctypes_function(SystemName, ObjectAttributes, DesiredAccess, PolicyHandle)
@Advapi32Proxy(error_check=result_is_ntstatus)
def LsaQueryInformationPolicy(PolicyHandle, InformationClass, Buffer):
return LsaQueryInformationPolicy.ctypes_function(PolicyHandle, InformationClass, Buffer)
@Advapi32Proxy(error_check=result_is_ntstatus)
def LsaClose(ObjectHandle):
return LsaClose.ctypes_function(ObjectHandle)
@Advapi32Proxy(error_check=result_is_ntstatus)
def LsaNtStatusToWinError(Status):
return LsaNtStatusToWinError.ctypes_function(Status)
@Advapi32Proxy(error_check=result_is_ntstatus)
def LsaLookupNames(PolicyHandle, Count, Names, ReferencedDomains, Sids):
return LsaLookupNames.ctypes_function(PolicyHandle, Count, Names, ReferencedDomains, Sids)
@Advapi32Proxy(error_check=result_is_ntstatus)
def LsaLookupNames2(PolicyHandle, Flags, Count, Names, ReferencedDomains, Sids):
return LsaLookupNames2.ctypes_function(PolicyHandle, Flags, Count, Names, ReferencedDomains, Sids)
@Advapi32Proxy(error_check=result_is_ntstatus)
def LsaLookupSids(PolicyHandle, Count, Sids, ReferencedDomains, Names):
return LsaLookupSids.ctypes_function(PolicyHandle, Count, Sids, ReferencedDomains, Names)
@Advapi32Proxy(error_check=result_is_ntstatus)
def LsaLookupSids2(PolicyHandle, LookupOptions, Count, Sids, ReferencedDomains, Names):
return LsaLookupSids2.ctypes_function(PolicyHandle, LookupOptions, Count, Sids, ReferencedDomains, Names)