From 36650362e2723036605ae336a40b4064ffd1b0fb Mon Sep 17 00:00:00 2001 From: Andrew Lamoureux Date: Sat, 16 Mar 2019 01:16:06 -0400 Subject: kaitai UI plugin + elf,macho,pe --- python/examples/kaitai/kshelpers.py | 450 ++++++++++++++++++++++++++++++++++++ 1 file changed, 450 insertions(+) create mode 100755 python/examples/kaitai/kshelpers.py (limited to 'python/examples/kaitai/kshelpers.py') diff --git a/python/examples/kaitai/kshelpers.py b/python/examples/kaitai/kshelpers.py new file mode 100755 index 00000000..374a1e77 --- /dev/null +++ b/python/examples/kaitai/kshelpers.py @@ -0,0 +1,450 @@ +#!/usr/bin/env python + +import io +import sys +import types + +import binaryninja + +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QTreeWidgetItem + +from kaitaistruct import KaitaiStruct + +#------------------------------------------------------------------------------ +# id and parse +#------------------------------------------------------------------------------ + +def id_data(data): + if len(data) < 4: + return None + + if data[0:4] == "\x7fELF": + return 'elf' + elif data[0:4] in ['\xfe\xed\xfa\xce', '\xce\xfa\xed\xfe', '\xfe\xed\xfa\xcf', '\xcf\xfa\xed\xfe']: + return 'macho' + elif data[0:2] == 'MZ': + return 'pe' + + return None + +def id_file(fpath): + data = None + with open(fpath, 'rb') as fp: + data = fp.read(16) + return id_data(data) + +def getKaitaiModuleFromFileType(ftype): + if ftype == 'elf': + from elf import Elf + return Elf + elif ftype == 'macho': + from mach_o import MachO + return MachO + elif ftype == 'pe': + from microsoft_pe import MicrosoftPe + return MicrosoftPe + + return None + +def parse_fpath(fpath): + kaitaiModule = getKaitaiModuleFromFileType(id_file(fpath)) + if not kaitaiModule: return None + parsed = kaitaiModule.from_file(fpath) + parsed._read() + return parsed + +def parse_data(data): + kaitaiModule = getKaitaiModuleFromFileType(id_data(data)) + if not kaitaiModule: return None + parsed = kaitaiModule.from_bytes(data) + parsed._read() + return parsed + +def parse_io(ioObj): + ioObj.seek(0, io.SEEK_SET) + kaitaiModule = getKaitaiModuleFromFileType(id_data(ioObj.read(16))) + ioObj.seek(0, io.SEEK_SET) + if not kaitaiModule: + print 'ERROR: finding suitable kaitai module' + return None + parsed = kaitaiModule.from_io(ioObj) + parsed._read() + return parsed + +#------------------------------------------------------------------------------ +# Kaitai IO Wrapper +#------------------------------------------------------------------------------ + +# wraps a BinaryView into an "IO" that KaitaiStream can use +# +# now Kaitai can parse directly from the BinaryView and we can avoid making a +# potentially giant copy of the file contents just for kaitai parsing +# +class KaitaiBinaryViewIO: + def __init__(self, binaryView): + self.binaryView = binaryView + self.position = 0 + + def seek(self, offs, whence=io.SEEK_SET): + #print 'seek(0x%X, %d)' % (offs, whence) + if whence == io.SEEK_SET: + self.position = offs + elif whence == io.SEEK_CUR: + self.position += offs + elif whence == io.SEEK_END: + self.position = len(self.binaryView) + else: + raise Exception('unknown whence in seek(): %d' % whence) + + def tell(self): + #print 'tell() returning 0x%X' % (self.position) + return self.position + + def read(self, length): + #print 'read(%d) (starting at position: 0x%X)' % (length, self.position) + data = self.binaryView.read(self.position, length) + self.position += length + return data + + def close(self): + pass + +#------------------------------------------------------------------------------ +# text dump/debug testing stuff +#------------------------------------------------------------------------------ + +def dump(obj, depth=0): + dump_exceptions = ['_root', '_parent', '_io'] + + indent = ' '*depth + + if isinstance(obj, KaitaiStruct): + for fieldName in dir(obj): + if hasattr(obj, fieldName): + getattr(obj, fieldName) + + for fieldName in dir(obj): + #print 'considering field: %s (hasattr returns: %d)' % (fieldName, hasattr(obj, fieldName)) + if (fieldName != '_debug' and fieldName.startswith('_')) or fieldName in dump_exceptions or not hasattr(obj, fieldName): + continue + + #print 'A: %s' % fieldName + + subObj = getattr(obj, fieldName) + + if type(subObj) == types.MethodType: + pass + elif type(subObj) == types.TypeType: + pass + elif type(subObj) == types.ListType: + if len(subObj)>0 and isinstance(subObj[0], KaitaiStruct): + for i in range(len(subObj)): + print '%s.%s[%d]:' % (indent, fieldName, i) + dump(subObj[i], depth+1) + else: + print '%s.%s: %s' % (indent, fieldName, str(subObj)) + elif type(subObj) == types.DictionaryType: + print '%s.%s: %s' % (indent, fieldName, subObj) + + elif type(subObj) == types.StringType: + print '%s.%s: %s' % (indent, fieldName, repr(subObj)) + + elif type(subObj) == int: + print '%s.%s: 0x%X (%d)' % (indent, fieldName, subObj, subObj) + + elif str(type(subObj)).startswith('0: + # CASE: is list of KaitaiObjects -> recurse! + if isinstance(subObj[0], KaitaiStruct): + child = KaitaiTreeWidgetItem() + populate_child(ksobj, fieldName, fieldName, None, child) + + # does _debug have an array version of start/end? + startsEnds = None + if hasattr(ksobj, '_debug'): + if fieldName in ksobj._debug: + if 'arr' in ksobj._debug[fieldName]: + startsEnds = ksobj._debug[fieldName]['arr'] + + for i in range(len(subObj)): + grandchild = build_qtree(subObj[i]) + fieldLabel = '%s[%d]' % (fieldName, i) + grandchild.setLabel(fieldLabel) + + if startsEnds: + grandchild.setStart(startsEnds[i]['start']) + grandchild.setEnd(startsEnds[i]['end']) + + child.addChild(grandchild) + + qwi.addChild(child) + + # CASE: is list of primitive objects -> create leaves + else: + child = KaitaiTreeWidgetItem() + populate_child(ksobj, fieldName, fieldName, None, child) + + # TODO: explain this hack + kstmp = KaitaiStruct(ksobj._io) + kstmp._parent = ksobj + child.setKaitaiObject(kstmp) + + # does _debug have an array version of start/end? + startsEnds = None + if hasattr(ksobj, '_debug'): + if fieldName in ksobj._debug: + if 'arr' in ksobj._debug[fieldName]: + startsEnds = ksobj._debug[fieldName]['arr'] + + for i in range(len(subObj)): + grandchild = createLeaf('%s[%d]'%(fieldName,i), subObj[i]) + if not grandchild: + continue + + if startsEnds: + grandchild.setStart(startsEnds[i]['start']) + grandchild.setEnd(startsEnds[i]['end']) + + child.addChild(grandchild) + + qwi.addChild(child) + else: + child = createLeaf(fieldName, subObj) + if child: + # don't override createLeaf()'s work on label, value + populate_child(ksobj, fieldName, None, None, child) + qwi.addChild(child) + + return qwi + +def createLeaf(fieldName, obj): + objtype = type(obj) + + if objtype == types.MethodType: + return None + elif objtype == types.TypeType: + return None + + fieldValue = None + + if objtype in [types.StringType, types.UnicodeType]: + #if filter(lambda c: c<32 or c>127, obj): + fieldValue = repr(obj) + elif objtype == types.IntType: + fieldValue = '0x%X (%d)' % (obj, obj) + elif objtype == types.BooleanType: + fieldValue = '%s' % (obj) + elif str(objtype).startswith('