angr/tests/analyses/decompiler/test_optimization_passes.py
Fish a544e59a63
VariableMap: Track Call prototype and calling_convention. (#6482)
* VariableMap: Track Call prototype and calling_convention.

* Lint code.

* Fix two _handle_stmt_SideEffectStatement.
2026-06-09 02:59:11 -07:00

179 lines
5.2 KiB
Python

#!/usr/bin/env python3
# pylint:disable=missing-class-docstring,no-self-use
from __future__ import annotations
__package__ = __package__ or "tests.analyses.decompiler" # pylint:disable=redefined-builtin
import logging
import unittest
import networkx as nx
import angr
from angr.ailment import Assignment, BinaryOp, Block, Const, Register
from angr.ailment.expression import Call
from angr.ailment.manager import Manager
from angr.ailment.statement import ConditionalJump, Return, Store
from angr.analyses.decompiler.optimization_passes import FlipBooleanCmp
from angr.analyses.decompiler.structurer_nodes import ConditionNode, SequenceNode
log = logging.getLogger(__name__)
# log.setLevel(logging.DEBUG)
def c(v):
"""Simple AIL Const shorthand"""
return Const(0, v, 32)
def r(o):
"""Simple AIL Register shorthand"""
return Register(0, o, 32)
class TestFlipBooleanCmp(unittest.TestCase):
"""
Test FlipBooleanCmp optimization pass.
"""
def test_type2_store_not_moved(self):
"""
Ensure that:
v0 = 123;
if (v0 <= 1000)
v0 = 456;
g_deadbeef = v0;
return;
is not mistakenly transformed to:
v0 = 123;
if (v0 > 1000) {
g_deadbeef = v0;
return;
}
v0 = 456;
"""
flip_size = 1
block_0 = Block(
0x400000,
1,
[
Assignment(0, r(0), c(0x123)),
ConditionalJump(
1, BinaryOp(2, "CmpLE", [r(0), c(0x1000)], False), c(0x400023), c(0x400037), ins_addr=0x400001
),
],
)
block_1 = Block(0x400023, 1, [Assignment(3, r(0), c(0x456)) for _ in range(flip_size)])
block_2 = Block(
0x400037,
1,
[
Store(4, c(0xDEADBEEF), r(0), 4, "Iend_LE"), # Must not be moved
Return(5, []),
],
)
graph = nx.DiGraph()
graph.add_edges_from([(block_0, block_1), (block_0, block_2), (block_1, block_2)])
func = None
proj = angr.load_shellcode(b"\x90\x90", "AMD64")
ri = proj.analyses.RegionIdentifier(func, graph=graph)
rs = proj.analyses.RecursiveStructurer(ri.region, ail_manager=Manager())
seq = rs.result
assert isinstance(seq, SequenceNode)
assert len(seq.nodes) == 3
assert isinstance(seq.nodes[0], Block)
assert isinstance(seq.nodes[1], ConditionNode)
assert isinstance(seq.nodes[2], Block)
assert isinstance(seq.nodes[2].statements[0], Store)
assert isinstance(seq.nodes[2].statements[1], Return)
pre_transform_seq_repr = seq.dbg_repr()
log.debug("Before:\n%s", pre_transform_seq_repr)
manager = Manager()
FlipBooleanCmp(func, manager, flip_size=flip_size, seq=seq, graph=graph)
post_transform_seq_repr = seq.dbg_repr()
log.debug("After:\n%s", post_transform_seq_repr)
assert pre_transform_seq_repr == post_transform_seq_repr
def test_type2_call_not_moved(self):
"""
Ensure that:
v0 = 123;
if (v0 <= 1000)
v0 = 456;
always_called(v0);
return;
is not mistakenly transformed to:
v0 = 123;
if (v0 > 1000) {
always_called(v0);
return;
}
v0 = 456;
"""
flip_size = 1
block_0 = Block(
0x400000,
1,
[
Assignment(0, r(0), c(0x123)),
ConditionalJump(
1, BinaryOp(2, "CmpLE", [r(0), c(0x1000)], False), c(0x400023), c(0x400037), ins_addr=0x400001
),
],
)
block_1 = Block(0x400023, 1, [Assignment(3, r(0), c(0x456)) for _ in range(flip_size)])
block_2 = Block(
0x400037,
1,
[
Call(4, "always_called", [r(0)]), # Must not be moved
Return(5, []),
],
)
graph = nx.DiGraph()
graph.add_edges_from([(block_0, block_1), (block_0, block_2), (block_1, block_2)])
func = None
proj = angr.load_shellcode(b"\x90\x90", "AMD64")
ri = proj.analyses.RegionIdentifier(func, graph=graph)
rs = proj.analyses.RecursiveStructurer(ri.region, ail_manager=Manager())
seq = rs.result
assert isinstance(seq, SequenceNode)
assert len(seq.nodes) == 3
assert isinstance(seq.nodes[0], Block)
assert isinstance(seq.nodes[1], ConditionNode)
assert isinstance(seq.nodes[2], Block)
assert isinstance(seq.nodes[2].statements[0], Call)
assert isinstance(seq.nodes[2].statements[1], Return)
pre_transform_seq_repr = seq.dbg_repr()
log.debug("Before:\n%s", pre_transform_seq_repr)
manager = Manager()
FlipBooleanCmp(func, manager, flip_size=flip_size, seq=seq, graph=graph)
post_transform_seq_repr = seq.dbg_repr()
log.debug("After:\n%s", post_transform_seq_repr)
assert pre_transform_seq_repr == post_transform_seq_repr
if __name__ == "__main__":
unittest.main()