1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """This module adds arbitrary file reading to Rekall."""
24
25 __author__ = "Michael Cohen <scudette@google.com>"
26 import fnmatch
27 import hashlib
28 import itertools
29 import platform
30 import re
31 import os
32
33 from rekall import plugin
34 from rekall.plugins.response import common
35 from rekall_lib import utils
36
37
38 BUFFER_SIZE = 10 * 1024 * 1024
39
40
41 -class IRFind(common.AbstractIRCommandPlugin):
42 """List files recursively from a root path."""
43 name = "find"
44
45 __args = [
46 dict(name="root", positional=True,
47 help="The root directory to start search from.")
48 ]
49
50 table_header = [
51 dict(name="Perms", type="Permissions", width=16),
52 dict(name="Size", align="r", width=10),
53 dict(name="Path"),
54 ]
55
65
66
67 -class IRStat(common.AbstractIRCommandPlugin):
68 name = "stat"
69
70 __args = [
71 dict(name="paths", positional=True, type="Array",
72 help="Paths to stat."),
73 ]
74
75 table_header = [
76 dict(name="Perms", type="Permissions", width=16),
77 dict(name="Size", align="r", width=10),
78 dict(name="Path"),
79 ]
80
87
88
90 """A class to hold a hash value."""
91 - def __init__(self, type="md5", value=None):
92 self.type = type
93 self.value = value
94
96 return "%s:%s" % (self.type, self.value.encode("hex"))
97
98
99 -class IRHash(common.AbstractIRCommandPlugin):
100 name = "hash"
101
102 __args = [
103 dict(name="paths", positional=True, type="Array",
104 help="Paths to hash."),
105 dict(name="hash", type="ChoiceArray", default=["sha1"],
106 choices=["md5", "sha1", "sha256"],
107 help="One or more hashes to calculate.")
108 ]
109
110 table_header = [
111 dict(name="Hashes", width=72),
112 dict(name="Path", type="FileInformation"),
113 ]
114
116 hashers = dict((name, getattr(hashlib, name)()) for name in hashes)
117 fd = file_info.open()
118 while 1:
119 data = fd.read(BUFFER_SIZE)
120 if not data:
121 break
122
123 for hasher in hashers.values():
124 hasher.update(data)
125
126 for key in list(hashers):
127 hashers[key] = hashers[key].hexdigest()
128
129 return hashers
130
139
140
142 - def __init__(self, session, component=None, cache=None):
146
147 - def stat(self, path):
156
158 return unicode(self) == unicode(other)
159
161 return hash(unicode(self))
162
164 return "%s:%s" % (self.__class__.__name__, self.component)
165
166
168
170 if platform.system() == "Windows":
171 return True
172
173 return False
174
198
199
215
216
221
222 - def filter(self, path, depth=0):
248
249
250 -class IRGlob(common.AbstractIRCommandPlugin):
251 """Search for files by filename glob.
252
253 This code roughly based on the Glob flow in GRR.
254 """
255
256 name = "glob"
257
258 __args = [
259 dict(name="globs", positional=True, type="ArrayString",
260 help="List of globs to return."),
261 dict(name="root",
262 help="Root directory to glob from."),
263 dict(name="case_insensitive", default=True, type="Bool",
264 help="Globs will be case insensitive."),
265 dict(name="path_sep",
266 help="Path separator character (/ or \\)"),
267 dict(name="filesystem", choices=list(common.FILE_SPEC_DISPATCHER),
268 type="Choices", default="API",
269 help="The virtual filesystem implementation to glob in.")
270 ]
271
272 table_header = [
273 dict(name="path", type="FileInformation"),
274 ]
275
278
279 INTERPOLATED_REGEX = re.compile(r"%%([^%]+?)%%")
280
281
282 GROUPING_PATTERN = re.compile("({([^}]+,[^}]+)}|%%([^%]+?)%%)")
283 RECURSION_REGEX = re.compile(r"\*\*(\d*)")
284
285
286 GLOB_MAGIC_CHECK = re.compile("[*?[]")
287
300
343
345 """Converts a glob pattern into a list of pathspec components.
346
347 Wildcards are also converted to regular expressions. The pathspec
348 components do not span directories, and are marked as a regex or a
349 literal component.
350
351 We also support recursion into directories using the ** notation. For
352 example, /home/**2/foo.txt will find all files named foo.txt recursed 2
353 directories deep. If the directory depth is omitted, it defaults to 3.
354
355 Example:
356 /home/test**/*exe -> [{path: 'home', type: "LITERAL",
357 {path: 'test.*\\Z(?ms)', type: "RECURSIVE",
358 {path: '.*exe\\Z(?ms)', type="REGEX"}]]
359
360 Args:
361 pattern: A glob expression with wildcards.
362
363 Returns:
364 A list of PathSpec instances for each component.
365
366 Raises:
367 ValueError: If the glob is invalid.
368
369 """
370 pattern_components = common.FileSpec(
371 pattern, path_sep=self.plugin_args.path_sep).components()
372
373 components = []
374 for path_component in pattern_components:
375 if not path_component:
376 continue
377
378
379
380 m = self.RECURSION_REGEX.search(path_component)
381 if m:
382 depth = 3
383
384
385 if m.group(1):
386 depth = int(m.group(1))
387
388 path_component = path_component.replace(m.group(0), "*")
389 component = RecursiveComponent(
390 session=self.session,
391 component=fnmatch.translate(path_component),
392 cache=self.component_cache,
393 depth=depth)
394
395 elif self.GLOB_MAGIC_CHECK.search(path_component):
396 component = RegexComponent(
397 session=self.session,
398 cache=self.component_cache,
399 component=fnmatch.translate(path_component))
400
401 else:
402 component = LiteralComponent(
403 session=self.session,
404 cache=self.component_cache,
405 component=path_component)
406
407 components.append(component)
408
409 return components
410
412 """Path is the pathspec of the path we begin evaluation with."""
413 for component, child_node in node.iteritems():
414
415 if not child_node:
416 for subpath in component.filter(path):
417 yield subpath
418
419 else:
420
421 for matching_path in component.filter(path):
422 for subpath in self._filter(child_node, matching_path):
423 yield subpath
424
426 expanded_globs = []
427 for glob in globs:
428 expanded_globs.extend(self._interpolate_grouping(glob))
429
430 component_tree = {}
431 for glob in expanded_globs:
432 node = component_tree
433 for component in self.convert_glob_into_path_components(glob):
434 node = node.setdefault(component, {})
435
436 return component_tree
437
444
448
449
451 """This is used for debugging the component_tree."""
452 if not tree:
453 return
454
455 for k, v in tree.iteritems():
456 print "%s %s:" % (depth, k)
457 print_component_tree(v, depth + " ")
458
459
460
462 """Hexdump files from disk."""
463
464 name = "hexdump_file"
465
466 __args = [
467 dict(name="start", type="IntParser", default=0,
468 help="An offset to hexdump."),
469
470 dict(name="length", type="IntParser", default=100,
471 help="Maximum length to dump."),
472
473 dict(name="width", type="IntParser", default=24,
474 help="Number of bytes per row"),
475
476 dict(name="rows", type="IntParser", default=4,
477 help="Number of bytes per row"),
478 ]
479
480 table_header = [
481 dict(name="divider", type="Divider"),
482 dict(name="FileSpec", hidden=True),
483 dict(name="offset", style="address"),
484 dict(name="hexdump", width=65),
485 ]
486
514