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): 
 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