From 54cc7fd74767ff577db735a9cdf5b7ac8175e0f7 Mon Sep 17 00:00:00 2001 From: Mariia Rybalka Date: Tue, 20 Jun 2023 15:16:35 +0200 Subject: [PATCH] Add init support for structure variable members being written, identified occasional recursion problem by struct members. --- decompiler/backend/cexpressiongenerator.py | 2 +- .../binaryninja/handlers/assignments.py | 29 ++++++++----------- .../frontend/binaryninja/handlers/types.py | 3 ++ .../frontend/binaryninja/handlers/unary.py | 7 ++--- decompiler/structures/pseudo/operations.py | 17 +++++++---- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/decompiler/backend/cexpressiongenerator.py b/decompiler/backend/cexpressiongenerator.py index 52d35c356..0f6a496a1 100644 --- a/decompiler/backend/cexpressiongenerator.py +++ b/decompiler/backend/cexpressiongenerator.py @@ -7,7 +7,7 @@ from decompiler.structures.pseudo import Float, Integer, OperationType, Pointer, StringSymbol from decompiler.structures.pseudo import instructions as instructions from decompiler.structures.pseudo import operations as operations -from decompiler.structures.pseudo.operations import StructMember +from decompiler.structures.pseudo.operations import StructMemberAccess from decompiler.structures.pseudo.typing import StructureType from decompiler.structures.visitors.interfaces import DataflowObjectVisitorInterface diff --git a/decompiler/frontend/binaryninja/handlers/assignments.py b/decompiler/frontend/binaryninja/handlers/assignments.py index 8d9e5ab7d..27ba26fb7 100644 --- a/decompiler/frontend/binaryninja/handlers/assignments.py +++ b/decompiler/frontend/binaryninja/handlers/assignments.py @@ -1,4 +1,5 @@ """Module implementing the AssignmentHandler for binaryninja.""" +import logging from functools import partial from typing import Union @@ -16,6 +17,7 @@ RegisterPair, UnaryOperation, ) +from decompiler.structures.pseudo.operations import StructMemberAccess class AssignmentHandler(Handler): @@ -170,21 +172,14 @@ def lift_split_assignment(self, assignment: mediumlevelil.MediumLevelILSetVarSpl 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), - writes_memory=instruction.dest_memory - ), - self._lifter.lift(instruction.src), + struct_variable = self._lifter.lift(instruction.dest) + struct_member_access = StructMemberAccess( + src=struct_variable, + member_name=vartype.type.members.get(instruction.offset), + offset=instruction.offset, + operands=[struct_variable], + vartype=vartype, + writes_memory=instruction.dest_memory, ) + src = self._lifter.lift(instruction.src) + return Assignment(struct_member_access, src) diff --git a/decompiler/frontend/binaryninja/handlers/types.py b/decompiler/frontend/binaryninja/handlers/types.py index d797e18ec..eb7ecc00f 100644 --- a/decompiler/frontend/binaryninja/handlers/types.py +++ b/decompiler/frontend/binaryninja/handlers/types.py @@ -71,6 +71,9 @@ def lift_struct(self, struct: StructureType, **kwargs) -> PseudoStructureType: return PseudoStructureType(tag_name=struct_name, members=members_dict, size=0) def lift_struct_member(self, member: StructureMember) -> PseudoStructureMember: + # TODO handle the case when struct member is a pointer on the same struct + if isinstance(member.type, PointerType) and (isinstance(member.type.target, StructureType) or isinstance(member.type.target, NamedTypeReferenceType)): + return CustomType("SomeStructTemp", size=0) return PseudoStructureMember(name=member.name, offset=member.offset, type=self._lifter.lift(member.type), size=0) def lift_void(self, _, **kwargs) -> CustomType: diff --git a/decompiler/frontend/binaryninja/handlers/unary.py b/decompiler/frontend/binaryninja/handlers/unary.py index 424c8a79d..797400259 100644 --- a/decompiler/frontend/binaryninja/handlers/unary.py +++ b/decompiler/frontend/binaryninja/handlers/unary.py @@ -15,7 +15,7 @@ Pointer, UnaryOperation, ) -from decompiler.structures.pseudo.operations import StructMember +from decompiler.structures.pseudo.operations import StructMemberAccess from decompiler.structures.pseudo.typing import StructureType @@ -94,17 +94,16 @@ def _lift_zx_operation(self, instruction: MediumLevelILInstruction, **kwargs) -> ) return self.lift_cast(instruction, **kwargs) - def _lift_load_struct(self, instruction: mediumlevelil.MediumLevelILLoadStruct, **kwargs) -> StructMember: + def _lift_load_struct(self, instruction: mediumlevelil.MediumLevelILLoadStruct, **kwargs) -> StructMemberAccess: """Lift a MLIL_LOAD_STRUCT_SSA (struct member access e.g. var#n->x) instruction.""" # TODO type of struct variable should be either ptr on struct or struct # TODO type of the member hm actually we want member instance to know the struct type. # TODO But it is not the same as vartype - # TODO check what happens if members values are changed struct_variable = self._lifter.lift(instruction.src) struct_ptr: Pointer = self._lifter.lift(instruction.src.expr_type) struct_type: StructureType = struct_ptr.type struct_member_name = struct_type.members.get(instruction.offset).name - return StructMember(src=struct_variable, vartype=struct_ptr, operands=[struct_variable], offset=instruction.offset, member_name=struct_member_name) + return StructMemberAccess(src=struct_variable, vartype=struct_ptr, operands=[struct_variable], offset=instruction.offset, member_name=struct_member_name) def _lift_ftrunc(self, instruction: mediumlevelil.MediumLevelILFtrunc, **kwargs) -> UnaryOperation: """Lift a MLIL_FTRUNC operation.""" diff --git a/decompiler/structures/pseudo/operations.py b/decompiler/structures/pseudo/operations.py index 6b8243130..c826a62a1 100644 --- a/decompiler/structures/pseudo/operations.py +++ b/decompiler/structures/pseudo/operations.py @@ -378,7 +378,7 @@ def accept(self, visitor: DataflowObjectVisitorInterface[T]) -> T: return visitor.visit_unary_operation(self) -class StructMember(Operation): +class StructMemberAccess(Operation): def __init__( self, src: Expression, @@ -388,10 +388,11 @@ def __init__( vartype: Type = UnknownType(), writes_memory: Optional[int] = None, ): - super().__init__(OperationType.struct_member, operands, vartype, writes_memory) + super().__init__(OperationType.struct_member, operands, vartype) self.struct_variable = src self.member_offset = offset self.member_name = member_name + self.writes_memory = writes_memory def __str__(self): return f"{self.struct_variable}->{self.member_name}" @@ -404,21 +405,27 @@ def substitute(self, replacee: Expression, replacement: Expression) -> None: self.struct_variable = replacement self.operands[:] = [replacement] - def copy(self) -> StructMember: + def copy(self) -> StructMemberAccess: """Copy the current UnaryOperation, copying all operands and the type.""" - return StructMember( + return StructMemberAccess( self.struct_variable, self.member_offset, self.member_name, [operand.copy() for operand in self._operands], self._type.copy(), - # writes_memory=self._writes_memory, + writes_memory=self.writes_memory, ) def accept(self, visitor: DataflowObjectVisitorInterface[T]) -> T: """Invoke the appropriate visitor for this Operation.""" return str(self) + def is_read_access(self): + return self.writes_memory is None + + def is_write_access(self): + return self.writes_memory is not None + class BinaryOperation(Operation): """Class representing operations with two operands."""