Exemplo n.º 1
0
class JavaExplorerPanel(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent=parent)

        self._app_window = parent

        self._handle_history = []

        self._setup_ui()
        self._setup_models()

    def _setup_ui(self):
        self.setContentsMargins(0, 0, 0, 0)

        top_font = QFont()
        top_font.setBold(True)
        top_font.setPixelSize(19)

        # main wrapper
        main_wrapper = QVBoxLayout()
        main_wrapper.setContentsMargins(1, 1, 1, 1)

        # wrapwdgt
        wrap_wdgt = QWidget()
        self._top_class_name = QLabel(wrap_wdgt)
        self._top_class_name.setContentsMargins(10, 10, 10, 10)
        self._top_class_name.setAttribute(Qt.WA_TranslucentBackground,
                                          True)  # keep this
        self._top_class_name.setFont(top_font)
        self._top_class_name.setStyleSheet('color: #ef5350;')
        wrap_wdgt.setMaximumHeight(self._top_class_name.height() + 20)

        main_wrapper.addWidget(wrap_wdgt)

        # left list
        left_wrap_wdgt = QWidget()

        left_v_box = QVBoxLayout(left_wrap_wdgt)
        left_v_box.setContentsMargins(0, 0, 0, 0)

        methods_label = QLabel('METHODS')
        font = methods_label.font()
        font.setBold(True)
        methods_label.setFont(font)
        methods_label.setContentsMargins(10, 0, 10, 2)
        methods_label.setAttribute(Qt.WA_TranslucentBackground,
                                   True)  # keep this
        left_v_box.addWidget(methods_label)

        self._methods_list = DwarfListView()
        left_v_box.addWidget(self._methods_list)

        # center list
        center_wrap_wdgt = QWidget()

        center_v_box = QVBoxLayout(center_wrap_wdgt)
        center_v_box.setContentsMargins(0, 0, 0, 0)

        methods_label = QLabel('NATIVE FIELDS')
        methods_label.setFont(font)
        methods_label.setContentsMargins(10, 0, 10, 2)
        methods_label.setAttribute(Qt.WA_TranslucentBackground,
                                   True)  # keep this
        center_v_box.addWidget(methods_label)

        self._native_fields_list = DwarfListView()
        self._native_fields_list.doubleClicked.connect(
            self._on_native_field_dblclicked)
        center_v_box.addWidget(self._native_fields_list)

        # right list
        right_wrap_wdgt = QWidget()

        right_v_box = QVBoxLayout(right_wrap_wdgt)
        right_v_box.setContentsMargins(0, 0, 0, 0)

        methods_label = QLabel('FIELDS')
        methods_label.setFont(font)
        methods_label.setContentsMargins(10, 0, 10, 2)
        methods_label.setAttribute(Qt.WA_TranslucentBackground,
                                   True)  # keep this
        right_v_box.addWidget(methods_label)

        self._fields_list = DwarfListView()
        self._fields_list.doubleClicked.connect(self._on_field_dblclicked)
        right_v_box.addWidget(self._fields_list)

        # main splitter
        main_splitter = QSplitter(Qt.Horizontal)
        main_splitter.setContentsMargins(0, 0, 0, 0)
        main_splitter.addWidget(left_wrap_wdgt)
        main_splitter.addWidget(center_wrap_wdgt)
        main_splitter.addWidget(right_wrap_wdgt)
        main_splitter.setSizes([250, 100, 100])

        main_wrapper.addWidget(main_splitter)
        main_wrapper.setSpacing(0)
        self.setLayout(main_wrapper)

    def _setup_models(self):
        # left list
        self._methods_model = QStandardItemModel(0, 3)
        self._methods_model.setHeaderData(0, Qt.Horizontal, 'Name')
        self._methods_model.setHeaderData(1, Qt.Horizontal, 'Return')
        self._methods_model.setHeaderData(2, Qt.Horizontal, 'Arguments')
        self._methods_list.setModel(self._methods_model)
        self._methods_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self._methods_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)
        # center list
        self._native_fields_model = QStandardItemModel(0, 2)
        self._native_fields_model.setHeaderData(0, Qt.Horizontal, 'Name')
        self._native_fields_model.setHeaderData(1, Qt.Horizontal, 'Value')
        self._native_fields_list.setModel(self._native_fields_model)
        self._native_fields_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        # right list
        self._fields_model = QStandardItemModel(0, 2)
        self._fields_model.setHeaderData(0, Qt.Horizontal, 'Name')
        self._fields_model.setHeaderData(1, Qt.Horizontal, 'Class')
        self._fields_list.setModel(self._fields_model)
        self._fields_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def _set_data(self, data):
        if 'class' not in data:
            return

        self._top_class_name.setText(data['class'])
        data = data['data']

        self._methods_list.clear()
        self._native_fields_list.clear()
        self._fields_list.clear()

        for key in data:
            ref = data[key]
            if ref['type'] == 'function':
                if not key.startswith('$'):
                    self._add_method(key, ref)
            elif ref['type'] == 'object':
                if ref['handle'] is not None:
                    if not key.startswith('$'):
                        self._add_field(key, ref['value'], ref['handle'],
                                        ref['handle_class'])
            else:
                if not key.startswith('$'):
                    self._add_field(key, ref['value'], is_native=True)

        self._methods_list.sortByColumn(0, 0)
        self._native_fields_list.sortByColumn(0, 0)
        self._fields_list.sortByColumn(0, 0)

    def _add_method(self, name, ref):
        ref_overloads = ref['overloads']
        for _, ref_overload in enumerate(ref_overloads):
            args = []

            if 'args' in ref_overload:
                for arg in ref_overload['args']:
                    if 'className' in arg:
                        args.append(arg['className'])

            self._methods_model.appendRow([
                QStandardItem(name),
                QStandardItem(ref_overload['return']['className']),
                QStandardItem('(%s)' % ', '.join(args)),
            ])

    def _add_field(self,
                   name,
                   value,
                   handle=None,
                   handle_class=None,
                   is_native=False):
        if handle:
            handle = {'handle': handle, 'handle_class': handle_class}
            handle_item = QStandardItem(name)
            handle_item.setData(handle, Qt.UserRole + 1)
        else:
            handle_item = QStandardItem(name)

        if not is_native:
            self._fields_model.appendRow(
                [handle_item, QStandardItem(str(value))])
        else:
            self._native_fields_model.appendRow(
                [handle_item, QStandardItem(str(value))])

    def _set_handle(self, handle):
        data = self._app_window.dwarf.dwarf_api('javaExplorer', handle)
        if not data:
            return
        self._handle_history.append({'handle': handle})
        self._set_data(data)

    def _set_handle_arg(self, arg):
        data = self._app_window.dwarf.dwarf_api('javaExplorer', arg)
        if not data:
            return
        self._handle_history.append({'handle': arg})
        self._set_data(data)

    def clear_panel(self):
        self._top_class_name.setText('')
        self._handle_history = []
        self._methods_list.clear()
        self._native_fields_list.clear()
        self._fields_list.clear()

    def _back(self):
        if len(self._handle_history) < 2:
            return
        self._handle_history.pop()
        data = self._handle_history.pop(len(self._handle_history) -
                                        1)['handle']
        if isinstance(data, int):
            self._set_handle_arg(data)
        else:
            self._set_handle(data)

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _on_field_dblclicked(self, _):
        field_row = self._fields_list.selectionModel().currentIndex().row()
        if field_row >= 0:
            field_handle = self._fields_model.item(field_row,
                                                   0).data(Qt.UserRole + 1)
            if field_handle:
                self._set_handle(field_handle)

    def _on_native_field_dblclicked(self, _):
        field_row = self._native_fields_list.selectionModel().currentIndex(
        ).row()
        if field_row:
            field_handle = self._native_fields_model.item(
                field_row, 0).data(Qt.UserRole + 1)
            if field_handle:
                self._set_handle(field_handle)

    def keyPressEvent(self, event):  # pylint: disable=invalid-name
        if event.key() == Qt.Key_Backspace:
            self._back()

        return super().keyPressEvent(event)
Exemplo n.º 2
0
class ContextPanel(QTabWidget):
    # consts
    CONTEXT_TYPE_NATIVE = 0
    CONTEXT_TYPE_JAVA = 1
    CONTEXT_TYPE_EMULATOR = 2

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

    def __init__(self, parent=None):
        super(ContextPanel, 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(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)
        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._emulatorctx_model = QStandardItemModel(0, 3)
        self._emulatorctx_model.setHeaderData(0, Qt.Horizontal, 'Reg')
        self._emulatorctx_model.setHeaderData(0, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)
        self._emulatorctx_model.setHeaderData(1, Qt.Horizontal, 'Value')
        self._emulatorctx_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)
        self._emulatorctx_model.setHeaderData(2, Qt.Horizontal, 'Decimal')

        self._emulatorctx_list = DwarfListView()
        self._emulatorctx_list.setModel(self._emulatorctx_model)

        self._emulatorctx_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self._emulatorctx_list.header().setSectionResizeMode(
            1, QHeaderView.ResizeToContents)

        self._emulatorctx_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._emulatorctx_list.customContextMenuRequested.connect(
            self._on_emulator_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(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)
        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._emulatorctx_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 == ContextPanel.CONTEXT_TYPE_NATIVE:
            self._nativectx_list.clear()
            self._set_native_context(ptr, context)
        elif context_type == ContextPanel.CONTEXT_TYPE_JAVA:
            self._javactx_list.clear()
            self._set_java_context(ptr, context)
        elif context_type == ContextPanel.CONTEXT_TYPE_EMULATOR:
            self._emulatorctx_list.clear()
            self._set_emulator_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 == 'emulator':
            index = self.indexOf(self._emulatorctx_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)

            value_x = QStandardItem()
            if context[register]['isValidPointer']:
                value_x.setForeground(Qt.red)
            # value_x.setTextAlignment(Qt.AlignCenter)
            value_dec = QStandardItem()
            # value_dec.setTextAlignment(Qt.AlignCenter)
            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_emulator_context(self, ptr, context):
        if self.indexOf(self._emulatorctx_list) == -1:
            self.addTab(self._emulatorctx_list, 'Emulator')
            self.show_context_tab('Emulator')
        else:
            self.show_context_tab('Emulator')

        context_ptr = ptr
        context = context.__dict__

        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)):
            if register.startswith('_') or register not in sorted_regs:
                continue

            reg_name = QStandardItem()
            reg_name.setTextAlignment(Qt.AlignCenter)
            reg_name.setForeground(QColor('#39c'))
            value_x = QStandardItem()
            # value_x.setTextAlignment(Qt.AlignCenter)
            value_dec = QStandardItem()
            # value_dec.setTextAlignment(Qt.AlignCenter)

            reg_name.setText(register)
            reg_name.setData(context_ptr)

            if context[register] is not None:
                if isinstance(context[register], int):
                    str_fmt = '0x{0:x}'
                    if self._emulatorctx_list.uppercase_hex:
                        str_fmt = '0x{0:X}'
                    value_x.setText(str_fmt.format(context[register]))
                    value_dec.setText('{0:d}'.format(context[register]))

            self._emulatorctx_model.appendRow([reg_name, value_x, value_dec])
            self._emulatorctx_list.resizeColumnToContents(0)
            self._emulatorctx_list.resizeColumnToContents(1)

    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:
            # show contextmenu
            context_menu.addAction(
                'Copy value', lambda: utils.copy_hex_to_clipboard(
                    self._nativectx_model.item(index, 1).text()))
            context_menu.addAction(
                'Jump to address', lambda: self._app_window.jump_to_address(
                    self._nativectx_model.item(index, 1).text()))
            context_menu.exec_(glbl_pt)

    def _on_emulator_contextmenu(self, pos):
        index = self._emulatorctx_list.indexAt(pos).row()
        glbl_pt = self._emulatorctx_list.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            # show contextmenu
            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
            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.º 3
0
class SpawnsList(QWidget):
    """ ProcessListWidget wich shows running Processes on Device
        Includes a Refresh Button to manually start refreshthread

        args:
            device needed

        Signals:
            onProcessSelected([pid, name]) - pid(int) name(str)
            onRefreshError(str)
    """

    onProcessSelected = pyqtSignal(list, name='onProcessSelected')
    onRefreshError = pyqtSignal(str, name='onRefreshError')

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

        # if not isinstance(device, frida.core.Device):
        #    print('No FridaDevice')
        #    return

        self._device = device

        self.spawn_list = DwarfListView()

        model = QStandardItemModel(0, 2, parent)
        model.setHeaderData(0, Qt.Horizontal, "Name")
        model.setHeaderData(1, Qt.Horizontal, "Package")

        self.spawn_list.doubleClicked.connect(self._on_item_clicked)

        v_box = QVBoxLayout()
        v_box.setContentsMargins(0, 0, 0, 0)
        v_box.addWidget(self.spawn_list)
        self.refresh_button = QPushButton('Refresh')
        self.refresh_button.clicked.connect(self._on_refresh_procs)
        self.refresh_button.setEnabled(False)
        v_box.addWidget(self.refresh_button)
        self.setLayout(v_box)

        self.spawn_list.setModel(model)
        self.spawn_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)

        self.spaw_update_thread = SpawnsThread(self, self._device)
        self.spaw_update_thread.add_spawn.connect(self._on_add_proc)
        self.spaw_update_thread.is_error.connect(self._on_error)
        self.spaw_update_thread.is_finished.connect(self._on_refresh_finished)
        self.spaw_update_thread.device = self._device
        self.spaw_update_thread.start()

    # ************************************************************************
    # **************************** Properties ********************************
    # ************************************************************************
    @property
    def device(self):
        """ Sets Device needs frida.core.device
        """
        return self._device

    @device.setter
    def device(self, value):
        self.set_device(value)

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def clear(self):
        """ Clears the List
        """
        self.spawn_list.clear()

    def set_device(self, device):
        """ Set frida Device
        """
        if isinstance(device, frida.core.Device):
            self._device = device
            self.spaw_update_thread.device = device
            self._on_refresh_procs()

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _on_item_clicked(self, model_index):
        model = self.spawn_list.model()

        index = model.itemFromIndex(model_index).row()

        if index != -1:
            sel_pid = self.spawn_list.get_item_text(index, 0)
            if model_index.column() == 0:
                sel_name = model.data(model_index, Qt.UserRole + 2)
            else:
                sel_name = self.spawn_list.get_item_text(index, 1)

            self.onProcessSelected.emit([sel_pid, sel_name])

    def _on_add_proc(self, item):
        model = self.spawn_list.model()
        name = QStandardItem()
        name.setText(item[0])
        name.setData(item[1], Qt.UserRole + 2)
        package = QStandardItem()
        package.setText(item[1])
        model.appendRow([name, package])

    def _on_error(self, error_str):
        self.onRefreshError.emit(error_str)

    def _on_refresh_procs(self):
        if not self._device:
            return

        if self.spaw_update_thread.isRunning():
            self.spaw_update_thread.terminate()

        if not self.spaw_update_thread.isRunning():
            self.clear()
            self.refresh_button.setEnabled(False)
            self.spaw_update_thread.device = self._device
            self.spaw_update_thread.start()

    def _on_refresh_finished(self):
        self.spawn_list.resizeColumnToContents(0)
        self.refresh_button.setEnabled(True)
Exemplo n.º 4
0
class JavaInspector(QWidget):
    """ Java Class/Methods Lists
    """
    def __init__(self, parent=None):
        super(JavaInspector, self).__init__(parent)

        self._app_window = parent

        self._app_window.dwarf.onEnumerateJavaMethodsComplete.connect(
            self._on_method_enumeration_complete)
        self._app_window.dwarf.onEnumerateJavaClassesStart.connect(
            self._on_class_enumeration_start)
        self._app_window.dwarf.onEnumerateJavaClassesMatch.connect(
            self._on_class_enumeration_match)
        self._app_window.dwarf.onEnumerateJavaClassesComplete.connect(
            self._on_class_enumeration_complete)

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

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

        h_box = QHBoxLayout()
        h_box.setContentsMargins(0, 0, 0, 0)
        h_box.addWidget(self._java_classes)
        h_box.addWidget(self._java_methods)
        self.setLayout(h_box)

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

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

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _class_clicked(self):
        index = self._java_classes.selectionModel().currentIndex().row()
        _class = self._javaclass_model.item(index, 0)
        if _class is None:
            return

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

    def _on_class_enumeration_start(self):
        self._java_classes.clear()

    def _on_class_enumeration_match(self, java_class):
        _class_name = QStandardItem()
        _class_name.setText(java_class)
        self._javaclass_model.appendRow(_class_name)

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

    def _on_method_enumeration_complete(self, data):
        self._java_methods.clear()
        _class, methods = data
        for method in methods:
            _method_name = QStandardItem()
            _method_name.setText(method)
            self._javamethod_model.appendRow(_method_name)

    def _class_dblclicked(self):
        """ Class DoubleClicked
        """
        index = self._java_classes.selectionModel().currentIndex().row()
        if index:
            class_item = self._javaclass_model.item(index, 0)
            if class_item:
                class_name = class_item.text()
                if class_name:
                    self._hook_class(class_name)

    def _method_dblclicked(self):
        """ Function DoubleClicked
        """
        class_index = self._java_classes.selectionModel().currentIndex().row()
        method_index = self._java_methods.selectionModel().currentIndex().row()
        if class_index and method_index:
            class_item = self._javaclass_model.item(class_index, 0)
            method_item = self._javamethod_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.hook_java(class_name + '.' +
                                                     method_name)

    def _hook_class(self, class_name):
        if class_name:
            self._app_window.dwarf.hook_java(class_name)

    def _hook_class_functions(self, class_name):
        if class_name:
            self._app_window.dwarf.dwarf_api('hookAllJavaMethods', class_name)

    def _on_class_contextmenu(self, pos):
        """ Modules ContextMenu
        """
        index = self._java_classes.indexAt(pos).row()
        glbl_pt = self._java_classes.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            context_menu.addAction(
                'Hook constructor', lambda: self._hook_class(
                    self._javaclass_model.item(index, 0).text()))
            context_menu.addAction(
                'Hook all methods', lambda: self._hook_class_functions(
                    self._javaclass_model.item(index, 0).text()))
            context_menu.addSeparator()

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

    def _hook_method(self, method_name):
        class_index = self._java_classes.selectionModel().currentIndex().row()
        if class_index:
            class_item = self._javaclass_model.item(class_index, 0)
            if class_item:
                class_name = class_item.text()
                if class_name and method_name:
                    self._app_window.dwarf.hook_java(class_name + '.' +
                                                     method_name)

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

        self.update_methods(_class.text())

    def _on_method_contextmenu(self, pos):
        """ Modules ContextMenu
        """
        index = self._java_methods.indexAt(pos).row()
        glbl_pt = self._java_methods.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            context_menu.addAction(
                'Hook method', lambda: self._hook_method(
                    self._javamethod_model.item(index, 0).text()))
            context_menu.addSeparator()

        context_menu.addAction('Refresh', self._cm_refresh_methods)
        context_menu.exec_(glbl_pt)
Exemplo n.º 5
0
class HooksPanel(QWidget):
    """ HooksPanel

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

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

    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(HooksPanel, 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_dblclicked)
        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_dblclicked(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.onShowMemoryRequest.emit(
                self._hooks_model.item(model_index.row(), 0).text())

    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.º 6
0
class SearchPanel(QWidget):
    """ SearchPanel
    """
    def __init__(self, parent=None, show_progress_dlg=False):
        super(SearchPanel, self).__init__(parent=parent)
        self._app_window = parent

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

        self._app_window.dwarf.onMemoryScanResult.connect(
            self._on_search_result)

        self._app_window.dwarf.onSetRanges.connect(self._on_setranges)

        self._ranges_model = None
        self._result_model = None

        self._blocking_search = show_progress_dlg
        self.progress = None
        self._pattern_length = 0

        self._search_results = []

        self.setContentsMargins(0, 0, 0, 0)

        main_wrap = QVBoxLayout()
        main_wrap.setContentsMargins(1, 1, 1, 1)

        wrapping_wdgt = QWidget()
        wrapping_wdgt.setContentsMargins(10, 10, 10, 10)
        v_box = QVBoxLayout(wrapping_wdgt)
        v_box.setContentsMargins(0, 0, 0, 0)
        self.input = QLineEdit()
        self.input.setPlaceholderText(
            'search for a sequence of bytes in hex format: deadbeef123456aabbccddeeff...'
        )
        v_box.addWidget(self.input)

        self.check_all_btn = QPushButton('check all')
        self.check_all_btn.clicked.connect(self._on_click_check_all)
        self.uncheck_all_btn = QPushButton('uncheck all')
        self.uncheck_all_btn.clicked.connect(self._on_click_uncheck_all)
        self.search_btn = QPushButton('search')
        self.search_btn.clicked.connect(self._on_click_search)

        h_box = QHBoxLayout()
        h_box.addWidget(self.check_all_btn)
        h_box.addWidget(self.uncheck_all_btn)
        h_box.addWidget(self.search_btn)
        v_box.addLayout(h_box)

        main_wrap.addWidget(wrapping_wdgt)

        self.ranges = DwarfListView(self)
        self.ranges.clicked.connect(self._on_show_results)
        self.results = DwarfListView(self)
        self.results.setVisible(False)

        h_box = QHBoxLayout()
        h_box.setContentsMargins(0, 0, 0, 0)
        h_box.addWidget(self.ranges)
        h_box.addWidget(self.results)
        main_wrap.addLayout(h_box)

        main_wrap.setSpacing(0)

        self.setLayout(main_wrap)

        self._setup_models()
        self._app_window.dwarf.dwarf_api('updateRanges')

    # ************************************************************************
    # **************************** Functions *********************************
    # ************************************************************************
    def _setup_models(self):
        self._ranges_model = QStandardItemModel(0, 7)

        # just replicate ranges panel model
        self._ranges_model.setHeaderData(
            0, Qt.Horizontal, 'x'
        )  # TODO: replace with checkbox in header - remove checkall btns
        self._ranges_model.setHeaderData(0, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self._ranges_model.setHeaderData(1, Qt.Horizontal, 'Address')
        self._ranges_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self._ranges_model.setHeaderData(2, Qt.Horizontal, 'Size')
        self._ranges_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self._ranges_model.setHeaderData(3, Qt.Horizontal, 'Protection')
        self._ranges_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self._ranges_model.setHeaderData(4, Qt.Horizontal, 'FileOffset')
        self._ranges_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self._ranges_model.setHeaderData(5, Qt.Horizontal, 'FileSize')
        self._ranges_model.setHeaderData(5, Qt.Horizontal, Qt.AlignCenter,
                                         Qt.TextAlignmentRole)
        self._ranges_model.setHeaderData(6, Qt.Horizontal, 'FilePath')

        self.ranges.setModel(self._ranges_model)
        self.ranges.header().setSectionResizeMode(0,
                                                  QHeaderView.ResizeToContents)
        self.ranges.header().setSectionResizeMode(1,
                                                  QHeaderView.ResizeToContents)
        self.ranges.header().setSectionResizeMode(2,
                                                  QHeaderView.ResizeToContents)
        self.ranges.header().setSectionResizeMode(3,
                                                  QHeaderView.ResizeToContents)
        self.ranges.header().setSectionResizeMode(4,
                                                  QHeaderView.ResizeToContents)
        self.ranges.header().setSectionResizeMode(5,
                                                  QHeaderView.ResizeToContents)

        self.ranges.doubleClicked.connect(self._on_range_dblclick)

        # setup results model
        self._result_model = QStandardItemModel(0, 1)
        self._result_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self.results.setModel(self._result_model)
        self.results.doubleClicked.connect(self._on_double_clicked)

    def _on_setranges(self, ranges):
        """ Fills Rangelist with Data
        """
        if self._ranges_model.rowCount():
            return

        self.ranges.header().setSectionResizeMode(0, QHeaderView.Fixed)
        if isinstance(ranges, list):
            self._ranges_model.removeRows(0, self._ranges_model.rowCount())
            for range_entry in ranges:
                if 'protection' in range_entry and isinstance(
                        range_entry['protection'], str):
                    if 'r' not in range_entry['protection']:
                        # skip not readable range
                        continue

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

                addr = QStandardItem()
                addr.setTextAlignment(Qt.AlignCenter)
                addr.setText(str_frmt.format(int(range_entry['base'], 16)))

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

                protection = QStandardItem()
                protection.setTextAlignment(Qt.AlignCenter)
                protection.setText(range_entry['protection'])

                file_path = None
                file_addr = None
                file_size = None

                if len(range_entry) > 3:
                    if range_entry['file']['path']:
                        file_path = QStandardItem()
                        file_path.setText(range_entry['file']['path'])

                    if range_entry['file']['offset']:
                        file_addr = QStandardItem()
                        file_addr.setTextAlignment(Qt.AlignCenter)
                        file_addr.setText(
                            str_frmt.format(range_entry['file']['offset']))

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

                checkbox = QStandardItem()
                checkbox.setCheckable(True)

                self._ranges_model.appendRow([
                    checkbox, addr, size, protection, file_addr, file_size,
                    file_path
                ])

    # ************************************************************************
    # **************************** Handlers **********************************
    # ************************************************************************
    def _on_range_dblclick(self, model_index):
        item = self._ranges_model.itemFromIndex(model_index)
        if item:
            if self._ranges_model.item(model_index.row(),
                                       0).checkState() != Qt.Checked:
                self._ranges_model.item(model_index.row(),
                                        0).setCheckState(Qt.Checked)
            else:
                self._ranges_model.item(model_index.row(),
                                        0).setCheckState(Qt.Unchecked)

    def _on_click_check_all(self):
        for i in range(self._ranges_model.rowCount()):
            self._ranges_model.item(i, 0).setCheckState(Qt.Checked)

    def _on_click_uncheck_all(self):
        for i in range(self._ranges_model.rowCount()):
            self._ranges_model.item(i, 0).setCheckState(Qt.Unchecked)

    def _on_double_clicked(self, model_index):
        item = self._result_model.itemFromIndex(model_index)
        if item:
            self._app_window.jump_to_address(
                self._result_model.item(model_index.row(), 0).text())

    def _on_click_search(self):
        pattern = self.input.text()
        if pattern == '':
            return 1

        # check if we already provide a hex string as input
        try:
            test = pattern.replace(' ', '')
            int(test, 16)
            pattern = test
        except ValueError:
            # search for string
            pattern = binascii.hexlify(pattern.encode('utf8')).decode('utf8')

        ranges = []
        self._search_results = []
        for i in range(self._ranges_model.rowCount()):
            item = self._ranges_model.item(i, 0)
            if item.checkState() == Qt.Checked:
                addr = self._ranges_model.item(i, 1)
                size = self._ranges_model.item(i, 2)
                ranges.append([addr.text(), size.text()])

        if len(ranges) == 0:
            return 1

        status_message = 'searching...'

        if self._blocking_search:
            self.progress = utils.progress_dialog(status_message)
            self.progress.forceShow()

        self._app_window.show_progress(status_message)
        self.input.setEnabled(False)
        self.search_btn.setEnabled(False)
        self.check_all_btn.setEnabled(False)
        self.uncheck_all_btn.setEnabled(False)

        self._pattern_length = len(pattern) * .5

        search_thread = SearchThread(self._app_window.dwarf, self)
        search_thread.onCmdCompleted.connect(self._on_search_complete)
        search_thread.onError.connect(self._on_search_error)
        search_thread.pattern = pattern
        search_thread.ranges = ranges
        search_thread.start()

    def _on_search_result(self, data):
        self._search_results.append(data)

    def _on_search_complete(self):
        self.input.setEnabled(True)
        self.search_btn.setEnabled(True)
        self.check_all_btn.setEnabled(True)
        self.uncheck_all_btn.setEnabled(True)
        self._app_window.hide_progress()
        if self._blocking_search:
            self.progress.cancel()

        self._ranges_model.removeColumns(4, 3)
        self._ranges_model.setHeaderData(3, Qt.Horizontal, 'Search Results')
        self._ranges_model.setHeaderData(3, Qt.Horizontal, None,
                                         Qt.TextAlignmentRole)

        results_count = 0
        is_selected = False
        for i in range(self._ranges_model.rowCount()):
            item = self._ranges_model.item(i, 0)
            if item.checkState() == Qt.Checked:
                item.setCheckState(Qt.Unchecked)
                if not is_selected:
                    is_selected = True
                    self.ranges.setCurrentIndex(self._ranges_model.index(i, 0))
            else:
                self._search_results.insert(i, None)
                self._ranges_model.item(i, 3).setText('')
                self._ranges_model.item(i, 3).setTextAlignment(Qt.AlignLeft)
                continue

            if len(self._search_results[i]):
                results_count += len(self._search_results[i])
                self._ranges_model.item(i, 3).setText('Matches: {0}'.format(
                    len(self._search_results[i])))
                self._ranges_model.item(i, 3).setTextAlignment(Qt.AlignLeft)
            else:
                self._ranges_model.item(i, 3).setText('')
                self._ranges_model.item(i, 3).setTextAlignment(Qt.AlignLeft)

        self._app_window.set_status_text(
            'Search complete: {0} matches'.format(results_count))
        if results_count:
            for i in self._search_results:
                if i and len(i):
                    self.results.setVisible(True)
                    for result in i:
                        self._result_model.appendRow(
                            QStandardItem(result['address']))

                    break

    def _on_search_error(self, msg):
        utils.show_message_box(msg)

    def _on_show_results(self):
        if self._search_results:
            self.results.clear()
            if self._app_window.memory_panel:
                self._app_window.memory_panel.remove_highlights('search')
            selected_index = self.ranges.selectionModel().currentIndex().row()
            if selected_index is not None:
                item_txt = self._ranges_model.item(selected_index, 3).text()
                if item_txt == '':
                    return

                for result in self._search_results[selected_index]:
                    self._result_model.appendRow(
                        QStandardItem(result['address']))

                    # TODO: fix hexview highlights performance
                    """
Exemplo n.º 7
0
class WatchersPanel(QWidget):
    """ WatcherPanel

        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(WatchersPanel, self).__init__(parent=parent)
        self._app_window = parent

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

        self._uppercase_hex = True
        self.setAutoFillBackground(True)

        # connect to dwarf
        self._app_window.dwarf.onWatcherAdded.connect(self._on_watcher_added)
        self._app_window.dwarf.onWatcherRemoved.connect(
            self._on_watcher_removed)

        # setup our model
        self._watchers_model = QStandardItemModel(0, 5)
        self._watchers_model.setHeaderData(0, Qt.Horizontal, 'Address')
        self._watchers_model.setHeaderData(1, Qt.Horizontal, 'R')
        self._watchers_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchers_model.setHeaderData(2, Qt.Horizontal, 'W')
        self._watchers_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchers_model.setHeaderData(3, Qt.Horizontal, 'X')
        self._watchers_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter,
                                           Qt.TextAlignmentRole)
        self._watchers_model.setHeaderData(4, Qt.Horizontal, 'S')
        self._watchers_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._watchers_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_addwatcher_dlg(self, ptr=None):  # pylint: disable=too-many-branches
        """ Shows AddWatcherDialog
        """
        watcher_dlg = AddWatcherDialog(self, ptr)
        if watcher_dlg.exec_() == QDialog.Accepted:
            mem_r = watcher_dlg.acc_read.isChecked()
            mem_w = watcher_dlg.acc_write.isChecked()
            mem_x = watcher_dlg.acc_execute.isChecked()
            mem_s = watcher_dlg.singleshot.isChecked()

            ptr = watcher_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('addWatcher', [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._watchers_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 watcher in dwarf too
            self._app_window.dwarf.dwarf_api('removeWatcher', 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 watcher', self._on_additem_clicked)
        if index != -1:
            context_menu.addSeparator()
            context_menu.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self._watchers_model.item(index, 0).text()))
            context_menu.addAction(
                'Jump to address', lambda: self._app_window.jump_to_address(
                    self._watchers_model.item(index, 0).text()))
            context_menu.addAction(
                'Delete watcher', lambda: self.remove_address(
                    self._watchers_model.item(index, 0).text()))
        context_menu.exec_(glbl_pt)

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

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

        self.do_addwatcher_dlg()

    def _on_watcher_added(self, ptr, flags):
        """ Callback from Dwarf after Watcher is added
        """
        ptr = utils.parse_ptr(ptr)
        # add to watcherslist
        self.add_address(ptr, flags, from_api=True)
        self.onItemAdded.emit(ptr)

    def _on_watcher_removed(self, ptr):
        """ Callback from Dwarf after watcher is removed
        """
        ptr = utils.parse_ptr(ptr)
        # remove from list
        self.remove_address(ptr, from_api=True)
        self.onItemRemoved.emit(ptr)
Exemplo n.º 8
0
class BookmarksPanel(QWidget):

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

    def __init__(self, parent=None):  # pylint: disable=too-many-statements
        super(BookmarksPanel, 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_dblclicked)
        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_A),
                                       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_dblclicked(self, index):
        index = self._bookmarks_list.selectionModel().currentIndex().row()
        if index != -1:
            addr = self._bookmarks_model.item(index, 0).text()
            if addr:
                self.onShowMemoryRequest.emit(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.º 9
0
class ModulesPanel(QSplitter):
    """ ModulesPanel

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

    onAddHook = pyqtSignal(list, name='onAddHook')
    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._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)

    # ************************************************************************
    # **************************** 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:
            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])

    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
        """
        module_index = self.modules_list.selectionModel().currentIndex().row()
        module = self.modules_model.item(module_index, 0)  # module name
        if module is None:
            return

        imports = self._app_window.dwarf.dwarf_api('enumerateImports',
                                                   module.text())
        if imports:
            imports = json.loads(imports)
            if imports:
                self.set_imports(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)

        exports = self._app_window.dwarf.dwarf_api('enumerateExports',
                                                   module.text())
        if exports:
            exports = json.loads(exports)
            if exports:
                self.set_exports(exports)
                self.exports_list.setVisible(True)
                self.exports_list.resizeColumnToContents(0)
                self.exports_list.resizeColumnToContents(1)
            else:
                self.exports_list.setVisible(False)

        symbols = self._app_window.dwarf.dwarf_api('enumerateSymbols',
                                                   module.text())
        if symbols:
            symbols = json.loads(symbols)
            if symbols:
                self.set_symbols(symbols)
                self.symbols_list.setVisible(True)
                self.symbols_list.resizeColumnToContents(0)
                self.symbols_list.resizeColumnToContents(1)
            else:
                self.symbols_list.setVisible(False)

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

    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.addAction(
                'Copy address', lambda: utils.copy_hex_to_clipboard(
                    self.modules_model.item(index, 1).text()))
            context_menu.addSeparator()
            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()

        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 Hook', lambda: self._add_hook(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()))

            # 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 Hook', lambda: self._add_hook(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))

            # 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_hook(self, ptr, name=None):
        """ MenuItem AddHook
        """
        if name is None:
            name = ptr
        if isinstance(ptr, str):
            if ptr.startswith('0x') or ptr.startswith('#'):
                self.onAddHook.emit([ptr, name])
        elif isinstance(ptr, int):
            str_fmt = '0x{0:x}'
            self.onAddHook.emit(str_fmt.format([ptr, name]))

    def _on_parse_elf(self, elf_path):
        from 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.º 10
0
class WelcomeDialog(QDialog):
    onSessionSelected = pyqtSignal(str, name='onSessionSelected')
    onUpdateComplete = pyqtSignal(name='onUpdateComplete')
    onIsNewerVersion = pyqtSignal(name='onIsNewerVersion')

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

        self._prefs = parent.prefs

        self._sub_titles = [
            ['duck', 'dumb', 'doctor', 'dutch', 'dark', 'dirty', 'debugging'],
            ['warriors', 'wardrobes', 'waffles', 'wishes', 'worcestershire'],
            ['are', 'aren\'t', 'ain\'t', 'appears to be'],
            ['rich', 'real', 'riffle', 'retarded', 'rock'],
            [
                'as f**k', 'fancy', 'f****d', 'front-ended', 'falafel',
                'french fries'
            ],
        ]

        self._update_thread = None

        # setup size and remove/disable titlebuttons
        self.desktop_geom = qApp.desktop().availableGeometry()
        self.setFixedSize(self.desktop_geom.width() * .45,
                          self.desktop_geom.height() * .4)
        self.setGeometry(
            QStyle.alignedRect(Qt.LeftToRight, Qt.AlignCenter, self.size(),
                               qApp.desktop().availableGeometry()))
        self.setSizeGripEnabled(False)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
        self.setWindowFlag(Qt.WindowCloseButtonHint, True)
        self.setModal(True)

        self._recent_list_model = QStandardItemModel(0, 2)
        self._recent_list_model.setHeaderData(0, Qt.Horizontal, 'Type')
        self._recent_list_model.setHeaderData(1, Qt.Horizontal, 'Path')

        self._recent_list = DwarfListView(self)
        self._recent_list.setModel(self._recent_list_model)

        self._recent_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents)
        self._recent_list.header().setSectionResizeMode(1, QHeaderView.Stretch)

        # setup ui elements
        self.setup_ui()

        random.seed(a=None, version=2)

        self.update_commits_thread = DwarfCommitsThread(parent)
        self.update_commits_thread.on_update_available.connect(
            self._on_dwarf_isupdate)
        self.update_commits_thread.start()
        # center
        self.setGeometry(
            QStyle.alignedRect(Qt.LeftToRight, Qt.AlignCenter, self.size(),
                               qApp.desktop().availableGeometry()))

    def setup_ui(self):
        """ Setup Ui
        """
        main_wrap = QVBoxLayout()
        main_wrap.setContentsMargins(0, 0, 0, 0)

        # updatebar on top
        self.update_bar = UpdateBar(self)
        self.update_bar.onUpdateNowClicked.connect(self._update_dwarf)
        self.update_bar.setVisible(False)
        main_wrap.addWidget(self.update_bar)

        # main content
        h_box = QHBoxLayout()
        h_box.setContentsMargins(15, 15, 15, 15)
        wrapper = QVBoxLayout()
        head = QHBoxLayout()
        head.setContentsMargins(50, 10, 0, 10)
        # dwarf icon
        icon = QLabel()
        icon.setPixmap(QPixmap(utils.resource_path('assets/dwarf.svg')))
        icon.setAlignment(Qt.AlignCenter)
        icon.setMinimumSize(QSize(125, 125))
        icon.setMaximumSize(QSize(125, 125))
        head.addWidget(icon)

        # main title
        v_box = QVBoxLayout()
        title = QLabel('Dwarf')
        title.setContentsMargins(0, 0, 0, 0)
        font = QFont('Anton', 100, QFont.Bold)
        font.setPixelSize(120)
        title.setFont(font)
        title.setMaximumHeight(125)
        title.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
        title.setAlignment(Qt.AlignCenter)
        head.addWidget(title)

        sub_title_text = (self._pick_random_word(0) + ' ' +
                          self._pick_random_word(1) + ' ' +
                          self._pick_random_word(2) + ' ' +
                          self._pick_random_word(3) + ' ' +
                          self._pick_random_word(4))
        sub_title_text = sub_title_text[:1].upper() + sub_title_text[1:]
        self._sub_title = QLabel(sub_title_text)
        font = QFont('OpenSans', 16, QFont.Bold)
        font.setPixelSize(24)
        self._sub_title.setFont(font)
        font_metric = QFontMetrics(self._sub_title.font())
        self._char_width = font_metric.widthChar('#')
        self._sub_title.setAlignment(Qt.AlignCenter)
        self._sub_title.setContentsMargins(175, 0, 0, 20)
        self._sub_title.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Minimum)
        v_box.addLayout(head)
        v_box.addWidget(self._sub_title)

        wrapper.addLayout(v_box)

        recent = QLabel('Recent saved Sessions')
        font = recent.font()
        font.setPixelSize(14)
        font.setBold(True)
        # font.setPointSize(10)
        recent.setFont(font)
        wrapper.addWidget(recent)
        wrapper.addWidget(self._recent_list)
        h_box.addLayout(wrapper, stretch=False)
        buttonSpacer = QSpacerItem(15, 100, QSizePolicy.Fixed,
                                   QSizePolicy.Minimum)
        h_box.addItem(buttonSpacer)
        wrapper = QVBoxLayout()

        btn = QPushButton()
        ico = QIcon(QPixmap(utils.resource_path('assets/android.svg')))
        btn.setIconSize(QSize(75, 75))
        btn.setIcon(ico)
        btn.setToolTip('New Android Session')
        btn.clicked.connect(self._on_android_button)
        wrapper.addWidget(btn)

        btn = QPushButton()
        ico = QIcon(QPixmap(utils.resource_path('assets/apple.svg')))
        btn.setIconSize(QSize(75, 75))
        btn.setIcon(ico)
        btn.setToolTip('New iOS Session')
        btn.clicked.connect(self._on_ios_button)
        wrapper.addWidget(btn)

        btn = QPushButton()
        ico = QIcon(QPixmap(utils.resource_path('assets/local.svg')))
        btn.setIconSize(QSize(75, 75))
        btn.setIcon(ico)
        btn.setToolTip('New Local Session')
        btn.clicked.connect(self._on_local_button)
        wrapper.addWidget(btn)

        btn = QPushButton()
        ico = QIcon(QPixmap(utils.resource_path('assets/remote.svg')))
        btn.setIconSize(QSize(75, 75))
        btn.setIcon(ico)
        btn.setToolTip('New Remote Session')
        btn.clicked.connect(self._on_remote_button)
        wrapper.addWidget(btn)

        h_box.addLayout(wrapper, stretch=False)
        main_wrap.addLayout(h_box)
        self.setLayout(main_wrap)

    def _on_dwarf_isupdate(self):
        self.update_bar.setVisible(True)
        self.setFixedHeight(self.height() + self.update_bar.height())
        self.onIsNewerVersion.emit()

    def _update_dwarf(self):
        self._update_thread = DwarfUpdateThread(self)
        self._update_thread.on_finished.connect(self._update_finished)
        if not self._update_thread.isRunning():
            self._update_thread.start()

    def _update_finished(self):
        self.onUpdateComplete.emit()

    def _on_android_button(self):
        self.onSessionSelected.emit('Android')
        self.close()

    def _on_local_button(self):
        self.onSessionSelected.emit('Local')
        self.close()

    def _on_ios_button(self):
        self.onSessionSelected.emit('Ios')
        self.close()

    def _on_remote_button(self):
        self.onSessionSelected.emit('Remote')
        self.close()

    def _pick_random_word(self, arr):
        return self._sub_titles[arr][random.randint(
            0,
            len(self._sub_titles[arr]) - 1)]

    def showEvent(self, QShowEvent):
        """ override to change font size when subtitle is cutted
        """
        if len(self._sub_title.text()) * self._char_width > (
                self._sub_title.width() - 155):
            font = QFont('OpenSans', 14, QFont.Bold)
            font.setPixelSize(20)
            self._sub_title.setFont(font)
        return super().showEvent(QShowEvent)
Exemplo n.º 11
0
class WelcomeDialog(QDialog):
    onSessionSelected = pyqtSignal(str, name='onSessionSelected')
    onSessionRestore = pyqtSignal(dict, name='onSessionRestore')
    onUpdateComplete = pyqtSignal(name='onUpdateComplete')
    onIsNewerVersion = pyqtSignal(name='onIsNewerVersion')

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

        self._prefs = parent.prefs

        self._sub_titles = [
            ['duck', 'dumb', 'doctor', 'dutch', 'dark', 'dirty', 'debugging'],
            ['warriors', 'wardrobes', 'waffles', 'wishes', 'worcestershire'],
            ['are', 'aren\'t', 'ain\'t', 'appears to be'],
            ['rich', 'real', 'riffle', 'retarded', 'rock'],
            [
                'as f**k', 'fancy', 'f****d', 'front-ended', 'falafel',
                'french fries'
            ],
        ]

        self._update_thread = None

        self._recent_list_model = QStandardItemModel(0, 6)
        self._recent_list_model.setHeaderData(0, Qt.Horizontal, 'Path')
        self._recent_list_model.setHeaderData(1, Qt.Horizontal, 'Session')
        self._recent_list_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)
        self._recent_list_model.setHeaderData(2, Qt.Horizontal, 'Breakpoints')
        self._recent_list_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)
        self._recent_list_model.setHeaderData(3, Qt.Horizontal, 'Watchers')
        self._recent_list_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)
        self._recent_list_model.setHeaderData(4, Qt.Horizontal, 'OnLoads')
        self._recent_list_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)
        self._recent_list_model.setHeaderData(5, Qt.Horizontal, 'Bookmarks')
        self._recent_list_model.setHeaderData(5, Qt.Horizontal, Qt.AlignCenter,
                                              Qt.TextAlignmentRole)
        #self._recent_list_model.setHeaderData(6, Qt.Horizontal, 'Custom script')
        #self._recent_list_model.setHeaderData(6, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole)

        self._recent_list = DwarfListView(self)
        self._recent_list.setModel(self._recent_list_model)

        self._recent_list.header().setSectionResizeMode(
            0, QHeaderView.ResizeToContents | QHeaderView.Interactive)
        self._recent_list.header().setSectionResizeMode(1, QHeaderView.Stretch)
        self._recent_list.header().setSectionResizeMode(2, QHeaderView.Stretch)
        self._recent_list.header().setSectionResizeMode(3, QHeaderView.Stretch)
        self._recent_list.header().setSectionResizeMode(4, QHeaderView.Stretch)
        self._recent_list.header().setSectionResizeMode(5, QHeaderView.Stretch)
        #self._recent_list.header().setSectionResizeMode(6, QHeaderView.Stretch)

        self._recent_list.setContextMenuPolicy(Qt.CustomContextMenu)
        self._recent_list.customContextMenuRequested.connect(
            self._on_recent_sessions_context_menu)
        self._recent_list.doubleClicked.connect(
            self._on_recent_session_double_click)

        # setup size and remove/disable titlebuttons
        self.desktop_geom = qApp.desktop().availableGeometry()
        self.setFixedSize(self.desktop_geom.width() * .45,
                          self.desktop_geom.height() * .4)
        self.setGeometry(
            QStyle.alignedRect(Qt.LeftToRight, Qt.AlignCenter, self.size(),
                               qApp.desktop().availableGeometry()))
        self.setSizeGripEnabled(False)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
        self.setWindowFlag(Qt.WindowCloseButtonHint, True)
        self.setModal(True)

        # setup ui elements
        self.setup_ui()

        random.seed(a=None, version=2)

        self.update_commits_thread = DwarfCommitsThread(parent)
        self.update_commits_thread.on_update_available.connect(
            self._on_dwarf_isupdate)
        self.update_commits_thread.start()
        # center
        self.setGeometry(
            QStyle.alignedRect(Qt.LeftToRight, Qt.AlignCenter, self.size(),
                               qApp.desktop().availableGeometry()))

    def setup_ui(self):
        """ Setup Ui
        """
        main_wrap = QVBoxLayout()
        main_wrap.setContentsMargins(0, 0, 0, 0)

        # updatebar on top
        self.update_bar = UpdateBar(self)
        self.update_bar.onUpdateNowClicked.connect(self._update_dwarf)
        self.update_bar.setVisible(False)
        main_wrap.addWidget(self.update_bar)

        # main content
        h_box = QHBoxLayout()
        h_box.setContentsMargins(15, 15, 15, 15)
        wrapper = QVBoxLayout()
        head = QHBoxLayout()
        head.setContentsMargins(50, 10, 0, 10)
        # dwarf icon
        icon = QLabel()
        icon.setPixmap(QPixmap(utils.resource_path('assets/dwarf.svg')))
        icon.setAlignment(Qt.AlignCenter)
        icon.setMinimumSize(QSize(125, 125))
        icon.setMaximumSize(QSize(125, 125))
        head.addWidget(icon)

        # main title
        v_box = QVBoxLayout()
        title = QLabel('Dwarf')
        title.setContentsMargins(0, 0, 0, 0)
        title.setFont(QFont('Anton', 100, QFont.Bold))
        title.setMaximumHeight(125)
        title.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
        title.setAlignment(Qt.AlignCenter)
        head.addWidget(title)

        sub_title_text = (self._pick_random_word(0) + ' ' +
                          self._pick_random_word(1) + ' ' +
                          self._pick_random_word(2) + ' ' +
                          self._pick_random_word(3) + ' ' +
                          self._pick_random_word(4))
        sub_title_text = sub_title_text[:1].upper() + sub_title_text[1:]
        self._sub_title = QLabel(sub_title_text)
        self._sub_title.setFont(QFont('OpenSans', 16, QFont.Bold))
        font_metric = QFontMetrics(self._sub_title.font())
        self._char_width = font_metric.widthChar('#')
        self._sub_title.setAlignment(Qt.AlignCenter)
        self._sub_title.setContentsMargins(175, 0, 0, 20)
        self._sub_title.setSizePolicy(QSizePolicy.Expanding,
                                      QSizePolicy.Minimum)
        v_box.addLayout(head)
        v_box.addWidget(self._sub_title)

        wrapper.addLayout(v_box)

        recent = QLabel('Last saved Sessions')
        font = recent.font()
        font.setPixelSize(14)
        font.setBold(True)
        #font.setPointSize(10)
        recent.setFont(font)
        wrapper.addWidget(recent)
        wrapper.addWidget(self._recent_list)
        h_box.addLayout(wrapper, stretch=False)
        buttonSpacer = QSpacerItem(15, 100, QSizePolicy.Fixed,
                                   QSizePolicy.Minimum)
        h_box.addItem(buttonSpacer)
        wrapper = QVBoxLayout()

        btn = QPushButton()
        ico = QIcon(QPixmap(utils.resource_path('assets/android.svg')))
        btn.setIconSize(QSize(75, 75))
        btn.setIcon(ico)
        btn.setToolTip('New Android Session')
        btn.clicked.connect(self._on_android_button)
        wrapper.addWidget(btn)

        btn = QPushButton()
        ico = QIcon(QPixmap(utils.resource_path('assets/apple.svg')))
        btn.setIconSize(QSize(75, 75))
        btn.setIcon(ico)
        btn.setToolTip('New iOS Session')
        btn.clicked.connect(self._on_ios_button)
        wrapper.addWidget(btn)

        btn = QPushButton()
        ico = QIcon(QPixmap(utils.resource_path('assets/local.svg')))
        btn.setIconSize(QSize(75, 75))
        btn.setIcon(ico)
        btn.setToolTip('New Local Session')
        btn.clicked.connect(self._on_local_button)
        wrapper.addWidget(btn)

        btn = QPushButton()
        ico = QIcon(QPixmap(utils.resource_path('assets/remote.svg')))
        btn.setIconSize(QSize(75, 75))
        btn.setIcon(ico)
        btn.setToolTip('New Remote Session')
        btn.clicked.connect(self._on_remote_button)
        wrapper.addWidget(btn)

        session_history = self._prefs.get(prefs.RECENT_SESSIONS, default=[])
        invalid_session_files = []
        for recent_session_file in session_history:
            if os.path.exists(recent_session_file):
                with open(recent_session_file, 'r') as f:
                    exported_session = json.load(f)
                hooks = '0'
                watchers = '0'
                on_loads = 0
                bookmarks = '0'
                #have_user_script = False
                if 'hooks' in exported_session and exported_session[
                        'hooks'] is not None:
                    hooks = str(len(exported_session['hooks']))
                if 'watchers' in exported_session and exported_session[
                        'watchers'] is not None:
                    watchers = str(len(exported_session['watchers']))
                if 'nativeOnLoads' in exported_session and exported_session[
                        'nativeOnLoads'] is not None:
                    on_loads += len(exported_session['nativeOnLoads'])
                if 'javaOnLoads' in exported_session and exported_session[
                        'javaOnLoads'] is not None:
                    on_loads += len(exported_session['javaOnLoads'])
                if 'bookmarks' in exported_session and exported_session[
                        'bookmarks'] is not None:
                    bookmarks = str(len(exported_session['bookmarks']))
                if 'user_script' in exported_session and exported_session[
                        'user_script']:
                    have_user_script = exported_session['user_script'] != ''

                #user_script_item = QStandardItem()
                #if have_user_script:
                #user_script_item.setIcon(self._dot_icon)

                on_loads = str(on_loads)

                recent_session_file_item = QStandardItem(recent_session_file)
                recent_session_file_item.setData(exported_session,
                                                 Qt.UserRole + 2)

                item_1 = QStandardItem(exported_session['session'])
                item_1.setTextAlignment(Qt.AlignCenter)
                item_2 = QStandardItem(hooks)
                item_2.setTextAlignment(Qt.AlignCenter)
                item_3 = QStandardItem(watchers)
                item_3.setTextAlignment(Qt.AlignCenter)
                item_4 = QStandardItem(on_loads)
                item_4.setTextAlignment(Qt.AlignCenter)
                item_5 = QStandardItem(bookmarks)
                item_5.setTextAlignment(Qt.AlignCenter)
                #item_6 = QStandardItem(user_script_item)
                #item_6.setTextAlignment(Qt.AlignCenter)

                self._recent_list_model.insertRow(
                    self._recent_list_model.rowCount(), [
                        recent_session_file_item, item_1, item_2, item_3,
                        item_4, item_5
                    ])
            else:
                invalid_session_files.append(recent_session_file)
        for invalid in invalid_session_files:
            session_history.pop(session_history.index(invalid))
        self._prefs.put(prefs.RECENT_SESSIONS, session_history)

        h_box.addLayout(wrapper, stretch=False)
        main_wrap.addLayout(h_box)
        self.setLayout(main_wrap)

    def _on_dwarf_isupdate(self):
        self.update_bar.setVisible(True)
        self.setFixedHeight(self.height() + self.update_bar.height())
        self.onIsNewerVersion.emit()

    def _update_dwarf(self):
        self._update_thread = DwarfUpdateThread(self)
        self._update_thread.on_finished.connect(self._update_finished)
        if not self._update_thread.isRunning():
            self._update_thread.start()

    def _update_finished(self):
        self.onUpdateComplete.emit()

    def _on_android_button(self):
        self.onSessionSelected.emit('Android')
        self.close()

    def _on_local_button(self):
        self.onSessionSelected.emit('Local')
        self.close()

    def _on_ios_button(self):
        self.onSessionSelected.emit('Ios')
        self.close()

    def _on_remote_button(self):
        self.onSessionSelected.emit('Remote')
        self.close()

    def _pick_random_word(self, arr):
        return self._sub_titles[arr][random.randint(
            0,
            len(self._sub_titles[arr]) - 1)]

    def _on_recent_sessions_context_menu(self, pos):
        index = self.list_view.indexAt(pos).row()
        glbl_pt = self.list_view.mapToGlobal(pos)
        context_menu = QMenu(self)
        if index != -1:
            context_menu.addAction(
                'Delete recent session', lambda: self._remove_recent_sessions(
                    self._recent_list_model.item(index, 0).text()))

            context_menu.exec_(glbl_pt)

    def _remove_recent_session(self, session_file):
        if os.path.exists(session_file):
            os.remove(session_file)
            session_history = self._prefs.get(prefs.RECENT_SESSIONS,
                                              default=[])
            if session_file in session_history:
                session_history.pop(session_history.index(session_file))
                self._prefs.put(prefs.RECENT_SESSIONS, session_history)

    def _on_recent_session_double_click(self, model_index):
        row = self._recent_list_model.itemFromIndex(model_index).row()
        recent_session_file = self._recent_list_model.item(row, 0)
        recent_session_data = recent_session_file.data(Qt.UserRole + 2)
        self.onSessionRestore.emit(recent_session_data)

    def showEvent(self, QShowEvent):
        """ override to change font size when subtitle is cutted
        """
        if len(self._sub_title.text()) * self._char_width > (
                self._sub_title.width() - 155):
            self._sub_title.setFont(QFont('OpenSans', 14, QFont.Bold))
        return super().showEvent(QShowEvent)