1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """This module adds plugins to inspect the windows cache manager.
22
23 The windows cache manager is responsible for maintaining file cache for files
24 read from disk. The manager maintains a large arena of 256kb cached
25 blocks. These blocks are controlled using the VACB (Virtual Address Control
26 Block) arrays.
27
28 References:
29 http://www.codemachine.com/article_kernelstruct.html
30
31 """
32
33 __author__ = "Michael Cohen <scudette@google.com>"
34 from rekall import obj
35 from rekall import testlib
36
37 from rekall.plugins import core
38 from rekall.plugins.windows import common
39 from rekall_lib import utils
40
41
43 """Enumerate all blocks cached in the cache manager."""
44 name = "vacbs"
45
47 """Yield all system VACBs.
48
49 Walks the VACB tables and produce all valid VACBs. This essentially
50 produces the entire contents of the cache manager.
51 """
52
53
54
55 total_vacb_arrays = self.profile.get_constant_object(
56 'CcVacbArraysAllocated', 'unsigned int')
57
58 vacb_arrays = self.profile.get_constant_object(
59 'CcVacbArrays',
60 target="Pointer",
61 target_args=dict(
62 target='Array',
63 target_args=dict(
64 target="Pointer",
65 target_args=dict(
66 target='_VACB_ARRAY_HEADER'
67 ),
68 count=int(total_vacb_arrays),
69 )
70 )
71 )
72
73 for table in vacb_arrays:
74 self.session.report_progress(
75 "Scanning VACB table %s", table.VacbArrayIndex)
76
77 for vacb in table.VACBs:
78 if vacb.ArrayHead != table:
79 continue
80
81 yield vacb
82
84 """Yield all system VACBs for older Windows XP based kernels.
85
86 Walks the VACB tables and produce all valid VACBs. This essentially
87 produces the entire contents of the cache manager.
88 """
89
90
91
92 total_vacb_arrays = self.profile.get_constant_object(
93 'CcNumberVacbs', 'unsigned int')
94
95 vacb_array = self.profile.get_constant_object(
96 'CcVacbs',
97 target="Pointer",
98 target_args=dict(
99 target='Array',
100 target_args=dict(
101 target="_VACB",
102 count=int(total_vacb_arrays),
103 )
104 )
105 )
106
107 for vacb in vacb_array:
108 yield vacb
109
116
117 table_header = [
118 dict(name="_VACB", style="address"),
119 dict(name='valid', width=7),
120 dict(name="base", style="address"),
121 dict(name="offset", style="address"),
122 dict(name="filename"),
123 ]
124
131
133 for vacb in self.GetVACBs():
134 filename = vacb.SharedCacheMap.FileObject.file_name_with_drive()
135 if filename:
136 yield (vacb,
137 bool(self.kernel_address_space.vtop(
138 vacb.BaseAddress.v()
139 )),
140 vacb.BaseAddress.v(),
141
142 vacb.Overlay.FileOffset.QuadPart,
143 filename,
144 )
145
146
147 -class DumpFiles(core.DirectoryDumperMixin, common.WinProcessFilter):
148 """Dump files from memory.
149
150 The interface is loosely based on the Volatility plugin of the same name,
151 although the implementation is quite different.
152 """
153 name = "dumpfiles"
154
155 __args = [
156 dict(name="file_objects", type="ArrayIntParser",
157 help="Kernel addresses of _FILE_OBJECT structs.")
158 ]
159
161 """Collect all known file objects."""
162
163
164 for task in self.filter_processes():
165
166 self.session.report_progress("Inspecting VAD for %s", task.name)
167 for vad in task.RealVadRoot.traverse():
168 file_object = vad.m("Subsection").ControlArea.FilePointer
169 if file_object:
170 self.file_objects.add(file_object)
171
172
173 self.session.report_progress("Inspecting Handles for %s", task.name)
174 for handle in task.ObjectTable.handles():
175 if handle.get_object_type() == "File":
176 self.file_objects.add(handle.Object)
177
178
179 for vacb in self.session.plugins.vacbs().GetVACBs():
180 shared_cache_map = vacb.SharedCacheMap.v()
181 if shared_cache_map:
182
183 self.vacb_by_cache_map.setdefault(
184 shared_cache_map, []).append(vacb)
185
186 - def _dump_ca(self, ca, out_fd, type, filename, renderer):
187 sectors_per_page = 0x1000 / 512
188
189 for subsection in ca.FirstSubsection.walk_list("NextSubsection"):
190 for i, pte in enumerate(subsection.SubsectionBase):
191 pte_value = pte.u.Long.v()
192 try:
193 phys_address = self.kernel_address_space.ResolveProtoPTE(
194 pte_value, 0)
195 except AttributeError:
196
197
198
199 if pte & 1:
200 phys_address = pte_value & 0xffffffffff000
201 else:
202 continue
203
204 if phys_address == None:
205 continue
206
207
208 file_sector_offset = (
209 subsection.StartingSector + i * sectors_per_page)
210
211
212 file_sectors_mapped_in_page = min(
213 sectors_per_page,
214 subsection.NumberOfFullSectors - i * sectors_per_page)
215
216 if file_sectors_mapped_in_page < 0:
217 continue
218
219
220 if phys_address > self.physical_address_space.end():
221 continue
222
223 renderer.table_row(
224 type, phys_address, file_sector_offset * 512,
225 file_sectors_mapped_in_page * 512, filename)
226
227
228 out_fd.seek(file_sector_offset * 512)
229 out_fd.write(self.physical_address_space.read(
230 phys_address, file_sectors_mapped_in_page * 512))
231
232 table_header = [
233 dict(name="type", width=20),
234 dict(name="p_offset", style="address"),
235 dict(name="f_offset", style="address"),
236 dict(name="f_length", style="address"),
237 dict(name="filename")
238 ]
239
241 return dict(type="VACB", p_offset=0, f_offset=0,
242 f_length=0x1000, filename="")
243
245
246 self.file_objects = set()
247 self.vacb_by_cache_map = {}
248
249 renderer = self.session.GetRenderer()
250 if not self.plugin_args.file_objects:
251 self.CollectFileObject()
252 else:
253 self.file_objects = set(
254 [self.session.profile._FILE_OBJECT(int(x))
255 for x in self.plugin_args.file_objects])
256
257 seen_filenames = set()
258 for file_object in self.file_objects:
259 filename = unicode(
260 file_object.file_name_with_device()).replace("\\", "_")
261
262 if filename in seen_filenames:
263 continue
264
265 seen_filenames.add(filename)
266
267 self.session.report_progress(" Dumping %s", filename)
268 with renderer.open(directory=self.dump_dir,
269 filename=filename, mode="w") as out_fd:
270 filename = out_fd.name
271
272
273 ca = file_object.SectionObjectPointer.ImageSectionObject
274 if ca:
275 self._dump_ca(ca, out_fd, "ImageSectionObject",
276 filename, renderer)
277
278 ca = file_object.SectionObjectPointer.DataSectionObject
279 if ca:
280 self._dump_ca(ca, out_fd, "DataSectionObject",
281 filename, renderer)
282
283 scm = file_object.SectionObjectPointer.SharedCacheMap.v()
284
285
286 for vacb in self.vacb_by_cache_map.get(scm, []):
287 base_address = vacb.BaseAddress.v()
288 file_offset = vacb.Overlay.FileOffset.QuadPart.v()
289
290
291 for offset in utils.xrange(0, 0x40000, 0x1000):
292 phys_address = self.kernel_address_space.vtop(
293 base_address + offset)
294
295 if phys_address:
296 yield dict(type="VACB",
297 p_offset=phys_address,
298 f_offset=file_offset+offset,
299 f_length=0x1000,
300 filename=filename)
301
302
303 out_fd.seek(file_offset + offset)
304 out_fd.write(self.physical_address_space.read(
305 phys_address, 0x1000))
306
307
312
313
317
318
319 -class MftDump(common.WindowsCommandPlugin):
320 """Enumerate MFT entries from the cache manager."""
321 name = "mftdump"
322
333
335 base = vacb.BaseAddress.v()
336 for offset in utils.xrange(base, base + self.vacb_size, self.mft_size):
337
338 mft = self.ntfs_profile.MFT_ENTRY(
339 offset, context=dict(mft=self.mfts, ApplyFixup=False))
340 if mft.magic != "FILE":
341 continue
342
343 mft_id = mft.mft_entry
344 self.mfts[mft_id] = mft
345 self.session.report_progress(
346 "Added: %s", lambda mft=mft: mft.filename.name)
347
348 parent_id = mft.filename.mftReference.v()
349 if parent_id not in self.dir_tree:
350 self.dir_tree[parent_id] = set()
351
352 self.dir_tree[parent_id].add(mft_id)
353
355 if root not in self.mfts or root in seen:
356 return
357
358 mft = self.mfts[root]
359 standard_info = mft.get_attribute(
360 "$STANDARD_INFORMATION").DecodeAttribute()
361
362 yield dict(MFT=root,
363 mft_entry=mft,
364 file_modified=standard_info.file_altered_time,
365 mft_modified=standard_info.mft_altered_time,
366 access=standard_info.file_accessed_time,
367 create_time=standard_info.create_time,
368 Name=self.mfts[root].filename.name,
369 depth=depth)
370 seen.add(root)
371
372 for child in sorted(self.dir_tree.get(root, [])):
373 if child not in seen:
374 for x in self.collect_tree(child, seen, depth=depth+1):
375 yield x
376
377 table_header = [
378 dict(name="MFT", width=5, align="r"),
379 dict(name="mft_entry", hidden=True),
380 dict(name="file_modified", width=25),
381 dict(name="mft_modified", width=25),
382 dict(name="access", width=25),
383 dict(name="create_time", width=25),
384 dict(name="Name", type="TreeNode", max_depth=15, width=100),
385 ]
386
396
408
409
411 """The order is someone non-deterministic."""
412