Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various binja backend fixes #2500

Merged
merged 3 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
- extractor: fix exception when PE extractor encounters unknown architecture #2440 @Tamir-K
- IDA Pro: rename ida to idapro module for plugin and idalib in IDA 9.0 #2453 @mr-tz
- ghidra: fix saving of base address @mr-tz
- binja: support loading raw x86/x86_64 shellcode #2489 @xusheng6
- binja: fix crash when the IL of certain functions are not available. #2249 @xusheng6

### capa Explorer Web

Expand All @@ -34,6 +36,7 @@

### Development
- CI: use macos-13 since macos-12 is deprecated and will be removed on December 3rd, 2024 #2173 @mr-tz
- CI: update Binary Ninja version to 4.2 #2499 @xusheng6

### Raw diffs
- [capa v7.4.0...master](https://github.com/mandiant/capa/compare/v7.4.0...master)
Expand Down
11 changes: 10 additions & 1 deletion capa/features/extractors/binja/extractor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import Iterator

import binaryninja as binja
from binaryninja import ILException

import capa.features.extractors.elf
import capa.features.extractors.binja.file
Expand Down Expand Up @@ -55,7 +56,15 @@ def get_basic_blocks(self, fh: FunctionHandle) -> Iterator[BBHandle]:
f: binja.Function = fh.inner
# Set up a MLIL basic block dict look up to associate the disassembly basic block with its MLIL basic block
mlil_lookup = {}
for mlil_bb in f.mlil.basic_blocks:
try:
mlil = f.mlil
except ILException:
return

if mlil is None:
return

for mlil_bb in mlil.basic_blocks:
mlil_lookup[mlil_bb.source_block.start] = mlil_bb

for bb in f.basic_blocks:
Expand Down
18 changes: 17 additions & 1 deletion capa/features/extractors/binja/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,16 @@
import capa.features.extractors.helpers
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.common import (
FORMAT_PE,
FORMAT_ELF,
FORMAT_SC32,
FORMAT_SC64,
Format,
String,
Feature,
Characteristic,
)
from capa.features.address import NO_ADDRESS, Address, FileOffsetAddress, AbsoluteVirtualAddress
from capa.features.extractors.binja.helpers import read_c_string, unmangle_c_name

Expand Down Expand Up @@ -133,6 +142,13 @@ def extract_file_format(bv: BinaryView) -> Iterator[tuple[Feature, Address]]:
yield Format(FORMAT_PE), NO_ADDRESS
elif view_type == "ELF":
yield Format(FORMAT_ELF), NO_ADDRESS
elif view_type == "Mapped":
if bv.arch.name == "x86":
yield Format(FORMAT_SC32), NO_ADDRESS
elif bv.arch.name == "x86_64":
yield Format(FORMAT_SC64), NO_ADDRESS
else:
raise NotImplementedError(f"unexpected raw file with arch: {bv.arch}")
elif view_type == "Raw":
# no file type to return when processing a binary file, but we want to continue processing
return
Expand Down
11 changes: 9 additions & 2 deletions capa/features/extractors/binja/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# See the License for the specific language governing permissions and limitations under the License.
from typing import Iterator

from binaryninja import Function, BinaryView, SymbolType, RegisterValueType, LowLevelILOperation
from binaryninja import Function, BinaryView, SymbolType, ILException, RegisterValueType, LowLevelILOperation

from capa.features.file import FunctionName
from capa.features.common import Feature, Characteristic
Expand All @@ -24,7 +24,14 @@ 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
llil = caller.llil
llil = None
try:
# Temporary fix for https://github.com/Vector35/binaryninja-api/issues/6020. Since `.llil` can throw an
# exception rather than returning None
llil = caller.llil
except ILException:
continue

if (llil is None) or llil.operation not in [
LowLevelILOperation.LLIL_CALL,
LowLevelILOperation.LLIL_CALL_STACK_ADJUST,
Expand Down
11 changes: 10 additions & 1 deletion capa/features/extractors/binja/insn.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
BinaryView,
ILRegister,
SymbolType,
ILException,
BinaryReader,
RegisterValueType,
LowLevelILOperation,
Expand Down Expand Up @@ -43,7 +44,15 @@ def is_stub_function(bv: BinaryView, addr: int) -> Optional[int]:

call_count = 0
call_target = None
for il in func.llil.instructions:
try:
llil = func.llil
except ILException:
return None

if llil is None:
continue

for il in llil.instructions:
if il.operation in [
LowLevelILOperation.LLIL_CALL,
LowLevelILOperation.LLIL_CALL_STACK_ADJUST,
Expand Down
2 changes: 1 addition & 1 deletion tests/test_binja_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,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 == 4 and version.minor == 1
assert version.major == 4 and version.minor == 2