summaryrefslogtreecommitdiff
path: root/python/examples/kaitai/view.py
diff options
context:
space:
mode:
authorXusheng <xusheng@vector35.com>2021-07-01 12:34:30 +0800
committerXusheng Li <xushengli@protonmail.com>2021-07-05 10:11:47 +0800
commit13ff200ba134b8704f37eca99c42b70dab0d17dc (patch)
tree876b3db3c53f704b31152aeaede1f2edc9d27b24 /python/examples/kaitai/view.py
parent4039a800349dcd587833d3e27d9137e4649818f4 (diff)
Remove snippet and kaitai plugin. Note their individual links.
Diffstat (limited to 'python/examples/kaitai/view.py')
-rw-r--r--python/examples/kaitai/view.py367
1 files changed, 0 insertions, 367 deletions
diff --git a/python/examples/kaitai/view.py b/python/examples/kaitai/view.py
deleted file mode 100644
index fa2d458f..00000000
--- a/python/examples/kaitai/view.py
+++ /dev/null
@@ -1,367 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# python stuff
-import io
-import os
-import sys
-import types
-import traceback
-
-# binja stuff
-from binaryninja import log
-from binaryninja import binaryview
-from binaryninja.settings import Settings
-from binaryninja import _binaryninjacore as core
-from binaryninjaui import View, ViewType, ViewFrame, UIContext, HexEditor, getMonospaceFont
-
-from PySide2.QtCore import Qt
-from PySide2.QtWidgets import QScrollArea, QWidget, QVBoxLayout, QGroupBox, QTreeWidget, QTreeWidgetItem, QLineEdit, QHeaderView, QSplitter
-
-from . import menu
-
-if sys.version_info[0] == 2:
- import kshelpers
-else:
- from . import kshelpers
-
-class MyQTreeWidget(QTreeWidget):
- def __init__(self):
- QTreeWidget.__init__(self)
-
- self.oldWidth = 0
- self.oldHeight = 0
-
- self.queueInitialPresentation = False
-
- def resizeEvent(self, event):
- QTreeWidget.resizeEvent(self, event)
-
- if self.queueInitialPresentation:
- self.initialPresentation()
-
- self.scrollTo(self.currentIndex())
-
- newWidth = self.width()
- newHeight = self.height()
-
- #log.log_debug('QTreeWidget resizeEvent(), now I\'m %dx%d' % (newWidth, newHeight))
-
- if newWidth == self.oldWidth and newHeight == self.oldHeight:
- return
-
- self.oldWidth = newWidth
- self.oldHeight = newHeight
-
- self.setColumnWidthsNicely()
-
- def initialPresentation(self):
- self.expandNicely()
-
- if self.topLevelItemCount():
- self.topLevelItem(0).setSelected(True)
-
- self.queueInitialPresentation = False
-
- def setColumnWidthsNicely(self):
- width = self.width()
- self.setColumnWidth(0, .6*width)
- self.setColumnWidth(1, .2*width)
- self.setColumnWidth(2, .1*width)
- self.setColumnWidth(3, .1*width)
-
- def expandNicely(self):
- visibleRows = self.topLevelItemCount()
- if not visibleRows:
- return
-
- height = self.height()
- rowHeight = self.visualItemRect(self.topLevelItem(0)).height()
- rowCapacity = height / rowHeight
-
- queue = []
- for i in range(visibleRows):
- queue.append(self.topLevelItem(i))
-
- while visibleRows < rowCapacity:
- #log.log_debug('visibleRows=%d, rowCapacity=%d len(queue)=%d' % (visibleRows, rowCapacity, len(queue)))
- if not queue:
- break
-
- item = queue[0]
- queue = queue[1:]
-
- if not item.isExpanded():
- item.setExpanded(True)
- for i in range(item.childCount()):
- queue.append(item.child(i))
- visibleRows += 1
-
-class KaitaiView(QScrollArea, View):
- def __init__(self, parent, binaryView):
- QScrollArea.__init__(self, parent)
-
- View.__init__(self)
- View.setBinaryDataNavigable(self, True)
- self.setupView(self)
-
- # BinaryViewType
- self.binaryView = binaryView
-
- self.rootSelectionStart = 0
- self.rootSelectionEnd = 1
-
- self.ioRoot = None
- self.ioCurrent = None
-
- # top half = treeWidget + structPath
- self.treeWidget = MyQTreeWidget()
- self.treeWidget.setColumnCount(4)
- self.treeWidget.setHeaderLabels(['label','value','start','end'])
- self.treeWidget.itemSelectionChanged.connect(self.onTreeSelect)
-
- self.structPath = QLineEdit("root")
- self.structPath.setReadOnly(True)
-
- topHalf = QWidget(self)
- layout = QVBoxLayout()
- layout.addWidget(self.treeWidget)
- layout.addWidget(self.structPath)
- topHalf.setLayout(layout)
-
- # bottom half = hexWidget
- self.hexWidget = HexEditor(binaryView, ViewFrame.viewFrameForWidget(self), 0)
-
- # splitter = top half, bottom half
- self.splitter = QSplitter(self)
- self.splitter.setOrientation(Qt.Vertical)
- self.splitter.addWidget(topHalf)
- self.splitter.addWidget(self.hexWidget)
-
- self.setWidgetResizable(True)
- self.setWidget(self.splitter)
-
- self.kaitaiParse()
-
- # parse the file using Kaitai, construct the TreeWidget
- def kaitaiParse(self, ksModuleName=None):
- log.log_debug('kaitaiParse() with len(bv)=%d and bv.file.filename=%s' % (len(self.binaryView), self.binaryView.file.filename))
-
- if len(self.binaryView) == 0:
- return
-
- kaitaiIO = kshelpers.KaitaiBinaryViewIO(self.binaryView)
- parsed = kshelpers.parseIo(kaitaiIO, ksModuleName)
- if not parsed:
- return
-
- # it SEEMS as if parsing is finished at this moment, but some parsing
- # is postponed until attributes are accessed, so we must try/catch here
- tree = None
- if True:
- try:
- tree = kshelpers.buildQtree(parsed)
- except Exception as e:
- log.log_error('kaitai module %s threw exception, check file type' % ksModuleName)
- tree = None
- else:
- tree = kshelpers.buildQtree(parsed)
-
- if not tree:
- return
-
- self.ioRoot = tree.ksobj._io
- self.ioCurrent = tree.ksobj._io
-
- self.treeWidget.clear()
- self.treeWidget.setSortingEnabled(False) # temporarily, for efficiency
- # two options with how we create the hierarchy
- if False:
- # treat root as top level "file" container
- tree.setLabel('file')
- tree.setValue(None)
- tree.setStart(0)
- tree.setEnd(0)
- self.treeWidget.insertTopLevelItem(0, tree)
- else:
- # add root's children as top level items
- self.treeWidget.insertTopLevelItems(0, tree.takeChildren())
-
- # enable sorting
- self.treeWidget.setSortingEnabled(True)
- self.treeWidget.sortByColumn(2, Qt.AscendingOrder)
-
- # TODO: select first item, maybe expand a few things
- self.rootSelectionStart = 0
- self.rootSelectionEnd = 1
-
- self.treeWidget.setUniformRowHeights(True)
- self.treeWidget.queueInitialPresentation = True
-
-
- # binja callbacks
- def getData(self):
- return self.binaryView
-
- def getStart(self):
- result = self.binaryView.start
- #log.log_debug('getStart() returning '+str(result))
- return result
-
- def getEnd(self):
- result = self.binaryView.end
- #log.log_debug('getEnd() returning '+str(result))
- return result
-
- def getLength(self):
- result = len(self.binaryView)
- #log.log_debug('getLength() returning '+str(result))
- return result
-
- def getCurrentOffset(self):
- result = self.rootSelectionStart + int((self.rootSelectionEnd - self.rootSelectionStart)/2)
- #result = self.rootSelectionStart
- #log.log_debug('getCurrentOffset() returning '+str(result))
- return result
-
- def getSelectionOffsets(self):
- result = None
- if self.hexWidget:
- result = self.hexWidget.getSelectionOffsets()
- else:
- result = (self.rootSelectionStart, self.rootSelectionStart)
- #log.log_debug('getSelectionOffsets() returning '+str(result))
- return result
-
- def setCurrentOffset(self, offset):
- #log.log_debug('setCurrentOffset(0x%X)' % offset)
- self.rootSelectionStart = offset
- UIContext.updateStatus(True)
-
- def getFont(self):
- return binaryninjaui.getMonospaceFont(self)
-
- def navigate(self, addr):
- self.rootSelectionStart = addr
- self.rootSelectionEnd = addr+1
- self.hexWidget.setSelectionRange(addr, addr+1)
- return True
-
- def navigateToFileOffset(self, offset):
- #log.log_debug('navigateToFileOffset()')
- return False
-
- def onTreeSelect(self, wtf=None):
- # get KaitaiTreeWidgetItem
- items = self.treeWidget.selectedItems()
- if not items or len(items)<1:
- return
- item = items[0]
-
- # build path, inform user
- structPath = item.label
- itemTmp = item
- while itemTmp.parent():
- itemTmp = itemTmp.parent()
- label = itemTmp.label
- if label.startswith('_m_'):
- label = label[3:]
- structPath = label + '.' + structPath
- self.structPath.setText('root.' + structPath)
-
- #
- (start, end) = (item.start, item.end)
- if start == None or end == None:
- return
-
- # determine current IO we're in (the Kaitai input/output abstraction)
- _io = None
- # if the tree item is linked to a KaitaiNode, simply read the IO
- if item.ksobj:
- _io = item.ksobj._parent._io
- else:
- # else we're a leaf
- parent = item.parent()
- if parent:
- # a leaf with a parent -> read parent's IO
- _io = parent.ksobj._io
- else:
- # a leaf without a parent -> we must be at root -> use root IO
- _io = self.ioRoot
-
- # if the selection is in the root view, store the interval so that upon
- # getCurrentOffset() callback, we return the middle and feature map is
- # updated
- if _io == self.ioRoot:
- self.rootSelectionStart = start
- self.rootSelectionEnd = end
-
- # current kaitai object is on a different io? then swap HexEditor
- if _io != self.ioCurrent:
- # delete old view
- self.hexWidget.hide()
- self.hexWidget.setParent(None)
- self.hexWidget.deleteLater()
- self.hexWidget = None
-
- # if it's the original file IO, wrap the already-open file binary view
- if _io == self.ioRoot:
- self.hexWidget = HexEditor(self.binaryView, ViewFrame.viewFrameForWidget(self), 0)
- # otherwise delete old view, create a temporary view
- else:
- # create new view
- length = _io.size()
- _io.seek(0)
- data = _io.read_bytes(length)
- bv = binaryview.BinaryView.new(data)
- self.hexWidget = HexEditor(bv, ViewFrame.viewFrameForWidget(self), 0)
-
- self.splitter.addWidget(self.hexWidget)
- self.ioCurrent = _io
-
- # now position selection in whatever HexEditor is current
- #log.log_debug('selecting to [0x%X, 0x%X)' % (start, end))
- self.hexWidget.setSelectionRange(start, end)
-
- # set hex group title to reflect current selection
- #self.hexGroup.setTitle('Hex View @ [0x%X, 0x%X)' % (start, end))
-
- def getStatusBarWidget(self):
- return menu.KaitaiStatusBarWidget(self)
-
-class KaitaiViewType(ViewType):
- def __init__(self):
- super(KaitaiViewType, self).__init__("Kaitai", "Kaitai")
-
- # binaryView: BinaryView
- def getPriority(self, binaryView, filename):
- #return 100
- #log.log_debug('len(bv)=0x%X executable=%d bytes=%s' % (len(binaryView), binaryView.executable, repr(binaryView.read(0,4))))
-
- # executable means the view is mapped like an OS loader would load an executable (eg: view=ELF)
- # !executable means executable image is not mapped (eg: view=Raw) (or something like .png is loaded)
- if binaryView.executable:
- return 0
-
- if binaryView.start != 0:
- return 1
-
- dataSample = binaryView.read(0, 16)
- if len(dataSample) != 16:
- return 1
-
- # if we don't recognize it, return 0
- ksModuleName = kshelpers.idData(dataSample, len(binaryView))
- if not ksModuleName:
- return 1
-
- # for executables, yield to triage (25)
- if ksModuleName in ['elf', 'microsoft_pe', 'mach_o']:
- return 24
-
- #
- return 100
-
- def create(self, binaryView, view_frame):
- return KaitaiView(view_frame, binaryView)
-
-ViewType.registerViewType(KaitaiViewType())