Skip to content

Commit 7871953

Browse files
committed
Python: Collect and pass mappings when copying MLIL functions
1 parent 3369388 commit 7871953

File tree

7 files changed

+313
-24
lines changed

7 files changed

+313
-24
lines changed

binaryninjacore.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3711,6 +3711,16 @@ extern "C"
37113711
AlwaysEnabledRenderLayerDefaultEnableState,
37123712
} BNRenderLayerDefaultEnableState;
37133713

3714+
typedef struct BNExprMapInfo
3715+
{
3716+
size_t lowerIndex;
3717+
size_t higherIndex;
3718+
bool mapLowerToHigher;
3719+
bool mapHigherToLower;
3720+
bool lowerToHigherDirect;
3721+
bool higherToLowerDirect;
3722+
} BNExprMapInfo;
3723+
37143724
BINARYNINJACOREAPI char* BNAllocString(const char* contents);
37153725
BINARYNINJACOREAPI char* BNAllocStringWithLength(const char* contents, size_t len);
37163726
BINARYNINJACOREAPI void BNFreeString(char* str);
@@ -5647,7 +5657,9 @@ extern "C"
56475657
BINARYNINJACOREAPI void BNSetLowLevelILFunction(
56485658
BNAnalysisContext* analysisContext, BNLowLevelILFunction* lowLevelIL);
56495659
BINARYNINJACOREAPI void BNSetMediumLevelILFunction(
5650-
BNAnalysisContext* analysisContext, BNMediumLevelILFunction* mediumLevelIL);
5660+
BNAnalysisContext* analysisContext, BNMediumLevelILFunction* mediumLevelIL,
5661+
size_t* llilSSAToMLILSSAInstrMap, size_t instrCount,
5662+
BNExprMapInfo* llilSSAToMLILSSAExprMap, size_t exprCount);
56515663
BINARYNINJACOREAPI void BNSetHighLevelILFunction(
56525664
BNAnalysisContext* analysisContext, BNHighLevelILFunction* highLevelIL);
56535665
BINARYNINJACOREAPI bool BNAnalysisContextInform(BNAnalysisContext* analysisContext, const char* request);

python/commonil.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
# IN THE SOFTWARE.
2020

2121
from dataclasses import dataclass
22-
from typing import Union
22+
from typing import Union, Optional
2323
from .flowgraph import FlowGraph, FlowGraphNode
2424
from .enums import BranchType
2525
from .interaction import show_graph_report
@@ -29,6 +29,9 @@
2929
from . import highlevelil
3030

3131

32+
invalid_il_index = 0xffffffffffffffff
33+
34+
3235
# This file contains a list of top level abstract classes for implementing BNIL instructions
3336
@dataclass(frozen=True, repr=False, eq=False)
3437
class BaseILInstruction:
@@ -210,7 +213,6 @@ class AliasedVariableInstruction(VariableInstruction):
210213
pass
211214

212215

213-
@dataclass
214216
class ILSourceLocation:
215217
"""
216218
ILSourceLocation is used to indicate where expressions were defined during the lifting process
@@ -220,15 +222,52 @@ class ILSourceLocation:
220222
address: int
221223
source_operand: int
222224

225+
source_llil_instruction: Optional['lowlevelil.LowLevelILInstruction'] = None
226+
source_mlil_instruction: Optional['mediumlevelil.MediumLevelILInstruction'] = None
227+
source_hlil_instruction: Optional['highlevelil.HighLevelILInstruction'] = None
228+
il_direct: bool = True
229+
230+
def __init__(self, address: int, source_operand: int):
231+
self.address = address
232+
self.source_operand = source_operand
233+
234+
def __repr__(self):
235+
instr = ""
236+
if self.source_llil_instruction is not None:
237+
instr = f" (from LLIL {self.source_llil_instruction})"
238+
if self.source_mlil_instruction is not None:
239+
instr = f" (from MLIL {self.source_mlil_instruction})"
240+
if self.source_hlil_instruction is not None:
241+
instr = f" (from HLIL {self.source_hlil_instruction})"
242+
return f"<ILSourceLocation: {self.address:x}, {self.source_operand}{instr}>"
243+
244+
def __hash__(self):
245+
return hash((self.address, self.source_operand))
246+
247+
def __eq__(self, other):
248+
if not isinstance(other, ILSourceLocation):
249+
return False
250+
return self.address == other.address and self.source_operand == other.source_operand
251+
223252
@classmethod
224253
def from_instruction(
225254
cls,
226-
instr: Union['lowlevelil.LowLevelILInstruction', 'mediumlevelil.MediumLevelILInstruction', 'highlevelil.HighLevelILInstruction']
255+
instr: Union['lowlevelil.LowLevelILInstruction', 'mediumlevelil.MediumLevelILInstruction', 'highlevelil.HighLevelILInstruction'],
256+
il_direct: bool = True
227257
) -> 'ILSourceLocation':
228258
"""
229259
Get the source location of a given instruction
230260
:param instr: Instruction, Low, Medium, or High level
231261
:return: Its location
232262
"""
233-
return cls(instr.address, instr.source_operand)
234-
263+
loc = cls(instr.address, instr.source_operand)
264+
if isinstance(instr, lowlevelil.LowLevelILInstruction):
265+
loc.source_llil_instruction = instr
266+
elif isinstance(instr, mediumlevelil.MediumLevelILInstruction):
267+
loc.source_mlil_instruction = instr
268+
elif isinstance(instr, highlevelil.HighLevelILInstruction):
269+
loc.source_hlil_instruction = instr
270+
else:
271+
log_warn(f"Unknown instruction type {type(instr)}")
272+
loc.il_direct = il_direct
273+
return loc

python/examples/wf_test_copy_expr.py

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import functools
12
import json
23
import math
34

@@ -67,7 +68,14 @@ def assert_llil_eq(old_insn: LowLevelILInstruction, new_insn: LowLevelILInstruct
6768
assert old_op[1] == new_op[1], err_msg
6869

6970

70-
def assert_mlil_eq(old_insn: LowLevelILInstruction, new_insn: LowLevelILInstruction):
71+
@functools.lru_cache(maxsize=8)
72+
def get_mlil_maps(mlil: MediumLevelILFunction, builders: bool) -> Tuple[LLILSSAToMLILInstructionMapping, LLILSSAToMLILExpressionMapping]:
73+
instr_map = mlil._get_llil_ssa_to_mlil_instr_map(builders)
74+
expr_map = mlil._get_llil_ssa_to_mlil_expr_map(builders)
75+
return instr_map, expr_map
76+
77+
78+
def assert_mlil_eq(old_insn: MediumLevelILInstruction, new_insn: MediumLevelILInstruction):
7179
"""
7280
Make sure that these two instructions are the same (probably correct). Asserts otherwise.
7381
@@ -78,10 +86,23 @@ def assert_mlil_eq(old_insn: LowLevelILInstruction, new_insn: LowLevelILInstruct
7886
"""
7987
err_msg = (hex(old_insn.address), old_insn, new_insn)
8088
assert old_insn.operation == new_insn.operation, err_msg
81-
# assert old_insn.attributes == new_insn.attributes, err_msg
89+
assert old_insn.attributes == new_insn.attributes, err_msg
8290
assert old_insn.size == new_insn.size, err_msg
8391
assert old_insn.source_location == new_insn.source_location, err_msg
8492
assert len(old_insn.operands) == len(new_insn.operands), err_msg
93+
# Type only applies once we've generated SSA form (probably not consistent)
94+
# assert old_insn.expr_type == new_insn.expr_type, f"{err_msg} {old_insn.expr_type} {new_insn.expr_type}"
95+
96+
instr_map, expr_map = get_mlil_maps(new_insn.function, True)
97+
98+
# Compare that the instruction's LLIL SSA map is the same as the old function
99+
if old_insn.instr_index is not None and old_insn.function.get_expr_index_for_instruction(old_insn.instr_index) == old_insn.expr_index:
100+
old_llil_ssa = old_insn.function.get_low_level_il_instruction_index(old_insn.instr_index)
101+
if old_llil_ssa is not None:
102+
assert [mlil for (llil, mlil) in instr_map.items() if llil == old_llil_ssa] == [new_insn.instr_index], err_msg
103+
else:
104+
assert [mlil for (llil, mlil) in instr_map.items() if llil == old_llil_ssa] == [], err_msg
105+
85106
# Can't compare operands directly since IL expression indices might change when
86107
# copying an instruction to another function
87108
for i, (old_op, new_op) in enumerate(zip(old_insn.detailed_operands, new_insn.detailed_operands)):
@@ -231,6 +252,11 @@ def translate_instr(
231252
report.append(FlowGraphReport("new graph", new_mlil.create_graph_immediate(settings)))
232253
show_report_collection("copy expr test", report)
233254

255+
# Check expr mappings are the same
256+
new_map = list(sorted(new_mlil._get_llil_ssa_to_mlil_expr_map(True), key=lambda o: (o.lower_index, o.higher_index)))
257+
old_map = list(sorted(old_mlil._get_llil_ssa_to_mlil_expr_map(False), key=lambda o: (o.lower_index, o.higher_index)))
258+
assert old_map == new_map
259+
234260
# Check all BBs have all the same instructions
235261
# Technically, this misses any instructions outside a BB, but those are not
236262
# picked up by analysis anyway, and therefore don't matter.
@@ -240,31 +266,52 @@ def translate_instr(
240266
for old_insn, new_insn in zip(old_bb, new_bb):
241267
assert_mlil_eq(old_insn, new_insn)
242268

269+
# Make sure mappings update correctly following set
270+
new_map = list(sorted(new_mlil._get_llil_ssa_to_mlil_expr_map(True), key=lambda o: (o.lower_index, o.higher_index)))
271+
context.mlil = new_mlil
272+
newer_map = list(sorted(context.mlil._get_llil_ssa_to_mlil_expr_map(False), key=lambda o: (o.lower_index, o.higher_index)))
273+
assert new_map == newer_map
274+
243275

244-
wf = Workflow("core.function.metaAnalysis").clone("TestCopyExpr")
276+
wf = Workflow("core.function.metaAnalysis").clone("core.function.metaAnalysis")
245277

246278
# Define the custom activity configuration
247279
wf.register_activity(Activity(
248280
configuration=json.dumps({
249281
"name": "extension.test_copy_expr.lil_action",
250282
"title": "Lifted IL copy_expr Test",
251-
"description": "Makes sure copy_expr works on Lifted IL functions."
283+
"description": "Makes sure copy_expr works on Lifted IL functions.",
284+
"eligibility": {
285+
"auto": {
286+
"default": False
287+
}
288+
}
252289
}),
253290
action=lil_action
254291
))
255292
wf.register_activity(Activity(
256293
configuration=json.dumps({
257294
"name": "extension.test_copy_expr.llil_action",
258295
"title": "Low Level IL copy_expr Test",
259-
"description": "Makes sure copy_expr works on Low Level IL functions."
296+
"description": "Makes sure copy_expr works on Low Level IL functions.",
297+
"eligibility": {
298+
"auto": {
299+
"default": False
300+
}
301+
}
260302
}),
261303
action=llil_action
262304
))
263305
wf.register_activity(Activity(
264306
configuration=json.dumps({
265307
"name": "extension.test_copy_expr.mlil_action",
266308
"title": "Medium Level IL copy_expr Test",
267-
"description": "Makes sure copy_expr works on Medium Level IL functions."
309+
"description": "Makes sure copy_expr works on Medium Level IL functions.",
310+
"eligibility": {
311+
"auto": {
312+
"default": False
313+
}
314+
}
268315
}),
269316
action=mlil_action
270317
))

0 commit comments

Comments
 (0)