Package rekall :: Package ui :: Module renderer
[frames] | no frames]

Source Code for Module rekall.ui.renderer

  1  # Rekall Memory Forensics 
  2  # Copyright (C) 2012 Michael Cohen 
  3  # Copyright 2013 Google Inc. All Rights Reserved. 
  4  # 
  5  # This program is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 2 of the License, or (at 
  8  # your option) any later version. 
  9  # 
 10  # This program is distributed in the hope that it will be useful, but 
 11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 13  # General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with this program; if not, write to the Free Software 
 17  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 18  # 
 19   
 20  """This module implements the Rekall renderer API. 
 21   
 22  Rekall has a pluggable rendering system. At the top level we have renderers 
 23  which are reponsible for converting the output of plugins into something usable 
 24  for the user (whatever that means). 
 25   
 26  A Rekall plugin uses the renderer to produce output by providing it with a bunch 
 27  of objects which are derived from the analysis stage. The renderer is then 
 28  responsible for rendering these special objects using pluggable 
 29  ObjectRenderer() classes. 
 30   
 31  1. The framework creates a BaseRenderer implementation (e.g. TextRenderer or 
 32     JsonRenderer) 
 33   
 34  2. This is passed to a plugin's render() method. 
 35   
 36  3. The Plugin provides various objects to the renderer via its table_row(), 
 37     format() etc methods. 
 38   
 39  4. The renderer than uses specialized ObjectRenderer() instances to render the 
 40     objects that the plugin gave it. 
 41   
 42  For example, by default the renderer used is an instance of TextRenderer. The 
 43  PSList() plugin in its render() method, calls renderer.table_row() passing a 
 44  WinFileTime() instance as the "Create Time" cell of the table. 
 45   
 46  The TextRenderer plugin aims to layout the output into the text terminal. For 
 47  this to happen it must convert the WinFileTime() instance to a rendering 
 48  primitive, specific to the TextRenderer - in this case a Cell() instance. This 
 49  conversion is done using an ObjectRenderer instance. 
 50   
 51  The TextRenderer therefore selects an ObjectRenderer instance based on two 
 52  criteria: 
 53   
 54  - The ObjectRenderer claims to support the WinFileTime() object, or any of its 
 55    base classes in order (using the ObjectRenderer.renders_type attribute). 
 56   
 57  - The ObjectRenderer claims to support the specific renderer 
 58    (i.e. TextRenderer) using its `renderers` attribute. 
 59   
 60  The TextRenderer searches for an ObjectRenderer() by traversing the 
 61  WinFileTime's __mro__ (i.e. inheritance hierarchy) for a plugin capable of 
 62  rendering it. This essentially goes from most specialized to the least 
 63  specialized renderer: 
 64   
 65  - WinFileTime 
 66  - UnixTimeStamp   <-- Specialized object renderer for unix times. 
 67  - NativeType 
 68  - BaseObject 
 69  - object          <--- Generic renderer for all objects. 
 70   
 71  Once a renderer is found, it is used to output the cell value. 
 72   
 73  ## NOTES 
 74   
 75  1. A specialized object renderer is written specifically for the renderer. This 
 76     means that it is possible to have a specialized _EPROCESS object renderer for 
 77     TextRenderer but when using the JsonRenderer a more general renderer may be 
 78     chosen (say at the BaseObject level). 
 79   
 80  2. The ObjectRenderer.render_row() method actually returns something which makes 
 81     sense to the supported renderer. There is no API specification for the return 
 82     value. For example the TextRenderer needs a Cell instance but the 
 83     JsonRenderer requires just a dict. Since ObjectRenderer instances are only 
 84     used within the renderer they only need to cooperate with the renderer class 
 85     they support. 
 86  """ 
 87  import collections 
 88  import gc 
 89  import inspect 
 90  import logging 
 91  import time 
 92   
 93  from rekall import config 
 94  from rekall import constants 
 95  from rekall_lib import registry 
 96  from rekall_lib import utils 
 97   
 98   
 99  config.DeclareOption( 
100      "-v", "--verbose", default=False, type="Boolean", 
101      help="Set logging to debug level.", group="Output control") 
102   
103  config.DeclareOption( 
104      "-q", "--quiet", default=False, type="Boolean", 
105      help="Turn off logging to stderr.", group="Output control") 
106   
107  config.DeclareOption( 
108      "--debug", default=False, type="Boolean", 
109      help="If set we break into the debugger on error conditions.") 
110   
111  config.DeclareOption( 
112      "--output_style", type="Choices", default="concise", 
113      choices=["concise", "full"], 
114      help="How much information to show. Default is 'concise'.") 
115   
116  config.DeclareOption( 
117      "--logging_level", type="Choices", default="WARNING", 
118      choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], 
119      help="The default logging level.") 
120   
121  config.DeclareOption( 
122      "--log_domain", type="ChoiceArray", default=[], 
123      choices=constants.LOG_DOMAINS, 
124      help="Add debug logging to these components.") 
125   
126   
127  # A cache to map a tuple (mro, renderer) to the corresponding object renderer. 
128  MRO_RENDERER_CACHE = utils.FastStore(100, lock=True) 
129   
130  # A cache to map a class to its reduced MRO list. Do not hold class references 
131  # in this cache as these capture closure variables via the Curry() classes on 
132  # the property methods. 
133  MRO_CACHE = utils.FastStore(100, lock=True) 
134 135 136 -class ObjectRenderer(object):
137 """Baseclass for all TestRenderer object renderers.""" 138 139 # Fall back renderer for all objects. This can also be a list or tuple of 140 # all types rendered by this renderer. 141 renders_type = "object" 142 143 # These are the renderers supported by this object renderer. 144 renderers = [] 145 146 __metaclass__ = registry.MetaclassRegistry 147 148 # A cache of Renderer, MRO mappings. 149 _RENDERER_CACHE = None 150
151 - def __init__(self, renderer=None, session=None, **options):
152 if not isinstance(renderer, BaseRenderer): 153 raise RuntimeError("Renderer object must be provided. Got %r." 154 % renderer) 155 156 self.renderer = renderer 157 self.session = session 158 159 self.options = options 160 if self.session: 161 self.output_style = self.session.GetParameter("output_style") 162 else: 163 self.output_style = None
164 165 @staticmethod
166 - def get_mro(item):
167 """Return the MRO of an item.""" 168 if not inspect.isclass(item): 169 item = item.__class__ 170 171 try: 172 return MRO_CACHE.Get(item.__name__) 173 except KeyError: 174 # Remove duplicated class names from the MRO (The current 175 # implementation uses the flat class name to select the 176 # ObjectRenderer so we can get duplicates but they dont matter). 177 result = tuple(collections.OrderedDict.fromkeys( 178 [unicode(x.__name__) for x in item.__mro__])) 179 180 MRO_CACHE.Put(item.__name__, result) 181 return result
182 183 @classmethod
184 - def ByName(cls, name, renderer):
185 """A constructor for an ObjectRenderer by name.""" 186 cls._BuildRendererCache() 187 188 if not isinstance(renderer, basestring): 189 renderer = renderer.__class__.__name__ 190 191 # Find the object renderer which works for this name. 192 return cls._RENDERER_CACHE.get((name, renderer))
193 194 @classmethod
195 - def FromMRO(cls, mro, renderer):
196 """Get the best object renderer class from the MRO.""" 197 try: 198 return MRO_RENDERER_CACHE[(mro, renderer)] 199 except KeyError: 200 cls._BuildRendererCache() 201 202 if not isinstance(renderer, basestring): 203 renderer = renderer.__class__.__name__ 204 205 # MRO is the list of object inheritance for each type. For example: 206 # FileAddressSpace,FDAddressSpace,BaseAddressSpace. We try to match 207 # the object renderer from most specific to least specific (or more 208 # general). 209 for class_name in mro.split(":"): 210 object_renderer_cls = cls._RENDERER_CACHE.get( 211 (class_name, renderer)) 212 213 if object_renderer_cls: 214 MRO_RENDERER_CACHE.Put((mro, renderer), object_renderer_cls) 215 return object_renderer_cls
216 217 @classmethod
218 - def _BuildRendererCache(cls):
219 # Build the cache if needed. 220 if cls._RENDERER_CACHE is None: 221 # Do this in a thread safe manner. 222 result = {} 223 for object_renderer_cls in cls.classes.values(): 224 for impl_renderer in object_renderer_cls.renderers: 225 render_types = object_renderer_cls.renders_type 226 if not isinstance(render_types, (list, tuple)): 227 render_types = (render_types,) 228 229 for render_type in render_types: 230 key = (render_type, impl_renderer) 231 if key in result: 232 raise RuntimeError( 233 "Multiple renderer implementations for class " 234 "%s: %s, %s" % (key, object_renderer_cls, 235 result[key])) 236 237 result[key] = object_renderer_cls 238 239 cls._RENDERER_CACHE = result
240 241 @classmethod
242 - def ForTarget(cls, target, renderer):
243 """Get the best ObjectRenderer to encode this target. 244 245 ObjectRenderer instances are chosen based on both the taget and the 246 renderer they implement. 247 248 Args: 249 taget: The target object to render. We walk the MRO to select the best 250 renderer. This is a python object to be rendered. 251 252 renderer: The renderer that will be used. This can be a string 253 (e.g. "TextRenderer") or a renderer instance. 254 255 Returns: 256 An ObjectRenderer class which is best suited for rendering the target. 257 """ 258 return cls.ForType(type(target), renderer)
259 260 @classmethod
261 - def ForType(cls, target_type, renderer):
262 """Get the best ObjectRenderer to encode this target type. 263 264 ObjectRenderer instances are chosen based on both the taget and the 265 renderer they implement. 266 267 Args: 268 taget_type: Type of the rendered object. We walk the MRO to select 269 the best renderer. 270 271 renderer: The renderer that will be used. This can be a string 272 (e.g. "TextRenderer") or a renderer instance. 273 274 Returns: 275 An ObjectRenderer class which is best suited for rendering the target. 276 """ 277 cls._BuildRendererCache() 278 279 if not isinstance(renderer, basestring): 280 renderer = renderer.__class__.__name__ 281 282 # Search for a handler which supports both the renderer and the object 283 # type. 284 for mro_cls in cls.get_mro(target_type): 285 handler = cls._RENDERER_CACHE.get((mro_cls, renderer)) 286 if handler: 287 return handler
288 289 @classmethod
290 - def cache_key(cls, item):
291 """Return a suitable cache key.""" 292 return repr(item)
293
294 - def DelegateObjectRenderer(self, item):
295 """Create an object renderer for an item based on this object renderer. 296 297 This is useful when delegating to render something else. 298 """ 299 renderer_cls = self.ForTarget(item, self.renderer) 300 return renderer_cls(session=self.session, renderer=self.renderer, 301 **self.options)
302
303 - def render_header(self, name=None, **options):
304 """This should be overloaded to return the header Cell. 305 306 Note that typically the same ObjectRenderer instance will be used to 307 render all Cells in the same column. 308 309 Args: 310 name: The name of the Column. 311 options: The options of the column (i.e. the dict which defines the 312 column). 313 314 Return: 315 A Cell instance containing the formatted Column header. 316 """
317
318 - def render_row(self, target, **options):
319 """Render the target suitably. 320 321 Args: 322 target: The object to be rendered. 323 324 options: A dict containing rendering options. The options are created 325 from the column options, overriden by the row options and finally 326 the cell options. It is ok for an instance to ignore some or all of 327 the options. Some options only make sense in certain Renderer 328 contexts. 329 330 Returns: 331 A Cell instance containing the rendering of target. 332 """
333
334 335 -class BaseTable(object):
336 """Renderers contain tables.""" 337
338 - def __init__(self, session=None, renderer=None, columns=None, **options):
339 self.session = session 340 self.renderer = renderer 341 self.options = options 342 self.column_specs = [] 343 344 if not isinstance(renderer, BaseRenderer): 345 raise TypeError("Renderer object must be supplied. Got %r." 346 % renderer) 347 348 # For now support the legacy column specification and normalized to a 349 # column_spec dict. 350 for column in columns or []: 351 # Old style column specification are a tuple. The new way is a dict 352 # which is more expressive but more verbose. 353 if isinstance(column, (tuple, list)): 354 column = dict(name=column[0], 355 formatstring=column[2]) 356 357 self.column_specs.append(column)
358
359 - def render_row(self, *row, **options):
360 """Render the row suitably."""
361
362 - def flush(self):
363 pass
364
365 366 -class BaseRenderer(object):
367 """All renderers inherit from this. 368 369 This class defines the only public interface for the rendering system. This 370 is the API which should be used by Rekall plugins to render the 371 output. Derived classes can add additional methods, but these should not be 372 directly used by the plugins - otherwise plugins will fail when being 373 rendered with different renderer implementations. 374 """ 375 376 __metaclass__ = registry.MetaclassRegistry 377 378 # The user friendly name of this renderer. This is used for selection from 379 # command line etc. 380 name = None 381 382 last_spin_time = 0 383 last_gc_time = 0 384 progress_interval = 0.2 385 386 # This is used to ensure that renderers are always called as context 387 # managers. This guarantees we call start() and end() automatically. 388 _started = False 389 390 # Currently used table. 391 table = None 392 393 table_class = BaseTable 394
395 - def __init__(self, session=None):
396 self.session = session
397
398 - def __enter__(self):
399 self._started = True 400 return self
401
402 - def __exit__(self, exc_type, exc_value, trace):
403 log_handler = getattr(self.session, "_log_handler", None) 404 if log_handler != None: 405 log_handler.SetRenderer(None) 406 self.end()
407
408 - def start(self, plugin_name=None, kwargs=None):
409 """The method is called when new output is required. 410 411 Metadata about the running plugin is provided so the renderer may log it 412 if desired. 413 414 Args: 415 plugin_name: The name of the plugin which is running. 416 kwargs: The args for this plugin. 417 """ 418 _ = plugin_name 419 _ = kwargs 420 self._started = True 421 422 # This handles the progress messages from rekall for the duration of 423 # the rendering. 424 if self.session: 425 self.session.progress.Register(id(self), self.RenderProgress) 426 427 return self
428
429 - def end(self):
430 """Tells the renderer that we finished using it for a while.""" 431 self._started = False 432 433 # Remove the progress handler from the session. 434 if self.session: 435 self.session.progress.UnRegister(id(self)) 436 437 self.flush()
438 439 # DEPRECATED
440 - def write(self, data):
441 """Renderer should write some data.""" 442 pass
443
444 - def section(self, name=None, width=50):
445 """Start a new section. 446 447 Sections are used to separate distinct entries (e.g. reports of 448 different files). 449 """ 450 _ = name 451 _ = width
452
453 - def format(self, formatstring, *data):
454 """Write formatted data. 455 456 For renderers that need access to the raw data (e.g. to check for 457 NoneObjects), it is preferred to call this method directly rather than 458 to format the string in the plugin itself. 459 460 By default we just call the format string directly. 461 """ 462 _ = formatstring 463 _ = data 464 if not self._started: 465 raise RuntimeError("Writing to a renderer that is not started.")
466
467 - def flush(self):
468 """Renderer should flush data.""" 469 if self.table: 470 self.table.flush() 471 self.table = None
472
473 - def table_header(self, columns=None, **options):
474 """Table header renders the title row of a table. 475 476 This also stores the header types to ensure everything is formatted 477 appropriately. It must be a list of specs rather than a dict for 478 ordering purposes. 479 """ 480 if not self._started: 481 raise RuntimeError("Renderer is used without a context manager.") 482 483 # Ensure the previous table is flushed. 484 if self.table: 485 self.table.flush() 486 487 self.table = self.table_class(session=self.session, renderer=self, 488 columns=columns, **options)
489
490 - def table_row(self, *row, **kwargs):
491 """Outputs a single row of a table.""" 492 self.table.render_row(row=row, **kwargs)
493
494 - def report_error(self, message):
495 """Render the error in an appropriate way.""" 496 # By default just log the error. Visual renderers may choose to render 497 # errors in a distinctive way. 498 # TODO(jordi): Remove in 3 months when any usage should have been 499 # noticed and fixed. 500 logging.error( 501 "**DEPRECATED** report_error is deprecated. Please use the session " 502 "logging feature instead. Original message was: %s", message) 503 self.session.logging.error( 504 "**DEPRECATED** (via report_error): %s", message)
505
506 - def RenderProgress(self, *_, **kwargs):
507 """Will be called to render a progress message to the user.""" 508 # Only write once per self.progress_interval. 509 now = time.time() 510 force = kwargs.get("force") 511 512 # GC is expensive so we need to do it less frequently. 513 if now > self.last_gc_time + 10: 514 gc.collect() 515 self.last_gc_time = now 516 517 if force or now > self.last_spin_time + self.progress_interval: 518 self.last_spin_time = now 519 520 # Signal that progress must be written. 521 return True 522 523 return False
524
525 - def open(self, directory=None, filename=None, mode="rb"):
526 """Opens a file for writing or reading.""" 527 _ = directory 528 _ = filename 529 _ = mode 530 raise IOError("Renderer does not support writing to files.")
531
532 - def get_object_renderer(self, target=None, type=None, target_renderer=None, 533 **options):
534 if target_renderer is None: 535 target_renderer = self 536 537 if isinstance(type, basestring): 538 obj_renderer = ObjectRenderer.ByName(type, target_renderer) 539 if not obj_renderer: 540 # We don't want to blow up because we might still find the 541 # renderer once we actually get the MRO. 542 return None 543 544 elif type is not None: 545 obj_renderer = ObjectRenderer.ForType(type, target_renderer) 546 else: 547 obj_renderer = ObjectRenderer.ForTarget(target, target_renderer) 548 549 if not obj_renderer: 550 # This should never happen if the renderer installs a handler for 551 # type object. 552 # pylint: disable=protected-access 553 raise RuntimeError("Unable to render object %r for renderer %s" % 554 (repr(target), target_renderer) + 555 str(ObjectRenderer._RENDERER_CACHE)) 556 557 return obj_renderer(renderer=self, session=self.session, **options)
558
559 - def Log(self, record):
560 """Logs a log message. Implement if you want to handle logging."""
561
562 563 -def CopyObjectRenderers(args, renderer=None):
564 """Automatically copy the object renderers for a renderer. 565 566 This is a convenience method which automatically generates the handlers for 567 the given renderer by copying them from the object renderers given in args. 568 569 Args: 570 args: classes to copy. 571 572 renderer: A string describing the renderer to apply the object renderers 573 to. 574 575 Return: 576 Nothing - new renderers are automatically registered. 577 578 """ 579 for arg in args: 580 # Make a new unique name. 581 new_class_name = renderer + arg.__name__ 582 type(new_class_name, (arg,), dict(renderers=[renderer]))
583