forked from naksyn/PythonMemoryModule
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathobject_manager.py
More file actions
226 lines (185 loc) · 8.32 KB
/
object_manager.py
File metadata and controls
226 lines (185 loc) · 8.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
import os.path
import ctypes
from collections import namedtuple
import windows
from windows import winproxy
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
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
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
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()]
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()]
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
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 """<{0} "{1}" (type="{2}")>""".format(type(self).__name__, self.fullname, self.type)
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))
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
class ObjectManager(object):
"""Represent the object manager.
.. note::
For now, it only offers the ``root`` :class:`KernelObject`. But I want a ``manager`` object accessible
from ``windows.system`` just like other API and not directly the ``root`` directory.
"""
@property
def root(self):
"""The root ``\\`` Directory
:type: :class:`KernelObject` -- The root :class:`KernelObject`
"""
return KernelObject("", "\\", "Directory")
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]