Package rekall :: Module obj
[frames] | no frames]

Source Code for Module rekall.obj

   1  # Rekall Memory Forensics 
   2  # Copyright (C) 2007,2008 Volatile Systems 
   3  # Copyright 2013 Google Inc. All Rights Reserved. 
   4  # 
   5  # Copyright (C) 2005,2006 4tphi Research 
   6  # Author: {npetroni,awalters}@4tphi.net (Nick Petroni and AAron Walters) 
   7  # Author: Michael Cohen scudette@gmail.com. 
   8  # 
   9  # This program is free software; you can redistribute it and/or modify 
  10  # it under the terms of the GNU General Public License as published by 
  11  # the Free Software Foundation; either version 2 of the License, or (at 
  12  # your option) any later version. 
  13  # 
  14  # This program is distributed in the hope that it will be useful, but 
  15  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  16  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
  17  # General Public License for more details. 
  18  # 
  19  # You should have received a copy of the GNU General Public License 
  20  # along with this program; if not, write to the Free Software 
  21  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
  22  # 
  23   
  24  """ 
  25  The Rekall Memory Forensics object system. 
  26   
  27  """ 
  28  __author__ = ("Michael Cohen <scudette@gmail.com> based on original code " 
  29                "by AAron Walters and Brendan Dolan-Gavitt with contributions " 
  30                "by Mike Auty") 
  31   
  32  import atexit 
  33  import inspect 
  34  import json 
  35  import logging 
  36  import pdb 
  37  import operator 
  38  import os 
  39  import struct 
  40  import StringIO 
  41  import copy 
  42   
  43  import traceback 
  44   
  45  from rekall import addrspace 
  46  from rekall.ui import renderer 
  47  from rekall_lib import registry 
  48  from rekall_lib import utils 
49 50 51 -class ProfileLog(object):
52 # Point this environment variable into a filename we will use to store 53 # profiling results. 54 ENVIRONMENT_VAR = "DEBUG_PROFILE" 55
56 - class JSONEncoder(json.JSONEncoder):
57 - def default(self, obj): # pylint: disable=method-hidden
58 if isinstance(obj, set): 59 return sorted(obj) 60 61 return json.JSONEncoder.default(self, obj)
62 63 @staticmethod
64 - def as_set(obj):
65 for k in obj: 66 if isinstance(obj[k], list): 67 obj[k] = set(obj[k]) 68 69 return obj
70
71 - def __init__(self):
72 self.data = {} 73 self.filename = os.environ.get(self.ENVIRONMENT_VAR) 74 if self.filename: 75 # Ensure we update the object access log when we exit. 76 atexit.register(self._DumpData)
77
78 - def _MergeData(self, data):
79 for profile, structs in data.items(): 80 if profile not in self.data: 81 self.data[profile] = data[profile] 82 else: 83 for c_struct, fields in structs.items(): 84 if c_struct not in self.data[profile]: 85 self.data[profile][c_struct] = fields 86 else: 87 self.data[profile][c_struct].update(fields)
88
89 - def _DumpData(self):
90 try: 91 with utils.FileLock(open(self.filename, "rb")) as fd: 92 self._MergeData(json.loads( 93 fd.read(), object_hook=self.JSONEncoder.as_set)) 94 95 with open(self.filename, "wb") as fd: 96 fd.write(json.dumps(self.data, cls=self.JSONEncoder)) 97 98 except (IOError, ValueError): 99 pass 100 101 logging.info("Updating object access database %s", self.filename) 102 with open(self.filename, "wb") as fd: 103 fd.write(json.dumps(self.data, cls=self.JSONEncoder))
104
105 - def LogFieldAccess(self, profile, obj_type, field_name):
106 # Do nothing unless the environment is set. 107 if self.is_active(): 108 profile = self.data.setdefault(profile, {}) 109 fields = profile.setdefault(obj_type, set()) 110 if field_name: 111 fields.add(field_name)
112
113 - def LogConstant(self, profile, name):
114 self.LogFieldAccess(profile, "Constants", name)
115 116 @staticmethod
117 - def is_active():
118 return bool(os.environ.get(ProfileLog.ENVIRONMENT_VAR))
119 120 121 # This is used to store Struct member access when the DEBUG_PROFILE environment 122 # variable is set. 123 ACCESS_LOG = ProfileLog()
124 125 126 -class Curry(object):
127 - def __init__(self, curry_target, *args, **kwargs):
128 self._target = curry_target 129 self._kwargs = kwargs 130 self._args = args 131 self._default_arguments = kwargs.pop("default_arguments", []) 132 self.__doc__ = self._target.__doc__ 133 self.__wrapped__ = self._target
134
135 - def __call__(self, *args, **kwargs):
136 # Merge the kwargs with the new kwargs 137 new_kwargs = self._kwargs.copy() 138 new_kwargs.update(kwargs) 139 return self._target(*(self._args + args), **new_kwargs)
140
141 - def get_default_arguments(self):
142 """Return a list of default args for the target.""" 143 if self._default_arguments is not None: 144 return self._default_arguments 145 146 args, _, _, defaults = inspect.getargspec(self._target) 147 if defaults: 148 return args[-len(defaults):] 149 150 return []
151
152 - def __getattr__(self, attr):
153 return getattr(self._target, attr)
154
155 156 -class NoneObject(object):
157 """A magical object which is like None but swallows bad 158 dereferences, __getattr__, iterators etc to return itself. 159 160 Instantiate with the reason for the error. 161 """ 162 __metaclass__ = registry.UniqueObjectIdMetaclass 163
164 - def __init__(self, reason="None Object", *args, **kwargs):
165 # Often None objects are instantiated on purpose so its not really that 166 # important to see their reason. 167 if kwargs.get("log"): 168 logging.log(logging.WARN, reason) 169 self.reason = unicode(reason) 170 self.strict = kwargs.get("strict") 171 self.args = args 172 if self.strict: 173 self.bt = ''.join(traceback.format_stack()[:-2])
174
175 - def __str__(self):
176 return unicode(self).encode('utf-8')
177
178 - def __unicode__(self):
179 # If we are strict we blow up here 180 if self.strict: 181 if "%" in self.reason: 182 reason = self.reason % self.args 183 else: 184 reason = self.reason.format(*self.args) 185 logging.error("%s\n%s", reason, self.bt) 186 187 return "-"
188
189 - def FormatReason(self):
190 if "%" in self.reason: 191 return self.reason % self.args 192 else: 193 return self.reason.format(*self.args)
194
195 - def __repr__(self):
196 return "<%s>" % self.FormatReason()
197
198 - def __setitem__(self, item, other):
199 return
200
201 - def __format__(self, formatstring):
202 """We suppress output for all format operators.""" 203 formatstring = formatstring.replace("d", "s") 204 formatstring = formatstring.replace("x", "s") 205 formatstring = formatstring.replace("#", "") 206 return ("{0:%s}" % formatstring).format("-")
207
208 - def write(self, _):
209 """Write procedure only ever returns False""" 210 return False
211 212 # Behave like an empty set
213 - def __iter__(self):
214 return iter([])
215
216 - def __len__(self):
217 return 0
218
219 - def __getattr__(self, attr):
220 # By returning self for any unknown attribute and ensuring the self is 221 # callable, we cover both properties and methods Override NotImplemented 222 # functions in object with self 223 return self
224
225 - def __bool__(self):
226 return False
227
228 - def __nonzero__(self):
229 return False
230 231 # Comparisons.
232 - def __eq__(self, other):
233 return other is None
234
235 - def __ne__(self, other):
236 return other is not None
237
238 - def __gt__(self, _):
239 return False
240 241 __lt__ = __gt__ 242 __le__ = __gt__ 243 __ge__ = __gt__ 244 245 # Make us subscriptable obj[j]
246 - def __getitem__(self, item):
247 return self
248
249 - def __call__(self, *arg, **kwargs):
250 return self
251
252 - def __int__(self):
253 return -1
254 255 __add__ = __call__ 256 __sub__ = __call__ 257 __mul__ = __call__ 258 __floordiv__ = __call__ 259 __mod__ = __call__ 260 __div__ = __call__ 261 __divmod__ = __call__ 262 __pow__ = __call__ 263 __lshift__ = __call__ 264 __rshift__ = __call__ 265 __and__ = __call__ 266 __xor__ = __call__ 267 __or__ = __call__ 268 269 __radd__ = __call__ 270 __rsub__ = __call__ 271 __rmul__ = __call__ 272 __rfloordiv__ = __call__ 273 __rmod__ = __call__ 274 __rdivmod__ = __call__ 275 __rpow__ = __call__ 276 __rlshift__ = __call__ 277 __rrshift__ = __call__ 278 __rand__ = __call__ 279 __rxor__ = __call__ 280 __ror__ = __call__ 281 282 # Override these methods too. 283 dereference_as = __call__ 284 __getitem__ = __call__
285
286 287 -class Error(Exception):
288 """All object related exceptions come from this one."""
289
290 291 -class ProfileError(Error):
292 """Errors in setting the profile."""
293
294 295 -class BaseObject(object):
296 __metaclass__ = registry.UniqueObjectIdMetaclass 297 298 obj_parent = NoneObject("No parent") 299 obj_name = NoneObject("No name") 300 obj_producers = None 301 302 # BaseObject implementations may take arbitrary **kwargs. The usual 303 # programming pattern is to define the keywords each class takes explicitely 304 # as as a generic **kwargs parameter. Then call the baseclass and pass the 305 # kwargs down. Any **kwargs which arrive here are not handled, and represent 306 # an error in the vtype specifications.
307 - def __init__(self, type_name=None, offset=0, vm=None, profile=None, 308 parent=None, name='', context=None, session=None, **kwargs):
309 """Constructor for Base object. 310 311 Args: 312 type_name: The name of the type of this object. This different 313 from the class name, since the same class may implement many types 314 (e.g. Struct implements every instance in the vtype definition). 315 316 offset: The offset within the address space to this object exists. 317 318 vm: The address space this object uses to read itself from. 319 320 profile: The profile this object may use to dereference other 321 types. 322 323 parent: The object which created this object. 324 325 name: The name of this object. 326 327 context: An opaque dict which is passed to all objects created from 328 this object. This dict may contain context specific information 329 which each derived instance can use. 330 331 kwargs: Arbitrary args this object may accept - these can be passed in 332 the vtype language definition. 333 """ 334 if kwargs: 335 session.logging.error("Unknown keyword args %s for %s", 336 kwargs, self.__class__.__name__) 337 338 if session is None: 339 raise RuntimeError("Session must be provided") 340 341 if profile is None: 342 profile = session.profile 343 344 self.obj_type = type_name or self.__class__.__name__ 345 346 # 64 bit addresses are always sign extended, so we need to clear the top 347 # bits. 348 self.obj_offset = Pointer.integer_to_address(int(offset or 0)) 349 self.obj_vm = vm 350 self.obj_parent = parent 351 self.obj_name = name 352 self.obj_profile = profile 353 self.obj_context = context or {} 354 355 if not session: 356 raise ValueError("Session must be provided.") 357 358 self.obj_session = session 359 self.obj_producers = set()
360 361 @utils.safe_property
362 - def obj_size(self):
363 return 0
364 365 @utils.safe_property
366 - def obj_end(self):
367 return self.obj_offset + self.obj_size
368
369 - def GetData(self):
370 """Returns the raw data of this object.""" 371 return self.obj_vm.read(self.obj_offset, self.obj_size)
372 373 @utils.safe_property
374 - def parents(self):
375 """Returns all the parents of this object.""" 376 obj = self 377 while obj.obj_parent: 378 obj = obj.obj_parent 379 yield obj
380
381 - def proxied(self):
382 return None
383
384 - def write(self, value):
385 """Function for writing the object back to disk""" 386 pass
387
388 - def __nonzero__(self):
389 """This method is called when we test the truth value of an Object. 390 391 In rekall we consider an object to have True truth value only when it is 392 a valid object. Its possible for example to have a Pointer object which 393 is not valid - this will have a truth value of False. 394 395 You should be testing for validity like this: 396 if X: 397 # object is valid 398 399 Do not test for validity like this: 400 401 if int(X) == 0: 402 403 or 404 405 if X is None: 406 ..... 407 408 the later form is not going to work when X is a NoneObject. 409 """ 410 return self.is_valid()
411
412 - def __eq__(self, other):
413 return self.v() == other or ( 414 # Same object type 415 (self.__class__ == other.__class__) and 416 417 # Same physical memory backing the two objects. Note that often two 418 # objects may exist in different address spaces, but be mapped to 419 # the same physical memory. In Rekall we assume these two objects 420 # are actually equivalent since they share the same physical memory. 421 (self.obj_vm.base == other.obj_vm.base) and 422 (self.obj_vm.vtop(self.obj_offset) == 423 other.obj_vm.vtop(other.obj_offset)))
424
425 - def __hash__(self):
426 # This needs to be the same as the object we proxy so that we can mix 427 # with native types in sets and dicts. For example: 428 # pids = set([1,2,3]) 429 # if task.UniqueProcessId in pids: .... 430 return hash(self.v())
431 432 @utils.safe_property
433 - def indices(self):
434 """Returns (usually 1) representation(s) of self usable as dict keys. 435 436 Using full base objects for indexing can be slow, especially with 437 Structs. This method returns a representation of the object that is 438 a suitable key - either the value of a primitive type, or the memory 439 address of the more complex ones. 440 """ 441 return (self.v(),)
442 443 @classmethod
444 - def getproperties(cls):
445 """Return all members that are intended to represent some data.""" 446 for name in dir(cls): 447 candidate = getattr(cls, name) 448 if isinstance(candidate, property): 449 yield name, candidate
450
451 - def m(self, memname):
452 return NoneObject("No member {0}", memname)
453
454 - def is_valid(self):
455 return True
456
457 - def deref(self, vm=None):
458 """An alias for dereference - less to type.""" 459 return self.dereference(vm=vm)
460
461 - def dereference(self, vm=None):
462 _ = vm 463 return NoneObject("Can't dereference {0}", self.obj_name)
464
465 - def reference(self):
466 """Produces a pointer to this object. 467 468 This is the same as the C & operator and is the opposite of deref(). 469 """ 470 return self.obj_profile.Pointer(value=self.obj_offset, vm=self.obj_vm, 471 target=self.obj_type)
472
473 - def cast(self, type_name=None, vm=None, **kwargs):
474 # Allow the caller to also change the offset, overriding the current 475 # object. 476 offset = kwargs.pop("offset", self.obj_offset) 477 profile_obj = kwargs.pop("profile", self.obj_profile) 478 479 return profile_obj.Object( 480 type_name=type_name or self.obj_type, offset=offset, 481 vm=vm or self.obj_vm, parent=self.obj_parent, 482 context=self.obj_context, **kwargs)
483
484 - def v(self, vm=None):
485 """ Do the actual reading and decoding of this member 486 487 When vm is specified, we are asked to evaluate this object is another 488 address space than the one it was created on. Derived classes should 489 allow for this. 490 """ 491 _ = vm 492 return NoneObject("No value for {0}", self.obj_name)
493
494 - def __str__(self):
495 return utils.SmartStr(unicode(self))
496
497 - def __unicode__(self):
498 fd = StringIO.StringIO() 499 ui_renderer = renderer.BaseRenderer.classes["TextRenderer"]( 500 session=self.obj_session, fd=fd) 501 with ui_renderer.start(): 502 ui_renderer.format("{}", self) 503 504 return utils.SmartUnicode(fd.getvalue())
505
506 - def __repr__(self):
507 # Is this a prototype? (See profile.GetPrototype for explanation.) 508 if self.obj_offset == 0 and self.obj_name == "Prototype": 509 return "%s Prototype" % self.__class__.__name__ 510 511 return "[{0} {1}] @ 0x{2:08X}".format( 512 self.__class__.__name__, self.obj_name, 513 self.obj_offset)
514
515 - def __dir__(self):
516 """Hide any members with _.""" 517 result = self.__dict__.keys() + dir(self.__class__) 518 519 return result
520
521 - def __format__(self, formatspec):
522 if not formatspec: 523 formatspec = "s" 524 525 if formatspec[-1] in "xdXD": 526 return format(int(self), formatspec) 527 528 return object.__format__(self, formatspec)
529
530 531 -def CreateMixIn(mixin):
532 def make_method(name): 533 def method(self, *args, **kw): 534 proxied = self.proxied() 535 try: 536 # Try to coerce the other in case its also a proxied 537 # class 538 args = list(args) 539 args[0] = args[0].proxied() 540 except (AttributeError, IndexError): 541 pass 542 543 try: 544 method = getattr(operator, name) 545 args = [proxied] + args 546 except AttributeError: 547 method = getattr(proxied, name) 548 549 return method(*args, **kw)
550 551 return method 552 553 for name in mixin._specials: # pylint: disable=protected-access 554 setattr(mixin, name, make_method(name)) 555
556 557 -class NumericProxyMixIn(object):
558 """ This MixIn implements the numeric protocol """ 559 _specials = [ 560 # Number protocols 561 '__add__', '__sub__', '__mul__', 562 '__floordiv__', '__mod__', '__divmod__', 563 '__pow__', '__lshift__', '__rshift__', 564 '__and__', '__xor__', '__or__', '__div__', 565 '__truediv__', '__radd__', '__rsub__', 566 '__rmul__', '__rdiv__', '__rtruediv__', 567 '__rfloordiv__', '__rmod__', '__rdivmod__', 568 '__rpow__', '__rlshift__', 569 '__rrshift__', '__rand__', '__rxor__', '__ror__', 570 '__neg__', '__pos__', 571 '__abs__', '__invert__', '__int__', '__long__', 572 '__float__', '__oct__', '__hex__', 573 574 # Comparisons 575 '__lt__', '__le__', '__eq__', '__ge__', '__gt__', '__index__', 576 ] 577
578 - def __ne__(self, other):
579 return not self == other
580
581 582 -class StringProxyMixIn(object):
583 """This MixIn implements proxying for strings.""" 584 _specials = [ 585 # Comparisons 586 '__lt__', '__le__', '__eq__', '__ge__', '__gt__', '__index__', 587 ] 588
589 - def __ne__(self, other):
590 return not self == other
591 592 593 CreateMixIn(NumericProxyMixIn) 594 CreateMixIn(StringProxyMixIn)
595 596 597 -class NativeType(NumericProxyMixIn, BaseObject):
598 - def __init__(self, value=None, format_string=None, session=None, 599 profile=None, **kwargs):
600 # If a value is specified, we dont technically need a profile at all. 601 if value is not None and profile is None: 602 profile = NoneObject() 603 604 super(NativeType, self).__init__( 605 session=session, profile=profile, **kwargs) 606 self.format_string = format_string 607 if callable(value): 608 value = value(self.obj_parent) 609 610 self.value = value
611
612 - def write(self, data):
613 """Writes the data back into the address space""" 614 output = struct.pack(self.format_string, int(data)) 615 return self.obj_vm.write(self.obj_offset, output)
616
617 - def proxied(self):
618 return self.v()
619
620 - def __radd__(self, other):
621 return long(other) + self.v()
622
623 - def __rsub__(self, other):
624 return long(other) - self.v()
625 626 @utils.safe_property
627 - def obj_size(self):
628 return struct.calcsize(self.format_string)
629
630 - def v(self, vm=None):
631 if self.value is not None: 632 return self.value 633 634 data = self.obj_vm.read(self.obj_offset, self.obj_size) 635 if not data: 636 return NoneObject("Unable to read {0} bytes from {1}", 637 self.obj_size, self.obj_offset) 638 639 # Cache this for next time. 640 (self.value,) = struct.unpack(self.format_string, data) 641 642 return self.value
643
644 - def cdecl(self):
645 return self.obj_name
646
647 - def __repr__(self):
648 try: 649 return " [{0}:{1}]: 0x{2:08X}".format(self.obj_type, self.obj_name, 650 self.v()) 651 except ValueError: 652 return " [{0}:{1}]: {2}".format(self.obj_type, self.obj_name, 653 repr(self.v()))
654
655 656 -class Bool(NativeType):
657 pass
658
659 660 -class BitField(NativeType):
661 """ A class splitting an integer into a bunch of bit. """ 662
663 - def __init__(self, start_bit=0, end_bit=32, target=None, 664 native_type=None, **kwargs):
665 super(BitField, self).__init__(**kwargs) 666 667 # TODO: Fully deprecate this parameter. 668 if native_type: 669 target = native_type 670 671 self._proxy = self.obj_profile.Object( 672 target or "address", offset=self.obj_offset, vm=self.obj_vm, 673 context=self.obj_context) 674 675 self.target = target 676 self.start_bit = start_bit 677 self.end_bit = end_bit 678 self.mask = ((1 << end_bit) - 1) ^ ((1 << start_bit) - 1)
679 680 @utils.safe_property
681 - def obj_size(self):
682 return self._proxy.obj_size
683
684 - def v(self, vm=None):
685 i = self._proxy.v() 686 return (i & ((1 << self.end_bit) - 1)) >> self.start_bit
687
688 - def write(self, data):
689 # To write we need to read the proxy, set the bits and then write the 690 # proxy again. 691 return 0
692
693 - def __repr__(self):
694 return " [{0}({1}-{2}):{3}]: 0x{4:08X}".format( 695 self.obj_type, self.start_bit, self.end_bit, self.obj_name, 696 self.v())
697
698 - def __nonzero__(self):
699 return bool(self._proxy.v() & self.mask)
700
701 702 -class Pointer(NativeType):
703 """A pointer reads an 'address' object from the address space.""" 704
705 - def __init__(self, target=None, target_args=None, value=None, **kwargs):
706 """Constructor. 707 708 Args: 709 target: The name of the target object (A string). We use the profile 710 to instantiate it. 711 target_args: The target will receive these as kwargs. 712 """ 713 super(Pointer, self).__init__(value=value, **kwargs) 714 715 if value is not None: 716 self.obj_offset = NoneObject("No address specified") 717 718 # We parse the address using the profile since address is a different 719 # size on different platforms. 720 self._proxy = self.obj_profile.Object( 721 "address", offset=self.obj_offset, value=value, 722 vm=self.obj_vm, context=self.obj_context) 723 724 # We just hold on to these so we can construct the objects later. 725 self.target = target 726 self.target_args = target_args or {} 727 self.target_size = 0 728 self.kwargs = kwargs
729 730 @utils.safe_property
731 - def obj_size(self):
732 return self._proxy.obj_size
733
734 - def v(self, vm=None):
735 # 64 bit addresses are always sign extended so we need to clear the top 736 # bits. 737 return Pointer.integer_to_address(self._proxy.v())
738
739 - def m(self, attr):
740 return self.deref().m(attr)
741
742 - def write(self, data):
743 return self._proxy.write(data)
744
745 - def __eq__(self, other):
746 try: 747 # Must use __int__() because int(other) when other is a string will 748 # convert it to an integer. 749 return Pointer.integer_to_address(other.__int__()) == self.v() 750 except (ValueError, AttributeError): 751 return False
752
753 - def is_valid(self):
754 """Returns if what we are pointing to is valid """ 755 # Null pointers are invalid. 756 return self.v() != 0
757
758 - def __getitem__(self, item):
759 """Indexing a pointer indexes its target. 760 761 Note this is different than C which treats pointers as arrays: 762 763 struct foobar *p1; 764 struct foobar *p2[]; 765 766 In C: 767 p[1] -> struct foobar 768 p[2] -> struct foobar * 769 770 In Rekall: 771 p[1] -> Not allowed since structs do not have []. 772 p[2] -> struct foobar. 773 """ 774 res = self.dereference() 775 return res[item]
776
777 - def dereference(self, vm=None):
778 offset = self.v() 779 780 # Casts into the correct AS: 781 vm = vm or self.obj_vm 782 783 if offset: 784 kwargs = copy.deepcopy(self.target_args) 785 kwargs.update(dict(offset=offset, session=self.obj_session, 786 vm=vm, profile=self.obj_profile, 787 parent=self.obj_parent, name=self.obj_name)) 788 789 if isinstance(self.target, basestring): 790 result = self.obj_profile.Object( 791 type_name=self.target, 792 context=self.obj_context, **kwargs) 793 794 elif callable(self.target): 795 result = self.target(**kwargs) 796 else: 797 # Target not valid, return void. 798 result = Void(**kwargs) 799 800 if result.is_valid(): 801 return result 802 803 return NoneObject("Pointer {0} @ {1} invalid", 804 self.obj_name, self.v())
805
806 - def __dir__(self):
807 return dir(self.dereference())
808
809 - def cdecl(self):
810 return "Pointer {0}".format(self.v())
811
812 - def __nonzero__(self):
813 """This method is used in comparison operations. 814 815 This ideas here is to make it possible to easily write a condition such 816 as: 817 818 while ptr: 819 ... 820 ptr += 1 821 822 Pointers are considered non-zero if they are invalid (i.e. what they 823 point to is not mapped in. This is very subtle and might be the wrong 824 choice. Note that if the kernel actually maps the zero page in (which 825 can happen in some situations), then a null pointer is actually valid. 826 """ 827 return bool(self.is_valid())
828
829 - def __add__(self, other):
830 """Return a new pointer advanced by this many positions. 831 832 Note that as usual for pointer arithmetic, the pointer moves by steps of 833 the size of the target. 834 """ 835 # Find out our target size for pointer arithmetics. 836 self.target_size = (self.target_size or 837 self.obj_profile.Object(self.target).obj_size) 838 839 offset = self.obj_offset + int(other) * self.target_size 840 if not self.obj_vm.is_valid_address(offset): 841 return NoneObject("Invalid offset") 842 843 return self.__class__( 844 target=self.target, target_args=self.target_args, 845 offset=offset, vm=self.obj_vm, 846 parent=self.obj_parent, session=self.obj_session, 847 context=self.obj_context, profile=self.obj_profile)
848
849 - def __sub__(self, other):
850 if isinstance(other, Pointer): 851 if not isinstance(other, self.__class__): 852 raise TypeError("Can not subtract non related pointers.") 853 854 # Find out our target size for pointer arithmetics. 855 self.target_size = (self.target_size or 856 self.obj_profile.Object(self.target).obj_size) 857 858 return (int(self) - int(other)) / self.target_size 859 860 return self.__add__(-other)
861
862 - def __iadd__(self, other):
863 # Increment our own offset. 864 self.target_size = (self.target_size or self.target().obj_size) 865 self.obj_offset += self.target_size * other
866
867 - def __repr__(self):
868 target = self.v() 869 target_name = self.obj_session.address_resolver.format_address( 870 target) 871 if target_name: 872 target_name = " (%s)" % target_name[0] 873 else: 874 target_name = "" 875 876 return "<%s %s to [%#010x%s] (%s)>" % ( 877 self.target, self.__class__.__name__, target, 878 target_name, self.obj_name or '')
879
880 - def __unicode__(self):
881 return u"Pointer to %s" % self.deref()
882 883 @utils.safe_property
884 - def indices(self):
885 return self.dereference().indices
886
887 - def __getattr__(self, attr):
888 # We just dereference ourself 889 result = self.dereference() 890 891 return getattr(result, attr)
892
893 - def __iter__(self):
894 """Delegate the iterator to the target.""" 895 return iter(self.dereference())
896
897 - def dereference_as(self, target=None, target_args=None, vm=None, 898 profile=None, parent=None):
899 """Dereference ourselves into another type, or address space. 900 901 This method allows callers to explicitly override the setting in the 902 profile for this pointer. 903 904 Args: 905 target: The target to override. 906 target_args: The args to instantiate this target with. 907 vm: The address space to dereference the pointer in. 908 profile: If a new profile should be used to instantiate the target. 909 """ 910 vm = vm or self.obj_vm 911 912 if profile is None: 913 profile = self.obj_profile 914 915 return profile.Object( 916 type_name=target or self.target, offset=self.v(), vm=vm, 917 parent=parent or self.obj_parent, context=self.obj_context, 918 **(target_args or {}))
919 920 @staticmethod
921 - def integer_to_address(value):
922 """Addresses only use 48 bits.""" 923 return 0xffffffffffff & int(value)
924
925 926 -class Pointer32(Pointer):
927 """A 32 bit pointer (Even in 64 bit arch). 928 929 These kinds of pointers are used most commonly in the Registry code which 930 always treats the hives as 32 bit address spaces. 931 """
932 - def __init__(self, **kwargs):
933 super(Pointer32, self).__init__(**kwargs) 934 self._proxy = self._proxy.cast("unsigned int")
935
936 937 -class Void(Pointer):
938 - def __init__(self, **kwargs):
939 kwargs['type_name'] = 'unsigned long' 940 super(Void, self).__init__(**kwargs)
941
942 - def v(self, vm=None):
943 return self.obj_offset
944
945 - def dereference(self, vm=None):
946 return NoneObject("Void reference")
947 948 @utils.safe_property
949 - def obj_size(self):
950 self.obj_session.logging.warning( 951 "Void objects have no size! Are you doing pointer arithmetic on a " 952 "pointer to void?") 953 return 1
954
955 - def cdecl(self):
956 return "0x{0:08X}".format(self.v())
957
958 - def __repr__(self):
959 return "Void[{0} {1}] (0x{2:08x})".format( 960 self.__class__.__name__, self.obj_name or '', self.v())
961
962 - def __nonzero__(self):
963 return bool(self.dereference())
964
965 966 -class Array(BaseObject):
967 """ An array of objects of the same size """ 968 969 target_size = 0 970
971 - def __init__(self, count=0, target=None, target_args=None, 972 target_size=None, max_count=100000, size=0, 973 **kwargs):
974 """Instantiate an array of like items. 975 976 Args: 977 count: How many items belong to the array (not strictly enforced - 978 i.e. it is possible to read past the end). By default the array is 979 unbound. 980 981 max_count: The maximum size of the array. This is a safety mechanism 982 if count is calculated. max_count should be set to an upper bound on 983 the size of the array. 984 985 target: The name of the element to be instantiated on each point. The 986 size of the object returned by this should be the same for all 987 members of the array (i.e. all elements should be the same size). 988 989 size: The total size of the Array. If this is nonzero we calculate the 990 count so that just the right number of items fit in this specified 991 size. 992 """ 993 super(Array, self).__init__(**kwargs) 994 995 # Allow the count to be callable. 996 if callable(count): 997 count = count(self.obj_parent) 998 999 if callable(target_size): 1000 target_size = target_size(self.obj_parent) 1001 1002 self.count = count 1003 self.max_count = max_count 1004 1005 if not target: 1006 raise AttributeError("Array must use a target parameter") 1007 1008 self.target = target 1009 self.target_args = target_args or {} 1010 1011 self.target_size = target_size 1012 if self.target_size is None: 1013 self.target_size = self.obj_profile.Object( 1014 self.target, offset=self.obj_offset, vm=self.obj_vm, 1015 profile=self.obj_profile, parent=self, 1016 **self.target_args).obj_size 1017 1018 if size > 0: 1019 self.count = size / self.target_size
1020 1021 @utils.safe_property
1022 - def obj_size(self):
1023 """The size of the entire array.""" 1024 return self.target_size * self.count
1025
1026 - def __iter__(self):
1027 # If the array is invalid we do not iterate. 1028 if not self.obj_vm.is_valid_address(self.obj_offset): 1029 return 1030 1031 for position in utils.xrange(0, self.count): 1032 # Since we often calculate array counts it is possible to 1033 # calculate huge arrays. This will then spin here 1034 # uncontrollably. We use max_count as a safety to break out 1035 # early - but we need to ensure that users see we hit this 1036 # artificial limit. 1037 if position > self.max_count: 1038 if self.obj_session.GetParameter("debug"): 1039 pdb.set_trace() 1040 1041 self.obj_session.logging.warn( 1042 "%s Array iteration truncated by max_count!", self.obj_name) 1043 break 1044 1045 # We don't want to stop on a NoneObject. Its 1046 # entirely possible that this array contains a bunch of 1047 # pointers and some of them may not be valid (or paged 1048 # in). This should not stop us though we just return the 1049 # invalid pointers to our callers. It's up to the callers 1050 # to do what they want with the array. 1051 yield self[position]
1052
1053 - def __repr__(self):
1054 return "<{3} {0} x {1} @ 0x{2:08X}>".format( 1055 self.count, self.target, self.obj_offset, self.__class__.__name__)
1056
1057 - def __unicode__(self):
1058 result = [repr(self)] 1059 for i, x in enumerate(self): 1060 result.append(u"0x%04X %r" % (i, x)) 1061 1062 if len(result) > 10: 1063 result.append(u"... More entries hidden") 1064 break 1065 1066 return u"\n".join(result)
1067
1068 - def __eq__(self, other):
1069 if not other or self.count != len(other): 1070 return False 1071 1072 for i in xrange(self.count): 1073 if not self[i] == other[i]: 1074 return False 1075 1076 return True
1077
1078 - def __getitem__(self, pos):
1079 # Check for slice object 1080 if isinstance(pos, slice): 1081 start, stop, step = pos.indices(self.count) 1082 return [self[i] for i in xrange(start, stop, step)] 1083 1084 pos = int(pos) 1085 offset = self.target_size * pos + self.obj_offset 1086 context = dict(index=pos) 1087 context.update(self.obj_context) 1088 1089 return self.obj_profile.Object( 1090 self.target, offset=offset, vm=self.obj_vm, 1091 parent=self, profile=self.obj_profile, 1092 name="{0}[{1}] ".format(self.obj_name, pos), 1093 context=context, **self.target_args)
1094
1095 - def __setitem__(self, item, value):
1096 if isinstance(item, int): 1097 self[item].write(value) 1098 else: 1099 super(Array, self).__setitem__(item, value)
1100
1101 - def __len__(self):
1102 return self.count
1103
1104 1105 -class PointerArray(Array):
1106 """This is an optimized Array implementation for arrays of Pointers. 1107 1108 The idea is to decode all pointers at once. 1109 """ 1110
1111 - def __init__(self, **kwargs):
1112 super(PointerArray, self).__init__(target="Pointer", **kwargs) 1113 1114 if self.target_size == 8: 1115 self.format_string = "<" + "Q" * self.count 1116 else: 1117 self.format_string = "<" + "I" * self.count 1118 1119 # Read all the data 1120 data = self.obj_vm.read(self.obj_offset, self.target_size * self.count) 1121 self._data = struct.unpack(self.format_string, data)
1122
1123 - def __iter__(self):
1124 for i in xrange(len(self._data)): 1125 yield self[i]
1126
1127 - def __getitem__(self, pos):
1128 return self.obj_profile.Pointer(value=self._data[pos], vm=self.obj_vm)
1129
1130 1131 -class ListArray(Array):
1132 """An array of structs which do not all have the same size.""" 1133
1134 - def __init__(self, maximum_size=None, maximum_offset=None, **kwargs):
1135 """Constructor. 1136 1137 This array may be initialized using one of the following parameters: 1138 1139 maximum_size: The maximum size of the array in bytes. 1140 maximum_offset: If we reach this offset iteration is terminated. 1141 count: The total count of items in this list. 1142 1143 max_count: The maximum size of the array. This is a safety mechanism if 1144 count is calculated. max_count should be set to an upper bound on the 1145 size of the array. 1146 """ 1147 super(ListArray, self).__init__(**kwargs) 1148 if callable(maximum_size): 1149 maximum_size = int(maximum_size(self.obj_parent)) 1150 1151 if callable(maximum_offset): 1152 maximum_offset = int(maximum_offset(self.obj_parent)) 1153 1154 # Check the values for sanity. 1155 if self.count == 0 and maximum_size is None and maximum_offset is None: 1156 raise TypeError( 1157 "One of count, maximum_offset, maximum_size must be specified.") 1158 1159 if maximum_size is not None: 1160 maximum_offset = self.obj_offset + maximum_size 1161 1162 self.maximum_offset = maximum_offset
1163
1164 - def __len__(self):
1165 """It is generally too expensive to rely on the count of this array.""" 1166 raise NotImplementedError
1167 1168 @utils.safe_property
1169 - def obj_size(self):
1170 """It is generally too expensive to rely on the size of this array.""" 1171 raise NotImplementedError
1172
1173 - def __iter__(self):
1174 offset = self.obj_offset 1175 count = 0 1176 while 1: 1177 # Exit conditions. 1178 if self.maximum_offset and offset > self.maximum_offset: 1179 break 1180 1181 if self.count and count >= self.count: 1182 break 1183 1184 if count >= self.max_count: 1185 self.obj_session.logging.warn( 1186 "%s ListArray iteration truncated by max_count!", 1187 self.obj_name) 1188 break 1189 1190 item = self.obj_profile.Object( 1191 self.target, offset=offset, vm=self.obj_vm, parent=self, 1192 profile=self.obj_profile, context=self.obj_context, 1193 name="{0}[{1}] ".format(self.obj_name, count), 1194 **self.target_args) 1195 1196 item_size = item.obj_size 1197 if item_size <= 0: 1198 break 1199 1200 offset += item_size 1201 count += 1 1202 1203 yield item
1204
1205 - def __getitem__(self, pos):
1206 for index, item in enumerate(self): 1207 if index == int(pos): 1208 return item 1209 1210 return NoneObject("Pos seems to be outside the array maximum_size.")
1211
1212 1213 -class BaseAddressComparisonMixIn(object):
1214 """A mixin providing comparison operators for its base offset.""" 1215
1216 - def __comparator__(self, other, method):
1217 # 64 bit addresses are always sign extended so we need to clear the top 1218 # bits. 1219 try: 1220 other_address = Pointer.integer_to_address(other.__int__()) 1221 except AttributeError: 1222 other_address = None 1223 1224 return method(Pointer.integer_to_address(self.__int__()), other_address)
1225
1226 - def __eq__(self, other):
1227 return self.__comparator__(other, operator.__eq__)
1228
1229 - def __lt__(self, other):
1230 return self.__comparator__(other, operator.__lt__)
1231
1232 - def __gt__(self, other):
1233 return self.__comparator__(other, operator.__gt__)
1234
1235 - def __le__(self, other):
1236 return self.__comparator__(other, operator.__le__)
1237
1238 - def __ge__(self, other):
1239 return self.__comparator__(other, operator.__ge__)
1240
1241 - def __ne__(self, other):
1242 return self.__comparator__(other, operator.__ne__)
1243
1244 1245 -class Struct(BaseAddressComparisonMixIn, BaseObject):
1246 """ A Struct is an object which represents a c struct 1247 1248 Structs have members at various fixed relative offsets from our own base 1249 offset. 1250 """ 1251
1252 - def __init__(self, members=None, struct_size=0, callable_members=None, 1253 **kwargs):
1254 """ This must be instantiated with a dict of members. The keys 1255 are the offsets, the values are Curried Object classes that 1256 will be instantiated when accessed. 1257 1258 Args: 1259 members: A dict of callables to use for retrieving each member. (Key 1260 is member name, value is a callable). Normally these are populated 1261 by the profile system 1262 1263 struct_size: The size of this struct if known (Can be None). 1264 """ 1265 super(Struct, self).__init__(**kwargs) 1266 ACCESS_LOG.LogFieldAccess(self.obj_profile.name, self.obj_type, None) 1267 1268 self.members = members or {} 1269 self.callable_members = callable_members or {} 1270 self.struct_size = struct_size 1271 self._cache = {}
1272
1273 - def __hash__(self):
1274 return hash(self.indices)
1275 1276 @utils.safe_property
1277 - def indices(self):
1278 return ("%s(%#x, vm=%s@%s)" % ( 1279 self.obj_type, 1280 self.obj_offset, 1281 self.obj_vm.vtop(self.obj_offset), 1282 self.obj_vm.base, 1283 ),)
1284
1285 - def __long__(self):
1286 return self.obj_offset
1287
1288 - def __int__(self):
1289 """Return our offset as an integer. 1290 1291 This allows us to interchange Struct and offsets. 1292 """ 1293 return self.obj_offset
1294
1295 - def preamble_size(self):
1296 """The number of bytes before the object which are part of the object. 1297 1298 Some objects are preceeded with data before obj_offset which is still 1299 considered part of the object. Note that in that case the size of the 1300 object includes the preamble_size - hence 1301 1302 object_end = obj_offset + obj_size - obj.preamble_size() 1303 """ 1304 return 0
1305 1306 @utils.safe_property
1307 - def obj_size(self):
1308 if callable(self.struct_size): 1309 # We must always return an integer, even if NoneObject is returned 1310 # from the callable. 1311 return self.struct_size(self) or 0 1312 1313 return self.struct_size
1314
1315 - def __repr__(self):
1316 if self.obj_offset == 0 and self.obj_name == "Prototype": 1317 return "%s Prototype" % self.__class__.__name__ 1318 1319 return "[{0} {1}] @ 0x{2:08X}".format( 1320 self.obj_type, self.obj_name or '', self.obj_offset)
1321
1322 - def __unicode__(self):
1323 result = self.__repr__() + "\n" 1324 width_name = 0 1325 1326 fields = [] 1327 # Print all the fields sorted by offset within the struct. 1328 for k in set(self.members).union(self.callable_members): 1329 width_name = max(width_name, len(k)) 1330 obj = getattr(self, k) 1331 if obj == None: 1332 obj = self.m(k) 1333 1334 fields.append( 1335 (getattr(obj, "obj_offset", self.obj_offset) - 1336 self.obj_offset, k, utils.SmartUnicode(repr(obj)))) 1337 1338 fields.sort() 1339 1340 return result + u"\n".join( 1341 [u" 0x%02X %s%s %s" % (offset, k, " " * (width_name - len(k)), v) 1342 for offset, k, v in fields]) + "\n"
1343
1344 - def v(self, vm=None):
1345 """ When a struct is evaluated we just return our offset. 1346 """ 1347 return self.obj_offset
1348
1349 - def m(self, attr, allow_callable_attributes=False):
1350 """Fetch the member named by attr. 1351 1352 NOTE: When the member does not exist in this struct, we return a 1353 NoneObject instance. This allows one to write code such as: 1354 1355 struct.m("Field1") or struct.m("Field2") struct.m("Field2") 1356 1357 To access a field which has been renamed in different OS versions. 1358 1359 By default this method does not allow callable methods specified in 1360 overlays. This is to enable overriding of normal struct members by 1361 callable properties (otherwise infinite recursion might occur). If you 1362 really want to call overlays, specify allow_callable_attributes as True. 1363 """ 1364 # Enable to log struct access. 1365 # ACCESS_LOG.LogFieldAccess(self.obj_profile.name, self.obj_type, attr) 1366 result = self._cache.get(attr) 1367 if result is not None: 1368 return result 1369 1370 # Allow subfields to be gotten via this function. 1371 if "." in attr: 1372 result = self 1373 for sub_attr in attr.split("."): 1374 if allow_callable_attributes: 1375 result = getattr(result, sub_attr, None) 1376 if result is None: 1377 result = NoneObject("Attribute %s not found in %s", 1378 sub_attr, self.obj_type) 1379 else: 1380 result = result.m(sub_attr) 1381 self._cache[attr] = result 1382 return result 1383 1384 element = self.members.get(attr) 1385 if element is not None: 1386 # Allow the element to be a callable rather than a list - this is 1387 # useful for aliasing member names 1388 if callable(element): 1389 return element(self) 1390 1391 offset, cls = element 1392 else: 1393 return NoneObject(u"Struct {0} has no member {1}", 1394 self.obj_name, attr) 1395 1396 if callable(offset): 1397 # If offset is specified as a callable its an absolute 1398 # offset 1399 offset = int(offset(self)) 1400 else: 1401 # Otherwise its relative to the start of our struct 1402 offset = int(offset) + int(self.obj_offset) 1403 1404 try: 1405 result = cls(offset=offset, vm=self.obj_vm, parent=self, name=attr, 1406 profile=self.obj_profile, context=self.obj_context) 1407 except Error as e: 1408 result = NoneObject(str(e)) 1409 1410 self._cache[attr] = result 1411 return result
1412
1413 - def multi_m(self, *args, **opts):
1414 """Retrieve a set of fields in order. 1415 1416 If a field is not found, then try the next field in the list until one 1417 field works. This approach allows us to propose a set of possible fields 1418 for an attribute to support renaming of struct fields in different 1419 versions. 1420 """ 1421 allow_callable_attributes = opts.pop("allow_callable_attributes", True) 1422 for field in args: 1423 result = self.m( 1424 field, allow_callable_attributes=allow_callable_attributes) 1425 if result != None: 1426 return result 1427 1428 return NoneObject("No fields were found.")
1429
1430 - def __getattr__(self, attr):
1431 result = self.m(attr) 1432 if result == None: 1433 raise AttributeError(attr) 1434 1435 return result
1436
1437 - def SetMember(self, attr, value):
1438 """Write a value to a member.""" 1439 member = self.m(attr) 1440 # Try to make the member write the new value. 1441 member.write(value) 1442 if not hasattr(member, 'write') or not member.write(value): 1443 raise ValueError("Error writing value to member " + attr)
1444
1445 - def walk_list(self, list_member, include_current=True, deref_as=None):
1446 """Walk a single linked list in this struct. 1447 1448 The current object can be optionally yielded as the first element. 1449 1450 Args: 1451 list_member: The member name which points to the next item in the 1452 list. 1453 """ 1454 if include_current: 1455 yield self 1456 1457 seen = set() 1458 seen.add(self.obj_offset) 1459 1460 item = self 1461 while True: 1462 if deref_as: 1463 item = getattr(item, list_member).dereference_as(deref_as) 1464 else: 1465 item = getattr(item, list_member).deref() 1466 1467 # Sometimes in usermode page 0 is mapped, hence bool(item) == True 1468 # even if item == 0 since bool(item) refers to the pointer's 1469 # validity. 1470 if not item or item == 0 or item.obj_offset in seen: 1471 break 1472 1473 seen.add(item.obj_offset) 1474 yield item
1475
1476 # Profiles are the interface for creating/interpreting 1477 # objects 1478 1479 -class ProfileSectionLoader(object):
1480 """A loader for a section in the profile JSON file. 1481 1482 The profile json serialization contains a number of sections, each has a 1483 well known name (e.g. $CONSTANTS, $FUNCTIONS, $STRUCT). When a profile class 1484 is initialized, it uses a variety of loaders to handle each section in the 1485 profile. This allows more complex sections to be introduced and extended. 1486 """ 1487 __metaclass__ = registry.MetaclassRegistry 1488 __abstract = True 1489 order = 100 1490
1491 - def LoadIntoProfile(self, session, profile, data):
1492 """Loads the data into the profile.""" 1493 _ = session, data 1494 return profile
1495
1496 1497 # Some standard profile Loaders. 1498 -class MetadataProfileSectionLoader(ProfileSectionLoader):
1499 name = "$METADATA" 1500 order = 1 1501
1502 - def LoadIntoProfile(self, session, profile, metadata):
1503 if profile is not None: 1504 return profile 1505 1506 profile_type = metadata.get("Type", "Profile") 1507 1508 # Support a symlink profile - this is a profile which is a short, 1509 # human meaningful name for another profile. 1510 if profile_type == "Symlink": 1511 return session.LoadProfile(metadata.get("Target")) 1512 1513 possible_implementations = [metadata.get("ProfileClass", profile_type)] 1514 1515 # For windows profile we can use a generic PE profile 1516 # implementation. 1517 if "GUID_AGE" in metadata: 1518 possible_implementations.append("BasicPEProfile") 1519 1520 if "PDBFile" in metadata: 1521 possible_class_name = metadata["PDBFile"].capitalize().split(".")[0] 1522 possible_implementations.insert(0, possible_class_name) 1523 1524 for impl in possible_implementations: 1525 profile_cls = Profile.ImplementationByClass(impl) 1526 if profile_cls: 1527 break 1528 1529 if profile_cls is None: 1530 session.logging.warn("No profile implementation class %s" % 1531 metadata["ProfileClass"]) 1532 1533 raise ProfileError( 1534 "No profile implementation class %s" % 1535 metadata["ProfileClass"]) 1536 1537 result = profile_cls(session=session, metadata=metadata) 1538 1539 return result
1540
1541 1542 -class ConstantProfileSectionLoader(ProfileSectionLoader):
1543 name = "$CONSTANTS" 1544
1545 - def LoadIntoProfile(self, session, profile, constants):
1546 profile.add_constants(constants_are_addresses=True, constants=constants) 1547 return profile
1548
1549 1550 -class ConstantTypeProfileSectionLoader(ProfileSectionLoader):
1551 name = "$CONSTANT_TYPES" 1552
1553 - def LoadIntoProfile(self, session, profile, constant_types):
1554 profile.constant_types.update(constant_types) 1555 return profile
1556
1557 1558 -class FunctionsProfileSectionLoader(ConstantProfileSectionLoader):
1559 name = "$FUNCTIONS"
1560
1561 1562 -class EnumProfileSectionLoader(ProfileSectionLoader):
1563 name = "$ENUMS" 1564
1565 - def LoadIntoProfile(self, session, profile, enums):
1566 profile.add_enums(**enums) 1567 return profile
1568
1569 1570 -class ReverseEnumProfileSectionLoader(ProfileSectionLoader):
1571 name = "$REVENUMS" 1572
1573 - def LoadIntoProfile(self, session, profile, reverse_enums):
1574 profile.add_reverse_enums(**reverse_enums) 1575 return profile
1576
1577 1578 -class StructProfileLoader(ProfileSectionLoader):
1579 name = "$STRUCTS" 1580
1581 - def LoadIntoProfile(self, session, profile, types):
1582 profile.add_types(types) 1583 return profile
1584
1585 1586 -class MergeProfileLoader(ProfileSectionLoader):
1587 """This section specifies a list of profiles to be merged into this one.""" 1588 name = "$MERGE" 1589
1590 - def LoadIntoProfile(self, session, profile, merge_list):
1591 for merge_target in merge_list: 1592 merge_profile = session.LoadProfile(merge_target) 1593 if merge_profile.data: 1594 profile.LoadProfileFromData( 1595 merge_profile.data, session=session, profile=profile) 1596 1597 return profile
1598
1599 -class DummyAS(object):
1600 name = 'dummy' 1601 volatile = False 1602
1603 - def __init__(self, session):
1604 self.session = session
1605
1606 - def is_valid_address(self, _offset):
1607 return True
1608
1609 - def read(self, _, length):
1610 return "\x00" * length
1611
1612 1613 -class Profile(object):
1614 """A collection of types relating to a single compilation unit. 1615 1616 Profiles are usually not instantiated directly. Rather, the profiles are 1617 loaded from the profile repository using the session.LoadProfile() method. 1618 """ 1619 # This is the list of overlays to be applied to the vtypes when compiling 1620 # into types. 1621 overlays = None 1622 1623 # These are the vtypes - they are just a dictionary describing the types 1624 # using the "vtype" language. This dictionary will be compiled into 1625 # executable code and placed into self.types. 1626 vtypes = None 1627 1628 # This hold the executable code compiled from the vtypes above. 1629 types = None 1630 1631 # This is the base class for all profiles. 1632 __metaclass__ = registry.MetaclassRegistry 1633 1634 # This is a dict of constants 1635 constants = None 1636 1637 # This is a record of all the modification classes that were applied to this 1638 # profile. 1639 applied_modifications = None 1640 1641 # An empty type descriptor. 1642 EMPTY_DESCRIPTOR = [0, {}] 1643 1644 # The metadata for this profile. This should be specified by derived 1645 # classes. It is OK To put a (mutable) dict in here. It will not be 1646 # directly modified by anything. 1647 METADATA = {} 1648 1649 # The constructor will build this dict of metadata by copying the values 1650 # from METADATA here. 1651 _metadata = None 1652 1653 @classmethod
1654 - def LoadProfileFromData(cls, data, session=None, name=None, profile=None):
1655 """Creates a profile directly from a JSON object. 1656 1657 Args: 1658 data: A data structure of an encoded profile. Described: 1659 http://www.rekall-forensic.com/docs/development.html#_profile_serializations 1660 session: A Session object. 1661 name: The name of the profile. 1662 profile: An optional initial profile to apply the new sections to. If 1663 None we create a new profile instance according to the $METADATA 1664 section. 1665 1666 Returns: 1667 a Profile() instance. 1668 1669 Raises: 1670 IOError if we can not load the profile. 1671 1672 """ 1673 if "$METADATA" not in data: 1674 data["$METADATA"] = {} 1675 1676 # Data is a dict with sections as keys. 1677 handlers = [] 1678 for section in data: 1679 try: 1680 handlers.append( 1681 ProfileSectionLoader.classes_by_name[section][0]) 1682 except KeyError: 1683 # This is not fatal in order to allow new sections to be safely 1684 # introduced to older binaries. 1685 session.logging.warn( 1686 "Unable to parse profile section %s", section) 1687 1688 # Sort the handlers in order: 1689 handlers.sort(key=lambda x: x.order) 1690 1691 # Delegate profile creation to the loaders. 1692 for handler in handlers: 1693 profile = handler().LoadIntoProfile( 1694 session, profile, data[handler.name]) 1695 1696 if profile and name: 1697 profile.name = name 1698 profile.data = data 1699 1700 return profile
1701 1702 # The common classes that are provided by the object framework. Plugins can 1703 # extend the framework by registering additional classes here - these 1704 # classes will be available everywhere profiles are used. 1705 COMMON_CLASSES = {'BitField': BitField, 1706 'Pointer': Pointer, 1707 'Pointer32': Pointer32, 1708 'Void': Void, 1709 'void': Void, 1710 'Array': Array, 1711 'PointerArray': PointerArray, 1712 'ListArray': ListArray, 1713 'NativeType': NativeType, 1714 'Struct': Struct} 1715 1716 @classmethod
1717 - def Initialize(cls, profile):
1718 """Install required types, classes and constants. 1719 1720 This method should be extended by derived classes. It is a class method 1721 to allow other profiles to call this method and install the various 1722 components into their own profiles. 1723 """ 1724 # Basic types used in all profiles. 1725 profile.add_classes(cls.COMMON_CLASSES) 1726 1727 profile._initialized = True # pylint: disable=protected-access
1728
1729 - def __init__(self, name=None, session=None, metadata=None, **kwargs):
1730 if kwargs: 1731 session.logging.error("Unknown keyword args %s", kwargs) 1732 1733 if name is None: 1734 name = self.__class__.__name__ 1735 1736 self._metadata = self.METADATA.copy() 1737 for basecls in reversed(self.__class__.__mro__): 1738 self._metadata.update(getattr(basecls, "METADATA", {})) 1739 1740 self._metadata.update(metadata or {}) 1741 1742 self.name = unicode(name) 1743 self.session = session 1744 if session is None: 1745 raise RuntimeError("Session must be specified.") 1746 1747 self.overlays = [] 1748 self.vtypes = {} 1749 self.constants = {} 1750 1751 # A map from symbol names to the types at that symbol. Key: Symbol name, 1752 # Value: (target, target_args). 1753 self.constant_types = {} 1754 self.constant_addresses = utils.SortedCollection(key=lambda x: x[0]) 1755 self.enums = {} 1756 self.reverse_enums = {} 1757 self.applied_modifications = set() 1758 self.object_classes = {} 1759 1760 # The original JSON data this profile is loaded from. 1761 self.data = None 1762 1763 # Keep track of all the known types so we can command line complete. 1764 self.known_types = set() 1765 1766 # This is the local cache of compiled expressions. 1767 self.flush_cache() 1768 1769 # Call Initialize on demand. 1770 self._initialized = False
1771
1772 - def EnsureInitialized(self):
1773 if not self._initialized: 1774 self.Initialize(self)
1775
1776 - def flush_cache(self):
1777 self.types = {}
1778
1779 - def copy(self):
1780 """Makes a copy of this profile.""" 1781 self.EnsureInitialized() 1782 1783 # pylint: disable=protected-access 1784 result = self.__class__(name=self.name, session=self.session) 1785 result.vtypes = self.vtypes.copy() 1786 result.overlays = self.overlays[:] 1787 result.enums = self.enums.copy() 1788 result.reverse_enums = self.reverse_enums.copy() 1789 result.constants = self.constants.copy() 1790 result.constant_types = self.constant_types.copy() 1791 result.constant_addresses = self.constant_addresses.copy() 1792 1793 # Object classes are shallow dicts. 1794 result.object_classes = self.object_classes.copy() 1795 result._initialized = self._initialized 1796 result.known_types = self.known_types.copy() 1797 result._metadata = self._metadata.copy() 1798 # pylint: enable=protected-access 1799 1800 return result
1801
1802 - def merge(self, other):
1803 """Merges another profile into this one. 1804 1805 The result is that we are able to parse all the types that the other 1806 profile has. 1807 """ 1808 other.EnsureInitialized() 1809 1810 self.vtypes.update(other.vtypes) 1811 self.overlays += other.overlays 1812 self.constants.update(other.constants) 1813 self.object_classes.update(other.object_classes) 1814 self.flush_cache() 1815 self.enums.update(other.enums) 1816 self.name = u"%s + %s" % (self.name, other.name) 1817 1818 # Merge in the other's profile metadata which is not in this profile. 1819 metadata = other._metadata.copy() # pylint: disable=protected-access 1820 metadata.update(self._metadata) 1821 self._metadata = metadata
1822
1823 - def merge_symbols(self, other, *args):
1824 for arg in args: 1825 if arg in other.vtypes: 1826 self.vtypes[arg] = other.vtypes[arg] 1827 1828 if arg in other.overlays: 1829 self.overlays[arg] = other.overlays[arg] 1830 1831 if arg in other.object_classes: 1832 self.object_classes[arg] = other.object_classes[arg] 1833 1834 self.flush_cache()
1835
1836 - def metadata(self, name, default=None):
1837 """Obtain metadata about this profile.""" 1838 self.EnsureInitialized() 1839 return self._metadata.get(name, default)
1840
1841 - def set_metadata(self, name, value):
1842 self._metadata[name] = value
1843
1844 - def metadatas(self, *args):
1845 """Obtain metadata about this profile.""" 1846 self.EnsureInitialized() 1847 return tuple([self._metadata.get(x) for x in args])
1848
1849 - def has_type(self, type_name):
1850 # Make sure we are initialized on demand. 1851 self.EnsureInitialized() 1852 return type_name in self.vtypes
1853
1854 - def has_class(self, class_name):
1855 # Make sure we are initialized on demand. 1856 self.EnsureInitialized() 1857 return class_name in self.object_classes
1858
1859 - def add_classes(self, classes_dict=None, **kwargs):
1860 """Add the classes in the dict to our object classes mapping.""" 1861 self.flush_cache() 1862 1863 if classes_dict: 1864 self.object_classes.update(classes_dict) 1865 1866 self.object_classes.update(kwargs) 1867 self.known_types.update(kwargs)
1868
1869 - def add_constant_type(self, constant, target, target_args):
1870 self.flush_cache() 1871 self.constant_types[constant] = (target, target_args)
1872
1873 - def add_constants(self, constants=None, constants_are_addresses=False, **_):
1874 """Add the kwargs as constants for this profile.""" 1875 self.flush_cache() 1876 1877 for k, v in constants.iteritems(): 1878 k = intern(str(k)) 1879 self.constants[k] = v 1880 if constants_are_addresses: 1881 try: 1882 # We need to interpret the value as a pointer. 1883 address = Pointer.integer_to_address(v) 1884 existing_value = self.constant_addresses.get(address) 1885 if existing_value is None: 1886 self.constant_addresses[address] = k 1887 elif isinstance(existing_value, list): 1888 if k not in existing_value: 1889 existing_value.append(k) 1890 elif existing_value != k: 1891 self.constant_addresses[address] = [ 1892 existing_value, k] 1893 except ValueError: 1894 pass
1895
1896 - def add_reverse_enums(self, **kwargs):
1897 """Add the kwargs as a reverse enum for this profile.""" 1898 for k, v in kwargs.iteritems(): 1899 self.reverse_enums[intern(str(k))] = intern(str(v))
1900
1901 - def add_enums(self, **kwargs):
1902 """Add the kwargs as an enum for this profile.""" 1903 # Alas JSON converts integer keys to strings. 1904 for k, v in kwargs.iteritems(): 1905 self.enums[intern(str(k))] = enum_definition = {} 1906 for enum, name in v.items(): 1907 enum_definition[intern(str(enum))] = name
1908
1909 - def add_types(self, abstract_types):
1910 self.flush_cache() 1911 1912 abstract_types = utils.InternObject(abstract_types) 1913 self.known_types.update(abstract_types) 1914 1915 # we merge the abstract_types with self.vtypes and then recompile 1916 # the whole thing again. This is essential because 1917 # definitions may have changed as a result of this call, and 1918 # we store curried objects (which might keep their previous 1919 # definitions). 1920 for k, v in abstract_types.items(): 1921 if isinstance(v, list): 1922 self.vtypes[k] = v 1923 1924 else: 1925 original = self.vtypes.get(k, self.EMPTY_DESCRIPTOR) 1926 original[1].update(v[1]) 1927 if v[0]: 1928 original[0] = v[0] 1929 1930 self.vtypes[k] = original
1931
1932 - def compile_type(self, type_name):
1933 """Compile the specific type and ensure it exists in the type cache. 1934 1935 The type_name here is a reference to the vtypes which are loaded into 1936 the profile. 1937 """ 1938 # Make sure we are initialized on demand. 1939 self.EnsureInitialized() 1940 if type_name in self.types: 1941 return 1942 1943 original_type_descriptor = type_descriptor = copy.deepcopy( 1944 self.vtypes.get(type_name, self.EMPTY_DESCRIPTOR)) 1945 1946 for overlay in self.overlays: 1947 type_overlay = copy.deepcopy(overlay.get(type_name)) 1948 type_descriptor = self._apply_type_overlay( 1949 type_descriptor, type_overlay) 1950 1951 # An overlay which specifies a string as a definition is simply an alias 1952 # for another struct. We just copy the old struct in place of the 1953 # aliased one. 1954 if isinstance(type_descriptor, str): 1955 self.compile_type(type_descriptor) 1956 self.types[type_name] = self.types[type_descriptor] 1957 type_descriptor = self.vtypes[type_descriptor] 1958 1959 if type_descriptor == self.EMPTY_DESCRIPTOR: 1960 # Mark that this is a pure object - not described by a 1961 # vtype. E.g. it is purely a class. 1962 self.types[type_name] = None 1963 1964 else: 1965 # Now type_overlay will have all the overlays applied on it. 1966 members = {} 1967 callable_members = {} 1968 1969 size, field_description = type_descriptor 1970 1971 for k, v in field_description.items(): 1972 k = str(k) 1973 1974 # If the overlay specifies a callable, we place it in the 1975 # callable_members dict, and revert back to the vtype 1976 # definition. 1977 if callable(v): 1978 callable_members[intern(k)] = v 1979 1980 # If the callable is masking an existing field, revert back 1981 # to it. 1982 original_v = original_type_descriptor[1].get(k) 1983 if original_v: 1984 members[intern(k)] = ( 1985 original_v[0], self.list_to_type(k, original_v[1])) 1986 1987 elif v[0] == None: 1988 self.session.logging.warning( 1989 "%s has no offset in object %s. Check that vtypes " 1990 "has a concrete definition for it.", 1991 k, type_name) 1992 else: 1993 members[intern(str(k))] = ( 1994 v[0], self.list_to_type(k, v[1])) 1995 1996 # Allow the class plugins to override the class constructor here 1997 cls = self.object_classes.get(type_name, Struct) 1998 1999 self.types[intern(str(type_name))] = self._make_struct_callable( 2000 cls, type_name, members, size, callable_members)
2001
2002 - def _make_struct_callable(self, cls, type_name, members, size, 2003 callable_members):
2004 """Compile the structs class into a callable. 2005 2006 For write support we would like to add a __setattr__ on the struct 2007 classes. However, in python, once there is a __setattr__ method 2008 defined on an object, _EVERY_ setattr() operation on the object will 2009 go through this method - this is a performance killer. Since in this 2010 case we only want to trap accesses to the member fields, we can create 2011 a class with @property methods to get and set the attributes. We then 2012 overlay this class over the original struct class. 2013 2014 The resulting callable will instantiate the derived class (with the 2015 additional properties) over the provided offset. 2016 2017 Note that due to the JIT compiler, this method is called only once when 2018 each Struct object is first accessed, so it is not really 2019 expensive. OTOH the callables we produce here are called many many times 2020 (Each time the object is instantiated, or a field is accessed) and need 2021 to be as fast as possible. 2022 """ 2023 # Note that lambdas below must get external parameters through default 2024 # args: 2025 # http://stackoverflow.com/questions/938429/scope-of-python-lambda-functions-and-their-parameters/938493#938493 2026 2027 properties = dict(callable_members=callable_members.keys()) 2028 for name in set(members).union(callable_members): 2029 2030 # Do not mask hand written methods with autogenerated properties. 2031 if hasattr(cls, name): 2032 continue 2033 2034 cb = callable_members.get(name) 2035 value = members.get(name) 2036 2037 getter = None 2038 setter = None 2039 2040 # This happens if the overlay specifies a callable. In that case we 2041 # install a special property which calls it. However, the callable 2042 # may still refer to self.m() to get the bare vtype item. 2043 if cb: 2044 # Only implement getter for callable fields since setters do 2045 # not really make sense. 2046 def CbGetter(self, cb=cb): 2047 try: 2048 return cb(self) 2049 except Exception as e: 2050 return NoneObject("Failed to run callback %s" % e)
2051 2052 getter = CbGetter 2053 2054 elif value: 2055 # Specify both getters and setter for the field. 2056 getter = lambda self, name=name: self.m(name) 2057 setter = lambda self, v=value, n=name: self.SetMember(n, v) 2058 2059 properties[name] = utils.safe_property(getter, setter, None, name) 2060 2061 # Extend the provided class by attaching the properties to it. We can 2062 # not just monkeypatch here because cls will be shared between all 2063 # structs which do not define an explicit extension class. By creating a 2064 # new temporary class this uses the usual inheritance behaviour to 2065 # override the methods in cls depending on the members dict, without 2066 # altering the cls class permanently (This is a kind of metaclass 2067 # programming). 2068 derived_cls = type(str(type_name), (cls,), properties) 2069 2070 return Curry(derived_cls, 2071 type_name=type_name, members=members, 2072 callable_members=callable_members, struct_size=size)
2073
2074 - def legacy_field_descriptor(self, typeList):
2075 """Converts the list expression into a target, target_args notation. 2076 2077 Legacy vtypes use lists to specify the objects. This function is used to 2078 convert from the legacy format to the more accurate modern 2079 format. Hopefully the legacy format can be deprecated at some point. 2080 2081 Args: 2082 typeList: A list of types. e.g. ['pointer64', ['_HMAP_TABLE']] 2083 2084 Returns: 2085 A target, target_args tuple. Target is the class name which should be 2086 instantiated, while target_args is a dict of args to be passed to 2087 this class. 2088 e.g. 'Pointer', {target="_HMAP_TABLE"} 2089 """ 2090 # This is of the form [ '_HMAP_TABLE' ] - First element is the target 2091 # name, with no args. 2092 if len(typeList) == 1: 2093 target = typeList[0] 2094 target_args = {} 2095 2096 # This is of the form [ 'pointer' , [ 'foobar' ]] 2097 # Target is the first item, args is the second item. 2098 elif typeList[0] == 'pointer' or typeList[0] == 'pointer64': 2099 target = "Pointer" 2100 target_args = self.legacy_field_descriptor(typeList[1]) 2101 2102 # This is an array: [ 'array', count, ['foobar'] ] 2103 elif typeList[0] == 'array': 2104 target = "Array" 2105 target_args = self.legacy_field_descriptor(typeList[2]) 2106 target_args['count'] = typeList[1] 2107 2108 elif len(typeList) > 2: 2109 self.session.logging.error("Invalid typeList %s" % (typeList,)) 2110 2111 else: 2112 target = typeList[0] 2113 target_args = typeList[1] 2114 2115 return dict(target=target, target_args=target_args)
2116
2117 - def list_to_type(self, name, typeList):
2118 """Parses a specification list and returns a VType object. 2119 2120 This function is a bit complex because we support lots of 2121 different list types for backwards compatibility. 2122 2123 This is the core function which effectively parses the VType language. 2124 """ 2125 # Convert legacy typeList expressions to the modern format. 2126 target_spec = self.legacy_field_descriptor(typeList) 2127 2128 # The modern format uses a target, target_args notation. 2129 target = target_spec['target'] 2130 target_args = target_spec['target_args'] 2131 2132 # This is currently the recommended way to specify a type: 2133 # e.g. [ 'Pointer', {target="int"}] 2134 if isinstance(target_args, dict): 2135 return Curry(self.Object, type_name=target, name=name, 2136 **target_args) 2137 2138 # This is of the deprecated form ['class_name', ['arg1', 'arg2']]. 2139 # Since the object framework moved to purely keyword args these are 2140 # meaningless. Issue a deprecation warning. 2141 elif isinstance(target_args, list): 2142 self.session.logging.warning( 2143 "Deprecated vtype expression %s for member %s, assuming int", 2144 typeList, name) 2145 2146 else: 2147 # If we get here we have no idea what this list is 2148 self.session.logging.warning( 2149 "Unable to find a type for %s, assuming int", typeList) 2150 2151 return Curry(self.Object, type_name='int', name=name)
2152
2153 - def GetPrototype(self, type_name):
2154 """Return a prototype of objects of type 'type_name'. 2155 2156 A prototype is a dummy object that looks like a type, but uses data 2157 from the profile to provide a list of members and type information. 2158 """ 2159 self.compile_type(type_name) 2160 return self.Object(type_name=type_name, name="Prototype", 2161 vm=DummyAS(self.session))
2162
2163 - def get_obj_offset(self, name, member):
2164 """ Returns a member's offset within the struct. 2165 2166 Note that this can be wrong if the offset is a callable. 2167 """ 2168 ACCESS_LOG.LogFieldAccess(self.name, name, member) 2169 2170 tmp = self.GetPrototype(name) 2171 return tmp.members.get(member, NoneObject("No member"))[0]
2172
2173 - def get_obj_size(self, name):
2174 """Returns the size of a struct""" 2175 tmp = self.GetPrototype(name) 2176 return tmp.obj_size
2177
2178 - def obj_has_member(self, name, member):
2179 """Returns whether an object has a certain member""" 2180 ACCESS_LOG.LogFieldAccess(self.name, name, member) 2181 2182 tmp = self.GetPrototype(name) 2183 return hasattr(tmp, member)
2184
2185 - def add_overlay(self, overlay):
2186 """Add an overlay to the current overlay stack.""" 2187 self.flush_cache() 2188 self.overlays.append(copy.deepcopy(overlay)) 2189 self.known_types.update(overlay)
2190
2191 - def _apply_type_overlay(self, type_member, overlay):
2192 """Update the overlay with the missing information from type. 2193 2194 If overlay has None in any slot it gets applied from vtype. 2195 2196 Args: 2197 type_member: A descriptor for a single type struct. This is always of 2198 the following form: 2199 2200 [StructSize, { 2201 field_name_1: [.... Field descriptor ...], 2202 field_name_2: [.... Field descriptor ...], 2203 }] 2204 2205 overlay: An overlay descriptor for the same type described by 2206 type_member or a callable which will be used to instantiate the 2207 required type. 2208 """ 2209 # A None in the overlay allows the vtype to bubble up. 2210 if overlay is None: 2211 return type_member 2212 2213 if type_member is None: 2214 return overlay 2215 2216 # this allows the overlay to just specify a class directly to be 2217 # instantiated for a particular type. 2218 if callable(overlay): 2219 return overlay 2220 2221 # A base string means its an alias of another type. 2222 if isinstance(overlay, basestring): 2223 return overlay 2224 2225 # Check the overlay and type descriptor for sanity. 2226 if len(overlay) != 2 or not isinstance(overlay[1], dict): 2227 raise RuntimeError("Overlay error: Invalid overlay %s" % overlay) 2228 2229 if len(type_member) != 2 or not isinstance(type_member[1], dict): 2230 raise RuntimeError("VType error: Invalid type descriptor %s" % 2231 type_member) 2232 2233 # Allow the overlay to override the struct size. 2234 if overlay[0] is None: 2235 overlay[0] = type_member[0] 2236 2237 # The field overlay describes each field in the struct. 2238 field_overlay = overlay[1] 2239 2240 # Now go over all the fields in the type_member and copy them into the 2241 # overlay. 2242 for k, v in type_member[1].items(): 2243 if k not in field_overlay: 2244 field_overlay[k] = v 2245 else: 2246 field_overlay[k] = self._apply_field_overlay( 2247 v, field_overlay[k]) 2248 2249 return overlay
2250
2251 - def _apply_field_overlay(self, field_member, field_overlay):
2252 """Update the field overlay with the missing information from type. 2253 2254 If the overlay has None in any slot it gets applied from vtype. 2255 2256 Args: 2257 field_member: A field descriptor. This can be of the modern form: 2258 2259 [Offset, [TargetName, dict(arg1=value1, arg2=value2)]] 2260 2261 The second part is termed the field descriptor. If the overlay 2262 specifies a field descriptor it will completely replace the 2263 vtype's descriptor. 2264 2265 Alternatively we also support the legacy form: 2266 [TargetName, [values]] 2267 2268 2269 Note that if the Target name differs we deem the entire field to 2270 be overlayed and replace the entire definition with the overlayed 2271 one. 2272 2273 field_overlay: Can be a field descriptor as above, or a callable - in 2274 which case this field member will be called when the field is 2275 accessed. 2276 """ 2277 # A None in the overlay allows the vtype to bubble up. 2278 if field_overlay is None: 2279 return field_member 2280 2281 if callable(field_overlay): 2282 return field_overlay 2283 2284 if callable(field_member): 2285 return field_member 2286 2287 # Check the overlay and type descriptor for sanity. 2288 if len(field_overlay) != 2 or not isinstance(field_overlay[1], list): 2289 raise RuntimeError( 2290 "Overlay error: Invalid overlay %s" % field_overlay) 2291 2292 if len(field_member) != 2 or not isinstance(field_member[1], list): 2293 raise RuntimeError("VType error: Invalid field type descriptor %s" % 2294 field_member) 2295 2296 offset, field_description = field_member 2297 if field_overlay[0] is None: 2298 field_overlay[0] = offset 2299 2300 if field_overlay[1] is None: 2301 field_overlay[1] = field_description 2302 2303 return field_overlay
2304
2305 - def get_constant(self, constant, is_address=False):
2306 """Retrieve a constant from the profile. 2307 2308 Args: 2309 constant: The name of the constant to retrieve. 2310 2311 is_address: If true the constant is converted to an address. 2312 """ 2313 ACCESS_LOG.LogConstant(self.name, constant) 2314 self.EnsureInitialized() 2315 2316 result = self.constants.get(constant) 2317 if callable(result): 2318 result = result() 2319 2320 if result is None: 2321 result = NoneObject( 2322 "Constant %s does not exist in profile." % constant) 2323 2324 elif is_address: 2325 result = Pointer.integer_to_address(result) 2326 2327 return result
2328
2329 - def get_constant_object(self, constant, target=None, target_args=None, 2330 vm=None, **kwargs):
2331 """A help function for retrieving pointers from the symbol table.""" 2332 self.EnsureInitialized() 2333 2334 if vm is None: 2335 vm = self.session.GetParameter("default_address_space") 2336 2337 if target is None: 2338 if constant not in self.constant_types: 2339 raise TypeError("Unknown constant type for %s" % constant) 2340 2341 # target_args are optional in the profile specification. 2342 try: 2343 target, target_args = self.constant_types[constant] 2344 except ValueError: 2345 target = self.constant_types[constant][0] 2346 2347 kwargs.update(target_args or {}) 2348 offset = self.get_constant(constant, is_address=True) 2349 if not offset: 2350 return offset 2351 2352 result = self.Object(target, profile=self, offset=offset, vm=vm, 2353 **kwargs) 2354 return result
2355
2356 - def get_constant_by_address(self, address):
2357 self.EnsureInitialized() 2358 2359 address = Pointer.integer_to_address(address) 2360 2361 lowest_eq, name = self.get_nearest_constant_by_address(address) 2362 if lowest_eq != address: 2363 return NoneObject("Constant not found") 2364 2365 return name
2366
2367 - def get_nearest_constant_by_address(self, address, below=True):
2368 """Returns the closest constant below or equal to the address.""" 2369 self.EnsureInitialized() 2370 2371 address = Pointer.integer_to_address(address) 2372 2373 if below: 2374 offset, names = self.constant_addresses.get_value_smaller_than( 2375 address) 2376 else: 2377 offset, names = self.constant_addresses.get_value_larger_than( 2378 address) 2379 2380 if offset is None: 2381 return -1, NoneObject("Constant not found") 2382 2383 if not isinstance(names, list): 2384 names = [names] 2385 2386 return offset, names
2387
2388 - def get_enum(self, enum_name, field=None):
2389 result = self.enums.get(enum_name) 2390 if result and field != None: 2391 result = result.get(field) 2392 return result
2393
2394 - def get_reverse_enum(self, enum_name, field=None):
2395 result = self.reverse_enums.get(enum_name) 2396 if result and field != None: 2397 result = result.get(field) 2398 return result
2399
2400 - def __dir__(self):
2401 """Support tab completion.""" 2402 return sorted(self.__dict__.keys() + list(self.known_types) + 2403 dir(self.__class__))
2404
2405 - def __getattr__(self, attr):
2406 """Make it easier to instantiate individual members. 2407 2408 This method makes it possible to use the form: 2409 2410 self.profile._EPROCESS(vm=self.kernel_address_space, offset=X) 2411 2412 Which is easier to type and works well with attribute completion 2413 (provided by __dir__). 2414 """ 2415 self.compile_type(attr) 2416 2417 if self.types[attr] is None and attr not in self.object_classes: 2418 raise AttributeError("No such vtype: %s" % attr) 2419 2420 return Curry(self.Object, attr)
2421
2422 - def Object(self, type_name=None, offset=None, vm=None, name=None, 2423 parent=None, context=None, **kwargs):
2424 """ A function which instantiates the object named in type_name (as 2425 a string) from the type in profile passing optional args of 2426 kwargs. 2427 2428 Args: 2429 type_name: The name of the Struct to instantiate (e.g. _EPROCESS). 2430 2431 vm: The address space to instantiate the object onto. If not provided 2432 we use a dummy null padded address space. 2433 2434 offset: The location in the address space where the object is 2435 instantiated. 2436 2437 name: An optional name for the object. 2438 2439 context: An opaque dict which is passed to all objects created from 2440 this object. 2441 2442 parent: The object can maintain a reference to its parent object. 2443 """ 2444 name = name or type_name 2445 2446 # Ensure we are called correctly. 2447 if name.__class__ not in (unicode, str): 2448 raise ValueError("Type name must be a string") 2449 2450 if offset is None: 2451 offset = 0 2452 if vm is None: 2453 vm = addrspace.BaseAddressSpace.classes["DummyAddressSpace"]( 2454 size=self.get_obj_size(name) or 0, session=self.session) 2455 2456 else: 2457 offset = int(offset) 2458 if vm is None: 2459 vm = self.session.GetParameter("default_address_space") 2460 2461 kwargs['profile'] = self 2462 kwargs.setdefault("session", self.session) 2463 2464 # Compile the type on demand. 2465 self.compile_type(type_name) 2466 2467 # If the cache contains a None, this member is not represented by a 2468 # vtype (it might be a pure object class or a constant). 2469 cls = self.types[type_name] 2470 if cls is not None: 2471 result = cls(offset=offset, vm=vm, name=name, 2472 parent=parent, context=context, 2473 **kwargs) 2474 2475 return result 2476 2477 elif type_name in self.object_classes: 2478 result = self.object_classes[type_name]( 2479 type_name=type_name, 2480 offset=offset, 2481 vm=vm, 2482 name=name, 2483 parent=parent, 2484 context=context, 2485 **kwargs) 2486 2487 if isinstance(result, Struct): 2488 # This should not normally happen. 2489 self.session.logging.error( 2490 "Instantiating a Struct class %s without an overlay. " 2491 "Please ensure an overlay is defined.", type_name) 2492 2493 return result 2494 2495 else: 2496 # If we get here we have no idea what the type is supposed to be? 2497 return NoneObject("Cant find object %s in profile %s?", 2498 type_name, self)
2499
2500 - def __unicode__(self):
2501 return u"<%s profile %s (%s)>" % ( 2502 self.metadata("arch"), self.name, self.__class__.__name__)
2503
2504 - def __repr__(self):
2505 return unicode(self)
2506
2507 - def integer_to_address(self, virtual_address):
2508 return virtual_address & self.constants.get( 2509 "MaxPointer", 0xffffffffffff)
2510
2511 2512 -class TestProfile(Profile):
2513 - def _SetupProfileFromData(self, data):
2514 """Let the test manipulate the data json object directly.""" 2515 self.data = data
2516
2517 - def copy(self):
2518 result = super(TestProfile, self).copy() 2519 result.data = self.data 2520 2521 return result
2522
2523 2524 -class ProfileModification(object):
2525 """A profile modification adds new types to an existing profile. 2526 2527 A ProfileModification must be invoked explicitely. We have these as plugins 2528 so its easier to find a modification by name. A typical invokation looks 2529 like: 2530 2531 class myPlugin(plugin.Command): 2532 def __init__(self, **kwargs): 2533 super(myPlugin, self).__init__(**kwargs) 2534 2535 # Update the profile with the "VolRegistrySupport" implementation. 2536 self.profile = obj.ProfileModification.classes[ 2537 'VolRegistrySupport'](self.profile) 2538 2539 Note that this plugin must explicitely apply the correct modification. This 2540 allows the plugin to choose from a number of different implementations. For 2541 example, in the above say we have one implementation (i.e. overlays, object 2542 classes etc) called VolRegistrySupport and another called 2543 ScudetteRegistrySupport, we can choose between them. 2544 2545 Now suppose that ScudetteRegistrySupport introduces an advanced class with 2546 extra methods: 2547 2548 class _CM_KEY_INDEX(obj.Struct): 2549 ..... 2550 def SpecialMethod(...): 2551 .... 2552 2553 The plugin relies on using this specific implementation (i.e. if we loaded 2554 the other profile modification, this myPlugin will fail because it will 2555 attempt to call an undefined method! Essentially by explicitely loading the 2556 modification, the plugin declares that it relies on the 2557 ScudetteRegistrySupport implementation, but does not preclude having another 2558 implementation. 2559 """
2560 - def __new__(cls, profile):
2561 # Return a copy of the profile. 2562 result = profile.copy() 2563 2564 # Apply the modification. 2565 cls.modify(result) 2566 2567 return result
2568 2569 @classmethod
2570 - def modify(cls, profile):
2571 """This class should modify the profile appropritately. 2572 2573 The profile will be a copy of the original profile and will be returned 2574 to the class caller. 2575 2576 Args: 2577 A profile to be modified. 2578 """
2579
2580 2581 -class Address(BaseObject):
2582 """A BaseObject representing an address."""
2583