Evtx.Views

Package Evtx :: Module Views |
[hide private]
[frames] | no frames]

# Source Code for Module Evtx.Views

      1  #!/usr/bin/python
      2  #    This file is part of python-evtx.
      3  #
      4  #   Copyright 2012, 2013 Willi Ballenthin <william.ballenthin@mandiant.com>
      5  #                    while at Mandiant <http://www.mandiant.com>
      6  #
      7  #   Licensed under the Apache License, Version 2.0 (the "License");
      8  #   you may not use this file except in compliance with the License.
      9  #   You may obtain a copy of the License at
     10  #
     11  #       http://www.apache.org/licenses/LICENSE-2.0
     12  #
     13  #   Unless required by applicable law or agreed to in writing, software
     14  #   distributed under the License is distributed on an "AS IS" BASIS,
     15  #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     16  #   See the License for the specific language governing permissions and
     17  #   limitations under the License.
     18  #
     19  #   Version v.0.3.0
     20  from Nodes import RootNode
     21  from Nodes import TemplateNode
     22  from Nodes import EndOfStreamNode
     23  from Nodes import OpenStartElementNode
     24  from Nodes import CloseStartElementNode
     25  from Nodes import CloseEmptyElementNode
     26  from Nodes import CloseElementNode
     27  from Nodes import ValueNode
     28  from Nodes import AttributeNode
     29  from Nodes import CDataSectionNode
     30  from Nodes import EntityReferenceNode
     31  from Nodes import ProcessingInstructionTargetNode
     32  from Nodes import ProcessingInstructionDataNode
     33  from Nodes import TemplateInstanceNode
     34  from Nodes import NormalSubstitutionNode
     35  from Nodes import ConditionalSubstitutionNode
     36  from Nodes import StreamStartNode
     37
     38
     39 -class UnexpectedElementException(Exception):
     40 -    def __init__(self, msg):
     41          super(UnexpectedElementException, self).__init__(msg)
     42
     43
     44 -def _make_template_xml_view(root_node, cache=None):
     45      """
     46      Given a RootNode, parse only the template/children
     47        and not the substitutions.
     48
     49      Note, the cache should be local to the Evtx.Chunk.
     50        Do not share caches across Chunks.
     51
     52      @type root_node: Nodes.RootNode
     53      @type cache: dict of {int: TemplateNode}
     54      @rtype: str
     55      """
     56      if cache is None:
     57          cache = {}
     58
     59      def escape_format_chars(s):
     60          return s.replace("{", "{{").replace("}", "}}")
     61
     62      def rec(node, acc):
     63          if isinstance(node, EndOfStreamNode):
     64              pass  # intended
     65          elif isinstance(node, OpenStartElementNode):
     66              acc.append("<")
     67              acc.append(node.tag_name())
     68              for child in node.children():
     69                  if isinstance(child, AttributeNode):
     70                      acc.append(" ")
     71                      acc.append(child.attribute_name().string())
     72                      acc.append("=\"")
     73                      rec(child.attribute_value(), acc)
     74                      acc.append("\"")
     75              acc.append(">")
     76              for child in node.children():
     77                  rec(child, acc)
     78              acc.append("</")
     79              acc.append(node.tag_name())
     80              acc.append(">\n")
     81          elif isinstance(node, CloseStartElementNode):
     82              pass  # intended
     83          elif isinstance(node, CloseEmptyElementNode):
     84              pass  # intended
     85          elif isinstance(node, CloseElementNode):
     86              pass  # intended
     87          elif isinstance(node, ValueNode):
     88              acc.append(escape_format_chars(node.children()[0].string()))
     89          elif isinstance(node, AttributeNode):
     90              pass  # intended
     91          elif isinstance(node, CDataSectionNode):
     92              acc.append("<![CDATA[")
     93              acc.append(node.cdata())
     94              acc.append("]]>")
     95          elif isinstance(node, EntityReferenceNode):
     96              acc.append(node.entity_reference())
     97          elif isinstance(node, ProcessingInstructionTargetNode):
     98              acc.append(node.processing_instruction_target())
     99          elif isinstance(node, ProcessingInstructionDataNode):
    100              acc.append(node.string())
    101          elif isinstance(node, TemplateInstanceNode):
    102              raise UnexpectedElementException("TemplateInstanceNode")
    103          elif isinstance(node, NormalSubstitutionNode):
    104              acc.append("{")
    105              acc.append("%d" % (node.index()))
    106              acc.append("}")
    107          elif isinstance(node, ConditionalSubstitutionNode):
    108              acc.append("{")
    109              acc.append("%d" % (node.index()))
    110              acc.append("}")
    111          elif isinstance(node, StreamStartNode):
    112              pass  # intended
    113
    114      acc = []
    115      template_instance = root_node.fast_template_instance()
    116      templ_off = template_instance.template_offset() + \
    117          template_instance._chunk.offset()
    118      if templ_off in cache:
    119          acc.append(cache[templ_off])
    120      else:
    121          node = TemplateNode(template_instance._buf, templ_off,
    122                              template_instance._chunk, template_instance)
    123          sub_acc = []
    124          for c in node.children():
    125              rec(c, sub_acc)
    126          sub_templ = "".join(sub_acc)
    127          cache[templ_off] = sub_templ
    128          acc.append(sub_templ)
    129      return "".join(acc)
    130
    131
    132 -def _build_record_xml(record, cache=None):
    133      """
    134      Note, the cache should be local to the Evtx.Chunk.
    135        Do not share caches across Chunks.
    136
    137      @type record: Evtx.Record
    138      @type cache: dict of {int: TemplateNode}
    139      @rtype: str
    140      """
    141      if cache is None:
    142          cache = {}
    143
    144      def rec(root_node):
    145          f = _make_template_xml_view(root_node, cache=cache)
    146          subs_strs = []
    147          for sub in root_node.fast_substitutions():
    148              if isinstance(sub, basestring):
    149                  subs_strs.append(sub.encode("ascii", "xmlcharrefreplace"))
    150              elif isinstance(sub, RootNode):
    151                  subs_strs.append(rec(sub))
    152              elif sub is None:
    153                  subs_strs.append("")
    154              else:
    155                  subs_strs.append(str(sub))
    156          return f.format(*subs_strs)
    157      xml = rec(record.root())
    158      xml = xml.replace("&", "&amp;")
    159      return xml
    160
    161
    162 -def evtx_record_xml_view(record, cache=None):
    163      """
    164      Generate an UTF-8 XML representation of an EVTX record.
    165
    166      Note, the cache should be local to the Evtx.Chunk.
    167        Do not share caches across Chunks.
    168
    169      @type record: Evtx.Record
    170      @type cache: dict of {int: TemplateNode}
    171      @rtype: str
    172      """
    173      if cache is None:
    174          cache = {}
    175      return _build_record_xml(record, cache=cache).encode("utf8", "xmlcharrefreplace")
    176
    177
    178 -def evtx_chunk_xml_view(chunk):
    179      """
    180      Generate UTF-8 XML representations of the records in an EVTX chunk.
    181
    182      Does not include the XML <?xml... header.
    183      Records are ordered by chunk.records()
    184
    185      @type chunk: Evtx.Chunk
    186      @rtype: generator of str, Evtx.Record
    187      """
    188      cache = {}
    189      for record in chunk.records():
    190          record_str = _build_record_xml(record, cache=cache)
    191          yield record_str.encode("utf8", "xmlcharrefreplace"), record
    192
    193
    194 -def evtx_file_xml_view(file_header):
    195      """
    196      Generate UTF-8 XML representations of the records in an EVTX file.
    197
    198      Does not include the XML <?xml... header.
    199      Records are ordered by file_header.chunks(), and then by chunk.records()
    200
    201      @type file_header: Evtx.FileHeader
    202      @rtype: generator of str, Evtx.Record
    203      """
    204      for chunk in file_header.chunks():
    205          cache = {}
    206          for record in chunk.records():
    207              record_str = _build_record_xml(record, cache=cache)
    208              yield record_str.encode("utf8", "xmlcharrefreplace"), record
    209
    210
    211 -def evtx_template_readable_view(template_node):
    212      """
    213      """
    214      def rec(node, acc):
    215          if isinstance(node, EndOfStreamNode):
    216              pass  # intended
    217          elif isinstance(node, OpenStartElementNode):
    218              acc.append("<")
    219              acc.append(node.tag_name())
    220              for child in node.children():
    221                  if isinstance(child, AttributeNode):
    222                      acc.append(" ")
    223                      acc.append(child.attribute_name().string())
    224                      acc.append("=\"")
    225                      rec(child.attribute_value(), acc)
    226                      acc.append("\"")
    227              acc.append(">")
    228              for child in node.children():
    229                  rec(child, acc)
    230              acc.append("</")
    231              acc.append(node.tag_name())
    232              acc.append(">\n")
    233          elif isinstance(node, CloseStartElementNode):
    234              pass  # intended
    235          elif isinstance(node, CloseEmptyElementNode):
    236              pass  # intended
    237          elif isinstance(node, CloseElementNode):
    238              pass  # intended
    239          elif isinstance(node, ValueNode):
    240              acc.append(node.children()[0].string())
    241          elif isinstance(node, AttributeNode):
    242              pass  # intended
    243          elif isinstance(node, CDataSectionNode):
    244              acc.append("<![CDATA[")
    245              acc.append(node.cdata())
    246              acc.append("]]>")
    247          elif isinstance(node, EntityReferenceNode):
    248              acc.append(node.entity_reference())
    249          elif isinstance(node, ProcessingInstructionTargetNode):
    250              acc.append(node.processing_instruction_target())
    251          elif isinstance(node, ProcessingInstructionDataNode):
    252              acc.append(node.string())
    253          elif isinstance(node, TemplateInstanceNode):
    254              raise UnexpectedElementException("TemplateInstanceNode")
    255          elif isinstance(node, NormalSubstitutionNode):
    256              acc.append("[Normal Substitution(index=%d, type=%d)]" % \
    257                             (node.index(), node.type()))
    258          elif isinstance(node, ConditionalSubstitutionNode):
    259              acc.append("[Condititional Substitution(index=%d, type=%d)]" % \
    260                             (node.index(), node.type()))
    261          elif isinstance(node, StreamStartNode):
    262              pass  # intended
    263
    264      sub_acc = []
    265      for c in template_node.children():
    266          rec(c, sub_acc)
    267      return "".join(sub_acc)
    268