mirror of
https://github.com/mandiant/capa.git
synced 2025-12-12 23:59:48 -08:00
@@ -51,7 +51,6 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
self.view_limit_results_by_function = None
|
self.view_limit_results_by_function = None
|
||||||
self.view_search_bar = None
|
self.view_search_bar = None
|
||||||
self.view_tree = None
|
self.view_tree = None
|
||||||
self.view_summary = None
|
|
||||||
self.view_attack = None
|
self.view_attack = None
|
||||||
self.view_tabs = None
|
self.view_tabs = None
|
||||||
self.view_menu_bar = None
|
self.view_menu_bar = None
|
||||||
@@ -94,20 +93,15 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
self.search_model_proxy = CapaExplorerSearchProxyModel()
|
self.search_model_proxy = CapaExplorerSearchProxyModel()
|
||||||
self.search_model_proxy.setSourceModel(self.range_model_proxy)
|
self.search_model_proxy.setSourceModel(self.range_model_proxy)
|
||||||
|
|
||||||
# load tree
|
|
||||||
self.view_tree = CapaExplorerQtreeView(self.search_model_proxy, self.parent)
|
self.view_tree = CapaExplorerQtreeView(self.search_model_proxy, self.parent)
|
||||||
|
|
||||||
# load summary table
|
|
||||||
self.load_view_summary()
|
|
||||||
self.load_view_attack()
|
self.load_view_attack()
|
||||||
|
|
||||||
# load parent tab and children tab views
|
# load parent tab and children tab views
|
||||||
self.load_view_tabs()
|
self.load_view_tabs()
|
||||||
self.load_view_checkbox_limit_by()
|
self.load_view_checkbox_limit_by()
|
||||||
self.load_view_search_bar()
|
self.load_view_search_bar()
|
||||||
self.load_view_summary_tab()
|
|
||||||
self.load_view_attack_tab()
|
|
||||||
self.load_view_tree_tab()
|
self.load_view_tree_tab()
|
||||||
|
self.load_view_attack_tab()
|
||||||
|
|
||||||
# load menu bar and sub menus
|
# load menu bar and sub menus
|
||||||
self.load_view_menu_bar()
|
self.load_view_menu_bar()
|
||||||
@@ -128,28 +122,6 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
bar = QtWidgets.QMenuBar()
|
bar = QtWidgets.QMenuBar()
|
||||||
self.view_menu_bar = bar
|
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):
|
def load_view_attack(self):
|
||||||
""" load MITRE ATT&CK table """
|
""" load MITRE ATT&CK table """
|
||||||
table_headers = [
|
table_headers = [
|
||||||
@@ -209,16 +181,6 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
|
|
||||||
self.view_tabs.addTab(tab, "Tree View")
|
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):
|
def load_view_attack_tab(self):
|
||||||
""" load MITRE ATT&CK tab view """
|
""" load MITRE ATT&CK tab view """
|
||||||
layout = QtWidgets.QVBoxLayout()
|
layout = QtWidgets.QVBoxLayout()
|
||||||
@@ -409,7 +371,6 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
self.doc = capa.render.convert_capabilities_to_result_document(meta, rules, capabilities)
|
self.doc = capa.render.convert_capabilities_to_result_document(meta, rules, capabilities)
|
||||||
|
|
||||||
self.model_data.render_capa_doc(self.doc)
|
self.model_data.render_capa_doc(self.doc)
|
||||||
self.render_capa_doc_summary()
|
|
||||||
self.render_capa_doc_mitre_summary()
|
self.render_capa_doc_mitre_summary()
|
||||||
|
|
||||||
self.set_view_tree_default_sort_order()
|
self.set_view_tree_default_sort_order()
|
||||||
@@ -420,24 +381,6 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
""" set capa tree view default sort order """
|
""" set capa tree view default sort order """
|
||||||
self.view_tree.sortByColumn(CapaExplorerDataModel.COLUMN_INDEX_RULE_INFORMATION, QtCore.Qt.AscendingOrder)
|
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):
|
def render_capa_doc_mitre_summary(self):
|
||||||
""" render capa MITRE ATT&CK results """
|
""" render capa MITRE ATT&CK results """
|
||||||
tactics = collections.defaultdict(set)
|
tactics = collections.defaultdict(set)
|
||||||
@@ -511,7 +454,6 @@ class CapaExplorerForm(idaapi.PluginForm):
|
|||||||
self.range_model_proxy.invalidate()
|
self.range_model_proxy.invalidate()
|
||||||
self.search_model_proxy.invalidate()
|
self.search_model_proxy.invalidate()
|
||||||
self.model_data.clear()
|
self.model_data.clear()
|
||||||
self.view_summary.setRowCount(0)
|
|
||||||
self.load_capa_results()
|
self.load_capa_results()
|
||||||
|
|
||||||
logger.debug("%s reload completed", self.form_title)
|
logger.debug("%s reload completed", self.form_title)
|
||||||
|
|||||||
@@ -147,10 +147,10 @@ class CapaExplorerRuleItem(CapaExplorerDataItem):
|
|||||||
|
|
||||||
fmt = "%s (%d matches)"
|
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
|
display = self.fmt % (name, count) if count > 1 else name
|
||||||
super(CapaExplorerRuleItem, self).__init__(parent, [display, "", ""])
|
super(CapaExplorerRuleItem, self).__init__(parent, [display, "", namespace])
|
||||||
self._source = source
|
self._source = source
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -450,7 +450,11 @@ class CapaExplorerDataModel(QtCore.QAbstractItemModel):
|
|||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
|
|
||||||
for rule in rutils.capability_rules(doc):
|
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():
|
for (location, match) in doc["rules"][rule["meta"]["name"]]["matches"].items():
|
||||||
if rule["meta"]["scope"] == capa.rules.FILE_SCOPE:
|
if rule["meta"]["scope"] == capa.rules.FILE_SCOPE:
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class CapaExplorerRangeProxyModel(QtCore.QSortFilterProxyModel):
|
|||||||
class CapaExplorerSearchProxyModel(QtCore.QSortFilterProxyModel):
|
class CapaExplorerSearchProxyModel(QtCore.QSortFilterProxyModel):
|
||||||
"""A SortFilterProxyModel that accepts rows with a substring match for a configurable query.
|
"""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,
|
Displays the entire tree row if any of the tree branches,
|
||||||
that is, you can filter by rule name, or also
|
that is, you can filter by rule name, or also
|
||||||
filter by "characteristic(nzxor)" to filter matches with some feature.
|
filter by "characteristic(nzxor)" to filter matches with some feature.
|
||||||
@@ -175,17 +175,25 @@ class CapaExplorerSearchProxyModel(QtCore.QSortFilterProxyModel):
|
|||||||
|
|
||||||
source_model = self.sourceModel()
|
source_model = self.sourceModel()
|
||||||
|
|
||||||
index = source_model.index(row, 0, parent)
|
for column in (
|
||||||
data = source_model.data(index, Qt.DisplayRole)
|
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:
|
if not data:
|
||||||
return False
|
continue
|
||||||
|
|
||||||
if not isinstance(data, str):
|
if not isinstance(data, str):
|
||||||
# sanity check: should already be a string, but double check
|
# 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):
|
def set_query(self, query):
|
||||||
self.query = query
|
self.query = query
|
||||||
|
|||||||
Reference in New Issue
Block a user