Some more py3 compat fix + few feature in POC

This commit is contained in:
hakril
2020-02-13 22:30:46 +01:00
parent 4dc71b0449
commit 66382b89f7
6 changed files with 137 additions and 25 deletions
+3 -2
View File
@@ -19,8 +19,9 @@ if sys.platform != "win32":
from windows import winproxy
from windows import winobject
from winobject.system import System
from winobject.process import CurrentProcess, CurrentThread, WinProcess, WinThread
from .winobject.system import System
from .winobject.process import CurrentProcess, CurrentThread, WinProcess, WinThread
from .winobject.file import WinFile
system = System()
+7 -8
View File
@@ -51,7 +51,6 @@ for func in windows.generated_def.meta.functions:
class IATHook(object):
"""Look at my hook <3"""
yolo = []
def __init__(self, IAT_entry, callback, types=None):
if types is None:
@@ -62,11 +61,14 @@ class IATHook(object):
self.callback_types = self.transform_arguments(self.original_types)
self.entry = IAT_entry
self.callback = callback
## No more circular ref -> but stub is destroyed -> segv :(
self.stub = ctypes.WINFUNCTYPE(*self.callback_types)(self.hook_callback)
self.stub_addr = ctypes.cast(self.stub, PVOID).value
# stub = ctypes.WINFUNCTYPE(*self.callback_types)(self.hook_callback)
# self.stub_addr = ctypes.cast(stub, PVOID) # Same problem as keep stub... (GC..)
self.stub_addr = ctypes.cast(self.stub, PVOID).value # Same problem as keep stub... (GC..)
# IAT_entry.stub = stub
self.realfunction = ctypes.WINFUNCTYPE(*types)(IAT_entry.nonhookvalue)
self.is_enable = False
#IATHook.yolo.append(self)
def transform_arguments(self, types):
res = []
@@ -82,12 +84,14 @@ class IATHook(object):
with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), PAGE_EXECUTE_READWRITE):
self.entry.value = self.stub_addr
self.is_enable = True
self.entry.enabled = True
def disable(self):
"""Disable the IAT hook"""
with utils.VirtualProtected(self.entry.addr, ctypes.sizeof(PVOID), PAGE_EXECUTE_READWRITE):
self.entry.value = self.entry.nonhookvalue
self.is_enable = False
self.entry.enabled = True
def hook_callback(self, *args):
adapted_args = []
@@ -105,11 +109,6 @@ class IATHook(object):
return self.realfunction(*args)
return self.callback(*adapted_args, real_function=real_function)
# Use this tricks to prevent garbage collection of hook ?
#def __del__(self):
# pass
## New simple hook API based on winproxy
def setup_hook(target, hook, dll_to_hook):
"TODO: Test and doc :D"
+57 -8
View File
@@ -6,6 +6,7 @@ from contextlib import contextmanager
import windows
import windows.generated_def as gdef
from windows import winproxy
from windows.pycompat import int_types
# Helpers
@@ -244,7 +245,8 @@ class EvtEvent(EvtHandle):
# What about classic channels where there is no event_metadata ?
# Return a dict with [0-1-2-3-4] as key ? raise ?
# Juste use the render_xml ?
return {k:v for k,v in zip(self.metadata.event_data, self.event_values())}
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 __repr__(self):
@@ -305,6 +307,27 @@ class ImprovedEVT_VARIANT(gdef.EVT_VARIANT):
v = v[:self.Count]
return v
@classmethod
def from_value(cls, value, vtype=None):
if vtype is None:
# Guess type
if isinstance(value, int_types):
vtype = gdef.EvtVarTypeUInt64
elif isinstance(value, basestring):
vtype = gdef.EvtVarTypeString
else:
raise NotImplementedError("LATER")
self = cls()
# import pdb;pdb.set_trace()
# Yolo test :)
super(ImprovedEVT_VARIANT, ImprovedEVT_VARIANT).Type.__set__(self, vtype)
# super(ImprovedEVT_VARIANT, self).Type = vtype
attrname = self.VALUE_MAPPER[self.Type]
setattr(self, attrname, value)
if self.Type in (gdef.EvtVarTypeBinary, gdef.EvtVarTypeString):
self.Count = len(value)
return self
def __repr__(self):
return "<{0} of type={1}>".format(type(self).__name__, self.Type)
@@ -360,7 +383,7 @@ class EvtChannel(object):
if ids and filter:
raise ValueError("<ids> and <filter> are mutually exclusive")
if ids is not None:
if isinstance(ids, (long, int)):
if isinstance(ids, int_types):
ids = (ids,)
ids_filter = " or ".join("EventID={0}".format(id) for id in ids)
filter = "Event/System[{0}]".format(ids_filter)
@@ -854,19 +877,45 @@ class EventMetadata(EvtHandle):
"""Identifies the template attribute of the event definition which is an XML string"""
return eventinfo(self, gdef.EventMetadataEventTemplate).value
def _parse_event_template_data_element(self, element):
res = {"type": "data"}
res["name"] = element.attributes["name"].value
res["inType"] = element.attributes["inType"].value
res["outType"] = element.attributes["outType"].value
count = element.attributes.get("count", None)
if count:
res["count"] = count.value
length = element.attributes.get("length", None)
if length:
res["length"] = length.value
return res
def _parse_event_template_struct_element(self, element):
res = {"type": "struct"}
res["name"] = element.attributes["name"].value
res["fields"] = [self._parse_event_template_data_element(elt) for elt in element.childNodes if elt.nodeType == elt.ELEMENT_NODE]
return res
def _event_data_generator(self, template):
xmldoc = xml.dom.minidom.parseString(template)
xmltemplate = xmldoc.getElementsByTagName("template")[0]
for element in (n for n in xmltemplate.childNodes if n.nodeType == n.ELEMENT_NODE):
if element.tagName == "data":
yield self._parse_event_template_data_element(element)
elif element.tagName == "struct":
yield self._parse_event_template_struct_element(element)
else:
raise ValueError("Unexpected XML element <{0}> in event template".format(element.tagName))
@property
def event_data(self):
"""The list of attribute specifique for this event.
Retrieved by parsing :data:`EventMetadata.template`
"""
result = []
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
return []
return list(self._event_data_generator(template))
def yolo(self):
template = self.template
+3 -3
View File
@@ -66,7 +66,7 @@ class EventRecord(gdef.EVENT_RECORD):
# def match(self, provider=None, id=None, opcode=None):
def __repr__(self):
guid = self.EventHeader.ProviderId.to_string()
guid = self.EventHeader.ProviderId
return """<{0} provider="{1}" id={2}>""".format(type(self).__name__, guid, self.id)
@@ -218,7 +218,7 @@ class EtwTrace(object):
# TODO : maybe raise an Exception instead of silently masking the value ?
level = gdef.UCHAR(chr(level & 0xff))
return windows.winproxy.EnableTraceEx2(self.handle, guid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, level , any_keyword, all_keyword, 0, None)
return windows.winproxy.EnableTraceEx2(self.handle, guid, EVENT_CONTROL_CODE_ENABLE_PROVIDER, level , any_keyword, all_keyword, 0, None)
def process(self, callback, begin=None, end=None, context = None):
@@ -295,7 +295,7 @@ class TraceProvider(object):
return self.infos.instances
def __repr__(self):
return """<{0} for "{1}">""".format(type(self).__name__, self.guid.to_string())
return """<{0} for "{1}">""".format(type(self).__name__, self.guid)
class TraceGuidInfo(gdef.TRACE_GUID_INFO):
+5
View File
@@ -203,6 +203,11 @@ class Task(gdef.IRegisteredTask):
self.RunEx(params, flags, sessionid, user, result)
return result
def get_security_descriptor(self, secinfo):
res = gdef.BSTR()
self.GetSecurityDescriptor(secinfo, res)
return res.value
def __repr__(self):
return """<{0} "{1}" at {2:#x}>""".format(type(self).__name__, self.name, id(self))
+62 -4
View File
@@ -2,6 +2,7 @@ import windows
import ctypes
import struct
import functools
from functools import partial
from collections import namedtuple
from ctypes.wintypes import *
@@ -9,7 +10,8 @@ from ctypes.wintypes import *
import windows.com
import windows.generated_def as gdef
from windows.generated_def.winstructs import *
from functools import partial
from windows.pycompat import basestring
# Common error check for all WMI COM interfaces
# This 'just' add the corresponding 'WBEMSTATUS' to the hresult error code
@@ -18,9 +20,44 @@ class WmiComInterface(object):
def errcheck(self, result, func, args):
if result < 0:
wmitag = gdef.WBEMSTATUS.mapper[result & 0xffffffff]
raise WindowsError(result, wmitag)
raise ctypes.WinError(result, wmitag)
return args
sentinel = object()
# POC
class QualifierSet(gdef.IWbemQualifierSet):
def get_variant(self, name):
"""Retrieve the value of property ``name`` as a :class:`~windows.com.Variant`
:return: :class:`~windows.com.Variant`
"""
if not isinstance(name, basestring):
nametype = type(name).__name__
raise TypeError("WmiObject attributes name must be str, not <{0}>".format(nametype))
variant_res = windows.com.Variant()
self.Get(name, 0, variant_res, None)
return variant_res
def get(self, name, default=sentinel):
"""Return the value of the property ``name``. The return value depends of the type of the property and can vary"""
try:
return self.get_variant(name).value
except WindowsError as e:
if (e.winerror & 0xffffffff) != gdef.WBEM_E_NOT_FOUND:
raise
if default is sentinel:
raise
return default
def names(self):
res = POINTER(windows.com.SafeArray)()
x = ctypes.pointer(res)
self.GetNames(0, cast(x, POINTER(POINTER(gdef.SAFEARRAY))))
# need to free the safearray / unlock ?
properties = [p for p in res[0].to_list(BSTR)]
return properties
# https://docs.microsoft.com/en-us/windows/desktop/api/wbemcli/nn-wbemcli-iwbemclassobject
WmiMethod = namedtuple("WmiMethod", ["inparam", "outparam"])
@@ -88,7 +125,6 @@ class WmiObject(gdef.IWbemClassObject, WmiComInterface):
return gdef.tag_WBEM_GENUS_TYPE.mapper[self.get("__GENUS")]
## Higher level API
def get_properties(self, system_properties=False):
"""Return the list of properties names available for the current object.
If ``system_properties`` is ``False`` property names begining with ``_`` are ignored.
@@ -108,6 +144,17 @@ class WmiObject(gdef.IWbemClassObject, WmiComInterface):
properties = property(get_properties) #: The properties of the object (exclude system properties)
@property
def qualifier_set(self): # changer de nom ?
res = QualifierSet()
self.GetQualifierSet(res)
return res
def get_p_set(self, name): # Changer de nom ?
res = QualifierSet()
self.GetPropertyQualifierSet(name, res)
return res
# Make WmiObject a mapping object
def keys(self):
@@ -133,6 +180,11 @@ class WmiObject(gdef.IWbemClassObject, WmiComInterface):
return """<{0} class "{1}">""".format(type(self).__name__, self.get("__Class"))
return """<{0} instance of "{1}">""".format(type(self).__name__, self.get("__Class"))
def __sprint__(self):
return """ {0}\n
{1}
""".format(repr(self), "\n".join(": ".join([x[0], str(x[1])]) for x in sorted(self.items())))
class WmiEnumeration(gdef.IEnumWbemClassObject, WmiComInterface):
"""Represent an enumeration of object that can be itered"""
@@ -150,7 +202,7 @@ class WmiEnumeration(gdef.IEnumWbemClassObject, WmiComInterface):
return_count = gdef.ULONG(0)
error = self.Next(timeout, 1, obj, return_count)
if error == gdef.WBEM_S_TIMEDOUT:
raise WindowsError(gdef.WBEM_S_TIMEDOUT, "Wmi timeout")
raise ctypes.WinError(gdef.WBEM_S_TIMEDOUT, "Wmi timeout")
elif error == WBEM_S_FALSE:
return None
else:
@@ -337,6 +389,12 @@ class WmiNamespace(gdef.IWbemServices, WmiComInterface):
self.PutInstance(instance, flags, None, res)
return res
def delete_instance(self, instance, flags=0):
"""TODO: Document"""
if isinstance(instance, gdef.IWbemClassObject):
instance = instance["__Path"]
return self.DeleteInstance(instance, flags, None, None)
def exec_method(self, obj, method, inparam, flags=0):
"""Exec method named on ``object`` with ``inparam``.