1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 """ This file defines some basic types which might be useful for many
25 OS's
26 """
27 import datetime
28 import socket
29 import struct
30
31 import arrow
32
33 from rekall import config
34 from rekall import obj
35 from rekall.plugins.overlays import native_types
36 from rekall_lib import utils
37
38
39 config.DeclareOption(
40 "--timezone", default="UTC", group="Interface",
41 help="Timezone to output all times (e.g. Australia/Sydney).")
42
43
44 -class String(obj.StringProxyMixIn, obj.NativeType):
45 """Class for dealing with Null terminated C Strings.
46
47 Note that these strings are _not_ text strings - they are effectively bytes
48 arrays and therefore are not encoded in any particular unicode encoding.
49 """
50
51 - def __init__(self, length=1024, max_length=1024000, term="\x00", **kwargs):
52 """Constructor.
53
54 Args:
55 length: The maximum length of the string.
56
57 terminator: The terminator for this string. If None, there will be no
58 checking for null terminations (Pure character array).
59 """
60 super(String, self).__init__(**kwargs)
61
62
63 if callable(length):
64 length = length(self.obj_parent)
65
66 self.term = term
67 self.length = int(length)
68 self.max_length = max_length
69
70 @utils.safe_property
72 return self.obj_offset + self.length
73
76
77 - def v(self, vm=None):
78
79 length = self.length
80 if self.length > self.max_length:
81 self.obj_session.logging.warn("%s@%#x truncated",
82 self.obj_name, self.obj_offset)
83 length = 0
84
85
86 vm = vm or self.obj_vm
87 data = vm.read(self.obj_offset, length)
88 if self.term is not None:
89 try:
90 left, sep, _ = data.partition(self.term)
91 data = left + sep
92
93 except ValueError:
94 pass
95
96 return data
97
99 return self.obj_vm.write(self.obj_offset, data)
100
102 """ Return an object to be proxied """
103 return str(self)
104
106
107 return self.v().rstrip("\x00")
108
110 return self.v().decode("utf8", "replace").split("\x00")[0] or u""
111
113 return len(str(self))
114
115 @utils.safe_property
118
121
123 """Set up mappings for concat"""
124 return str(self) + other
125
127 """Set up mappings for reverse concat"""
128 return other + str(self)
129
130 @utils.safe_property
132 """This is equivalent to strlen() plus the terminator."""
133
134 return len(self.v())
135
138 """A string forming a signature."""
139
140 - def __init__(self, value=None, **kwargs):
144
146 return self.v() == self.signature
147
150 """A class for dealing with encoded text strings.
151
152 Text strings are always encoded in some way in memory. The specific way of
153 encoding them is called the "encoding" - for example usually (but not
154 always) in windows the encoding is called "utf16", while on linux its
155 usually "utf8".
156
157 By default we take the encoding from the profile constant
158 "default_text_encoding".
159 """
160
161 - def __init__(self, encoding=None, **kwargs):
165
166 - def v(self, vm=None):
180
183
185 return self.v().split("\x00")[0] or u""
186
189
191 return len(unicode(self))
192
194 value = utils.SmartStr(self)
195 elide = ""
196 if len(value) > 50:
197 elide = "..."
198 value = value[:50]
199
200 return "%s (%s%s)" % (super(UnicodeString, self).__repr__(),
201 value, elide)
202
203 @utils.safe_property
205 return len(self.v()) * 2
206
207
208
212
213
214 -class Flags(obj.NativeType):
215 """ This object decodes each flag into a string """
216
217 maskmap = None
218
219 - def __init__(self, bitmap=None, maskmap=None,
220 target="unsigned long", target_args=None, **kwargs):
231
232 @utils.safe_property
235
236 - def v(self, vm=None):
237 return self.target_obj.v(vm=vm)
238
240 value = self.v()
241 for k, v in sorted(self.maskmap.items()):
242 if value & v:
243 yield k
244
246 flags = []
247 length = 0
248
249 for flag in self:
250 length += len(flag)
251 if length >= 40:
252 flags.append(u'...')
253 break
254
255 flags.append(flag)
256
257 return "%s (%s)" % (super(Flags, self).__repr__(), ", ".join(flags))
258
260 if isinstance(data, basestring):
261 value = 0
262 for item in data.split("|"):
263 item = item.strip()
264 mask = self.maskmap.get(item, 0)
265 value |= mask
266
267 data = value
268
269 return self.target_obj.write(data)
270
277
280 """Enumeration class for handling multiple meanings for a single value"""
281
282 - def __init__(self, choices=None, enum_name=None,
283 target="unsigned long", target_args=None, value=None,
284 default=None, **kwargs):
285 """Construct an enumeration instance.
286
287 The enumeration is constructed over the top of a target (which is
288 assumed to produce an integer value). The value of the target is then
289 looked up in the choices. Note that the enumeration is treated as an
290 integer.
291
292 Args:
293 choices: A dict of int values (keys) and names (values).
294
295 enum_name: If provided, the choices dict is retrieved from the
296 profile's constant area. This avoids the profile generator from
297 having to make copies of the enum choices for each field which uses
298 the same enum.
299
300 target: The target type which we overlay on.
301
302 value: Usually the value is parsed from the address space, but if the
303 value parameter is provided, we initialize from this value.
304
305 default: If the underlying integer does not appear in the choices
306 dict, we use this default value.
307 """
308 super(Enumeration, self).__init__(**kwargs)
309
310 if enum_name:
311 choices = self.obj_profile.get_enum(enum_name) or {}
312
313 if callable(choices):
314 choices = choices(self.obj_parent)
315 elif not choices:
316 choices = {}
317
318
319
320 self.choices = dict((str(k), v) for k, v in choices.iteritems())
321 self.default = default
322 if callable(value):
323 value = value(self.obj_parent)
324
325 self.value = value
326 if value is None:
327 self.target = target
328 self.target_obj = self.obj_profile.Object(
329 target, offset=self.obj_offset,
330 vm=self.obj_vm, context=self.obj_context,
331 **(target_args or {}))
332
333 @utils.safe_property
336
338 return str(self.v()) in self.choices
339
340 - def v(self, vm=None):
341 if self.value is None:
342 return self.target_obj.v(vm=vm)
343
344
345 return self.value
346
352
354
355
356
357
358
359 return hash(self.v())
360
362 value = self.v()
363
364 return self.choices.get(utils.SmartStr(value), self.default) or (
365 u"UNKNOWN (%s)" % utils.SmartUnicode(value))
366
368 if isinstance(other, (int, long)):
369 return str(self.v()) == str(other)
370
371
372 for k, v in self.choices.iteritems():
373 if v == other:
374 return str(self.v()) == k
375
379
380 _reverse_choices = None
381
382 @utils.safe_property
388
390 value = self.reverse_choices.get(attr, None)
391 if value is None:
392 raise AttributeError(attr)
393 return value is not None and self.v() == value
394
397 """Provides proper output for Ipv4Address objects"""
398
404
405 - def v(self, vm=None):
406 value = super(Ipv4Address, self).v(vm=vm)
407 return socket.inet_ntoa(struct.pack("<I", value))
408
411 """Provides proper output for Ipv6Address objects"""
412
417
418 - def v(self, vm=None):
420
423 """A MAC address."""
424
429
430 - def v(self, vm=None):
433
436 """A helper for following lists."""
437 _forward = "Flink"
438 _backward = "Blink"
439
441 """Recasts the list entry as a member in a type, and return the type.
442
443 Args:
444 type: The name of this Struct type.
445 member: The name of the member of this Struct.
446 address_space: An optional address space to switch during
447 deferencing.
448 """
449 offset = self.obj_profile.get_obj_offset(type, member)
450
451 item = self.obj_profile.Object(
452 type_name=type, offset=self.obj_offset - offset,
453 vm=vm or self.obj_vm, parent=self.obj_parent,
454 name=type, context=self.obj_context)
455
456 return item
457
459 """Follows all the list entries starting from self.
460
461 We basically convert the list to a tree and recursively search it for
462 new nodes. From each node we follow the Flink and then the Blink. When
463 we see a node we already have, we backtrack. This allows us to find
464 nodes which do not satisfy the relation (Due to smear):
465
466 x.Flink.Blink = x
467
468 Reference:
469 http://en.wikipedia.org/wiki/Depth-first_search
470 """
471
472 result = []
473 seen = set()
474
475 stack = [self]
476 while stack:
477 item = stack.pop()
478 if item.obj_offset not in seen:
479 offset = item.obj_offset
480 seen.add(offset)
481 result.append(offset)
482
483 Blink = item.m(self._backward)
484 if Blink.is_valid():
485 stack.append(Blink.dereference())
486
487 Flink = item.m(self._forward)
488 if Flink.is_valid():
489 stack.append(Flink.dereference())
490
491 return result
492
510
515
517 """Reflect this list element by following its Flink and Blink.
518
519 This is basically the same as Flink.Blink except that it also checks
520 Blink.Flink. It also ensures that Flink and Blink are dereferences to
521 the correct type in case the vtypes do not specify them as pointers.
522
523 Returns:
524 the result of Flink.Blink.
525 """
526 result = self.m(self._forward).dereference_as(
527 self.obj_type, vm=vm).m(self._backward).dereference_as(
528 self.obj_type)
529
530 if not result:
531 return obj.NoneObject("Flink not valid.")
532
533 return result
534
537
541
544
545
546 -class _LIST_ENTRY(ListMixIn, obj.Struct):
547 """ Adds iterators for _LIST_ENTRY types """
548
552 tz_name = ""
553 tz_dst = datetime.timedelta(0)
554
557
560
563
566
569 """A unix timestamp (seconds since the epoch)."""
570
571 timeformat = "YYYY-MM-DD HH:mm:ss"
572
573 - def __init__(self, format_string="I", **kwargs):
576
579
581 if isinstance(other, (float, int, long)):
582 return UnixTimeStamp(
583 value=self.v() + other, profile=self.obj_profile)
584
585 raise NotImplementedError
586
587 - def display(self, custom_tz=None, utc_shift=None):
588 try:
589 arw = self.as_arrow()
590 if custom_tz:
591 try:
592 arw = arw.to(custom_tz)
593 except RuntimeError:
594 pass
595 elif utc_shift is not None:
596 arw = arw.to(ShiftedTimezone(int(utc_shift)))
597
598 else:
599 arw = arw.to(self.obj_session.GetParameter("timezone", "UTC"))
600
601
602 formatted_date = arw.format(self.timeformat)
603 formatted_tz = arw.format("Z")
604 if formatted_tz == "-0000":
605 formatted_tz = "Z"
606
607 return formatted_date + formatted_tz
608
609 except ValueError as e:
610 return obj.NoneObject("Error: %s", e)
611
614
618
620 value = self.v()
621 if not value:
622 return obj.NoneObject("")
623
624 try:
625
626 return arrow.Arrow.utcfromtimestamp(self.v())
627 except (ValueError, TypeError) as e:
628 return obj.NoneObject("Datetime conversion failure: " + str(e))
629
632
635 """An enumeration which receives its value from a callable."""
636
637 - def __init__(self, value=None, parent=None, **kwargs):
643
644 - def v(self, vm=None):
646
647
648 -class timeval(UnixTimeStamp, obj.Struct):
649
650 - def v(self, vm=None):
651 return float(self.m("tv_sec")) + self.m("tv_usec") / 1e6
652
655 """Class for handling Windows Time Stamps"""
656
657 - def __init__(self, is_utc=False, **kwargs):
660
663
664 - def v(self, vm=None):
665 value = self.as_windows_timestamp()
666
667 unix_time = value / 10000000 - 11644473600
668 if unix_time < 0:
669 unix_time = 0
670
671 return unix_time
672
675 """Handles ThreadCreateTimeStamps which are bit shifted WinFileTimes"""
676
679
682 """An array which can be addressed via constant names."""
683
684 - def __init__(self, index_table=None, **kwargs):
685 super(IndexedArray, self).__init__(**kwargs)
686 try:
687 self.index_table = dict(
688 (x, int(y)) for x, y in index_table.items())
689 except ValueError:
690 self.index_table = dict(
691 (y, int(x)) for x, y in index_table.items())
692
693 if self.count == 0:
694 self.count = len(index_table)
695
697
698 if isinstance(item, (int, long)):
699 index = item
700
701
702 for k, v in self.index_table.items():
703 if v == item:
704 item = k
705 break
706
707 elif item in self.index_table:
708 index = self.index_table[item]
709 else:
710 raise KeyError("Unknown index %s" % item)
711
712 result = super(IndexedArray, self).__getitem__(index)
713 result.obj_name = str(item)
714
715 return result
716
721 """Basic profile for 32 bit MIPS systems."""
722 METADATA = dict(
723 arch="MIPS",
724 data_model="BE32"
725 )
726
727 @classmethod
733
736 """Basic profile for 32 bit systems."""
737 METADATA = dict(
738 arch="I386",
739 data_model="ILP32"
740 )
741
742 @classmethod
748
751 """Basic profile for 64 bit Windows systems."""
752 METADATA = dict(
753 arch="AMD64",
754 data_model="LLP64"
755 )
756
757 @classmethod
763
766 """Basic profile for 64 bit Linux systems."""
767 METADATA = dict(
768 arch="AMD64",
769 data_model="LP64"
770 )
771
772 @classmethod
776
777
778 common_overlay = {
779 'LIST_ENTRY32': [0x8, {
780 'Flink': [0x0, ['Pointer32', dict(
781 target='LIST_ENTRY32'
782 )]],
783 'Blink': [0x4, ['Pointer32', dict(
784 target='LIST_ENTRY32'
785 )]],
786 }],
787
788 'LIST_ENTRY64': [0x10, {
789 'Flink': [0x0, ['pointer', ['LIST_ENTRY64']]],
790 'Blink': [0x8, ['pointer', ['LIST_ENTRY64']]],
791 }]
792 }
796 """Basic profile which introduces the basic classes."""
797
798 @classmethod
800 super(BasicClasses, cls).Initialize(profile)
801
802 profile.add_classes({
803 'String': String,
804 "Signature": Signature,
805 'UnicodeString': UnicodeString,
806 'Flags': Flags,
807 'Enumeration': Enumeration,
808 'Ipv4Address': Ipv4Address,
809 'Ipv6Address': Ipv6Address,
810 'MacAddress': MacAddress,
811 '_LIST_ENTRY': _LIST_ENTRY,
812 'LIST_ENTRY32': _LIST_ENTRY,
813 'LIST_ENTRY64': _LIST_ENTRY,
814 'WinFileTime': WinFileTime,
815 'ThreadCreateTimeStamp': ThreadCreateTimeStamp,
816 'UnixTimeStamp': UnixTimeStamp, 'timeval': timeval,
817 "IndexedArray": IndexedArray,
818 "ValueEnumeration": ValueEnumeration,
819 })
820 profile.add_constants(dict(default_text_encoding="utf-16-le"))
821 profile.add_overlay(common_overlay)
822
825 """A mixin which shifts all constant addresses by a constant."""
826
835
836
839
840 - def add_constants(self, constants=None, constants_are_absolute=False,
841 **opts):
846
848 """Gets the constant from the profile.
849
850 The windows profile specify addresses relative to the kernel image base.
851 """
852 base_constant = super(RelativeOffsetMixin, self).get_constant(
853 name, is_address=is_address)
854 if is_address and isinstance(base_constant, (int, long)):
855 return base_constant + self.GetImageBase()
856
857
858 if base_constant == None:
859 absolute_constant = self.absolute_constants.get(name)
860 if absolute_constant:
861
862 if callable(absolute_constant):
863 absolute_constant = absolute_constant()
864 return absolute_constant
865
866 return base_constant
867
880
883 """cast a member of a structure out to the containing structure.
884
885 http://lxr.free-electrons.com/source/include/linux/kernel.h?v=3.7#L677
886 """
887 offset = ptr.v() - ptr.obj_profile.get_obj_offset(type, member)
888 return ptr.obj_profile.Object(type, offset=offset, vm=ptr.obj_vm)
889