Exemplo n.º 1
0
class WatchpointsWidget(QWidget):
    """ WatchpointsWidget

        Signals:
            onItemSelected(addr_str) - item dblclicked
            onItemAddClick

        Constants:
            MEMORY_ACCESS_READ = 1
            MEMORY_ACCESS_WRITE = 2
            MEMORY_ACCESS_EXECUTE = 4
            MEMORY_WATCH_SINGLESHOT = 8
    """
    MEMORY_ACCESS_READ = 1
    MEMORY_ACCESS_WRITE = 2
    MEMORY_ACCESS_EXECUTE = 4
    MEMORY_WATCH_SINGLESHOT = 8

    onItemDoubleClicked = pyqtSignal(int, name='onItemDoubleClicked')
    onItemAdded = pyqtSignal(int, name='onItemAdded')
    onItemRemoved = pyqtSignal(int, name='onItemRemoved')

    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(WatchpointsWidget, self).__init__(parent=parent)
        self._app_window = parent

        if self._app_window.dwarf is None:
            print('WatchpointsWidget created before Dwarf exists')
            return

        self._uppercase_hex = True
        self.setAutoFillBackground(True)

        # connect to dwarf
        self._app_window.dwarf.onWatchpointAdded.connect(self._on_watchpoint_added)
        self._app_window.dwarf.onWatchpointRemoved.connect(
            self._on_watchpoint_removed)

        # setup our model
        self._watchpoints_model = QStandardItemModel(0, 5)
        self._watchpoints_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self._watchpoints_model.setHeaderData(1, Qt.Horizontal, 'R')
        self._watchpoints_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchpoints_model.setHeaderData(2, Qt.Horizontal, 'W')
        self._watchpoints_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchpoints_model.setHeaderData(3, Qt.Horizontal, 'X')
        self._watchpoints_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchpoints_model.setHeaderData(4, Qt.Horizontal, 'S')
        self._watchpoints_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)

        # setup ui
        v_box = QVBoxLayout(self)
        v_box.setContentsMargins(0, 0, 0, 0)
        self.list_view = DwarfListView()
        self.list_view.setModel(self._watchpoints_model)
        self.list_view.header().setSectionResizeMode(0, QHeaderView.Stretch)
        self.list_view.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents | QHeaderView.Fixed)
        self.list_view.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents | QHeaderView.Fixed)
        self.list_view.header().setSectionResizeMode(
            3, QHeaderView.ResizeToContents | QHeaderView.Fixed)
        self.list_view.header().setSectionResizeMode(
            4, QHeaderView.ResizeToContents | QHeaderView.Fixed)
        self.list_view.header().setStretchLastSection(False)
        self.list_view.doubleClicked.connect(self._on_item_dblclick)
        self.list_view.setContextMenuPolicy(Qt.CustomContextMenu)
        self.list_view.customContextMenuRequested.connect(self._on_contextmenu)

        v_box.addWidget(self.list_view)
        #header = QHeaderView(Qt.Horizontal, self)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(5, 2, 5, 5)
        btn1 = QPushButton(
            QIcon(utils.resource_path('assets/icons/plus.svg')), '')
        btn1.setFixedSize(20, 20)
        btn1.clicked.connect(self._on_additem_clicked)
        btn2 = QPushButton(
            QIcon(utils.resource_path('assets/icons/dash.svg')), '')
        btn2.setFixedSize(20, 20)
        btn2.clicked.connect(self.delete_items)
        btn3 = QPushButton(
            QIcon(utils.resource_path('assets/icons/trashcan.svg')), '')
        btn3.setFixedSize(20, 20)
        btn3.clicked.connect(self.clear_list)
        h_box.addWidget(btn1)
        h_box.addWidget(btn2)
        h_box.addSpacerItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))
        h_box.addWidget(btn3)
        # header.setLayout(h_box)
        # header.setFixedHeight(25)
        # v_box.addWidget(header)
        v_box.addLayout(h_box)

        # create a centered dot icon
        _section_width = self.list_view.header().sectionSize(2)
        self._new_pixmap = QPixmap(_section_width, 20)
        self._new_pixmap.fill(Qt.transparent)
        painter = QPainter(self._new_pixmap)
        rect = QRect((_section_width * 0.5), 0, 20, 20)
        painter.setBrush(QColor('#666'))
        painter.setPen(QColor('#666'))
        painter.drawEllipse(rect)
        self._dot_icon = QIcon(self._new_pixmap)

        # shortcuts
        shortcut_add = QShortcut(
            QKeySequence(Qt.CTRL + Qt.Key_W), self._app_window,
            self._on_additem_clicked)
        shortcut_add.setAutoRepeat(False)

        self.setLayout(v_box)

    # ************************************************************************
    # **************************** Properties ********************************
    # ************************************************************************
    @property
    def uppercase_hex(self):
        """ Addresses displayed lower/upper-case
        """
        return self._uppercase_hex

    @uppercase_hex.setter
    def uppercase_hex(self, value):
        """ Addresses displayed lower/upper-case
            value - bool or str
                    'upper', 'lower'
        """
        if isinstance(value, bool):
            self._uppercase_hex = value
        elif isinstance(value, str):
            self._uppercase_hex = (value == 'upper')

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def do_addwatchpoint_dlg(self, ptr=None):  # pylint: disable=too-many-branches
        """ Shows AddWatchpointDialog
        """
        watchpoint_dlg = AddWatchpointDialog(self, ptr)
        if watchpoint_dlg.exec_() == QDialog.Accepted:
            mem_r = watchpoint_dlg.acc_read.isChecked()
            mem_w = watchpoint_dlg.acc_write.isChecked()
            mem_x = watchpoint_dlg.acc_execute.isChecked()
            mem_s = watchpoint_dlg.singleshot.isChecked()

            ptr = watchpoint_dlg.text_field.toPlainText()

            if ptr:
                if isinstance(ptr, str):
                    if ptr.startswith('0x') or ptr.startswith('#'):
                        ptr = utils.parse_ptr(ptr)
                    else:
                        try:
                            ptr = int(ptr, 10)
                        except ValueError:
                            pass

                    # int now?
                    if not isinstance(ptr, int):
                        try:
                            ptr = int(
                                self._app_window.dwarf.dwarf_api(
                                    'evaluatePtr', ptr), 16)
                        except ValueError:
                            ptr = 0

                        if ptr == 0:
                            return

                        if not self._app_window.dwarf.dwarf_api(
                                'isValidPointer', ptr):
                            return
                else:
                    return

                mem_val = 0
                if mem_r:
                    mem_val |= self.MEMORY_ACCESS_READ
                if mem_w:
                    mem_val |= self.MEMORY_ACCESS_WRITE
                if mem_x:
                    mem_val |= self.MEMORY_ACCESS_EXECUTE
                if mem_s:
                    mem_val |= self.MEMORY_WATCH_SINGLESHOT

                self.add_address(ptr, mem_val, from_api=False)

                # return [ptr, mem_val]

    def add_address(self, ptr, flags, from_api=False):
        """ Adds Address to display

            ptr - str or int
            flags - int
        """
        if isinstance(ptr, str):
            ptr = utils.parse_ptr(ptr)

        if not isinstance(flags, int):
            try:
                flags = int(flags, 10)
            except ValueError:
                flags = 3

        if not from_api:
            # function was called directly so add it to dwarf
            if not self._app_window.dwarf.is_address_watched(ptr):
                self._app_window.dwarf.dwarf_api('putWatchpoint', [ptr, flags])
                return

        # show header
        self.list_view.setHeaderHidden(False)

        # create items to add
        if self._uppercase_hex:
            str_frmt = '0x{0:X}'
        else:
            str_frmt = '0x{0:x}'

        addr = QStandardItem()
        addr.setText(str_frmt.format(ptr))

        read = QStandardItem()
        write = QStandardItem()
        execute = QStandardItem()
        singleshot = QStandardItem()

        if flags & self.MEMORY_ACCESS_READ:
            read.setIcon(self._dot_icon)
        if flags & self.MEMORY_ACCESS_WRITE:
            write.setIcon(self._dot_icon)
        if flags & self.MEMORY_ACCESS_EXECUTE:
            execute.setIcon(self._dot_icon)
        if flags & self.MEMORY_WATCH_SINGLESHOT:
            singleshot.setIcon(self._dot_icon)

        # add items as new row on top
        self._watchpoints_model.insertRow(
            0, [addr, read, write, execute, singleshot])

    def remove_address(self, ptr, from_api=False):
        """ Remove Address from List
        """
        if isinstance(ptr, str):
            ptr = utils.parse_ptr(ptr)

        if not from_api:
            # called somewhere so remove watchpoint in dwarf too
            self._app_window.dwarf.dwarf_api('removeWatchpoint', ptr)
            return

        str_frmt = ''
        if self._uppercase_hex:
            str_frmt = '0x{0:X}'.format(ptr)
        else:
            str_frmt = '0x{0:x}'.format(ptr)

        model = self.list_view.model()
        for item in range(model.rowCount()):
            if str_frmt == model.item(item).text():
                model.removeRow(item)

    def delete_items(self):
        """ Delete selected Items
        """
        model = self.list_view.model()

        index = self.list_view.selectionModel().currentIndex().row()
        if index != -1:
            ptr = model.item(index, 0).text()
            self.remove_address(ptr)

    def clear_list(self):
        """ Clear the List
        """
        model = self.list_view.model()

        # go through all items and tell it gets removed
        for item in range(model.rowCount()):
            ptr = model.item(item, 0).text()
            self.remove_address(ptr)

        if model.rowCount() > 0:
            # something was wrong it should be empty
            model.removeRows(0, model.rowCount())

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _on_contextmenu(self, pos):
        index = self.list_view.indexAt(pos).row()
        glbl_pt = self.list_view.mapToGlobal(pos)
        context_menu = QMenu(self)
        context_menu.addAction('Add watchpoint', self._on_additem_clicked)
        if index != -1:
            context_menu.addSeparator()
            context_menu.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self._watchpoints_model.item(index, 0).text()))
            context_menu.addAction(
                'Jump to address', lambda: self._app_window.jump_to_address(
                    self._watchpoints_model.item(index, 0).text()))
            context_menu.addAction(
                'Delete watchpoint', lambda: self.remove_address(
                    self._watchpoints_model.item(index, 0).text()))
            if self.list_view.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction('Search', self.list_view._on_cm_search)
        context_menu.exec_(glbl_pt)

    def _on_item_dblclick(self, model_index):
        row = self._watchpoints_model.itemFromIndex(model_index).row()
        if row != -1:
            ptr = self._watchpoints_model.item(row, 0).text()
            self.onItemDoubleClicked.emit(ptr)

    def _on_additem_clicked(self):
        if self._app_window.dwarf.pid == 0:
            return

        self.do_addwatchpoint_dlg()

    def _on_watchpoint_added(self, watchpoint):
        """ Callback from Dwarf after Watchpoint is added
        """
        # add to watchpointslist
        self.add_address(watchpoint.address, watchpoint.flags, from_api=True)
        self.onItemAdded.emit(watchpoint.address)

    def _on_watchpoint_removed(self, ptr):
        """ Callback from Dwarf after watchpoint is removed
        """
        ptr = utils.parse_ptr(ptr)
        # remove from list
        self.remove_address(ptr, from_api=True)
        self.onItemRemoved.emit(ptr)
Exemplo n.º 2
0
class HooksWidget(QWidget):
    """ HooksPanel

        Signals:
            onShowMemoryRequest(str) - ptr
            onHookChanged(str) - ptr
            onHookRemoved(str) - ptr
    """

    onHookChanged = pyqtSignal(str, name='onHookChanged')
    onHookRemoved = pyqtSignal(str, name='onHookRemoved')

    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(HooksWidget, self).__init__(parent=parent)

        self._app_window = parent

        if self._app_window.dwarf is None:
            print('HooksPanel created before Dwarf exists')
            return

        # connect to dwarf
        self._app_window.dwarf.onAddJavaHook.connect(self._on_add_hook)
        self._app_window.dwarf.onAddNativeHook.connect(self._on_add_hook)
        self._app_window.dwarf.onAddNativeOnLoadHook.connect(self._on_add_hook)
        self._app_window.dwarf.onAddJavaOnLoadHook.connect(self._on_add_hook)
        self._app_window.dwarf.onHitNativeOnLoad.connect(
            self._on_hit_native_on_load)
        self._app_window.dwarf.onHitJavaOnLoad.connect(
            self._on_hit_java_on_load)
        self._app_window.dwarf.onDeleteHook.connect(self._on_hook_deleted)

        self._hooks_list = DwarfListView()
        self._hooks_list.doubleClicked.connect(self._on_double_clicked)
        self._hooks_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._hooks_list.customContextMenuRequested.connect(
            self._on_context_menu)
        self._hooks_model = QStandardItemModel(0, 5)

        self._hooks_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self._hooks_model.setHeaderData(1, Qt.Horizontal, 'T')
        self._hooks_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                        Qt.TextAlignmentRole)
        self._hooks_model.setHeaderData(2, Qt.Horizontal, 'Input')
        self._hooks_model.setHeaderData(3, Qt.Horizontal, '{}')
        self._hooks_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter,
                                        Qt.TextAlignmentRole)
        self._hooks_model.setHeaderData(4, Qt.Horizontal, '<>')
        self._hooks_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter,
                                        Qt.TextAlignmentRole)

        self._hooks_list.setModel(self._hooks_model)

        self._hooks_list.header().setStretchLastSection(False)
        self._hooks_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents | QHeaderView.Interactive)
        self._hooks_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self._hooks_list.header().setSectionResizeMode(2, QHeaderView.Stretch)
        self._hooks_list.header().setSectionResizeMode(
            3, QHeaderView.ResizeToContents)
        self._hooks_list.header().setSectionResizeMode(
            4, QHeaderView.ResizeToContents)

        v_box = QVBoxLayout(self)
        v_box.setContentsMargins(0, 0, 0, 0)
        v_box.addWidget(self._hooks_list)
        #header = QHeaderView(Qt.Horizontal, self)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(5, 2, 5, 5)
        self.btn1 = QPushButton(
            QIcon(utils.resource_path('assets/icons/plus.svg')), '')
        self.btn1.setFixedSize(20, 20)
        self.btn1.clicked.connect(self._on_additem_clicked)
        btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')),
                           '')
        btn2.setFixedSize(20, 20)
        btn2.clicked.connect(self.delete_items)
        btn3 = QPushButton(
            QIcon(utils.resource_path('assets/icons/trashcan.svg')), '')
        btn3.setFixedSize(20, 20)
        btn3.clicked.connect(self.clear_list)
        h_box.addWidget(self.btn1)
        h_box.addWidget(btn2)
        h_box.addSpacerItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))
        h_box.addWidget(btn3)
        # header.setLayout(h_box)
        # header.setFixedHeight(25)
        # v_box.addWidget(header)
        v_box.addLayout(h_box)
        self.setLayout(v_box)

        self._bold_font = QFont(self._hooks_list.font())
        self._bold_font.setBold(True)

        shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_N),
                                       self._app_window, self._on_addnative)
        shortcut_addnative.setAutoRepeat(False)

        shortcut_addjava = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_J),
                                     self._app_window, self._on_addjava)
        shortcut_addjava.setAutoRepeat(False)

        shortcut_add_native_on_load = QShortcut(
            QKeySequence(Qt.CTRL + Qt.Key_O), self._app_window,
            self._on_add_native_on_load)
        shortcut_add_native_on_load.setAutoRepeat(False)

        # new menu
        self.new_menu = QMenu('New')
        self.new_menu.addAction('Native', self._on_addnative)
        self.new_menu.addAction('Java', self._on_addjava)
        self.new_menu.addAction('Module loading', self._on_add_native_on_load)

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def delete_items(self):
        """ Delete selected Items
        """
        index = self._hooks_list.selectionModel().currentIndex().row()
        if index != -1:
            self._on_delete_hook(index)
            self._hooks_model.removeRow(index)

    def clear_list(self):
        """ Clear the List
        """
        # go through all items and tell it gets removed
        for item in range(self._hooks_model.rowCount()):
            self._on_delete_hook(item)

        if self._hooks_model.rowCount() > 0:
            # something was wrong it should be empty
            self._hooks_model.removeRows(0, self._hooks_model.rowCount())

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _on_add_hook(self, hook):
        type_ = QStandardItem()
        type_.setFont(self._bold_font)
        type_.setTextAlignment(Qt.AlignCenter)
        if hook.hook_type == HOOK_NATIVE:
            type_.setText('N')
            type_.setToolTip('Native hook')
        elif hook.hook_type == HOOK_JAVA:
            type_.setText('J')
            type_.setToolTip('Java hook')
        elif hook.hook_type == HOOK_ONLOAD:
            type_.setText('O')
            type_.setToolTip('On load hook')
        else:
            type_.setText('U')
            type_.setToolTip('Unknown Type')

        addr = QStandardItem()

        if hook.hook_type == HOOK_JAVA:
            parts = hook.get_input().split('.')
            addr.setText('.'.join(parts[:len(parts) - 1]))
        else:
            str_fmt = '0x{0:x}'
            if self._hooks_list.uppercase_hex:
                str_fmt = '0x{0:X}'
            # addr.setTextAlignment(Qt.AlignCenter)
            addr.setText(str_fmt.format(hook.get_ptr()))

        inp = QStandardItem()
        inp_text = hook.get_input()
        if hook.hook_type == HOOK_JAVA:
            parts = inp_text.split('.')
            inp_text = parts[len(parts) - 1]
        # if len(inp_text) > 15:
        #    inp_text = inp_text[:15] + '...'
        #    inp.setToolTip(hook.get_input())
        inp.setText(inp_text)
        inp.setData(hook.get_input(), Qt.UserRole + 2)
        inp.setToolTip(hook.get_input())

        logic = QStandardItem()
        logic.setTextAlignment(Qt.AlignCenter)
        logic.setFont(self._bold_font)
        if hook.logic and hook.logic != 'null' and hook.logic != 'undefined':
            logic.setText('ƒ')
            logic.setToolTip(hook.logic)
            logic.setData(hook.logic, Qt.UserRole + 2)

        condition = QStandardItem()
        condition.setTextAlignment(Qt.AlignCenter)
        condition.setFont(self._bold_font)
        if hook.condition and hook.condition != 'null' and hook.condition != 'undefined':
            condition.setText('ƒ')
            condition.setToolTip(hook.condition)
            condition.setData(hook.condition, Qt.UserRole + 2)

        self._hooks_model.appendRow([addr, type_, inp, logic, condition])

    def _on_hit_native_on_load(self, data):
        items = self._hooks_model.findItems(data[1]['module'], Qt.MatchExactly,
                                            2)
        if len(items) > 0:
            self._hooks_model.item(items[0].row(),
                                   0).setText(data[1]['moduleBase'])

    def _on_hit_java_on_load(self, data):
        items = self._hooks_model.findItems(data[0], Qt.MatchExactly, 2)
        if len(items) > 0:
            pass

    def _on_double_clicked(self, model_index):
        item = self._hooks_model.itemFromIndex(model_index)
        if model_index.column() == 3 and item.text() == 'ƒ':
            self._on_modify_logic(model_index.row())
        elif model_index.column() == 4 and item.text() == 'ƒ':
            self._on_modify_condition(model_index.row())
        else:
            self._app_window.jump_to_address(self._hooks_model.item(
                model_index.row(), 0).text(),
                                             view=1)

    def _on_context_menu(self, pos):
        context_menu = QMenu(self)
        context_menu.addMenu(self.new_menu)

        context_menu.addSeparator()
        index = self._hooks_list.indexAt(pos).row()
        if index != -1:
            context_menu.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self._hooks_model.item(index, 0).text()))
            context_menu.addAction(
                'Jump to address', lambda: self._app_window.jump_to_address(
                    self._hooks_model.item(index, 0).text()))
            context_menu.addSeparator()
            context_menu.addAction('Edit Logic',
                                   lambda: self._on_modify_logic(index))
            context_menu.addAction('Edit Condition',
                                   lambda: self._on_modify_condition(index))
            context_menu.addSeparator()
            context_menu.addAction('Delete Hook',
                                   lambda: self._on_delete_hook(index))

            if self._hooks_list.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction('Search',
                                       self._hooks_list._on_cm_search)

        # show context menu
        global_pt = self._hooks_list.mapToGlobal(pos)
        context_menu.exec(global_pt)

    def _on_modify_logic(self, num_row):
        item = self._hooks_model.item(num_row, 3)
        data = item.data(Qt.UserRole + 2)
        if data is None:
            data = ''
        ptr = self._hooks_model.item(num_row, 0).text()
        accept, input_ = InputMultilineDialog().input('Insert logic for %s' %
                                                      ptr,
                                                      input_content=data)
        if accept:
            what = utils.parse_ptr(ptr)
            if what == 0:
                what = self._hooks_model.item(num_row, 2).data(Qt.UserRole + 2)
            if self._app_window.dwarf.dwarf_api(
                    'setHookLogic', [what, input_.replace('\n', '')]):
                item.setData(input_, Qt.UserRole + 2)
                if not item.text():
                    item.setText('ƒ')
                item.setToolTip(input_)
                self.onHookChanged.emit(ptr)

    def _on_modify_condition(self, num_row):
        item = self._hooks_model.item(num_row, 4)
        data = item.data(Qt.UserRole + 2)
        if data is None:
            data = ''
        ptr = self._hooks_model.item(num_row, 0).text()
        accept, input_ = InputDialog().input(self._app_window,
                                             'Insert condition for %s' % ptr,
                                             input_content=data)
        if accept:
            what = utils.parse_ptr(ptr)
            if what == 0:
                what = self._hooks_model.item(num_row, 2).data(Qt.UserRole + 2)
            if self._app_window.dwarf.dwarf_api('setHookCondition',
                                                [what, input_]):
                item.setData(input_, Qt.UserRole + 2)
                if not item.text():
                    item.setText('ƒ')
                item.setToolTip(input_)
                self.onHookChanged.emit(ptr)

    # + button
    def _on_additem_clicked(self):
        self.new_menu.exec_(QCursor.pos())

    # shortcuts/menu
    def _on_addnative(self):
        self._app_window.dwarf.hook_native()

    def _on_addjava(self):
        self._app_window.dwarf.hook_java()

    def _on_add_native_on_load(self):
        self._app_window.dwarf.hook_native_on_load()

    def _on_add_java_on_load(self):
        self._app_window.dwarf.hook_java_on_load()

    def _on_delete_hook(self, num_row):
        hook_type = self._hooks_model.item(num_row, 1).text()
        if hook_type == 'N':
            ptr = self._hooks_model.item(num_row, 0).text()
            ptr = utils.parse_ptr(ptr)
            self._app_window.dwarf.dwarf_api('deleteHook', ptr)
            self.onHookRemoved.emit(str(ptr))
        elif hook_type == 'J':
            input_ = self._hooks_model.item(num_row, 2).data(Qt.UserRole + 2)
            self._app_window.dwarf.dwarf_api('deleteHook', input_)
        elif hook_type == 'O':
            input_ = self._hooks_model.item(num_row, 2).data(Qt.UserRole + 2)
            self._app_window.dwarf.dwarf_api('deleteHook', input_)
        elif hook_type == 'U':
            ptr = self._hooks_model.item(num_row, 0).text()
            ptr = utils.parse_ptr(ptr)
            self._app_window.dwarf.dwarf_api('deleteHook', ptr)
            self.onHookRemoved.emit(str(ptr))

    def _on_hook_deleted(self, parts):
        _msg, _type, _val = parts

        additional = None

        if _type == 'java' or _type == 'java_on_load':
            _val = _val.split('.')
            str_frmt = '.'.join(_val[:-1])
            additional = _val[-1]
            item_index = 0
        elif _type == 'native_on_load':
            str_frmt = _val
            item_index = 2
        else:
            _ptr = utils.parse_ptr(_val)
            if self._hooks_list._uppercase_hex:
                str_frmt = '0x{0:X}'.format(_ptr)
            else:
                str_frmt = '0x{0:x}'.format(_ptr)
            item_index = 0

        for _item in range(self._hooks_model.rowCount()):
            item = self._hooks_model.item(_item, item_index)

            if item is None:
                continue

            if str_frmt == item.text():
                if additional is not None:
                    if additional == self._hooks_model.item(_item, 2).text():
                        self._hooks_model.removeRow(_item)
                else:
                    self._hooks_model.removeRow(_item)
Exemplo n.º 3
0
class BookmarksWidget(QWidget):
    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(BookmarksWidget, self).__init__(parent=parent)

        self._app_window = parent

        if self._app_window.dwarf is None:
            print('BookmarksPanel created before Dwarf exists')
            return

        self.bookmarks = {}

        self._bookmarks_list = DwarfListView()
        self._bookmarks_list.doubleClicked.connect(self._on_double_clicked)
        self._bookmarks_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._bookmarks_list.customContextMenuRequested.connect(
            self._on_contextmenu)

        self._bookmarks_model = QStandardItemModel(0, 2)
        self._bookmarks_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self._bookmarks_model.setHeaderData(1, Qt.Horizontal, 'Notes')

        self._bookmarks_list.setModel(self._bookmarks_model)

        self._bookmarks_list.header().setStretchLastSection(False)
        self._bookmarks_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents | QHeaderView.Interactive)
        self._bookmarks_list.header().setSectionResizeMode(
            1, QHeaderView.Stretch | QHeaderView.Interactive)

        v_box = QVBoxLayout(self)
        v_box.setContentsMargins(0, 0, 0, 0)
        v_box.addWidget(self._bookmarks_list)
        #header = QHeaderView(Qt.Horizontal, self)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(5, 2, 5, 5)
        self.btn1 = QPushButton(
            QIcon(utils.resource_path('assets/icons/plus.svg')), '')
        self.btn1.setFixedSize(20, 20)
        self.btn1.clicked.connect(lambda: self._create_bookmark(-1))
        btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')),
                           '')
        btn2.setFixedSize(20, 20)
        btn2.clicked.connect(self.delete_items)
        btn3 = QPushButton(
            QIcon(utils.resource_path('assets/icons/trashcan.svg')), '')
        btn3.setFixedSize(20, 20)
        btn3.clicked.connect(self.clear_list)
        h_box.addWidget(self.btn1)
        h_box.addWidget(btn2)
        h_box.addSpacerItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))
        h_box.addWidget(btn3)
        # header.setLayout(h_box)
        # header.setFixedHeight(25)
        # v_box.addWidget(header)
        v_box.addLayout(h_box)
        self.setLayout(v_box)

        self._bold_font = QFont(self._bookmarks_list.font())
        self._bold_font.setBold(True)

        shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_B),
                                       self._app_window, self._create_bookmark)
        shortcut_addnative.setAutoRepeat(False)

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def delete_items(self):
        """ Delete selected Items
        """
        index = self._bookmarks_list.selectionModel().currentIndex().row()
        if index != -1:
            self._on_delete_bookmark(index)
            self._bookmarks_model.removeRow(index)

    def clear_list(self):
        """ Clear the List
        """
        # go through all items and tell it gets removed
        for item in range(self._bookmarks_model.rowCount()):
            self._on_delete_bookmark(item)

        if self._bookmarks_model.rowCount() > 0:
            # something was wrong it should be empty
            self._bookmarks_model.removeRows(0,
                                             self._bookmarks_model.rowCount())

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _on_double_clicked(self, index):
        index = self._bookmarks_list.selectionModel().currentIndex().row()
        if index != -1:
            addr = self._bookmarks_model.item(index, 0).text()
            if addr:
                self._app_window.jump_to_address(addr)

    def _on_contextmenu(self, pos):
        context_menu = QMenu(self)
        index = self._bookmarks_list.indexAt(pos).row()
        if index != -1:
            context_menu.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self._bookmarks_model.item(index, 0).text()))
            context_menu.addAction(
                'Jump to address', lambda: self._app_window.jump_to_address(
                    self._bookmarks_model.item(index, 0).text()))

            # todo: add hook address menu

            context_menu.addSeparator()
            context_menu.addAction('Edit',
                                   lambda: self._create_bookmark(index=index))
            context_menu.addAction('Delete',
                                   lambda: self._on_delete_bookmark(index))
            context_menu.addSeparator()
            if self._bookmarks_list.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction('Search',
                                       self._bookmarks_list._on_cm_search)
                context_menu.addSeparator()
        context_menu.addAction('New', self._create_bookmark)
        global_pt = self._bookmarks_list.mapToGlobal(pos)
        context_menu.exec(global_pt)

    # + button
    def _create_bookmark(self, index=-1, ptr=''):
        note = ''

        if ptr == '':
            if isinstance(index, int) and index >= 0:
                ptr = self._bookmarks_model.item(index, 0).text()
                note = self._bookmarks_model.item(index, 1).text()

            ptr, _ = InputDialog.input_pointer(parent=self._app_window,
                                               input_content=ptr)
        else:
            if not isinstance(ptr, int):
                try:
                    if ptr.startswith('0x'):
                        ptr = int(ptr, 16)
                    else:
                        ptr = int(ptr)
                except ValueError:
                    ptr = 0

        if ptr > 0:
            ptr = hex(ptr)
            if self._bookmarks_list.uppercase_hex:
                ptr = ptr.upper().replace('0X', '0x')

            index = self._bookmarks_model.findItems(ptr, Qt.MatchExactly)
            if len(index) > 0:
                index = index[0].row()
                note = self._bookmarks_model.item(index, 1).text()
            else:
                index = -1

            accept, note = InputDialog.input(hint='Insert notes for %s' % ptr,
                                             input_content=note)
            if accept:
                if index < 0:
                    self.insert_bookmark(ptr, note)
                else:
                    item = self._bookmarks_model.item(index, 0)
                    item.setText(ptr)
                    item = self._bookmarks_model.item(index, 1)
                    item.setText(note)

                self.bookmarks[ptr] = note

    def insert_bookmark(self, ptr_as_hex, note):
        if self._bookmarks_list.uppercase_hex:
            ptr_as_hex = ptr_as_hex.upper().replace('0X', '0x')

        self._bookmarks_model.appendRow(
            [QStandardItem(ptr_as_hex),
             QStandardItem(note)])
        self._bookmarks_list.resizeColumnToContents(0)

    # shortcuts/menu
    def _on_delete_bookmark(self, index):
        ptr = self._bookmarks_model.item(index, 0).text()
        del self.bookmarks[ptr]

        self._bookmarks_model.removeRow(index)
Exemplo n.º 4
0
class ContextWidget(QTabWidget):
    # consts
    CONTEXT_TYPE_NATIVE = 0
    CONTEXT_TYPE_JAVA = 1

    onShowMemoryRequest = pyqtSignal(str, name='onShowMemoryRequest')

    def __init__(self, parent=None):
        super(ContextWidget, self).__init__(parent=parent)

        self._app_window = parent
        self.setAutoFillBackground(True)

        self._app_window.dwarf.onContextChanged.connect(
            self._on_context_changed)

        self._nativectx_model = QStandardItemModel(0, 4)
        self._nativectx_model.setHeaderData(0, Qt.Horizontal, 'Reg')
        self._nativectx_model.setHeaderData(0, Qt.Horizontal, Qt.AlignCenter,
                                            Qt.TextAlignmentRole)
        self._nativectx_model.setHeaderData(1, Qt.Horizontal, 'Value')
        self._nativectx_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                            Qt.TextAlignmentRole)
        self._nativectx_model.setHeaderData(2, Qt.Horizontal, 'Decimal')
        self._nativectx_model.setHeaderData(3, Qt.Horizontal, 'Telescope')

        self._nativectx_list = DwarfListView()
        self._nativectx_list.setModel(self._nativectx_model)

        self._nativectx_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self._nativectx_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self._nativectx_list.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)

        self._nativectx_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._nativectx_list.customContextMenuRequested.connect(
            self._on_native_contextmenu)

        self._javactx_model = QStandardItemModel(0, 3)
        self._javactx_model.setHeaderData(0, Qt.Horizontal, 'Argument')
        self._javactx_model.setHeaderData(0, Qt.Horizontal, Qt.AlignCenter,
                                          Qt.TextAlignmentRole)
        self._javactx_model.setHeaderData(1, Qt.Horizontal, 'Class')
        self._javactx_model.setHeaderData(2, Qt.Horizontal, 'Value')

        self._javactx_list = DwarfListView()
        self._javactx_list.setModel(self._javactx_model)

        self._javactx_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self._javactx_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self._javactx_list.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)

        self._javactx_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._javactx_list.customContextMenuRequested.connect(
            self._on_java_contextmenu)

        self.addTab(self._nativectx_list, 'Native')
        self.show_context_tab('Native')

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def clear(self):
        self._nativectx_list.clear()
        self._javactx_list.clear()

    def set_context(self, ptr, context_type, context):
        if isinstance(context, str):
            context = json.loads(context)

        if context_type == ContextWidget.CONTEXT_TYPE_NATIVE:
            self._nativectx_list.clear()
            self._set_native_context(ptr, context)
        elif context_type == ContextWidget.CONTEXT_TYPE_JAVA:
            self._javactx_list.clear()
            self._set_java_context(ptr, context)
        else:
            raise Exception('unknown context type')

    def have_context(self):
        return self.count() > 0

    def show_context_tab(self, tab_name):
        index = 0
        tab_name = tab_name.join(tab_name.split()).lower()
        if tab_name == 'native':
            index = self.indexOf(self._nativectx_list)
        elif tab_name == 'java':
            index = self.indexOf(self._javactx_list)

        if self.count() > 0:
            self.setCurrentIndex(index)

    def _set_native_context(self, ptr, context):
        if self.indexOf(self._nativectx_list) == -1:
            self.addTab(self._nativectx_list, 'Native')
            self.show_context_tab('Native')
        else:
            self.show_context_tab('Native')

        context_ptr = ptr
        sorted_regs = self.get_sort_order()

        for register in sorted(context,
                               key=lambda x: sorted_regs[x]
                               if x in sorted_regs else len(sorted_regs)):
            reg_name = QStandardItem()
            reg_name.setTextAlignment(Qt.AlignCenter)
            if context[register]['isValidPointer']:
                reg_name.setData(context_ptr, Qt.UserRole + 1)

            value_x = QStandardItem()
            if context[register]['isValidPointer']:
                value_x.setForeground(Qt.red)

            value_dec = QStandardItem()
            telescope = QStandardItem()

            reg_name.setText(register)

            if context[register] is not None:
                str_fmt = '0x{0:x}'
                if self._nativectx_list.uppercase_hex:
                    str_fmt = '0x{0:X}'

                value_x.setText(
                    str_fmt.format(int(context[register]['value'], 16)))

                value_dec.setText('{0:d}'.format(
                    int(context[register]['value'], 16)))

                if context[register]['isValidPointer']:
                    if 'telescope' in context[register] and context[register][
                            'telescope'] is not None:
                        telescope = QStandardItem()

                        telescope_value = str(
                            context[register]['telescope'][1]).replace(
                                '\n', ' ')
                        if len(telescope_value) > 50:
                            telescope_value = telescope_value[:50] + '...'

                        telescope.setText(telescope_value)
                        if context[register]['telescope'][0] == 2:
                            telescope.setData(
                                context[register]['telescope'][1],
                                Qt.UserRole + 1)

                        if context[register]['telescope'][0] == 0:
                            telescope.setForeground(Qt.darkGreen)
                        elif context[register]['telescope'][0] == 2:
                            telescope.setForeground(Qt.white)
                        elif context[register]['telescope'][0] != 1:
                            telescope.setForeground(Qt.darkGray)

            self._nativectx_model.appendRow(
                [reg_name, value_x, value_dec, telescope])
            self._nativectx_list.resizeColumnToContents(0)

    def _set_java_context(self, ptr, context):
        if self.indexOf(self._javactx_list) == -1:
            self.addTab(self._javactx_list, 'Java')
            self.show_context_tab('Java')
        else:
            self.show_context_tab('Java')

        for arg in context:
            _arg = QStandardItem()
            _arg.setText(arg)

            _class = QStandardItem()
            _class.setText(context[arg]['className'])
            if isinstance(context[arg]['handle'], str):
                _class.setForeground(Qt.lightGray)

            _value = QStandardItem()
            if 'arg' not in context[arg] or context[arg]['arg'] is None:
                _value.setText('null')
                _value.setForeground(Qt.gray)
            else:
                _value.setText(context[arg]['arg'])

            self._javactx_model.appendRow([_arg, _class, _value])
            self._javactx_list.resizeColumnToContents(0)
            self._javactx_list.resizeColumnToContents(1)

    def get_sort_order(self):
        reg_order = []
        if self._app_window.dwarf.arch == 'arm':  # arm
            reg_order = [
                'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9',
                'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'sp', 'lr', 'sb',
                'sl', 'fp', 'ip', 'pc', 'cspr'
            ]
        elif self._app_window.dwarf.arch == 'arm64':
            reg_order = [
                'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9',
                'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18',
                'x19', 'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27',
                'x28', 'x29', 'x30', 'w0', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6',
                'w7', 'w8', 'w9', 'w10', 'w11', 'w12', 'w13', 'w14', 'w15',
                'w16', 'w17', 'w18', 'w19', 'w20', 'w21', 'w22', 'w23', 'w24',
                'w25', 'w26', 'w27', 'w28', 'w29', 'w30', 'sp', 'lr', 'fp',
                'wsp', 'wzr', 'xzr', 'nzcv', 'ip0', 'ip1', 's0', 's1', 's2',
                's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', 's12',
                's13', 's14', 's15', 's16', 's17', 's18', 's19', 's20', 's21',
                's22', 's23', 's24', 's25', 's26', 's27', 's28', 's29', 's30',
                's31', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8',
                'd9', 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', 'd16', 'd17',
                'd18', 'd19', 'd20', 'd21', 'd22', 'd23', 'd24', 'd25', 'd26',
                'd27', 'd28', 'd29', 'd30', 'd31', 'q0', 'q1', 'q2', 'q3',
                'q4', 'q5', 'q6', 'q7', 'q8', 'q9', 'q10', 'q11', 'q12', 'q13',
                'q14', 'q15', 'q16', 'q17', 'q18', 'q19', 'q20', 'q21', 'q22',
                'q23', 'q24', 'q25', 'q26', 'q27', 'q28', 'q29', 'q30', 'q31',
                'sp', 'lr', 'sb', 'sl', 'fp', 'ip', 'pc', 'cspr'
            ]
        elif self._app_window.dwarf.arch == 'ia32':
            reg_order = [
                'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'esp', 'r8d', 'r9d',
                'r10d', 'r11d', 'r12d', 'r13d', 'r14d', 'r15d', 'ebp', 'eip',
                'sp', 'pc'
            ]
        elif self._app_window.dwarf.arch == 'x64':  # x64
            reg_order = [
                'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi', 'rbp', 'rsp', 'r8',
                'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'esp', 'ebp',
                'rip', 'eip', 'sp', 'pc'
            ]

        sorted_regs = {b: i for i, b in enumerate(reg_order)}
        return sorted_regs

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _on_native_contextmenu(self, pos):
        index = self._nativectx_list.indexAt(pos).row()
        glbl_pt = self._nativectx_list.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            item = self._nativectx_model.item(index, 1)
            dec = self._nativectx_model.item(index, 2)
            telescope = self._nativectx_model.item(index, 3)
            # show contextmenu
            if self._nativectx_model.item(index, 0).data(Qt.UserRole + 1):
                context_menu.addAction(
                    'Jump to {0}'.format(item.text()),
                    lambda: self._app_window.jump_to_address(item.text()))
                context_menu.addSeparator()
            # copy menu
            context_sub_menu = QMenu('Copy', context_menu)
            context_sub_menu.addAction(
                'Value', lambda: utils.copy_str_to_clipboard(item.text()))
            if dec.text():
                context_sub_menu.addAction(
                    'Decimal', lambda: utils.copy_str_to_clipboard(dec.text()))
            if telescope.text():
                context_sub_menu.addAction(
                    'Telescope',
                    lambda: utils.copy_str_to_clipboard(telescope.text()))
            context_menu.addMenu(context_sub_menu)

            context_menu.exec_(glbl_pt)

    def _on_java_contextmenu(self, pos):
        index = self._javactx_list.indexAt(pos).row()
        glbl_pt = self._javactx_list.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            # show contextmenu
            argument = self._javactx_model.item(index, 1)
            _class = self._javactx_model.item(index, 2)
            value = self._javactx_model.item(index, 3)
            context_sub_menu = QMenu('Copy', context_menu)
            context_sub_menu.addAction(
                'Argument',
                lambda: utils.copy_str_to_clipboard(argument.text()))
            if _class.text():
                context_sub_menu.addAction(
                    'Class',
                    lambda: utils.copy_str_to_clipboard(_class.text()))
            if value.text():
                context_sub_menu.addAction(
                    'Value', lambda: utils.copy_str_to_clipboard(value.text()))
            context_menu.addMenu(context_sub_menu)
            context_menu.exec_(glbl_pt)

    def _on_context_changed(self, reg_name, reg_val):
        x_in = 0
        for c in reg_val:
            if c.lower() not in '1234567890abcdef':
                if c.lower() == 'x' and x_in == 0:
                    x_in += 1
                    continue

                self._app_window.dwarf.onLogToConsole.emit(
                    'error: invalid reg_value: ' + reg_val +
                    ' - expected dec/hex')
                return

        if isinstance(reg_val, str) and reg_val.startswith('0x'):
            try:
                reg_val = int(reg_val, 16)
            except ValueError:
                self._app_window.dwarf.onLogToConsole.emit(
                    'error: invalid reg_value: ' + reg_val +
                    ' - expected dec/hex')
                return
        try:
            reg_val = int(reg_val)
        except ValueError:
            self._app_window.dwarf.onLogToConsole.emit(
                'error: invalid reg_value: ' + reg_val + ' - expected dec/hex')
            return

        reg_val = hex(reg_val)
        was_found, find_result = self._nativectx_list.contains_text(
            reg_name, True, True, True)
        if was_found:
            if len(find_result) == 1:
                find_result = find_result[0]
                if self._nativectx_model.item(find_result[0],
                                              0).text() == reg_name:
                    str_fmt = '0x{0:x}'
                    if self._nativectx_list.uppercase_hex:
                        str_fmt = '0x{0:X}'

                    value_x = str_fmt.format(int(reg_val, 16))

                    value_dec = '{0:d}'.format(int(reg_val, 16))
                    self._nativectx_model.item(find_result[0],
                                               1).setText(value_x)
                    self._nativectx_model.item(find_result[0],
                                               2).setText(value_dec)
                    self._nativectx_model.item(find_result[0], 3).setText("")
Exemplo n.º 5
0
class ModulesPanel(QSplitter):
    """ ModulesPanel

        Signals:
            onAddBreakpoint([ptr, funcname]) - MenuItem AddBreakpoint
            onDumpBinary([ptr, size#int]) - MenuItem DumpBinary
            onModuleSelected([ptr, size#int]) - ModuleDoubleClicked
            onModuleFuncSelected(ptr) - FunctionDoubleClicked
    """
    # pylint: disable=too-many-instance-attributes

    onAddBreakpoint = pyqtSignal(list, name='onAddBreakpoint')
    onDumpBinary = pyqtSignal(list, name='onDumpBinary')
    onModuleSelected = pyqtSignal(list, name='onModuleSelected')
    onModuleFuncSelected = pyqtSignal(str, name='onModuleFuncSelected')

    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(ModulesPanel, self).__init__(parent)
        self._app_window = parent

        if self._app_window.dwarf is None:
            print('ModulesPanel created before Dwarf exists')
            return

        self._app_window.dwarf.onSetModules.connect(self.set_modules)
        self._app_window.dwarf.onModuleLoaded.connect(self.on_module_loaded)

        self._uppercase_hex = True
        self._sized = False
        self.setContentsMargins(0, 0, 0, 0)

        # setup models
        self.modules_list = None
        self.modules_model = QStandardItemModel(0, 4, self)
        self.modules_model.setHeaderData(0, Qt.Horizontal, 'Name')
        self.modules_model.setHeaderData(1, Qt.Horizontal, 'Base')
        self.modules_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self.modules_model.setHeaderData(2, Qt.Horizontal, 'Size')
        self.modules_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self.modules_model.setHeaderData(3, Qt.Horizontal, 'Path')

        self.imports_list = None
        self.imports_model = QStandardItemModel(0, 4, self)
        self.imports_model.setHeaderData(0, Qt.Horizontal, 'Import')
        self.imports_model.setHeaderData(1, Qt.Horizontal, 'Address')
        self.imports_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self.imports_model.setHeaderData(2, Qt.Horizontal, 'Module')
        self.imports_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self.imports_model.setHeaderData(3, Qt.Horizontal, 'Type')

        self.exports_list = None
        self.exports_model = QStandardItemModel(0, 3, self)
        self.exports_model.setHeaderData(0, Qt.Horizontal, 'Export')
        self.exports_model.setHeaderData(1, Qt.Horizontal, 'Address')
        self.exports_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self.exports_model.setHeaderData(2, Qt.Horizontal, 'Type')

        self.symbols_list = None
        self.symbols_model = QStandardItemModel(0, 3, self)
        self.symbols_model.setHeaderData(0, Qt.Horizontal, 'Symbol')
        self.symbols_model.setHeaderData(1, Qt.Horizontal, 'Address')
        self.symbols_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self.symbols_model.setHeaderData(2, Qt.Horizontal, 'Type')

        # setup ui
        self.modules_list = DwarfListView()
        self.modules_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.modules_list.customContextMenuRequested.connect(
            self._on_modules_contextmenu)
        self.modules_list.setEditTriggers(self.modules_list.NoEditTriggers)
        self.modules_list.clicked.connect(self._module_clicked)
        self.modules_list.doubleClicked.connect(self._module_dblclicked)
        self.modules_list.setModel(self.modules_model)
        self.modules_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.modules_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.modules_list.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)
        self.modules_list.selectionModel().selectionChanged.connect(
            self._module_clicked)

        self.addWidget(self.modules_list)

        v_splitter = QSplitter(Qt.Vertical)
        self.imports_list = DwarfListView()
        self.imports_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.imports_list.customContextMenuRequested.connect(
            self._on_imports_contextmenu)
        self.imports_list.setEditTriggers(self.modules_list.NoEditTriggers)
        self.imports_list.doubleClicked.connect(self._import_dblclicked)
        self.imports_list.setModel(self.imports_model)
        self.imports_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.imports_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.imports_list.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)
        self.imports_list.setVisible(False)
        self.exports_list = DwarfListView()
        self.exports_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.exports_list.customContextMenuRequested.connect(
            self._on_exports_contextmenu)
        self.exports_list.setEditTriggers(self.modules_list.NoEditTriggers)
        self.exports_list.doubleClicked.connect(self._export_dblclicked)
        self.exports_list.setModel(self.exports_model)
        self.exports_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.exports_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.exports_list.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)
        self.exports_list.setVisible(False)
        self.symbols_list = DwarfListView()
        self.symbols_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self.symbols_list.doubleClicked.connect(self._symbol_dblclicked)
        self.symbols_list.setModel(self.symbols_model)
        self.symbols_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self.symbols_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self.symbols_list.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)
        self.symbols_list.setVisible(False)
        v_splitter.addWidget(self.imports_list)
        v_splitter.addWidget(self.exports_list)
        v_splitter.addWidget(self.symbols_list)
        v_splitter.setSizes([100, 100, 100])
        self.addWidget(v_splitter)

        self.update_modules()

    # ************************************************************************
    # **************************** Properties ********************************
    # ************************************************************************
    @property
    def uppercase_hex(self):
        """ HexDisplayStyle
        """
        return self._uppercase_hex

    @uppercase_hex.setter
    def uppercase_hex(self, value):
        """ HexDisplayStyle
        """
        if isinstance(value, bool):
            self._uppercase_hex = value
        elif isinstance(value, str):
            self._uppercase_hex = (value == 'upper')

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def set_modules(self, modules):
        """ Fills the ModulesList with data
        """
        if self.modules_list is None:
            return

        self.modules_list.clear()
        for module in modules:
            self.add_module(module)

        self.modules_list.resizeColumnToContents(0)
        self.modules_list.resizeColumnToContents(1)
        self.modules_list.resizeColumnToContents(2)
        self.modules_list.resizeColumnToContents(3)

    def on_module_loaded(self, data):
        module = data[0]
        self.add_module(module)

    def add_module(self, module):
        name = QStandardItem()
        name.setTextAlignment(Qt.AlignLeft)
        if 'name' in module:
            name.setText(module['name'])

        base = QStandardItem()
        base.setTextAlignment(Qt.AlignCenter)

        str_fmt = '0x{0:X}'
        if not self.uppercase_hex:
            str_fmt = '0x{0:x}'

        if 'base' in module:
            base.setText(str_fmt.format(int(module['base'], 16)))

        size = QStandardItem()
        size.setTextAlignment(Qt.AlignRight)
        if 'size' in module:
            size.setText("{0:,d}".format(int(module['size'])))

        path = QStandardItem()
        path.setTextAlignment(Qt.AlignLeft)
        if 'path' in module:
            path.setText(module['path'])

        self.modules_model.appendRow([name, base, size, path])

        module_info = ModuleInfo(module)
        if 'exports' in module and module['exports']:
            module_info.apply_exports(module['exports'])
        if 'imports' in module and module['imports']:
            module_info.apply_imports(module['imports'])
        if 'symbols' in module and module['symbols']:
            module_info.apply_symbols(module['symbols'])
        module_info._updated_details = True

    def update_modules(self):
        """ DwarfApiCall updateModules
        """
        return self._app_window.dwarf.dwarf_api('updateModules')

    def set_imports(self, imports):
        """ Fills the ImportsList with data
        """
        if self.imports_list is None:
            return

        self.imports_list.clear()
        for import_ in imports:
            name = QStandardItem()
            name.setTextAlignment(Qt.AlignLeft)
            if 'name' in import_:
                name.setText(import_['name'])

            address = QStandardItem()
            address.setTextAlignment(Qt.AlignCenter)

            str_fmt = '0x{0:X}'
            if not self.uppercase_hex:
                str_fmt = '0x{0:x}'
            if 'address' in import_:
                address.setText(str_fmt.format(int(import_['address'], 16)))

            module = QStandardItem()
            if 'module' in import_:
                module.setTextAlignment(Qt.AlignLeft)
                module.setText(import_['module'])

            type_ = QStandardItem()
            if 'type' in import_:
                type_.setTextAlignment(Qt.AlignLeft)
                type_.setText(import_['type'])

            self.imports_model.appendRow([name, address, module, type_])

    def set_exports(self, exports):
        """ Fills the ExportsList with data
        """
        if self.exports_list is None:
            return

        self.exports_list.clear()
        for export in exports:
            name = QStandardItem()
            name.setTextAlignment(Qt.AlignLeft)
            if 'name' in export:
                name.setText(export['name'])

            address = QStandardItem()
            address.setTextAlignment(Qt.AlignCenter)
            str_fmt = '0x{0:X}'
            if not self.uppercase_hex:
                str_fmt = '0x{0:x}'

            if 'address' in export:
                address.setText(str_fmt.format(int(export['address'], 16)))

            type_ = QStandardItem()
            type_.setTextAlignment(Qt.AlignLeft)
            if 'type' in export:
                type_.setText(export['type'])

            self.exports_model.appendRow([name, address, type_])

    def set_symbols(self, symbols):
        """ Fills the SymbolsList with data
        """
        if self.symbols_list is None:
            return

        self.symbols_list.clear()
        for symbol in symbols:
            name = QStandardItem()
            name.setTextAlignment(Qt.AlignLeft)
            if 'name' in symbol:
                name.setText(symbol['name'])

            address = QStandardItem()
            address.setTextAlignment(Qt.AlignCenter)
            str_fmt = '0x{0:X}'
            if not self.uppercase_hex:
                str_fmt = '0x{0:x}'

            if 'address' in symbol:
                address.setText(str_fmt.format(int(symbol['address'], 16)))

            type_ = QStandardItem()
            type_.setTextAlignment(Qt.AlignLeft)
            if 'type' in symbol:
                type_.setText(symbol['type'])

            self.symbols_model.appendRow([name, address, type_])

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _module_clicked(self):
        """ Module Clicked updates imports/exports/symbols
        """
        if not self.modules_list.hasFocus():
            return

        module_index = self.modules_list.selectionModel().currentIndex().row()
        module_name = self.modules_model.item(module_index, 0)
        if module_name is None:
            return

        module_name = module_name.text()
        module_address = self.modules_model.item(module_index, 1).text()

        module_info = self._app_window.dwarf.database.get_module_info(
            module_address)
        if module_info is not None:
            if not module_info.have_details:
                module_info.update_details(self._app_window.dwarf)
        else:
            module_info = ModuleInfo.build_module_info(self._app_window.dwarf,
                                                       module_name,
                                                       fill_ied=True)

        if module_info is not None:
            self.update_module_ui(module_info)

        if not self._sized:
            self.setSizes([100, 100])
            self._sized = True

    def update_module_ui(self, module_info):
        if len(module_info.imports) > 0:
            self.set_imports(module_info.imports)
            self.imports_list.setVisible(True)
            self.imports_list.resizeColumnToContents(0)
            self.imports_list.resizeColumnToContents(1)
            self.imports_list.resizeColumnToContents(2)
        else:
            self.imports_list.setVisible(False)

        if len(module_info.exports) > 0:
            self.set_exports(module_info.exports)
            self.exports_list.setVisible(True)
            self.exports_list.resizeColumnToContents(0)
            self.exports_list.resizeColumnToContents(1)
        else:
            self.exports_list.setVisible(False)

        if len(module_info.symbols) > 0:
            self.set_symbols(module_info.symbols)
            self.symbols_list.setVisible(True)
            self.symbols_list.resizeColumnToContents(0)
            self.symbols_list.resizeColumnToContents(1)
        else:
            self.symbols_list.setVisible(False)

    def _module_dblclicked(self):
        """ Module DoubleClicked
        """
        module_index = self.modules_list.selectionModel().currentIndex().row()
        base = self.modules_model.item(module_index, 1).text()
        size = self.modules_model.item(module_index, 2).text().replace(',', '')
        self.onModuleSelected.emit([base, size])

    def _import_dblclicked(self):
        """ ImportFunction DoubleClicked
        """
        index = self.imports_list.selectionModel().currentIndex().row()
        addr = self.imports_model.item(index, 1).text()
        self.onModuleFuncSelected.emit(addr)

    def _export_dblclicked(self):
        """ ExportFunction DoubleClicked
        """
        index = self.exports_list.selectionModel().currentIndex().row()
        addr = self.exports_model.item(index, 1).text()
        self.onModuleFuncSelected.emit(addr)

    def _symbol_dblclicked(self):
        """ Symbol DoubleClicked
        """
        index = self.symbols_list.selectionModel().currentIndex().row()
        addr = self.symbols_model.item(index, 1).text()
        self.onModuleFuncSelected.emit(addr)

    def _on_modules_contextmenu(self, pos):
        """ Modules ContextMenu
        """
        index = self.modules_list.indexAt(pos).row()
        glbl_pt = self.modules_list.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            context_menu.addAction(
                'Dump Binary', lambda: self._on_dumpmodule(
                    self.modules_model.item(index, 1).text(),
                    self.modules_model.item(index, 2).text()))
            context_menu.addSeparator()
            context_menu.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self.modules_model.item(index, 1).text()))
            context_menu.addAction(
                'Copy Name', lambda: utils.copy_str_to_clipboard(
                    self.modules_model.item(index, 0).text()))
            context_menu.addAction(
                'Copy Path', lambda: utils.copy_str_to_clipboard(
                    self.modules_model.item(index, 3).text()))
            context_menu.addSeparator()
            file_path = self.modules_model.item(index, 3).text()
            if self._app_window.dwarf._platform == 'linux':
                context_menu.addAction('Show ELF Info',
                                       lambda: self._on_parse_elf(file_path))
                context_menu.addSeparator()
            # elif file_path and (file_path.endswith('.dll') or file_path.endswith('.exe')):
            #   context_menu.addAction('Show PE Info', lambda: self._on_parse_pe(file_path))
            #   context_menu.addSeparator()

            if self.modules_list.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction('Search',
                                       self.modules_list._on_cm_search)
                context_menu.addSeparator()

        context_menu.addAction('Refresh', self.update_modules)
        context_menu.exec_(glbl_pt)

    def _on_imports_contextmenu(self, pos):
        """ ImportList ContextMenu
        """
        index = self.imports_list.indexAt(pos).row()
        if index != -1:
            context_menu = QMenu(self)
            func_name = self.imports_model.item(index, 0).text()
            addr = self.imports_model.item(index, 1).text()
            context_menu.addAction(
                'Add Breakpoint',
                lambda: self._add_breakpoint(addr, func_name))
            context_menu.addSeparator()
            context_menu.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self.imports_model.item(index, 1).text()))
            context_menu.addSeparator()
            context_menu.addAction(
                'Copy FunctionName',
                lambda: utils.copy_str_to_clipboard(func_name))
            context_menu.addAction(
                'Copy ModuleName', lambda: utils.copy_str_to_clipboard(
                    self.imports_model.item(index, 2).text()))

            if self.imports_list.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction('Search',
                                       self.imports_list._on_cm_search)

            # show context menu
            glbl_pt = self.imports_list.mapToGlobal(pos)
            context_menu.exec_(glbl_pt)

    def _on_exports_contextmenu(self, pos):
        """ ExportsList ContextMenu
        """
        index = self.exports_list.indexAt(pos).row()
        if index != -1:
            context_menu = QMenu(self)
            func_name = self.exports_model.item(index, 0).text()
            addr = self.exports_model.item(index, 1).text()
            context_menu.addAction(
                'Add Breakpoint',
                lambda: self._add_breakpoint(addr, func_name))
            context_menu.addSeparator()
            context_menu.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self.exports_model.item(index, 1).text()))
            context_menu.addSeparator()
            context_menu.addAction(
                'Copy FunctionName',
                lambda: utils.copy_str_to_clipboard(func_name))

            if self.exports_list.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction('Search',
                                       self.exports_list._on_cm_search)

            # show contextmenu
            glbl_pt = self.exports_list.mapToGlobal(pos)
            context_menu.exec_(glbl_pt)

    def _on_dumpmodule(self, ptr, size):
        """ MenuItem DumpBinary
        """
        if isinstance(ptr, int):
            str_fmt = '0x{0:X}'
            if not self.uppercase_hex:
                str_fmt = '0x{0:x}'
            ptr = str_fmt.format(ptr)

        size = size.replace(',', '')
        self.onDumpBinary.emit([ptr, size])

    def _add_breakpoint(self, ptr, name=None):
        """ MenuItem AddBreakpoint
        """
        if name is None:
            name = ptr
        if isinstance(ptr, str):
            if ptr.startswith('0x') or ptr.startswith('#'):
                self.onAddBreakpoint.emit([ptr, name])
        elif isinstance(ptr, int):
            str_fmt = '0x{0:x}'
            self.onAddBreakpoint.emit(str_fmt.format([ptr, name]))

    def _on_parse_elf(self, elf_path):
        from dwarf.ui.dialogs.elf_info_dlg import ElfInfo
        parsed_infos = self._app_window.dwarf.dwarf_api('parseElf', elf_path)
        if parsed_infos:
            elf_dlg = ElfInfo(self._app_window, elf_path)
            elf_dlg.onShowMemoryRequest.connect(self.onModuleFuncSelected)
            elf_dlg.set_parsed_data(parsed_infos)
            elf_dlg.show()
Exemplo n.º 6
0
class ObjCInspector(QWidget):
    """ ObjC Class/Methods Lists
    """

    def __init__(self, parent=None):
        super(ObjCInspector, self).__init__(parent)

        self._app_window = parent

        self._app_window.dwarf.onEnumerateObjCModules.connect(self._on_enumerate_objc_modules)
        self._app_window.dwarf.onEnumerateObjCMethodsStart.connect(
            self._on_method_enumeration_start)
        self._app_window.dwarf.onEnumerateObjCMethodsMatch.connect(
            self._on_method_enumeration_match)
        self._app_window.dwarf.onEnumerateObjCMethodsComplete.connect(
            self._on_method_enumeration_complete)

        self._app_window.dwarf.onEnumerateObjCClassesStart.connect(
            self._on_class_enumeration_start)
        self._app_window.dwarf.onEnumerateObjCClassesMatch.connect(
            self._on_class_enumeration_match)
        self._app_window.dwarf.onEnumerateObjCClassesComplete.connect(
            self._on_class_enumeration_complete)

        self._ObjC_modules = DwarfListView(self)
        self._ObjCmodule_model = QStandardItemModel(0, 1)
        self._ObjCmodule_model.setHeaderData(0, Qt.Horizontal, 'Modules')
        self._ObjC_modules.setModel(self._ObjCmodule_model)
        self._ObjC_modules.selectionModel().selectionChanged.connect(
            self._module_clicked)
        self._ObjC_modules.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self._ObjC_modules.setContextMenuPolicy(Qt.CustomContextMenu)
        self._ObjC_modules.customContextMenuRequested.connect(
            self._on_module_contextmenu)
        self._ObjC_modules.doubleClicked.connect(self._class_dblclicked)

        self._ObjC_classes = DwarfListView(self)
        self._ObjCclass_model = QStandardItemModel(0, 1)
        self._ObjCclass_model.setHeaderData(0, Qt.Horizontal, 'Class')
        self._ObjC_classes.setModel(self._ObjCclass_model)
        self._ObjC_classes.selectionModel().selectionChanged.connect(
            self._class_clicked)
        self._ObjC_classes.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self._ObjC_classes.setContextMenuPolicy(Qt.CustomContextMenu)
        self._ObjC_classes.customContextMenuRequested.connect(
            self._on_class_contextmenu)
        self._ObjC_classes.doubleClicked.connect(self._class_dblclicked)

        self._ObjC_methods = DwarfListView(self)
        self._ObjCmethod_model = QStandardItemModel(0, 1)
        self._ObjCmethod_model.setHeaderData(0, Qt.Horizontal, 'Method')
        self._ObjC_methods.setModel(self._ObjCmethod_model)
        self._ObjC_methods.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self._ObjC_methods.setContextMenuPolicy(Qt.CustomContextMenu)
        self._ObjC_methods.customContextMenuRequested.connect(
            self._on_method_contextmenu)
        self._ObjC_methods.doubleClicked.connect(self._method_dblclicked)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(0, 0, 0, 0)
        h_box.addWidget(self._ObjC_modules)
        h_box.addWidget(self._ObjC_classes)
        h_box.addWidget(self._ObjC_methods)
        self.setLayout(h_box)

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def update_classes(self, module_name):
        """ Refresh Classeslist
        """
        self._app_window.dwarf.dwarf_api('enumerateObjCClasses', module_name)

    def update_methods(self, class_name):
        """ Refresh Methodslist
        """
        if class_name:
            self._app_window.dwarf.dwarf_api('enumerateObjCMethods',
                                             class_name)

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _module_clicked(self):
        index = self._ObjC_modules.selectionModel().currentIndex().row()
        _module = self._ObjCmodule_model.item(index, 0)
        if _module is None:
            return

        self._app_window.dwarf.dwarf_api('enumerateObjCClasses', _module.text())

    def _class_clicked(self):
        index = self._ObjC_classes.selectionModel().currentIndex().row()
        _class = self._ObjCclass_model.item(index, 0)
        if _class is None:
            return

        self._app_window.dwarf.dwarf_api('enumerateObjCMethods', _class.text())

    def _on_class_enumeration_start(self):
        self._ObjC_classes.clear()
        self._ObjC_methods.clear()

    def _on_method_enumeration_start(self):
        self._ObjC_methods.clear()

    def _on_class_enumeration_match(self, ObjC_class):
        _class_name = QStandardItem()
        _class_name.setText(ObjC_class)
        self._ObjCclass_model.appendRow(_class_name)

    def _on_method_enumeration_match(self, ObjC_method):
        _method_name = QStandardItem()
        _method_name.setText(ObjC_method)
        self._ObjCmethod_model.appendRow(_method_name)

    def _on_class_enumeration_complete(self):
        self._ObjC_classes.sortByColumn(0, 0)

    def _on_method_enumeration_complete(self):
        self._ObjC_methods.sortByColumn(0, 0)

    def _class_dblclicked(self):
        """ Class DoubleClicked
        """
        index = self._ObjC_classes.selectionModel().currentIndex().row()
        if index:
            class_item = self._ObjCclass_model.item(index, 0)
            if class_item:
                class_name = class_item.text()
                if class_name:
                    self._breakpoint_class(class_name)

    def _method_dblclicked(self):
        """ Function DoubleClicked
        """
        class_index = self._ObjC_classes.selectionModel().currentIndex().row()
        method_index = self._ObjC_methods.selectionModel().currentIndex().row()
        if class_index and method_index:
            class_item = self._ObjCclass_model.item(class_index, 0)
            method_item = self._ObjCmethod_model.item(method_index, 0)
            if class_item and method_item:
                class_name = class_item.text()
                method_name = method_item.text()
                if class_name and method_name:
                    self._app_window.dwarf.breakpoint_objc(class_name + '.'
                                                           + method_name)

    def _breakpoint_class(self, class_name):
        if class_name:
            self._app_window.dwarf.breakpoint_objc(class_name)

    def _breakpoint_class_functions(self, class_name):
        if class_name:
            self._app_window.dwarf.dwarf_api('breakpointAllObjCMethods', class_name)

    def _on_class_contextmenu(self, pos):
        """ Class ContextMenu
        """
        index = self._ObjC_classes.indexAt(pos).row()
        glbl_pt = self._ObjC_classes.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            context_menu.addAction(
                'Breakpoint constructor', lambda: self._breakpoint_class(
                    self._ObjCclass_model.item(index, 0).text()))
            context_menu.addAction(
                'Breakpoint all methods', lambda: self._breakpoint_class_functions(
                    self._ObjCclass_model.item(index, 0).text()))
            context_menu.addSeparator()

            if self._ObjC_classes.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction(
                    'Search', self._ObjC_classes._on_cm_search)

        context_menu.addAction('Refresh', self._cm_refresh_classes)
        context_menu.exec_(glbl_pt)

    def _breakpoint_method(self, method_name):
        class_index = self._ObjC_classes.selectionModel().currentIndex().row()
        if class_index:
            class_item = self._ObjCclass_model.item(class_index, 0)
            if class_item:
                class_name = class_item.text()
                if class_name and method_name:
                    self._app_window.dwarf.breakpoint_objc(class_name + '.' + method_name)

    def _cm_refresh_methods(self):
        index = self._ObjC_classes.selectionModel().currentIndex().row()
        _class = self._ObjCclass_model.item(index, 0)
        if _class is None:
            return

        self.update_methods(_class.text())

    def _on_method_contextmenu(self, pos):
        """ Method ContextMenu
        """
        index = self._ObjC_methods.indexAt(pos).row()
        glbl_pt = self._ObjC_methods.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            context_menu.addAction(
                'Breakpoint method', lambda: self._breakpoint_method(
                    self._ObjCmethod_model.item(index, 0).text()))
            context_menu.addSeparator()

            if self._ObjC_methods.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction(
                    'Search', self._ObjC_methods._on_cm_search)

        context_menu.addAction('Refresh', self._cm_refresh_methods)
        context_menu.exec_(glbl_pt)

    def _cm_refresh_classes(self):
        index = self._ObjC_modules.selectionModel().currentIndex().row()
        _module = self._ObjCmodule_model.item(index, 0)
        if _module is None:
            return

        self.update_classes(_module.text())

    def _enumerate_objc_modules(self):
        """ DwarfApiCall enumerateObjCModules
        """
        return self._app_window.dwarf.dwarf_api('enumerateObjCModules')

    def _on_module_contextmenu(self, pos):
        """ Module ContextMenu
        """
        index = self._ObjC_modules.indexAt(pos).row()
        glbl_pt = self._ObjC_modules.mapToGlobal(pos)
        context_menu = QMenu(self)

        context_menu.addAction('Refresh', self._enumerate_objc_modules)
        context_menu.exec_(glbl_pt)

    def _on_enumerate_objc_modules(self, modules):
        """ Fills the ModulesList with data
        """
        if self._ObjC_modules is None:
            return

        self._ObjC_modules.clear()
        for module in modules:
            self.add_module(module)

    def add_module(self, module):
        _module_name = QStandardItem()
        _module_name.setText(module)
        self._ObjCmodule_model.appendRow(_module_name)
Exemplo n.º 7
0
class ElfInfo(DwarfDialog):
    onShowMemoryRequest = pyqtSignal(str, name='onShowMemoryRequest')

    def __init__(self, parent=None, file_path=None):
        super().__init__(parent=parent)
        h_box = QHBoxLayout(self)
        self._ident_titles = [
            'EI_MAG0', 'EI_MAG1', 'EI_MAG2', 'EI_MAG3', 'EI_CLASS', 'EI_DATA',
            'EI_VERSION', 'EI_OSABI', 'EI_ABIVERSION', 'EI_PAD'
        ]
        self.elf_info = DwarfListView(self)
        self.elf_info.setRootIsDecorated(True)
        self.elf_info.setExpandsOnDoubleClick(True)
        self.elf_info.setContextMenuPolicy(Qt.CustomContextMenu)
        self.elf_info.customContextMenuRequested.connect(self._on_context_menu)
        h_box.addWidget(self.elf_info)
        self._elf_info_mdl = QStandardItemModel(0, 2)
        self._elf_info_mdl.setHeaderData(0, Qt.Horizontal, 'Name')
        self._elf_info_mdl.setHeaderData(1, Qt.Horizontal, 'Value')
        self.elf_info.setModel(self._elf_info_mdl)
        self.elf_info.doubleClicked.connect(self._on_dblclicked)
        self.title = "ELF Info"
        self._elf_file_path = None
        self.setLayout(h_box)
        if file_path:
            self.set_elf_file(file_path)

    def set_elf_file(self, file_path):
        self._elf_file_path = file_path
        self.title = 'ELF Info for ' + file_path
        self._elf_info_mdl.insertRow(
            0, [QStandardItem('File'),
                QStandardItem(file_path)])

    def set_parsed_data(self, parsed_data):
        parent_item = self._elf_info_mdl.item(0)
        if 'endian' in parsed_data:
            parent_item.appendRow([
                QStandardItem('Endian'),
                QStandardItem(parsed_data['endian'])
            ])
        if 'is64bit' in parsed_data:
            txt = 'No'
            if parsed_data['is64bit']:
                txt = 'Yes'
            parent_item.appendRow([QStandardItem('64Bit'), QStandardItem(txt)])

        if 'header' in parsed_data:
            elf_header = QStandardItem('ELF Header')
            parent_item.appendRow([elf_header])
            for d in parsed_data['header']:
                if d == 'e_ident':
                    ident_item = QStandardItem(d)
                    elf_header.appendRow([
                        ident_item,
                        QStandardItem(str(parsed_data['header'][d]))
                    ])
                    a = 0
                    for i in parsed_data['header'][d]:
                        if a >= len(self._ident_titles):
                            a = len(self._ident_titles) - 1
                        ident_item.appendRow([
                            QStandardItem(self._ident_titles[a]),
                            QStandardItem(str(i))
                        ])
                        a += 1
                else:
                    elf_header.appendRow([
                        QStandardItem(d),
                        QStandardItem(hex(parsed_data['header'][d]))
                    ])

        if 'programheaders' in parsed_data:
            prog_headers_item = QStandardItem('Program Headers')
            parent_item.appendRow([prog_headers_item])
            i = 1
            for header in parsed_data['programheaders']:
                header_item = QStandardItem("%d" % i)
                prog_headers_item.appendRow([header_item])
                i += 1
                for d in header:
                    header_item.appendRow(
                        [QStandardItem(d),
                         QStandardItem(hex(header[d]))])

        if 'sectionheaders' in parsed_data:
            sect_headers = QStandardItem('Section Headers')
            parent_item.appendRow([sect_headers])
            i = 1
            for header in parsed_data['sectionheaders']:
                txt = header['name']
                if not txt:
                    txt = 'NULL'
                header_item = QStandardItem(txt)
                sect_headers.appendRow([header_item])
                i += 1
                for d in header:
                    if d == 'name':
                        continue
                    elif d == 'data' and header[d]:
                        data_item = QStandardItem('Data')
                        header_item.appendRow(data_item)
                        base = self.parent().dwarf.dwarf_api(
                            'findModule', self._elf_file_path)['base']
                        for ptr in header[d]:
                            if int(ptr):
                                va = hex(int(base, 16) + int(ptr))
                                fo = hex(int(ptr))
                                if self.elf_info.uppercase_hex:
                                    va = va.upper().replace('0X', '0x')
                                    fo = fo.upper().replace('0X', '0x')
                                data_item.appendRow([
                                    QStandardItem(va),
                                    QStandardItem('FileOffset: ' + fo)
                                ])
                    else:
                        header_item.appendRow(
                            [QStandardItem(d),
                             QStandardItem(hex(header[d]))])

            self.elf_info.expandAll()
            self.elf_info.resizeColumnToContents(0)
            self.elf_info.resizeColumnToContents(1)
            self.setMinimumWidth(
                self.elf_info.columnWidth(0) + self.elf_info.columnWidth(1) +
                50)
            self.setMinimumHeight(400)
            self.elf_info.collapseAll()
            self.elf_info.expandToDepth(1)

    def _on_dblclicked(self, model_index):
        if model_index.column() == 0 and model_index.data():
            if model_index.data().startswith('0x'):
                self.onShowMemoryRequest.emit(model_index.data())

    def _on_context_menu(self, pos):
        model_index = self.elf_info.indexAt(pos)
        if model_index.column() == 0 and model_index.data():
            if model_index.data().startswith('0x'):
                context_menu = QMenu()
                context_menu.addAction(
                    'Add to Bookmarks',
                    lambda: self.parent().bookmarks_panel.insert_bookmark(
                        model_index.data(), 'init_array'))
                context_menu.addAction(
                    'Copy Address',
                    lambda: utils.copy_hex_to_clipboard(model_index.data()))
                # show contextmenu
                glbl_pt = self.elf_info.mapToGlobal(pos)
                context_menu.exec_(glbl_pt)
Exemplo n.º 8
0
class DataPanel(QSplitter):
    def __init__(self, app):
        super(DataPanel, self).__init__(app)

        self.app = app
        self.data = {}

        self.setOrientation(Qt.Horizontal)

        self._key_list_model = QStandardItemModel(0, 1)
        self.key_lists = DwarfListView(parent=self.app)
        self.key_lists.setHeaderHidden(True)
        self.key_lists.setModel(self._key_list_model)
        self.key_lists.selectionModel().selectionChanged.connect(
            self.item_selected)
        self.key_lists.setContextMenuPolicy(Qt.CustomContextMenu)
        self.key_lists.customContextMenuRequested.connect(
            self._on_context_menu)
        self.addWidget(self.key_lists)

        self.editor = QPlainTextEdit()
        self.addWidget(self.editor)

        self.hex_view = HexEditor(self.app)
        self.hex_view.have_context_menu = False
        self.hex_view.setVisible(False)
        self.addWidget(self.hex_view)
        #self.setStretchFactor(0, 8)
        self.setStretchFactor(1, 4)
        self.setStretchFactor(2, 4)

    def clear(self):
        self._key_list_model.clear()
        self.editor.setPlainText('')
        self.hex_view.clear_panel()

    def append_data(self, data_type, key, text_data):
        if key not in self.data:
            self._key_list_model.appendRow([QStandardItem(key)])
        self.data[key] = [data_type, text_data]

    def item_selected(self, item1, item2):
        item = self._key_list_model.itemFromIndex(item1.indexes()[0])
        if self.data[item.text()][0] == 'plain':
            self.hex_view.setVisible(False)
            self.editor.setVisible(True)
            self.editor.setPlainText(self.data[item.text()][1])
        else:
            data = self.data[item.text()][1]
            try:
                as_tx = data.decode('utf8')
                self.editor.setVisible(True)
                self.editor.setPlainText(as_tx)
            except:
                self.editor.setVisible(False)
            self.hex_view.setVisible(True)
            self.hex_view.bytes_per_line = 16
            self.hex_view.set_data(data)

    def _on_context_menu(self, pos):
        context_menu = QMenu(self)

        index = self.key_lists.indexAt(pos).row()
        if index != -1:
            context_menu.addAction('Clear', self.clear)
            global_pt = self.key_lists.mapToGlobal(pos)
            context_menu.exec(global_pt)
Exemplo n.º 9
0
class BreakpointsWidget(QWidget):
    """ BreakpointsWidget

        Signals:
            onBreakpointChanged(str) - ptr
            onBreakpointRemoved(str) - ptr
    """

    onBreakpointChanged = pyqtSignal(str, name='onBreakpointChanged')
    onBreakpointsRemoved = pyqtSignal(str, name='onBreakpointRemoved')

    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(BreakpointsWidget, self).__init__(parent=parent)

        self._app_window = parent

        if self._app_window.dwarf is None:
            print('BreakpointsWidget created before Dwarf exists')
            return

        # connect to dwarf
        self._app_window.dwarf.onApplyContext.connect(self._on_apply_context)
        self._app_window.dwarf.onAddJavaBreakpoint.connect(
            self._on_add_breakpoint)
        self._app_window.dwarf.onAddNativeBreakpoint.connect(
            self._on_add_breakpoint)
        self._app_window.dwarf.onAddModuleInitializationBreakpoint.connect(
            self._on_add_breakpoint)
        self._app_window.dwarf.onAddJavaClassInitializationBreakpoint.connect(
            self._on_add_breakpoint)
        self._app_window.dwarf.onHitModuleInitializationBreakpoint.connect(
            self._on_hit_module_initialization_breakpoint)
        self._app_window.dwarf.onHitJavaClassInitializationBreakpoint.connect(
            self._on_hit_java_class_initialization_breakpoint)
        self._app_window.dwarf.onDeleteBreakpoint.connect(
            self._on_breakpoint_deleted)

        self._breakpoints_list = DwarfListView()
        self._breakpoints_list.doubleClicked.connect(self._on_double_clicked)
        self._breakpoints_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._breakpoints_list.customContextMenuRequested.connect(
            self._on_context_menu)
        self._breakpoints_model = QStandardItemModel(0, 3)

        self._breakpoints_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self._breakpoints_model.setHeaderData(1, Qt.Horizontal, 'T')
        self._breakpoints_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)
        self._breakpoints_model.setHeaderData(2, Qt.Horizontal, '<>')
        self._breakpoints_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)

        self._breakpoints_list.setModel(self._breakpoints_model)

        self._breakpoints_list.header().setStretchLastSection(False)
        self._breakpoints_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents | QHeaderView.Interactive)
        self._breakpoints_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        self._breakpoints_list.header().setSectionResizeMode(
            2, QHeaderView.ResizeToContents)

        v_box = QVBoxLayout(self)
        v_box.setContentsMargins(0, 0, 0, 0)
        v_box.addWidget(self._breakpoints_list)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(5, 2, 5, 5)
        self.btn1 = QPushButton(
            QIcon(utils.resource_path('assets/icons/plus.svg')), '')
        self.btn1.setFixedSize(20, 20)
        self.btn1.clicked.connect(self._on_add_item_clicked)
        btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')),
                           '')
        btn2.setFixedSize(20, 20)
        btn2.clicked.connect(self.delete_items)
        btn3 = QPushButton(
            QIcon(utils.resource_path('assets/icons/trashcan.svg')), '')
        btn3.setFixedSize(20, 20)
        btn3.clicked.connect(self.clear_list)
        h_box.addWidget(self.btn1)
        h_box.addWidget(btn2)
        h_box.addSpacerItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred))
        h_box.addWidget(btn3)
        # header.setLayout(h_box)
        # header.setFixedHeight(25)
        # v_box.addWidget(header)
        v_box.addLayout(h_box)
        self.setLayout(v_box)

        self._bold_font = QFont(self._breakpoints_list.font())
        self._bold_font.setBold(True)

        shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_N),
                                       self._app_window,
                                       self._on_add_native_breakpoint)
        shortcut_addnative.setAutoRepeat(False)

        shortcut_addjava = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_J),
                                     self._app_window,
                                     self._on_add_java_breakpoint)
        shortcut_addjava.setAutoRepeat(False)

        shortcut_add_native_onload = QShortcut(
            QKeySequence(Qt.CTRL + Qt.Key_O), self._app_window,
            self._on_add_module_initialization_breakpoint)
        shortcut_add_native_onload.setAutoRepeat(False)

        # new menu
        self.new_menu = QMenu('New')
        self.new_menu.addAction('Native', self._on_add_native_breakpoint)
        self.new_menu.addAction('Module initialization',
                                self._on_add_module_initialization_breakpoint)

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def delete_items(self):
        """ Delete selected Items
        """
        index = self._breakpoints_list.selectionModel().currentIndex().row()
        if index != -1:
            self._on_delete_breakpoint(index)
            self._breakpoints_model.removeRow(index)

    def clear_list(self):
        """ Clear the List
        """
        # go through all items and tell it gets removed
        for item in range(self._breakpoints_model.rowCount()):
            self._on_delete_breakpoint(item)

        if self._breakpoints_model.rowCount() > 0:
            # something was wrong it should be empty
            self._breakpoints_model.removeRows(
                0, self._breakpoints_model.rowCount())

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _on_apply_context(self, context):
        if context['reason'] == -1:
            if self._app_window.dwarf.java_available:
                self.new_menu.addAction('Java', self._on_add_java_breakpoint)
                self.new_menu.addAction(
                    'Java class initialization',
                    self._on_add_java_class_initialization_breakpoint)

    def _on_add_breakpoint(self, breakpoint):
        type_ = QStandardItem()
        type_.setFont(self._bold_font)
        type_.setTextAlignment(Qt.AlignCenter)
        if breakpoint.breakpoint_type == BREAKPOINT_NATIVE:
            type_.setText('N')
            type_.setToolTip('Native breakpoint')
        elif breakpoint.breakpoint_type == BREAKPOINT_JAVA:
            type_.setText('J')
            type_.setToolTip('Java breakpoint')
        elif breakpoint.breakpoint_type == BREAKPOINT_INITIALIZATION:
            type_.setText('C')
            type_.setToolTip('Initialization breakpoint')
        else:
            type_.setText('U')
            type_.setToolTip('Unknown Type')

        addr = QStandardItem()

        if breakpoint.breakpoint_type == BREAKPOINT_JAVA:
            addr.setText(breakpoint.get_target())
        elif breakpoint.breakpoint_type == BREAKPOINT_INITIALIZATION:
            addr.setText(breakpoint.get_target())
            addr.setData(breakpoint.debug_symbol, Qt.UserRole + 2)
        else:
            str_fmt = '0x{0:x}'
            if self._breakpoints_list.uppercase_hex:
                str_fmt = '0x{0:X}'
            # addr.setTextAlignment(Qt.AlignCenter)
            addr.setText(str_fmt.format(breakpoint.get_target()))

        condition = QStandardItem()
        condition.setTextAlignment(Qt.AlignCenter)
        condition.setFont(self._bold_font)
        if breakpoint.condition and breakpoint.condition != 'null' and breakpoint.condition != 'undefined':
            condition.setText('ƒ')
            condition.setToolTip(breakpoint.condition)
            condition.setData(breakpoint.condition, Qt.UserRole + 2)

        self._breakpoints_model.appendRow([addr, type_, condition])
        self._breakpoints_list.resizeColumnToContents(0)

    def _on_hit_module_initialization_breakpoint(self, data):
        items = self._breakpoints_model.findItems(data[1]['module'],
                                                  Qt.MatchExactly, 2)
        if len(items) > 0:
            self._breakpoints_model.item(items[0].row(),
                                         0).setText(data[1]['moduleBase'])

    def _on_hit_java_class_initialization_breakpoint(self, data):
        items = self._breakpoints_model.findItems(data[0], Qt.MatchExactly, 2)
        if len(items) > 0:
            pass

    def _on_double_clicked(self, model_index):
        item = self._breakpoints_model.itemFromIndex(model_index)
        if model_index.column() == 2 and item.text() == 'ƒ':
            self._on_modify_condition(model_index.row())
        else:
            self._app_window.jump_to_address(self._breakpoints_model.item(
                model_index.row(), 0).text(),
                                             view=1)

    def _on_context_menu(self, pos):
        context_menu = QMenu(self)
        context_menu.addMenu(self.new_menu)

        context_menu.addSeparator()
        index = self._breakpoints_list.indexAt(pos).row()
        if index != -1:
            context_menu.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self._breakpoints_model.item(index, 0).text()))
            context_menu.addAction(
                'Jump to address', lambda: self._app_window.jump_to_address(
                    self._breakpoints_model.item(index, 0).text()))
            context_menu.addSeparator()
            context_menu.addAction('Edit Condition',
                                   lambda: self._on_modify_condition(index))
            context_menu.addSeparator()
            context_menu.addAction('Delete Breakpoint',
                                   lambda: self._on_delete_breakpoint(index))

            if self._breakpoints_list.search_enabled:
                context_menu.addSeparator()
                context_menu.addAction('Search',
                                       self._breakpoints_list._on_cm_search)

        # show context menu
        global_pt = self._breakpoints_list.mapToGlobal(pos)
        context_menu.exec(global_pt)

    def _on_modify_condition(self, num_row):
        item = self._breakpoints_model.item(num_row, 2)
        data = item.data(Qt.UserRole + 2)
        if data is None:
            data = ''
        ptr = self._breakpoints_model.item(num_row, 0).text()
        accept, input_ = InputMultilineDialog().input(
            'Condition for breakpoint %s' % ptr, input_content=data)
        if accept:
            what = utils.parse_ptr(ptr)
            if what == 0:
                what = self._breakpoints_model.item(num_row,
                                                    2).data(Qt.UserRole + 2)
            if self._app_window.dwarf.dwarf_api(
                    'setBreakpointCondition',
                [what, input_.replace('\n', '')]):
                item.setData(input_, Qt.UserRole + 2)
                if not item.text():
                    item.setText('ƒ')
                item.setToolTip(input_)
                self.onBreakpointChanged.emit(ptr)

    # + button
    def _on_add_item_clicked(self):
        self.new_menu.exec_(QCursor.pos())

    # shortcuts/menu
    def _on_add_native_breakpoint(self):
        self._app_window.dwarf.breakpoint_native()

    def _on_add_java_breakpoint(self):
        self._app_window.dwarf.breakpoint_java()

    def _on_add_module_initialization_breakpoint(self):
        self._app_window.dwarf.breakpoint_module_initialization()

    def _on_add_java_class_initialization_breakpoint(self):
        self._app_window.dwarf.breakpoint_java_class_initialization()

    def _on_delete_breakpoint(self, num_row):
        breakpoint_type = self._breakpoints_model.item(num_row, 1).text()
        if breakpoint_type == 'N':
            ptr = self._breakpoints_model.item(num_row, 0).text()
            ptr = utils.parse_ptr(ptr)
            self._app_window.dwarf.dwarf_api('removeBreakpoint', ptr)
            self.onBreakpointRemoved.emit(str(ptr))
        elif breakpoint_type == 'J':
            target = self._breakpoints_model.item(num_row, 0).text()
            self._app_window.dwarf.dwarf_api('removeBreakpoint', target)
        elif breakpoint_type == 'C':
            item = self._breakpoints_model.item(num_row, 0)
            target = item.text()
            is_native = item.data(Qt.UserRole + 2) is None
            if is_native:
                self._app_window.dwarf.dwarf_api(
                    'removeModuleInitializationBreakpoint', target)
            else:
                self._app_window.dwarf.dwarf_api(
                    'removeJavaClassInitializationBreakpoint', target)
        elif breakpoint_type == 'U':
            ptr = self._breakpoints_model.item(num_row, 0).text()
            ptr = utils.parse_ptr(ptr)
            self._app_window.dwarf.dwarf_api('removeBreakpoint', ptr)
            self.onBreakpointRemoved.emit(str(ptr))

    def _on_breakpoint_deleted(self, parts):
        _msg, _type, _val = parts

        additional = None

        if _type == 'java' or _type == 'java_class_initialization':
            str_frmt = _val
            item_index = 0
        elif _type == 'module_initialization':
            str_frmt = _val
            item_index = 0
        else:
            _ptr = utils.parse_ptr(_val)
            if self._breakpoints_list._uppercase_hex:
                str_frmt = '0x{0:X}'.format(_ptr)
            else:
                str_frmt = '0x{0:x}'.format(_ptr)
            item_index = 0

        for _item in range(self._breakpoints_model.rowCount()):
            item = self._breakpoints_model.item(_item, item_index)

            if item is None:
                continue

            if str_frmt == item.text():
                if additional is not None:
                    if additional == self._breakpoints_model.item(_item,
                                                                  2).text():
                        self._breakpoints_model.removeRow(_item)
                else:
                    self._breakpoints_model.removeRow(_item)
Exemplo n.º 10
0
class Explorer(QWidget):

    def __init__(self, plugin):
        super().__init__()

        self.plugin = plugin
        self.adb = plugin.adb

        self.current_path = ''

        self.path_input = QComboBox(self)
        self.path_input.activated[str].connect(self.set_path)

        self.explorer = DwarfListView()
        self.explorer_model = QStandardItemModel(0, 6)
        self.explorer_model.setHeaderData(0, Qt.Horizontal, 'permissions')
        self.explorer_model.setHeaderData(1, Qt.Horizontal, 'uid')
        self.explorer_model.setHeaderData(2, Qt.Horizontal, 'gid')
        self.explorer_model.setHeaderData(3, Qt.Horizontal, 'size')
        self.explorer_model.setHeaderData(4, Qt.Horizontal, 'modified')
        self.explorer_model.setHeaderData(5, Qt.Horizontal, 'name')
        self.explorer.setModel(self.explorer_model)
        self.explorer.doubleClicked.connect(self._item_double_clicked)
        self.explorer.setContextMenuPolicy(Qt.CustomContextMenu)
        self.explorer.customContextMenuRequested.connect(self._on_context_menu)

        box = QVBoxLayout()
        box.addWidget(self.path_input)
        box.addWidget(self.explorer)
        self.setLayout(box)

    def set_path(self, path):
        if self.current_path == path:
            return

        self.current_path = path

        ls = self.adb.su_cmd('ls -al %s' % path)
        if ls and ls[:2] != 'ls':
            ls = ls.split('\n')[1:]

            self.path_input.clear()
            self.explorer_model.setRowCount(0)

            split = path.split('/')
            self.path_input.addItem(path)
            for i in range(1, len(split)):
                path = '/'.join(split[:-i])
                if path:
                    self.path_input.addItem(path)

            for line in ls:
                if line:
                    parts = line.split()
                    self.explorer_model.appendRow([
                        QStandardItem(parts[0]),
                        QStandardItem(parts[2]),
                        QStandardItem(parts[3]),
                        QStandardItem(parts[4]),
                        QStandardItem('%s %s' % (parts[5], parts[6])),
                        QStandardItem(parts[7]),
                    ])

    def _item_double_clicked(self, model_index):
        row = self.explorer_model.itemFromIndex(model_index).row()
        if row != -1:
            name = self.explorer_model.item(row, 5).text()
            if name == '..':
                path = '/'.join(self.current_path.split('/')[:-1])
            else:
                path = self.current_path + '/' + name

            ret = self.adb.su_cmd('cd %s' % path)
            if not ret:
                self.set_path(path)

    def _on_context_menu(self, pos):
        row = self.explorer.indexAt(pos).row()
        glbl_pt = self.explorer.mapToGlobal(pos)
        if row != -1:
            context_menu = QMenu(self)
            name = self.explorer_model.item(row, 5).text()
            context_menu.addAction('pull file', lambda: self._on_pull_file(name))
            context_menu.exec_(glbl_pt)

    def _on_pull_file(self, name):
        from PyQt5.QtWidgets import QFileDialog
        _file = QFileDialog.getSaveFileName(self.plugin.app, '', name)
        if _file[0]:
            path = self.current_path + '/' + name if self.current_path != '/' else self.current_path + name
            dwarf_temp = '/sdcard/.dwarf_temp'
            self.adb.su_cmd('cp -r %s %s' % (path, dwarf_temp))
            self.adb.pull(dwarf_temp, _file[0])
            self.adb.su_cmd('rm -rf %s' % dwarf_temp)