class FilterTab(QWidget): """This class is the GUI for creating filters for the FilterBox module. This GUI will be added as a tab to the ModuleFrame tab dialog. """ applySignal = Signal(Clause) def __init__(self, parent, mframe, existing_filters): """Create a FilterTab with the given parent TabDialog, logical parent ModuleFrame mframe, and existing_filters list of Clause objects. """ super(FilterTab, self).__init__(parent) self.mframe = mframe self.parent = parent self.attributes = self.mframe.agent.datatree.generateAttributeList() self.clause_list = list() self.clause_dict = dict() # Right now we only look at the first passed in filter. # TODO: At GUI to switch between existing filters if existing_filters is not None and len(existing_filters) > 0: for clause in existing_filters[0].conditions.clauses: self.clause_list.append(str(clause)) self.clause_dict[str(clause)] = clause self.clause_model = QStringListModel(self.clause_list) layout = QVBoxLayout(self) self.sidesplitter = QSplitter(Qt.Horizontal) # You can only select one attribute at a time to build the # filter clauses self.data_view = QTreeView(self) self.data_view.setModel(self.mframe.agent.datatree) self.data_view.setDragEnabled(True) self.data_view.setDropIndicatorShown(True) self.data_view.expandAll() self.sidesplitter.addWidget(self.data_view) self.sidesplitter.setStretchFactor(1,1) self.filter_widget = self.buildFilterWidget() self.sidesplitter.addWidget(self.filter_widget) self.sidesplitter.setStretchFactor(1,0) layout.addWidget(self.sidesplitter) # Apply buttons buttonWidget = QWidget() buttonLayout = QHBoxLayout(buttonWidget) self.applyButton = QPushButton("Apply") self.applyButton.clicked.connect(self.applyFilter) self.closeButton = QPushButton("Apply & Close") self.closeButton.clicked.connect(self.applyCloseFilter) buttonLayout.addWidget(self.applyButton) buttonLayout.addWidget(self.closeButton) buttonWidget.setLayout(buttonLayout) layout.addWidget(buttonWidget) self.setLayout(layout) def applyFilter(self): """Emits the applySignal with the Clause object currently represented by this FilterTab. """ num_clauses = len(self.clause_list) if num_clauses == 0: self.applySignal.emit(None) else: self.applySignal.emit(Clause("and", *self.clause_dict.values())) def applyCloseFilter(self): """Calls applyFilter and then closes the containing TabDialog.""" self.applyFilter() self.parent.close() def buildFilterWidget(self): """Creates the filter portion of the widget by laying out the subwidgets for relations, workspace and existing clauses. """ filter_widget = QWidget() filter_layout = QVBoxLayout(filter_widget) filter_layout.addWidget(self.buildRelationsWidget()) filter_layout.addItem(QSpacerItem(5,5)) filter_layout.addWidget(self.buildWorkFrame()) filter_layout.addItem(QSpacerItem(5,5)) filter_layout.addWidget(self.buildFilterListView()) filter_widget.setLayout(filter_layout) return filter_widget def buildFilterListView(self): """Creates the QListView that contains all of the basic Clause objects. """ groupBox = QGroupBox("Clauses") layout = QVBoxLayout(groupBox) self.list_view = QListView(groupBox) self.list_view.setModel(self.clause_model) layout.addWidget(self.list_view) layout.addItem(QSpacerItem(5,5)) self.delButton = QPushButton("Remove Selected Clause") self.delButton.clicked.connect(self.deleteClause) layout.addWidget(self.delButton) groupBox.setLayout(layout) return groupBox def buildWorkFrame(self): """Creates the grouped set of widgets that allow users to build basic Clause objects. """ groupBox = QGroupBox("Clause Workspace") layout = QHBoxLayout(groupBox) attributeCompleter = QCompleter(self.attributes) attributeCompleter.setCompletionMode(QCompleter.InlineCompletion) self.dropAttribute = DropLineEdit(self, self.mframe.agent.datatree, "", attributeCompleter) self.dropRelation = DropTextLabel("__") self.dropValue = FilterValueLineEdit(groupBox, self.mframe.agent.datatree, self.dropAttribute) # Clear dropValue when dropAttribute changes self.dropAttribute.textChanged.connect(self.dropValue.clear) # Enter in dropValue works like addButton self.dropValue.returnPressed.connect(self.addClause) self.addButton = QPushButton("Add", groupBox) self.addButton.clicked.connect(self.addClause) layout.addWidget(self.dropAttribute) layout.addItem(QSpacerItem(5,5)) layout.addWidget(self.dropRelation) layout.addItem(QSpacerItem(5,5)) layout.addWidget(self.dropValue) layout.addItem(QSpacerItem(5,5)) layout.addWidget(self.addButton) groupBox.setLayout(layout) return groupBox def buildRelationsWidget(self): """Creates the set of draggable relations. These relations are whatever is available in the relations dict of Table. """ relations_widget = QWidget() layout = QHBoxLayout(relations_widget) for relation in Table.relations: layout.addWidget(DragTextLabel(relation)) relations_widget.setLayout(layout) return relations_widget def addClause(self): """Adds a basic Clause to the current filter.""" if self.dropRelation.text() in Table.relations \ and len(self.dropValue.text()) > 0 \ and len(self.dropAttribute.text()) > 0: clause = Clause(self.dropRelation.text(), TableAttribute(self.dropAttribute.text()), self.dropValue.text()) # Guard double add if str(clause) not in self.clause_dict: self.clause_list.append(str(clause)) self.clause_dict[str(clause)] = clause self.clause_model.setStringList(self.clause_list) def deleteClause(self): """Removes the selected basic Clause objects from the current filter. """ clause = self.clause_model.data( self.list_view.selectedIndexes()[0], Qt.DisplayRole) if clause is not None and clause in self.clause_list: self.clause_list.remove(clause) del self.clause_dict[clause] self.clause_model.setStringList(self.clause_list)
class AutoREView(idaapi.PluginForm): ADDR_ROLE = QtCore.Qt.UserRole + 1 def __init__(self, data): super(AutoREView, self).__init__() self._data = data self.tv = None self._model = None def Show(self): return idaapi.PluginForm.Show(self, 'AutoRE', options=idaapi.PluginForm.FORM_PERSIST) def OnCreate(self, form): if HAS_PYSIDE: self.parent = self.FormToPySideWidget(form) else: self.parent = self.FormToPyQtWidget(form) self._idp_hooks = AutoReIDPHooks(self) if not self._idp_hooks.hook(): print 'IDP_Hooks.hook() failed' self.tv = QTreeView() self.tv.setExpandsOnDoubleClick(False) root_layout = QVBoxLayout(self.parent) # self.le_filter = QLineEdit(self.parent) # root_layout.addWidget(self.le_filter) root_layout.addWidget(self.tv) self.parent.setLayout(root_layout) self._model = QtGui.QStandardItemModel() self._init_model() self.tv.setModel(self._model) self.tv.setColumnWidth(0, 200) self.tv.setColumnWidth(1, 300) self.tv.header().setStretchLastSection(True) self.tv.expandAll() self.tv.doubleClicked.connect(self.on_navigate_to_method_requested) # self.le_filter.textChanged.connect(self.on_filter_text_changed) self.tv.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.tv.customContextMenuRequested.connect(self._tree_customContextMenuRequesssted) rename_action = QAction('Rename...', self.tv) rename_action.setShortcut('n') rename_action.triggered.connect(self._tv_rename_action_triggered) self.tv.addAction(rename_action) # def __event_filter(self, source, event): # if event.type() == QtCore.QEvent. def _tree_customContextMenuRequesssted(self, pos): idx = self.tv.indexAt(pos) if not idx.isValid(): return addr = idx.data(role=self.ADDR_ROLE) if not addr: return name_idx = idx.sibling(idx.row(), 1) old_name = name_idx.data() menu = QMenu() rename_action = menu.addAction('Rename `%s`...' % old_name) rename_action.setShortcut('n') action = menu.exec_(self.tv.mapToGlobal(pos)) if action == rename_action: return self._rename_ea_requested(addr, name_idx) def _tv_rename_action_triggered(self): selected = self.tv.selectionModel().selectedIndexes() if not selected: return idx = selected[0] if not idx.isValid(): return addr = idx.data(role=self.ADDR_ROLE) if not addr: return name_idx = idx.sibling(idx.row(), 1) if not name_idx.isValid(): return return self._rename_ea_requested(addr, name_idx) def _rename_ea_requested(self, addr, name_idx): old_name = name_idx.data() if idaapi.IDA_SDK_VERSION >= 700: new_name = idaapi.ask_str(str(old_name), 0, 'New name:') else: new_name = idaapi.askstr(0, str(old_name), 'New name:') if new_name is None: return self._rename(addr, new_name) renamed_name = idaapi.get_ea_name(addr) name_idx.model().setData(name_idx, renamed_name) @classmethod def _rename(cls, ea, new_name): if not ea or ea == idaapi.BADADDR: return if idaapi.IDA_SDK_VERSION >= 700: return idaapi.force_name(ea, new_name, idaapi.SN_NOCHECK) return idaapi.do_name_anyway(ea, new_name, 0) def OnClose(self, form): if self._idp_hooks: self._idp_hooks.unhook() def _tv_init_header(self, model): item_header = QtGui.QStandardItem("EA") item_header.setToolTip("Address") model.setHorizontalHeaderItem(0, item_header) item_header = QtGui.QStandardItem("Function name") model.setHorizontalHeaderItem(1, item_header) item_header = QtGui.QStandardItem("API called") model.setHorizontalHeaderItem(2, item_header) # noinspection PyMethodMayBeStatic def _tv_make_tag_item(self, name): rv = QtGui.QStandardItem(name) rv.setEditable(False) return [rv, QtGui.QStandardItem(), QtGui.QStandardItem()] def _tv_make_ref_item(self, tag, ref): ea_item = QtGui.QStandardItem(('%0' + get_addr_width() + 'X') % ref['ea']) ea_item.setEditable(False) ea_item.setData(ref['ea'], self.ADDR_ROLE) name_item = QtGui.QStandardItem(ref['name']) name_item.setEditable(False) name_item.setData(ref['ea'], self.ADDR_ROLE) apis = ', '.join(ref['tags'][tag]) api_name = QtGui.QStandardItem(apis) api_name.setEditable(False) api_name.setData(ref['ea'], self.ADDR_ROLE) api_name.setToolTip(apis) return [ea_item, name_item, api_name] def _init_model(self): self._model.clear() root_node = self._model.invisibleRootItem() self._tv_init_header(self._model) for tag, refs in self._data.items(): item_tag_list = self._tv_make_tag_item(tag) item_tag = item_tag_list[0] root_node.appendRow(item_tag_list) for ref in refs: ref_item_list = self._tv_make_ref_item(tag, ref) item_tag.appendRow(ref_item_list) def on_navigate_to_method_requested(self, index): addr = index.data(role=self.ADDR_ROLE) if addr is not None: idaapi.jumpto(addr)
class AutoREView(idaapi.PluginForm): ADDR_ROLE = QtCore.Qt.UserRole + 1 def __init__(self, data): super(AutoREView, self).__init__() self._data = data def Show(self): return idaapi.PluginForm.Show(self, 'AutoRE', options=idaapi.PluginForm.FORM_PERSIST) def OnCreate(self, form): # if HAS_PYSIDE: # self.parent = self.FormToPySideWidget(form) # else: self.parent = self.FormToPyQtWidget(form) self.tv = QTreeView() self.tv.setExpandsOnDoubleClick(False) root_layout = QVBoxLayout(self.parent) # self.le_filter = QLineEdit(self.parent) # root_layout.addWidget(self.le_filter) root_layout.addWidget(self.tv) self.parent.setLayout(root_layout) self._model = QtGui.QStandardItemModel() self._init_model() self.tv.setModel(self._model) self.tv.setColumnWidth(0, 200) self.tv.setColumnWidth(1, 300) self.tv.header().setStretchLastSection(True) self.tv.expandAll() self.tv.doubleClicked.connect(self.on_navigate_to_method_requested) # self.le_filter.textChanged.connect(self.on_filter_text_changed) def OnClose(self, form): # print 'TODO: OnClose(): clear the pointer to form in the plugin' pass def _tv_init_header(self, model): item_header = QtGui.QStandardItem("EA") item_header.setToolTip("Address") model.setHorizontalHeaderItem(0, item_header) item_header = QtGui.QStandardItem("Function name") model.setHorizontalHeaderItem(1, item_header) item_header = QtGui.QStandardItem("API called") model.setHorizontalHeaderItem(2, item_header) def _tv_make_tag_item(self, name): rv = QtGui.QStandardItem(name) rv.setEditable(False) return [rv, QtGui.QStandardItem(), QtGui.QStandardItem()] def _tv_make_ref_item(self, tag, ref): ea_item = QtGui.QStandardItem( ('%0' + get_addr_width() + 'X') % ref['ea']) ea_item.setEditable(False) ea_item.setData(ref['ea'], self.ADDR_ROLE) name_item = QtGui.QStandardItem(ref['name']) name_item.setEditable(False) name_item.setData(ref['ea'], self.ADDR_ROLE) apis = ', '.join(ref['tags'][tag]) api_name = QtGui.QStandardItem(apis) api_name.setEditable(False) api_name.setData(ref['ea'], self.ADDR_ROLE) api_name.setToolTip(apis) return [ea_item, name_item, api_name] def _init_model(self): self._model.clear() root_node = self._model.invisibleRootItem() self._tv_init_header(self._model) for tag, refs in self._data.items(): item_tag_list = self._tv_make_tag_item(tag) item_tag = item_tag_list[0] root_node.appendRow(item_tag_list) for ref in refs: ref_item_list = self._tv_make_ref_item(tag, ref) item_tag.appendRow(ref_item_list) def on_navigate_to_method_requested(self, index): addr = index.data(role=self.ADDR_ROLE) if addr is not None: idaapi.jumpto(addr)