1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
128 MRO_RENDERER_CACHE = utils.FastStore(100, lock=True)
129
130
131
132
133 MRO_CACHE = utils.FastStore(100, lock=True)
137 """Baseclass for all TestRenderer object renderers."""
138
139
140
141 renders_type = "object"
142
143
144 renderers = []
145
146 __metaclass__ = registry.MetaclassRegistry
147
148
149 _RENDERER_CACHE = None
150
151 - def __init__(self, renderer=None, session=None, **options):
164
165 @staticmethod
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
175
176
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):
193
194 @classmethod
216
217 @classmethod
219
220 if cls._RENDERER_CACHE is None:
221
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
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
283
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
291 """Return a suitable cache key."""
292 return repr(item)
293
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
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
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
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
349
350 for column in columns or []:
351
352
353 if isinstance(column, (tuple, list)):
354 column = dict(name=column[0],
355 formatstring=column[2])
356
357 self.column_specs.append(column)
358
360 """Render the row suitably."""
361
364
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
379
380 name = None
381
382 last_spin_time = 0
383 last_gc_time = 0
384 progress_interval = 0.2
385
386
387
388 _started = False
389
390
391 table = None
392
393 table_class = BaseTable
394
397
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
423
424 if self.session:
425 self.session.progress.Register(id(self), self.RenderProgress)
426
427 return self
428
430 """Tells the renderer that we finished using it for a while."""
431 self._started = False
432
433
434 if self.session:
435 self.session.progress.UnRegister(id(self))
436
437 self.flush()
438
439
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
466
468 """Renderer should flush data."""
469 if self.table:
470 self.table.flush()
471 self.table = None
472
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
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
491 """Outputs a single row of a table."""
492 self.table.render_row(row=row, **kwargs)
493
495 """Render the error in an appropriate way."""
496
497
498
499
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
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
558
559 - def Log(self, record):
560 """Logs a log message. Implement if you want to handle logging."""
561
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
581 new_class_name = renderer + arg.__name__
582 type(new_class_name, (arg,), dict(renderers=[renderer]))
583