Skip to content

Commit 5192206

Browse files
committed
Python: Add LLILFunction.translate and sample workflow using it
1 parent 5736062 commit 5192206

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import json
2+
from binaryninja import Workflow, Activity, AnalysisContext
3+
from binaryninja.lowlevelil import *
4+
5+
6+
def rewrite_action(context: AnalysisContext):
7+
def translate_instr(
8+
new_func: LowLevelILFunction,
9+
old_block: LowLevelILBasicBlock,
10+
old_instr: LowLevelILInstruction
11+
) -> ExpressionIndex:
12+
"""
13+
Copy and translate ``old_instr`` in ``old_block`` into ``new_func``
14+
15+
:param new_func: new function to receive translated instructions
16+
:param old_block: original block containing old_instr
17+
:param old_instr: original instruction
18+
:return: expression index of newly created instruction in ``new_func``
19+
"""
20+
21+
if old_instr.operation == LowLevelILOperation.LLIL_PUSH:
22+
# push(x)
23+
# -----------------------
24+
# sp = sp - sizeof(void*)
25+
# *(sp) = x
26+
27+
old_instr: LowLevelILPush
28+
# sp = sp - sizeof(void*)
29+
new_func.append(
30+
new_func.set_reg(
31+
old_instr.size,
32+
old_block.arch.stack_pointer,
33+
new_func.sub(
34+
old_instr.size,
35+
new_func.reg(
36+
old_instr.size,
37+
old_block.arch.stack_pointer,
38+
loc=ILSourceLocation.from_instruction(old_instr)
39+
),
40+
new_func.const(
41+
old_instr.size,
42+
old_block.arch.address_size,
43+
loc=ILSourceLocation.from_instruction(old_instr)
44+
),
45+
loc=ILSourceLocation.from_instruction(old_instr)
46+
),
47+
loc=ILSourceLocation.from_instruction(old_instr)
48+
)
49+
)
50+
# *(sp) = x
51+
return new_func.store(
52+
old_instr.size,
53+
new_func.reg(
54+
old_instr.size,
55+
old_block.arch.stack_pointer,
56+
loc=ILSourceLocation.from_instruction(old_instr)
57+
),
58+
old_instr.src.copy_to(new_func),
59+
loc=ILSourceLocation.from_instruction(old_instr)
60+
)
61+
elif old_instr.operation == LowLevelILOperation.LLIL_POP:
62+
# pop
63+
# -----------------------
64+
# sp = sp + sizeof(void*)
65+
# *(sp - sizeof(void*))
66+
67+
# We need to append any helper instructions first and then return an expression
68+
# that replaces the ``pop`` in the original IL (since ``pop`` has a value).
69+
# So anything that is ``rax = pop`` becomes ``sp = sp + 8 ; rax = *(sp - 8)``
70+
71+
old_instr: LowLevelILPop
72+
# sp = sp + sizeof(void*)
73+
new_func.append(
74+
new_func.set_reg(
75+
old_instr.size,
76+
old_block.arch.stack_pointer,
77+
new_func.add(
78+
old_instr.size,
79+
new_func.reg(
80+
old_instr.size,
81+
old_block.arch.stack_pointer,
82+
loc=ILSourceLocation.from_instruction(old_instr)
83+
),
84+
new_func.const(
85+
old_instr.size,
86+
old_block.arch.address_size,
87+
loc=ILSourceLocation.from_instruction(old_instr)
88+
),
89+
loc=ILSourceLocation.from_instruction(old_instr)
90+
),
91+
loc=ILSourceLocation.from_instruction(old_instr)
92+
)
93+
)
94+
# *(sp - sizeof(void*))
95+
return new_func.load(
96+
old_instr.size,
97+
new_func.sub(
98+
old_instr.size,
99+
new_func.reg(
100+
old_instr.size,
101+
old_block.arch.stack_pointer,
102+
loc=ILSourceLocation.from_instruction(old_instr)
103+
),
104+
new_func.const(
105+
old_instr.size,
106+
old_block.arch.address_size,
107+
loc=ILSourceLocation.from_instruction(old_instr)
108+
),
109+
loc=ILSourceLocation.from_instruction(old_instr)
110+
),
111+
loc=ILSourceLocation.from_instruction(old_instr)
112+
)
113+
else:
114+
# All other instructions: copy as-is
115+
return old_instr.copy_to(
116+
new_func,
117+
lambda sub_instr: translate_instr(new_func, old_block, sub_instr)
118+
)
119+
120+
# Modify the existing Lifted IL function by our translator above
121+
translated_func = context.lifted_il.translate(translate_instr)
122+
# Clean up blocks and prepare this function for the rest of analysis
123+
translated_func.finalize()
124+
# Tell the analysis to use the new form of this function
125+
context.lifted_il = translated_func
126+
127+
128+
# Create and register the workflow for translating these instructions
129+
wf = Workflow("core.function.metaAnalysis").clone("RewritePushPop")
130+
131+
# Define the custom activity configuration
132+
wf.register_activity(Activity(
133+
configuration=json.dumps({
134+
"name": "extension.rewrite_push_pop.rewrite_action",
135+
"title": "Rewrite LLIL_PUSH/LLIL_POP",
136+
"description": "Rewrites LLIL_PUSH/LLIL_POP instructions into their component store/load/register parts, demonstrating modifying and inserting Lifted IL instructions.",
137+
"eligibility": {
138+
"auto": {
139+
"default": True
140+
}
141+
}
142+
}),
143+
action=rewrite_action
144+
))
145+
146+
# This action is run right after generateLiftedIL so we can poke the IL before LLIL flag and stack
147+
# adjustment resolution happens.
148+
# core.function.generateLiftedIL -> (this) -> core.function.resetIndirectBranchesOnFullUpdate
149+
wf.insert("core.function.resetIndirectBranchesOnFullUpdate", ["extension.rewrite_push_pop.rewrite_action"])
150+
wf.register()

python/lowlevelil.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4126,6 +4126,37 @@ def copy_expr_to(
41264126

41274127
raise NotImplementedError(f"unknown expr operation {expr.operation} in copy_expr_to")
41284128

4129+
def translate(
4130+
self, expr_handler: Callable[['LowLevelILFunction', 'LowLevelILBasicBlock', 'LowLevelILInstruction'], ExpressionIndex]
4131+
) -> 'LowLevelILFunction':
4132+
"""
4133+
``translate`` clones an IL function and modifies its expressions as specified by
4134+
a given ``expr_handler``, returning the updated IL function.
4135+
4136+
:param expr_handler: Function to modify an expression and copy it to the new function.
4137+
The function should have the following signature:
4138+
4139+
.. function:: expr_handler(new_func: LowLevelILFunction, old_block: LowLevelILBasicBlock, old_instr: LowLevelILInstruction) -> ExpressionIndex
4140+
4141+
Where:
4142+
- **new_func** (*LowLevelILFunction*): New function to receive translated instructions
4143+
- **old_block** (*LowLevelILBasicBlock*): Original block containing old_instr
4144+
- **old_instr** (*LowLevelILInstruction*): Original instruction
4145+
- **returns** (*ExpressionIndex*): Expression index of newly created instruction in ``new_func``
4146+
:return: Cloned IL function with modifications
4147+
"""
4148+
4149+
propagated_func = LowLevelILFunction(self.arch, source_func=self.source_function)
4150+
propagated_func.prepare_to_copy_function(self)
4151+
for block in self.basic_blocks:
4152+
propagated_func.prepare_to_copy_block(block)
4153+
for instr_index in range(block.start, block.end):
4154+
instr: LowLevelILInstruction = self[InstructionIndex(instr_index)]
4155+
propagated_func.set_current_address(instr.address, block.arch)
4156+
propagated_func.append(expr_handler(propagated_func, block, instr))
4157+
4158+
return propagated_func
4159+
41294160
def set_expr_attributes(self, expr: InstructionOrExpression, value: ILInstructionAttributeSet):
41304161
"""
41314162
``set_expr_attributes`` allows modification of instruction attributes but ONLY during lifting.

0 commit comments

Comments
 (0)