diff --git a/windows/security.py b/windows/security.py
index a3eaee0..4c6c295 100644
--- a/windows/security.py
+++ b/windows/security.py
@@ -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))
diff --git a/windows/winobject/event_log.py b/windows/winobject/event_log.py
index 63bd459..267273d 100644
--- a/windows/winobject/event_log.py
+++ b/windows/winobject/event_log.py
@@ -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:
+ # FIELD_VALUE
+ 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:
+ #
+ #
+ # FIELD_VALUE
+ #
+ #
+ 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``"""
diff --git a/windows/winobject/event_trace.py b/windows/winobject/event_trace.py
index f96af18..6a09090 100644
--- a/windows/winobject/event_trace.py
+++ b/windows/winobject/event_trace.py
@@ -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)
\ No newline at end of file
diff --git a/windows/winobject/system.py b/windows/winobject/system.py
index 51fc82a..276c455 100644
--- a/windows/winobject/system.py
+++ b/windows/winobject/system.py
@@ -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()
diff --git a/windows/winobject/token.py b/windows/winobject/token.py
index aedb7e7..027525f 100644
--- a/windows/winobject/token.py
+++ b/windows/winobject/token.py
@@ -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
diff --git a/windows/winproxy/apis/advapi32.py b/windows/winproxy/apis/advapi32.py
index c4e8d4a..485bcfb 100644
--- a/windows/winproxy/apis/advapi32.py
+++ b/windows/winproxy/apis/advapi32.py
@@ -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)
\ No newline at end of file
+ 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)
+