From c3c790af0f2b4aa90b55e61927cc20792672375e Mon Sep 17 00:00:00 2001 From: Mariia Rybalka Date: Mon, 28 Aug 2023 09:37:24 +0200 Subject: [PATCH] Add review changes --- decompiler/backend/cexpressiongenerator.py | 6 +++--- decompiler/frontend/binaryninja/frontend.py | 4 ++-- .../frontend/binaryninja/handlers/assignments.py | 7 +++---- decompiler/frontend/binaryninja/parser.py | 14 ++++++++++---- decompiler/task.py | 2 +- tests/frontend/test_parser.py | 10 +++++----- 6 files changed, 24 insertions(+), 19 deletions(-) diff --git a/decompiler/backend/cexpressiongenerator.py b/decompiler/backend/cexpressiongenerator.py index 081dfc27c..d6111961c 100644 --- a/decompiler/backend/cexpressiongenerator.py +++ b/decompiler/backend/cexpressiongenerator.py @@ -66,6 +66,7 @@ class CExpressionGenerator(DataflowObjectVisitorInterface): OperationType.greater_or_equal_us: ">=", OperationType.dereference: "*", OperationType.address: "&", + OperationType.member_access: ".", # Handled in code # OperationType.cast: "cast", # OperationType.pointer: "point", @@ -182,9 +183,8 @@ def visit_list_operation(self, op: operations.ListOperation) -> str: def visit_unary_operation(self, op: operations.UnaryOperation) -> str: """Return a string representation of the given unary operation (e.g. !a or &a).""" if isinstance(op, MemberAccess): - if isinstance(op.struct_variable.type, Pointer): - return f"{self.visit(op.struct_variable)}->{op.member_name}" - return f"{self.visit(op.struct_variable)}.{op.member_name}" + operator_str = "->" if isinstance(op.struct_variable.type, Pointer) else self.C_SYNTAX[op.operation] + return f"{self.visit(op.struct_variable)}{operator_str}{op.member_name}" operand = self._visit_bracketed(op.operand) if self._has_lower_precedence(op.operand, op) else self.visit(op.operand) if op.operation == OperationType.cast and op.contraction: return f"({int(op.type.size / 8)}: ){operand}" diff --git a/decompiler/frontend/binaryninja/frontend.py b/decompiler/frontend/binaryninja/frontend.py index f2ac27ed6..94c2c8546 100644 --- a/decompiler/frontend/binaryninja/frontend.py +++ b/decompiler/frontend/binaryninja/frontend.py @@ -12,7 +12,7 @@ from decompiler.task import DecompilerTask from decompiler.util.options import Options -from ...structures.pseudo.complextypes import ComplexTypeMap +from decompiler.structures.pseudo.complextypes import ComplexTypeMap from ..frontend import Frontend from .lifter import BinaryninjaLifter from .parser import BinaryninjaParser @@ -160,4 +160,4 @@ def _extract_cfg(self, function: Function, options: Options) -> Tuple[ControlFlo report_threshold = options.getint("lifter.report_threshold", fallback=3) no_masks = options.getboolean("lifter.no_bit_masks", fallback=True) parser = BinaryninjaParser(BinaryninjaLifter(no_masks, bv=function.view), report_threshold) - return parser.parse(function) + return parser.parse(function), parser.complex_types diff --git a/decompiler/frontend/binaryninja/handlers/assignments.py b/decompiler/frontend/binaryninja/handlers/assignments.py index aed87a080..7991ce8f2 100644 --- a/decompiler/frontend/binaryninja/handlers/assignments.py +++ b/decompiler/frontend/binaryninja/handlers/assignments.py @@ -1,7 +1,6 @@ """Module implementing the AssignmentHandler for binaryninja.""" import logging from functools import partial -from typing import Union import binaryninja from binaryninja import mediumlevelil @@ -20,7 +19,7 @@ UnaryOperation, ) from decompiler.structures.pseudo.complextypes import Struct -from decompiler.structures.pseudo.complextypes import Union as _Union +from decompiler.structures.pseudo.complextypes import Union from decompiler.structures.pseudo.operations import MemberAccess @@ -101,7 +100,7 @@ def lift_get_field(self, instruction: mediumlevelil.MediumLevelILVarField, is_al (x = ) <- for the sake of example, only rhs expression is lifted here. """ source = self._lifter.lift(instruction.src, is_aliased=is_aliased, parent=instruction) - if isinstance(source.type, Struct) or isinstance(source.type, _Union): + if isinstance(source.type, Struct) or isinstance(source.type, Union): return self._get_field_as_member_access(instruction, source, **kwargs) cast_type = source.type.resize(instruction.size * self.BYTE_SIZE) if instruction.offset: @@ -134,7 +133,7 @@ def lift_store(self, assignment: mediumlevelil.MediumLevelILStoreSsa, **kwargs) self._lifter.lift(assignment.src), ) - def _lift_store_destination(self, store_assignment: mediumlevelil.MediumLevelILStoreSsa) -> Union[UnaryOperation, GlobalVariable]: + def _lift_store_destination(self, store_assignment: mediumlevelil.MediumLevelILStoreSsa) -> UnaryOperation | GlobalVariable: """ Lift destination operand of store operation which is used for modelling both assignments of dereferences and global variables. """ diff --git a/decompiler/frontend/binaryninja/parser.py b/decompiler/frontend/binaryninja/parser.py index f510a65a1..4abe18e5e 100644 --- a/decompiler/frontend/binaryninja/parser.py +++ b/decompiler/frontend/binaryninja/parser.py @@ -26,9 +26,10 @@ def __init__(self, lifter: Lifter, report_threshold: int = 3): self._lifter = lifter self._unlifted_instructions: List[MediumLevelILInstruction] = [] self._report_threshold = int(report_threshold) + self._complex_types = None - def parse(self, function: Function) -> Tuple[ControlFlowGraph, ComplexTypeMap]: - """Generate a cfg and complex types from the given function.""" + def parse(self, function: Function) -> ControlFlowGraph: + """Generate a cfg from the given function.""" cfg = ControlFlowGraph() index_to_BasicBlock = dict() for basic_block in function.medium_level_il.ssa_form: @@ -36,9 +37,14 @@ def parse(self, function: Function) -> Tuple[ControlFlowGraph, ComplexTypeMap]: cfg.add_node(index_to_BasicBlock[basic_block.index]) for basic_block in function.medium_level_il.ssa_form: self._add_basic_block_edges(cfg, index_to_BasicBlock, basic_block) - complex_types = self._lifter.complex_types + self._complex_types = self._lifter.complex_types self._report_lifter_errors() - return cfg, complex_types + return cfg + + @property + def complex_types(self) -> ComplexTypeMap: + """Return complex type map for the given function.""" + return self._complex_types def _add_basic_block_edges(self, cfg: ControlFlowGraph, vertices: dict, basic_block: MediumLevelILBasicBlock) -> None: """Add all outgoing edges of the given basic block to the given cfg.""" diff --git a/decompiler/task.py b/decompiler/task.py index cebb04843..38f149a7b 100644 --- a/decompiler/task.py +++ b/decompiler/task.py @@ -97,5 +97,5 @@ def failure_message(self) -> str: @property def complex_types(self) -> ComplexTypeMap: - """Returns complex types present in the function (structs, unions, enums, etc.).""" + """Return complex types present in the function (structs, unions, enums, etc.).""" return self._complex_types diff --git a/tests/frontend/test_parser.py b/tests/frontend/test_parser.py index e2febaa14..a0f41437b 100644 --- a/tests/frontend/test_parser.py +++ b/tests/frontend/test_parser.py @@ -125,7 +125,7 @@ def parser(): def test_trivial(parser): """Function with a single empty basic block.""" function = MockFunction([MockBlock(0, [])]) - cfg, _ = parser.parse(function) + cfg = parser.parse(function) assert len(cfg.nodes) == 1 assert len(list(cfg.instructions)) == 0 assert len(cfg.edges) == 0 @@ -140,7 +140,7 @@ def test_chain(parser): MockBlock(2, []), ] ) - cfg, _ = parser.parse(function) + cfg = parser.parse(function) assert [v.name for v in cfg.nodes] == [0, 1, 2] assert [(edge.source.name, edge.sink.name) for edge in cfg.edges] == [(0, 1), (1, 2)] assert len(list(cfg.instructions)) == 0 @@ -157,7 +157,7 @@ def test_branch(parser): MockBlock(3, []), ] ) - cfg, _ = parser.parse(function) + cfg = parser.parse(function) assert [v.name for v in cfg.nodes] == [0, 1, 2, 3] assert [(edge.source.address, edge.sink.address) for edge in cfg.edges] == [(0, 1), (0, 2), (1, 3), (2, 3)] assert len(list(cfg.instructions)) == 0 @@ -186,7 +186,7 @@ def test_switch(parser): MockBlock(4, []), ] ) - cfg, _ = parser.parse(function) + cfg = parser.parse(function) assert [v.name for v in cfg.nodes] == [0, 1, 2, 3, 4] assert [(edge.source.name, edge.sink.name) for edge in cfg.edges] == [(0, 1), (0, 2), (0, 3), (1, 4), (2, 4), (3, 4)] assert [getattr(edge, "cases", None) for edge in cfg.edges] == [ @@ -210,7 +210,7 @@ def test_loop(parser): MockBlock(3, []), ] ) - cfg, _ = parser.parse(function) + cfg = parser.parse(function) assert [v.name for v in cfg.nodes] == [0, 1, 2, 3] assert [(edge.source.address, edge.sink.address) for edge in cfg.edges] == [(0, 1), (1, 2), (2, 1), (2, 3)] assert len(list(cfg.instructions)) == 0