From b9e4ce61a086528659edeed4574c9a5942a231fd Mon Sep 17 00:00:00 2001 From: Michael Hunhoff Date: Tue, 14 Jul 2020 21:06:53 -0600 Subject: [PATCH 1/3] adding file menu option to export json file --- capa/ida/ida_capa_explorer.py | 41 ++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/capa/ida/ida_capa_explorer.py b/capa/ida/ida_capa_explorer.py index ad1c7a7f..c1b22cd8 100644 --- a/capa/ida/ida_capa_explorer.py +++ b/capa/ida/ida_capa_explorer.py @@ -1,6 +1,7 @@ import os import logging import collections +import json import idaapi from PyQt5 import QtGui, QtCore, QtWidgets @@ -80,6 +81,7 @@ class CapaExplorerForm(idaapi.PluginForm): self.parent = None self.ida_hooks = None + self.doc = None # models self.model_data = None @@ -252,15 +254,26 @@ class CapaExplorerForm(idaapi.PluginForm): actions = ( ("Reset view", "Reset plugin view", self.reset), ("Run analysis", "Run capa analysis on current database", self.reload), + ("Export results...", "Export capa results as JSON file", self.export_json) ) menu = self.view_menu_bar.addMenu("File") - for (name, _, handle) in actions: action = QtWidgets.QAction(name, self.parent) action.triggered.connect(handle) menu.addAction(action) + def export_json(self): + """ export capa results as JSON file """ + if not self.doc: + idaapi.info("No capa results to export.") + return + path = idaapi.ask_file(True, "*.json", "Choose file") + if os.path.exists(path) and 1 != idaapi.ask_yn(1, "File already exists. Overwrite?"): + return + with open(path, "w") as export_file: + json.dump(self.doc, export_file, sort_keys=True, cls=capa.render.CapaJsonObjectEncoder) + def load_ida_hooks(self): """ load IDA Pro UI hooks """ action_hooks = { @@ -375,11 +388,11 @@ class CapaExplorerForm(idaapi.PluginForm): logger.info("analysis completed.") - 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(doc) - self.render_capa_doc_summary(doc) - self.render_capa_doc_mitre_summary(doc) + 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() @@ -389,12 +402,9 @@ 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, doc): - """ render capa summary results - - @param doc: capa doc - """ - for (row, rule) in enumerate(rutils.capability_rules(doc)): + 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: @@ -410,14 +420,11 @@ class CapaExplorerForm(idaapi.PluginForm): # resize columns to content self.view_summary.resizeColumnsToContents() - def render_capa_doc_mitre_summary(self, doc): - """ render capa MITRE ATT&CK results - - @param doc: capa doc - """ + def render_capa_doc_mitre_summary(self): + """ render capa MITRE ATT&CK results """ tactics = collections.defaultdict(set) - for rule in rutils.capability_rules(doc): + for rule in rutils.capability_rules(self.doc): if not rule["meta"].get("att&ck"): continue From cc3e5fd7c653da8d0b00d903b4041b7d92d368f2 Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Wed, 15 Jul 2020 14:25:04 -0600 Subject: [PATCH 2/3] explorer: use binary mode for opening files --- capa/ida/ida_capa_explorer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/capa/ida/ida_capa_explorer.py b/capa/ida/ida_capa_explorer.py index c1b22cd8..c3a63390 100644 --- a/capa/ida/ida_capa_explorer.py +++ b/capa/ida/ida_capa_explorer.py @@ -271,8 +271,8 @@ class CapaExplorerForm(idaapi.PluginForm): path = idaapi.ask_file(True, "*.json", "Choose file") if os.path.exists(path) and 1 != idaapi.ask_yn(1, "File already exists. Overwrite?"): return - with open(path, "w") as export_file: - json.dump(self.doc, export_file, sort_keys=True, cls=capa.render.CapaJsonObjectEncoder) + with open(path, "wb") as export_file: + export_file.write(json.dumps(self.doc, sort_keys=True, cls=capa.render.CapaJsonObjectEncoder).encode('utf-8')) def load_ida_hooks(self): """ load IDA Pro UI hooks """ From 940137fad85a6c461654472a69d618c234f399bb Mon Sep 17 00:00:00 2001 From: William Ballenthin Date: Wed, 15 Jul 2020 14:25:39 -0600 Subject: [PATCH 3/3] explorer: use binary mode when opening files --- capa/ida/ida_capa_explorer.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/capa/ida/ida_capa_explorer.py b/capa/ida/ida_capa_explorer.py index c3a63390..a5c8d4ea 100644 --- a/capa/ida/ida_capa_explorer.py +++ b/capa/ida/ida_capa_explorer.py @@ -1,7 +1,7 @@ import os +import json import logging import collections -import json import idaapi from PyQt5 import QtGui, QtCore, QtWidgets @@ -254,7 +254,7 @@ class CapaExplorerForm(idaapi.PluginForm): actions = ( ("Reset view", "Reset plugin view", self.reset), ("Run analysis", "Run capa analysis on current database", self.reload), - ("Export results...", "Export capa results as JSON file", self.export_json) + ("Export results...", "Export capa results as JSON file", self.export_json), ) menu = self.view_menu_bar.addMenu("File") @@ -272,7 +272,9 @@ class CapaExplorerForm(idaapi.PluginForm): if os.path.exists(path) and 1 != idaapi.ask_yn(1, "File already exists. Overwrite?"): return with open(path, "wb") as export_file: - export_file.write(json.dumps(self.doc, sort_keys=True, cls=capa.render.CapaJsonObjectEncoder).encode('utf-8')) + export_file.write( + json.dumps(self.doc, sort_keys=True, cls=capa.render.CapaJsonObjectEncoder).encode("utf-8") + ) def load_ida_hooks(self): """ load IDA Pro UI hooks """