def _init_widgets(self):
        self._text = '"' + self._node.content.decode("utf-8") + '"'

        self._text_item = QGraphicsSimpleTextItem(self._text, self)
        self._text_item.setBrush(self.STRING_NODE_TEXT_COLOR)
        self._text_item.setFont(Conf.symexec_font)
        self._text_item.setPos(self.HORIZONTAL_PADDING, self.VERTICAL_PADDING)
示例#2
0
    def setChart(self, chart: QtCharts.QChart) -> None:
        """ Sets a new chart in the view. Doesn't delete the previous chart """
        # Set New chart
        super().setChart(chart)
        # Update fields
        series: List[QtCharts.QAbstractSeries] = chart.series()
        if not series:
            # Empty chart
            self._chartIsSet = False
        else:
            self._chartIsSet = True

        # self.__callouts = list()
        self._tooltip = Callout(chart)
        chart.setAcceptHoverEvents(True)
        for s in series:
            # s.clicked.connect(self.keepCallout)
            s.hovered.connect(self.tooltip)

        if self._chartIsSet and self._positionTrackerEnabled:
            self._coordX = QGraphicsSimpleTextItem(chart)
            self._coordX.setText("X: ")
            self._coordY = QGraphicsSimpleTextItem(chart)
            self._coordY.setText("Y: ")
            self._updateMouseTrackerPosition(
            )  # Show them in the correct place
示例#3
0
class MoveHistoryView(QGraphicsView):
    """A widget containing a view of the move history."""
    def __init__(self, parent):
        QGraphicsView.__init__(self, parent)
        self.setMinimumWidth(180)
        self.setMaximumWidth(180)
        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.text_item = QGraphicsSimpleTextItem()
        self.scene.addItem(self.text_item)
        self.show()

    def set_move_history(self, move_history):
        """
        Sets the move history according to a list of moves.

        :param move_history: List of half moves
        """
        text = ''
        half_move_number = 1
        full_move_number = 1
        for half_move in move_history:
            if half_move_number % 2 == 1:
                text += str(full_move_number) + '. ' + half_move
                half_move_number += 1
            else:
                text += ' ' + half_move + '\n'
                half_move_number += 1
                full_move_number += 1

        self.text_item.setText(text)
示例#4
0
    def _init_widgets(self):

        self._addr_text = "%08x" % self.addr
        self._bytes = [ ]
        if self.memory_data.content:
            for byt in self.memory_data.content:
                self._bytes.append(byt)

        if len(self._bytes) < self.memory_data.size:
            # load more from mapped memory
            start_address = self.memory_data.addr + len(self._bytes)
            size = self.memory_data.size - len(self._bytes)
            try:
                mem_bytes = self.workspace.instance.project.loader.memory.load(start_address, size)
            except KeyError:
                mem_bytes = b""
            self._bytes += [ b for b in mem_bytes ] + [ '??' ] * (size - len(mem_bytes))

        # address
        self._addr_item = QGraphicsSimpleTextItem(self._addr_text, self)
        self._addr_item.setFont(Conf.disasm_font)
        self._addr_item.setBrush(Conf.disasm_view_node_address_color)

        # label
        self._init_label_item()

        # bytes
        self._init_bytes()

        self._layout_items_and_update_size()
示例#5
0
    def __init__(self,
                 addr: int,
                 obj: QBlockCodeObj,
                 config: ConfigurationManager,
                 disasm_view: 'QDisassemblyBaseControl',
                 workspace: 'Workspace',
                 infodock: InfoDock,
                 parent: Any = None):
        super().__init__(parent=parent)
        self.addr = addr
        self._addr_str = "%08x" % self.addr
        self._addr_item: QGraphicsSimpleTextItem = None
        self.obj = obj
        self._width = 0
        self._height = 0
        self._config = config
        self.parent = parent
        self.workspace = workspace
        self.infodock = infodock
        self._disasm_view = disasm_view
        self._qtextdoc = QTextDocument()
        self._qtextdoc.setDefaultFont(self._config.disasm_font)
        self._qtextdoc.setDocumentMargin(0)

        self._addr_item = QGraphicsSimpleTextItem(self._addr_str, self)
        self._addr_item.setBrush(Conf.disasm_view_node_address_color)
        self._addr_item.setFont(Conf.disasm_font)

        self.update_document()
        self.setToolTip("Address: " + self._addr_str)

        self.refresh()
示例#6
0
 def __init__(self, parent):
     QGraphicsView.__init__(self, parent)
     self.setMinimumWidth(180)
     self.setMaximumWidth(180)
     self.scene = QGraphicsScene()
     self.setScene(self.scene)
     self.text_item = QGraphicsSimpleTextItem()
     self.scene.addItem(self.text_item)
     self.show()
示例#7
0
 def _init_label_item(self):
     lbl_text = get_label_text(self.addr, self.workspace.instance.kb)
     if lbl_text:
         self._label_item = QGraphicsSimpleTextItem(lbl_text, self)
         self._label_item.setFont(Conf.code_font)
         self._label_item.setBrush(Conf.disasm_view_label_color)
     else:
         if self._label_item is not None:
             self._label_item.setParentItem(None)
             self._label_item = None
示例#8
0
    def __init__(self, parent=None):
        super(View, self).__init__(parent)
        self.setScene(QGraphicsScene(self))

        self.setDragMode(QGraphicsView.NoDrag)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # Chart
        self._chart = QtCharts.QChart()
        self._chart.setMinimumSize(640, 480)
        self._chart.setTitle("Hover the line to show callout. Click the line "
                             "to make it stay")
        self._chart.legend().hide()
        self.series = QtCharts.QLineSeries()
        self.series.append(1, 3)
        self.series.append(4, 5)
        self.series.append(5, 4.5)
        self.series.append(7, 1)
        self.series.append(11, 2)
        self._chart.addSeries(self.series)

        self.series2 = QtCharts.QSplineSeries()
        self.series2.append(1.6, 1.4)
        self.series2.append(2.4, 3.5)
        self.series2.append(3.7, 2.5)
        self.series2.append(7, 4)
        self.series2.append(10, 2)
        self._chart.addSeries(self.series2)

        self._chart.createDefaultAxes()
        self._chart.setAcceptHoverEvents(True)

        self.setRenderHint(QPainter.Antialiasing)
        self.scene().addItem(self._chart)

        self._coordX = QGraphicsSimpleTextItem(self._chart)
        self._coordX.setPos(self._chart.size().width() / 2 - 50,
                            self._chart.size().height())
        self._coordX.setText("X: ")
        self._coordY = QGraphicsSimpleTextItem(self._chart)
        self._coordY.setPos(self._chart.size().width() / 2 + 50,
                            self._chart.size().height())
        self._coordY.setText("Y: ")

        self._callouts = []
        self._tooltip = Callout(self._chart)

        self.series.clicked.connect(self.keepCallout)
        self.series.hovered.connect(self.tooltip)

        self.series2.clicked.connect(self.keepCallout)
        self.series2.hovered.connect(self.tooltip)

        self.setMouseTracking(True)
示例#9
0
    def __init__(self, parent = None):
        super(View, self).__init__(parent)
        self.setScene(QGraphicsScene(self))

        self.setDragMode(QGraphicsView.NoDrag)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # Chart
        self._chart = QtCharts.QChart()
        self._chart.setMinimumSize(640, 480)
        self._chart.setTitle("Hover the line to show callout. Click the line "
            "to make it stay")
        self._chart.legend().hide()
        self.series = QtCharts.QLineSeries()
        self.series.append(1, 3)
        self.series.append(4, 5)
        self.series.append(5, 4.5)
        self.series.append(7, 1)
        self.series.append(11, 2)
        self._chart.addSeries(self.series)

        self.series2 = QtCharts.QSplineSeries()
        self.series2.append(1.6, 1.4)
        self.series2.append(2.4, 3.5)
        self.series2.append(3.7, 2.5)
        self.series2.append(7, 4)
        self.series2.append(10, 2)
        self._chart.addSeries(self.series2)

        self._chart.createDefaultAxes()
        self._chart.setAcceptHoverEvents(True)

        self.setRenderHint(QPainter.Antialiasing)
        self.scene().addItem(self._chart)

        self._coordX = QGraphicsSimpleTextItem(self._chart)
        self._coordX.setPos(
            self._chart.size().width()/2 - 50, self._chart.size().height())
        self._coordX.setText("X: ");
        self._coordY = QGraphicsSimpleTextItem(self._chart)
        self._coordY.setPos(
            self._chart.size().width()/2 + 50, self._chart.size().height())
        self._coordY.setText("Y: ")

        self._callouts = []
        self._tooltip = Callout(self._chart)

        self.series.clicked.connect(self.keepCallout)
        self.series.hovered.connect(self.tooltip)

        self.series2.clicked.connect(self.keepCallout)
        self.series2.hovered.connect(self.tooltip)

        self.setMouseTracking(True)
    def __init__(self, toolbox, x, y, w, h, project_item, icon_file,
                 icon_color, background_color):
        """Base class for project item icons drawn in Design View.

        Args:
            toolbox (ToolBoxUI): QMainWindow instance
            x (float): Icon x coordinate
            y (float): Icon y coordinate
            w (float): Icon width
            h (float): Icon height
            project_item (ProjectItem): Item
            icon_file (str): Path to icon resource
            icon_color (QColor): Icon's color
            background_color (QColor): Background color
        """
        super().__init__()
        self._toolbox = toolbox
        self._project_item = project_item
        self._moved_on_scene = False
        self.renderer = QSvgRenderer()
        self.svg_item = QGraphicsSvgItem()
        self.colorizer = QGraphicsColorizeEffect()
        self.setRect(QRectF(x, y, w, h))  # Set ellipse coordinates and size
        self.text_font_size = 10  # point size
        # Make item name graphics item.
        name = project_item.name if project_item else ""
        self.name_item = QGraphicsSimpleTextItem(name)
        shadow_effect = QGraphicsDropShadowEffect()
        shadow_effect.setOffset(1)
        shadow_effect.setEnabled(False)
        self.setGraphicsEffect(shadow_effect)
        self.set_name_attributes()  # Set font, size, position, etc.
        # Make connector buttons
        self.connectors = dict(
            bottom=ConnectorButton(self, toolbox, position="bottom"),
            left=ConnectorButton(self, toolbox, position="left"),
            right=ConnectorButton(self, toolbox, position="right"),
        )
        # Make exclamation and rank icons
        self.exclamation_icon = ExclamationIcon(self)
        self.rank_icon = RankIcon(self)
        # Group the drawn items together by setting the background rectangle as the parent of other QGraphicsItems
        # NOTE: setting the parent item moves the items as one!
        self.name_item.setParentItem(self)
        for conn in self.connectors.values():
            conn.setParentItem(self)
        self.svg_item.setParentItem(self)
        self.exclamation_icon.setParentItem(self)
        self.rank_icon.setParentItem(self)
        brush = QBrush(background_color)
        self._setup(brush, icon_file, icon_color)
        # Add items to scene
        scene = self._toolbox.ui.graphicsView.scene()
        scene.addItem(self)
示例#11
0
    def _init_comments_or_string(self):

        # remove existing comments or strings
        if self._comment_items:
            for comm in self._comment_items:
                comm: QGraphicsSimpleTextItem
                comm.setParentItem(None)
            self._comment_items = None
        elif self._string_item is not None:
            self._string_item.setParentItem(None)
            self._string_item = None

        # comment or string - comments have precedence
        if self._comment:
            self._string_item = None
            lines = self._comment.split('\n')
            self._comment_items = []
            for line in lines:
                comment = QGraphicsSimpleTextItem(self.COMMENT_PREFIX + line,
                                                  self)
                comment.setFont(self._config.disasm_font)
                comment.setBrush(
                    Qt.darkGreen)  # TODO: Expose it as a setting in Config
                self._comment_items.append(comment)
        elif self._string is not None:
            self._comment_items = None
            self._string_item = QGraphicsSimpleTextItem(self._string, self)
            self._string_item.setFont(self._config.disasm_font)
            self._string_item.setBrush(
                Qt.gray)  # TODO: Expose it as a setting in Config
class QProximityGraphStringBlock(QProximityGraphBlock):
    """
    String Block
    """
    def __init__(self, is_selected, proximity_view: 'ProximityView',
                 node: StringProxiNode):
        self._text = None
        self._text_item: QGraphicsSimpleTextItem = None
        super().__init__(is_selected, proximity_view, node)

    def _init_widgets(self):
        self._text = '"' + self._node.content.decode("utf-8") + '"'

        self._text_item = QGraphicsSimpleTextItem(self._text, self)
        self._text_item.setBrush(self.STRING_NODE_TEXT_COLOR)
        self._text_item.setFont(Conf.symexec_font)
        self._text_item.setPos(self.HORIZONTAL_PADDING, self.VERTICAL_PADDING)

    def paint(self, painter, option, widget):
        self._paint_boundary(painter)

    def _update_size(self):
        width_candidates = [
            self.HORIZONTAL_PADDING * 2 +
            self._text_item.boundingRect().width(),
        ]

        self._width = max(width_candidates)
        self._height = self.VERTICAL_PADDING * 2 + self._text_item.boundingRect(
        ).height()

        self._width = max(30, self._width)
        self._height = max(10, self._height)
        self.recalculate_size()
示例#13
0
    def __init__(self, toolbox, x, y, project_item, icon_file, icon_color,
                 background_color):
        """Base class for project item icons drawn in Design View.

        Args:
            toolbox (ToolBoxUI): QMainWindow instance
            x (float): Icon x coordinate
            y (float): Icon y coordinate
            project_item (ProjectItem): Item
            icon_file (str): Path to icon resource
            icon_color (QColor): Icon's color
            background_color (QColor): Background color
        """
        super().__init__()
        self._toolbox = toolbox
        self._project_item = project_item
        self._moved_on_scene = False
        self._previous_pos = QPointF()
        self._current_pos = QPointF()
        self.icon_group = {self}
        self.renderer = QSvgRenderer()
        self.svg_item = QGraphicsSvgItem(self)
        self.colorizer = QGraphicsColorizeEffect()
        self.setRect(
            QRectF(x - self.ITEM_EXTENT / 2, y - self.ITEM_EXTENT / 2,
                   self.ITEM_EXTENT, self.ITEM_EXTENT))
        self.text_font_size = 10  # point size
        # Make item name graphics item.
        name = project_item.name if project_item else ""
        self.name_item = QGraphicsSimpleTextItem(name, self)
        self.set_name_attributes()  # Set font, size, position, etc.
        # Make connector buttons
        self.connectors = dict(
            bottom=ConnectorButton(self, toolbox, position="bottom"),
            left=ConnectorButton(self, toolbox, position="left"),
            right=ConnectorButton(self, toolbox, position="right"),
        )
        # Make exclamation and rank icons
        self.exclamation_icon = ExclamationIcon(self)
        self.rank_icon = RankIcon(self)
        brush = QBrush(background_color)
        self._setup(brush, icon_file, icon_color)
        self.activate()
示例#14
0
 def __init__(self, toolbox, icon_file, icon_color, background_color):
     """
     Args:
         toolbox (ToolboxUI): QMainWindow instance
         icon_file (str): Path to icon resource
         icon_color (QColor): Icon's color
         background_color (QColor): Background color
     """
     super().__init__()
     self._toolbox = toolbox
     self.icon_file = icon_file
     self._moved_on_scene = False
     self.previous_pos = QPointF()
     self.current_pos = QPointF()
     self.icon_group = {self}
     self.renderer = QSvgRenderer()
     self.svg_item = QGraphicsSvgItem(self)
     self.colorizer = QGraphicsColorizeEffect()
     self.setRect(
         QRectF(-self.ITEM_EXTENT / 2, -self.ITEM_EXTENT / 2,
                self.ITEM_EXTENT, self.ITEM_EXTENT))
     self.text_font_size = 10  # point size
     # Make item name graphics item.
     self._name = ""
     self.name_item = QGraphicsSimpleTextItem(self._name, self)
     self.set_name_attributes()  # Set font, size, position, etc.
     # Make connector buttons
     self.connectors = dict(
         bottom=ConnectorButton(self, toolbox, position="bottom"),
         left=ConnectorButton(self, toolbox, position="left"),
         right=ConnectorButton(self, toolbox, position="right"),
     )
     # Make exclamation and rank icons
     self.exclamation_icon = ExclamationIcon(self)
     self.execution_icon = ExecutionIcon(self)
     self.rank_icon = RankIcon(self)
     brush = QBrush(background_color)
     self._setup(brush, icon_file, icon_color)
     shadow_effect = QGraphicsDropShadowEffect()
     shadow_effect.setOffset(1)
     shadow_effect.setEnabled(False)
     self.setGraphicsEffect(shadow_effect)
示例#15
0
    def _init_widgets(self):

        # variable name
        self._variable_name = "" if not self.variable.name else self.variable.name
        self._variable_name_item = QGraphicsSimpleTextItem(
            self._variable_name, self)
        self._variable_name_item.setFont(self._config.disasm_font)
        self._variable_name_item.setBrush(
            Qt.darkGreen)  # TODO: Expose it as a configuration entry in Config

        # variable ident
        self._variable_ident = "<%s>" % ("" if not self.variable.ident else
                                         self.variable.ident)
        self._variable_ident_item = QGraphicsSimpleTextItem(
            self._variable_ident, self)
        self._variable_ident_item.setFont(self._config.disasm_font)
        self._variable_ident_item.setBrush(
            Qt.blue)  # TODO: Expose it as a configuration entry in Config
        self._variable_ident_item.setVisible(
            self.disasm_view.show_variable_identifier)

        # variable offset
        self._variable_offset = "%#x" % self.variable.offset
        self._variable_offset_item = QGraphicsSimpleTextItem(
            self._variable_offset, self)
        self._variable_offset_item.setFont(self._config.disasm_font)
        self._variable_offset_item.setBrush(
            Qt.darkYellow
        )  # TODO: Expose it as a configuration entry in Config

        self._layout_items_and_update_size()
示例#16
0
class GameStateView(QGraphicsView):
    """A widget displaying a view of the chess game state."""
    def __init__(self, parent):
        QGraphicsView.__init__(self, parent)
        self.setMinimumSize(180, 115)
        self.setMaximumSize(180, 115)
        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.text_item = QGraphicsSimpleTextItem()
        self.scene.addItem(self.text_item)
        self.show()

    def set_game_state(self, game_state):
        """
        Updates the game state view with the provided values.

        :param game_state: Dictionary containing {topic:value} pairs
        """
        text = ''
        for topic in game_state:
            text += topic + ': ' + game_state[topic] + '\n'

        self.text_item.setText(text)
class QProximityGraphFunctionBlock(QProximityGraphBlock):
    """
    Function Block
    """
    def __init__(self, is_selected, proximity_view: 'ProximityView',
                 node: FunctionProxiNode):
        self._text = None
        self._text_item: QGraphicsSimpleTextItem = None
        super().__init__(is_selected, proximity_view, node)

    def _init_widgets(self):
        self._text = f"Function {self._node.func.name}"
        self._text_item = QGraphicsSimpleTextItem(self._text, self)
        self._text_item.setFont(Conf.symexec_font)
        self._text_item.setBrush(self.FUNCTION_NODE_TEXT_COLOR)
        self._text_item.setPos(self.HORIZONTAL_PADDING, self.VERTICAL_PADDING)

    def mouseDoubleClickEvent(self, event):
        if event.button() == Qt.LeftButton and (
                event.modifiers() & Qt.ControlModifier) == Qt.ControlModifier:
            # ctrl + double click to collapse a function call
            event.accept()
            self._proximity_view.collapse_function(self._node.func)
            return

        super().mouseDoubleClickEvent(event)

    def paint(self, painter, option, widget):
        self._paint_boundary(painter)

    def _update_size(self):
        width_candidates = [
            self.HORIZONTAL_PADDING * 2 +
            self._text_item.boundingRect().width(),
        ]

        self._width = max(width_candidates)
        self._height = self.VERTICAL_PADDING * 2 + self._text_item.boundingRect(
        ).height()

        self._width = max(30, self._width)
        self._height = max(10, self._height)
        self.recalculate_size()
示例#18
0
if __name__ == '__main__':
    app = QApplication(sys.argv)
    scene = QGraphicsScene(QRectF(-50, -50, 400, 200))

    rect = QGraphicsRectItem(scene.sceneRect().adjusted(1, 1, -1, -1))
    rect.setPen(QPen(Qt.red, 1))
    scene.addItem(rect)

    rectItem = QGraphicsRectItem(QRectF(-25, 25, 200, 40))
    rectItem.setPen(QPen(Qt.red, 3, Qt.DashDotLine))
    rectItem.setBrush(Qt.gray)
    scene.addItem(rectItem)
    print(f'Rect Pos: {rectItem.pos()}')

    textItem = QGraphicsSimpleTextItem("Foundation of Qt")
    scene.addItem(textItem)
    print(f'Text Pos: {textItem.pos()}')
    textItem.setPos(50, 0)
    print(f'Text Pos: {textItem.pos()}')

    ellipseItem = QGraphicsEllipseItem(QRectF(170, 20, 100, 75))
    ellipseItem.setPen(QPen(Qt.darkBlue))
    ellipseItem.setBrush(Qt.blue)
    scene.addItem(ellipseItem)

    points = [
        QPointF(10, 10),
        QPointF(0, 90),
        QPointF(40, 70),
        QPointF(80, 110),
示例#19
0
    def _init_widgets(self):

        if self.is_branch_target:
            # a branch instruction

            is_target_func = bool(self.branch_targets is not None and next(
                iter(self.branch_targets)) in self.disasm.kb.functions)

            self._label = self.operand.render()[0]
            self._is_target_func = is_target_func

            if self.is_indirect_branch:
                # indirect jump
                self._branch_targets = self.branch_targets
                first_n_targets = self._first_n_branch_targets(
                    self._branch_targets, 3)
                if first_n_targets:
                    targets = []
                    for t in first_n_targets:
                        txt = None
                        if is_target_func:
                            # try to get a function
                            try:
                                target_func = self.disasm.kb.functions.get_by_addr(
                                    t)
                                txt = target_func.demangled_name
                            except KeyError:
                                pass
                        # is the address a label?
                        if txt is None and t in self.disasm.kb.labels:
                            txt = self.disasm.kb.labels[t]
                        if txt is None:
                            # use the hex text
                            txt = "%#08x" % t
                        targets.append(txt)
                    self._branch_targets_text = "[ " + ", ".join(
                        targets) + " ]"
                else:
                    self._branch_targets_text = "[ unknown ]"

                if self._branch_targets and len(self._branch_targets) == 1:
                    self._branch_target = next(iter(self._branch_targets))

            else:
                self._branch_target = self._branch_target_for_operand(
                    self.operand, self.branch_targets)

        else:
            # not a branch

            formatting = {}
            if isinstance(self.operand, MemoryOperand):
                variable_sort = 'memory'
            elif isinstance(self.operand, RegisterOperand):
                variable_sort = 'register'
            else:
                variable_sort = None

            # without displaying variable
            self._label = self.operand.render(formatting=formatting)[0]

            if variable_sort:
                # try find the corresponding variable
                variable_and_offsets = self.variable_manager[
                    self.func_addr].find_variables_by_insn(
                        self.insn.addr, variable_sort)
                if variable_and_offsets:
                    variable, offset = self._pick_variable(
                        variable_and_offsets)

                    if variable is not None:
                        self.variable = variable
                        self._variable_ident = "<%s>" % variable.ident
                        if offset is None:
                            offset = 0

                        variable_str = variable.name

                        ident = (self.insn.addr, 'operand', self.operand_index)
                        if 'custom_values_str' not in formatting:
                            formatting['custom_values_str'] = {}
                        if variable_sort == 'memory':
                            if offset == 0: custom_value_str = variable_str
                            else:
                                custom_value_str = "%s[%d]" % (variable_str,
                                                               offset)
                        else:
                            custom_value_str = ''

                        ##
                        # Hacks
                        ##
                        if self.infodock.induction_variable_analysis is not None:
                            r = self.infodock.induction_variable_analysis.variables.get(
                                variable.ident, None)
                            if r is not None and r.expr.__class__.__name__ == "InductionExpr":
                                custom_value_str = "i*%d+%d" % (r.expr.stride,
                                                                r.expr.init)
                            if r is not None and r.expr.__class__.__name__ == "Add" and r.expr.operands[
                                    0].__class__.__name__ == "InductionExpr":
                                custom_value_str = "i*%d+%d" % (
                                    r.expr.operands[0].stride,
                                    r.expr.operands[0].init +
                                    r.expr.operands[1].value)

                        formatting['custom_values_str'][
                            ident] = custom_value_str

                        if 'show_prefix' not in formatting:
                            formatting['show_prefix'] = {}
                        formatting['show_prefix'][ident] = 'False'

                        if 'values_style' not in formatting:
                            formatting['values_style'] = {}
                        formatting['values_style'][ident] = 'curly'

                    # with variable displayed
                    if variable_sort == 'memory':
                        self._variable_label = self.operand.render(
                            formatting=formatting)[0]
                    else:
                        self._variable_label = ''

        if self._branch_target or self._branch_targets:
            if self._is_target_func:
                label_color = self._config.disasm_view_target_addr_color
            else:
                label_color = self._config.disasm_view_antitarget_addr_color
        else:
            if self.is_constant:
                label_color = self._config.disasm_view_operand_constant_color
            else:
                label_color = self._config.disasm_view_operand_color

        # label
        # [rax]
        self._label_item = QGraphicsSimpleTextItem(self._label, self)
        self._label_item.setFont(self._config.disasm_font)
        self._label_item.setBrush(label_color)

        # variable
        # {s_10}
        if self._variable_label:
            self._variable_label_item = QGraphicsSimpleTextItem(
                self._variable_label, self)
            self._variable_label_item.setFont(self._config.disasm_font)
            self._variable_label_item.setBrush(
                self._config.disasm_view_variable_label_color)

        # additional branch targets
        if self._branch_targets_text:
            self._branch_targets_item = QGraphicsSimpleTextItem(
                self._branch_targets_text, self)
            self._branch_targets_item.setFont(self._config.disasm_font)
            self._branch_targets_item.setBrush(
                Qt.darkYellow
            )  # TODO: Expose as a configuration entry in Config

        # variable identifier
        if self.variable is not None and self.disasm_view.show_variable_identifier:
            self._variable_ident_item = QGraphicsSimpleTextItem(
                self._variable_ident, self)
            self._variable_ident_item.setFont(self._config.disasm_font)
            self._variable_ident_item.setBrush(
                Qt.darkGreen
            )  # TODO: Expose as a configuration entry in Config

        self._layout_items_and_update_size()
示例#20
0
class QOperand(QCachedGraphicsItem):

    BRANCH_TARGETS_SPACING = 5
    LABEL_VARIABLE_SPACING = 5
    VARIABLE_IDENT_SPACING = 5

    def __init__(self,
                 workspace,
                 func_addr,
                 disasm_view,
                 disasm,
                 infodock,
                 insn,
                 operand,
                 operand_index,
                 is_branch_target,
                 is_indirect_branch,
                 branch_targets,
                 config,
                 parent=None):
        super().__init__(parent=parent)

        self.workspace = workspace
        self.func_addr = func_addr
        self.disasm_view = disasm_view
        self.disasm = disasm
        self.infodock = infodock
        self.variable_manager = infodock.variable_manager
        self.insn = insn
        self.operand = operand
        self.operand_index = operand_index
        self.is_branch_target = is_branch_target
        self.is_indirect_branch = is_indirect_branch
        self.branch_targets = branch_targets

        self._branch_target: Optional[int] = None

        # the variable involved
        self.variable = None

        self._cachy = None

        self._config = config

        # "widgets"
        self._label = None
        self._label_item: Optional[QGraphicsSimpleTextItem] = None
        self._variable_label = None
        self._variable_label_item: Optional[QGraphicsSimpleTextItem] = None
        self._variable_ident = None
        self._variable_ident_item: Optional[QGraphicsSimpleTextItem] = None
        self._branch_targets = None
        self._branch_targets_text = None
        self._branch_targets_item: Optional[QGraphicsSimpleTextItem] = None
        self._is_target_func = None

        self._width = None
        self._height = None

        self._init_widgets()

    #
    # Properties
    #

    @property
    def text(self):
        return self._label

    @property
    def is_constant(self):
        return isinstance(self.operand, ConstantOperand)

    @property
    def constant_value(self):
        if self.is_constant:
            return self.operand.cs_operand.imm
        return None

    @property
    def is_constant_memory(self):
        return (isinstance(self.operand, MemoryOperand)
                and len(self.operand.values) == 1
                and isinstance(self.operand.values[0], Value)
                and isinstance(self.operand.values[0].val, int))

    @property
    def constant_memory_value(self):
        if self.is_constant_memory:
            return self.operand.values[0].val
        return None

    @property
    def selected(self):
        return self.infodock.is_operand_selected(self.insn.addr,
                                                 self.operand_index)

    @property
    def operand_descriptor(self):
        return OperandDescriptor(self.text,
                                 None,
                                 func_addr=self.func_addr,
                                 variable_ident=self.variable.ident
                                 if self.variable is not None else None)

    #
    # Event handlers
    #

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            selected = self.infodock.toggle_operand_selection(
                self.insn.addr,
                self.operand_index,
                self.operand_descriptor,
                insn_pos=self.parentItem().scenePos(),
                unique=QApplication.keyboardModifiers() != Qt.ControlModifier)
            if selected:
                # select the current instruction, too
                self.infodock.select_instruction(self.insn.addr,
                                                 insn_pos=QPointF(
                                                     self.x(), self.y()),
                                                 unique=True)
        else:
            super().mousePressEvent(event)

    def mouseDoubleClickEvent(self, event):
        button = event.button()
        if button == Qt.LeftButton:
            if self._branch_target is not None:
                self.disasm_view.jump_to(self._branch_target,
                                         src_ins_addr=self.insn.addr,
                                         use_animation=True)
                return
            if self.is_constant:
                self.disasm_view.jump_to(self.constant_value,
                                         src_ins_addr=self.insn.addr,
                                         use_animation=True)
                return
            if self.is_constant_memory:
                self.disasm_view.jump_to(self.constant_memory_value,
                                         src_ins_addr=self.insn.addr,
                                         use_animation=True)
        else:
            super().mouseDoubleClickEvent(event)

    #
    # Public methods
    #

    def refresh(self):
        self._layout_items_and_update_size()
        self.recalculate_size()

    def paint(self, painter, option, widget):  #pylint: disable=unused-argument

        # Background
        if self.selected:
            painter.setPen(self._config.disasm_view_operand_select_color)
            painter.setBrush(self._config.disasm_view_operand_select_color)
            painter.drawRect(0, 0, self.width, self.height)
        else:
            for _, selected_operand_desc in self.infodock.selected_operands.items(
            ):
                if self._equals_for_highlighting_purposes(
                        selected_operand_desc):
                    painter.setBrush(
                        self._config.disasm_view_operand_highlight_color)
                    painter.setPen(
                        self._config.disasm_view_operand_highlight_color)
                    painter.drawRect(0, 0, self.width, self.height)
                    break

    #
    # Private methods
    #

    def _branch_target_for_operand(self, operand, branch_targets):
        if not branch_targets:
            return None

        if len(branch_targets) == 1:
            return next(iter(branch_targets))

        # there are more than one targets
        # we pick the one that complies with the operand's text
        # my solution is pretty hackish...

        imm = self.constant_value
        if imm is not None and imm in branch_targets:
            # problem solved
            return imm
        else:
            # umm why?
            pass

        # try to render it
        rendered = operand.render()[0]
        for t in branch_targets:
            if rendered in ("%x" % t, "%#x" % t):
                return t
            if t == rendered:
                return t

        # ouch not sure what to do
        l.warning(
            'Cannot determine branch targets for operand "%s". Please report on GitHub.',
            rendered)
        # return a random one
        return next(iter(branch_targets))

    @staticmethod
    def _first_n_branch_targets(branch_targets, n):

        if not branch_targets:
            return []

        return list(branch_targets)[:n]

    def _init_widgets(self):

        if self.is_branch_target:
            # a branch instruction

            is_target_func = bool(self.branch_targets is not None and next(
                iter(self.branch_targets)) in self.disasm.kb.functions)

            self._label = self.operand.render()[0]
            self._is_target_func = is_target_func

            if self.is_indirect_branch:
                # indirect jump
                self._branch_targets = self.branch_targets
                first_n_targets = self._first_n_branch_targets(
                    self._branch_targets, 3)
                if first_n_targets:
                    targets = []
                    for t in first_n_targets:
                        txt = None
                        if is_target_func:
                            # try to get a function
                            try:
                                target_func = self.disasm.kb.functions.get_by_addr(
                                    t)
                                txt = target_func.demangled_name
                            except KeyError:
                                pass
                        # is the address a label?
                        if txt is None and t in self.disasm.kb.labels:
                            txt = self.disasm.kb.labels[t]
                        if txt is None:
                            # use the hex text
                            txt = "%#08x" % t
                        targets.append(txt)
                    self._branch_targets_text = "[ " + ", ".join(
                        targets) + " ]"
                else:
                    self._branch_targets_text = "[ unknown ]"

                if self._branch_targets and len(self._branch_targets) == 1:
                    self._branch_target = next(iter(self._branch_targets))

            else:
                self._branch_target = self._branch_target_for_operand(
                    self.operand, self.branch_targets)

        else:
            # not a branch

            formatting = {}
            if isinstance(self.operand, MemoryOperand):
                variable_sort = 'memory'
            elif isinstance(self.operand, RegisterOperand):
                variable_sort = 'register'
            else:
                variable_sort = None

            # without displaying variable
            self._label = self.operand.render(formatting=formatting)[0]

            if variable_sort:
                # try find the corresponding variable
                variable_and_offsets = self.variable_manager[
                    self.func_addr].find_variables_by_insn(
                        self.insn.addr, variable_sort)
                if variable_and_offsets:
                    variable, offset = self._pick_variable(
                        variable_and_offsets)

                    if variable is not None:
                        self.variable = variable
                        self._variable_ident = "<%s>" % variable.ident
                        if offset is None:
                            offset = 0

                        variable_str = variable.name

                        ident = (self.insn.addr, 'operand', self.operand_index)
                        if 'custom_values_str' not in formatting:
                            formatting['custom_values_str'] = {}
                        if variable_sort == 'memory':
                            if offset == 0: custom_value_str = variable_str
                            else:
                                custom_value_str = "%s[%d]" % (variable_str,
                                                               offset)
                        else:
                            custom_value_str = ''

                        ##
                        # Hacks
                        ##
                        if self.infodock.induction_variable_analysis is not None:
                            r = self.infodock.induction_variable_analysis.variables.get(
                                variable.ident, None)
                            if r is not None and r.expr.__class__.__name__ == "InductionExpr":
                                custom_value_str = "i*%d+%d" % (r.expr.stride,
                                                                r.expr.init)
                            if r is not None and r.expr.__class__.__name__ == "Add" and r.expr.operands[
                                    0].__class__.__name__ == "InductionExpr":
                                custom_value_str = "i*%d+%d" % (
                                    r.expr.operands[0].stride,
                                    r.expr.operands[0].init +
                                    r.expr.operands[1].value)

                        formatting['custom_values_str'][
                            ident] = custom_value_str

                        if 'show_prefix' not in formatting:
                            formatting['show_prefix'] = {}
                        formatting['show_prefix'][ident] = 'False'

                        if 'values_style' not in formatting:
                            formatting['values_style'] = {}
                        formatting['values_style'][ident] = 'curly'

                    # with variable displayed
                    if variable_sort == 'memory':
                        self._variable_label = self.operand.render(
                            formatting=formatting)[0]
                    else:
                        self._variable_label = ''

        if self._branch_target or self._branch_targets:
            if self._is_target_func:
                label_color = self._config.disasm_view_target_addr_color
            else:
                label_color = self._config.disasm_view_antitarget_addr_color
        else:
            if self.is_constant:
                label_color = self._config.disasm_view_operand_constant_color
            else:
                label_color = self._config.disasm_view_operand_color

        # label
        # [rax]
        self._label_item = QGraphicsSimpleTextItem(self._label, self)
        self._label_item.setFont(self._config.disasm_font)
        self._label_item.setBrush(label_color)

        # variable
        # {s_10}
        if self._variable_label:
            self._variable_label_item = QGraphicsSimpleTextItem(
                self._variable_label, self)
            self._variable_label_item.setFont(self._config.disasm_font)
            self._variable_label_item.setBrush(
                self._config.disasm_view_variable_label_color)

        # additional branch targets
        if self._branch_targets_text:
            self._branch_targets_item = QGraphicsSimpleTextItem(
                self._branch_targets_text, self)
            self._branch_targets_item.setFont(self._config.disasm_font)
            self._branch_targets_item.setBrush(
                Qt.darkYellow
            )  # TODO: Expose as a configuration entry in Config

        # variable identifier
        if self.variable is not None and self.disasm_view.show_variable_identifier:
            self._variable_ident_item = QGraphicsSimpleTextItem(
                self._variable_ident, self)
            self._variable_ident_item.setFont(self._config.disasm_font)
            self._variable_ident_item.setBrush(
                Qt.darkGreen
            )  # TODO: Expose as a configuration entry in Config

        self._layout_items_and_update_size()

    def _layout_items_and_update_size(self):

        x, y = 0, 0

        # label
        self._label_item.setPos(x, y)
        x += self._label_item.boundingRect().width()

        # variable
        if self._variable_label_item is not None:
            if self.disasm_view.show_variable:
                x += self.LABEL_VARIABLE_SPACING
                self._variable_label_item.setPos(x, y)
                x += self._variable_label_item.boundingRect().width()
                self._variable_label_item.show()
            else:
                self._variable_label_item.hide()

        # additional branch targets
        if self._branch_targets_item is not None:
            x += self.BRANCH_TARGETS_SPACING
            self._branch_targets_item.setPos(x, y)
            x += self._branch_targets_item.boundingRect().width()

        # variable identifier
        if self._variable_ident_item is not None:
            x += self.VARIABLE_IDENT_SPACING
            self._variable_ident_item.setPos(x, y)
            x += self._variable_ident_item.boundingRect().width()

        self._width = x
        self._height = self._label_item.boundingRect().height()
        self.recalculate_size()

    def _boundingRect(self):
        return QRectF(0, 0, self._width, self._height)

    def _pick_variable(self, variable_and_offsets):
        """
        Pick the corresponding variable for the current operand.

        :param list variable_and_offsets:   A list of variables and the offsets into each variable.
        :return:                            A tuple of variable and the offset.
        :rtype:                             tuple
        """

        if isinstance(self.operand, MemoryOperand):
            if len(variable_and_offsets) > 1:
                l.error(
                    "Instruction %#x has two memory operands. Please report it on GitHub.",
                    self.insn.addr)
            return variable_and_offsets[0]

        elif isinstance(self.operand, RegisterOperand):
            # there might be multiple register-type variables for an instruction. pick the right one is... not easy

            the_reg = self.operand.register
            if the_reg is None:
                # huh, it does not have a Register child
                return None, None

            reg_name = the_reg.reg
            arch = self.workspace.instance.project.arch

            if len(variable_and_offsets) == 1:
                # only one candidate...
                var, offset = variable_and_offsets[0]
                if arch.registers[reg_name][0] == var.reg:
                    return var, offset
                return None, None

            if self.operand_index > 0:
                # this is the source operand
                # which variable is read here?
                for var, offset in variable_and_offsets:
                    if arch.registers[reg_name][0] == var.reg:
                        if self._variable_has_access(var, self.insn.addr,
                                                     'read'):
                            return var, offset

                l.debug(
                    'Cannot find any source variable for operand %d at instruction %#x.',
                    self.operand_index, self.insn.addr)
                return None, None

            # this is the destination operand
            # which variable is written here?
            for var, offset in variable_and_offsets:
                if arch.registers[reg_name][0] == var.reg:
                    if self._variable_has_access(var, self.insn.addr, 'write'):
                        return var, offset

            l.debug(
                'Cannot find any destination variable for operand %d at instruction %#x.',
                self.operand_index, self.insn.addr)
            # just return the first one
            return None, None

        else:
            # what's this type? why am I here?
            l.error('_pick_variable: Unsupported operand type %s.',
                    self.operand.__class__)

            return None, None

    def _variable_has_access(self, variable, ins_addr, access_type):

        if variable not in self.variable_manager[
                self.func_addr]._variable_accesses:
            l.error('Variable %s does not have any accessing records.',
                    variable)
            return False

        accesses = self.variable_manager[
            self.func_addr]._variable_accesses[variable]
        for access in accesses:
            if access.location.ins_addr == ins_addr and access.access_type == access_type:
                return True

        return False

    def _equals_for_highlighting_purposes(self, other):
        """

        :param OperandDescriptor other: The other operand to compare with.
        :return:
        """

        if other is None:
            return False

        highlight_mode = self.infodock.highlight_mode

        if highlight_mode == OperandHighlightMode.SAME_TEXT or self.variable is None:
            # when there is no related variable, we highlight as long as they have the same text
            return other.text == self.text
        elif highlight_mode == OperandHighlightMode.SAME_IDENT:
            if self.variable is not None and other.variable_ident is not None:
                return self.func_addr == other.func_addr and self.variable.ident == other.variable_ident

        return False
示例#21
0
    def _init_line(self, addr, byte_offset, all_bytes):

        # colors
        printable_byte_color = Conf.disasm_view_printable_byte_color
        printable_char_color = Conf.disasm_view_printable_character_color
        unprintable_byte_color = Conf.disasm_view_unprintable_byte_color
        unprintable_char_color = Conf.disasm_view_unprintable_character_color
        unknown_byte_color = Conf.disasm_view_unknown_byte_color
        unknown_char_color = Conf.disasm_view_unknown_character_color

        # address
        addr_text = "%08x" % addr
        addr_item = QGraphicsSimpleTextItem(addr_text, self)
        addr_item.setBrush(Conf.disasm_view_node_address_color)
        addr_item.setFont(Conf.disasm_font)

        # draw each byte
        bytes_list = [ ]
        for idx, byt in enumerate(all_bytes):
            if type(byt) is int:
                if is_printable(byt):
                    color = printable_byte_color
                else:
                    color = unprintable_byte_color
                o = QGraphicsSimpleTextItem("%02x" % byt, self)
                o.setFont(Conf.disasm_font)
                o.setBrush(color)
            else:  # str, usually because it is an unknown byte, in which case the str is "??"
                o = QGraphicsSimpleTextItem(byt, self)
                o.setBrush(unknown_byte_color)
                o.setFont(Conf.disasm_font)
            bytes_list.append(o)

            line_chars = byte_offset + idx + 1  # the number of existing characters on this line, including spaces
            if line_chars % 8 == 0 and line_chars != self.bytes_per_line:
                # print a deliminator
                o = QGraphicsSimpleTextItem("-", self)
                o.setBrush(Qt.black)
                o.setFont(Conf.disasm_font)
                bytes_list.append(o)

        # printable characters
        character_list = [ ]
        for byt in all_bytes:
            if type(byt) is int:
                if is_printable(byt):
                    color = printable_char_color
                    ch = chr(byt)
                else:
                    color = unprintable_char_color
                    ch = "."
            else:
                color = unknown_char_color
                ch = "?"
            o = QGraphicsSimpleTextItem(ch, self)
            o.setBrush(color)
            o.setFont(Conf.disasm_font)
            character_list.append(o)

        return addr_item, bytes_list, character_list
示例#22
0
class QMemoryDataBlock(QCachedGraphicsItem):

    ADDRESS_LABEL_OFFSET = 20
    LINEAR_INSTRUCTION_OFFSET = 120
    BYTE_AREA_SPACING = 15

    def __init__(self, workspace, infodock, addr, memory_data, bytes_per_line=16, parent=None):
        super().__init__(parent=parent)
        self.workspace = workspace
        self.infodock = infodock
        self.addr = addr
        self.memory_data: MemoryData = memory_data
        self.bytes_per_line: int = bytes_per_line  # TODO: Move it to Conf

        self._addr_text = None
        self._width = None
        self._height = None

        self._bytes = [ ]

        # widgets
        self._addr_item: QGraphicsSimpleTextItem = None
        self._label_item: Optional[QGraphicsSimpleTextItem] = None
        self._line_items: List[Tuple[int,
                                     QGraphicsSimpleTextItem,
                                     List[QGraphicsSimpleTextItem],
                                     List[QGraphicsSimpleTextItem]]] = None

        self._init_widgets()

    #
    # Public methods
    #

    @property
    def width(self):
        return self.boundingRect().width()

    @property
    def height(self):
        return self.boundingRect().height()

    def paint(self, painter, option, widget):

        should_highlight = self.infodock.is_label_selected(self.addr)

        highlight_color = Conf.disasm_view_label_highlight_color
        if should_highlight:
            painter.setBrush(highlight_color)
            painter.setPen(highlight_color)
            painter.drawRect(0, 0, self.width, self.height)

    #
    # Event handlers
    #

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            # unselect all other labels
            self.infodock.unselect_all_labels()
            # select this label
            self.infodock.select_label(self.addr)

    #
    # Private methods
    #

    def _init_widgets(self):

        self._addr_text = "%08x" % self.addr
        self._bytes = [ ]
        if self.memory_data.content:
            for byt in self.memory_data.content:
                self._bytes.append(byt)

        if len(self._bytes) < self.memory_data.size:
            # load more from mapped memory
            start_address = self.memory_data.addr + len(self._bytes)
            size = self.memory_data.size - len(self._bytes)
            try:
                mem_bytes = self.workspace.instance.project.loader.memory.load(start_address, size)
            except KeyError:
                mem_bytes = b""
            self._bytes += [ b for b in mem_bytes ] + [ '??' ] * (size - len(mem_bytes))

        # address
        self._addr_item = QGraphicsSimpleTextItem(self._addr_text, self)
        self._addr_item.setFont(Conf.disasm_font)
        self._addr_item.setBrush(Conf.disasm_view_node_address_color)

        # label
        self._init_label_item()

        # bytes
        self._init_bytes()

        self._layout_items_and_update_size()

    def _init_label_item(self):
        lbl_text = get_label_text(self.addr, self.workspace.instance.kb)
        if lbl_text:
            self._label_item = QGraphicsSimpleTextItem(lbl_text, self)
            self._label_item.setFont(Conf.code_font)
            self._label_item.setBrush(Conf.disasm_view_label_color)
        else:
            if self._label_item is not None:
                self._label_item.setParentItem(None)
                self._label_item = None

    def _init_bytes(self):

        if self._line_items:
            # remove existing items
            for line in self._line_items:
                for item in line:
                    item.setParentItem(None)
            self._line_items = None

        addr = self.addr
        i = 0
        self._line_items = []

        while i < len(self._bytes):

            byte_offset = addr % self.bytes_per_line
            if byte_offset == 0:
                end_pos = i + self.bytes_per_line
            else:
                end_pos = self.bytes_per_line - byte_offset

            all_bytes = self._bytes[i : end_pos]
            # print("... print %#x, %d bytes, byte_offset %d" % (addr, len(all_bytes), byte_offset))
            addr_item, bytes_list, character_list = self._init_line(addr, byte_offset, all_bytes)
            self._line_items.append((byte_offset, addr_item, bytes_list, character_list))

            addr += end_pos - i
            i = end_pos

    def _init_line(self, addr, byte_offset, all_bytes):

        # colors
        printable_byte_color = Conf.disasm_view_printable_byte_color
        printable_char_color = Conf.disasm_view_printable_character_color
        unprintable_byte_color = Conf.disasm_view_unprintable_byte_color
        unprintable_char_color = Conf.disasm_view_unprintable_character_color
        unknown_byte_color = Conf.disasm_view_unknown_byte_color
        unknown_char_color = Conf.disasm_view_unknown_character_color

        # address
        addr_text = "%08x" % addr
        addr_item = QGraphicsSimpleTextItem(addr_text, self)
        addr_item.setBrush(Conf.disasm_view_node_address_color)
        addr_item.setFont(Conf.disasm_font)

        # draw each byte
        bytes_list = [ ]
        for idx, byt in enumerate(all_bytes):
            if type(byt) is int:
                if is_printable(byt):
                    color = printable_byte_color
                else:
                    color = unprintable_byte_color
                o = QGraphicsSimpleTextItem("%02x" % byt, self)
                o.setFont(Conf.disasm_font)
                o.setBrush(color)
            else:  # str, usually because it is an unknown byte, in which case the str is "??"
                o = QGraphicsSimpleTextItem(byt, self)
                o.setBrush(unknown_byte_color)
                o.setFont(Conf.disasm_font)
            bytes_list.append(o)

            line_chars = byte_offset + idx + 1  # the number of existing characters on this line, including spaces
            if line_chars % 8 == 0 and line_chars != self.bytes_per_line:
                # print a deliminator
                o = QGraphicsSimpleTextItem("-", self)
                o.setBrush(Qt.black)
                o.setFont(Conf.disasm_font)
                bytes_list.append(o)

        # printable characters
        character_list = [ ]
        for byt in all_bytes:
            if type(byt) is int:
                if is_printable(byt):
                    color = printable_char_color
                    ch = chr(byt)
                else:
                    color = unprintable_char_color
                    ch = "."
            else:
                color = unknown_char_color
                ch = "?"
            o = QGraphicsSimpleTextItem(ch, self)
            o.setBrush(color)
            o.setFont(Conf.disasm_font)
            character_list.append(o)

        return addr_item, bytes_list, character_list

    def _layout_items_and_update_size(self):

        x, y = 0, 0

        #
        # first line
        #

        # address
        self._addr_item.setPos(x, y)
        x += self._addr_item.boundingRect().width()

        # label
        if self._label_item:
            x += self.ADDRESS_LABEL_OFFSET
            self._label_item.setPos(x, y)

        #
        # the following lines: content
        #

        max_x = x
        x = 0
        y += self._addr_item.boundingRect().height()

        for byte_offset, addr_item, bytes_line, characters_line in self._line_items:
            addr_item.setPos(x, y)
            x += addr_item.boundingRect().width() + self.LINEAR_INSTRUCTION_OFFSET

            # skip byte offset
            byte_width = bytes_line[0].boundingRect().width()
            byte_spacing = byte_width // 2
            x += byte_offset * (byte_width + byte_spacing)

            all_bytes = 0
            pos = 0
            while pos < len(bytes_line):
                byte_ = bytes_line[pos]
                byte_.setPos(x, y)
                x += byte_width

                line_chars = byte_offset + all_bytes + 1  # the number of existing characters on this line, including spaces
                if line_chars % 8 == 0 and line_chars != self.bytes_per_line:
                    # now we get a delimiter
                    pos += 1
                    delimiter = bytes_line[pos]
                    delimiter.setPos(x, y)

                x += byte_spacing
                pos += 1
                all_bytes += 1

            if (byte_offset + all_bytes) % self.bytes_per_line != 0:
                more_chars = self.bytes_per_line - (byte_offset + all_bytes % self.bytes_per_line)
                x += more_chars * (byte_width + byte_spacing)

            x += self.BYTE_AREA_SPACING

            # printable characters
            character_width = characters_line[0].boundingRect().width()
            x += byte_offset * character_width
            for o in characters_line:
                o.setPos(x, y)
                x += character_width

            max_x = max(x, max_x)

            # next line!
            x = 0
            y += bytes_line[0].boundingRect().height()

        self._width = max_x
        self._height = y
        self.recalculate_size()

    def _boundingRect(self):
        return QRectF(0, 0, self._width, self._height)
示例#23
0
    def _init_widgets(self):

        self.load_comment()
        self._operands.clear()

        # address
        self._addr = "%08x" % self.insn.addr
        self._addr_item = QGraphicsSimpleTextItem(self)
        self._addr_item.setBrush(
            QBrush(self._config.disasm_view_node_address_color))
        self._addr_item.setFont(self._config.disasm_font)
        self._addr_item.setText(self._addr)

        # mnemonic
        self._mnemonic = self.insn.mnemonic.render()[0]
        self._mnemonic_item = QGraphicsSimpleTextItem(self)
        self._mnemonic_item.setFont(self._config.disasm_font)
        self._mnemonic_item.setBrush(
            self._config.disasm_view_node_mnemonic_color)
        self._mnemonic_item.setText(self._mnemonic)

        # operands
        for i, operand in enumerate(self.insn.operands):
            is_branch_target = self.insn.type in (
                'branch', 'call') and i == self.insn.branch_target_operand
            is_indirect_branch = self.insn.branch_type == 'indirect'
            branch_targets = None
            if is_branch_target:
                if self.out_branch is not None:
                    branch_targets = self.out_branch.targets
                else:
                    # it does not create multiple branches. e.g., a call instruction
                    if len(operand.children) == 1 and type(
                            operand.children[0]) is Value:
                        branch_targets = (operand.children[0].val, )
            qoperand = QOperand(self.workspace,
                                self.func_addr,
                                self.disasm_view,
                                self.disasm,
                                self.infodock,
                                self.insn,
                                operand,
                                i,
                                is_branch_target,
                                is_indirect_branch,
                                branch_targets,
                                self._config,
                                parent=self)
            self._operands.append(qoperand)

        # all commas
        for _ in range(len(self._operands) - 1):
            comma = QGraphicsSimpleTextItem(self.INTERSPERSE_ARGS, self)
            comma.setFont(self._config.disasm_font)
            comma.setBrush(self._config.disasm_view_node_mnemonic_color)
            self._commas.append(comma)

        if should_display_string_label(self.workspace.instance.cfg,
                                       self.insn.addr,
                                       self.workspace.instance.project):
            # yes we should display a string label
            self._string = get_string_for_display(
                self.workspace.instance.cfg, self.insn.addr,
                self.workspace.instance.project)
            if self._string is None:
                self._string = "<Unknown>"

        self._init_comments_or_string()

        self._layout_items_and_update_size()
示例#24
0
def center_text_item(item: QGraphicsSimpleTextItem, x: float, y: float):
    bounds = item.boundingRect()
    x -= bounds.width() // 2
    y -= bounds.height() // 2
    item.setPos(x, y)
示例#25
0
class View(QGraphicsView):
    def __init__(self, parent=None):
        super(View, self).__init__(parent)
        self.setScene(QGraphicsScene(self))

        self.setDragMode(QGraphicsView.NoDrag)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # Chart
        self._chart = QtCharts.QChart()
        self._chart.setMinimumSize(640, 480)
        self._chart.setTitle("Hover the line to show callout. Click the line "
                             "to make it stay")
        self._chart.legend().hide()
        self.series = QtCharts.QLineSeries()
        self.series.append(1, 3)
        self.series.append(4, 5)
        self.series.append(5, 4.5)
        self.series.append(7, 1)
        self.series.append(11, 2)
        self._chart.addSeries(self.series)

        self.series2 = QtCharts.QSplineSeries()
        self.series2.append(1.6, 1.4)
        self.series2.append(2.4, 3.5)
        self.series2.append(3.7, 2.5)
        self.series2.append(7, 4)
        self.series2.append(10, 2)
        self._chart.addSeries(self.series2)

        self._chart.createDefaultAxes()
        self._chart.setAcceptHoverEvents(True)

        self.setRenderHint(QPainter.Antialiasing)
        self.scene().addItem(self._chart)

        self._coordX = QGraphicsSimpleTextItem(self._chart)
        self._coordX.setPos(self._chart.size().width() / 2 - 50,
                            self._chart.size().height())
        self._coordX.setText("X: ")
        self._coordY = QGraphicsSimpleTextItem(self._chart)
        self._coordY.setPos(self._chart.size().width() / 2 + 50,
                            self._chart.size().height())
        self._coordY.setText("Y: ")

        self._callouts = []
        self._tooltip = Callout(self._chart)

        self.series.clicked.connect(self.keepCallout)
        self.series.hovered.connect(self.tooltip)

        self.series2.clicked.connect(self.keepCallout)
        self.series2.hovered.connect(self.tooltip)

        self.setMouseTracking(True)

    def resizeEvent(self, event):
        if self.scene():
            self.scene().setSceneRect(QRectF(QPointF(0, 0), event.size()))
            self._chart.resize(event.size())
            self._coordX.setPos(self._chart.size().width() / 2 - 50,
                                self._chart.size().height() - 20)
            self._coordY.setPos(self._chart.size().width() / 2 + 50,
                                self._chart.size().height() - 20)
            for callout in self._callouts:
                callout.updateGeometry()
        QGraphicsView.resizeEvent(self, event)

    def mouseMoveEvent(self, event):
        self._coordX.setText("X: {0:.2f}".format(
            self._chart.mapToValue(event.pos()).x()))
        self._coordY.setText("Y: {0:.2f}".format(
            self._chart.mapToValue(event.pos()).y()))
        QGraphicsView.mouseMoveEvent(self, event)

    def keepCallout(self):
        self._callouts.append(self._tooltip)
        self._tooltip = Callout(self._chart)

    def tooltip(self, point, state):
        if self._tooltip == 0:
            self._tooltip = Callout(self._chart)

        if state:
            self._tooltip.setText("X: {0:.2f} \nY: {1:.2f} ".format(
                point.x(), point.y()))
            self._tooltip.setAnchor(point)
            self._tooltip.setZValue(11)
            self._tooltip.updateGeometry()
            self._tooltip.show()
        else:
            self._tooltip.hide()
示例#26
0
class QInstruction(QCachedGraphicsItem):

    GRAPH_ADDR_SPACING = 20
    GRAPH_MNEMONIC_SPACING = 10
    GRAPH_OPERAND_SPACING = 2
    GRAPH_COMMENT_STRING_SPACING = 10

    INTERSPERSE_ARGS = ', '

    LINEAR_INSTRUCTION_OFFSET = 120
    COMMENT_PREFIX = "// "

    def __init__(self,
                 workspace,
                 func_addr,
                 disasm_view,
                 disasm,
                 infodock,
                 insn,
                 out_branch,
                 config,
                 parent=None):
        super().__init__(parent=parent)

        # initialization
        self.workspace = workspace
        self.func_addr = func_addr
        self.disasm_view = disasm_view
        self.disasm = disasm
        self.infodock = infodock
        self.variable_manager = infodock.variable_manager
        self.insn = insn
        self.out_branch = out_branch
        self._config = config

        # all "widgets"
        self._addr = None
        self._mnemonic = None
        self._addr_item: QGraphicsSimpleTextItem = None
        self._mnemonic_item: QGraphicsSimpleTextItem = None
        self._operands: List[QOperand] = []
        self._commas: List[QGraphicsSimpleTextItem] = []
        self._string = None
        self._string_item: Optional[QGraphicsSimpleTextItem] = None
        self._comment = None
        self._comment_items: Optional[
            List[QGraphicsSimpleTextItem]] = None  # one comment per line
        self._legend = None
        self._width = 0
        self._height = 0

        self._init_widgets()

    def contextMenuEvent(
            self,
            event: PySide2.QtWidgets.QGraphicsSceneContextMenuEvent) -> None:
        pass

    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        if self.workspace.plugins.handle_click_insn(self, event):
            # stop handling this event if the event has been handled by a plugin
            event.accept()
        elif event.button(
        ) == Qt.LeftButton and QApplication.keyboardModifiers() in (
                Qt.NoModifier, Qt.ControlModifier):
            # toggle selection
            self.infodock.toggle_instruction_selection(
                self.addr,
                insn_pos=self.scenePos(),
                unique=QApplication.keyboardModifiers() != Qt.ControlModifier)
            event.accept()
        elif event.button(
        ) == Qt.RightButton and QApplication.keyboardModifiers(
        ) == Qt.NoModifier:
            if self.addr not in self.infodock.selected_insns:
                self.infodock.toggle_instruction_selection(
                    self.addr, insn_pos=self.scenePos(), unique=True)
            self.disasm_view.instruction_context_menu(self.insn, QCursor.pos())
            event.accept()
        else:
            super().mousePressEvent(event)

    @property
    def addr(self):
        return self.insn.addr

    def _calc_backcolor(self):
        # First we'll check for customizations
        color = self.workspace.plugins.color_insn(self.insn.addr,
                                                  self.selected)
        if color is not None:
            return color

        if self.selected:
            return self._config.disasm_view_node_instruction_selected_background_color

        return None  # None here means transparent, reusing the block color

    @property
    def selected(self):
        """
        If this instruction is selected or not.

        :return:    True if it is selected, False otherwise.
        :rtype:     bool
        """

        return self.infodock.is_instruction_selected(self.addr)

    def clear_cache(self):
        super().clear_cache()
        for obj in self._operands:
            obj.clear_cache()

    def refresh(self):
        self.load_comment()
        self._init_comments_or_string()

        for operand in self._operands:
            operand.refresh()

        self._layout_items_and_update_size()
        self.recalculate_size()

    def get_operand(self, operand_idx):
        if operand_idx < len(self._operands):
            return self._operands[operand_idx]
        return None

    def load_comment(self):
        self._comment = get_comment_for_display(self.workspace.instance.kb,
                                                self.insn.addr)

    def paint(self, painter, option, widget):  # pylint: disable=unused-argument

        painter.setRenderHints(QPainter.Antialiasing
                               | QPainter.SmoothPixmapTransform
                               | QPainter.HighQualityAntialiasing)

        # background color
        backcolor = self._calc_backcolor()
        if backcolor is not None:
            painter.setBrush(backcolor)
            painter.setPen(backcolor)
            painter.drawRect(0, 0, self.width, self.height)

        # any plugin instruction rendering passes
        self.workspace.plugins.draw_insn(self, painter)

    #
    # Private methods
    #

    def _init_widgets(self):

        self.load_comment()
        self._operands.clear()

        # address
        self._addr = "%08x" % self.insn.addr
        self._addr_item = QGraphicsSimpleTextItem(self)
        self._addr_item.setBrush(
            QBrush(self._config.disasm_view_node_address_color))
        self._addr_item.setFont(self._config.disasm_font)
        self._addr_item.setText(self._addr)

        # mnemonic
        self._mnemonic = self.insn.mnemonic.render()[0]
        self._mnemonic_item = QGraphicsSimpleTextItem(self)
        self._mnemonic_item.setFont(self._config.disasm_font)
        self._mnemonic_item.setBrush(
            self._config.disasm_view_node_mnemonic_color)
        self._mnemonic_item.setText(self._mnemonic)

        # operands
        for i, operand in enumerate(self.insn.operands):
            is_branch_target = self.insn.type in (
                'branch', 'call') and i == self.insn.branch_target_operand
            is_indirect_branch = self.insn.branch_type == 'indirect'
            branch_targets = None
            if is_branch_target:
                if self.out_branch is not None:
                    branch_targets = self.out_branch.targets
                else:
                    # it does not create multiple branches. e.g., a call instruction
                    if len(operand.children) == 1 and type(
                            operand.children[0]) is Value:
                        branch_targets = (operand.children[0].val, )
            qoperand = QOperand(self.workspace,
                                self.func_addr,
                                self.disasm_view,
                                self.disasm,
                                self.infodock,
                                self.insn,
                                operand,
                                i,
                                is_branch_target,
                                is_indirect_branch,
                                branch_targets,
                                self._config,
                                parent=self)
            self._operands.append(qoperand)

        # all commas
        for _ in range(len(self._operands) - 1):
            comma = QGraphicsSimpleTextItem(self.INTERSPERSE_ARGS, self)
            comma.setFont(self._config.disasm_font)
            comma.setBrush(self._config.disasm_view_node_mnemonic_color)
            self._commas.append(comma)

        if should_display_string_label(self.workspace.instance.cfg,
                                       self.insn.addr,
                                       self.workspace.instance.project):
            # yes we should display a string label
            self._string = get_string_for_display(
                self.workspace.instance.cfg, self.insn.addr,
                self.workspace.instance.project)
            if self._string is None:
                self._string = "<Unknown>"

        self._init_comments_or_string()

        self._layout_items_and_update_size()

    def _init_comments_or_string(self):

        # remove existing comments or strings
        if self._comment_items:
            for comm in self._comment_items:
                comm: QGraphicsSimpleTextItem
                comm.setParentItem(None)
            self._comment_items = None
        elif self._string_item is not None:
            self._string_item.setParentItem(None)
            self._string_item = None

        # comment or string - comments have precedence
        if self._comment:
            self._string_item = None
            lines = self._comment.split('\n')
            self._comment_items = []
            for line in lines:
                comment = QGraphicsSimpleTextItem(self.COMMENT_PREFIX + line,
                                                  self)
                comment.setFont(self._config.disasm_font)
                comment.setBrush(
                    Qt.darkGreen)  # TODO: Expose it as a setting in Config
                self._comment_items.append(comment)
        elif self._string is not None:
            self._comment_items = None
            self._string_item = QGraphicsSimpleTextItem(self._string, self)
            self._string_item.setFont(self._config.disasm_font)
            self._string_item.setBrush(
                Qt.gray)  # TODO: Expose it as a setting in Config

    def _layout_items_and_update_size(self):

        x, y = 0, 0

        # address
        if self.disasm_view.show_address:
            self._addr_item.setVisible(True)
            self._addr_item.setPos(x, y)
            x += self._addr_item.boundingRect().width(
            ) + self.GRAPH_ADDR_SPACING
        else:
            self._addr_item.setVisible(False)

        # mnemonic
        self._mnemonic_item.setPos(x, y)
        x += self._mnemonic_item.boundingRect().width(
        ) + self.GRAPH_MNEMONIC_SPACING

        # operands and commas
        for operand, comma in zip(self._operands, self._commas):
            operand.setPos(x, y)
            x += operand.boundingRect().width()
            comma.setPos(x, y)
            x += comma.boundingRect().width()

        # the last operand
        if self._operands:
            self._operands[-1].setPos(x, y)
            x += self._operands[-1].boundingRect().width()

        # comments and strings
        if self._comment_items:
            x += self.GRAPH_COMMENT_STRING_SPACING
            max_comment_width = 0
            for comment in self._comment_items:
                comment.setPos(x, y)
                max_comment_width = max(comment.boundingRect().width(),
                                        max_comment_width)
                y += comment.boundingRect().height()
            x += max_comment_width
        elif self._string_item is not None:
            x += self.GRAPH_COMMENT_STRING_SPACING
            self._string_item.setPos(x, y)
            x += self._string_item.boundingRect().width()
            y += self._string_item.boundingRect().height()
        else:
            y = self._mnemonic_item.boundingRect().height()

        # update size
        self._width = x
        self._height = y
        self.recalculate_size()

    def _boundingRect(self):
        return QRectF(0, 0, self._width, self._height)
    def _init_widgets(self):
        self._node: CallProxiNode
        self._func_name = self._node.callee.name
        if self._node.args is not None:
            self._args = [self._argument_text(arg) for arg in self._node.args]
        else:
            self._args = []

        # func name
        self._func_name_item = QGraphicsSimpleTextItem(self._func_name, self)
        if self._node.callee.is_simprocedure:
            pen_color = self.CALL_NODE_TEXT_COLOR_SIMPROC
        elif self._node.callee.is_plt:
            pen_color = self.CALL_NODE_TEXT_COLOR_SIMPROC
        else:
            pen_color = self.CALL_NODE_TEXT_COLOR
        self._func_name_item.setBrush(pen_color)
        self._func_name_item.setFont(Conf.symexec_font)
        self._func_name_item.setPos(self.HORIZONTAL_PADDING,
                                    self.VERTICAL_PADDING)

        x = self.HORIZONTAL_PADDING + self._func_name_item.boundingRect(
        ).width()
        y = self.VERTICAL_PADDING
        # left parenthesis
        self._left_parenthesis_item = QGraphicsSimpleTextItem("(", self)
        self._left_parenthesis_item.setBrush(pen_color)
        self._left_parenthesis_item.setFont(Conf.symexec_font)
        self._left_parenthesis_item.setPos(x, y)

        x += self._left_parenthesis_item.boundingRect().width()

        # arguments
        self._args_list = []
        for i, (type_, arg) in enumerate(self._args):
            if type_ is StringProxiNode:
                color = self.STRING_NODE_TEXT_COLOR
            elif type_ is IntegerProxiNode:
                color = self.INTEGER_NODE_TEXT_COLOR
            elif type_ is VariableProxiNode:
                color = self.VARIABLE_NODE_TEXT_COLOR
            elif type_ is UnknownProxiNode:
                color = self.UNKNOWN_NODE_TEXT_COLOR
            else:
                color = self.CALL_NODE_TEXT_COLOR
            o = QGraphicsSimpleTextItem(arg, self)
            o.setBrush(color)
            o.setFont(Conf.symexec_font)
            o.setPos(x, y)
            self._args_list.append(o)
            x += o.boundingRect().width()

            # comma
            if i != len(self._args) - 1:
                comma = QGraphicsSimpleTextItem(", ", self)
                comma.setBrush(pen_color)
                comma.setFont(Conf.symexec_font)
                comma.setPos(x, y)
                self._args_list.append(comma)
                x += comma.boundingRect().width()

        # right parenthesis
        self._right_parenthesis_item = QGraphicsSimpleTextItem(")", self)
        self._right_parenthesis_item.setBrush(pen_color)
        self._right_parenthesis_item.setFont(Conf.symexec_font)
        self._right_parenthesis_item.setPos(x, y)
class QProximityGraphCallBlock(QProximityGraphBlock):
    """
    Call Block
    """
    def __init__(self, is_selected, proximity_view: 'ProximityView',
                 node: CallProxiNode):
        self._func_name: str = None
        self._args: List[Tuple[Type, str]] = None

        self._func_name_item: QGraphicsSimpleTextItem = None
        self._left_parenthesis_item: QGraphicsSimpleTextItem = None
        self._args_list: List[QGraphicsSimpleTextItem] = None
        self._right_parenthesis_item: QGraphicsSimpleTextItem = None

        super().__init__(is_selected, proximity_view, node)

    def _init_widgets(self):
        self._node: CallProxiNode
        self._func_name = self._node.callee.name
        if self._node.args is not None:
            self._args = [self._argument_text(arg) for arg in self._node.args]
        else:
            self._args = []

        # func name
        self._func_name_item = QGraphicsSimpleTextItem(self._func_name, self)
        if self._node.callee.is_simprocedure:
            pen_color = self.CALL_NODE_TEXT_COLOR_SIMPROC
        elif self._node.callee.is_plt:
            pen_color = self.CALL_NODE_TEXT_COLOR_SIMPROC
        else:
            pen_color = self.CALL_NODE_TEXT_COLOR
        self._func_name_item.setBrush(pen_color)
        self._func_name_item.setFont(Conf.symexec_font)
        self._func_name_item.setPos(self.HORIZONTAL_PADDING,
                                    self.VERTICAL_PADDING)

        x = self.HORIZONTAL_PADDING + self._func_name_item.boundingRect(
        ).width()
        y = self.VERTICAL_PADDING
        # left parenthesis
        self._left_parenthesis_item = QGraphicsSimpleTextItem("(", self)
        self._left_parenthesis_item.setBrush(pen_color)
        self._left_parenthesis_item.setFont(Conf.symexec_font)
        self._left_parenthesis_item.setPos(x, y)

        x += self._left_parenthesis_item.boundingRect().width()

        # arguments
        self._args_list = []
        for i, (type_, arg) in enumerate(self._args):
            if type_ is StringProxiNode:
                color = self.STRING_NODE_TEXT_COLOR
            elif type_ is IntegerProxiNode:
                color = self.INTEGER_NODE_TEXT_COLOR
            elif type_ is VariableProxiNode:
                color = self.VARIABLE_NODE_TEXT_COLOR
            elif type_ is UnknownProxiNode:
                color = self.UNKNOWN_NODE_TEXT_COLOR
            else:
                color = self.CALL_NODE_TEXT_COLOR
            o = QGraphicsSimpleTextItem(arg, self)
            o.setBrush(color)
            o.setFont(Conf.symexec_font)
            o.setPos(x, y)
            self._args_list.append(o)
            x += o.boundingRect().width()

            # comma
            if i != len(self._args) - 1:
                comma = QGraphicsSimpleTextItem(", ", self)
                comma.setBrush(pen_color)
                comma.setFont(Conf.symexec_font)
                comma.setPos(x, y)
                self._args_list.append(comma)
                x += comma.boundingRect().width()

        # right parenthesis
        self._right_parenthesis_item = QGraphicsSimpleTextItem(")", self)
        self._right_parenthesis_item.setBrush(pen_color)
        self._right_parenthesis_item.setFont(Conf.symexec_font)
        self._right_parenthesis_item.setPos(x, y)

    def _argument_text(self, arg) -> Tuple[Type, str]:  # pylint: disable=no-self-use
        if isinstance(arg, StringProxiNode):
            return StringProxiNode, '"' + arg.content.decode("utf-8").replace(
                "\n", "\\n") + '"'
        elif isinstance(arg, IntegerProxiNode):
            return IntegerProxiNode, str(arg.value)
        elif isinstance(arg, VariableProxiNode):
            return VariableProxiNode, str(arg.name)
        elif isinstance(arg, UnknownProxiNode):
            return UnknownProxiNode, str(arg.dummy_value)
        return object, "Unknown"

    def mouseDoubleClickEvent(self, event):
        if event.button() == Qt.LeftButton and (
                event.modifiers() & Qt.ControlModifier) == Qt.ControlModifier:
            # ctrl + double click to expand a function call
            event.accept()
            self._proximity_view.expand_function(self._node.callee)
            return

        super().mouseDoubleClickEvent(event)

    def paint(self, painter, option, widget):
        self._paint_boundary(painter)

    def _update_size(self):
        width_candidates = [
            self.HORIZONTAL_PADDING * 2 +
            self._func_name_item.boundingRect().width() +
            self._left_parenthesis_item.boundingRect().width() +
            sum(map(lambda x: x.boundingRect().width(), self._args_list)) +
            self._right_parenthesis_item.boundingRect().width()
        ]

        self._width = max(width_candidates)
        self._height = self.VERTICAL_PADDING * 2 + self._func_name_item.boundingRect(
        ).height()

        self._width = max(30, self._width)
        self._height = max(10, self._height)
        self.recalculate_size()
 def _init_widgets(self):
     self._text = f"Function {self._node.func.name}"
     self._text_item = QGraphicsSimpleTextItem(self._text, self)
     self._text_item.setFont(Conf.symexec_font)
     self._text_item.setBrush(self.FUNCTION_NODE_TEXT_COLOR)
     self._text_item.setPos(self.HORIZONTAL_PADDING, self.VERTICAL_PADDING)
示例#30
0
class QBlockCode(QCachedGraphicsItem):
    """
    Top-level code widget for a selection of text. Will construct an AST using
    QBlockCodeObj, mirroring the structure associated with the target object.
    This text is then rendered using a QTextDocument, with appropriate styles
    applied to it. Interaction events will be propagated to corresponding
    objects.
    """

    GRAPH_ADDR_SPACING = 20

    addr: int
    _addr_str: str
    obj: QBlockCodeObj
    _config: ConfigurationManager
    disasm_view: 'QDisassemblyBaseControl'
    workspace: 'Workspace'
    infodock: InfoDock
    parent: Any

    def __init__(self,
                 addr: int,
                 obj: QBlockCodeObj,
                 config: ConfigurationManager,
                 disasm_view: 'QDisassemblyBaseControl',
                 workspace: 'Workspace',
                 infodock: InfoDock,
                 parent: Any = None):
        super().__init__(parent=parent)
        self.addr = addr
        self._addr_str = "%08x" % self.addr
        self._addr_item: QGraphicsSimpleTextItem = None
        self.obj = obj
        self._width = 0
        self._height = 0
        self._config = config
        self.parent = parent
        self.workspace = workspace
        self.infodock = infodock
        self._disasm_view = disasm_view
        self._qtextdoc = QTextDocument()
        self._qtextdoc.setDefaultFont(self._config.disasm_font)
        self._qtextdoc.setDocumentMargin(0)

        self._addr_item = QGraphicsSimpleTextItem(self._addr_str, self)
        self._addr_item.setBrush(Conf.disasm_view_node_address_color)
        self._addr_item.setFont(Conf.disasm_font)

        self.update_document()
        self.setToolTip("Address: " + self._addr_str)

        self.refresh()

    def refresh(self):
        self._addr_item.setVisible(self._disasm_view.show_address)
        self._layout_items_and_update_size()

    def update_document(self):
        self._qtextdoc.clear()
        cur = QTextCursor(self._qtextdoc)
        self.obj.render_to_doc(cur)

    def paint(self, painter, option, widget):  #pylint: disable=unused-argument
        self.update_document()
        painter.setRenderHints(QPainter.Antialiasing
                               | QPainter.SmoothPixmapTransform
                               | QPainter.HighQualityAntialiasing)
        painter.setFont(self._config.disasm_font)

        if self.infodock.is_instruction_selected(
                self.addr) or self.obj.should_highlight_line:
            highlight_color = Conf.disasm_view_node_instruction_selected_background_color
            painter.setBrush(highlight_color)
            painter.setPen(highlight_color)
            painter.drawRect(0, 0, self.width, self.height)

        x = 0

        if self._disasm_view.show_address:
            x += self._addr_item.boundingRect().width(
            ) + self.GRAPH_ADDR_SPACING

        painter.translate(QPointF(x, 0))
        self._qtextdoc.drawContents(painter)

    #
    # Event handlers
    #

    def get_obj_for_mouse_event(self, event: QMouseEvent) -> QBlockCodeObj:
        p = event.pos()

        if self._disasm_view.show_address:
            offset = self._addr_item.boundingRect().width(
            ) + self.GRAPH_ADDR_SPACING
            p.setX(p.x() - offset)

        if p.x() >= 0:
            hitpos = self._qtextdoc.documentLayout().hitTest(
                p, Qt.HitTestAccuracy.ExactHit)
            if hitpos >= 0:
                return self.obj.get_hit_obj(hitpos)

        return None

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.infodock.select_instruction(self.addr)

        obj = self.get_obj_for_mouse_event(event)
        if obj is not None:
            obj.mousePressEvent(event)

    def mouseDoubleClickEvent(self, event):
        obj = self.get_obj_for_mouse_event(event)
        if obj is not None:
            obj.mouseDoubleClickEvent(event)

    #
    # Private methods
    #

    def _layout_items_and_update_size(self):
        self.update_document()

        x, y = 0, 0
        if self._disasm_view.show_address:
            self._addr_item.setPos(x, y)
            x += self._addr_item.boundingRect().width(
            ) + self.GRAPH_ADDR_SPACING

        x += self._qtextdoc.size().width()
        y += self._qtextdoc.size().height()
        self._width = x
        self._height = y
        self.recalculate_size()

    def _boundingRect(self):
        return QRectF(0, 0, self._width, self._height)
示例#31
0
class View(QGraphicsView):
    def __init__(self, parent = None):
        super(View, self).__init__(parent)
        self.setScene(QGraphicsScene(self))

        self.setDragMode(QGraphicsView.NoDrag)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

        # Chart
        self._chart = QtCharts.QChart()
        self._chart.setMinimumSize(640, 480)
        self._chart.setTitle("Hover the line to show callout. Click the line "
            "to make it stay")
        self._chart.legend().hide()
        self.series = QtCharts.QLineSeries()
        self.series.append(1, 3)
        self.series.append(4, 5)
        self.series.append(5, 4.5)
        self.series.append(7, 1)
        self.series.append(11, 2)
        self._chart.addSeries(self.series)

        self.series2 = QtCharts.QSplineSeries()
        self.series2.append(1.6, 1.4)
        self.series2.append(2.4, 3.5)
        self.series2.append(3.7, 2.5)
        self.series2.append(7, 4)
        self.series2.append(10, 2)
        self._chart.addSeries(self.series2)

        self._chart.createDefaultAxes()
        self._chart.setAcceptHoverEvents(True)

        self.setRenderHint(QPainter.Antialiasing)
        self.scene().addItem(self._chart)

        self._coordX = QGraphicsSimpleTextItem(self._chart)
        self._coordX.setPos(
            self._chart.size().width()/2 - 50, self._chart.size().height())
        self._coordX.setText("X: ");
        self._coordY = QGraphicsSimpleTextItem(self._chart)
        self._coordY.setPos(
            self._chart.size().width()/2 + 50, self._chart.size().height())
        self._coordY.setText("Y: ")

        self._callouts = []
        self._tooltip = Callout(self._chart)

        self.series.clicked.connect(self.keepCallout)
        self.series.hovered.connect(self.tooltip)

        self.series2.clicked.connect(self.keepCallout)
        self.series2.hovered.connect(self.tooltip)

        self.setMouseTracking(True)

    def resizeEvent(self, event):
        if self.scene():
            self.scene().setSceneRect(QRectF(QPointF(0, 0), event.size()))
            self._chart.resize(event.size())
            self._coordX.setPos(
                self._chart.size().width()/2 - 50,
                self._chart.size().height() - 20)
            self._coordY.setPos(
                self._chart.size().width()/2 + 50,
                self._chart.size().height() - 20)
            for callout in self._callouts:
                callout.updateGeometry()
        QGraphicsView.resizeEvent(self, event)


    def mouseMoveEvent(self, event):
        self._coordX.setText("X: {0:.2f}"
            .format(self._chart.mapToValue(event.pos()).x()))
        self._coordY.setText("Y: {0:.2f}"
            .format(self._chart.mapToValue(event.pos()).y()))
        QGraphicsView.mouseMoveEvent(self, event)

    def keepCallout(self):
        self._callouts.append(self._tooltip);
        self._tooltip = Callout(self._chart)

    def tooltip(self, point, state):
        if self._tooltip == 0:
            self._tooltip = Callout(self._chart)

        if state:
            self._tooltip.setText("X: {0:.2f} \nY: {1:.2f} "
                .format(point.x(),point.y()))
            self._tooltip.setAnchor(point)
            self._tooltip.setZValue(11)
            self._tooltip.updateGeometry()
            self._tooltip.show()
        else:
            self._tooltip.hide()
示例#32
0
class ProjectItemIcon(QGraphicsRectItem):
    """Base class for project item icons drawn in Design View."""

    ITEM_EXTENT = 64

    def __init__(self, toolbox, icon_file, icon_color, background_color):
        """
        Args:
            toolbox (ToolboxUI): QMainWindow instance
            icon_file (str): Path to icon resource
            icon_color (QColor): Icon's color
            background_color (QColor): Background color
        """
        super().__init__()
        self._toolbox = toolbox
        self.icon_file = icon_file
        self._moved_on_scene = False
        self.previous_pos = QPointF()
        self.current_pos = QPointF()
        self.icon_group = {self}
        self.renderer = QSvgRenderer()
        self.svg_item = QGraphicsSvgItem(self)
        self.colorizer = QGraphicsColorizeEffect()
        self.setRect(
            QRectF(-self.ITEM_EXTENT / 2, -self.ITEM_EXTENT / 2,
                   self.ITEM_EXTENT, self.ITEM_EXTENT))
        self.text_font_size = 10  # point size
        # Make item name graphics item.
        self._name = ""
        self.name_item = QGraphicsSimpleTextItem(self._name, self)
        self.set_name_attributes()  # Set font, size, position, etc.
        # Make connector buttons
        self.connectors = dict(
            bottom=ConnectorButton(self, toolbox, position="bottom"),
            left=ConnectorButton(self, toolbox, position="left"),
            right=ConnectorButton(self, toolbox, position="right"),
        )
        # Make exclamation and rank icons
        self.exclamation_icon = ExclamationIcon(self)
        self.execution_icon = ExecutionIcon(self)
        self.rank_icon = RankIcon(self)
        brush = QBrush(background_color)
        self._setup(brush, icon_file, icon_color)
        shadow_effect = QGraphicsDropShadowEffect()
        shadow_effect.setOffset(1)
        shadow_effect.setEnabled(False)
        self.setGraphicsEffect(shadow_effect)

    def finalize(self, name, x, y):
        """
        Names the icon and moves it by given amount.

        Args:
            name (str): icon's name
            x (int): horizontal offset
            y (int): vertical offset
        """
        self.update_name_item(name)
        self.moveBy(x, y)

    def _setup(self, brush, svg, svg_color):
        """Setup item's attributes.

        Args:
            brush (QBrush): Used in filling the background rectangle
            svg (str): Path to SVG icon file
            svg_color (QColor): Color of SVG icon
        """
        self.setPen(QPen(Qt.black, 1, Qt.SolidLine))
        self.setBrush(brush)
        self.colorizer.setColor(svg_color)
        # Load SVG
        loading_ok = self.renderer.load(svg)
        if not loading_ok:
            self._toolbox.msg_error.emit(
                "Loading SVG icon from resource:{0} failed".format(svg))
            return
        size = self.renderer.defaultSize()
        self.svg_item.setSharedRenderer(self.renderer)
        self.svg_item.setElementId(
            "")  # guess empty string loads the whole file
        dim_max = max(size.width(), size.height())
        rect_w = self.rect().width()  # Parent rect width
        margin = 32
        self.svg_item.setScale((rect_w - margin) / dim_max)
        self.svg_item.setPos(self.rect().center() -
                             self.svg_item.sceneBoundingRect().center())
        self.svg_item.setGraphicsEffect(self.colorizer)
        self.setFlag(QGraphicsItem.ItemIsMovable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, enabled=True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, enabled=True)
        self.setFlag(QGraphicsItem.ItemSendsScenePositionChanges, enabled=True)
        self.setAcceptHoverEvents(True)
        self.setCursor(Qt.PointingHandCursor)
        # Set exclamation, execution_log, and rank icons position
        self.exclamation_icon.setPos(
            self.rect().topRight() -
            self.exclamation_icon.sceneBoundingRect().topRight())
        self.execution_icon.setPos(
            self.rect().bottomRight() -
            0.5 * self.execution_icon.sceneBoundingRect().bottomRight())
        self.rank_icon.setPos(self.rect().topLeft())

    def name(self):
        """Returns name of the item that is represented by this icon.

        Returns:
            str: icon's name
        """
        return self._name

    def update_name_item(self, new_name):
        """Set a new text to name item.

        Args:
            new_name (str): icon's name
        """
        self._name = new_name
        self.name_item.setText(new_name)
        self.set_name_attributes()

    def set_name_attributes(self):
        """Set name QGraphicsSimpleTextItem attributes (font, size, position, etc.)"""
        # Set font size and style
        font = self.name_item.font()
        font.setPointSize(self.text_font_size)
        font.setBold(True)
        self.name_item.setFont(font)
        # Set name item position (centered on top of the master icon)
        name_width = self.name_item.boundingRect().width()
        name_height = self.name_item.boundingRect().height()
        self.name_item.setPos(
            self.rect().x() + self.rect().width() / 2 - name_width / 2,
            self.rect().y() - name_height - 4)

    def conn_button(self, position="left"):
        """Returns item's connector button.

        Args:
            position (str): "left", "right" or "bottom"

        Returns:
            QWidget: connector button
        """
        return self.connectors.get(position, self.connectors["left"])

    def outgoing_links(self):
        """Collects outgoing links.

        Returns:
            list of LinkBase: outgoing links
        """
        return [
            l for conn in self.connectors.values()
            for l in conn.outgoing_links()
        ]

    def incoming_links(self):
        """Collects incoming links.

        Returns:
            list of LinkBase: outgoing links
        """
        return [
            l for conn in self.connectors.values()
            for l in conn.incoming_links()
        ]

    def run_execution_leave_animation(self, skipped):
        """
        Starts the animation associated with execution leaving the icon.

        Args:
            skipped (bool): True if project item was not actually executed.
        """
        animation_group = QParallelAnimationGroup(self._toolbox)
        for link in self.outgoing_links():
            animation_group.addAnimation(
                link.make_execution_animation(skipped))
        animation_group.start()

    def hoverEnterEvent(self, event):
        """Sets a drop shadow effect to icon when mouse enters its boundaries.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        self.prepareGeometryChange()
        self.graphicsEffect().setEnabled(True)
        event.accept()

    def hoverLeaveEvent(self, event):
        """Disables the drop shadow when mouse leaves icon boundaries.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        self.prepareGeometryChange()
        self.graphicsEffect().setEnabled(False)
        event.accept()

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        self.icon_group = set(x for x in self.scene().selectedItems()
                              if isinstance(x, ProjectItemIcon)) | {self}
        for icon in self.icon_group:
            icon.previous_pos = icon.scenePos()

    def mouseMoveEvent(self, event):
        """Moves icon(s) while the mouse button is pressed.
        Update links that are connected to selected icons.

        Args:
            event (QGraphicsSceneMouseEvent): Event
        """
        super().mouseMoveEvent(event)
        self.update_links_geometry()

    def moveBy(self, dx, dy):
        super().moveBy(dx, dy)
        self.update_links_geometry()

    def update_links_geometry(self):
        """Updates geometry of connected links to reflect this item's most recent position."""
        links = set(link for icon in self.icon_group
                    for conn in icon.connectors.values()
                    for link in conn.links)
        for link in links:
            link.update_geometry()

    def mouseReleaseEvent(self, event):
        for icon in self.icon_group:
            icon.current_pos = icon.scenePos()
        # pylint: disable=undefined-variable
        if (self.current_pos - self.previous_pos
            ).manhattanLength() > qApp.startDragDistance():
            self._toolbox.undo_stack.push(
                MoveIconCommand(self, self._toolbox.project()))
        super().mouseReleaseEvent(event)

    def notify_item_move(self):
        if self._moved_on_scene:
            self._moved_on_scene = False
            scene = self.scene()
            scene.item_move_finished.emit(self)

    def contextMenuEvent(self, event):
        """Show item context menu.

        Args:
            event (QGraphicsSceneMouseEvent): Mouse event
        """
        event.accept()
        self.scene().clearSelection()
        self.setSelected(True)
        ind = self._toolbox.project_item_model.find_item(self.name())
        self._toolbox.show_project_item_context_menu(event.screenPos(), ind)

    def itemChange(self, change, value):
        """
        Reacts to item removal and position changes.

        In particular, destroys the drop shadow effect when the items is removed from a scene
        and keeps track of item's movements on the scene.

        Args:
            change (GraphicsItemChange): a flag signalling the type of the change
            value: a value related to the change

        Returns:
             Whatever super() does with the value parameter
        """
        if change == QGraphicsItem.ItemScenePositionHasChanged:
            self._moved_on_scene = True
        elif change == QGraphicsItem.GraphicsItemChange.ItemSceneChange and value is None:
            self.prepareGeometryChange()
            self.setGraphicsEffect(None)
        return super().itemChange(change, value)

    def select_item(self):
        """Update GUI to show the details of the selected item."""
        ind = self._toolbox.project_item_model.find_item(self.name())
        self._toolbox.ui.treeView_project.setCurrentIndex(ind)