Source code for windows.winobject.object_manager
import os.path
import ctypes
from collections import namedtuple
import windows
from windows import winproxy
import windows.generated_def as gdef
from windows.pycompat import urepr_encode
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
obj_attr.ObjectName = ctypes.pointer(gdef.LSA_UNICODE_STRING.from_string(linkpath))
obj_attr.Attributes = gdef.OBJ_CASE_INSENSITIVE
obj_attr.SecurityDescriptor = 0
obj_attr.SecurityQualityOfService = 0
res = gdef.HANDLE()
x = winproxy.NtOpenSymbolicLinkObject(res, gdef.DIRECTORY_QUERY | gdef.READ_CONTROL , obj_attr)
v = gdef.LSA_UNICODE_STRING.from_size(1000)
s = gdef.ULONG()
try:
winproxy.NtQuerySymbolicLinkObject(res, v, s)
except WindowsError as e:
if not (e.winerror & 0xffffffff) == gdef.STATUS_BUFFER_TOO_SMALL:
raise
# If our initial 1000 buffer is not enought (improbable) retry with correct size
v = gdef.LSA_UNICODE_STRING.from_size(s.value)
winproxy.NtQuerySymbolicLinkObject(res, v, s)
return v.str # Unicode
[docs]
class KernelObject(object):
"""Represent an object in the Object Manager namespace"""
def __init__(self, path, name, type=None):
self.path = path
self.name = name
if path and not path.endswith("\\"):
path += "\\"
self.fullname = path + name
self.type = type
@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
[docs]
def items(self):
"""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()]
[docs]
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)
[docs]
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()]
def _open_directory(self):
path = self.fullname
utf16_len = len(path) * 2
obj_attr = gdef.OBJECT_ATTRIBUTES()
obj_attr.Length = ctypes.sizeof(obj_attr)
obj_attr.RootDirectory = None
obj_attr.ObjectName = ctypes.pointer(gdef.LSA_UNICODE_STRING.from_string(path))
obj_attr.Attributes = gdef.OBJ_CASE_INSENSITIVE
obj_attr.SecurityDescriptor = 0
obj_attr.SecurityQualityOfService = 0
res = gdef.HANDLE()
winproxy.NtOpenDirectoryObject(res, gdef.DIRECTORY_QUERY | gdef.READ_CONTROL , obj_attr)
return res.value
def _directory_query_generator(self):
handle = self._open_directory()
size = 0x1000
buf = ctypes.c_buffer(size)
rres = gdef.ULONG()
ctx = gdef.ULONG()
while True:
try:
# Restart == True has we don't save the buffer when resizing it for next call
winproxy.NtQueryDirectoryObject(handle, buf, size, False, True, ctypes.byref(ctx), rres)
break
except gdef.NtStatusException as e:
if e.code == gdef.STATUS_NO_MORE_ENTRIES:
return
if e.code == gdef.STATUS_MORE_ENTRIES:
# If the call did not extrack all data: retry with bigger buffer
size *= 2
buf = ctypes.c_buffer(size)
continue
raise
# Function -> _extract_objects ?
t = gdef.OBJECT_DIRECTORY_INFORMATION.from_buffer(buf)
t = gdef.POBJECT_DIRECTORY_INFORMATION(t)
res = {}
for v in t:
if v.Name.Buffer is None:
break
yield v.Name.str, v.TypeName.str
[docs]
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):
return urepr_encode(u"""<{0} "{1}" (type="{2}")>""".format(type(self).__name__, self.fullname, self.type))
[docs]
def get(self, name):
"""Retrieve the object ``name`` in the current directory.
:rtype: :class:`KernelObject`
"""
for objname, objtype in self._directory_query_generator():
if objname.lower() == name.lower():
return KernelObject(self.fullname, name, objtype)
raise KeyError("Could not find WinObject <{0}> under <{1}>".format(name, self.fullname))
[docs]
def __getitem__(self, name):
"""Query object ``name`` from the directory, split and subquery on ``\\``::
>>> obj
<KernelObject "\Windows" (type="Directory")>
>>> obj["WindowStations"]["WinSta0"]
<KernelObject "\Windows\WindowStations" (type="Directory")>
>>> obj["WindowStations\\WinSta0"]
<KernelObject "\Windows\WindowStations" (type="Directory")>
: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 name == "\\": # Ask for root ? return ourself
return self
else:
name = name[1:] # Strip the leading \ and go to normal case
obj = self
for part in name.split("\\"):
try:
obj = obj.get(part)
except gdef.NtStatusException as e:
if e.code == gdef.STATUS_OBJECT_TYPE_MISMATCH:
raise KeyError("Could not find object <{0}> under <{1}> because it is a <{2}>".format(
part, obj.name, obj.type))
raise # Something smart to do ?
return obj
[docs]
class ObjectManager(object):
"""Represent the object manager.
"""
@property
def root(self):
"""The root ``\\`` Directory
:type: :class:`KernelObject` -- The root :class:`KernelObject`
"""
return KernelObject("", "\\", "Directory")
[docs]
def __getitem__(self, name):
"""Query ``name`` from the root ``\\`` directory::
object_manager["RPC Control"]["lsasspirpc"]
object_manager[r"\\RPC Control\\lsasspirpc"]
:rtype: :class:`KernelObject`
"""
return self.root[name]