#!/usr/bin/env python from __future__ import absolute_import import io import sys import types import binaryninja from PySide2.QtCore import Qt from PySide2.QtWidgets import QTreeWidgetItem from .kaitaistruct import KaitaiStruct #sys.path.append('kaitai_struct_formats/archive') #sys.path.append('kaitai_struct_formats/cad') #sys.path.append('kaitai_struct_formats/common') #sys.path.append('kaitai_struct_formats/database') #sys.path.append('./kaitai_struct_formats/executable') #sys.path.append('kaitai_struct_formats/filesystem') #sys.path.append('kaitai_struct_formats/firmware') #sys.path.append('kaitai_struct_formats/font') #sys.path.append('kaitai_struct_formats/game') #sys.path.append('kaitai_struct_formats/geospatial') #sys.path.append('kaitai_struct_formats/hardware') #sys.path.append('kaitai_struct_formats/image') #sys.path.append('kaitai_struct_formats/log') #sys.path.append('kaitai_struct_formats/machine_code') #sys.path.append('kaitai_struct_formats/media') #sys.path.append('kaitai_struct_formats/network') #sys.path.append('kaitai_struct_formats/scientific') #sys.path.append('kaitai_struct_formats/security') #sys.path.append('kaitai_struct_formats/serialization') #sys.path.append('kaitai_struct_formats/windows') #------------------------------------------------------------------------------ # id and parse #------------------------------------------------------------------------------ def id_data(data): if len(data) < 16: return None if data[0:4] == "\x7fELF": return 'elf' if data[0:4] in ['\xfe\xed\xfa\xce', '\xce\xfa\xed\xfe', '\xfe\xed\xfa\xcf', '\xcf\xfa\xed\xfe']: return 'macho' if data[0:2] == 'MZ': return 'pe' if data[0:8] == '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a': return 'png' if data[2:11] == '0xFF\xe0\x00\x10JFIF\x00': return 'jpeg' if data[0:6] == 'GIF89a': return 'gif' 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 .kaitai_struct_formats.executable.elf import Elf return Elf elif ftype == 'macho': from .kaitai_struct_formats.executable.mach_o import MachO return MachO elif ftype == 'pe': from .kaitai_struct_formats.executable.microsoft_pe import MicrosoftPe return MicrosoftPe elif ftype == 'png': from .kaitai_struct_formats.image.png import Png return Png elif ftype == 'jpeg': from .kaitai_struct_formats.image.jpeg import Jpeg return Jpeg elif ftype == 'gif': from .kaitai_struct_formats.image.gif import Gif return Gif 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('