From d2d32f88effd494f1168199e1cad23d521d56d72 Mon Sep 17 00:00:00 2001 From: Xusheng Date: Thu, 21 Sep 2023 15:32:55 +0800 Subject: [PATCH 1/4] binja: add support for forwarded exports --- CHANGELOG.md | 1 + capa/features/extractors/binja/file.py | 20 +++++++++++++++++++- capa/features/extractors/binja/helpers.py | 20 +++++++++++++++++++- tests/test_binja_features.py | 6 ------ 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc0d9a50..e5b1ddfb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### New Features - ghidra: add Ghidra feature extractor and supporting code #1770 @colton-gabertan - ghidra: add entry script helping users run capa against a loaded Ghidra database #1767 @mike-hunhoff +- binja: add support for forwarded exports #1646 @xusheng6 ### Breaking Changes diff --git a/capa/features/extractors/binja/file.py b/capa/features/extractors/binja/file.py index d46451e7..61d962d2 100644 --- a/capa/features/extractors/binja/file.py +++ b/capa/features/extractors/binja/file.py @@ -17,7 +17,7 @@ import capa.features.extractors.strings from capa.features.file import Export, Import, Section, FunctionName from capa.features.common import FORMAT_PE, FORMAT_ELF, Format, String, Feature, Characteristic from capa.features.address import NO_ADDRESS, Address, FileOffsetAddress, AbsoluteVirtualAddress -from capa.features.extractors.binja.helpers import unmangle_c_name +from capa.features.extractors.binja.helpers import read_c_string, unmangle_c_name def check_segment_for_pe(bv: BinaryView, seg: Segment) -> Iterator[Tuple[int, int]]: @@ -82,6 +82,24 @@ def extract_file_export_names(bv: BinaryView) -> Iterator[Tuple[Feature, Address if name != unmangled_name: yield Export(unmangled_name), AbsoluteVirtualAddress(sym.address) + for sym in bv.get_symbols_of_type(SymbolType.DataSymbol): + if sym.binding not in [SymbolBinding.GlobalBinding]: + continue + + name = sym.short_name + if not name.startswith("__forwarder_name"): + continue + + # Due to https://github.com/Vector35/binaryninja-api/issues/4641, in binja version 3.5, the symbol's name + # does not contain the DLL name. As a workaround, we read the C string at the symbol's address, which contains + # both the DLL name and the function name. + # Once the above issue is closed in the next binjs stable release, we can update the code here to use the + # symbol name directly. + name = read_c_string(bv, sym.address, 1024) + forwarded_name = capa.features.extractors.helpers.reformat_forwarded_export_name(name) + yield Export(forwarded_name), AbsoluteVirtualAddress(sym.address) + yield Characteristic("forwarded export"), AbsoluteVirtualAddress(sym.address) + def extract_file_import_names(bv: BinaryView) -> Iterator[Tuple[Feature, Address]]: """extract function imports diff --git a/capa/features/extractors/binja/helpers.py b/capa/features/extractors/binja/helpers.py index a96f64da..b19ffa9f 100644 --- a/capa/features/extractors/binja/helpers.py +++ b/capa/features/extractors/binja/helpers.py @@ -9,7 +9,7 @@ import re from typing import List, Callable from dataclasses import dataclass -from binaryninja import LowLevelILInstruction +from binaryninja import BinaryView, LowLevelILInstruction from binaryninja.architecture import InstructionTextToken @@ -51,3 +51,21 @@ def unmangle_c_name(name: str) -> str: return match.group(1) return name + + +def read_c_string(bv: BinaryView, offset: int, max_len: int) -> str: + s = "" + count = 0 + while count < max_len: + try: + c = bv.read(offset + count, 1)[0] + except Exception: + break + + if c == 0: + break + + s += chr(c) + count += 1 + + return s diff --git a/tests/test_binja_features.py b/tests/test_binja_features.py index fdb7ff88..a7e2332c 100644 --- a/tests/test_binja_features.py +++ b/tests/test_binja_features.py @@ -40,12 +40,6 @@ except ImportError: indirect=["sample", "scope"], ) def test_binja_features(sample, scope, feature, expected): - if isinstance(feature, capa.features.file.Export) and "." in str(feature.value): - pytest.xfail("skip Binja unsupported forwarded export feature, see #1646") - - if feature == capa.features.common.Characteristic("forwarded export"): - pytest.xfail("skip Binja unsupported forwarded export feature, see #1646") - fixtures.do_test_feature_presence(fixtures.get_binja_extractor, sample, scope, feature, expected) From 3dffa8145fb90acbbe9778f9c3655e262829bc23 Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Wed, 27 Sep 2023 08:47:52 +0200 Subject: [PATCH 2/4] Update capa/features/extractors/binja/helpers.py --- capa/features/extractors/binja/helpers.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/capa/features/extractors/binja/helpers.py b/capa/features/extractors/binja/helpers.py index b19ffa9f..a0a04da6 100644 --- a/capa/features/extractors/binja/helpers.py +++ b/capa/features/extractors/binja/helpers.py @@ -54,9 +54,8 @@ def unmangle_c_name(name: str) -> str: def read_c_string(bv: BinaryView, offset: int, max_len: int) -> str: - s = "" - count = 0 - while count < max_len: + s = [] + while len(s) < max_len: try: c = bv.read(offset + count, 1)[0] except Exception: @@ -65,7 +64,6 @@ def read_c_string(bv: BinaryView, offset: int, max_len: int) -> str: if c == 0: break - s += chr(c) - count += 1 + s.append(chr(c)) - return s + return "".join(s) From d8eebf524e9f12a24513024e59d54df8bd572a3b Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Wed, 27 Sep 2023 08:51:12 +0200 Subject: [PATCH 3/4] Update capa/features/extractors/binja/helpers.py --- capa/features/extractors/binja/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/features/extractors/binja/helpers.py b/capa/features/extractors/binja/helpers.py index a0a04da6..33117a19 100644 --- a/capa/features/extractors/binja/helpers.py +++ b/capa/features/extractors/binja/helpers.py @@ -57,7 +57,7 @@ def read_c_string(bv: BinaryView, offset: int, max_len: int) -> str: s = [] while len(s) < max_len: try: - c = bv.read(offset + count, 1)[0] + c = bv.read(offset + len(s), 1)[0] except Exception: break From 321ef100c54c2994af1bfd1d1bc617e5f84da63c Mon Sep 17 00:00:00 2001 From: Willi Ballenthin Date: Wed, 27 Sep 2023 08:56:42 +0200 Subject: [PATCH 4/4] Update capa/features/extractors/binja/helpers.py --- capa/features/extractors/binja/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/capa/features/extractors/binja/helpers.py b/capa/features/extractors/binja/helpers.py index 33117a19..0ce0f073 100644 --- a/capa/features/extractors/binja/helpers.py +++ b/capa/features/extractors/binja/helpers.py @@ -54,7 +54,7 @@ def unmangle_c_name(name: str) -> str: def read_c_string(bv: BinaryView, offset: int, max_len: int) -> str: - s = [] + s: List[str] = [] while len(s) < max_len: try: c = bv.read(offset + len(s), 1)[0]