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._breakpoint_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.breakpoint_java(class_name + '.' + method_name) def _breakpoint_class(self, class_name): if class_name: self._app_window.dwarf.breakpoint_java(class_name) def _breakpoint_class_functions(self, class_name): if class_name: self._app_window.dwarf.dwarf_api('breakpointAllJavaMethods', 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( 'Breakpoint constructor', lambda: self._breakpoint_class( self._javaclass_model.item(index, 0).text())) context_menu.addAction( 'Breakpoint all methods', lambda: self._breakpoint_class_functions( self._javaclass_model.item(index, 0).text())) context_menu.addSeparator() if self._java_classes.search_enabled: context_menu.addSeparator() context_menu.addAction('Search', self._java_classes._on_cm_search) context_menu.addAction('Refresh', self.update_classes) context_menu.exec_(glbl_pt) def _breakpoint_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.breakpoint_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( 'Breakpoint method', lambda: self._breakpoint_method( self._javamethod_model.item(index, 0).text())) context_menu.addSeparator() if self._java_methods.search_enabled: context_menu.addSeparator() context_menu.addAction('Search', self._java_methods._on_cm_search) context_menu.addAction('Refresh', self._cm_refresh_methods) context_menu.exec_(glbl_pt)
class ElfInfo(DwarfDialog): onShowMemoryRequest = pyqtSignal(str, name='onShowMemoryRequest') def __init__(self, parent=None, file_path=None): super().__init__(parent=parent) h_box = QHBoxLayout(self) self._ident_titles = [ 'EI_MAG0', 'EI_MAG1', 'EI_MAG2', 'EI_MAG3', 'EI_CLASS', 'EI_DATA', 'EI_VERSION', 'EI_OSABI', 'EI_ABIVERSION', 'EI_PAD' ] self.elf_info = DwarfListView(self) self.elf_info.setRootIsDecorated(True) self.elf_info.setExpandsOnDoubleClick(True) self.elf_info.setContextMenuPolicy(Qt.CustomContextMenu) self.elf_info.customContextMenuRequested.connect(self._on_context_menu) h_box.addWidget(self.elf_info) self._elf_info_mdl = QStandardItemModel(0, 2) self._elf_info_mdl.setHeaderData(0, Qt.Horizontal, 'Name') self._elf_info_mdl.setHeaderData(1, Qt.Horizontal, 'Value') self.elf_info.setModel(self._elf_info_mdl) self.elf_info.doubleClicked.connect(self._on_dblclicked) self.title = "ELF Info" self._elf_file_path = None self.setLayout(h_box) if file_path: self.set_elf_file(file_path) def set_elf_file(self, file_path): self._elf_file_path = file_path self.title = 'ELF Info for ' + file_path self._elf_info_mdl.insertRow( 0, [QStandardItem('File'), QStandardItem(file_path)]) def set_parsed_data(self, parsed_data): parent_item = self._elf_info_mdl.item(0) if 'endian' in parsed_data: parent_item.appendRow([ QStandardItem('Endian'), QStandardItem(parsed_data['endian']) ]) if 'is64bit' in parsed_data: txt = 'No' if parsed_data['is64bit']: txt = 'Yes' parent_item.appendRow([QStandardItem('64Bit'), QStandardItem(txt)]) if 'header' in parsed_data: elf_header = QStandardItem('ELF Header') parent_item.appendRow([elf_header]) for d in parsed_data['header']: if d == 'e_ident': ident_item = QStandardItem(d) elf_header.appendRow([ ident_item, QStandardItem(str(parsed_data['header'][d])) ]) a = 0 for i in parsed_data['header'][d]: if a >= len(self._ident_titles): a = len(self._ident_titles) - 1 ident_item.appendRow([ QStandardItem(self._ident_titles[a]), QStandardItem(str(i)) ]) a += 1 else: elf_header.appendRow([ QStandardItem(d), QStandardItem(hex(parsed_data['header'][d])) ]) if 'programheaders' in parsed_data: prog_headers_item = QStandardItem('Program Headers') parent_item.appendRow([prog_headers_item]) i = 1 for header in parsed_data['programheaders']: header_item = QStandardItem("%d" % i) prog_headers_item.appendRow([header_item]) i += 1 for d in header: header_item.appendRow( [QStandardItem(d), QStandardItem(hex(header[d]))]) if 'sectionheaders' in parsed_data: sect_headers = QStandardItem('Section Headers') parent_item.appendRow([sect_headers]) i = 1 for header in parsed_data['sectionheaders']: txt = header['name'] if not txt: txt = 'NULL' header_item = QStandardItem(txt) sect_headers.appendRow([header_item]) i += 1 for d in header: if d == 'name': continue elif d == 'data' and header[d]: data_item = QStandardItem('Data') header_item.appendRow(data_item) base = self.parent().dwarf.dwarf_api( 'findModule', self._elf_file_path)['base'] for ptr in header[d]: if int(ptr): va = hex(int(base, 16) + int(ptr)) fo = hex(int(ptr)) if self.elf_info.uppercase_hex: va = va.upper().replace('0X', '0x') fo = fo.upper().replace('0X', '0x') data_item.appendRow([ QStandardItem(va), QStandardItem('FileOffset: ' + fo) ]) else: header_item.appendRow( [QStandardItem(d), QStandardItem(hex(header[d]))]) self.elf_info.expandAll() self.elf_info.resizeColumnToContents(0) self.elf_info.resizeColumnToContents(1) self.setMinimumWidth( self.elf_info.columnWidth(0) + self.elf_info.columnWidth(1) + 50) self.setMinimumHeight(400) self.elf_info.collapseAll() self.elf_info.expandToDepth(1) def _on_dblclicked(self, model_index): if model_index.column() == 0 and model_index.data(): if model_index.data().startswith('0x'): self.onShowMemoryRequest.emit(model_index.data()) def _on_context_menu(self, pos): model_index = self.elf_info.indexAt(pos) if model_index.column() == 0 and model_index.data(): if model_index.data().startswith('0x'): context_menu = QMenu() context_menu.addAction( 'Add to Bookmarks', lambda: self.parent().bookmarks_panel.insert_bookmark( model_index.data(), 'init_array')) context_menu.addAction( 'Copy Address', lambda: utils.copy_hex_to_clipboard(model_index.data())) # show contextmenu glbl_pt = self.elf_info.mapToGlobal(pos) context_menu.exec_(glbl_pt)
class DataPanel(QSplitter): def __init__(self, app): super(DataPanel, self).__init__(app) self.app = app self.data = {} self.setOrientation(Qt.Horizontal) self._key_list_model = QStandardItemModel(0, 1) self.key_lists = DwarfListView(parent=self.app) self.key_lists.setHeaderHidden(True) self.key_lists.setModel(self._key_list_model) self.key_lists.selectionModel().selectionChanged.connect( self.item_selected) self.key_lists.setContextMenuPolicy(Qt.CustomContextMenu) self.key_lists.customContextMenuRequested.connect( self._on_context_menu) self.addWidget(self.key_lists) self.editor = QPlainTextEdit() self.addWidget(self.editor) self.hex_view = HexEditor(self.app) self.hex_view.have_context_menu = False self.hex_view.setVisible(False) self.addWidget(self.hex_view) #self.setStretchFactor(0, 8) self.setStretchFactor(1, 4) self.setStretchFactor(2, 4) def clear(self): self._key_list_model.clear() self.editor.setPlainText('') self.hex_view.clear_panel() def append_data(self, data_type, key, text_data): if key not in self.data: self._key_list_model.appendRow([QStandardItem(key)]) self.data[key] = [data_type, text_data] def item_selected(self, item1, item2): item = self._key_list_model.itemFromIndex(item1.indexes()[0]) if self.data[item.text()][0] == 'plain': self.hex_view.setVisible(False) self.editor.setVisible(True) self.editor.setPlainText(self.data[item.text()][1]) else: data = self.data[item.text()][1] try: as_tx = data.decode('utf8') self.editor.setVisible(True) self.editor.setPlainText(as_tx) except: self.editor.setVisible(False) self.hex_view.setVisible(True) self.hex_view.bytes_per_line = 16 self.hex_view.set_data(data) def _on_context_menu(self, pos): context_menu = QMenu(self) index = self.key_lists.indexAt(pos).row() if index != -1: context_menu.addAction('Clear', self.clear) global_pt = self.key_lists.mapToGlobal(pos) context_menu.exec(global_pt)
class ModulesPanel(QSplitter): """ ModulesPanel Signals: onAddBreakpoint([ptr, funcname]) - MenuItem AddBreakpoint onDumpBinary([ptr, size#int]) - MenuItem DumpBinary onModuleSelected([ptr, size#int]) - ModuleDoubleClicked onModuleFuncSelected(ptr) - FunctionDoubleClicked """ # pylint: disable=too-many-instance-attributes onAddBreakpoint = pyqtSignal(list, name='onAddBreakpoint') onDumpBinary = pyqtSignal(list, name='onDumpBinary') onModuleSelected = pyqtSignal(list, name='onModuleSelected') onModuleFuncSelected = pyqtSignal(str, name='onModuleFuncSelected') def __init__(self, parent=None): # pylint: disable=too-many-statements super(ModulesPanel, self).__init__(parent) self._app_window = parent if self._app_window.dwarf is None: print('ModulesPanel created before Dwarf exists') return self._app_window.dwarf.onSetModules.connect(self.set_modules) self._app_window.dwarf.onModuleLoaded.connect(self.on_module_loaded) self._uppercase_hex = True self._sized = False self.setContentsMargins(0, 0, 0, 0) # setup models self.modules_list = None self.modules_model = QStandardItemModel(0, 4, self) self.modules_model.setHeaderData(0, Qt.Horizontal, 'Name') self.modules_model.setHeaderData(1, Qt.Horizontal, 'Base') self.modules_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.modules_model.setHeaderData(2, Qt.Horizontal, 'Size') self.modules_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.modules_model.setHeaderData(3, Qt.Horizontal, 'Path') self.imports_list = None self.imports_model = QStandardItemModel(0, 4, self) self.imports_model.setHeaderData(0, Qt.Horizontal, 'Import') self.imports_model.setHeaderData(1, Qt.Horizontal, 'Address') self.imports_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.imports_model.setHeaderData(2, Qt.Horizontal, 'Module') self.imports_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.imports_model.setHeaderData(3, Qt.Horizontal, 'Type') self.exports_list = None self.exports_model = QStandardItemModel(0, 3, self) self.exports_model.setHeaderData(0, Qt.Horizontal, 'Export') self.exports_model.setHeaderData(1, Qt.Horizontal, 'Address') self.exports_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.exports_model.setHeaderData(2, Qt.Horizontal, 'Type') self.symbols_list = None self.symbols_model = QStandardItemModel(0, 3, self) self.symbols_model.setHeaderData(0, Qt.Horizontal, 'Symbol') self.symbols_model.setHeaderData(1, Qt.Horizontal, 'Address') self.symbols_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self.symbols_model.setHeaderData(2, Qt.Horizontal, 'Type') # setup ui self.modules_list = DwarfListView() self.modules_list.setContextMenuPolicy(Qt.CustomContextMenu) self.modules_list.customContextMenuRequested.connect( self._on_modules_contextmenu) self.modules_list.setEditTriggers(self.modules_list.NoEditTriggers) self.modules_list.clicked.connect(self._module_clicked) self.modules_list.doubleClicked.connect(self._module_dblclicked) self.modules_list.setModel(self.modules_model) self.modules_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.modules_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.modules_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self.modules_list.selectionModel().selectionChanged.connect( self._module_clicked) self.addWidget(self.modules_list) v_splitter = QSplitter(Qt.Vertical) self.imports_list = DwarfListView() self.imports_list.setContextMenuPolicy(Qt.CustomContextMenu) self.imports_list.customContextMenuRequested.connect( self._on_imports_contextmenu) self.imports_list.setEditTriggers(self.modules_list.NoEditTriggers) self.imports_list.doubleClicked.connect(self._import_dblclicked) self.imports_list.setModel(self.imports_model) self.imports_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.imports_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.imports_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self.imports_list.setVisible(False) self.exports_list = DwarfListView() self.exports_list.setContextMenuPolicy(Qt.CustomContextMenu) self.exports_list.customContextMenuRequested.connect( self._on_exports_contextmenu) self.exports_list.setEditTriggers(self.modules_list.NoEditTriggers) self.exports_list.doubleClicked.connect(self._export_dblclicked) self.exports_list.setModel(self.exports_model) self.exports_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.exports_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.exports_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self.exports_list.setVisible(False) self.symbols_list = DwarfListView() self.symbols_list.setContextMenuPolicy(Qt.CustomContextMenu) self.symbols_list.doubleClicked.connect(self._symbol_dblclicked) self.symbols_list.setModel(self.symbols_model) self.symbols_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self.symbols_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.symbols_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self.symbols_list.setVisible(False) v_splitter.addWidget(self.imports_list) v_splitter.addWidget(self.exports_list) v_splitter.addWidget(self.symbols_list) v_splitter.setSizes([100, 100, 100]) self.addWidget(v_splitter) self.update_modules() # ************************************************************************ # **************************** Properties ******************************** # ************************************************************************ @property def uppercase_hex(self): """ HexDisplayStyle """ return self._uppercase_hex @uppercase_hex.setter def uppercase_hex(self, value): """ HexDisplayStyle """ if isinstance(value, bool): self._uppercase_hex = value elif isinstance(value, str): self._uppercase_hex = (value == 'upper') # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def set_modules(self, modules): """ Fills the ModulesList with data """ if self.modules_list is None: return self.modules_list.clear() for module in modules: self.add_module(module) self.modules_list.resizeColumnToContents(0) self.modules_list.resizeColumnToContents(1) self.modules_list.resizeColumnToContents(2) self.modules_list.resizeColumnToContents(3) def on_module_loaded(self, data): module = data[0] self.add_module(module) def add_module(self, module): name = QStandardItem() name.setTextAlignment(Qt.AlignLeft) if 'name' in module: name.setText(module['name']) base = QStandardItem() base.setTextAlignment(Qt.AlignCenter) str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' if 'base' in module: base.setText(str_fmt.format(int(module['base'], 16))) size = QStandardItem() size.setTextAlignment(Qt.AlignRight) if 'size' in module: size.setText("{0:,d}".format(int(module['size']))) path = QStandardItem() path.setTextAlignment(Qt.AlignLeft) if 'path' in module: path.setText(module['path']) self.modules_model.appendRow([name, base, size, path]) module_info = ModuleInfo(module) if 'exports' in module and module['exports']: module_info.apply_exports(module['exports']) if 'imports' in module and module['imports']: module_info.apply_imports(module['imports']) if 'symbols' in module and module['symbols']: module_info.apply_symbols(module['symbols']) module_info._updated_details = True def update_modules(self): """ DwarfApiCall updateModules """ return self._app_window.dwarf.dwarf_api('updateModules') def set_imports(self, imports): """ Fills the ImportsList with data """ if self.imports_list is None: return self.imports_list.clear() for import_ in imports: name = QStandardItem() name.setTextAlignment(Qt.AlignLeft) if 'name' in import_: name.setText(import_['name']) address = QStandardItem() address.setTextAlignment(Qt.AlignCenter) str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' if 'address' in import_: address.setText(str_fmt.format(int(import_['address'], 16))) module = QStandardItem() if 'module' in import_: module.setTextAlignment(Qt.AlignLeft) module.setText(import_['module']) type_ = QStandardItem() if 'type' in import_: type_.setTextAlignment(Qt.AlignLeft) type_.setText(import_['type']) self.imports_model.appendRow([name, address, module, type_]) def set_exports(self, exports): """ Fills the ExportsList with data """ if self.exports_list is None: return self.exports_list.clear() for export in exports: name = QStandardItem() name.setTextAlignment(Qt.AlignLeft) if 'name' in export: name.setText(export['name']) address = QStandardItem() address.setTextAlignment(Qt.AlignCenter) str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' if 'address' in export: address.setText(str_fmt.format(int(export['address'], 16))) type_ = QStandardItem() type_.setTextAlignment(Qt.AlignLeft) if 'type' in export: type_.setText(export['type']) self.exports_model.appendRow([name, address, type_]) def set_symbols(self, symbols): """ Fills the SymbolsList with data """ if self.symbols_list is None: return self.symbols_list.clear() for symbol in symbols: name = QStandardItem() name.setTextAlignment(Qt.AlignLeft) if 'name' in symbol: name.setText(symbol['name']) address = QStandardItem() address.setTextAlignment(Qt.AlignCenter) str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' if 'address' in symbol: address.setText(str_fmt.format(int(symbol['address'], 16))) type_ = QStandardItem() type_.setTextAlignment(Qt.AlignLeft) if 'type' in symbol: type_.setText(symbol['type']) self.symbols_model.appendRow([name, address, type_]) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _module_clicked(self): """ Module Clicked updates imports/exports/symbols """ if not self.modules_list.hasFocus(): return module_index = self.modules_list.selectionModel().currentIndex().row() module_name = self.modules_model.item(module_index, 0) if module_name is None: return module_name = module_name.text() module_address = self.modules_model.item(module_index, 1).text() module_info = self._app_window.dwarf.database.get_module_info(module_address) if module_info is not None: if not module_info.have_details: module_info = ModuleInfo.build_module_info(self._app_window.dwarf, module_name, fill_ied=True) else: module_info = ModuleInfo.build_module_info(self._app_window.dwarf, module_name, fill_ied=True) if module_info is not None: self.update_module_ui(module_info) if not self._sized: self.setSizes([100, 100]) self._sized = True def update_module_ui(self, module_info): if module_info.imports: self.set_imports(module_info.imports) self.imports_list.setVisible(True) self.imports_list.resizeColumnToContents(0) self.imports_list.resizeColumnToContents(1) self.imports_list.resizeColumnToContents(2) else: self.imports_list.setVisible(False) if module_info.exports: self.set_exports(module_info.exports) self.exports_list.setVisible(True) self.exports_list.resizeColumnToContents(0) self.exports_list.resizeColumnToContents(1) else: self.exports_list.setVisible(False) if module_info.symbols: self.set_symbols(module_info.symbols) self.symbols_list.setVisible(True) self.symbols_list.resizeColumnToContents(0) self.symbols_list.resizeColumnToContents(1) else: self.symbols_list.setVisible(False) def _module_dblclicked(self): """ Module DoubleClicked """ module_index = self.modules_list.selectionModel().currentIndex().row() base = self.modules_model.item(module_index, 1).text() size = self.modules_model.item(module_index, 2).text().replace(',', '') self.onModuleSelected.emit([base, size]) def _import_dblclicked(self): """ ImportFunction DoubleClicked """ index = self.imports_list.selectionModel().currentIndex().row() addr = self.imports_model.item(index, 1).text() self.onModuleFuncSelected.emit(addr) def _export_dblclicked(self): """ ExportFunction DoubleClicked """ index = self.exports_list.selectionModel().currentIndex().row() addr = self.exports_model.item(index, 1).text() self.onModuleFuncSelected.emit(addr) def _symbol_dblclicked(self): """ Symbol DoubleClicked """ index = self.symbols_list.selectionModel().currentIndex().row() addr = self.symbols_model.item(index, 1).text() self.onModuleFuncSelected.emit(addr) def _on_modules_contextmenu(self, pos): """ Modules ContextMenu """ index = self.modules_list.indexAt(pos).row() glbl_pt = self.modules_list.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: context_menu.addAction( 'Dump Binary', lambda: self._on_dumpmodule( self.modules_model.item(index, 1).text(), self.modules_model.item(index, 2).text())) context_menu.addSeparator() context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self.modules_model.item(index, 1).text())) context_menu.addAction( 'Copy Name', lambda: utils.copy_str_to_clipboard( self.modules_model.item(index, 0).text())) context_menu.addAction( 'Copy Path', lambda: utils.copy_str_to_clipboard( self.modules_model.item(index, 3).text())) context_menu.addSeparator() file_path = self.modules_model.item(index, 3).text() if self._app_window.dwarf._platform == 'linux': context_menu.addAction( 'Show ELF Info', lambda: self._on_parse_elf(file_path)) context_menu.addSeparator() # elif file_path and (file_path.endswith('.dll') or file_path.endswith('.exe')): # context_menu.addAction('Show PE Info', lambda: self._on_parse_pe(file_path)) # context_menu.addSeparator() if self.modules_list.search_enabled: context_menu.addSeparator() context_menu.addAction( 'Search', self.modules_list._on_cm_search) context_menu.addSeparator() context_menu.addAction('Refresh', self.update_modules) context_menu.exec_(glbl_pt) def _on_imports_contextmenu(self, pos): """ ImportList ContextMenu """ index = self.imports_list.indexAt(pos).row() if index != -1: context_menu = QMenu(self) func_name = self.imports_model.item(index, 0).text() addr = self.imports_model.item(index, 1).text() context_menu.addAction( 'Add Breakpoint', lambda: self._add_breakpoint(addr, func_name)) context_menu.addSeparator() context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self.imports_model.item(index, 1).text())) context_menu.addSeparator() context_menu.addAction( 'Copy FunctionName', lambda: utils.copy_str_to_clipboard( func_name)) context_menu.addAction( 'Copy ModuleName', lambda: utils.copy_str_to_clipboard( self.imports_model.item(index, 2).text())) if self.imports_list.search_enabled: context_menu.addSeparator() context_menu.addAction( 'Search', self.imports_list._on_cm_search) # show context menu glbl_pt = self.imports_list.mapToGlobal(pos) context_menu.exec_(glbl_pt) def _on_exports_contextmenu(self, pos): """ ExportsList ContextMenu """ index = self.exports_list.indexAt(pos).row() if index != -1: context_menu = QMenu(self) func_name = self.exports_model.item(index, 0).text() addr = self.exports_model.item(index, 1).text() context_menu.addAction( 'Add Breakpoint', lambda: self._add_breakpoint(addr, func_name)) context_menu.addSeparator() context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self.exports_model.item(index, 1).text())) context_menu.addSeparator() context_menu.addAction( 'Copy FunctionName', lambda: utils.copy_str_to_clipboard( func_name)) if self.exports_list.search_enabled: context_menu.addSeparator() context_menu.addAction( 'Search', self.exports_list._on_cm_search) # show contextmenu glbl_pt = self.exports_list.mapToGlobal(pos) context_menu.exec_(glbl_pt) def _on_dumpmodule(self, ptr, size): """ MenuItem DumpBinary """ if isinstance(ptr, int): str_fmt = '0x{0:X}' if not self.uppercase_hex: str_fmt = '0x{0:x}' ptr = str_fmt.format(ptr) size = size.replace(',', '') self.onDumpBinary.emit([ptr, size]) def _add_breakpoint(self, ptr, name=None): """ MenuItem AddBreakpoint """ if name is None: name = ptr if isinstance(ptr, str): if ptr.startswith('0x') or ptr.startswith('#'): self.onAddBreakpoint.emit([ptr, name]) elif isinstance(ptr, int): str_fmt = '0x{0:x}' self.onAddBreakpoint.emit(str_fmt.format([ptr, name])) def _on_parse_elf(self, elf_path): from dwarf_debugger.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()
class ContextWidget(QTabWidget): # consts CONTEXT_TYPE_NATIVE = 0 CONTEXT_TYPE_JAVA = 1 onShowMemoryRequest = pyqtSignal(str, name='onShowMemoryRequest') def __init__(self, parent=None): super(ContextWidget, self).__init__(parent=parent) self._app_window = parent self.setAutoFillBackground(True) self._app_window.dwarf.onContextChanged.connect( self._on_context_changed) self._nativectx_model = QStandardItemModel(0, 4) self._nativectx_model.setHeaderData(0, Qt.Horizontal, 'Reg') self._nativectx_model.setHeaderData(0, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._nativectx_model.setHeaderData(1, Qt.Horizontal, 'Value') self._nativectx_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._nativectx_model.setHeaderData(2, Qt.Horizontal, 'Decimal') self._nativectx_model.setHeaderData(3, Qt.Horizontal, 'Telescope') self._nativectx_list = DwarfListView() self._nativectx_list.setModel(self._nativectx_model) self._nativectx_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._nativectx_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self._nativectx_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self._nativectx_list.setContextMenuPolicy(Qt.CustomContextMenu) self._nativectx_list.customContextMenuRequested.connect( self._on_native_contextmenu) self._javactx_model = QStandardItemModel(0, 3) self._javactx_model.setHeaderData(0, Qt.Horizontal, 'Argument') self._javactx_model.setHeaderData(0, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._javactx_model.setHeaderData(1, Qt.Horizontal, 'Class') self._javactx_model.setHeaderData(2, Qt.Horizontal, 'Value') self._javactx_list = DwarfListView() self._javactx_list.setModel(self._javactx_model) self._javactx_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents) self._javactx_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self._javactx_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) self._javactx_list.setContextMenuPolicy(Qt.CustomContextMenu) self._javactx_list.customContextMenuRequested.connect( self._on_java_contextmenu) self.addTab(self._nativectx_list, 'Native') self.show_context_tab('Native') # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def clear(self): self._nativectx_list.clear() self._javactx_list.clear() def set_context(self, ptr, context_type, context): if isinstance(context, str): context = json.loads(context) if context_type == ContextWidget.CONTEXT_TYPE_NATIVE: self._nativectx_list.clear() self._set_native_context(ptr, context) elif context_type == ContextWidget.CONTEXT_TYPE_JAVA: self._javactx_list.clear() self._set_java_context(ptr, context) else: raise Exception('unknown context type') def have_context(self): return self.count() > 0 def show_context_tab(self, tab_name): index = 0 tab_name = tab_name.join(tab_name.split()).lower() if tab_name == 'native': index = self.indexOf(self._nativectx_list) elif tab_name == 'java': index = self.indexOf(self._javactx_list) if self.count() > 0: self.setCurrentIndex(index) def _set_native_context(self, ptr, context): if self.indexOf(self._nativectx_list) == -1: self.addTab(self._nativectx_list, 'Native') self.show_context_tab('Native') else: self.show_context_tab('Native') context_ptr = ptr sorted_regs = self.get_sort_order() for register in sorted(context, key=lambda x: sorted_regs[x] if x in sorted_regs else len(sorted_regs)): reg_name = QStandardItem() reg_name.setTextAlignment(Qt.AlignCenter) if context[register]['isValidPointer']: reg_name.setData(context_ptr, Qt.UserRole + 1) value_x = QStandardItem() if context[register]['isValidPointer']: value_x.setForeground(Qt.red) value_dec = QStandardItem() telescope = QStandardItem() reg_name.setText(register) if context[register] is not None: str_fmt = '0x{0:x}' if self._nativectx_list.uppercase_hex: str_fmt = '0x{0:X}' value_x.setText( str_fmt.format(int(context[register]['value'], 16))) value_dec.setText('{0:d}'.format( int(context[register]['value'], 16))) if context[register]['isValidPointer']: if 'telescope' in context[register] and context[register][ 'telescope'] is not None: telescope = QStandardItem() telescope_value = str( context[register]['telescope'][1]).replace( '\n', ' ') if len(telescope_value) > 50: telescope_value = telescope_value[:50] + '...' telescope.setText(telescope_value) if context[register]['telescope'][0] == 2: telescope.setData( context[register]['telescope'][1], Qt.UserRole + 1) if context[register]['telescope'][0] == 0: telescope.setForeground(Qt.darkGreen) elif context[register]['telescope'][0] == 2: telescope.setForeground(Qt.white) elif context[register]['telescope'][0] != 1: telescope.setForeground(Qt.darkGray) self._nativectx_model.appendRow( [reg_name, value_x, value_dec, telescope]) self._nativectx_list.resizeColumnToContents(0) def _set_java_context(self, ptr, context): if self.indexOf(self._javactx_list) == -1: self.addTab(self._javactx_list, 'Java') self.show_context_tab('Java') else: self.show_context_tab('Java') for arg in context: _arg = QStandardItem() _arg.setText(arg) _class = QStandardItem() _class.setText(context[arg]['className']) if isinstance(context[arg]['handle'], str): _class.setForeground(Qt.lightGray) _value = QStandardItem() if 'arg' not in context[arg] or context[arg]['arg'] is None: _value.setText('null') _value.setForeground(Qt.gray) else: _value.setText(context[arg]['arg']) self._javactx_model.appendRow([_arg, _class, _value]) self._javactx_list.resizeColumnToContents(0) self._javactx_list.resizeColumnToContents(1) def get_sort_order(self): reg_order = [] if self._app_window.dwarf.arch == 'arm': # arm reg_order = [ 'r0', 'r1', 'r2', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'sp', 'lr', 'sb', 'sl', 'fp', 'ip', 'pc', 'cspr' ] elif self._app_window.dwarf.arch == 'arm64': reg_order = [ 'x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12', 'x13', 'x14', 'x15', 'x16', 'x17', 'x18', 'x19', 'x20', 'x21', 'x22', 'x23', 'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30', 'w0', 'w1', 'w2', 'w3', 'w4', 'w5', 'w6', 'w7', 'w8', 'w9', 'w10', 'w11', 'w12', 'w13', 'w14', 'w15', 'w16', 'w17', 'w18', 'w19', 'w20', 'w21', 'w22', 'w23', 'w24', 'w25', 'w26', 'w27', 'w28', 'w29', 'w30', 'sp', 'lr', 'fp', 'wsp', 'wzr', 'xzr', 'nzcv', 'ip0', 'ip1', 's0', 's1', 's2', 's3', 's4', 's5', 's6', 's7', 's8', 's9', 's10', 's11', 's12', 's13', 's14', 's15', 's16', 's17', 's18', 's19', 's20', 's21', 's22', 's23', 's24', 's25', 's26', 's27', 's28', 's29', 's30', 's31', 'd0', 'd1', 'd2', 'd3', 'd4', 'd5', 'd6', 'd7', 'd8', 'd9', 'd10', 'd11', 'd12', 'd13', 'd14', 'd15', 'd16', 'd17', 'd18', 'd19', 'd20', 'd21', 'd22', 'd23', 'd24', 'd25', 'd26', 'd27', 'd28', 'd29', 'd30', 'd31', 'q0', 'q1', 'q2', 'q3', 'q4', 'q5', 'q6', 'q7', 'q8', 'q9', 'q10', 'q11', 'q12', 'q13', 'q14', 'q15', 'q16', 'q17', 'q18', 'q19', 'q20', 'q21', 'q22', 'q23', 'q24', 'q25', 'q26', 'q27', 'q28', 'q29', 'q30', 'q31', 'sp', 'lr', 'sb', 'sl', 'fp', 'ip', 'pc', 'cspr' ] elif self._app_window.dwarf.arch == 'ia32': reg_order = [ 'eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'esp', 'r8d', 'r9d', 'r10d', 'r11d', 'r12d', 'r13d', 'r14d', 'r15d', 'ebp', 'eip', 'sp', 'pc' ] elif self._app_window.dwarf.arch == 'x64': # x64 reg_order = [ 'rax', 'rbx', 'rcx', 'rdx', 'rsi', 'rdi', 'rbp', 'rsp', 'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'esp', 'ebp', 'rip', 'eip', 'sp', 'pc' ] sorted_regs = {b: i for i, b in enumerate(reg_order)} return sorted_regs # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_native_contextmenu(self, pos): index = self._nativectx_list.indexAt(pos).row() glbl_pt = self._nativectx_list.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: item = self._nativectx_model.item(index, 1) dec = self._nativectx_model.item(index, 2) telescope = self._nativectx_model.item(index, 3) # show contextmenu if self._nativectx_model.item(index, 0).data(Qt.UserRole + 1): context_menu.addAction( 'Jump to {0}'.format(item.text()), lambda: self._app_window.jump_to_address(item.text())) context_menu.addSeparator() # copy menu context_sub_menu = QMenu('Copy', context_menu) context_sub_menu.addAction( 'Value', lambda: utils.copy_str_to_clipboard(item.text())) if dec.text(): context_sub_menu.addAction( 'Decimal', lambda: utils.copy_str_to_clipboard(dec.text())) if telescope.text(): context_sub_menu.addAction( 'Telescope', lambda: utils.copy_str_to_clipboard(telescope.text())) context_menu.addMenu(context_sub_menu) context_menu.exec_(glbl_pt) def _on_java_contextmenu(self, pos): index = self._javactx_list.indexAt(pos).row() glbl_pt = self._javactx_list.mapToGlobal(pos) context_menu = QMenu(self) if index != -1: # show contextmenu argument = self._javactx_model.item(index, 1) _class = self._javactx_model.item(index, 2) value = self._javactx_model.item(index, 3) context_sub_menu = QMenu('Copy', context_menu) context_sub_menu.addAction( 'Argument', lambda: utils.copy_str_to_clipboard(argument.text())) if _class.text(): context_sub_menu.addAction( 'Class', lambda: utils.copy_str_to_clipboard(_class.text())) if value: if value.text(): context_sub_menu.addAction( 'Value', lambda: utils.copy_str_to_clipboard(value.text())) context_menu.addMenu(context_sub_menu) context_menu.exec_(glbl_pt) def _on_context_changed(self, reg_name, reg_val): x_in = 0 for c in reg_val: if c.lower() not in '1234567890abcdef': if c.lower() == 'x' and x_in == 0: x_in += 1 continue self._app_window.dwarf.onLogToConsole.emit( 'error: invalid reg_value: ' + reg_val + ' - expected dec/hex') return if isinstance(reg_val, str) and reg_val.startswith('0x'): try: reg_val = int(reg_val, 16) except ValueError: self._app_window.dwarf.onLogToConsole.emit( 'error: invalid reg_value: ' + reg_val + ' - expected dec/hex') return try: reg_val = int(reg_val) except ValueError: self._app_window.dwarf.onLogToConsole.emit( 'error: invalid reg_value: ' + reg_val + ' - expected dec/hex') return reg_val = hex(reg_val) was_found, find_result = self._nativectx_list.contains_text( reg_name, True, True, True) if was_found: if len(find_result) == 1: find_result = find_result[0] if self._nativectx_model.item(find_result[0], 0).text() == reg_name: str_fmt = '0x{0:x}' if self._nativectx_list.uppercase_hex: str_fmt = '0x{0:X}' value_x = str_fmt.format(int(reg_val, 16)) value_dec = '{0:d}'.format(int(reg_val, 16)) self._nativectx_model.item(find_result[0], 1).setText(value_x) self._nativectx_model.item(find_result[0], 2).setText(value_dec) self._nativectx_model.item(find_result[0], 3).setText("")
class BreakpointsWidget(QWidget): """ BreakpointsWidget Signals: onBreakpointChanged(str) - ptr onBreakpointRemoved(str) - ptr """ onBreakpointChanged = pyqtSignal(str, name='onBreakpointChanged') onBreakpointsRemoved = pyqtSignal(str, name='onBreakpointRemoved') def __init__(self, parent=None): # pylint: disable=too-many-statements super(BreakpointsWidget, self).__init__(parent=parent) self._app_window = parent if self._app_window.dwarf is None: print('BreakpointsWidget created before Dwarf exists') return # connect to dwarf self._app_window.dwarf.onApplyContext.connect(self._on_apply_context) self._app_window.dwarf.onAddJavaBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onAddObjCBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onAddNativeBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onAddModuleInitializationBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onAddJavaClassInitializationBreakpoint.connect( self._on_add_breakpoint) self._app_window.dwarf.onHitModuleInitializationBreakpoint.connect( self._on_hit_module_initialization_breakpoint) self._app_window.dwarf.onHitJavaClassInitializationBreakpoint.connect( self._on_hit_java_class_initialization_breakpoint) self._app_window.dwarf.onDeleteBreakpoint.connect( self._on_breakpoint_deleted) self._breakpoints_list = DwarfListView() self._breakpoints_list.doubleClicked.connect(self._on_double_clicked) self._breakpoints_list.setContextMenuPolicy(Qt.CustomContextMenu) self._breakpoints_list.customContextMenuRequested.connect( self._on_context_menu) self._breakpoints_model = QStandardItemModel(0, 3) self._breakpoints_model.setHeaderData(0, Qt.Horizontal, 'Address') self._breakpoints_model.setHeaderData(1, Qt.Horizontal, 'T') self._breakpoints_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._breakpoints_model.setHeaderData(2, Qt.Horizontal, '<>') self._breakpoints_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._breakpoints_list.setModel(self._breakpoints_model) self._breakpoints_list.header().setStretchLastSection(False) self._breakpoints_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents | QHeaderView.Interactive) self._breakpoints_list.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self._breakpoints_list.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents) v_box = QVBoxLayout(self) v_box.setContentsMargins(0, 0, 0, 0) v_box.addWidget(self._breakpoints_list) h_box = QHBoxLayout() h_box.setContentsMargins(5, 2, 5, 5) self.btn1 = QPushButton( QIcon(utils.resource_path('assets/icons/plus.svg')), '') self.btn1.setFixedSize(20, 20) self.btn1.clicked.connect(self._on_add_item_clicked) btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')), '') btn2.setFixedSize(20, 20) btn2.clicked.connect(self.delete_items) btn3 = QPushButton( QIcon(utils.resource_path('assets/icons/trashcan.svg')), '') btn3.setFixedSize(20, 20) btn3.clicked.connect(self.clear_list) h_box.addWidget(self.btn1) h_box.addWidget(btn2) h_box.addSpacerItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred)) h_box.addWidget(btn3) # header.setLayout(h_box) # header.setFixedHeight(25) # v_box.addWidget(header) v_box.addLayout(h_box) self.setLayout(v_box) self._bold_font = QFont(self._breakpoints_list.font()) self._bold_font.setBold(True) shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_N), self._app_window, self._on_add_native_breakpoint) shortcut_addnative.setAutoRepeat(False) shortcut_addjava = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_J), self._app_window, self._on_add_java_breakpoint) shortcut_addjava.setAutoRepeat(False) shortcut_add_native_onload = QShortcut( QKeySequence(Qt.CTRL + Qt.Key_O), self._app_window, self._on_add_module_initialization_breakpoint) shortcut_add_native_onload.setAutoRepeat(False) # new menu self.new_menu = QMenu('New') self.new_menu.addAction('Native', self._on_add_native_breakpoint) self.new_menu.addAction('Module initialization', self._on_add_module_initialization_breakpoint) # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def delete_items(self): """ Delete selected Items """ index = self._breakpoints_list.selectionModel().currentIndex().row() if index != -1: self._on_delete_breakpoint(index) self._breakpoints_model.removeRow(index) def clear_list(self): """ Clear the List """ # go through all items and tell it gets removed for item in range(self._breakpoints_model.rowCount()): self._on_delete_breakpoint(item) if self._breakpoints_model.rowCount() > 0: # something was wrong it should be empty self._breakpoints_model.removeRows( 0, self._breakpoints_model.rowCount()) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_apply_context(self, context): if context['reason'] == -1: if self._app_window.dwarf.java_available: self.new_menu.addAction('Java', self._on_add_java_breakpoint) self.new_menu.addAction( 'Java class initialization', self._on_add_java_class_initialization_breakpoint) def _on_add_breakpoint(self, breakpoint): type_ = QStandardItem() type_.setFont(self._bold_font) type_.setTextAlignment(Qt.AlignCenter) if breakpoint.breakpoint_type == BREAKPOINT_NATIVE: type_.setText('N') type_.setToolTip('Native breakpoint') elif breakpoint.breakpoint_type == BREAKPOINT_JAVA: type_.setText('J') type_.setToolTip('Java breakpoint') elif breakpoint.breakpoint_type == BREAKPOINT_INITIALIZATION: type_.setText('C') type_.setToolTip('Initialization breakpoint') elif breakpoint.breakpoint_type == BREAKPOINT_OBJC: type_.setText('O') type_.setToolTip('ObjC breakpoint') else: type_.setText('U') type_.setToolTip('Unknown Type') addr = QStandardItem() if breakpoint.breakpoint_type == BREAKPOINT_JAVA: addr.setText(breakpoint.get_target()) elif breakpoint.breakpoint_type == BREAKPOINT_OBJC: addr.setText(breakpoint.get_target()) elif breakpoint.breakpoint_type == BREAKPOINT_INITIALIZATION: addr.setText(breakpoint.get_target()) addr.setData(breakpoint.debug_symbol, Qt.UserRole + 2) else: str_fmt = '0x{0:x}' if self._breakpoints_list.uppercase_hex: str_fmt = '0x{0:X}' # addr.setTextAlignment(Qt.AlignCenter) addr.setText(str_fmt.format(breakpoint.get_target())) condition = QStandardItem() condition.setTextAlignment(Qt.AlignCenter) condition.setFont(self._bold_font) if breakpoint.condition and breakpoint.condition != 'null' and breakpoint.condition != 'undefined': condition.setText('ƒ') condition.setToolTip(breakpoint.condition) condition.setData(breakpoint.condition, Qt.UserRole + 2) self._breakpoints_model.appendRow([addr, type_, condition]) self._breakpoints_list.resizeColumnToContents(0) def _on_hit_module_initialization_breakpoint(self, data): items = self._breakpoints_model.findItems(data[1]['module'], Qt.MatchExactly, 2) if len(items) > 0: self._breakpoints_model.item(items[0].row(), 0).setText(data[1]['moduleBase']) def _on_hit_java_class_initialization_breakpoint(self, data): items = self._breakpoints_model.findItems(data[0], Qt.MatchExactly, 2) if len(items) > 0: pass def _on_double_clicked(self, model_index): item = self._breakpoints_model.itemFromIndex(model_index) if model_index.column() == 2 and item.text() == 'ƒ': self._on_modify_condition(model_index.row()) else: self._app_window.jump_to_address(self._breakpoints_model.item( model_index.row(), 0).text(), view=1) def _on_context_menu(self, pos): context_menu = QMenu(self) context_menu.addMenu(self.new_menu) context_menu.addSeparator() index = self._breakpoints_list.indexAt(pos).row() if index != -1: context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self._breakpoints_model.item(index, 0).text())) context_menu.addAction( 'Jump to address', lambda: self._app_window.jump_to_address( self._breakpoints_model.item(index, 0).text())) context_menu.addSeparator() context_menu.addAction('Edit Condition', lambda: self._on_modify_condition(index)) context_menu.addSeparator() context_menu.addAction('Delete Breakpoint', lambda: self._on_delete_breakpoint(index)) if self._breakpoints_list.search_enabled: context_menu.addSeparator() context_menu.addAction('Search', self._breakpoints_list._on_cm_search) # show context menu global_pt = self._breakpoints_list.mapToGlobal(pos) context_menu.exec(global_pt) def _on_modify_condition(self, num_row): item = self._breakpoints_model.item(num_row, 2) data = item.data(Qt.UserRole + 2) if data is None: data = '' ptr = self._breakpoints_model.item(num_row, 0).text() accept, input_ = InputMultilineDialog().input( 'Condition for breakpoint %s' % ptr, input_content=data) if accept: what = utils.parse_ptr(ptr) if what == 0: what = self._breakpoints_model.item(num_row, 2).data(Qt.UserRole + 2) if self._app_window.dwarf.dwarf_api( 'setBreakpointCondition', [what, input_.replace('\n', '')]): item.setData(input_, Qt.UserRole + 2) if not item.text(): item.setText('ƒ') item.setToolTip(input_) self.onBreakpointChanged.emit(ptr) # + button def _on_add_item_clicked(self): self.new_menu.exec_(QCursor.pos()) # shortcuts/menu def _on_add_native_breakpoint(self): self._app_window.dwarf.breakpoint_native() def _on_add_java_breakpoint(self): self._app_window.dwarf.breakpoint_java() def _on_add_module_initialization_breakpoint(self): self._app_window.dwarf.breakpoint_module_initialization() def _on_add_java_class_initialization_breakpoint(self): self._app_window.dwarf.breakpoint_java_class_initialization() def _on_delete_breakpoint(self, num_row): breakpoint_type = self._breakpoints_model.item(num_row, 1).text() if breakpoint_type == 'N': ptr = self._breakpoints_model.item(num_row, 0).text() ptr = utils.parse_ptr(ptr) self._app_window.dwarf.dwarf_api('removeBreakpoint', ptr) self.onBreakpointRemoved.emit(str(ptr)) elif breakpoint_type == 'J': target = self._breakpoints_model.item(num_row, 0).text() self._app_window.dwarf.dwarf_api('removeBreakpoint', target) elif breakpoint_type == 'O': target = self._breakpoints_model.item(num_row, 0).text() self._app_window.dwarf.dwarf_api('removeBreakpoint', target) elif breakpoint_type == 'C': item = self._breakpoints_model.item(num_row, 0) target = item.text() is_native = item.data(Qt.UserRole + 2) is None if is_native: self._app_window.dwarf.dwarf_api( 'removeModuleInitializationBreakpoint', target) else: self._app_window.dwarf.dwarf_api( 'removeJavaClassInitializationBreakpoint', target) elif breakpoint_type == 'U': ptr = self._breakpoints_model.item(num_row, 0).text() ptr = utils.parse_ptr(ptr) self._app_window.dwarf.dwarf_api('removeBreakpoint', ptr) self.onBreakpointRemoved.emit(str(ptr)) def _on_breakpoint_deleted(self, parts): _msg, _type, _val = parts additional = None if _type == 'objc' or _type == 'java' or _type == 'java_class_initialization': str_frmt = _val item_index = 0 elif _type == 'module_initialization': str_frmt = _val item_index = 0 else: _ptr = utils.parse_ptr(_val) if self._breakpoints_list._uppercase_hex: str_frmt = '0x{0:X}'.format(_ptr) else: str_frmt = '0x{0:x}'.format(_ptr) item_index = 0 for _item in range(self._breakpoints_model.rowCount()): item = self._breakpoints_model.item(_item, item_index) if item is None: continue if str_frmt == item.text(): if additional is not None: if additional == self._breakpoints_model.item(_item, 2).text(): self._breakpoints_model.removeRow(_item) else: self._breakpoints_model.removeRow(_item)
class WatchpointsWidget(QWidget): """ WatchpointsWidget Signals: onItemSelected(addr_str) - item dblclicked onItemAddClick Constants: MEMORY_ACCESS_READ = 1 MEMORY_ACCESS_WRITE = 2 MEMORY_ACCESS_EXECUTE = 4 MEMORY_WATCH_SINGLESHOT = 8 """ MEMORY_ACCESS_READ = 1 MEMORY_ACCESS_WRITE = 2 MEMORY_ACCESS_EXECUTE = 4 MEMORY_WATCH_SINGLESHOT = 8 onItemDoubleClicked = pyqtSignal(int, name='onItemDoubleClicked') onItemAdded = pyqtSignal(int, name='onItemAdded') onItemRemoved = pyqtSignal(int, name='onItemRemoved') def __init__(self, parent=None): # pylint: disable=too-many-statements super(WatchpointsWidget, self).__init__(parent=parent) self._app_window = parent if self._app_window.dwarf is None: print('WatchpointsWidget created before Dwarf exists') return self._uppercase_hex = True self.setAutoFillBackground(True) # connect to dwarf self._app_window.dwarf.onWatchpointAdded.connect( self._on_watchpoint_added) self._app_window.dwarf.onWatchpointRemoved.connect( self._on_watchpoint_removed) # setup our model self._watchpoints_model = QStandardItemModel(0, 5) self._watchpoints_model.setHeaderData(0, Qt.Horizontal, 'Address') self._watchpoints_model.setHeaderData(1, Qt.Horizontal, 'R') self._watchpoints_model.setHeaderData(1, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._watchpoints_model.setHeaderData(2, Qt.Horizontal, 'W') self._watchpoints_model.setHeaderData(2, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._watchpoints_model.setHeaderData(3, Qt.Horizontal, 'X') self._watchpoints_model.setHeaderData(3, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) self._watchpoints_model.setHeaderData(4, Qt.Horizontal, 'S') self._watchpoints_model.setHeaderData(4, Qt.Horizontal, Qt.AlignCenter, Qt.TextAlignmentRole) # setup ui v_box = QVBoxLayout(self) v_box.setContentsMargins(0, 0, 0, 0) self.list_view = DwarfListView() self.list_view.setModel(self._watchpoints_model) self.list_view.header().setSectionResizeMode(0, QHeaderView.Stretch) self.list_view.header().setSectionResizeMode( 1, QHeaderView.ResizeToContents | QHeaderView.Fixed) self.list_view.header().setSectionResizeMode( 2, QHeaderView.ResizeToContents | QHeaderView.Fixed) self.list_view.header().setSectionResizeMode( 3, QHeaderView.ResizeToContents | QHeaderView.Fixed) self.list_view.header().setSectionResizeMode( 4, QHeaderView.ResizeToContents | QHeaderView.Fixed) self.list_view.header().setStretchLastSection(False) self.list_view.doubleClicked.connect(self._on_item_dblclick) self.list_view.setContextMenuPolicy(Qt.CustomContextMenu) self.list_view.customContextMenuRequested.connect(self._on_contextmenu) v_box.addWidget(self.list_view) #header = QHeaderView(Qt.Horizontal, self) h_box = QHBoxLayout() h_box.setContentsMargins(5, 2, 5, 5) btn1 = QPushButton(QIcon(utils.resource_path('assets/icons/plus.svg')), '') btn1.setFixedSize(20, 20) btn1.clicked.connect(self._on_additem_clicked) btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')), '') btn2.setFixedSize(20, 20) btn2.clicked.connect(self.delete_items) btn3 = QPushButton( QIcon(utils.resource_path('assets/icons/trashcan.svg')), '') btn3.setFixedSize(20, 20) btn3.clicked.connect(self.clear_list) h_box.addWidget(btn1) h_box.addWidget(btn2) h_box.addSpacerItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred)) h_box.addWidget(btn3) # header.setLayout(h_box) # header.setFixedHeight(25) # v_box.addWidget(header) v_box.addLayout(h_box) # create a centered dot icon _section_width = self.list_view.header().sectionSize(2) self._new_pixmap = QPixmap(_section_width, 20) self._new_pixmap.fill(Qt.transparent) painter = QPainter(self._new_pixmap) rect = QRect(int(_section_width * 0.5), 0, 20, 20) painter.setBrush(QColor('#666')) painter.setPen(QColor('#666')) painter.drawEllipse(rect) self._dot_icon = QIcon(self._new_pixmap) # shortcuts shortcut_add = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_W), self._app_window, self._on_additem_clicked) shortcut_add.setAutoRepeat(False) self.setLayout(v_box) # ************************************************************************ # **************************** Properties ******************************** # ************************************************************************ @property def uppercase_hex(self): """ Addresses displayed lower/upper-case """ return self._uppercase_hex @uppercase_hex.setter def uppercase_hex(self, value): """ Addresses displayed lower/upper-case value - bool or str 'upper', 'lower' """ if isinstance(value, bool): self._uppercase_hex = value elif isinstance(value, str): self._uppercase_hex = (value == 'upper') # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def do_addwatchpoint_dlg(self, ptr=None): # pylint: disable=too-many-branches """ Shows AddWatchpointDialog """ watchpoint_dlg = AddWatchpointDialog(self, ptr) if watchpoint_dlg.exec_() == QDialog.Accepted: mem_r = watchpoint_dlg.acc_read.isChecked() mem_w = watchpoint_dlg.acc_write.isChecked() mem_x = watchpoint_dlg.acc_execute.isChecked() mem_s = watchpoint_dlg.singleshot.isChecked() ptr = watchpoint_dlg.text_field.toPlainText() if ptr: if isinstance(ptr, str): if ptr.startswith('0x') or ptr.startswith('#'): ptr = utils.parse_ptr(ptr) else: try: ptr = int(ptr, 10) except ValueError: pass # int now? if not isinstance(ptr, int): try: ptr = int( self._app_window.dwarf.dwarf_api( 'evaluatePtr', ptr), 16) except ValueError: ptr = 0 if ptr == 0: return if not self._app_window.dwarf.dwarf_api( 'isValidPointer', ptr): return else: return mem_val = 0 if mem_r: mem_val |= self.MEMORY_ACCESS_READ if mem_w: mem_val |= self.MEMORY_ACCESS_WRITE if mem_x: mem_val |= self.MEMORY_ACCESS_EXECUTE if mem_s: mem_val |= self.MEMORY_WATCH_SINGLESHOT self.add_address(ptr, mem_val, from_api=False) # return [ptr, mem_val] def add_address(self, ptr, flags, from_api=False): """ Adds Address to display ptr - str or int flags - int """ if isinstance(ptr, str): ptr = utils.parse_ptr(ptr) if not isinstance(flags, int): try: flags = int(flags, 10) except ValueError: flags = 3 if not from_api: # function was called directly so add it to dwarf if not self._app_window.dwarf.is_address_watched(ptr): self._app_window.dwarf.dwarf_api('putWatchpoint', [ptr, flags]) return # show header self.list_view.setHeaderHidden(False) # create items to add if self._uppercase_hex: str_frmt = '0x{0:X}' else: str_frmt = '0x{0:x}' addr = QStandardItem() addr.setText(str_frmt.format(ptr)) read = QStandardItem() write = QStandardItem() execute = QStandardItem() singleshot = QStandardItem() if flags & self.MEMORY_ACCESS_READ: read.setIcon(self._dot_icon) if flags & self.MEMORY_ACCESS_WRITE: write.setIcon(self._dot_icon) if flags & self.MEMORY_ACCESS_EXECUTE: execute.setIcon(self._dot_icon) if flags & self.MEMORY_WATCH_SINGLESHOT: singleshot.setIcon(self._dot_icon) # add items as new row on top self._watchpoints_model.insertRow( 0, [addr, read, write, execute, singleshot]) def remove_address(self, ptr, from_api=False): """ Remove Address from List """ if isinstance(ptr, str): ptr = utils.parse_ptr(ptr) if not from_api: # called somewhere so remove watchpoint in dwarf too self._app_window.dwarf.dwarf_api('removeWatchpoint', ptr) return str_frmt = '' if self._uppercase_hex: str_frmt = '0x{0:X}'.format(ptr) else: str_frmt = '0x{0:x}'.format(ptr) model = self.list_view.model() for item in range(model.rowCount()): if str_frmt == model.item(item).text(): model.removeRow(item) def delete_items(self): """ Delete selected Items """ model = self.list_view.model() index = self.list_view.selectionModel().currentIndex().row() if index != -1: ptr = model.item(index, 0).text() self.remove_address(ptr) def clear_list(self): """ Clear the List """ model = self.list_view.model() # go through all items and tell it gets removed for item in range(model.rowCount()): ptr = model.item(item, 0).text() self.remove_address(ptr) if model.rowCount() > 0: # something was wrong it should be empty model.removeRows(0, model.rowCount()) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_contextmenu(self, pos): index = self.list_view.indexAt(pos).row() glbl_pt = self.list_view.mapToGlobal(pos) context_menu = QMenu(self) context_menu.addAction('Add watchpoint', self._on_additem_clicked) if index != -1: context_menu.addSeparator() context_menu.addAction( 'Copy address', lambda: utils.copy_hex_to_clipboard( self._watchpoints_model.item(index, 0).text())) context_menu.addAction( 'Jump to address', lambda: self._app_window.jump_to_address( self._watchpoints_model.item(index, 0).text())) context_menu.addAction( 'Delete watchpoint', lambda: self.remove_address( self._watchpoints_model.item(index, 0).text())) if self.list_view.search_enabled: context_menu.addSeparator() context_menu.addAction('Search', self.list_view._on_cm_search) context_menu.exec_(glbl_pt) def _on_item_dblclick(self, model_index): row = self._watchpoints_model.itemFromIndex(model_index).row() if row != -1: ptr = self._watchpoints_model.item(row, 0).text() self.onItemDoubleClicked.emit(ptr) def _on_additem_clicked(self): if self._app_window.dwarf.pid == 0: return self.do_addwatchpoint_dlg() def _on_watchpoint_added(self, watchpoint): """ Callback from Dwarf after Watchpoint is added """ # add to watchpointslist self.add_address(watchpoint.address, watchpoint.flags, from_api=True) self.onItemAdded.emit(watchpoint.address) def _on_watchpoint_removed(self, ptr): """ Callback from Dwarf after watchpoint is removed """ ptr = utils.parse_ptr(ptr) # remove from list self.remove_address(ptr, from_api=True) self.onItemRemoved.emit(ptr)
class BookmarksWidget(QWidget): def __init__(self, parent=None): # pylint: disable=too-many-statements super(BookmarksWidget, self).__init__(parent=parent) self._app_window = parent if self._app_window.dwarf is None: print('BookmarksPanel created before Dwarf exists') return self.bookmarks = {} self._bookmarks_list = DwarfListView() self._bookmarks_list.doubleClicked.connect(self._on_double_clicked) self._bookmarks_list.setContextMenuPolicy(Qt.CustomContextMenu) self._bookmarks_list.customContextMenuRequested.connect( self._on_contextmenu) self._bookmarks_model = QStandardItemModel(0, 2) self._bookmarks_model.setHeaderData(0, Qt.Horizontal, 'Address') self._bookmarks_model.setHeaderData(1, Qt.Horizontal, 'Notes') self._bookmarks_list.setModel(self._bookmarks_model) self._bookmarks_list.header().setStretchLastSection(False) self._bookmarks_list.header().setSectionResizeMode( 0, QHeaderView.ResizeToContents | QHeaderView.Interactive) self._bookmarks_list.header().setSectionResizeMode( 1, QHeaderView.Stretch | QHeaderView.Interactive) v_box = QVBoxLayout(self) v_box.setContentsMargins(0, 0, 0, 0) v_box.addWidget(self._bookmarks_list) #header = QHeaderView(Qt.Horizontal, self) h_box = QHBoxLayout() h_box.setContentsMargins(5, 2, 5, 5) self.btn1 = QPushButton( QIcon(utils.resource_path('assets/icons/plus.svg')), '') self.btn1.setFixedSize(20, 20) self.btn1.clicked.connect(lambda: self._create_bookmark(-1)) btn2 = QPushButton(QIcon(utils.resource_path('assets/icons/dash.svg')), '') btn2.setFixedSize(20, 20) btn2.clicked.connect(self.delete_items) btn3 = QPushButton( QIcon(utils.resource_path('assets/icons/trashcan.svg')), '') btn3.setFixedSize(20, 20) btn3.clicked.connect(self.clear_list) h_box.addWidget(self.btn1) h_box.addWidget(btn2) h_box.addSpacerItem( QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Preferred)) h_box.addWidget(btn3) # header.setLayout(h_box) # header.setFixedHeight(25) # v_box.addWidget(header) v_box.addLayout(h_box) self.setLayout(v_box) self._bold_font = QFont(self._bookmarks_list.font()) self._bold_font.setBold(True) shortcut_addnative = QShortcut(QKeySequence(Qt.CTRL + Qt.Key_B), self._app_window, self._create_bookmark) shortcut_addnative.setAutoRepeat(False) # ************************************************************************ # **************************** Functions ********************************* # ************************************************************************ def delete_items(self): """ Delete selected Items """ index = self._bookmarks_list.selectionModel().currentIndex().row() if index != -1: self._on_delete_bookmark(index) self._bookmarks_model.removeRow(index) def clear_list(self): """ Clear the List """ # go through all items and tell it gets removed for item in range(self._bookmarks_model.rowCount()): self._on_delete_bookmark(item) if self._bookmarks_model.rowCount() > 0: # something was wrong it should be empty self._bookmarks_model.removeRows(0, self._bookmarks_model.rowCount()) # ************************************************************************ # **************************** Handlers ********************************** # ************************************************************************ def _on_double_clicked(self, index): index = self._bookmarks_list.selectionModel().currentIndex().row() if index != -1: addr = self._bookmarks_model.item(index, 0).text() if addr: self._app_window.jump_to_address(addr, view=1) 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 breakpoint 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: ptr = utils.parse_ptr(ptr) 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 != '' if note == '': 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) def is_address_bookmarked(self, ptr): return utils.parse_ptr(ptr) in self.bookmarks # 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)