1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """
21 @author: Bradley L Schatz
22 @license: GNU General Public License 2.0 or later
23 @contact: bradley@schatzforensic.com.au
24
25 This file provides support for windows Windows 7 SP 0.
26 """
27
28
29 from rekall import addrspace
30 from rekall import kb
31 from rekall import obj
32 from rekall.plugins.overlays.windows import common
33 from rekall_lib import utils
37 if x.obj_profile.metadata("arch") == "AMD64":
38 return x.obj_offset - 12
39 return x.obj_offset - 4
40
41
42
43 win7_overlays = {
44 '_EPROCESS': [None, {
45
46 'RealVadRoot': lambda x: x.VadRoot.BalancedRoot
47 }],
48
49 '_MMADDRESS_NODE': [None, {
50 'Tag': [TagOffset, ['String', dict(length=4)]],
51 }],
52
53 '_MMVAD_SHORT': [None, {
54 'Tag': [TagOffset, ['String', dict(length=4)]],
55 'Start': lambda x: x.StartingVpn << 12,
56 'End': lambda x: ((x.EndingVpn + 1) << 12) - 1,
57 'Length': lambda x: x.End - x.Start + 1,
58 'CommitCharge': lambda x: x.u.VadFlags.CommitCharge,
59 }],
60
61 '_MMVAD': [None, {
62 'Tag': [TagOffset, ['String', dict(length=4)]],
63 'ControlArea': lambda x: x.Subsection.ControlArea,
64 'Start': lambda x: x.StartingVpn << 12,
65 'End': lambda x: ((x.EndingVpn + 1) << 12) - 1,
66 'Length': lambda x: x.End - x.Start + 1,
67 'CommitCharge': lambda x: x.u.VadFlags.CommitCharge,
68 }],
69
70 '_MMVAD_LONG': [None, {
71 'Tag': [TagOffset, ['String', dict(length=4)]],
72 'ControlArea': lambda x: x.Subsection.ControlArea,
73 'Start': lambda x: x.StartingVpn << 12,
74 'End': lambda x: ((x.EndingVpn + 1) << 12) - 1,
75 'Length': lambda x: x.End - x.Start + 1,
76 'CommitCharge': lambda x: x.u.VadFlags.CommitCharge,
77 }],
78
79 "_CONTROL_AREA": [None, {
80 'FilePointer': [None, ['_EX_FAST_REF', dict(
81 target="_FILE_OBJECT"
82 )]],
83 }],
84
85 "_OBJECT_HEADER": [None, {
86 "InfoMask": [None, ["Flags", dict(
87 maskmap=utils.Invert({
88 0x01: "CreatorInfo",
89 0x2: "NameInfo",
90 0x4: "HandleInfo",
91 0x8: "QuotaInfo",
92 0x10: "ProcessInfo",
93 0x20: "AuditInfo",
94 0x40: "PaddingInfo",
95 }),
96 target="unsigned char",
97 )]],
98 }],
99
100 '_MM_SESSION_SPACE': [None, {
101
102
103 'ImageIterator': lambda x: x.ImageList.list_of_type(
104 "_IMAGE_ENTRY_IN_SESSION", "Link")
105 }],
106
107 '_IMAGE_ENTRY_IN_SESSION': [None, {
108 'ImageBase': lambda x: x.Address.v() & ~7
109 }],
110 }
114 """A Rekall Memory Forensics object to handle Windows 7 object headers.
115
116 Windows 7 changes the way objects are handled:
117 References: http://www.codemachine.com/article_objectheader.html
118
119 The headers look like this:
120
121 _POOL_HEADER
122
123 # These are optional headers:
124
125 _OBJECT_HEADER_PROCESS_INFO
126 _OBJECT_HEADER_QUOTA_INFO
127 _OBJECT_HEADER_HANDLE_INFO
128
129 _OBJECT_HEADER:
130 .....
131 InfoMask
132 ....
133
134 When the object manager wants to access a specific optional header, it can
135 use the constant lookup table nt!ObpInfoMaskToOffset to quickly calculate
136 the offset of that header (The headers always appear in the same order):
137
138 table = profile.get_constant_object(
139 "ObpInfoMaskToOffset",
140 target="Array",
141 target_args=dict(
142 target="byte"
143 count=0x80
144 )
145 )
146
147 option_header_offset = table[
148 OBJECT_HEADER->InfoMask & (DesiredHeaderBit | (DesiredHeaderBit-1))]
149 """
150
151
152
153
154 optional_header_mask = (
155 ('CreatorInfo', '_OBJECT_HEADER_CREATOR_INFO', 0x01),
156 ('NameInfo', '_OBJECT_HEADER_NAME_INFO', 0x02),
157 ('HandleInfo', '_OBJECT_HEADER_HANDLE_INFO', 0x04),
158 ('QuotaInfo', '_OBJECT_HEADER_QUOTA_INFO', 0x08),
159 ('ProcessInfo', '_OBJECT_HEADER_PROCESS_INFO', 0x10),
160 ('AuditInfo', '_OBJECT_HEADER_AUDIT_INFO', 0x20),
161 ('PaddingInfo', '_OBJECT_HEADER_PADDING_INFO', 0x40),
162 )
163
165 if not self.InfoMask & desired_bit:
166 return obj.NoneObject("Header not set")
167
168 lookup = self.obj_session.GetParameter("ObpInfoMaskToOffset")
169 offset = lookup[self.InfoMask & (desired_bit | (desired_bit - 1))]
170 return self.obj_profile.Object(
171 struct_name, offset=self.obj_offset - offset,
172 vm=self.obj_vm, parent=self)
173
175 """Return the object's type as a string."""
176 return self.obj_session.GetParameter("ObjectTypeMap")[
177 self.TypeIndex].Name.v()
178
179 @utils.safe_property
181 """In windows 10 the type index is obfuscated.
182 Windows 10 obfuscates the object type using a cookie:
183
184 ------ nt!ObpRemoveObjectRoutine ------: 0xf801a628e7e0
185 0xf801a628e7e0 MOV [RSP+0x10], RBX
186 0xf801a628e7e5 MOV [RSP+0x18], RBP
187 0xf801a628e7ea MOV [RSP+0x20], RSI
188 0xf801a628e7ef PUSH RDI
189 0xf801a628e7f0 SUB RSP, 0x50
190 0xf801a628e7f4 MOV RBX, RCX // RCX is object header.
191 0xf801a628e7f7 LEA RDI, [RIP-0x48f1e] 0x0 nt!ObTypeIndexTable
192 0xf801a628e7fe MOV RAX, RCX
193 0xf801a628e801 MOVZX ESI, DL
194 0xf801a628e804 SHR RAX, 0x8 // Shift address by 8
195 0xf801a628e808 MOVZX ECX, AL
196 0xf801a628e80b MOVZX EAX, BYTE [RBX+0x18] // _OBJECT_HEADER.TypeIndex
197 0xf801a628e80f XOR RCX, RAX // XOR with object type
198 0xf801a628e812 MOVZX EAX, BYTE [RIP-0x493ed] 0x1dd4015af55 nt!ObHeaderCookie
199 0xf801a628e819 XOR RCX, RAX // XOR with cookie
200 0xf801a628e81c MOV RDI, [RDI+RCX*8] // Dereference table.
201 """
202 cookie = self.obj_profile.get_constant_object(
203 "ObHeaderCookie", target="byte").v()
204
205
206 if cookie == None:
207 return self.m("TypeIndex")
208
209
210
211
212
213
214 if self.obj_vm.metadata("image"):
215
216 resolver = self.obj_session.GetParameter(
217 "physical_address_resolver")
218
219 vaddr, _ = resolver.PA2VA_for_DTB(
220 self.obj_offset,
221 self.obj_session.GetParameter("dtb"),
222 userspace=False)
223
224
225 if vaddr is None:
226 return 0
227 else:
228 vaddr = self.obj_offset
229
230 return ((vaddr >> 8) ^ cookie ^ int(self.m("TypeIndex"))) & 0xFF
231
233 """Determine if the object makes sense."""
234
235 pointer_count = int(self.PointerCount)
236 if pointer_count > 0x100000 or pointer_count < 0:
237 return False
238
239 handle_count = int(self.HandleCount)
240 if handle_count > 0x1000 or handle_count < 0:
241 return False
242
243
244 if self.TypeIndex >= 50 or self.TypeIndex < 1:
245 return False
246
247 return True
248
249
250 for _name, _y, _z in _OBJECT_HEADER.optional_header_mask:
251 setattr(_OBJECT_HEADER, _name, property(
252 lambda x, y=_y, z=_z: x._GetOptionalHeader(y, z)))
256 """In win7 the base of all Vad objects is _MMADDRESS_NODE.
257
258 The Vad structures can be either _MMVAD_SHORT or _MMVAD or _MMVAD_LONG. At
259 the base of each struct there is an _MMADDRESS_NODE which contains the
260 LeftChild and RightChild members. In order to traverse the tree, we follow
261 the _MMADDRESS_NODE and create the required _MMVAD type at each point
262 depending on their tags.
263 """
264
265
266 tag_map = {'Vadl': '_MMVAD_LONG',
267 'VadS': '_MMVAD_SHORT',
268 'Vad ': '_MMVAD',
269 'VadF': '_MMVAD_SHORT',
270 'Vadm': '_MMVAD_LONG',
271 }
272
275 """A class for pool headers"""
276
277 MAX_PREAMBLE_SIZE = 0x50
278
279 @utils.safe_property
281 return self.PoolType.v() % 2 == 0 and self.PoolType.v() > 0
282
283 @utils.safe_property
285 return self.PoolType.v() % 2 == 1
286
287 @utils.safe_property
289 return self.PoolType.v() == 0
290
291
292
293 lookup = {}
294
296 """Create a fast lookup table mapping InfoMask -> minimum_offset.
297
298 We are interested in the maximum distance between the _POOL_HEADER and
299 _OBJECT_HEADER. This is dictated by the InfoMask field. Here we build a
300 quick lookup table between the InfoMask field and the offset of the
301 first optional header.
302 """
303 ObpInfoMaskToOffset = self.obj_session.GetParameter(
304 "ObpInfoMaskToOffset")
305
306 self.lookup["\x00"] = 0
307
308
309
310 for i in range(0x100):
311
312
313 bit_position = 0x80
314 while bit_position > 0:
315
316 if bit_position & i:
317 self.lookup[chr(i)] = ObpInfoMaskToOffset[
318 i & (bit_position | (bit_position - 1))]
319
320 break
321 bit_position >>= 1
322
324 """Generates possible _OBJECT_HEADER accounting for optional headers.
325
326 Note that not all pool allocations have an _OBJECT_HEADER - only ones
327 allocated from the the object manager. This means calling this method
328 depends on which pool allocation you are after.
329
330 On windows 8, pool allocations are done from preset sizes. This means
331 that the allocation is never exactly the same size and we can not use
332 the bottom up method like before.
333
334 We therefore, have to build the headers forward by checking the preamble
335 size and validity of each object. This is a little slower than with
336 earlier versions of windows.
337
338 Args:
339 type: The object type name. If not specified we return all objects.
340 """
341 pool_align = self.obj_profile.get_constant("PoolAlignment")
342 allocation_size = self.BlockSize * pool_align
343
344
345
346 start = self.obj_end
347 cached_data = self.obj_vm.read(start, allocation_size)
348 cached_vm = addrspace.BufferAddressSpace(
349 base_offset=start, data=cached_data, session=self.obj_session)
350
351
352
353
354
355
356
357
358 info_mask_offset = self.obj_profile.get_obj_offset(
359 "_OBJECT_HEADER", "InfoMask")
360
361
362 if not self.lookup:
363 self._BuildLookupTable()
364
365
366
367 for i in utils.xrange(start,
368 start + allocation_size - info_mask_offset,
369 pool_align):
370 possible_info_mask = cached_data[i - start + info_mask_offset]
371
372
373
374
375
376 minimum_offset = self.lookup[possible_info_mask]
377
378
379 if minimum_offset > i - start:
380 continue
381
382
383
384 test_object = self.obj_profile._OBJECT_HEADER(
385 offset=i, vm=cached_vm)
386
387 if test_object.is_valid():
388 if (type is None or
389 test_object.get_object_type() == type or
390
391
392 (freed and test_object.TypeIndex <= 2)):
393 yield test_object
394
397 """Get and cache the object type map.
398
399 In windows 7, rather than store a pointer to the _OBJECT_TYPE object
400 directly, there is a global table of object types, and the object simply
401 stores an index to it.
402 """
403 name = "ObjectTypeMap"
404
416
425