Merge pull request #287 from fireeye/fix-286

fix 286
This commit is contained in:
Willi Ballenthin
2020-09-02 14:50:24 -06:00
committed by GitHub
4 changed files with 26 additions and 72 deletions

View File

@@ -51,7 +51,6 @@ class CapaExplorerForm(idaapi.PluginForm):
self.view_limit_results_by_function = None
self.view_search_bar = None
self.view_tree = None
self.view_summary = None
self.view_attack = None
self.view_tabs = None
self.view_menu_bar = None
@@ -94,20 +93,15 @@ class CapaExplorerForm(idaapi.PluginForm):
self.search_model_proxy = CapaExplorerSearchProxyModel()
self.search_model_proxy.setSourceModel(self.range_model_proxy)
# load tree
self.view_tree = CapaExplorerQtreeView(self.search_model_proxy, self.parent)
# load summary table
self.load_view_summary()
self.load_view_attack()
# load parent tab and children tab views
self.load_view_tabs()
self.load_view_checkbox_limit_by()
self.load_view_search_bar()
self.load_view_summary_tab()
self.load_view_attack_tab()
self.load_view_tree_tab()
self.load_view_attack_tab()
# load menu bar and sub menus
self.load_view_menu_bar()
@@ -128,28 +122,6 @@ class CapaExplorerForm(idaapi.PluginForm):
bar = QtWidgets.QMenuBar()
self.view_menu_bar = bar
def load_view_summary(self):
""" load capa summary table """
table_headers = [
"Capability",
"Namespace",
]
table = QtWidgets.QTableWidget()
table.setColumnCount(len(table_headers))
table.verticalHeader().setVisible(False)
table.setSortingEnabled(False)
table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
table.setFocusPolicy(QtCore.Qt.NoFocus)
table.setSelectionMode(QtWidgets.QAbstractItemView.NoSelection)
table.setHorizontalHeaderLabels(table_headers)
table.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignLeft)
table.setShowGrid(False)
table.setStyleSheet("QTableWidget::item { padding: 25px; }")
self.view_summary = table
def load_view_attack(self):
""" load MITRE ATT&CK table """
table_headers = [
@@ -209,16 +181,6 @@ class CapaExplorerForm(idaapi.PluginForm):
self.view_tabs.addTab(tab, "Tree View")
def load_view_summary_tab(self):
""" load capa summary tab view """
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.view_summary)
tab = QtWidgets.QWidget()
tab.setLayout(layout)
self.view_tabs.addTab(tab, "Summary")
def load_view_attack_tab(self):
""" load MITRE ATT&CK tab view """
layout = QtWidgets.QVBoxLayout()
@@ -409,7 +371,6 @@ class CapaExplorerForm(idaapi.PluginForm):
self.doc = capa.render.convert_capabilities_to_result_document(meta, rules, capabilities)
self.model_data.render_capa_doc(self.doc)
self.render_capa_doc_summary()
self.render_capa_doc_mitre_summary()
self.set_view_tree_default_sort_order()
@@ -420,24 +381,6 @@ class CapaExplorerForm(idaapi.PluginForm):
""" set capa tree view default sort order """
self.view_tree.sortByColumn(CapaExplorerDataModel.COLUMN_INDEX_RULE_INFORMATION, QtCore.Qt.AscendingOrder)
def render_capa_doc_summary(self):
""" render capa summary results """
for (row, rule) in enumerate(rutils.capability_rules(self.doc)):
count = len(rule["matches"])
if count == 1:
capability = rule["meta"]["name"]
else:
capability = "%s (%d matches)" % (rule["meta"]["name"], count)
self.view_summary.setRowCount(row + 1)
self.view_summary.setItem(row, 0, self.render_new_table_header_item(capability))
self.view_summary.setItem(row, 1, QtWidgets.QTableWidgetItem(rule["meta"]["namespace"]))
# resize columns to content
self.view_summary.resizeColumnsToContents()
def render_capa_doc_mitre_summary(self):
""" render capa MITRE ATT&CK results """
tactics = collections.defaultdict(set)
@@ -511,7 +454,6 @@ class CapaExplorerForm(idaapi.PluginForm):
self.range_model_proxy.invalidate()
self.search_model_proxy.invalidate()
self.model_data.clear()
self.view_summary.setRowCount(0)
self.load_capa_results()
logger.debug("%s reload completed", self.form_title)

View File

@@ -147,10 +147,10 @@ class CapaExplorerRuleItem(CapaExplorerDataItem):
fmt = "%s (%d matches)"
def __init__(self, parent, display, count, source):
def __init__(self, parent, name, namespace, count, source):
""" """
display = self.fmt % (display, count) if count > 1 else display
super(CapaExplorerRuleItem, self).__init__(parent, [display, "", ""])
display = self.fmt % (name, count) if count > 1 else name
super(CapaExplorerRuleItem, self).__init__(parent, [display, "", namespace])
self._source = source
@property

View File

@@ -450,7 +450,11 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
self.beginResetModel()
for rule in rutils.capability_rules(doc):
parent = CapaExplorerRuleItem(self.root_node, rule["meta"]["name"], len(rule["matches"]), rule["source"])
rule_name = rule["meta"]["name"]
rule_namespace = rule["meta"].get("namespace")
parent = CapaExplorerRuleItem(
self.root_node, rule_name, rule_namespace, len(rule["matches"]), rule["source"]
)
for (location, match) in doc["rules"][rule["meta"]["name"]]["matches"].items():
if rule["meta"]["scope"] == capa.rules.FILE_SCOPE:

View File

@@ -116,7 +116,7 @@ class CapaExplorerRangeProxyModel(QtCore.QSortFilterProxyModel):
class CapaExplorerSearchProxyModel(QtCore.QSortFilterProxyModel):
"""A SortFilterProxyModel that accepts rows with a substring match for a configurable query.
Looks for matches in the RULE_INFORMATION column (e.g. column 0).
Looks for matches in the text of all rows.
Displays the entire tree row if any of the tree branches,
that is, you can filter by rule name, or also
filter by "characteristic(nzxor)" to filter matches with some feature.
@@ -175,17 +175,25 @@ class CapaExplorerSearchProxyModel(QtCore.QSortFilterProxyModel):
source_model = self.sourceModel()
index = source_model.index(row, 0, parent)
for column in (
CapaExplorerDataModel.COLUMN_INDEX_RULE_INFORMATION,
CapaExplorerDataModel.COLUMN_INDEX_VIRTUAL_ADDRESS,
CapaExplorerDataModel.COLUMN_INDEX_DETAILS,
):
index = source_model.index(row, column, parent)
data = source_model.data(index, Qt.DisplayRole)
if not data:
return False
continue
if not isinstance(data, str):
# sanity check: should already be a string, but double check
return False
continue
return self.query in data
if self.query in data:
return True
return False
def set_query(self, query):
self.query = query