Merge pull request #1791 from xusheng6/test_binja_forwarded_export

binja: add support for forwarded exports
This commit is contained in:
Willi Ballenthin
2023-09-27 11:35:00 +02:00
committed by GitHub
4 changed files with 37 additions and 8 deletions

View File

@@ -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
- binja: add support for symtab names #1504 @xusheng6
### Breaking Changes

View File

@@ -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

View File

@@ -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,19 @@ 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: List[str] = []
while len(s) < max_len:
try:
c = bv.read(offset + len(s), 1)[0]
except Exception:
break
if c == 0:
break
s.append(chr(c))
return "".join(s)

View File

@@ -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)