diff --git a/CHANGELOG.md b/CHANGELOG.md index 538ee89d80..1debc4bd27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ ### capa Explorer Web ### capa Explorer IDA Pro plugin +- ida: display file-scope feature addresses (`file:0x...`) in rule generator tree and safely handle file-offset navigation/filtering @vee1e #3009 ### Development - tests: update binja version to 5.3 @mr-tz #3011 @@ -187,7 +188,7 @@ Additionally a Binary Ninja bug has been fixed. Released binaries now include AR ### Bug Fixes -- binja: fix a crash during feature extraction when the MLIL is unavailable @xusheng6 #2714 +- binja: fix a crash during feature extraction when the MLIL is unavailable @xusheng6 #2714 ### capa Explorer Web diff --git a/capa/ida/plugin/view.py b/capa/ida/plugin/view.py index 4c21de1c9d..bc1568d867 100644 --- a/capa/ida/plugin/view.py +++ b/capa/ida/plugin/view.py @@ -18,6 +18,7 @@ import idc import idaapi +import ida_loader import capa.rules import capa.engine @@ -25,7 +26,7 @@ import capa.features.common import capa.features.basicblock from capa.ida.plugin.item import CapaExplorerFunctionItem -from capa.features.address import AbsoluteVirtualAddress, _NoAddress +from capa.features.address import FileOffsetAddress, AbsoluteVirtualAddress, _NoAddress from capa.ida.plugin.model import CapaExplorerDataModel from capa.ida.plugin.qt_compat import QtGui, QtCore, Signal, QAction, QtWidgets @@ -905,7 +906,22 @@ def slot_custom_context_menu_requested(self, pos): def slot_item_double_clicked(self, o, column): """ """ if column == CapaExplorerRulegenFeatures.get_column_address_index() and o.text(column): - idc.jumpto(int(o.text(column), 0x10)) + addr_text = o.text(column).strip() + + if addr_text.startswith("file:"): + try: + file_offset = int(addr_text[len("file:") :], 16) + except (ValueError, TypeError): + return + + ea = ida_loader.get_fileregion_ea(file_offset) + if ea != idc.BADADDR: + idc.jumpto(ea) + else: + try: + idc.jumpto(int(addr_text, 16)) + except (ValueError, TypeError): + return elif o.capa_type == CapaExplorerRulegenFeatures.get_node_type_leaf(): self.editor.update_features([o.data(0, 0x100)]) @@ -955,13 +971,18 @@ def show_item_and_parents(_o): # read ea from "Address" column o_ea = o.text(CapaExplorerRulegenFeatures.get_column_address_index()) - if o_ea == "": - # ea may be empty, hide by default + if o_ea == "" or o_ea.startswith("file:"): + # ea may be empty or a file offset, hide by default when filtering by VA if not o.isHidden(): o.setHidden(True) continue - o_ea = int(o_ea, 16) + try: + o_ea = int(o_ea, 16) + except (ValueError, TypeError): + if not o.isHidden(): + o.setHidden(True) + continue if max_ea is not None and min_ea <= o_ea <= max_ea: show_item_and_parents(o) @@ -1045,8 +1066,9 @@ def parse_features_for_tree(self, parent, features): def format_address(e): if isinstance(e, AbsoluteVirtualAddress): return f"{hex(int(e))}" - else: - return "" + if isinstance(e, FileOffsetAddress): + return f"file:{hex(e)}" + return "" def format_feature(feature): """ """