Skip to content

Commit f2e11ed

Browse files
committed
binja: retrieve the LLIL instruction itself without requesting the entire IL function
1 parent 5a284de commit f2e11ed

File tree

3 files changed

+34
-48
lines changed

3 files changed

+34
-48
lines changed

capa/features/extractors/binja/function.py

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from capa.features.common import Feature, Characteristic
1414
from capa.features.address import Address, AbsoluteVirtualAddress
1515
from capa.features.extractors import loops
16+
from capa.features.extractors.binja.helpers import get_llil_instr_at_addr
1617
from capa.features.extractors.base_extractor import FunctionHandle
1718

1819

@@ -24,14 +25,7 @@ def extract_function_calls_to(fh: FunctionHandle):
2425
# Everything that is a code reference to the current function is considered a caller, which actually includes
2526
# many other references that are NOT a caller. For example, an instruction `push function_start` will also be
2627
# considered a caller to the function
27-
llil = None
28-
try:
29-
# Temporary fix for https://github.com/Vector35/binaryninja-api/issues/6020. Since `.llil` can throw an
30-
# exception rather than returning None
31-
llil = caller.llil
32-
except ILException:
33-
continue
34-
28+
llil = get_llil_instr_at_addr(func.view, caller.address)
3529
if (llil is None) or llil.operation not in [
3630
LowLevelILOperation.LLIL_CALL,
3731
LowLevelILOperation.LLIL_CALL_STACK_ADJUST,
@@ -40,14 +34,13 @@ def extract_function_calls_to(fh: FunctionHandle):
4034
]:
4135
continue
4236

43-
if llil.dest.value.type not in [
44-
RegisterValueType.ImportedAddressValue,
45-
RegisterValueType.ConstantValue,
46-
RegisterValueType.ConstantPointerValue,
37+
if llil.dest.operation not in [
38+
LowLevelILOperation.LLIL_CONST,
39+
LowLevelILOperation.LLIL_CONST_PTR,
4740
]:
4841
continue
4942

50-
address = llil.dest.value.value
43+
address = llil.dest.constant
5144
if address != func.start:
5245
continue
5346

capa/features/extractors/binja/helpers.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
77
# See the License for the specific language governing permissions and limitations under the License.
88
import re
9-
from typing import Callable
9+
from typing import Callable, Optional
1010
from dataclasses import dataclass
1111

12-
from binaryninja import BinaryView, LowLevelILInstruction
12+
from binaryninja import BinaryView, LowLevelILFunction, LowLevelILInstruction
1313
from binaryninja.architecture import InstructionTextToken
1414

1515

@@ -67,3 +67,13 @@ def read_c_string(bv: BinaryView, offset: int, max_len: int) -> str:
6767
s.append(chr(c))
6868

6969
return "".join(s)
70+
71+
72+
def get_llil_instr_at_addr(bv: BinaryView, addr: int) -> Optional[LowLevelILInstruction]:
73+
arch = bv.arch
74+
buffer = bv.read(addr, arch.max_instr_length)
75+
llil = LowLevelILFunction(arch=arch)
76+
llil.current_address = addr
77+
if arch.get_instruction_low_level_il(buffer, addr, llil) == 0:
78+
return None
79+
return llil[0]

capa/features/extractors/binja/insn.py

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from capa.features.insn import API, MAX_STRUCTURE_SIZE, Number, Offset, Mnemonic, OperandNumber, OperandOffset
2525
from capa.features.common import MAX_BYTES_FEATURE_SIZE, Bytes, String, Feature, Characteristic
2626
from capa.features.address import Address, AbsoluteVirtualAddress
27-
from capa.features.extractors.binja.helpers import DisassemblyInstruction, visit_llil_exprs
27+
from capa.features.extractors.binja.helpers import DisassemblyInstruction, visit_llil_exprs, get_llil_instr_at_addr
2828
from capa.features.extractors.base_extractor import BBHandle, InsnHandle, FunctionHandle
2929

3030
# security cookie checks may perform non-zeroing XORs, these are expected within a certain
@@ -37,40 +37,23 @@
3737
# 2. The function must only make one call/jump to another address
3838
# If the function being checked is a stub function, returns the target address. Otherwise, return None.
3939
def is_stub_function(bv: BinaryView, addr: int) -> Optional[int]:
40-
funcs = bv.get_functions_at(addr)
41-
for func in funcs:
42-
if len(func.basic_blocks) != 1:
43-
continue
44-
45-
call_count = 0
46-
call_target = None
47-
try:
48-
llil = func.llil
49-
except ILException:
50-
return None
40+
llil = get_llil_instr_at_addr(bv, addr)
41+
if llil.operation not in [
42+
LowLevelILOperation.LLIL_CALL,
43+
LowLevelILOperation.LLIL_CALL_STACK_ADJUST,
44+
LowLevelILOperation.LLIL_JUMP,
45+
LowLevelILOperation.LLIL_TAILCALL,
46+
]:
47+
return None
5148

52-
if llil is None:
53-
continue
49+
if llil.dest.value.type not in [
50+
RegisterValueType.ImportedAddressValue,
51+
RegisterValueType.ConstantValue,
52+
RegisterValueType.ConstantPointerValue,
53+
]:
54+
return None
5455

55-
for il in llil.instructions:
56-
if il.operation in [
57-
LowLevelILOperation.LLIL_CALL,
58-
LowLevelILOperation.LLIL_CALL_STACK_ADJUST,
59-
LowLevelILOperation.LLIL_JUMP,
60-
LowLevelILOperation.LLIL_TAILCALL,
61-
]:
62-
call_count += 1
63-
if il.dest.value.type in [
64-
RegisterValueType.ImportedAddressValue,
65-
RegisterValueType.ConstantValue,
66-
RegisterValueType.ConstantPointerValue,
67-
]:
68-
call_target = il.dest.value.value
69-
70-
if call_count == 1 and call_target is not None:
71-
return call_target
72-
73-
return None
56+
return llil.dest.value.value
7457

7558

7659
def extract_insn_api_features(fh: FunctionHandle, bbh: BBHandle, ih: InsnHandle) -> Iterator[tuple[Feature, Address]]:

0 commit comments

Comments
 (0)