diff --git a/docs/source/object_manager.rst b/docs/source/object_manager.rst index fe893fa..209753b 100644 --- a/docs/source/object_manager.rst +++ b/docs/source/object_manager.rst @@ -6,7 +6,15 @@ Object Manager -- Kernel objects The :class:`ObjectManager` instance is accessible via :py:attr:`windows.system.object_manager ` -TODO: doc + sample + +.. note:: + + See sample at :ref:`sample_object_manager` + + +.. warning:: + + This API have not been tested on real case yet and may be subject to changes. ObjectManager """"""""""""" @@ -24,4 +32,4 @@ KernelObject .. autoclass:: KernelObject :members: :undoc-members: - :special-members: __getitem__ \ No newline at end of file + :special-members: __getitem__, __iter__ \ No newline at end of file diff --git a/docs/source/samples_output/object_manager_findobj.txt b/docs/source/samples_output/object_manager_findobj.txt new file mode 100644 index 0000000..491b894 --- /dev/null +++ b/docs/source/samples_output/object_manager_findobj.txt @@ -0,0 +1,23 @@ +(cmd) python object_manager\findobj.py +Looking for object name containing +* +* +* +<\DriverStores\SYSTEM> -> STATUS_ACCESS_DENIED +* -> <\Device\Mup\;MailslotRedirector> +* +<\Device\00000020> -> STATUS_ACCESS_DENIED +<\Device\00000020> -> STATUS_ACCESS_DENIED +<\Device\00000020> -> STATUS_ACCESS_DENIED +<\Device\00000020> -> STATUS_ACCESS_DENIED +<\Device\00000020> -> STATUS_ACCESS_DENIED +<\KernelObjects\PrefetchTracesReady> -> STATUS_ACCESS_DENIED +<\KnownDlls\powrprof.dll> -> STATUS_ACCESS_DENIED +* +* +* +<\Windows\SbApiPort> -> STATUS_ACCESS_DENIED +<\Windows\SbApiPort> -> STATUS_ACCESS_DENIED +<\Sessions\BNOLINKS\1> -> STATUS_ACCESS_DENIED +<\Sessions\BNOLINKS\1> -> STATUS_ACCESS_DENIED +<\Sessions\BNOLINKS\1> -> STATUS_ACCESS_DENIED diff --git a/docs/source/samples_output/object_manager_object_manager.txt b/docs/source/samples_output/object_manager_object_manager.txt new file mode 100644 index 0000000..adde931 --- /dev/null +++ b/docs/source/samples_output/object_manager_object_manager.txt @@ -0,0 +1,22 @@ +(cmd) python object_manager\object_manager.py +Object manager is +Root object is + +Listing some of root-subobject: + * PendingRenameMutex: + * ObjectTypes: + * storqosfltport: + * MicrosoftMalwareProtectionRemoteIoPortWD: + +Retrieving <\Rpc Control\lsasspirpc>: +Object is: + * name: + * path: <\Rpc Control> + * fullname: <\Rpc Control\lsasspirpc> + * type: + * target: + +Looking for a SymbolicLink in +Object is: + * name: + * target: <\Device\Harddisk0\Partition0> diff --git a/samples/object_manager/findobj.py b/samples/object_manager/findobj.py new file mode 100644 index 0000000..cf0e2d2 --- /dev/null +++ b/samples/object_manager/findobj.py @@ -0,0 +1,35 @@ +import argparse + +import windows +import windows.generated_def as gdef + +def obj_with_link(obj): + target = obj.target + if target is None: + return str(obj) + return "{0} -> <{1}>".format(obj, target) + + +def find_name(root, findname): + TODO = [root] + while TODO: + try: + for name, obj in TODO.pop().items(): + if findname in name: + print("* {0}".format(obj_with_link(obj))) + if obj.type == "Directory": + TODO.append(obj) + except gdef.NtStatusException as e: + print("<{0}> -> {1}".format(obj.fullname, e.name)) + + + +parser = argparse.ArgumentParser(prog=__file__) +parser.add_argument('name', nargs='?', default="ls", help='The name of the object to find') +res = parser.parse_args() + +objmanag = windows.system.object_manager +print("Looking for object name containing <{0}>".format(res.name)) +find_name(objmanag.root, res.name) + + diff --git a/samples/object_manager/object_manager.py b/samples/object_manager/object_manager.py new file mode 100644 index 0000000..6d2060c --- /dev/null +++ b/samples/object_manager/object_manager.py @@ -0,0 +1,44 @@ +import sys +import os.path +sys.path.append(os.path.abspath(__file__ + "\..\..")) + +import windows +import windows.generated_def as gdef + +object_manager = windows.system.object_manager +print("Object manager is {0}".format(object_manager)) +root = object_manager.root +print("Root object is {0}".format(root)) + +print("") +print("Listing some of root-subobject:") +# Kernel object of type 'Directory' are iterable +for i, (name, obj) in enumerate(root.items()): + print(" * {0}: {1}".format(name, obj)) + if i == 3: + break + +print("") +print(r"Retrieving <\Rpc Control\lsasspirpc>:") +# You can retrieve this value in one request +x1 = root[r"\Rpc Control\lsasspirpc"] +# Sub-directory also allow __getitem__ +x2 = root["Rpc Control"]["lsasspirpc"] +# You can directly request the object manager that will request `root` +x3 = object_manager[r"\Rpc Control\lsasspirpc"] +assert x1.fullname == x2.fullname == x3.fullname + +lsasspirpc = x1 +print("Object is: {0}".format(lsasspirpc)) +print(" * name: <{0}>".format(lsasspirpc.name)) +print(" * path: <{0}>".format(lsasspirpc.path)) +print(" * fullname: <{0}>".format(lsasspirpc.fullname)) +print(" * type: <{0}>".format(lsasspirpc.type)) +print(" * target: <{0}>".format(lsasspirpc.target)) # None on non-symlink + +print("") +print("Looking for a SymbolicLink in ") +slo = [o for o in root["ArcName"].values() if o.type == "SymbolicLink"][0] +print("Object is: {0}".format(slo)) +print(" * name: <{0}>".format(slo.name)) +print(" * target: <{0}>".format(slo.target)) diff --git a/samples/object_manager/winobj.py b/samples/object_manager/winobj.py new file mode 100644 index 0000000..de038b4 --- /dev/null +++ b/samples/object_manager/winobj.py @@ -0,0 +1,22 @@ +import argparse + +import windows +import windows.generated_def as gdef + +def obj_with_link(obj): + target = obj.target + if target is None: + return str(obj) + return "{0} -> <{1}>".format(obj, target) + +def fulllistdir(dir, depth=0): + for name, obj in dir.items(): + print("{0} * {1}".format(" " * depth, obj_with_link(obj))) + if obj.type == "Directory": + try: + fulllistdir(obj, depth + 4) + except gdef.NtStatusException as e: + print("{0} * {1}".format(" " * (depth + 4), e)) + + +fulllistdir(windows.system.object_manager.root) \ No newline at end of file diff --git a/windows/winobject/object_manager.py b/windows/winobject/object_manager.py index cddf215..e8bd1ab 100644 --- a/windows/winobject/object_manager.py +++ b/windows/winobject/object_manager.py @@ -4,11 +4,11 @@ from collections import namedtuple import windows from windows import winproxy -# from windows.generated_def.winstructs import * import windows.generated_def as gdef def query_link(linkpath): + """Resolve the link object with path ``linkpath``""" obj_attr = gdef.OBJECT_ATTRIBUTES() obj_attr.Length = ctypes.sizeof(obj_attr) obj_attr.RootDirectory = 0 @@ -20,11 +20,12 @@ def query_link(linkpath): x = winproxy.NtOpenSymbolicLinkObject(res, gdef.DIRECTORY_QUERY | gdef.READ_CONTROL , obj_attr) v = gdef.LSA_UNICODE_STRING.from_string("\x00" * 1000) s = gdef.ULONG() - winproxy.NtQuerySymbolicLinkObject(res, v, s) + winproxy.NtQuerySymbolicLinkObject(res, v, s) # Handle Buffer-too-small ? return v.str class KernelObject(object): + """Represent an object in the Object Manager namespace""" def __init__(self, path, name, type): self.path = path self.name = name @@ -35,20 +36,55 @@ class KernelObject(object): @property def target(self): + """Resolve the target of a symbolic link object. + + :rtype: :class:`str` or None if object is not a link + """ try: return query_link(self.fullname) except windows.generated_def.ntstatus.NtStatusException as e: + if e.code != gdef.STATUS_OBJECT_TYPE_MISMATCH: + raise return None def items(self): - """Todo: better name ?""" + """Return the list of tuple (object's name, object) in the current directory object. + + :rtype: [(:class:`str`, :class:`KernelObject`)] -- A list of tuple + + .. note:: + + the :class:`KernelObject` must be of type ``Directory`` or + it will raise :class:`~windows.generated_def.ntstatus.NtStatusException` with + code :data:`~windows.generated_def.STATUS_OBJECT_TYPE_MISMATCH` + """ path = self.fullname return [(name, KernelObject(path, name, typename)) for name, typename in self._directory_query_generator()] def keys(self): + """Return the list of objects' name in the current directory object. + + :rtype: [:class:`str`] -- A list of name + + .. note:: + + the :class:`KernelObject` must be of type ``Directory`` or + it will raise :class:`~windows.generated_def.ntstatus.NtStatusException` with + code :data:`~windows.generated_def.STATUS_OBJECT_TYPE_MISMATCH` + """ return list(self) def values(self): + """Return the list of objects in the current directory object. + + :rtype: [:class:`KernelObject`] -- A list of object + + .. note:: + + the :class:`KernelObject` must be of type ``Directory`` or + it will raise :class:`~windows.generated_def.ntstatus.NtStatusException` with + code :data:`~windows.generated_def.STATUS_OBJECT_TYPE_MISMATCH` + """ path = self.fullname return [KernelObject(path, name, typename) for name, typename in self._directory_query_generator()] @@ -96,6 +132,16 @@ class KernelObject(object): yield v.Name.str, v.TypeName.str def __iter__(self): + """Iter over the list of name in the Directory object. + + :yield: :class:`str` -- The names of objects in the directory. + + .. note:: + + the :class:`KernelObject` must be of type ``Directory`` or + it will raise :class:`~windows.generated_def.ntstatus.NtStatusException` with + code :data:`~windows.generated_def.STATUS_OBJECT_TYPE_MISMATCH` + """ return (name for name, type in self._directory_query_generator()) def __repr__(self): @@ -111,20 +157,30 @@ class KernelObject(object): return KernelObject(self.fullname, name, objtype) raise KeyError("Could not find WinObject <{0}> under <{1}>".format(name, self.fullname)) - def __getitem__(self, value): - print(self, value) - if value.startswith("\\"): + def __getitem__(self, name): + """Query object ``name`` from the directory, split and subquery on ``\\``:: + + >>> obj + + >>> obj["WindowStations"]["WinSta0"] + + >>> obj["WindowStations\\WinSta0"] + + + :rtype: :class:`KernelObject` + :raise: :class:`KeyError` if ``name`` can not be found. + """ + if name.startswith("\\"): # Are we the root directory ? if not self.fullname == "\\" : raise ValueError("Cannot query an object path begining by '\\' from an object other than '\\'") - elif value == "\\": # Ask for root ? return ourself + elif name == "\\": # Ask for root ? return ourself return self else: - value = value[1:] + name = name[1:] obj = self - print(value.split("\\")) - for part in value.split("\\"): + for part in name.split("\\"): try: obj = obj.get(part) except gdef.NtStatusException as e: @@ -159,4 +215,5 @@ class ObjectManager(object): :rtype: :class:`KernelObject` """ - return self.root[name] \ No newline at end of file + return self.root[name] +