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 @author: Andreas Schuster
26 @license: GNU General Public License 2.0 or later
27 @contact: a.schuster@forensikblog.de
28 @organization: http://computer.forensikblog.de/en/
29 """
30 from rekall.plugins.windows import common
31
32
34 """PoolScanner for File objects"""
36 super(PoolScanFile, self).__init__(**kwargs)
37 self.checks = [
38 ('PoolTagCheck', dict(
39 tag=self.profile.get_constant("FILE_POOLTAG"))),
40
41 ('CheckPoolSize', dict(
42 min_size=self.profile.get_obj_size("_FILE_OBJECT"))),
43
44 ('CheckPoolType', dict(
45 paged=True, non_paged=True, free=True)),
46
47 ('CheckPoolIndex', dict(value=0)),
48 ]
49
50
51 -class FileScan(common.PoolScannerPlugin):
52 """ Scan Physical memory for _FILE_OBJECT pool allocations
53 """
54 __name = "filescan"
55
56 table_header = [
57 dict(name='a', width=1),
58 dict(name="offset", style="address"),
59 dict(name="ptr_no", width=6, align="r"),
60 dict(name="hnd_no", width=3, align="r"),
61 dict(name="access", width=6),
62 dict(name='Owner', type="_EPROCESS"),
63 dict(name="path")
64 ]
65
66 scanner_defaults = dict(
67 scan_kernel_nonpaged_pool=True
68 )
69
71 """Generate possible hits."""
72 for run in self.generate_memory_ranges():
73 scanner = PoolScanFile(profile=self.profile, session=self.session,
74 address_space=run.address_space)
75
76 for pool_obj in scanner.scan(run.start, run.length):
77 for object_obj in pool_obj.IterObject("File", freed=True):
78
79 file_obj = self.session.profile._FILE_OBJECT(
80 offset=object_obj.obj_end, vm=run.address_space)
81
82 if not file_obj.FileName.v(vm=self.kernel_address_space):
83 continue
84
85
86 device_obj = file_obj.DeviceObject.deref(
87 vm=self.session.kernel_address_space)
88
89 if not device_obj.DeviceType.is_valid():
90 continue
91
92
93
94 owner_process = (
95 object_obj.HandleInfo.SingleEntry.Process.deref(
96 vm=self.kernel_address_space))
97
98 filename = file_obj.file_name_with_drive(
99 vm=self.kernel_address_space)
100
101 yield dict(a='F' if pool_obj.FreePool else "",
102 offset=file_obj.obj_offset,
103 ptr_no=object_obj.PointerCount,
104 hnd_no=object_obj.HandleCount,
105 access=file_obj.AccessString,
106 Owner=owner_process,
107 path=filename)
108
109
111 """ Scanner for _DRIVER_OBJECT """
112
114 super(PoolScanDriver, self).__init__(**kwargs)
115 self.checks = [
116 ('PoolTagCheck', dict(
117 tag=self.profile.get_constant("DRIVER_POOLTAG"))),
118
119
120 ('CheckPoolSize', dict(
121 condition=lambda x: x > self.profile.get_obj_size(
122 "_DRIVER_OBJECT"))),
123
124 ('CheckPoolType', dict(
125 paged=True, non_paged=True, free=True)),
126
127 ('CheckPoolIndex', dict(value=0)),
128 ]
129
130
132 "Scan for driver objects _DRIVER_OBJECT "
133
134 __name = "driverscan"
135
136 table_header = [
137 dict(name='a', width=1),
138 dict(name="offset", style="address"),
139 dict(name="ptr_no", width=6, align="r"),
140 dict(name="hnd_no", width=3, align="r"),
141 dict(name="start", style="address"),
142 dict(name="size", style="address"),
143 dict(name="servicekey", width=20),
144 dict(name="name", width=12),
145 dict(name="path")
146 ]
147
148 scanner_defaults = dict(
149 scan_kernel_nonpaged_pool=True
150 )
151
153 """Generate possible hits."""
154 for run in self.generate_memory_ranges():
155 scanner = PoolScanDriver(session=self.session,
156 profile=self.profile,
157 address_space=run.address_space)
158
159 for pool_obj in scanner.scan(run.start, run.length):
160 for object_obj in pool_obj.IterObject("Driver", freed=True):
161 object_name = object_obj.NameInfo.Name.v(
162 vm=self.kernel_address_space)
163
164 driver_obj = self.profile._DRIVER_OBJECT(
165 object_obj.obj_end, vm=run.address_space)
166
167 extension_obj = self.profile._DRIVER_EXTENSION(
168 driver_obj.obj_end, vm=run.address_space)
169
170 yield dict(a='F' if pool_obj.FreePool else "",
171 offset=driver_obj.obj_offset,
172 ptr_no=object_obj.PointerCount,
173 hnd_no=object_obj.HandleCount,
174 start=driver_obj.DriverStart,
175 size=driver_obj.DriverSize,
176 servicekey=extension_obj.ServiceKeyName.v(
177 vm=self.kernel_address_space),
178 name=object_name,
179 path=driver_obj.DriverName.v(
180 vm=self.kernel_address_space)
181 )
182
183
185 """ Scanner for symbolic link objects """
187 super(PoolScanSymlink, self).__init__(**kwargs)
188 self.checks = [
189 ('PoolTagCheck', dict(
190 tag=self.profile.get_constant("SYMLINK_POOLTAG"))),
191
192 ('CheckPoolSize', dict(
193 min_size=self.profile.get_obj_size(
194 "_OBJECT_SYMBOLIC_LINK"))),
195
196 ('CheckPoolType', dict(paged=True, non_paged=True, free=True)),
197 ]
198
199
201 "Scan for symbolic link objects "
202
203 __name = "symlinkscan"
204
205 table_header = [
206 dict(name='a', width=1),
207 dict(name="offset", style="address"),
208 dict(name="ptr_no", width=6, align="r"),
209 dict(name="hnd_no", width=3, align="r"),
210 dict(name="creation_time", width=24),
211 dict(name="from_link"),
212 dict(name="to_link", width=60),
213 ]
214
215 scanner_defaults = dict(
216
217 scan_kernel_paged_pool=True
218 )
219
221 """Generate possible hits."""
222 for run in self.generate_memory_ranges():
223 scanner = PoolScanSymlink(profile=self.profile,
224 session=self.session,
225 address_space=run.address_space)
226 for pool_obj in scanner.scan(run.start, run.length):
227 for object_obj in pool_obj.IterObject(
228 "SymbolicLink", freed=True):
229 object_name = object_obj.NameInfo.Name.v(
230 vm=self.kernel_address_space)
231
232 link_obj = self.profile._OBJECT_SYMBOLIC_LINK(
233 object_obj.obj_end, vm=run.address_space)
234
235 yield dict(a='F' if pool_obj.FreePool else "",
236 offset=link_obj.obj_offset,
237 ptr_no=object_obj.PointerCount,
238 hnd_no=object_obj.HandleCount,
239 creation_time=link_obj.CreationTime or '',
240 from_link=object_name,
241 to_link=link_obj.LinkTarget.v(
242 vm=self.kernel_address_space))
243
244
246 """ Scanner for Mutants _KMUTANT """
248 super(PoolScanMutant, self).__init__(**kwargs)
249 self.checks = [
250 ('PoolTagCheck', dict(tag=self.profile.get_constant(
251 "MUTANT_POOLTAG"))),
252
253 ('CheckPoolSize', dict(
254 min_size=self.profile.get_obj_size("_KMUTANT"))),
255
256 ('CheckPoolType', dict(
257 paged=True, non_paged=True, free=True)),
258
259 ('CheckPoolIndex', dict(value=0)),
260 ]
261
262
264 "Scan for mutant objects _KMUTANT "
265
266 __name = "mutantscan"
267
268 table_header = [
269 dict(name='a', width=1),
270 dict(name="offset", style="address"),
271 dict(name="ptr_no", width=6, align="r"),
272 dict(name="hnd_no", width=3, align="r"),
273 dict(name="signal", width=6),
274 dict(name="thread", style="address"),
275 dict(name="cid", width=9, align="r"),
276 dict(name="name")
277 ]
278
279 scanner_defaults = dict(
280 scan_kernel_nonpaged_pool=True,
281 )
282
284 for run in self.generate_memory_ranges():
285 scanner = PoolScanMutant(profile=self.profile, session=self.session,
286 address_space=run.address_space)
287
288 for pool_obj in scanner.scan(run.start, run.length):
289 for object_obj in pool_obj.IterObject("Mutant", freed=True):
290 object_name = object_obj.NameInfo.Name.v(
291 vm=self.kernel_address_space)
292
293
294
295 if self.plugin_args.verbosity < 5 and not object_name:
296 continue
297
298 mutant = self.profile._KMUTANT(
299 object_obj.obj_end, vm=run.address_space)
300
301 if mutant.OwnerThread > 0x80000000:
302 thread = self.profile._ETHREAD(
303 offset=mutant.OwnerThread,
304 vm=self.kernel_address_space)
305
306 CID = "{0}:{1}".format(thread.Cid.UniqueProcess,
307 thread.Cid.UniqueThread)
308 else:
309 CID = ""
310
311 yield dict(a='F' if pool_obj.FreePool else "",
312 offset=mutant.obj_offset,
313 ptr_no=object_obj.PointerCount,
314 hnd_no=object_obj.HandleCount,
315 signal=mutant.Header.SignalState,
316 thread=mutant.OwnerThread,
317 cid=CID,
318 name=object_obj.NameInfo.Name.v(
319 vm=self.kernel_address_space))
320
321
323 """PoolScanner for File objects"""
324
325
326 kernel = 0x80000000
327
329 super(PoolScanProcess, self).__init__(**kwargs)
330 self.kernel = self.profile.get_constant_object(
331 "MmSystemRangeStart", "Pointer").v() or 0x80000000
332
333 self.checks = [
334
335 ('PoolTagCheck', dict(
336 tag=self.profile.get_constant("EPROCESS_POOLTAG"))),
337
338
339 ('CheckPoolSize', dict(min_size=self.profile.get_obj_size(
340 "_EPROCESS"))),
341
342
343
344 ('CheckPoolType', dict(
345 paged=True, non_paged=True, free=True)),
346
347 ('CheckPoolIndex', dict(value=0)),
348 ]
349
350
351
352 if self.session.kernel_address_space.metadata("pae"):
353 self.dtb_alignment = 0x20
354 else:
355 self.dtb_alignment = 0x1000
356
357 - def scan(self, **kwargs):
376
377
378 -class PSScan(common.WinScanner):
379 """Scan Physical memory for _EPROCESS pool allocations.
380
381 Status flags:
382 E: A known _EPROCESS address from pslist.
383 P: A known pid from pslist.
384 """
385
386 name = "psscan"
387
388 table_header = [
389 dict(name='a', width=1),
390 dict(name="offset_p", type="_EPROCESS"),
391 dict(name="offset_v", style="address"),
392 dict(name="ppid", width=6, align="r"),
393 dict(name="pdb", style="address"),
394 dict(name='stat', width=4),
395 dict(name="create_time", width=24),
396 dict(name="exit_time", width=24),
397 ]
398
399
400 scanner_defaults = dict(
401 scan_kernel_nonpaged_pool=True
402 )
403
405 """Render results in a table."""
406
407
408 pslist = self.session.plugins.pslist()
409
410
411 known_eprocess = set()
412 known_pids = set()
413 for task in pslist.list_eprocess():
414 known_eprocess.add(task)
415 known_pids.add(task.UniqueProcessId)
416
417
418 for run in self.generate_memory_ranges():
419
420 scanner = PoolScanProcess(session=self.session,
421 profile=self.profile,
422 address_space=run.address_space)
423
424 for pool_obj, eprocess in scanner.scan(
425 offset=run.start, maxlen=run.length):
426 if run.data["type"] == "PhysicalAS":
427
428 virtual_eprocess = (
429 pslist.virtual_process_from_physical_offset(eprocess))
430 else:
431 virtual_eprocess = eprocess
432
433 known = ""
434 if virtual_eprocess in known_eprocess:
435 known += "E"
436
437 if eprocess.UniqueProcessId in known_pids:
438 known += "P"
439
440 yield dict(a='F' if pool_obj.FreePool else "",
441 offset_p=eprocess,
442 offset_v=virtual_eprocess.obj_offset,
443 ppid=eprocess.InheritedFromUniqueProcessId,
444 pdb=eprocess.Pcb.DirectoryTableBase,
445 stat=known,
446 create_time=eprocess.CreateTime or '',
447 exit_time=eprocess.ExitTime or '')
448