From 59d03b3ba3b821dd08e71c4bd0539d16cc5121d1 Mon Sep 17 00:00:00 2001 From: Xusheng Date: Wed, 20 Sep 2023 20:56:20 +0800 Subject: [PATCH 1/3] binja: bump Binary Ninja version to 3.5 --- tests/test_binja_features.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_binja_features.py b/tests/test_binja_features.py index a2f0cd78..fdb7ff88 100644 --- a/tests/test_binja_features.py +++ b/tests/test_binja_features.py @@ -69,4 +69,4 @@ def test_standalone_binja_backend(): @pytest.mark.skipif(binja_present is False, reason="Skip binja tests if the binaryninja Python API is not installed") def test_binja_version(): version = binaryninja.core_version_info() - assert version.major == 3 and version.minor == 4 + assert version.major == 3 and version.minor == 5 From bc71c941718e2b541064df7aafb770c695f96988 Mon Sep 17 00:00:00 2001 From: Xusheng Date: Wed, 20 Sep 2023 20:56:36 +0800 Subject: [PATCH 2/3] binja: use binaryninja.load to open a binary --- capa/main.py | 5 +++-- tests/fixtures.py | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/capa/main.py b/capa/main.py index b91ca1e2..ae842156 100644 --- a/capa/main.py +++ b/capa/main.py @@ -558,7 +558,8 @@ def get_extractor( sys.path.append(str(bn_api)) try: - from binaryninja import BinaryView, BinaryViewType + import binaryninja + from binaryninja import BinaryView except ImportError: raise RuntimeError( "Cannot import binaryninja module. Please install the Binary Ninja Python API first: " @@ -568,7 +569,7 @@ def get_extractor( import capa.features.extractors.binja.extractor with halo.Halo(text="analyzing program", spinner="simpleDots", stream=sys.stderr, enabled=not disable_progress): - bv: BinaryView = BinaryViewType.get_view_of_file(str(path)) + bv: BinaryView = binaryninja.load(str(path)) if bv is None: raise RuntimeError(f"Binary Ninja cannot open file {path}") diff --git a/tests/fixtures.py b/tests/fixtures.py index a8a930b3..230fa803 100644 --- a/tests/fixtures.py +++ b/tests/fixtures.py @@ -159,7 +159,8 @@ def get_dnfile_extractor(path: Path): @lru_cache(maxsize=1) def get_binja_extractor(path: Path): - from binaryninja import Settings, BinaryViewType + import binaryninja + from binaryninja import Settings import capa.features.extractors.binja.extractor @@ -168,7 +169,7 @@ def get_binja_extractor(path: Path): if path.name.endswith("kernel32-64.dll_"): old_pdb = settings.get_bool("pdb.loadGlobalSymbols") settings.set_bool("pdb.loadGlobalSymbols", False) - bv = BinaryViewType.get_view_of_file(str(path)) + bv = binaryninja.load(str(path)) if path.name.endswith("kernel32-64.dll_"): settings.set_bool("pdb.loadGlobalSymbols", old_pdb) From b3dccb3841400ea11907c23a6cb70734bb55f4a1 Mon Sep 17 00:00:00 2001 From: Xusheng Date: Wed, 20 Sep 2023 20:57:19 +0800 Subject: [PATCH 3/3] binja: improve function call site detection --- CHANGELOG.md | 3 +++ capa/features/extractors/binja/function.py | 20 +++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bfc635a..fc0d9a50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ ### Bug Fixes - ghidra: fix ints_to_bytes performance #1761 @mike-hunhoff +- binja: improve function call site detection @xusheng6 +- binja: use binaryninja.load to open files @xusheng6 +- binja: bump binja version to 3.5 #1789 @xusheng6 ### capa explorer IDA Pro plugin diff --git a/capa/features/extractors/binja/function.py b/capa/features/extractors/binja/function.py index a502a5f4..d2e67aa3 100644 --- a/capa/features/extractors/binja/function.py +++ b/capa/features/extractors/binja/function.py @@ -7,7 +7,7 @@ # See the License for the specific language governing permissions and limitations under the License. from typing import Tuple, Iterator -from binaryninja import Function, BinaryView, LowLevelILOperation +from binaryninja import Function, BinaryView, RegisterValueType, LowLevelILOperation from capa.features.common import Feature, Characteristic from capa.features.address import Address, AbsoluteVirtualAddress @@ -23,13 +23,27 @@ def extract_function_calls_to(fh: FunctionHandle): # Everything that is a code reference to the current function is considered a caller, which actually includes # many other references that are NOT a caller. For example, an instruction `push function_start` will also be # considered a caller to the function - if caller.llil is not None and caller.llil.operation in [ + llil = caller.llil + if (llil is None) or llil.operation not in [ LowLevelILOperation.LLIL_CALL, LowLevelILOperation.LLIL_CALL_STACK_ADJUST, LowLevelILOperation.LLIL_JUMP, LowLevelILOperation.LLIL_TAILCALL, ]: - yield Characteristic("calls to"), AbsoluteVirtualAddress(caller.address) + continue + + if llil.dest.value.type not in [ + RegisterValueType.ImportedAddressValue, + RegisterValueType.ConstantValue, + RegisterValueType.ConstantPointerValue, + ]: + continue + + address = llil.dest.value.value + if address != func.start: + continue + + yield Characteristic("calls to"), AbsoluteVirtualAddress(caller.address) def extract_function_loop(fh: FunctionHandle):