1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """This module implements a JSON render.
21
22 A renderer is used by plugins to produce formatted output.
23
24 This code is tested in plugins/tools/render_test.py
25 """
26 import copy
27 import json
28 import pdb
29 import sys
30
31 from rekall import constants
32 from rekall.ui import renderer as renderer_module
33 from rekall_lib import utils
37 """Raised if there is a decoding error."""
38
41 """Raised if we can not encode the object properly."""
42
51
53 if self.logging:
54 self.logging.error(
55 "Unable to encode %r (%s) as json, replacing with None", o,
56 type(o))
57 return None
58
61 """A decorator which caches objects in the renderer's LRU.
62
63 This applies to StateBasedObjectRenderer state dicts, which must have a
64 unique id member.
65 """
66
67 def DecodeFromJsonSafe(self, value, options):
68 obj_id = None
69 try:
70 obj_id = value.get("id")
71 result = self.renderer.cache.Get(obj_id)
72 except KeyError:
73 result = func(self, value, options)
74 if obj_id is not None:
75 self.renderer.cache.Put(obj_id, result)
76
77 return result
78
79 return DecodeFromJsonSafe
80
83 """An ObjectRenderer for Json encoding.
84
85 For the JsonRenderer we convert objects into json safe python primitives
86 (These must be json serializable).
87 """
88 renderers = ["JsonRenderer"]
89
90 @classmethod
92 """Get the cache key from the object."""
93 try:
94 return item._object_id
95 except AttributeError:
96
97
98
99 return None
100
101 @classmethod
103 """Get an JsonObjectRenderer class to parse the encoded item."""
104 if isinstance(item, dict):
105 obj_renderer = item.get("obj_renderer")
106 if obj_renderer is not None:
107 return cls.ImplementationByClass(obj_renderer)
108
109 mro = item.get("mro")
110 if mro is not None:
111 return cls.FromMRO(mro, renderer)
112
113 return cls.ForTarget(item, renderer)
114
115 @staticmethod
117 """Get the class referred to by the head of the value's MRO."""
118 class_name = value["mro"].split(":")[0]
119
120 for cls in base_class.__subclasses__():
121 if class_name == cls.__name__:
122 return cls
123
132
139
141 """The Json object renderer returns a json safe object for encoding."""
142 self.EncodeToJsonSafe(item, **options)
143
144 - def Summary(self, item, **options):
145 """Returns the object formatted as a string."""
146 _ = item
147 _ = options
148 return ""
149
151 """Convert the item into a JSON safe item.
152
153 JSON is only capable of encoding some simple types (dict, list, int,
154 float, unicode strings etc). This method is called to convert the item
155 to one of these representations. Note that this method will be called on
156 the ObjectRenderer instance with a renders_type attribute which appears
157 on the item's MRO.
158
159 Args:
160 item: A python object derived from the class mentioned in the
161 renders_type attribite.
162
163 Returns:
164 A JSON serializable object (e.g. dict, list, unicode string etc).
165 """
166 if item == None:
167 return None
168
169
170 elif item.__class__ is dict:
171
172 result = {}
173 for k, v in item.items():
174 result[k] = self._encode_value(v, **options)
175
176 return result
177
178
179 elif isinstance(item, list):
180 return list(self._encode_value(x, **options) for x in item)
181
182 elif isinstance(item, tuple):
183 return tuple(self._encode_value(x, **options) for x in item)
184
185
186 if isinstance(item, (unicode, int, long, float)):
187 return item
188
189
190
191
192
193
194 self.session.logging.error(
195 "Unable to encode objects of type %s", type(item))
196 if "strict" in options:
197 raise EncodingError(
198 "Unable to encode objects of type %s" % type(item))
199
200 return None
201
203 """Decode the item from its Json safe representation.
204
205 This should essentially be the reverse of EncodeToJsonSafe(). Each
206 ObjectRenderer class should implement this method to invert
207 EncodeToJsonSafe().
208
209 Args:
210 value: The json safe object to decode.
211 options: A dict which will receive any options encoded by the encoder.
212
213 Returns:
214 A python object.
215 """
216 if value == None:
217 return None
218
219 if value.__class__ is dict:
220 result = dict()
221 for k, v in value.iteritems():
222 result[k] = self._decode_value(v, options)
223
224 return result
225
226 if value.__class__ is list:
227 return list(self._decode_value(x, options) for x in value)
228
229 if value.__class__ is tuple:
230 return tuple(self._decode_value(x, options) for x in value)
231
232
233 if isinstance(value, (unicode, int, long, float)):
234 return value
235
236 return value
237
240 """An object renderer which serializes an object to a dict."""
241 renders_type = ""
242
243 @classmethod
245 """Get the decoding cache key from the json safe encoding."""
246 return item.get("id")
247
249 _ = item
250 return {}
251
252 @CacheableState
257
259 state = self.GetState(item, **options)
260 if state.__class__ is not dict:
261 raise EncodingError(
262 "%s.GetState method must return a plain dict." %
263 self.__class__.__name__)
264
265
266 if not "mro" in state:
267
268
269 state["mro"] = ":".join(self.get_mro(item))
270
271
272
273
274 try:
275 object_id = item._object_id
276 state["id"] = object_id
277 except AttributeError:
278 pass
279
280
281 if details:
282 state["details"] = unicode(repr(item))
283
284 return super(StateBasedObjectRenderer, self).EncodeToJsonSafe(
285 state, **options)
286
289
290
291 renders_type = "str"
292
294 result = value.get("str")
295 if result is None:
296 result = value.get("b64").decode("base64")
297 else:
298 return result.encode("utf8")
299
300 return result
301
303 try:
304
305
306
307 return dict(str=unicode(item, "utf8"))
308 except UnicodeError:
309
310 return dict(b64=unicode(item.encode("base64")).rstrip("\n"))
311
321
343
346 renders_type = "TreeNode"
347
356
358 result = options
359 result["child"] = item
360 result["type_name"] = u"TreeNode"
361
362 return result
363
366 - def __init__(self, session=None, renderer=None):
371
372 - def Encode(self, item, **options):
392
395 """An empty class to access the real instance later."""
396
399
402 """A Decoder for JSON encoded data."""
403
407
408 - def Decode(self, item, options=None):
456
459 """Render the output as a json object.
460
461 The JSON output is designed to be streamed to a remote end - that is results
462 are sent incrementally as soon as they are available. The receiver can then
463 process the results as they come, rendering them to screen or GUI.
464
465 The data is essentially a list of commands.
466
467 Each command is a list. The first parameter is the command name, further
468 parameters are the args to the command.
469
470 Currently the following commands are supported:
471
472 m: This is a metadata, followed by a dict of various metadata.
473
474 s: Start a new section. Followed by section name.
475
476 f: A free format text line. Followed by format string and a list of
477 parameters. Parameters are dicts encoded using the lexicon.
478
479 t: Start a new table. Followed by Table headers. Followed by a list of lists
480 (human_name, name, formatstring).
481
482 r: A table row. Followed by a list of dicts for each row cell. Each row cell
483 is encoded using the lexicon for both keys and values.
484
485 p: A progress message. Followed by a single string which is the formatted
486 message.
487
488 L: Log message sent via session.logging logger.
489 """
490
491 name = "json"
492
493 progress_interval = 1
494
495
496
497 data = None
498
499 spinner = r"/-\|"
500 last_spin = 0
501
502 - def __init__(self, output=None, send_message_callback=None, **kwargs):
503 super(JsonRenderer, self).__init__(**kwargs)
504
505 self.send_message_callback = send_message_callback
506
507
508 self.output = output
509
510
511
512 self.object_renderers = []
513
514 fd = None
515 if self.output:
516 if hasattr(self.output, "write") and hasattr(self.output, "flush"):
517 fd = self.output
518 else:
519
520 fd = open(self.output, "wb")
521
522 if fd == None:
523 fd = self.session.fd
524
525 if fd == None:
526 fd = sys.stdout
527
528 self.fd = fd
529 self.encoder = JsonEncoder(session=self.session, renderer=self)
530 self.decoder = JsonDecoder(session=self.session, renderer=self)
531
532
533 self.cache = utils.FastStore(100)
534 self.data = []
535
536 - def start(self, plugin_name=None, kwargs=None):
549
552
560
561 - def section(self, name=None, **kwargs):
564
567
575
577 result = []
578 for i, arg in enumerate(args):
579 result.append(self.encoder.Encode(
580 arg, type=self.object_renderers[i]))
581
582 self.SendMessage(["r", result, kwargs])
583
591
597
604
606 if super(JsonRenderer, self).RenderProgress():
607 if "%(" in message:
608 self.last_spin += 1
609 kwargs["spinner"] = self.spinner[
610 self.last_spin % len(self.spinner)]
611
612 formatted_message = message % kwargs
613 elif args:
614 format_args = []
615 for arg in args:
616 if callable(arg):
617 format_args.append(arg())
618 else:
619 format_args.append(arg)
620
621 formatted_message = message % tuple(format_args)
622 else:
623 formatted_message = message
624
625 self.SendMessage(["p", formatted_message])
626
627 return True
628
629 - def Log(self, record):
630 log_message = {
631 "msg": record.getMessage(),
632 "level": record.levelname,
633 "name": record.name,
634 "time": record.created,
635 }
636 self.SendMessage(["L", log_message])
637
639 """Convenience method for fast encoding of objects.
640
641 Args:
642 obj: An arbitrary object which should be encoded.
643
644 Returns:
645 a Json serializable data object.
646 """
647 return self.encoder.Encode(obj)
648
650 """Decode a json representation into an object."""
651 return self.decoder.Decode(data)
652