Skip to content

Commit

Permalink
Version 2.5 class bases lifter (#26)
Browse files Browse the repository at this point in the history
* implementation of the new lifter.
  • Loading branch information
0x6e62 authored Feb 7, 2022
1 parent b3efece commit c1cdab6
Show file tree
Hide file tree
Showing 17 changed files with 871 additions and 770 deletions.
4 changes: 2 additions & 2 deletions decompiler/frontend/binaryninja/frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ def _extract_cfg(self, function: Function, options: Options = None) -> ControlFl
def _extract_return_type_and_params(self, function: Function) -> Tuple[Type, List[Variable]]:
"""Extracts the type of the return value of the function and the list of its parameters"""
lifter = BinaryninjaLifter()
params: List[Variable] = [lifter.lift_function_parameter(param) for param in function.function_type.parameters]
return_type: Type = lifter.lift_type(function.function_type.return_value)
params: List[Variable] = [lifter.lift(param) for param in function.function_type.parameters]
return_type: Type = lifter.lift(function.function_type.return_value)
return return_type, params

def _get_address(self, text: str) -> int:
Expand Down
25 changes: 25 additions & 0 deletions decompiler/frontend/binaryninja/handlers/__init__.py
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 decompiler/frontend/binaryninja/handlers/assignments.py
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),
)
62 changes: 62 additions & 0 deletions decompiler/frontend/binaryninja/handlers/binary.py
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),
)
79 changes: 79 additions & 0 deletions decompiler/frontend/binaryninja/handlers/calls.py
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(",")]
Loading

0 comments on commit c1cdab6

Please sign in to comment.