diff --git a/decompiler/pipeline/pipeline.py b/decompiler/pipeline/pipeline.py index 74e4267a3..00c0dba0f 100644 --- a/decompiler/pipeline/pipeline.py +++ b/decompiler/pipeline/pipeline.py @@ -9,6 +9,7 @@ from decompiler.pipeline.preprocessing import ( Coherence, CompilerIdiomHandling, + FindFunctionPointer, InsertMissingDefinitions, MemPhiConverter, PhiFunctionFixer, @@ -35,6 +36,7 @@ MemPhiConverter, InsertMissingDefinitions, PhiFunctionFixer, + FindFunctionPointer, ] POSTPROCESSING_STAGES = [OutOfSsaTranslation, PatternIndependentRestructuring] diff --git a/decompiler/pipeline/preprocessing/__init__.py b/decompiler/pipeline/preprocessing/__init__.py index 5dfe26cc0..0b3aad3af 100644 --- a/decompiler/pipeline/preprocessing/__init__.py +++ b/decompiler/pipeline/preprocessing/__init__.py @@ -2,6 +2,7 @@ from .coherence import Coherence from .compiler_idiom_handling import CompilerIdiomHandling +from .find_function_pointer import FindFunctionPointer from .mem_phi_conversion import MemPhiConverter from .missing_definitions import InsertMissingDefinitions from .phi_predecessors import PhiFunctionFixer diff --git a/decompiler/pipeline/preprocessing/find_function_pointer.py b/decompiler/pipeline/preprocessing/find_function_pointer.py new file mode 100644 index 000000000..7de378d3b --- /dev/null +++ b/decompiler/pipeline/preprocessing/find_function_pointer.py @@ -0,0 +1,33 @@ +"""Module to find and declare function pointer variables""" + +from decompiler.pipeline.stage import PipelineStage +from decompiler.structures.pseudo.instructions import Assignment, Variable +from decompiler.structures.pseudo.operations import Call +from decompiler.structures.pseudo.typing import FunctionTypeDef, Pointer +from decompiler.task import DecompilerTask + + +class FindFunctionPointer(PipelineStage): + """Pipeline stage to identify and annotate function pointers in the decompiled code.""" + + name = "find-function-pointer" + + def run(self, task: DecompilerTask): + """ + Run the pipeline stage in the given task, search in all expressions for a + variable that is called and adjust its type information. + """ + for block in task.graph: + for expression in block: + if ( + isinstance(expression, Assignment) + and isinstance(expression.value, Call) + and isinstance(expression.value.function, Variable) + ): + expression.value.function._type = Pointer( + basetype=FunctionTypeDef( + size=expression.value.function.type.size, + return_type=expression.value.function.type, + parameters=tuple(expression.value.parameters), + ), + ) diff --git a/decompiler/util/default.json b/decompiler/util/default.json index e2eedadac..5cc50e766 100644 --- a/decompiler/util/default.json +++ b/decompiler/util/default.json @@ -507,10 +507,10 @@ "default": [ "expression-propagation", "bit-field-comparison-unrolling", - "type-propagation", "dead-path-elimination", "dead-loop-elimination", "dead-code-elimination", + "type-propagation", "expression-propagation-memory", "expression-propagation-function-call", "expression-simplification-cfg", diff --git a/tests/pipeline/preprocessing/test_find_function_pointer.py b/tests/pipeline/preprocessing/test_find_function_pointer.py new file mode 100644 index 000000000..a56af525e --- /dev/null +++ b/tests/pipeline/preprocessing/test_find_function_pointer.py @@ -0,0 +1,101 @@ +from decompiler.pipeline.preprocessing import FindFunctionPointer +from decompiler.structures.graphs.cfg import BasicBlock, ControlFlowGraph, FalseCase, TrueCase, UnconditionalEdge +from decompiler.structures.pseudo import ( + Assignment, + Call, + Condition, + Constant, + ImportedFunctionSymbol, + Integer, + ListOperation, + OperationType, + Variable, +) +from decompiler.structures.pseudo.instructions import Branch, Return +from decompiler.structures.pseudo.typing import FunctionTypeDef, Pointer +from decompiler.task import DecompilerTask + + +def test_set_variable_to_function_pointer(): + """ + Test the change of a variable type to function pointer if there is a call on this variable. + + a = 0x0804c020 + b = 1 + if (a == 0) + return b + else + a() + """ + cfg = ControlFlowGraph() + var_a = Variable("a", Integer.int32_t()) + var_b = Variable("b", Integer.int32_t()) + cfg.add_nodes_from( + [ + n0 := BasicBlock(0, instructions=[Assignment(var_a, Constant(0x0804C020)), Assignment(var_b, Constant(1))]), + n1 := BasicBlock(1, instructions=[Branch(Condition(OperationType.equal, [var_a, Constant(0)]))]), + n2 := BasicBlock(2, instructions=[Return([var_b])]), + n3 := BasicBlock(3, instructions=[Assignment(ListOperation([]), Call(var_a, []))]), + ] + ) + cfg.add_edges_from([UnconditionalEdge(n0, n1), TrueCase(n1, n2), FalseCase(n1, n3)]) + FindFunctionPointer().run(DecompilerTask("test", cfg)) + assert var_a.type == Pointer(FunctionTypeDef(32, Integer.int32_t(), ())) + + +def test_set_variable_to_function_pointer_with_parameters(): + """ + Test the change of a variable type to function pointer if there is a call on this variable with parameters. + + a = 0x0804c020 + b = 1 + if (a == 0) + return b + else + a(c, d) + """ + cfg = ControlFlowGraph() + var_a = Variable("a", Integer.int32_t()) + var_b = Variable("b", Integer.int32_t()) + var_c = Variable("c", Integer.int32_t()) + var_d = Variable("d", Integer.int32_t()) + cfg.add_nodes_from( + [ + n0 := BasicBlock(0, instructions=[Assignment(var_a, Constant(0x0804C020)), Assignment(var_b, Constant(1))]), + n1 := BasicBlock(1, instructions=[Branch(Condition(OperationType.equal, [var_a, Constant(0)]))]), + n2 := BasicBlock(2, instructions=[Return([var_b])]), + n3 := BasicBlock(3, instructions=[Assignment(ListOperation([]), Call(var_a, [var_c, var_d]))]), + ] + ) + cfg.add_edges_from([UnconditionalEdge(n0, n1), TrueCase(n1, n2), FalseCase(n1, n3)]) + FindFunctionPointer().run(DecompilerTask("test", cfg)) + assert var_a.type == Pointer(FunctionTypeDef(32, Integer.int32_t(), (var_c, var_d))) + + +def test_skip_set_variable_to_function_pointer(): + """ + Test the skip of a change of a variable type to function pointer if there is a call without a variable. + + a = 0x0804c020 + b = 1 + if (a == 0) + return b + else + printf("%d\n", a) + """ + cfg = ControlFlowGraph() + var_a = Variable("a", Integer.int32_t()) + var_b = Variable("b", Integer.int32_t()) + cfg.add_nodes_from( + [ + n0 := BasicBlock(0, instructions=[Assignment(var_a, Constant(0x0804C020)), Assignment(var_b, Constant(1))]), + n1 := BasicBlock(1, instructions=[Branch(Condition(OperationType.equal, [var_a, Constant(0)]))]), + n2 := BasicBlock(2, instructions=[Return([var_b])]), + n3 := BasicBlock( + 3, instructions=[Assignment(ListOperation([]), Call(ImportedFunctionSymbol("printf", 0), [Constant("%d\n"), var_a]))] + ), + ] + ) + cfg.add_edges_from([UnconditionalEdge(n0, n1), TrueCase(n1, n2), FalseCase(n1, n3)]) + FindFunctionPointer().run(DecompilerTask("test", cfg)) + assert not any(isinstance(variable.type, Pointer) for variable in cfg.get_variables())