Esempio n. 1
0
class DebugConsoleWidget(QWidget, DockContextHandler):
	def __init__(self, parent, name, data):
		if not type(data) == binaryninja.binaryview.BinaryView:
			raise Exception('expected widget data to be a BinaryView')

		self.bv = data

		QWidget.__init__(self, parent)
		DockContextHandler.__init__(self, self, name)
		self.actionHandler = UIActionHandler()
		self.actionHandler.setupActionHandler(self)

		layout = QVBoxLayout()
		self.consoleText = QTextEdit(self)
		self.consoleText.setReadOnly(True)
		layout.addWidget(self.consoleText, 1)

		inputLayout = QHBoxLayout()
		inputLayout.setContentsMargins(4, 4, 4, 4)
		
		promptLayout = QVBoxLayout()
		promptLayout.setContentsMargins(0, 5, 0, 5)

		inputLayout.addLayout(promptLayout)

		self.consoleEntry = QLineEdit(self)
		inputLayout.addWidget(self.consoleEntry, 1)

		label = QLabel("lldb>>> ", self)
		label.setFont(getMonospaceFont(self))
		promptLayout.addWidget(label)
		promptLayout.addStretch(1)

		self.consoleEntry.returnPressed.connect(lambda: self.consoleText.append("TODO"))

		layout.addLayout(inputLayout)
		layout.setContentsMargins(0, 0, 0, 0)
		layout.setSpacing(0)
		self.setLayout(layout)

	def sizeHint(self):
		return QSize(300, 100)

	#--------------------------------------------------------------------------
	# callbacks to us api/ui/dockhandler.h
	#--------------------------------------------------------------------------
	def notifyOffsetChanged(self, offset):
		pass

	def notifyViewChanged(self, view_frame):
		pass

	def contextMenuEvent(self, event):
		self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

	def shouldBeVisible(self, view_frame):
		if view_frame is None:
			return False
		else:
			return True
class HelloDockWidget(QWidget, DockContextHandler):
    def __init__(self, parent, name, data):
        global instance_id
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)
        offset_layout = QHBoxLayout()
        offset_layout.addWidget(QLabel("Offset: "))
        self.offset = QLabel(hex(0))
        offset_layout.addWidget(self.offset)
        offset_layout.setAlignment(QtCore.Qt.AlignCenter)
        datatype_layout = QHBoxLayout()
        datatype_layout.addWidget(QLabel("Data Type: "))
        self.datatype = QLabel("")
        datatype_layout.addWidget(self.datatype)
        datatype_layout.setAlignment(QtCore.Qt.AlignCenter)
        layout = QVBoxLayout()
        title = QLabel(name, self)
        title.setAlignment(QtCore.Qt.AlignCenter)
        instance = QLabel("Instance: " + str(instance_id), self)
        instance.setAlignment(QtCore.Qt.AlignCenter)
        layout.addStretch()
        layout.addWidget(title)
        layout.addWidget(instance)
        layout.addLayout(datatype_layout)
        layout.addLayout(offset_layout)
        layout.addStretch()
        self.setLayout(layout)
        instance_id += 1
        self.data = data

    def notifyOffsetChanged(self, offset):
        self.offset.setText(hex(offset))

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True

    def notifyViewChanged(self, view_frame):
        if view_frame is None:
            self.datatype.setText("None")
            self.data = None
        else:
            self.datatype.setText(view_frame.getCurrentView())
            view = view_frame.getCurrentViewInterface()
            self.data = view.getData()

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

    @staticmethod
    def create_widget(name, parent, data=None):
        return HelloDockWidget(parent, name, data)
Esempio n. 3
0
class DebugBreakpointsWidget(QWidget, DockContextHandler):
	def __init__(self, parent, name, data):
		if not type(data) == binaryninja.binaryview.BinaryView:
			raise Exception('expected widget data to be a BinaryView')

		self.bv = data
		
		QWidget.__init__(self, parent)
		DockContextHandler.__init__(self, self, name)
		self.actionHandler = UIActionHandler()
		self.actionHandler.setupActionHandler(self)

		self.table = QTableView(self)
		self.model = DebugBreakpointsListModel(self.table, data)
		self.table.setModel(self.model)

		self.item_delegate = DebugBreakpointsItemDelegate(self)
		self.table.setItemDelegate(self.item_delegate)

		# self.table.setSortingEnabled(True)
		self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
		self.table.setSelectionMode(QAbstractItemView.ExtendedSelection)

		self.table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
		self.table.verticalHeader().setVisible(False)

		self.table.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
		self.table.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)

		self.table.resizeColumnsToContents()
		self.table.resizeRowsToContents()

		for i in range(len(self.model.columns)):
			self.table.setColumnWidth(i, self.item_delegate.sizeHint(self.table.viewOptions(), self.model.index(-1, i, QModelIndex())).width())
		self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)

		layout = QVBoxLayout()
		layout.setContentsMargins(0, 0, 0, 0)
		layout.setSpacing(0)
		layout.addWidget(self.table)
		self.setLayout(layout)

	def notifyOffsetChanged(self, offset):
		pass

	def notifyBreakpointsChanged(self, new_rows):
		self.model.update_rows(new_rows)

	def contextMenuEvent(self, event):
		self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

	def shouldBeVisible(self, view_frame):
		if view_frame is None:
			return False
		else:
			return True
Esempio n. 4
0
    class DockableWidget(QtWidgets.QWidget, DockContextHandler):
        """
        A dockable Qt widget for Binary Ninja.
        """
        def __init__(self, parent, name):
            QtWidgets.QWidget.__init__(self, parent)
            DockContextHandler.__init__(self, self, name)

            self.actionHandler = UIActionHandler()
            self.actionHandler.setupActionHandler(self)

            self._active_view = None
            self._visible_for_view = collections.defaultdict(lambda: False)

        @property
        def visible(self):
            return self._visible_for_view[self._active_view]

        @visible.setter
        def visible(self, is_visible):
            self._visible_for_view[self._active_view] = is_visible

        def shouldBeVisible(self, view_frame):
            if not view_frame:
                return False

            if USING_PYSIDE6:
                import shiboken6 as shiboken
            else:
                import shiboken2 as shiboken

            vf_ptr = shiboken.getCppPointer(view_frame)[0]
            return self._visible_for_view[vf_ptr]

        def notifyVisibilityChanged(self, is_visible):
            self.visible = is_visible

        def notifyViewChanged(self, view_frame):
            if not view_frame:
                self._active_view = None
                return

            if USING_PYSIDE6:
                import shiboken6 as shiboken
            else:
                import shiboken2 as shiboken

            self._active_view = shiboken.getCppPointer(view_frame)[0]

            if self.visible:
                dock_handler = DockHandler.getActiveDockHandler()
                dock_handler.setVisible(self.m_name, True)
class HelloGlobalAreaWidget(GlobalAreaWidget):
	def __init__(self, name):
		global instance_id
		GlobalAreaWidget.__init__(self, name)
		self.actionHandler = UIActionHandler()
		self.actionHandler.setupActionHandler(self)
		offset_layout = QHBoxLayout()
		offset_layout.addWidget(QLabel("Offset: "))
		self.offset = QLabel(hex(0))
		offset_layout.addWidget(self.offset)
		offset_layout.setAlignment(QtCore.Qt.AlignCenter)
		datatype_layout = QHBoxLayout()
		datatype_layout.addWidget(QLabel("Data Type: "))
		self.datatype = QLabel("")
		datatype_layout.addWidget(self.datatype)
		datatype_layout.setAlignment(QtCore.Qt.AlignCenter)
		layout = QVBoxLayout()
		title = QLabel(name, self)
		title.setAlignment(QtCore.Qt.AlignCenter)
		instance = QLabel("Instance: " + str(instance_id), self)
		instance.setAlignment(QtCore.Qt.AlignCenter)
		layout.addStretch()
		layout.addWidget(title)
		layout.addWidget(instance)
		layout.addLayout(datatype_layout)
		layout.addLayout(offset_layout)
		layout.addStretch()
		self.setLayout(layout)
		instance_id += 1
		self.data = None

	def notifyOffsetChanged(self, offset):
		self.offset.setText(hex(offset))

	def notifyViewChanged(self, view_frame):
		if view_frame is None:
			self.datatype.setText("None")
			self.data = None
		else:
			self.datatype.setText(view_frame.getCurrentView())
			view = view_frame.getCurrentViewInterface()
			self.data = view.getData()

	def contextMenuEvent(self, event):
		self.m_contextMenuManager.show(self.m_menu, self.actionHandler)
Esempio n. 6
0
class DebugMemoryWidget(QWidget, DockContextHandler):
    def __init__(self, parent, name, data):
        if not type(data) == binaryninja.binaryview.BinaryView:
            raise Exception('expected widget data to be a BinaryView')

        self.bv = data

        memory_view = binjaplug.get_state(data).memory_view

        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)

        self.editor = LinearView(memory_view,
                                 ViewFrame.viewFrameForWidget(self))
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(self.editor)
        self.setLayout(layout)

    def notifyOffsetChanged(self, offset):
        pass

    def notifyMemoryChanged(self):
        adapter = binjaplug.get_state(self.bv).adapter

        # Refresh the editor
        if adapter is None:
            self.editor.navigate(0)
            return

        self.editor.navigate(adapter.reg_read('rsp'))

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True
Esempio n. 7
0
class HyaraDockWidget(QtWidgets.QWidget, DockContextHandler):
    def __init__(self, parent, name, data):
        QtWidgets.QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)

        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        self.HyaraBinaryNinja = HyaraBinaryNinja()
        self.setLayout(self.HyaraBinaryNinja.layout)

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True

    def notifyViewChanged(self, view_frame):
        pass

    @staticmethod
    def create_widget(name, parent, data=None):
        return HyaraDockWidget(parent, name, data)
Esempio n. 8
0
class JumpOverview(QWidget, DockContextHandler):
    def __init__(self, parent, name):
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        font = getMonospaceFont(self)
        fm = QFontMetricsF(font)

        table_layout = QVBoxLayout()
        self.table = QTableView()
        self.table.setFont(font)
        self.table.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.table.verticalHeader().hide()
        self.table.verticalHeader().setSectionResizeMode(QHeaderView.Fixed)
        self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)

        # sorting
        self.table.setSortingEnabled(True)
        self.table.horizontalHeader().setSortIndicator(0, QtCore.Qt.AscendingOrder)

        data = []
        self.model = TableModel(data)
        self.table.setModel(self.model)
        table_layout.addWidget(self.table)

        layout = QVBoxLayout()
        layout.addLayout(table_layout)
        self.setLayout(layout)

        # init double click action
        self.table.doubleClicked.connect(self._ui_entry_double_click)
        # init right click menu
        self.ctx_menu = QMenu()
        self._action_patch = QAction("Invert Branch", None)
        self.ctx_menu.addAction(self._action_patch)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self._ui_table_ctx_menu_handler)

        self.bv = None
        self.filename = None
        self.do_sync = True

    def _ui_entry_double_click(self, index):
        self.navigate_to_addr(index)

    def _ui_table_ctx_menu_handler(self, pos):
        action = self.ctx_menu.exec_(self.table.viewport().mapToGlobal(pos))
        if not action:
            return

        rows = self.table.selectionModel().selectedRows()
        indices = [i.row() for i in rows]
        if len(indices) > 0 and action == self._action_patch:
            for i in indices:
                addr = int(self.model._data[i][0], 16)
                if self.patch(addr):
                    if not addr in self.model._patched:
                        self.model._patched.append(addr)
                    else:
                        self.model._patched.remove(addr)
                    self.model.data(rows[i], role=QtCore.Qt.BackgroundRole)
                else:
                    interaction.show_message_box(
                        "Patching Error",
                        "This architecture does not seem to support auto-patching yet.",
                        icon=MessageBoxIcon.ErrorIcon,
                    )

    def navigate_to_addr(self, index):
        addr = int(self.model._data[index.row()][0], 16)
        dh = DockHandler.getActiveDockHandler()
        vf = dh.getViewFrame()
        vi = vf.getCurrentViewInterface()
        vi.navigate(addr)

    def patch(self, addr):
        if self.bv is None:
            return False
        try:
            return self.bv.invert_branch(addr)
        except:
            return False

    def notifyViewChanged(self, view_frame):
        if view_frame is None:
            pass
        else:
            self.bv = view_frame.actionContext().binaryView
            self.filename = self.bv.file.original_filename

    @staticmethod
    def create_widget(name, parent, data=None):
        return JumpOverview(parent, name)
Esempio n. 9
0
class TriageFilePicker(QWidget):
	def __init__(self, context):
		super(TriageFilePicker, self).__init__()
		self.context = context
		self.actionHandler = UIActionHandler()
		self.actionHandler.setupActionHandler(self)
		self.contextMenu = Menu()
		self.contextMenuManager = ContextMenuManager(self)

		layout = QVBoxLayout()
		layout.setContentsMargins(0, 0, 0, 0)

		self.model = QFileSystemModel()
		self.model.setRootPath("")
		self.model.setFilter(QDir.AllEntries | QDir.Hidden | QDir.System)
		self.tree = QTreeView(self)
		self.tree.setModel(self.model)
		self.tree.setSelectionMode(QAbstractItemView.ExtendedSelection)
		self.tree.setColumnWidth(0, 500)
		layout.addWidget(self.tree, 1)

		self.setLayout(layout)

		self.tree.doubleClicked.connect(self.onDoubleClick)

		recentFile = QSettings().value("triage/recentFile", os.path.expanduser("~"))
		while len(recentFile) > 0:
			f = self.model.index(recentFile)
			if f.isValid():
				self.tree.scrollTo(f)
				self.tree.setExpanded(f, True)
				break
			parentDir = os.path.dirname(recentFile)
			if parentDir == recentFile:
				break
			recentFile = parentDir

		self.actionHandler.bindAction("Open Selected Files", UIAction(
			lambda context: self.openSelectedFiles(),
			lambda context: self.areFilesSelected()))
		self.contextMenu.addAction("Open Selected Files", "Open")

	def contextMenuEvent(self, event):
		self.contextMenuManager.show(self.contextMenu, self.actionHandler)

	def onDoubleClick(self, index):
		self.openSelectedFiles()

	def openSelectedFiles(self):
		failedToOpen = []
		files = set()

		for index in self.tree.selectionModel().selectedIndexes():
			if self.model.fileInfo(index).isFile():
				files.add(self.model.fileInfo(index).absoluteFilePath())

		for filename in files:
				QSettings().setValue("triage/recentFile", filename)

				f = FileContext.openFilename(filename)
				if not f:
					failedToOpen.append(filename)
					continue

				f.createBinaryViews()
				for data in f.getAllDataViews():
					Settings().set_string("analysis.mode", Settings().get_string("triage.analysisMode"), data)
					Settings().set_bool("triage.preferSummaryView", True, data)
					if data.view_type != "Raw":
						linearSweepMode = Settings().get_string("triage.linearSweep")
						if linearSweepMode == "none":
							Settings().set_bool("analysis.linearSweep.autorun", False, data)
						elif linearSweepMode == "partial":
							Settings().set_bool("analysis.linearSweep.autorun", True, data)
							Settings().set_bool("analysis.linearSweep.controlFlowGraph", False, data)
						elif linearSweepMode == "full":
							Settings().set_bool("analysis.linearSweep.autorun", True, data)
							Settings().set_bool("analysis.linearSweep.controlFlowGraph", True, data)

				self.context.openFileContext(f)

		if len(failedToOpen) > 0:
			QMessageBox.critical(self, "Error", "Unable to open:\n" + "\n".join(failedToOpen))

	def areFilesSelected(self):
		return self.tree.selectionModel().hasSelection()
Esempio n. 10
0
class DebugModulesWidget(QWidget, DockContextHandler):
    def __init__(self, parent, name, data):
        if not type(data) == binaryninja.binaryview.BinaryView:
            raise Exception('expected widget data to be a BinaryView')

        self.bv = data

        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        self.table = QTableView(self)
        self.model = DebugModulesListModel(self.table, data)
        self.table.setModel(self.model)

        self.item_delegate = DebugModulesItemDelegate(self)
        self.table.setItemDelegate(self.item_delegate)

        # self.table.setSortingEnabled(True)
        self.table.setSelectionBehavior(
            QAbstractItemView.SelectionBehavior.SelectRows)
        self.table.setSelectionMode(QAbstractItemView.ExtendedSelection)

        self.table.verticalHeader().setSectionResizeMode(
            QHeaderView.ResizeToContents)
        self.table.verticalHeader().setVisible(False)

        self.table.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
        self.table.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)

        self.table.resizeColumnsToContents()
        self.table.resizeRowsToContents()

        for i in range(len(self.model.columns)):
            self.table.setColumnWidth(
                i,
                self.item_delegate.sizeHint(
                    self.table.viewOptions(),
                    self.model.index(-1, i, QModelIndex())).width())

        update_layout = QHBoxLayout()
        update_layout.setContentsMargins(0, 0, 0, 0)

        update_label = QLabel("Data is Stale")
        update_button = QPushButton("Refresh")
        update_button.clicked.connect(lambda: self.refresh())

        update_layout.addWidget(update_label)
        update_layout.addStretch(1)
        update_layout.addWidget(update_button)

        self.update_box = QWidget()
        self.update_box.setLayout(update_layout)

        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(0)
        self.layout.addWidget(self.table)
        self.setLayout(self.layout)

    def notifyOffsetChanged(self, offset):
        pass

    def refresh(self):
        debug_state = binjaplug.get_state(self.bv)
        debug_state.ui.update_modules()

    def notifyModulesChanged(self, new_modules):
        self.model.update_rows(new_modules)
        self.table.resizeColumnsToContents()
        self.layout.removeWidget(self.update_box)
        self.update_box.setVisible(False)

    def mark_dirty(self):
        self.layout.addWidget(self.update_box)
        self.update_box.setVisible(True)

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True
Esempio n. 11
0
class DebugConsoleWidget(QWidget, DockContextHandler):
    def __init__(self, parent, name, data):
        if not type(data) == binaryninja.binaryview.BinaryView:
            raise Exception('expected widget data to be a BinaryView')

        self.bv = data

        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        layout = QVBoxLayout()
        self.consoleText = QTextEdit(self)
        self.consoleText.setReadOnly(True)
        self.consoleText.setFont(getMonospaceFont(self))
        layout.addWidget(self.consoleText, 1)

        inputLayout = QHBoxLayout()
        inputLayout.setContentsMargins(4, 4, 4, 4)

        promptLayout = QVBoxLayout()
        promptLayout.setContentsMargins(0, 5, 0, 5)

        inputLayout.addLayout(promptLayout)

        self.consoleEntry = QLineEdit(self)
        inputLayout.addWidget(self.consoleEntry, 1)

        label = QLabel("dbg>>> ", self)
        label.setFont(getMonospaceFont(self))
        promptLayout.addWidget(label)
        promptLayout.addStretch(1)

        self.consoleEntry.returnPressed.connect(lambda: self.sendLine())

        layout.addLayout(inputLayout)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.setLayout(layout)

    def sizeHint(self):
        return QSize(300, 100)

    def sendLine(self):
        line = self.consoleEntry.text()
        self.consoleEntry.setText("")

        debug_state = binjaplug.get_state(self.bv)
        try:
            debug_state.send_console_input(line)
        except Exception as e:
            self.notifyStdout("Error sending input: {} {}\n".format(
                type(e).__name__, ' '.join(e.args)))

    def notifyStdout(self, line):
        self.consoleText.insertPlainText(line)

        # Scroll down
        cursor = self.consoleText.textCursor()
        cursor.clearSelection()
        cursor.movePosition(QTextCursor.End)
        self.consoleText.setTextCursor(cursor)

    #--------------------------------------------------------------------------
    # callbacks to us api/ui/dockhandler.h
    #--------------------------------------------------------------------------
    def notifyOffsetChanged(self, offset):
        pass

    def notifyViewChanged(self, view_frame):
        pass

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True
Esempio n. 12
0
class BBViewerWidget(QWidget, DockContextHandler):
    PER_PAGE_COUNT = 50

    def __init__(self, name, parent, bv, db):
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)

        self.hitcounts = []
        self.orig_hitcounts = []
        self.db = db
        self.bv = bv
        self.descending = True
        self.highlight = False
        self.current_page = 0

        self.hitcounts = self.db.get_hitcounts()
        self.orig_hitcounts = [e for e in self.hitcounts]

        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        vlayout = QVBoxLayout()

        # Top label
        self.hitcount_counter = QLabel("Basic block count: {}".format(
            self.db.hitcount_count))
        self.hitcount_counter.setAlignment(Qt.AlignLeft | Qt.AlignTop)

        # BB Hitcount table
        self.hit_table = QTableWidget(0, 3)
        self.hit_table.setHorizontalHeaderLabels(
            ["Address", "Hitcount", "Function"])
        self.hit_table.verticalHeader().hide()
        self.hit_table.horizontalHeader().setStretchLastSection(True)
        self.hit_table.itemDoubleClicked.connect(self._cb_table)
        self._render_page()

        # Option buttons
        optionslayout = QHBoxLayout()
        optionsbox = QGroupBox("Options")

        ascending_checkbox = QCheckBox("Sort ascending")
        highlight_checkbox = QCheckBox("Highlight basic blocks")

        ascending_checkbox.stateChanged.connect(self._cb_ascending)
        highlight_checkbox.stateChanged.connect(self._cb_highlight)

        optionslayout.addWidget(ascending_checkbox)
        optionslayout.addWidget(highlight_checkbox)
        optionsbox.setLayout(optionslayout)

        # Diffing buttons
        diffinglayout = QHBoxLayout()
        diffingoptions = QGroupBox("Diffing")

        diffing_reset_button = QPushButton("Reset")
        diffing_diff_button = QPushButton("Difference")
        diffing_inter_button = QPushButton("Intersection")

        diffing_reset_button.clicked.connect(self._cb_diff_reset)
        diffing_diff_button.clicked.connect(self._cb_diff_diff)
        diffing_inter_button.clicked.connect(self._cb_diff_inter)

        diffinglayout.addWidget(diffing_diff_button)
        diffinglayout.addWidget(diffing_inter_button)
        diffinglayout.addWidget(diffing_reset_button)

        diffingoptions.setLayout(diffinglayout)

        # Bottom buttons for page change
        prevnextlayout = QHBoxLayout()
        self.back_button = QPushButton("<")
        self.next_button = QPushButton(">")
        self.page_count_label = QLabel("")
        self.page_count_label.setAlignment(Qt.AlignCenter)
        self._render_nav_line()

        self.back_button.clicked.connect(self._cb_prev_page)
        self.next_button.clicked.connect(self._cb_next_page)

        prevnextlayout.addWidget(self.back_button)
        prevnextlayout.addWidget(self.page_count_label)
        prevnextlayout.addWidget(self.next_button)

        vlayout.addWidget(self.hitcount_counter)
        vlayout.addWidget(self.hit_table)
        vlayout.addWidget(optionsbox)
        vlayout.addWidget(diffingoptions)
        vlayout.addLayout(prevnextlayout)

        self.setLayout(vlayout)

    def _cb_table(self, elem):
        addr = self.hitcounts[(BBViewerWidget.PER_PAGE_COUNT *
                               self.current_page) + elem.row()][0]
        self.bv.navigate(self.bv.view, addr)

    def _cb_ascending(self, elem):
        self.descending = not self.descending
        self.hitcounts = sorted(self.hitcounts,
                                key=lambda a: a[1],
                                reverse=self.descending)
        self.current_page = 0

        self._render_nav_line()
        self._render_page()

    def _cb_prev_page(self, elem):
        # We do not need to check as _render_nav_line disables the button at the limit
        self._render_nav_line()
        self.current_page -= 1
        self._render_nav_line()
        self._render_page()

    def _cb_next_page(self, elem):
        # We do not need to check as _render_nav_line disables the button at the limit
        self._render_nav_line()
        self.current_page += 1
        self._render_nav_line()
        self._render_page()

    def _cb_highlight(self, elem):
        self.highlight = not self.highlight
        self._bb_highlight(self.highlight)

    def _cb_diff_reset(self, item):
        if self.highlight:
            self._bb_highlight(False)

        self.hitcounts = [e for e in self.orig_hitcounts]

        if self.highlight:
            self._bb_highlight(True)

        self.current_page = 0
        self.hitcount_counter.setText("Basic block count: {}".format(
            len(self.hitcounts)))
        self._render_nav_line()
        self._render_page()

    def _cb_diff_diff(self, item):
        new_db = _load_db(self.bv)

        if not new_db:
            return

        source_bbs = set([e[0] for e in self.hitcounts])
        new_bbs = set([e[0] for e in new_db.get_hitcounts()])
        result_bbs = source_bbs - new_bbs

        if self.highlight:
            self._bb_highlight(False)

        previous_count = len(self.hitcounts)
        self.hitcounts = list(
            filter(lambda e: e[0] in result_bbs, self.hitcounts))
        self.hitcount_counter.setText(
            "Basic block count: {} (previously {})".format(
                len(self.hitcounts), previous_count))

        if self.highlight:
            self._bb_highlight(True)

        self.current_page = 0
        self._render_nav_line()
        self._render_page()

    def _cb_diff_inter(self, item):
        new_db = _load_db(self.bv)

        if not new_db:
            return

        source_bbs = set([e[0] for e in self.hitcounts])
        new_bbs = set([e[0] for e in new_db.get_hitcounts()])
        result_bbs = source_bbs & new_bbs

        if self.highlight:
            self._bb_highlight(False)

        previous_count = len(self.hitcounts)
        self.hitcounts = list(
            filter(lambda e: e[0] in result_bbs, self.hitcounts))
        self.hitcount_counter.setText(
            "Basic block count: {} (previously {})".format(
                len(self.hitcounts), previous_count))

        if self.highlight:
            self._bb_highlight(True)

        self.current_page = 0
        self._render_nav_line()
        self._render_page()

    def _render_nav_line(self):
        max_pages = math.ceil(
            len(self.hitcounts) / BBViewerWidget.PER_PAGE_COUNT)
        self.page_count_label.setText("Page: {} / {}".format(
            self.current_page + 1, max_pages))

        if self.current_page == 0:
            self.back_button.setEnabled(False)
        else:
            self.back_button.setEnabled(True)

        if self.current_page == max_pages - 1 or max_pages == 0:
            self.next_button.setEnabled(False)
        else:
            self.next_button.setEnabled(True)

    def _render_page(self):
        start = BBViewerWidget.PER_PAGE_COUNT * self.current_page
        hitcounts = self.hitcounts[start:start + BBViewerWidget.PER_PAGE_COUNT]

        self.hit_table.setRowCount(len(hitcounts))

        for i, e in enumerate(hitcounts):
            address_item = QTableWidgetItem("0x{:x}".format(e[0]))
            hitcount_item = QTableWidgetItem("{}".format(e[1]))
            name_item = QTableWidgetItem(_name_from_address(self.bv, e[0]))

            address_item.setFlags(Qt.ItemIsEnabled)
            hitcount_item.setFlags(Qt.ItemIsEditable)
            name_item.setFlags(Qt.ItemIsEditable)

            self.hit_table.setItem(i, 0, address_item)
            self.hit_table.setItem(i, 1, hitcount_item)
            self.hit_table.setItem(i, 2, name_item)

    def _bb_highlight(self, highlight):
        def delete_hitcount_str(input_str):
            if "(hitcount: " not in input_str:
                return input_str

            chk = input_str[input_str.find("(hitcount: "):]

            if ")\n" not in input_str:
                return input_str

            chk = chk[:input_str.find(")\n") + 2]

            return input_str.replace(chk, "")

        # (0, 255, 106)
        colorHighlight = HighlightColor(red=0, green=255, blue=106)

        for bbaddr, bbhitcount in self.hitcounts:
            bbs = self.bv.get_basic_blocks_at(bbaddr)

            if not bbs:
                print("Could not find basic block at address: 0x{:x}".format(
                    bbaddr))
                continue

            bb = bbs[0]
            fn = bb.function

            if not fn:
                print(
                    "Could not find function containing block at address: 0x{:x}"
                    .format(bbaddr))
                continue

            cur_comment = delete_hitcount_str(fn.get_comment_at(bbaddr))

            if highlight:
                bb.set_user_highlight(colorHighlight)
                fn.set_comment_at(
                    bbaddr,
                    "(hitcount: {})\n".format(bbhitcount) + cur_comment)
            else:
                bb.set_user_highlight(HighlightStandardColor.NoHighlightColor)
                fn.set_comment_at(bbaddr, cur_comment)
Esempio n. 13
0
class SyncDockWidget(QWidget, DockContextHandler):
    def __init__(self, parent, name, data):
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        status_layout = QHBoxLayout()
        status_layout.addWidget(QLabel('Status: '))
        self.status = QLabel('idle')
        status_layout.addWidget(self.status)
        status_layout.setAlignment(QtCore.Qt.AlignCenter)

        client_dbg_layout = QHBoxLayout()
        client_dbg_layout.addWidget(QLabel('Client debugger: '))
        self.client_dbg = QLabel('n/a')
        client_dbg_layout.addWidget(self.client_dbg)
        client_dbg_layout.setAlignment(QtCore.Qt.AlignCenter)

        client_pgm_layout = QHBoxLayout()
        client_pgm_layout.addWidget(QLabel('Client program: '))
        self.client_pgm = QLabel('n/a')
        client_pgm_layout.addWidget(self.client_pgm)
        client_pgm_layout.setAlignment(QtCore.Qt.AlignCenter)

        layout = QVBoxLayout()
        layout.addStretch()
        layout.addLayout(status_layout)
        layout.addLayout(client_dbg_layout)
        layout.addLayout(client_pgm_layout)
        layout.addStretch()
        self.setLayout(layout)

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

    def set_status(self, status):
        if status == SyncStatus.RUNNING:
            self.status.setStyleSheet('color: green')
        elif status == SyncStatus.ENABLED:
            self.status.setStyleSheet('color: blue')
        else:
            self.status.setStyleSheet('')

        self.status.setText(status)

    def set_connected(self, dialect):
        self.set_status(SyncStatus.RUNNING)
        self.client_dbg.setText(dialect)

    def set_program(self, pgm):
        self.client_pgm.setText(pgm)

    def reset_client(self):
        self.set_status(SyncStatus.ENABLED)
        self.client_pgm.setText('n/a')
        self.client_dbg.setText('n/a')

    def reset_status(self):
        self.set_status(SyncStatus.IDLE)
        self.client_pgm.setText('n/a')
        self.client_dbg.setText('n/a')

    @staticmethod
    def create_widget(name, parent, data=None):
        return SyncDockWidget(parent, name, data)
Esempio n. 14
0
class MemoryView(QWidget, DockContextHandler):
    def __init__(self, parent, name, data):
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)

        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)
        self.data = data
        self.parent = parent

        self.current_state = None
        self.arch = None
        self.address_start = None
        self.size = 512
        self.changes = set()
        self.tab_name = None

        self.symb_idx = 0

        self._layout = QVBoxLayout()
        self.button = QPushButton("Monitor Memory")
        self.button.clicked.connect(self.on_monitor_button_click)

        self.hexWidget = HexViewWidget(
            menu_handler=self.on_customContextMenuRequested)
        self.hexWidget.data_edited.connect(self._handle_data_edited)
        self.hexWidget.setEnabled(False)

        self._layout.addWidget(self.button)
        self._layout.addWidget(self.hexWidget)
        self._layout.setContentsMargins(0, 0, 0, 0)

        self.setMaximumWidth(self.hexWidget.optimal_width + 25)

        self.setLayout(self._layout)

    def _monitor_changes(self, address, size):
        if symbolic(address):
            self.changes.clear()
            self.changes.add(
                (self.address_start, self.address_start + self.size))
            return

        address = address.value
        if (self.address_start + self.size >= address
                and self.address_start <= address + size):
            to_monitor_min = max(self.address_start,
                                 address) - self.address_start
            to_monitor_max = min(self.address_start + self.size,
                                 address + size) - self.address_start
            self.changes.add((to_monitor_min, to_monitor_max))

    def on_monitor_button_click(self):
        if self.current_state is None:
            return

        address = get_int_input("Memory address", "Set Memory Monitor")
        if address is None: return

        self.hexWidget.setEnabled(True)
        self.address_start = address
        self.size = 512
        self.current_state.mem.register_store_hook(self._monitor_changes)
        self.update_mem(self.current_state)

    def init(self, arch, state):
        self.arch = arch
        self.tab_name = _normalize_tab_name(self.parent.getTabName())
        self.update_mem(state)

    def update_mem(self, state):
        self.current_state = state
        if self.address_start is None:
            return
        self.changes.clear()
        data = {}
        for i in range(self.size):
            if not state.mem.is_mapped(self.address_start + i):
                val = "__"
            else:
                b = state.mem.load(self.address_start + i, 1)
                val = ".."
                if isinstance(b, BVV):
                    val = "{:02x}".format(b.value)
            data[i] = val

        self.hexWidget.full_data_changed.emit(self.address_start, data,
                                              self.size)

    def update_mem_delta(self, state):
        self.current_state = state
        if self.address_start is None:
            return

        load_cache = {}
        for begin, end in self.changes:
            for i in range(begin, end):
                if not state.mem.is_mapped(self.address_start + i):
                    val = "__"
                else:
                    if i in load_cache:
                        b = load_cache[i]
                    else:
                        b = state.mem.load(self.address_start + i, 1)
                        load_cache[i] = b
                    val = ".."
                    if isinstance(b, BVV):
                        val = "{:02x}".format(b.value)
                self.hexWidget.single_data_changed.emit(i, val)
        # self.hexWidget.view.viewport().update()
        self.changes.clear()

    def reset(self):
        self.current_state = None
        self.address_start = None
        self.size = None
        self.hexWidget.full_data_changed.emit(0, {}, 0)

    def _handle_data_edited(self, offset, value):
        if not self.current_state or not self.address_start:
            return

        self.current_state.mem.store(self.address_start + offset,
                                     BVV(value, 8))
        # self.hexWidget.single_data_changed.emit(address - self.address_start, hex(value)[2:])

    def _show_reg_expression(self, address, expr):
        show_message_box("Expression at %s" % hex(address), str(expr.z3obj))

    def _evaluate_with_solver(self, address, expr):
        val = ""
        if not self.current_state.solver.symbolic(expr):
            new_expr = self.current_state.solver.evaluate(expr)
            self.current_state.mem.store(address, new_expr)
            self.changes.add(
                (address - self.address_start,
                 address - self.address_start + new_expr.size // 8))
            self.update_mem_delta(self.current_state)
            show_message_box("Expression at %s" % hex(address),
                             "The value was indeed concrete! State modified")
        else:
            val = self.current_state.solver.evaluate(expr).value
            show_message_box("Value at %s (with solver):" % hex(address),
                             hex(val))

    def _concretize(self, address, expr):
        new_expr = self.current_state.solver.evaluate(expr)
        res = get_choice_input(
            "Concretize *%s to %s?" % (hex(address), hex(new_expr.value)),
            "Concretize", ["Yes", "No"])
        if res == 0:
            self.current_state.mem.store(address, new_expr)
            self.current_state.solver.add_constraints(expr == new_expr)
            self.changes.add(
                (address - self.address_start,
                 address - self.address_start + new_expr.size // 8))
            self.update_mem_delta(self.current_state)

    def _make_symbolic(self, address, size):
        expr = BVS("symbol_injected_through_ui_mem_%d" % self.symb_idx,
                   size * 8)
        self.current_state.mem.store(address, expr)
        self.changes.add((address - self.address_start, size))
        self.symb_idx += 1
        self.update_mem_delta(self.current_state)

    def _copy_big_endian(self, expr):
        mime = QMimeData()
        mime.setText(hex(expr.value))
        QApplication.clipboard().setMimeData(mime)

    def _copy_little_endian(self, expr):
        mime = QMimeData()
        expr_bytes = split_bv_in_list(expr, 8)
        res = 0
        i = 0
        for el in reversed(expr_bytes):
            res += el.value << i * 8
            i += 1
        mime.setText(hex(res))
        QApplication.clipboard().setMimeData(mime)

    def _copy_string(self, expr):
        mime = QMimeData()
        expr_bytes = split_bv_in_list(expr, 8)
        res = ""
        for el in reversed(expr_bytes):
            res += chr(el.value) if el.value >= 32 and el.value <= 126 else "."

        mime.setText(res)
        QApplication.clipboard().setMimeData(mime)

    def _copy_binary(self, expr):
        mime = QMimeData()
        expr_bytes = split_bv_in_list(expr, 8)
        res = "\""
        for el in reversed(expr_bytes):
            res += "\\x{:02x}".format(el.value)
        res += "\""

        mime.setText(res)
        QApplication.clipboard().setMimeData(mime)

    @staticmethod
    def _condom(f, *pars):
        def g():
            f(*pars)

        return g

    def on_customContextMenuRequested(self, qpoint):
        if self.current_state is None:
            return
        menu = QMenu(self)
        index = self.hexWidget.view.indexAt(qpoint)

        sel_start = self.hexWidget._hsm.start
        sel_end = self.hexWidget._hsm.end
        if sel_start is None:
            return

        expr = self.current_state.mem.load(sel_start + self.address_start,
                                           sel_end - sel_start + 1)

        if symbolic(expr):
            a = menu.addAction("Show reg expression")
            a.triggered.connect(
                MemoryView._condom(self._show_reg_expression,
                                   sel_start + self.address_start, expr))
            a = menu.addAction("Evaluate with solver")
            a.triggered.connect(
                MemoryView._condom(self._evaluate_with_solver,
                                   sel_start + self.address_start, expr))
            a = menu.addAction("Concretize")
            a.triggered.connect(
                MemoryView._condom(self._concretize,
                                   sel_start + self.address_start, expr))
        else:
            a = menu.addAction("Make symbolic")
            a.triggered.connect(
                MemoryView._condom(self._make_symbolic,
                                   sel_start + self.address_start,
                                   sel_end - sel_start + 1))
            copy_menu = menu.addMenu("Copy...")
            a = copy_menu.addAction("Copy Little Endian")
            a.triggered.connect(
                MemoryView._condom(self._copy_little_endian, expr))
            a = copy_menu.addAction("Copy Big Endian")
            a.triggered.connect(MemoryView._condom(self._copy_big_endian,
                                                   expr))
            a = copy_menu.addAction("Copy String")
            a.triggered.connect(MemoryView._condom(self._copy_string, expr))
            a = copy_menu.addAction("Copy Binary")
            a.triggered.connect(MemoryView._condom(self._copy_binary, expr))

        return menu

    def notifyOffsetChanged(self, offset):
        pass

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        elif self.tab_name is None:
            return False
        elif _normalize_tab_name(view_frame.getTabName()) != self.tab_name:
            return False
        return True

    def notifyViewChanged(self, view_frame):
        if view_frame is None:
            pass
        else:
            pass  # implement this

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)
class DebugThreadsWidget(QWidget, DockContextHandler):
	def __init__(self, parent, name, data):
		if not type(data) == BinaryView:
			raise Exception('expected widget data to be a BinaryView')

		self.bv = data

		QWidget.__init__(self, parent)
		DockContextHandler.__init__(self, self, name)
		self.actionHandler = UIActionHandler()
		self.actionHandler.setupActionHandler(self)

		self.table = QTableView(self)
		self.model = DebugThreadsListModel(self.table)
		self.table.setModel(self.model)
		self.table.clicked.connect(self.threadRowClicked)

		self.item_delegate = DebugThreadsItemDelegate(self)
		self.table.setItemDelegate(self.item_delegate)

		# self.table.setSortingEnabled(True)
		self.table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
		self.table.setSelectionMode(QAbstractItemView.ExtendedSelection)

		self.table.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
		self.table.verticalHeader().setVisible(False)

		self.table.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
		self.table.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel)

		self.table.resizeColumnsToContents()
		self.table.resizeRowsToContents()

		for i in range(len(self.model.columns)):
			self.table.setColumnWidth(i, self.item_delegate.sizeHint(self.table.viewOptions(), self.model.index(-1, i, QModelIndex())).width())
		self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)

		layout = QVBoxLayout()
		layout.setContentsMargins(0, 0, 0, 0)
		layout.setSpacing(0)
		layout.addWidget(self.table)
		self.setLayout(layout)

	def notifyOffsetChanged(self, offset):
		pass

	# called from QTableView's clicked signal
	# index: QModelIndex
	def threadRowClicked(self, index):
		index = self.model.createIndex(index.row(), 0)
		tid_str = self.model.data(index, Qt.DisplayRole)
		#print('clicked to change to thread %s' % tid_str)
		stateObj = binjaplug.get_state(self.bv)
		if stateObj.connected and not stateObj.running:
			tid = int(tid_str, 16)
			stateObj.threads.current = tid
			stateObj.ui.context_display()
			stateObj.ui.on_step()
		else:
			print('cannot set thread in current state')

	# called from plugin's context_display() function
	def notifyThreadsChanged(self, new_threads):
		idx_selected = self.model.update_rows(new_threads)
		if idx_selected:
			self.table.setCurrentIndex(idx_selected)

	def contextMenuEvent(self, event):
		self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

	def shouldBeVisible(self, view_frame):
		if view_frame is None:
			return False
		else:
			return True
Esempio n. 16
0
class MemoryView(QWidget, DockContextHandler):
    def __init__(self, parent, name, data, bnwidgets):
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)

        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)
        self.data = data
        self.parent = parent
        self.bnwidgets = bnwidgets

        self.current_state = None
        self.arch = None
        self.address_start = None
        self.size = 512
        self.changes = set()
        self.tab_name = None

        self.monitor_history = list()

        self.symb_idx = 0

        self._layout = QGridLayout()
        self.button = QPushButton("Monitor Memory")
        self.button.setStyleSheet("margin-left: 10px;")
        self.button.clicked.connect(
            self._condom_async(self, self.on_monitor_button_click))
        self.back_button = QPushButton("Back")
        self.back_button.setStyleSheet("margin-right: 10px;")
        self.back_button.clicked.connect(
            self._condom_async(self, self.on_back_click))

        self.hexWidget = HexViewWidget(
            menu_handler=self.on_customContextMenuRequested)
        self.hexWidget.data_edited.connect(self._handle_data_edited)
        self.hexWidget.setEnabled(False)

        self._layout.addWidget(self.button, 0, 0, 1, 4)
        self._layout.addWidget(self.back_button, 0, 4, 1, 1)
        self._layout.addWidget(self.hexWidget, 1, 0, 1, 5)
        self._layout.setContentsMargins(0, 0, 0, 0)

        self.setMaximumWidth(self.hexWidget.optimal_width + 25)

        self.setLayout(self._layout)

    def _monitor_changes(self, address, size):
        if symbolic(address):
            self.changes.clear()
            self.changes.add(
                (self.address_start, self.address_start + self.size))
            return

        address = address.value
        if (self.address_start + self.size >= address
                and self.address_start <= address + size):
            to_monitor_min = max(self.address_start,
                                 address) - self.address_start
            to_monitor_max = min(self.address_start + self.size,
                                 address + size) - self.address_start
            self.changes.add((to_monitor_min, to_monitor_max))

    def on_monitor_button_click(self):
        if self.current_state is None:
            return

        address = get_int_input("Memory address", "Set Memory Monitor")
        if address is None:
            return
        address = address & 0xffffffffffffffff  # signed to unsigned
        self.monitor_history.append(address)

        self.hexWidget.setEnabled(True)
        self.address_start = address
        self.size = 512
        self.current_state.mem.register_store_hook(self._monitor_changes)
        self.update_mem(self.current_state)

    def on_back_click(self):
        if self.current_state is None:
            return

        if len(self.monitor_history) < 2:
            return

        self.monitor_history.pop()
        address = self.monitor_history[-1]

        self.hexWidget.setEnabled(True)
        self.address_start = address
        self.size = 512
        self.current_state.mem.register_store_hook(self._monitor_changes)
        self.update_mem(self.current_state)

    def init(self, arch, state):
        self.arch = arch
        self.tab_name = _normalize_tab_name(self.parent.getTabName())
        self.update_mem(state)

    def update_mem(self, state):
        self.current_state = state
        if self.address_start is None:
            return
        self.changes.clear()
        data = {}
        for i in range(self.size):
            if not state.mem.is_mapped(self.address_start + i):
                val = "__"
            else:
                b = state.mem.load(self.address_start + i, 1)
                val = ".."
                if isinstance(b, BVV):
                    val = "{:02x}".format(b.value)
            data[i] = val

        self.hexWidget.full_data_changed.emit(self.address_start, data,
                                              self.size)

    def update_mem_delta(self, state):
        self.current_state = state
        if self.address_start is None:
            return

        load_cache = {}
        for begin, end in self.changes:
            for i in range(begin, end):
                if not state.mem.is_mapped(self.address_start + i):
                    val = "__"
                else:
                    if i in load_cache:
                        b = load_cache[i]
                    else:
                        b = state.mem.load(self.address_start + i, 1)
                        load_cache[i] = b
                    val = ".."
                    if isinstance(b, BVV):
                        val = "{:02x}".format(b.value)
                self.hexWidget.single_data_changed.emit(i, val)
        # self.hexWidget.view.viewport().update()
        self.changes.clear()

    def reset(self):
        self.tab_name = None
        self.current_state = None
        self.address_start = None
        self.size = None
        self.hexWidget.full_data_changed.emit(0, {}, 0)

    def _handle_data_edited(self, offset, value):
        if not self.current_state or not self.address_start:
            return

        self.current_state.mem.store(self.address_start + offset,
                                     BVV(value, 8))
        # self.hexWidget.single_data_changed.emit(address - self.address_start, hex(value)[2:])

    def _show_expression(self, address, expr):
        show_message_box("Expression at %s" % hex(address),
                         str(expr.z3obj.sexpr()))

    def _evaluate_with_solver(self, address, expr):
        val = ""
        if not self.current_state.solver.symbolic(expr):
            new_expr = self.current_state.solver.evaluate(expr)
            self.current_state.mem.store(address, new_expr)
            self.changes.add(
                (address - self.address_start,
                 address - self.address_start + new_expr.size // 8))
            self.update_mem_delta(self.current_state)
            show_message_box("Expression at %s" % hex(address),
                             "The value was indeed concrete! State modified")
        else:
            val = self.current_state.solver.evaluate(expr).value
            show_message_box("Value at %s (with solver):" % hex(address),
                             hex(val))

    def _concretize(self, address, expr):
        new_expr = self.current_state.solver.evaluate(expr)
        self.current_state.mem.store(address, new_expr)
        self.current_state.solver.add_constraints(expr == new_expr)
        self.changes.add((address - self.address_start,
                          address - self.address_start + new_expr.size // 8))
        self.update_mem_delta(self.current_state)

    def _concretize_ascii_str(self, address, expr):
        extra_constraints = []
        for i in range(expr.size // 8):
            b = expr.Extract(i * 8 + 7, i * 8)
            extra_constraints.extend([b <= 0x7e, b >= 0x20])
        if not self.current_state.solver.satisfiable(extra_constraints):
            show_message_box(
                "Info", "The selected memory is not an ascii str (unsat)")
            return
        new_expr = self.current_state.solver.evaluate(expr, extra_constraints)
        self.current_state.mem.store(address, new_expr)
        self.current_state.solver.add_constraints(expr == new_expr)
        self.changes.add((address - self.address_start,
                          address - self.address_start + new_expr.size // 8))
        self.update_mem_delta(self.current_state)

    def _make_symbolic(self, address, size):
        buff = BVS("b_ui_mem_%d" % self.symb_idx, size * 8)
        self.current_state.mem.store(address, buff)
        self.current_state.symbolic_buffers.append((buff, address, ""))
        self.changes.add((address - self.address_start, size))
        self.symb_idx += 1
        self.update_mem_delta(self.current_state)
        self.bnwidgets.BW.onNewBufferSignal.emit(self.current_state)

    def _copy_big_endian(self, expr):
        mime = QMimeData()
        mime.setText(hex(expr.value))
        QApplication.clipboard().setMimeData(mime)

    def _copy_little_endian(self, expr):
        mime = QMimeData()
        expr_bytes = split_bv_in_list(expr, 8)
        res = 0
        i = 0
        for el in reversed(expr_bytes):
            res += el.value << i * 8
            i += 1
        mime.setText(hex(res))
        QApplication.clipboard().setMimeData(mime)

    def _copy_string(self, expr):
        mime = QMimeData()
        expr_bytes = split_bv_in_list(expr, 8)
        res = ""
        for el in reversed(expr_bytes):
            res += chr(el.value) if el.value >= 32 and el.value <= 126 else "."

        mime.setText(res)
        QApplication.clipboard().setMimeData(mime)

    def _copy_expression(self, expr):
        mime = QMimeData()
        mime.setText(str(expr.z3obj.sexpr()))
        QApplication.clipboard().setMimeData(mime)

    def _copy_binary(self, expr):
        mime = QMimeData()
        expr_bytes = split_bv_in_list(expr, 8)
        res = "\""
        for el in reversed(expr_bytes):
            res += "\\x{:02x}".format(el.value)
        res += "\""

        mime.setText(res)
        QApplication.clipboard().setMimeData(mime)

    @staticmethod
    def _condom(f, *pars):
        def g():
            f(*pars)

        return g

    @staticmethod
    def _condom_async(mw, f, *pars):
        def g():
            bt = MemoryViewBT("MemoryView background task...", mw, f, pars)
            bt.start()

        return g

    def on_customContextMenuRequested(self, qpoint):
        if self.current_state is None:
            return
        menu = QMenu(self)

        sel_start = self.hexWidget._hsm.start
        sel_end = self.hexWidget._hsm.end
        if sel_start is None:
            return

        if not self.current_state.mem.is_mapped(self.address_start +
                                                sel_start):
            return
        if not self.current_state.mem.is_mapped(self.address_start + sel_end):
            return

        expr = self.current_state.mem.load(sel_start + self.address_start,
                                           sel_end - sel_start + 1)

        if symbolic(expr):
            a = menu.addAction("Show expression")
            a.triggered.connect(
                MemoryView._condom(self._show_expression,
                                   sel_start + self.address_start, expr))
            a = menu.addAction("Evaluate with solver")
            a.triggered.connect(
                MemoryView._condom_async(self, self._evaluate_with_solver,
                                         sel_start + self.address_start, expr))
            a = menu.addAction("Concretize")
            a.triggered.connect(
                MemoryView._condom_async(self, self._concretize,
                                         sel_start + self.address_start, expr))
            a = menu.addAction("Concretize (ascii str)")
            a.triggered.connect(
                MemoryView._condom_async(self, self._concretize_ascii_str,
                                         sel_start + self.address_start, expr))
            a = menu.addAction("Copy expression")
            a.triggered.connect(MemoryView._condom(self._copy_expression,
                                                   expr))
        else:
            a = menu.addAction("Make symbolic")
            a.triggered.connect(
                MemoryView._condom(self._make_symbolic,
                                   sel_start + self.address_start,
                                   sel_end - sel_start + 1))
            copy_menu = menu.addMenu("Copy...")
            a = copy_menu.addAction("Copy Little Endian")
            a.triggered.connect(
                MemoryView._condom(self._copy_little_endian, expr))
            a = copy_menu.addAction("Copy Big Endian")
            a.triggered.connect(MemoryView._condom(self._copy_big_endian,
                                                   expr))
            a = copy_menu.addAction("Copy String")
            a.triggered.connect(MemoryView._condom(self._copy_string, expr))
            a = copy_menu.addAction("Copy Binary")
            a.triggered.connect(MemoryView._condom(self._copy_binary, expr))

        return menu

    def notifyOffsetChanged(self, offset):
        pass

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        elif self.tab_name is None:
            return False
        elif _normalize_tab_name(view_frame.getTabName()) != self.tab_name:
            return False
        return True

    def notifyViewChanged(self, view_frame):
        if view_frame is None:
            pass
        else:
            pass  # implement this

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)
Esempio n. 17
0
class BufferView(QWidget, DockContextHandler):
    onNewBufferSignal = QtCore.Signal(object)

    def __init__(self, parent, name, data):
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)

        self.onNewBufferSignal.connect(self.update)

        self.parent = parent
        self.current_state = None
        self.data = data
        self.tab_name = None

        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        self._layout = QVBoxLayout()

        # Set up register table
        self._table = QTableWidget()
        self._table.setColumnCount(4)
        self._table.setHorizontalHeaderLabels(
            ['Address', 'Name', 'Size', 'Constraints'])
        self._table.horizontalHeader().setStretchLastSection(True)
        self._table.verticalHeader().setVisible(False)

        self._table.setContextMenuPolicy(Qt.CustomContextMenu)
        self._table.customContextMenuRequested.connect(
            self.on_customContextMenuRequested)
        self._table.doubleClicked.connect(self.on_doubleClick)

        self.button = QPushButton("New Buffer")
        self.button.clicked.connect(self.on_newBufferClick)

        self._layout.addWidget(self.button)
        self._layout.addWidget(self._table)

        self.setLayout(self._layout)

    def on_newBufferClick(self):
        if self.current_state is None:
            return

        blacklisted_names = [
            b[0].name for b in self.current_state.symbolic_buffers]

        new_buff_dialog = CreateBufferDialog(
            blacklisted_names=blacklisted_names)
        new_buff_dialog.exec_()

        if new_buff_dialog.res_name is None:
            return

        buff = BVS(new_buff_dialog.res_name, new_buff_dialog.res_width * 8)
        if new_buff_dialog.res_terminator:
            buff_to_store = buff.Concat(BVV(0, 8))
        else:
            buff_to_store = buff

        address = self.current_state.mem.allocate(new_buff_dialog.res_width)
        if new_buff_dialog.res_constraints == ALPHANUMERIC_STRING:
            constraint_alphanumeric_string(buff, self.current_state)
        elif new_buff_dialog.res_constraints == ASCII_STRING:
            constraint_ascii_string(buff, self.current_state)

        constraint_str = ""
        if new_buff_dialog.res_constraints != NO_CONSTRAINTS:
            constraint_str = CreateBufferDialog.constraint_list[new_buff_dialog.res_constraints]
        self.current_state.mem.store(address, buff_to_store)
        self.current_state.symbolic_buffers.append(
            (buff, address, constraint_str)
        )
        self.update_state(self.current_state)

    def reset(self):
        self.tab_name = None
        self.current_state = None
        self._table.setRowCount(0)

    def init(self, state):
        self.current_state = state
        self.tab_name = _normalize_tab_name(self.parent.getTabName())
        self.update_state(state)

    def update_state(self, state):
        self.current_state = state
        self._table.setRowCount(0)
        self._table.setRowCount(len(self.current_state.symbolic_buffers))
        i = 0
        for buff, address, constraints in self.current_state.symbolic_buffers:
            self._table.setItem(i, 0, _makewidget(self, hex(address)))
            self._table.setItem(i, 1, _makewidget(self, buff.name))
            self._table.setItem(i, 2, _makewidget(self, buff.size // 8))
            self._table.setItem(i, 3, _makewidget(self, constraints))
            i += 1

    @staticmethod
    def _condom(f, *pars):
        def g():
            f(*pars)
        return g

    @staticmethod
    def _condom_async(bw, f, *pars):
        def g():
            bt = BufferViewBT("BufferView background task...", bw, f, pars)
            bt.start()
        return g

    # right click menu
    def on_customContextMenuRequested(self, pos):
        item = self._table.itemAt(pos)
        if item is None:
            return
        row_idx = item.row()
        menu = QMenu()

        copy_address = menu.addAction("Copy address")
        copy_address.triggered.connect(BufferView._condom(
            self._menuAction_copy_address, row_idx))
        eval_upto = menu.addAction("Evaluate upto")
        eval_upto.triggered.connect(BufferView._condom_async(
            self, self._menuAction_evaluate_upto_buffer, row_idx))
        eval_as_bytes = menu.addAction("Evaluate as bytes")
        eval_as_bytes.triggered.connect(BufferView._condom_async(
            self, self._menuAction_evaluate_buffer, row_idx))
        copy_eval = menu.addAction("Copy evaluated bytes")
        copy_eval.triggered.connect(BufferView._condom_async(
            self, self._menuAction_copy_evaluated_buffer, row_idx))
        add_constraint = menu.addAction("Add constraint")
        add_constraint.triggered.connect(BufferView._condom(
            self._menuAction_add_constraint, row_idx))

        menu.exec_(self._table.viewport().mapToGlobal(pos))

    def _menuAction_copy_address(self, buffer_id):
        mime = QMimeData()
        mime.setText(hex(self.current_state.symbolic_buffers[buffer_id][1]))
        QApplication.clipboard().setMimeData(mime)

    def _menuAction_evaluate_buffer(self, buffer_id):
        buff = self.current_state.symbolic_buffers[buffer_id][0]
        res = self.current_state.solver.evaluate(buff).as_bytes()
        res = repr(res)[2:-1]
        show_message_box("%s evaluate" % buff.name, res)

    def _menuAction_evaluate_upto_buffer(self, buffer_id):
        buff = self.current_state.symbolic_buffers[buffer_id][0]

        n_eval = get_int_input("How many values (upto) ?", "Number of distinct values")
        r = ""
        for i, v in enumerate(self.current_state.solver.evaluate_upto(buff, n_eval)):
            r += "solution %d: %s\n" % (i, hex(v.value))

        show_message_box("%s evaluate" % buff.name, r)

    def _menuAction_copy_evaluated_buffer(self, buffer_id):
        mime = QMimeData()
        buff = self.current_state.symbolic_buffers[buffer_id][0]
        res = self.current_state.solver.evaluate(buff).as_bytes()
        res = '"' + repr(res)[2:-1] + '"'
        mime.setText(res)
        QApplication.clipboard().setMimeData(mime)

    def _menuAction_add_constraint(self, buffer_id):
        buff = self.current_state.symbolic_buffers[buffer_id][0]
        constraints = self.current_state.symbolic_buffers[buffer_id][2]
        if constraints != "":
            show_message_box("Error", "The buffer already has a constraint.")
            return

        choices = [CreateBufferDialog.constraint_list[i]
                   for i in CreateBufferDialog.constraint_list.keys() if i != NO_CONSTRAINTS]
        res = get_choice_input(
            "Constraint buffer", "choices:", choices
        )
        if choices[res] == "Alphanumeric string":
            constraint_alphanumeric_string(buff, self.current_state)
        elif choices[res] == "ASCII string":
            constraint_ascii_string(buff, self.current_state)
        else:
            return

        t = self.current_state.symbolic_buffers[buffer_id]
        t = t[0], t[1], choices[res]
        self.current_state.symbolic_buffers[buffer_id] = t
        self.update_state(self.current_state)

    # double click event
    def on_doubleClick(self, item):
        # row_idx = item.row()
        pass

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        elif self.tab_name is None:
            return False
        elif _normalize_tab_name(view_frame.getTabName()) != self.tab_name:
            return False
        return True

    def notifyViewChanged(self, view_frame):
        if view_frame is None:
            pass
        else:
            pass

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)
Esempio n. 18
0
class SourceryPane(QWidget, DockContextHandler):
    def __init__(self, parent, name):
        global panes
        panes.append(self)
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        # Top: Headers with line info
        header_layout = QFormLayout()
        self.function_info = QLabel("")
        self.line_info = QLabel("")
        header_layout.addRow(self.tr("Function:"), self.function_info)
        header_layout.addRow(self.tr("Line:"), self.line_info)

        # Middle: main source display pane
        textbox_layout = QVBoxLayout()
        self.textbox = QPlainTextEdit()
        self.textbox.setLineWrapMode(QPlainTextEdit.LineWrapMode.NoWrap)
        self.textbox.setReadOnly(True)
        font = getMonospaceFont(self)
        self.textbox.setFont(font)
        font = QFontMetrics(font)
        self.textbox.setMinimumWidth(40 * font.averageCharWidth())
        self.textbox.setMinimumHeight(30 * font.lineSpacing())
        textbox_layout.addWidget(self.textbox)

        # Bottom: buttons for stop/start, and substitution paths
        footer_layout = QVBoxLayout()

        sync_button_layout = QHBoxLayout()
        self.sync_button = QPushButton("Turn Source Sync Off")
        sync_button_layout.addWidget(self.sync_button)

        path_layout = QFormLayout()
        self.original_path = QLineEdit()
        self.substitute_path = QLineEdit()
        self.substitute_path_button = QPushButton("Do Path Substitution")
        path_layout.addRow(self.tr("Original Path:"), self.original_path)
        path_layout.addRow(self.substitute_path_button, self.substitute_path)

        footer_layout.addLayout(sync_button_layout)
        footer_layout.addLayout(path_layout)

        # Putting all the child layouts together
        layout = QVBoxLayout()
        layout.addLayout(header_layout)
        layout.addLayout(textbox_layout)
        layout.addLayout(footer_layout)
        self.setLayout(layout)

        # Set up button signals
        self.substitute_path_button.clicked.connect(self.do_path_substitution)
        self.sync_button.clicked.connect(self.toggle_sync)

        # Actual storage variables
        self.bv = None
        self.filename = None
        self.do_sync = True
        self.path_substitutions = {}
        self.failed_substitutions = []

    def do_path_substitution(self):
        original_path = self.original_path.text()
        new_path = self.substitute_path.text()
        if isinstance(original_path, bytes):
            original_path = original_path.decode()
            new_path = new_path()
        if original_path == "":
            log_warn("Path substitution error: Original path can't be blank")
        elif new_path == "":
            if original_path in self.path_substitutions:
                old_sub = self.path_substitutions.pop(original_path)
                log_info("Removed path substitution: %s -> %s" %
                         (original_path, old_sub))
            else:
                log_warn(
                    "Path substitution error: New substitute path can't be blank"
                )
        else:
            self.path_substitutions[original_path] = new_path
            log_info("Added path substitution: %s -> %s" %
                     (original_path, new_path))
            self.failed_substitutions = [
            ]  # clear failures when new path added

    def toggle_sync(self):
        if self.do_sync is True:
            self.do_sync = False
            self.sync_button.setText("Turn Source Sync On")
        else:  # self.do_sync is False:
            self.do_sync = True
            self.sync_button.setText("Turn Source Sync Off")

    def set_text(self, text):
        self.textbox.setPlainText(text)

    def set_line(self, text):
        self.line_info.setText(text)

    def set_function(self, text):
        self.function_info.setText(text)

    def check_path_substitution(self, path):
        """Checks for files using path substitutions, going from longest to shortest original path"""
        sorted_original_paths = sorted(self.path_substitutions.keys(),
                                       key=lambda k: len(k),
                                       reverse=True)
        candidate_matches = []
        for candidate_path in sorted_original_paths:
            if candidate_path in path:
                substitute_pattern = self.path_substitutions[candidate_path]
                substitute_path = path.replace(candidate_path,
                                               substitute_pattern)
                substitute_path = os.path.expanduser(substitute_path)
                candidate_matches.append(substitute_path)
                if os.path.exists(substitute_path):
                    return substitute_path
        # Only log_warn once per file, and only if the user has tried to add translations
        if path not in self.failed_substitutions:
            if len(self.path_substitutions) > 0:
                log_warn("Failed to find substitution for %s" % path)
                log_info("Current substitution paths:")
                for orig_path, sub_path in self.path_substitutions.items():
                    log_info("  %s => %s" % (orig_path, sub_path))
                log_info("Matching patterns' failed substitute paths:")
                for candidate in candidate_matches:
                    log_info("  %s" % candidate)
            self.failed_substitutions.append(path)
        return ""

    def update_source(self, current_location):
        source_line = addr2line(self.filename, current_location)
        line_number_int = -1
        text = ""
        function_name = ""
        if source_line.startswith("?"):
            line_text = "No source mapping for address 0x%x" % current_location
        elif source_line.startswith("ERROR:"):
            line_text = "%s" % source_line
        else:
            filepath, line_number_str, function_name = source_line.split(":")
            # handle lines like: "16 (discriminator 1)"
            line_number_int = int(line_number_str.split(' ')[0])
            line_text = "%s:%s" % (filepath, line_number_str)
            # Check for the file, then for subsitutions
            if not os.path.exists(filepath):
                new_path = self.check_path_substitution(filepath)
                if new_path == "":
                    self.textbox.setLineWrapMode(
                        QPlainTextEdit.LineWrapMode.WidgetWidth)
                    text = '[!] Source file "%s" not found\n' % filepath
                    text += '[*] Associated line info: "%s"' % source_line
                else:
                    filepath = new_path
            # If we still don't have a good path, the text is set to the correct error
            if os.path.exists(filepath):
                self.textbox.setLineWrapMode(
                    QPlainTextEdit.LineWrapMode.NoWrap)
                with open(filepath, "r") as f:
                    text = f.read()
        self.set_text(text)
        self.set_line(line_text)
        self.set_function(function_name)
        if line_number_int != -1:
            self.set_cursor(line_number_int)
        else:
            self.reset_cursor()

    def reset_cursor(self):
        doc = self.textbox.document()
        cursor = QTextCursor(doc)
        cursor.movePosition(QTextCursor.Start)
        self.textbox.setTextCursor(cursor)

    def set_cursor(self, line_number):
        doc = self.textbox.document()
        cursor = QTextCursor(doc)
        cursor.movePosition(QTextCursor.Start)
        for _ in range(line_number - 1):
            cursor.movePosition(QTextCursor.Down)
        cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
        self.textbox.setTextCursor(cursor)
        self.textbox.centerCursor()

    def notifyOffsetChanged(self, offset):
        if self.filename:
            if self.do_sync:
                self.update_source(offset)

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        else:
            return True

    def notifyViewChanged(self, view_frame):
        if view_frame is None:
            pass
        else:
            self.bv = view_frame.actionContext().binaryView
            self.filename = self.bv.file.original_filename

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

    @staticmethod
    def create_widget(name, parent, data=None):
        return SourceryPane(parent, name)
Esempio n. 19
0
class ExportsTreeView(QTreeView, FilterTarget):
    def __init__(self, parent, view, data):
        QTreeView.__init__(self, parent)
        FilterTarget.__init__(self)
        self.data = data
        self.parent = parent
        self.view = view

        # Allow view-specific shortcuts when exports are focused
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)
        self.actionHandler.setActionContext(lambda: self.view.actionContext())

        self.model = GenericExportsModel(self.data)
        self.setModel(self.model)
        self.setRootIsDecorated(False)
        self.setUniformRowHeights(True)
        self.setSortingEnabled(True)
        self.sortByColumn(0, Qt.AscendingOrder)
        if self.model.ordinal_col is not None:
            self.setColumnWidth(self.model.ordinal_col, 55)

        self.setFont(binaryninjaui.getMonospaceFont(self))

        self.selectionModel().currentChanged.connect(self.exportSelected)
        self.doubleClicked.connect(self.exportDoubleClicked)

    def exportSelected(self, cur, prev):
        sym = self.model.getSymbol(cur)
        if sym is not None:
            self.view.setCurrentOffset(sym.address)

    def exportDoubleClicked(self, cur):
        sym = self.model.getSymbol(cur)
        if sym is not None:
            viewFrame = ViewFrame.viewFrameForWidget(self)
            if len(self.data.get_functions_at(sym.address)) > 0:
                viewFrame.navigate("Graph:" + viewFrame.getCurrentDataType(),
                                   sym.address)
            else:
                viewFrame.navigate("Linear:" + viewFrame.getCurrentDataType(),
                                   sym.address)

    def setFilter(self, filterText):
        self.model.setFilter(filterText)

    def scrollToFirstItem(self):
        self.scrollToTop()

    def scrollToCurrentItem(self):
        self.scrollTo(self.currentIndex())

    def selectFirstItem(self):
        self.setCurrentIndex(self.model.index(0, 0, QModelIndex()))

    def activateFirstItem(self):
        self.exportDoubleClicked(self.model.index(0, 0, QModelIndex()))

    def closeFilter(self):
        self.setFocus(Qt.OtherFocusReason)

    def keyPressEvent(self, event):
        if (len(event.text()) == 1) and (ord(event.text()[0:1]) > 32) and (ord(
                event.text()[0:1]) < 127):
            self.parent.filter.showFilter(event.text())
            event.accept()
        elif (event.key() == Qt.Key_Return) or (event.key() == Qt.Key_Enter):
            sel = self.selectedIndexes()
            if len(sel) != 0:
                self.exportDoubleClicked(sel[0])
        super(ExportsTreeView, self).keyPressEvent(event)
Esempio n. 20
0
class GhinjaDockWidget(QWidget, DockContextHandler):
	def __init__(self, parent, name, data):
		# Read the configuration
		settings = Settings()
		settings.register_group("ghinja", "Ghinja")
		settings.register_setting("ghinja.ghidra_install_path", """
				{
					"title" : "Ghidra Installation Path",
					"type" : "string",
					"default" : "",
					"description" : "Path to analyzeHeadless file in Ghidra installation dir."
				}
				""")
		if not os.path.exists(settings.get_string("ghinja.ghidra_install_path")):
			show_message_box("Path to Ghidra headless was not found!", "To allow the Ghinja plugin to work, you will be prompted to specify the path to the \"analyzeHeadless(.bat)\" file.", buttons=0, icon=2)
			settings.set_string("ghinja.ghidra_install_path",get_open_filename_input("Provide Path to Ghidra \"analyzeHeadless(.bat)\" file (Usually: <GHIDRA_INSTALL>/support/analyzeHeadless)").decode("utf-8"))
		
		self.rename_settings = Settings()
		self.rename_settings.register_group("ghinja_rename","Rename")
		self.rename_settings.register_setting("ghinja_rename.ghinja_rename_struct", """
				{
					"title" : "Ghidra Rename Struct",
					"type" : "string",
					"default" : "{}",
					"description" : "Settings to hold renames for variables."
				}
				""")

		global instance_id
		self.binja_renames = {} # {"function_name":[{"original":"new"})]}
		self.current_function = None
		self.current_offset = None
		self.decomp = None
		self.current_view = None
		self.function_output = None
		self.decompile_result_path = None
		self.decompile_offset_path = None
		self.decompiler_done = False
		self.function_args = []
		QWidget.__init__(self, parent)
		DockContextHandler.__init__(self, self, name)
		self.actionHandler = UIActionHandler()
		self.actionHandler.setupActionHandler(self)
		layout = QVBoxLayout()
		self.editor = QTextEdit(self)
		self.editor.setReadOnly(True)
		self.editor.installEventFilter(self)
		self.editor.setStyleSheet("QTextEdit { font-family: Consolas }")
		self.editor.setPlainText(" Click anywhere in the dock to start decompiler")
		self.editor.selectionChanged.connect(self.onSelect)
		highlighter = Highlighter(self.editor.document(),"",self.function_args)
		layout.addWidget(self.editor)
		layout.setAlignment(QtCore.Qt.AlignLeft)
		self.setLayout(layout)
		instance_id += 1
		self.data = data

	def onSelect(self):
		cursor = self.editor.textCursor()
		if cursor.selectedText():
			ch = Highlighter(self.editor.document(),"\\b" + cursor.selectedText() + "\\b",self.function_args)
		else:
			ch = Highlighter(self.editor.document(),"",self.function_args)

	def notifyOffsetChanged(self, offset):
		if self.decompiler_done:
			#if not self.current_function or not (self.current_function.lowest_address < offset and self.current_function.highest_address > offset):
			scrollbar_value = self.editor.verticalScrollBar().value()
			self.current_offset = offset
			try:
				self.editor.setPlainText(self.find_function(offset))
				ch = Highlighter(self.editor.document(),"",self.function_args)
				self.editor.verticalScrollBar().setValue(scrollbar_value)
			except:
				pass

	def shouldBeVisible(self, view_frame):
		if view_frame is None:
			return False
		else:
			return True

	def eventFilter(self, obj, event):
		if event.type() == QtCore.QEvent.FocusIn and self.editor.hasFocus() and not self.decompiler_done:
			self.decomp = Decompiler(self.filename,self.current_path)
			self.editor.setPlainText(" Decompiler running ... ")
			self.decomp.start()
			self.decompiler_done = True
		if event.type() == QtCore.QEvent.KeyPress and obj is self.editor:
			cursor = self.editor.textCursor()
			if event.key() == QtCore.Qt.Key_F and self.editor.hasFocus():
				# Find TODO
				search_string = get_text_line_input("Find: ","Find")
				if search_string:
					ch = Highlighter(self.editor.document(),search_string.decode("UTF-8"),self.function_args)
			if event.key() == QtCore.Qt.Key_N and self.editor.hasFocus():
				if self.current_view.file.has_database == False:
					show_message_box("Project not saved", "To enable renaming, make sure that the current project is saved to a BNDB file.", buttons=0, icon=2)
					return False
				# Handle rename action
				selected = cursor.selectedText()
				if selected != "":
					#self.binja_renames = json.loads(self.rename_settings.get_string("ghinja_rename.ghinja_rename_struct",self.current_view))
					# Get selected text
					new_name = get_text_line_input(f"Rename {selected}: ","Rename")
					if new_name and not re.match(b"^\\w+$", new_name):
						show_message_box("Name not valid", "Please use only 'word' characters (A-Z, a-z, 0-9 and _)", buttons=0, icon=2)
						return False
					if re.search(f"\\b{new_name.decode('UTF-8')}\\b",self.function_output):
						show_message_box("Name not unique", "Please use only unique name to avoid conflicts.", buttons=0, icon=2)
						return False
					found = False
					for key in self.binja_renames[hex(self.current_function.start)]:
						if key["original"] == selected:
							# Was already renamed so just reassign to avoid accumulating tons of data
							key["new"] = new_name.decode("UTF-8")
							found = True
						if key["new"] == selected:
							key["new"] = new_name.decode("UTF-8")
							found = True
					if not found:
						self.binja_renames[hex(self.current_function.start)].append({"original":selected,"new":new_name.decode("UTF-8")})
					self.rename_settings.set_string("ghinja_rename.ghinja_rename_struct",json.dumps(self.binja_renames),self.current_view,SettingsScope.SettingsResourceScope)
					self.notifyOffsetChanged(self.current_offset)
		return False
	

	def notifyViewChanged(self, view_frame):
		if view_frame is None:
			pass
		else:
			self.current_view = view_frame.actionContext().binaryView
			self.binja_renames = json.loads(self.rename_settings.get_string("ghinja_rename.ghinja_rename_struct",self.current_view))
			md5 = hashlib.md5()
			try:
				with open(view_frame.actionContext().binaryView.file.original_filename,'rb') as binary:
					file_content = binary.read()
					md5.update(file_content)
					self.current_path = Path(Path(user_plugin_path()) / ".." / f"ghinja_projects/{str(Path(view_frame.actionContext().binaryView.file.original_filename).name) + '_' + md5.hexdigest()}")
				self.filename = view_frame.actionContext().binaryView.file.original_filename
				self.current_path.mkdir(parents=True, exist_ok=True)
			except:
				# File does not exist
				tmp_path = Path(user_plugin_path()) / ".." / f"ghinja_projects/{str(Path(view_frame.actionContext().binaryView.file.original_filename).name)}"
				self.current_view.save(str(tmp_path))
				while not os.path.exists(str(tmp_path)):
					pass
				with open(str(tmp_path),'rb') as binary:
					file_content = binary.read()
					md5.update(file_content)
				self.current_path = Path(Path(user_plugin_path()) / ".." / f"ghinja_projects/{str(Path(view_frame.actionContext().binaryView.file.original_filename).name) + '_' + md5.hexdigest()}")
				self.current_path.mkdir(parents=True, exist_ok=True)
				shutil.move(tmp_path, self.current_path / tmp_path.name)
				self.filename = str(self.current_path / tmp_path.name)
			# Create relevant_folder
			#current_path = Path(Path.home() / f".ghinja_projects/{str(Path(view_frame.actionContext().binaryView.file.original_filename).name) + '_' + md5.hexdigest()}")
			self.decompile_result_path = Path(self.current_path / "decomp_")
			self.decompile_offset_path = Path(self.current_path / "decomp_offset")
			
			if os.path.exists(str(self.decompile_offset_path)):
				# Already decompiled we dont have to do anything
				self.decompiler_done = True
				


	def contextMenuEvent(self, event):
		self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

	@staticmethod
	def create_widget(name, parent, data = None):
		return GhinjaDockWidget(parent, name, data)

	def myroundup(self,n, step):
		return ((n - 1) // step + 1) * step

	def find_function(self, offset):
		function_output = "DECOMPILER OUTPUT FOR THIS FUNCTION WAS NOT FOUND"
		try:
			self.current_function = self.current_view.get_functions_containing(offset)[0]
			try:
				self.binja_renames[hex(self.current_function.start)]
			except:
				self.binja_renames[hex(self.current_function.start)] = []
		except:
			return "DECOMPILER OUTPUT FOR THIS FUNCTION WAS NOT FOUND"
		# Get different offset functions os.listdir()
		offset = 0
		with open(str(self.decompile_offset_path),"r") as offset_file:
			ghidra_offset = int(offset_file.read())
			offset_diff = ghidra_offset - self.current_view.functions[0].start
			if offset_diff > 0x100 and not os.path.exists(str(self.decompile_result_path) + str(offset_diff)):
				offset_diff = self.myroundup(offset_diff,0x100)
			#log_info(f"GHIDRA OFFSET: {hex(ghidra_offset)} DIFF: {hex(offset_diff)} ROUND: {hex(self.myroundup(offset_diff,0x100))}")
			if offset_diff == 0:
				offset = self.current_function.start
			else:
				offset = self.current_function.start + offset_diff

		if os.path.exists(str(self.decompile_result_path) + str(offset)):
			with open(str(self.decompile_result_path) + str(offset),"r") as function_file:
				function_output = function_file.read()
			# Replace function name
			function_output = re.sub("\\b\\w*\\(", self.current_function.name + "(", function_output, 1)
			# Rename functions
			for callee in self.current_function.callees:
				if offset_diff == 0:
					look_for = f'FUN_{callee.start:08x}'
				else:
					look_for = f'FUN_{callee.start + offset_diff:08x}'
				function_output = function_output.replace(look_for,callee.name)
			
			#for ghinja_rename in self.binja_renames[hex(self.current_function.start)]:
			js = json.loads(self.rename_settings.get_string("ghinja_rename.ghinja_rename_struct",self.current_view))
			if js:
				try:
					for ghinja_rename in js[hex(self.current_function.start)]:
						function_output = re.sub('\\b'+ghinja_rename["original"]+'\\b',ghinja_rename["new"],function_output)
				except:
					pass
			# Searching in frist 300 chars is enough
			self.function_args = []
			for arg in re.findall("\\w+ [*&]?(\\w+),|\\w+ [*&]?(\\w+)\\)", function_output[:300]):
				if arg[0]:
					self.function_args.append(arg[0])
				elif arg[1]:
					self.function_args.append(arg[1])
			# Rename locals - quite buggy actually
			for local in self.current_function.stack_layout:
				if local.storage < 0:
					look_for = f"local_{hex(local.storage)[3:]}"
					function_output = re.sub(look_for,local.name,function_output)
		self.function_output = function_output
		return function_output
Esempio n. 21
0
class RegisterView(QWidget, DockContextHandler):

    dirty_color = QBrush(QColor(255, 153, 51))
    symbolic_color = QBrush(QColor(245, 66, 72))
    no_color = QBrush(QColor(255, 255, 255))

    def __init__(self, parent, name, data):
        QWidget.__init__(self, parent)
        DockContextHandler.__init__(self, self, name)

        self.parent = parent
        self.arch = None
        self.current_state = None
        self.symb_idx = 0
        self.reg_to_index = dict()
        self.index_to_reg = dict()
        self.reg_cache = dict()
        self.data = data
        self.tab_name = None

        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)

        self._layout = QVBoxLayout()

        # Set up register table
        self._table = QTableWidget()
        self._table.setColumnCount(2)
        self._table.setHorizontalHeaderLabels(['Register', 'Value'])
        self._table.horizontalHeader().setStretchLastSection(True)
        self._table.verticalHeader().setVisible(False)

        self._table.setContextMenuPolicy(Qt.CustomContextMenu)
        self._table.customContextMenuRequested.connect(
            self.on_customContextMenuRequested)
        self._table.doubleClicked.connect(self.on_doubleClick)

        self._layout.addWidget(self._table)

        self.setLayout(self._layout)

    def reset(self):
        self.tab_name = None
        self.arch = None
        self.reg_to_index = dict()
        self.symb_idx = 0
        self._table.setRowCount(0)

    def init(self, arch, state):
        self.arch = arch
        self.tab_name = _normalize_tab_name(self.parent.getTabName())

        regs = self.arch.reg_names()

        self._table.setRowCount(len(regs))
        for i, reg in enumerate(regs):
            self.reg_to_index[reg] = i
            self.index_to_reg[i] = reg
            self._table.setItem(i, 0, _makewidget(self, reg))
            self._table.setItem(i, 1, _makewidget(self, ""))

        self.set_reg_values(state)

    def set_reg_value(self, reg, value, color=None):
        assert self.arch is not None

        idx = self.reg_to_index[reg]

        if symbolic(value):
            if isinstance(value, BVS):
                val_str = value.name
            else:
                val_str = "< symbolic expression >"
                if color is None:
                    color = RegisterView.symbolic_color
        else:
            val_str = "0x{obj:0{width}x}".format(obj=value.value,
                                                 width=(value.size + 3) // 4)

        self.reg_cache[reg] = val_str
        table_item = self._table.item(idx, 1)
        table_item.setText(val_str)
        if color is not None:
            table_item.setForeground(color)
        else:
            table_item.setForeground(self.no_color)

    def set_reg_values(self, state):
        self.current_state = state
        for reg in self.reg_to_index:
            val = getattr(state.regs, reg)
            self.set_reg_value(reg, val)

    # right click menu
    def on_customContextMenuRequested(self, pos):
        item = self._table.itemAt(pos)
        if item is None:
            return
        row_idx = item.row()

        if self.index_to_reg[row_idx] == self.arch.getip_reg():
            return

        expr = getattr(self.current_state.regs, self.index_to_reg[row_idx])

        menu = QMenu()
        show_reg_expr = menu.addAction(
            "Show reg expression") if not isinstance(expr, BVV) else None
        make_reg_symb = menu.addAction("Make reg symbolic") if isinstance(
            expr, BVV) else None
        set_reg_value = menu.addAction("Set reg value")
        eval_with_sol = menu.addAction(
            "Evaluate with solver") if not isinstance(expr, BVV) else None
        eval_upto_with_sol = menu.addAction(
            "Evaluate upto with solver") if not isinstance(expr, BVV) else None
        concretize = menu.addAction("Concretize") if not isinstance(
            expr, BVV) else None
        copy = menu.addAction("Copy to clipboard") if not isinstance(
            expr, BVS) else None
        bind_to_buffer = menu.addAction("Bind to symbolic buffer")

        action = menu.exec_(self._table.viewport().mapToGlobal(pos))
        if action is None:
            return

        if action == bind_to_buffer:
            buffer_names = [
                b[0].name for b in self.current_state.symbolic_buffers
            ]
            if len(buffer_names) == 0:
                return
            buff_id = get_choice_input("Select a buffer", "choices",
                                       buffer_names)
            address = self.current_state.symbolic_buffers[buff_id][1]
            buff_p = BVV(address, self.current_state.arch.bits())
            setattr(self.current_state.regs, self.index_to_reg[row_idx],
                    buff_p)
            self.set_reg_value(self.index_to_reg[row_idx], buff_p,
                               RegisterView.dirty_color)
        if action == show_reg_expr:
            show_message_box("Reg Expression", str(expr.z3obj.sexpr()))
        if action == make_reg_symb:
            new_expr = BVS('symb_injected_through_ui_%d' % self.symb_idx,
                           expr.size)
            setattr(self.current_state.regs, self.index_to_reg[row_idx],
                    new_expr)
            self.set_reg_value(self.index_to_reg[row_idx], new_expr,
                               RegisterView.dirty_color)
            self.symb_idx += 1
        if action == set_reg_value:
            self.on_doubleClick(item)
        if action == eval_with_sol:
            expr = getattr(self.current_state.regs, self.index_to_reg[row_idx])
            if not self.current_state.solver.symbolic(expr):
                new_expr = self.current_state.solver.evaluate(expr)
                setattr(self.current_state.regs, self.index_to_reg[row_idx],
                        new_expr)
                self.set_reg_value(self.index_to_reg[row_idx], new_expr,
                                   RegisterView.dirty_color)
                show_message_box(
                    "Reg Value (with solver)",
                    "The value was indeed concrete! State modified")
            else:
                show_message_box(
                    "Reg Value (with solver)",
                    hex(self.current_state.solver.evaluate(expr).value))
        if action == eval_upto_with_sol:
            expr = getattr(self.current_state.regs, self.index_to_reg[row_idx])
            if not self.current_state.solver.symbolic(expr):
                new_expr = self.current_state.solver.evaluate(expr)
                setattr(self.current_state.regs, self.index_to_reg[row_idx],
                        new_expr)
                self.set_reg_value(self.index_to_reg[row_idx], new_expr,
                                   RegisterView.dirty_color)
                show_message_box(
                    "Reg Value (with solver)",
                    "The value was indeed concrete! State modified")
            else:
                n_eval = get_int_input("How many values (upto) ?",
                                       "Number of distinct values")
                r = ""
                for i, v in enumerate(
                        self.current_state.solver.evaluate_upto(expr, n_eval)):
                    r += "solution %d: %s\n" % (i, hex(v.value))
                show_message_box("Reg Value (with solver)", r)
        if action == concretize:
            expr = getattr(self.current_state.regs, self.index_to_reg[row_idx])
            new_expr = self.current_state.solver.evaluate(expr)
            res = get_choice_input(
                "Concretize %s to %s?" %
                (self.index_to_reg[row_idx], hex(new_expr.value)),
                "Concretize", ["Yes", "No"])
            if res == 0:
                setattr(self.current_state.regs, self.index_to_reg[row_idx],
                        new_expr)
                self.current_state.solver.add_constraints(expr == new_expr)
                self.set_reg_value(self.index_to_reg[row_idx], new_expr,
                                   RegisterView.dirty_color)

        if action == copy:
            mime = QMimeData()
            if isinstance(expr, BVV):
                mime.setText(hex(expr.value))
            else:
                mime.setText(str(expr.z3obj.sexpr()))
            QApplication.clipboard().setMimeData(mime)

    # double click event
    def on_doubleClick(self, item):
        row_idx = item.row()
        if self.index_to_reg[row_idx] == self.arch.getip_reg():
            return

        old_expr = getattr(self.current_state.regs, self.index_to_reg[row_idx])
        new_val = get_int_input("value for %s" % self.index_to_reg[row_idx],
                                "Set Reg")
        if new_val is None:
            return
        new_expr = BVV(new_val, old_expr.size)
        setattr(self.current_state.regs, self.index_to_reg[row_idx], new_expr)
        self.set_reg_value(self.index_to_reg[row_idx], new_expr,
                           RegisterView.dirty_color)

    def notifyOffsetChanged(self, offset):
        pass

    def shouldBeVisible(self, view_frame):
        if view_frame is None:
            return False
        elif self.tab_name is None:
            return False
        elif _normalize_tab_name(view_frame.getTabName()) != self.tab_name:
            return False
        return True

    def notifyViewChanged(self, view_frame):
        if view_frame is None:
            pass
        else:
            pass

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)
Esempio n. 22
0
class HelloPaneWidget(QWidget, UIContextNotification):
    def __init__(self, data):
        global instance_id
        QWidget.__init__(self)
        self.actionHandler = UIActionHandler()
        self.actionHandler.setupActionHandler(self)
        offset_layout = QHBoxLayout()
        offset_layout.addWidget(QLabel("Offset: "))
        self.offset = QLabel(hex(0))
        offset_layout.addWidget(self.offset)
        offset_layout.setAlignment(QtCore.Qt.AlignCenter)
        datatype_layout = QHBoxLayout()
        datatype_layout.addWidget(QLabel("Data Type: "))
        self.datatype = QLabel("")
        datatype_layout.addWidget(self.datatype)
        datatype_layout.setAlignment(QtCore.Qt.AlignCenter)
        layout = QVBoxLayout()
        title = QLabel("Hello Pane", self)
        title.setAlignment(QtCore.Qt.AlignCenter)
        instance = QLabel("Instance: " + str(instance_id), self)
        instance.setAlignment(QtCore.Qt.AlignCenter)
        layout.addStretch()
        layout.addWidget(title)
        layout.addWidget(instance)
        layout.addLayout(datatype_layout)
        layout.addLayout(offset_layout)
        layout.addStretch()
        self.setLayout(layout)
        instance_id += 1
        self.data = data

        # Populate initial state
        self.updateState()

        # Set up view and address change notifications
        self.notifications = HelloNotifications(self)

    def updateState(self):
        # Get the currently active view frame for this group of panes. There can be
        # multiple view frames in a single window, or the pane could be popped out
        # into its own window. UIContext.currentViewFrameForWidget will determine
        # the best view frame to use for context.
        frame = UIContext.currentViewFrameForWidget(self)

        # Update UI according to the active frame
        if frame:
            self.datatype.setText(frame.getCurrentView())
            view = frame.getCurrentViewInterface()
            self.data = view.getData()
            self.offset.setText(hex(view.getCurrentOffset()))
        else:
            self.datatype.setText("None")
            self.data = None

    def contextMenuEvent(self, event):
        self.m_contextMenuManager.show(self.m_menu, self.actionHandler)

    @staticmethod
    def createPane(context):
        if context.context and context.binaryView:
            widget = HelloPaneWidget(context.binaryView)
            pane = WidgetPane(widget, "Hello")
            context.context.openPane(pane)

    @staticmethod
    def canCreatePane(context):
        return context.context and context.binaryView