Skip to content

Commit fad7659

Browse files
evtx: add .xml and .lxml helpers to records
1 parent 1d869c6 commit fad7659

3 files changed

Lines changed: 40 additions & 33 deletions

File tree

Evtx/Evtx.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@
2323
from functools import wraps
2424
import logging
2525

26-
from .BinaryParser import ParseException
27-
from .BinaryParser import Block
28-
from .Nodes import NameStringNode
29-
from .Nodes import TemplateNode
26+
import Evtx.Views as e_views
3027
from .Nodes import RootNode
28+
from .Nodes import TemplateNode
29+
from .Nodes import NameStringNode
30+
from .BinaryParser import Block
31+
from .BinaryParser import ParseException
3132

3233
logger = logging.getLogger(__name__)
3334

@@ -395,7 +396,7 @@ def add_template(self, offset, parent=None):
395396
self._load_templates()
396397

397398
node = TemplateNode(self._buf, self._offset + offset,
398-
self, parent or self)
399+
self, parent or self)
399400
self._templates[offset] = node
400401
return node
401402

@@ -466,3 +467,29 @@ def data(self):
466467
up this record.
467468
"""
468469
return self._buf[self.offset():self.offset() + self.size()]
470+
471+
def xml(self):
472+
'''
473+
render the record into XML.
474+
does not include the xml declaration header.
475+
476+
Returns:
477+
str: the rendered xml document.
478+
'''
479+
return e_views.evtx_record_xml_view(self)
480+
481+
def lxml(self):
482+
'''
483+
render the record into a lxml document.
484+
this is useful for querying data from the record using xpath, etc.
485+
486+
note: lxml must be installed.
487+
488+
Returns:
489+
lxml.etree.ElementTree: the rendered and parsed xml document.
490+
491+
Raises:
492+
ImportError: if lxml is not installed.
493+
'''
494+
import lxml
495+
return lxml.etree.fromstring((e_views.XML_HEADER + self.xml()).encode('utf-8'))

Evtx/Views.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
import xml.sax.saxutils
2222

2323

24+
XML_HEADER = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n"
25+
26+
2427
class UnexpectedElementException(Exception):
2528
def __init__(self, msg):
2629
super(UnexpectedElementException, self).__init__(msg)

tests/test_records.py

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,8 @@ def test_render_record(system):
266266
chunk = one(fh.chunks())
267267
record = one(chunk.records())
268268

269-
xml = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n%s" % e_views.evtx_record_xml_view(record)
269+
xml = record.xml()
270270
assert xml == textwrap.dedent('''\
271-
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
272271
<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event"><System><Provider Name="Microsoft-Windows-Eventlog" Guid="{fc65ddd8-d6ef-4962-83d5-6e5cfe9ce148}"></Provider>
273272
<EventID Qualifiers="">105</EventID>
274273
<Version>0</Version>
@@ -302,8 +301,7 @@ def test_render_records(system):
302301
fh = evtx.FileHeader(system, 0x0)
303302
for chunk in fh.chunks():
304303
for record in chunk.records():
305-
xml = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n%s" % e_views.evtx_record_xml_view(record)
306-
assert xml is not None
304+
assert record.xml() is not None
307305

308306

309307
def test_render_records2(security):
@@ -316,26 +314,7 @@ def test_render_records2(security):
316314
fh = evtx.FileHeader(security, 0x0)
317315
for chunk in fh.chunks():
318316
for record in chunk.records():
319-
xml = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n%s" % e_views.evtx_record_xml_view(record)
320-
assert xml is not None
321-
322-
323-
def to_lxml(record):
324-
"""
325-
convert the given record to a lxml-parsed document.
326-
327-
Args:
328-
record (evtx.Record): the record to parse.
329-
330-
Returns:
331-
lxml.etree.ETree: the lxml document.
332-
"""
333-
text = e_views.evtx_record_xml_view(record)
334-
try:
335-
return lxml.etree.fromstring(b"<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>" + text.encode('utf-8'))
336-
except:
337-
print(text)
338-
raise
317+
assert record.xml() is not None
339318

340319

341320
@pytest.mark.skipif(no_lxml, reason='lxml not installed')
@@ -349,8 +328,7 @@ def test_render_records_lxml(system):
349328
fh = evtx.FileHeader(system, 0x0)
350329
for i, chunk in enumerate(fh.chunks()):
351330
for j, record in enumerate(chunk.records()):
352-
xml = to_lxml(record)
353-
assert xml is not None
331+
assert record.lxml() is not None
354332

355333

356334
@pytest.mark.skipif(no_lxml, reason='lxml not installed')
@@ -364,5 +342,4 @@ def test_render_records_lxml2(security):
364342
fh = evtx.FileHeader(security, 0x0)
365343
for i, chunk in enumerate(fh.chunks()):
366344
for j, record in enumerate(chunk.records()):
367-
xml = to_lxml(record)
368-
assert xml is not None
345+
assert record.lxml() is not None

0 commit comments

Comments
 (0)