diff --git a/angr/ailment/__init__.py b/angr/ailment/__init__.py index e074f7f24..4402d7a36 100644 --- a/angr/ailment/__init__.py +++ b/angr/ailment/__init__.py @@ -2,6 +2,8 @@ from __future__ import annotations import logging +from angr.rustylib.ailment import ExpressionKind + from . import expression, statement from .block import Block from .block_walker import AILBlockRewriter, AILBlockViewer, AILBlockWalker @@ -69,6 +71,7 @@ __all__ = [ "Const", "Expr", "Expression", + "ExpressionKind", "IRSBConverter", "Manager", "NoOp", diff --git a/angr/analyses/decompiler/peephole_optimizations/eager_eval.py b/angr/analyses/decompiler/peephole_optimizations/eager_eval.py index 17aae80e1..2558516f1 100644 --- a/angr/analyses/decompiler/peephole_optimizations/eager_eval.py +++ b/angr/analyses/decompiler/peephole_optimizations/eager_eval.py @@ -2,6 +2,7 @@ from __future__ import annotations from math import gcd +from angr.ailment import ExpressionKind from angr.ailment.expression import BinaryOp, Const, Convert, StackBaseOffset, UnaryOp from angr.utils.bits import sign_extend @@ -216,7 +217,7 @@ class EagerEvaluation(PeepholeOptimizationExprBase): # constant multiplication mask = (1 << expr.bits) - 1 return Const(expr.idx, (op0.value * op1.value) & mask, expr.bits, **expr.tags) - if {type(op0), type(op1)} == {BinaryOp, Const}: + if {op0.pykind, op1.pykind} == {ExpressionKind.BinaryOp, ExpressionKind.Const}: op0, op1 = expr.operands const_, x0 = (op0, op1) if isinstance(op0, Const) else (op1, op0) if x0.op == "Mul" and (isinstance(x0.operands[0], Const) or isinstance(x0.operands[1], Const)): diff --git a/tests/analyses/decompiler/test_peephole_optimizations.py b/tests/analyses/decompiler/test_peephole_optimizations.py index c3108a5fb..76a1ec4a1 100755 --- a/tests/analyses/decompiler/test_peephole_optimizations.py +++ b/tests/analyses/decompiler/test_peephole_optimizations.py @@ -177,6 +177,22 @@ class TestPeepholeOptimizations(unittest.TestCase): ) assert opt.optimize(expr) is None + def test_eager_eval_var_mul_a_mul_b(self): + bin_path = os.path.join(test_location, "x86_64", "ls_ubuntu_2004") + proj = angr.Project(bin_path) + _ = proj.analyses.CFGFast(normalize=True, regions=[(0x410FC0, 0x410FC0 + 1000)]) + f = proj.kb.functions[0x410FC0] + d = proj.analyses.Decompiler(f, fail_fast=True) + assert d.codegen is not None and d.codegen.text is not None + + # a * 2 * 5 ==> a * 10 + # we should see `a * 10`` or `a % 10` in the output + if "* 2" in d.codegen.text or "* 5" in d.codegen.text: + raise AssertionError( + "The decompiler output should not contain `* 2` or `* 5` after peephole optimization, but it does." + ) + assert "* 10" in d.codegen.text or "% 10" in d.codegen.text + def test_cmp_masked_shift(self): proj = angr.load_shellcode(b"\x90", "AMD64") manager = Manager()