-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Version 2.5 class bases lifter (#26)
* implementation of the new lifter.
- Loading branch information
Showing
17 changed files
with
871 additions
and
770 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
"""Main module containing all binaryninja handlers.""" | ||
from .assignments import AssignmentHandler | ||
from .binary import BinaryOperationHandler | ||
from .calls import CallHandler | ||
from .conditions import ConditionHandler | ||
from .constants import ConstantHandler | ||
from .controlflow import FlowHandler | ||
from .phi import PhiHandler | ||
from .types import TypeHandler | ||
from .unary import UnaryOperationHandler | ||
from .variables import VariableHandler | ||
|
||
# List of all available binaryninja handlers | ||
HANDLERS = [ | ||
VariableHandler, | ||
ConstantHandler, | ||
TypeHandler, | ||
BinaryOperationHandler, | ||
UnaryOperationHandler, | ||
ConditionHandler, | ||
FlowHandler, | ||
AssignmentHandler, | ||
PhiHandler, | ||
CallHandler, | ||
] |
181 changes: 181 additions & 0 deletions
181
decompiler/frontend/binaryninja/handlers/assignments.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
"""Module implementing the AssignmentHandler for binaryninja.""" | ||
from functools import partial | ||
|
||
from binaryninja import mediumlevelil | ||
from decompiler.frontend.lifter import Handler | ||
from decompiler.structures.pseudo import ( | ||
Assignment, | ||
BinaryOperation, | ||
Constant, | ||
Integer, | ||
Operation, | ||
OperationType, | ||
Pointer, | ||
RegisterPair, | ||
UnaryOperation, | ||
) | ||
|
||
|
||
class AssignmentHandler(Handler): | ||
"""Handler for assignments, split assignments as well as field accesses.""" | ||
|
||
def register(self): | ||
"""Register the handler with the parent ObserverLifter.""" | ||
self._lifter.HANDLERS.update( | ||
{ | ||
mediumlevelil.MediumLevelILSetVar: self.lift_assignment, | ||
mediumlevelil.MediumLevelILSetVarSsa: self.lift_assignment, | ||
mediumlevelil.MediumLevelILSetVarField: self.lift_set_field, | ||
mediumlevelil.MediumLevelILSetVarSsaField: self.lift_set_field, | ||
mediumlevelil.MediumLevelILSetVarSplit: self.lift_split_assignment, | ||
mediumlevelil.MediumLevelILSetVarSplitSsa: self.lift_split_assignment, | ||
mediumlevelil.MediumLevelILSetVarAliased: partial(self.lift_assignment, is_aliased=True), | ||
mediumlevelil.MediumLevelILSetVarAliasedField: partial(self.lift_set_field, is_aliased=True), | ||
mediumlevelil.MediumLevelILVarField: self.lift_get_field, | ||
mediumlevelil.MediumLevelILVarSsaField: self.lift_get_field, | ||
mediumlevelil.MediumLevelILVarAliasedField: partial(self.lift_get_field, is_aliased=True), | ||
mediumlevelil.MediumLevelILStore: self.lift_store, | ||
mediumlevelil.MediumLevelILStoreSsa: self.lift_store, | ||
mediumlevelil.MediumLevelILStoreStruct: self._lift_store_struct, | ||
mediumlevelil.MediumLevelILStoreStructSsa: self._lift_store_struct, | ||
mediumlevelil.MediumLevelILLowPart: self._lift_mask_high, | ||
} | ||
) | ||
|
||
def lift_assignment(self, assignment: mediumlevelil.MediumLevelILSetVar, is_aliased=False, **kwargs) -> Assignment: | ||
"""Lift assignment operations (e.g. eax = ebx).""" | ||
return Assignment( | ||
self._lifter.lift(assignment.dest, is_aliased=is_aliased, parent=assignment), | ||
self._lifter.lift(assignment.src, parent=assignment), | ||
) | ||
|
||
def lift_set_field(self, assignment: mediumlevelil.MediumLevelILSetVarField, is_aliased=False, **kwargs) -> Assignment: | ||
""" | ||
Lift an instruction writing to a subset of the given value. | ||
In case of lower register (offset 0) lift as contraction | ||
e.g. eax.al = .... <=> (char)eax .... | ||
In case higher registers use masking | ||
e.g. eax.ah = x <=> eax = (eax & 0xffff00ff) + (x << 2) | ||
""" | ||
if assignment.offset == 0 and self._lifter.is_omitting_masks: | ||
destination = self._lift_contraction(assignment, is_aliased=is_aliased, parent=assignment) | ||
value = self._lifter.lift(assignment.src) | ||
else: | ||
destination = self._lifter.lift(assignment.dest, is_aliased=is_aliased, parent=assignment) | ||
value = self._lift_masked_operand(assignment) | ||
return Assignment(destination, value) | ||
|
||
def lift_get_field(self, instruction: mediumlevelil.MediumLevelILVarField, is_aliased=False, **kwargs) -> Operation: | ||
""" | ||
Lift an instruction accessing a field from the outside. | ||
e.g. x = eax.ah <=> x = eax & 0x0000ff00 | ||
""" | ||
source = self._lifter.lift(instruction.src, is_aliased=is_aliased, parent=instruction) | ||
cast_type = source.type.resize(instruction.size * self.BYTE_SIZE) | ||
if instruction.offset: | ||
return BinaryOperation( | ||
OperationType.bitwise_and, | ||
[source, Constant(self._get_all_ones_mask_for_type(instruction.size) << instruction.offset)], | ||
vartype=cast_type, | ||
) | ||
return UnaryOperation(OperationType.cast, [source], vartype=cast_type, contraction=True) | ||
|
||
def lift_store(self, assignment: mediumlevelil.MediumLevelILStoreSsa, **kwargs) -> Assignment: | ||
"""Lift a store operation to pseudo (e.g. [ebp+4] = eax).""" | ||
return Assignment( | ||
UnaryOperation( | ||
OperationType.dereference, | ||
[op := self._lifter.lift(assignment.dest, parent=assignment)], | ||
vartype=op.type, | ||
writes_memory=assignment.dest_memory, | ||
), | ||
self._lifter.lift(assignment.src), | ||
) | ||
|
||
def _lift_contraction(self, assignment: mediumlevelil.MediumLevelILSetVarField, is_aliased=False, **kwargs) -> UnaryOperation: | ||
""" | ||
Lift assignment to lower register part (offset 0 from register start) as contraction (cast) | ||
e.g.: | ||
eax.al = 10; | ||
becomes: | ||
(byte) eax = 10; // Assign(Cast([eax], byte, contraction=true), Constant(10)) | ||
""" | ||
destination_operand = self._lifter.lift(assignment.dest, is_aliased=is_aliased, parent=assignment) | ||
contraction_type = destination_operand.type.resize(assignment.size * self.BYTE_SIZE) | ||
return UnaryOperation(OperationType.cast, [destination_operand], vartype=contraction_type, contraction=True) | ||
|
||
def _lift_masked_operand(self, assignment: mediumlevelil.MediumLevelILSetVarField, is_aliased=False, **kwargs) -> BinaryOperation: | ||
"""Lift the rhs value for subregister assignments (e.g. eax.ah = x <=> eax = (eax & 0xffff00ff) + (x << 2)).""" | ||
return BinaryOperation( | ||
OperationType.bitwise_or, | ||
[ | ||
BinaryOperation( | ||
OperationType.bitwise_and, | ||
[ | ||
self._lifter.lift(assignment.prev, parent=assignment, is_aliased=is_aliased), | ||
Constant( | ||
self._get_all_ones_mask_for_type(assignment.dest.var.type.width) | ||
- self._get_all_ones_mask_for_type(assignment.size) | ||
<< (assignment.offset * self.BYTE_SIZE) | ||
), | ||
], | ||
vartype=self._lifter.lift(assignment.src.expr_type, parent=assignment), | ||
), | ||
BinaryOperation( | ||
OperationType.left_shift, | ||
[self._lifter.lift(assignment.src, parent=assignment), Constant(assignment.offset * self.BYTE_SIZE)], | ||
vartype=self._lifter.lift(assignment.src.expr_type, parent=assignment), | ||
), | ||
], | ||
vartype=self._lifter.lift(assignment.expr_type, parent=assignment), | ||
) | ||
|
||
def _lift_mask_high(self, instruction: mediumlevelil.MediumLevelILSetVarField, **kwargs) -> BinaryOperation: | ||
""" | ||
Lift an instruction masking the higher part of a value. | ||
e.g. eax.al = eax & 0x000000ff | ||
""" | ||
return BinaryOperation( | ||
OperationType.bitwise_and, | ||
[op := self._lifter.lift(instruction.src, parent=instruction), Constant(self._get_all_ones_mask_for_type(instruction.size))], | ||
vartype=op.type.resize(instruction.size * self.BYTE_SIZE), | ||
) | ||
|
||
def _get_all_ones_mask_for_type(self, type_size: int, **kwargs) -> int: | ||
"""Generate a bit mask for the given type_size.""" | ||
return int(2 ** (type_size * self.BYTE_SIZE) - 1) | ||
|
||
def lift_split_assignment(self, assignment: mediumlevelil.MediumLevelILSetVarSplit, **kwargs) -> Assignment: | ||
"""Lift an instruction writing to a register pair such as MUL instructions.""" | ||
return Assignment( | ||
RegisterPair( | ||
high := self._lifter.lift(assignment.high, parent=assignment), | ||
low := self._lifter.lift(assignment.low, parent=assignment), | ||
vartype=high.type.resize((high.type.size + low.type.size)), | ||
), | ||
self._lifter.lift(assignment.src, parent=assignment), | ||
) | ||
|
||
def _lift_store_struct(self, instruction: mediumlevelil.MediumLevelILStoreStruct, **kwargs) -> Assignment: | ||
"""Lift a MLIL_STORE_STRUCT_SSA instruction to pseudo (e.g. object->field = x).""" | ||
vartype = self._lifter.lift(instruction.dest.expr_type) | ||
return Assignment( | ||
UnaryOperation( | ||
OperationType.dereference, | ||
[ | ||
BinaryOperation( | ||
OperationType.plus, | ||
[ | ||
UnaryOperation(OperationType.cast, [self._lifter.lift(instruction.dest)], vartype=Pointer(Integer.char())), | ||
Constant(instruction.offset), | ||
], | ||
vartype=vartype, | ||
), | ||
], | ||
vartype=Pointer(vartype), | ||
), | ||
self._lifter.lift(instruction.src), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
"""Module implementing the handler for binaryninja's binary operations.""" | ||
from functools import partial | ||
|
||
from binaryninja import MediumLevelILInstruction, mediumlevelil | ||
from decompiler.frontend.lifter import Handler | ||
from decompiler.structures.pseudo import BinaryOperation, OperationType | ||
|
||
|
||
class BinaryOperationHandler(Handler): | ||
"""Handler lifting mlil binary operation to pseudo operations.""" | ||
|
||
def register(self): | ||
"""Register the handler at the parent lifter.""" | ||
self._lifter.HANDLERS.update( | ||
{ | ||
mediumlevelil.MediumLevelILAdd: partial(self.lift_binary_operation, OperationType.plus), | ||
mediumlevelil.MediumLevelILFadd: partial(self.lift_binary_operation, OperationType.plus_float), | ||
mediumlevelil.MediumLevelILAdc: partial(self._lift_binary_operation_with_carry, OperationType.plus), | ||
mediumlevelil.MediumLevelILSub: partial(self.lift_binary_operation, OperationType.minus), | ||
mediumlevelil.MediumLevelILFsub: partial(self.lift_binary_operation, OperationType.minus_float), | ||
mediumlevelil.MediumLevelILSbb: partial(self._lift_binary_operation_with_carry, OperationType.minus), | ||
mediumlevelil.MediumLevelILAnd: partial(self.lift_binary_operation, OperationType.bitwise_and), | ||
mediumlevelil.MediumLevelILOr: partial(self.lift_binary_operation, OperationType.bitwise_or), | ||
mediumlevelil.MediumLevelILXor: partial(self.lift_binary_operation, OperationType.bitwise_xor), | ||
mediumlevelil.MediumLevelILLsl: partial(self.lift_binary_operation, OperationType.left_shift), | ||
mediumlevelil.MediumLevelILLsr: partial(self.lift_binary_operation, OperationType.right_shift_us), | ||
mediumlevelil.MediumLevelILAsr: partial(self.lift_binary_operation, OperationType.right_shift), | ||
mediumlevelil.MediumLevelILRol: partial(self.lift_binary_operation, OperationType.left_rotate), | ||
mediumlevelil.MediumLevelILRor: partial(self.lift_binary_operation, OperationType.right_rotate), | ||
mediumlevelil.MediumLevelILMul: partial(self.lift_binary_operation, OperationType.multiply), | ||
mediumlevelil.MediumLevelILFmul: partial(self.lift_binary_operation, OperationType.multiply_float), | ||
mediumlevelil.MediumLevelILMulsDp: partial(self.lift_binary_operation, OperationType.multiply), | ||
mediumlevelil.MediumLevelILMuluDp: partial(self.lift_binary_operation, OperationType.multiply_us), | ||
mediumlevelil.MediumLevelILFdiv: partial(self.lift_binary_operation, OperationType.divide_float), | ||
mediumlevelil.MediumLevelILDivs: partial(self.lift_binary_operation, OperationType.divide), | ||
mediumlevelil.MediumLevelILDivsDp: partial(self.lift_binary_operation, OperationType.divide), | ||
mediumlevelil.MediumLevelILDivu: partial(self.lift_binary_operation, OperationType.divide_us), | ||
mediumlevelil.MediumLevelILDivuDp: partial(self.lift_binary_operation, OperationType.divide_us), | ||
mediumlevelil.MediumLevelILMods: partial(self.lift_binary_operation, OperationType.modulo), | ||
mediumlevelil.MediumLevelILModsDp: partial(self.lift_binary_operation, OperationType.modulo), | ||
mediumlevelil.MediumLevelILModu: partial(self.lift_binary_operation, OperationType.modulo_us), | ||
mediumlevelil.MediumLevelILModuDp: partial(self.lift_binary_operation, OperationType.modulo_us), | ||
mediumlevelil.MediumLevelILTestBit: partial(self.lift_binary_operation, OperationType.bitwise_and), | ||
} | ||
) | ||
|
||
def lift_binary_operation(self, op_type: OperationType, operation: MediumLevelILInstruction, **kwargs) -> BinaryOperation: | ||
"""Lift the given binary operation (e.g. a + b, a % b, ..)""" | ||
return BinaryOperation( | ||
op_type, | ||
[self._lifter.lift(x, parent=operation) for x in operation.operands], | ||
vartype=self._lifter.lift(operation.expr_type, parent=operation), | ||
) | ||
|
||
def _lift_binary_operation_with_carry(self, op_type: OperationType, operation: MediumLevelILInstruction, **kwargs) -> BinaryOperation: | ||
"""Lift the adc assembler instruction as two nested BinaryOperations.""" | ||
operands = [self._lifter.lift(x, parent=operation) for x in operation.operands] | ||
return BinaryOperation( | ||
op_type, | ||
[operands[0], BinaryOperation(OperationType.plus, [operands[1], operands[2]])], | ||
vartype=self._lifter.lift(operation.expr_type, parent=operation), | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
"""Module implementing the binaryninja CallHandler.""" | ||
from functools import partial | ||
from typing import List | ||
|
||
from binaryninja import MediumLevelILInstruction, Tailcall, mediumlevelil | ||
from decompiler.frontend.lifter import Handler | ||
from decompiler.structures.pseudo import Assignment, Call, ImportedFunctionSymbol, IntrinsicSymbol, ListOperation | ||
|
||
|
||
class CallHandler(Handler): | ||
"""Class lifting mlil calls to their pseudo counterparts.""" | ||
|
||
def register(self): | ||
"""Register the handler in its parent lifter.""" | ||
self._lifter.HANDLERS.update( | ||
{ | ||
mediumlevelil.MediumLevelILCall: self.lift_call, | ||
mediumlevelil.MediumLevelILCallSsa: partial(self.lift_call, ssa=True), | ||
mediumlevelil.MediumLevelILCallUntyped: self.lift_call, | ||
mediumlevelil.MediumLevelILCallUntypedSsa: partial(self.lift_call, ssa=True), | ||
mediumlevelil.MediumLevelILSyscall: self.lift_syscall, | ||
mediumlevelil.MediumLevelILSyscallSsa: partial(self.lift_syscall, ssa=True), | ||
mediumlevelil.MediumLevelILSyscallUntyped: self.lift_syscall, | ||
mediumlevelil.MediumLevelILSyscallUntypedSsa: partial(self.lift_syscall, ssa=True), | ||
mediumlevelil.MediumLevelILTailcall: self.lift_call, | ||
mediumlevelil.MediumLevelILTailcallSsa: partial(self.lift_call, ssa=True), | ||
mediumlevelil.MediumLevelILTailcallUntyped: self.lift_call, | ||
mediumlevelil.MediumLevelILTailcallUntypedSsa: partial(self.lift_call, ssa=True), | ||
mediumlevelil.MediumLevelILIntrinsic: self.lift_intrinsic, | ||
mediumlevelil.MediumLevelILIntrinsicSsa: partial(self.lift_intrinsic, ssa=True), | ||
} | ||
) | ||
|
||
def lift_call(self, call: mediumlevelil.MediumLevelILCall, ssa: bool = False, **kwargs) -> Assignment: | ||
"""Lift mlil call instructions, remembering the new memory version.""" | ||
return Assignment( | ||
ListOperation([self._lifter.lift(output, parent=call) for output in call.output]), | ||
Call( | ||
dest := self._lifter.lift(call.dest, parent=call), | ||
[self._lifter.lift(parameter, parent=call) for parameter in call.params], | ||
vartype=dest.type.copy(), | ||
writes_memory=call.output_dest_memory if ssa else None, | ||
meta_data={"param_names": self._lift_call_parameter_names(call), "is_failcall": isinstance(call, Tailcall)}, | ||
), | ||
) | ||
|
||
def lift_syscall(self, call: mediumlevelil.MediumLevelILSyscall, ssa: bool = False, **kwargs) -> Assignment: | ||
"""Lift a syscall instructions invoking system level functionality.""" | ||
return Assignment( | ||
ListOperation([self._lifter.lift(output, parent=call) for output in call.output]), | ||
Call( | ||
dest := ImportedFunctionSymbol("Syscall", value=-1), | ||
[self._lifter.lift(parameter, parent=call) for parameter in call.params], | ||
vartype=dest.type.copy(), | ||
writes_memory=call.output_dest_memory if ssa else None, | ||
meta_data={"param_names": self._lift_call_parameter_names(call)}, | ||
), | ||
) | ||
|
||
def lift_intrinsic(self, call: mediumlevelil.MediumLevelILIntrinsic, ssa: bool = False, **kwargs) -> Assignment: | ||
""" | ||
Lift operations not supported by mlil and modeled as intrinsic operations. | ||
e.g. temp0_1#2 = _mm_add_epi32(zmm1#2, zmm5#1) | ||
""" | ||
return Assignment( | ||
ListOperation([self._lifter.lift(value, parent=call) for value in call.output]), | ||
Call( | ||
IntrinsicSymbol(str(call.intrinsic)), | ||
[self._lifter.lift(param, parent=call) for param in call.params], | ||
writes_memory=call.output_dest_memory if ssa else None, | ||
), | ||
) | ||
|
||
@staticmethod | ||
def _lift_call_parameter_names(instruction: MediumLevelILInstruction) -> List[str]: | ||
"""Lift parameter names of call from type string of instruction.dest.expr_type""" | ||
clean_type_string_of_parameters = instruction.dest.expr_type.get_string_after_name().strip("()") | ||
return [type_parameter.rsplit(" ", 1)[-1] for type_parameter in clean_type_string_of_parameters.split(",")] |
Oops, something went wrong.