angr 9.2.136__py3-none-macosx_11_0_arm64.whl → 9.2.138__py3-none-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +2 -1
- angr/analyses/calling_convention/fact_collector.py +10 -2
- angr/analyses/cfg/cfg_base.py +3 -33
- angr/analyses/cfg/cfg_emulated.py +0 -103
- angr/analyses/cfg/cfg_fast.py +31 -12
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +15 -0
- angr/analyses/class_identifier.py +1 -2
- angr/analyses/complete_calling_conventions.py +6 -3
- angr/analyses/decompiler/ail_simplifier.py +12 -1
- angr/analyses/decompiler/block_simplifier.py +2 -2
- angr/analyses/decompiler/ccall_rewriters/__init__.py +2 -0
- angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
- angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +69 -0
- angr/analyses/decompiler/clinic.py +77 -65
- angr/analyses/decompiler/condition_processor.py +2 -0
- angr/analyses/decompiler/decompilation_options.py +10 -0
- angr/analyses/decompiler/decompiler.py +1 -0
- angr/analyses/decompiler/dephication/dephication_base.py +2 -0
- angr/analyses/decompiler/dephication/rewriting_engine.py +8 -6
- angr/analyses/decompiler/dephication/seqnode_dephication.py +10 -1
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +2 -2
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +1 -2
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_nots.py +21 -3
- angr/analyses/decompiler/sequence_walker.py +6 -2
- angr/analyses/decompiler/ssailification/rewriting.py +11 -1
- angr/analyses/decompiler/ssailification/rewriting_engine.py +56 -19
- angr/analyses/decompiler/ssailification/ssailification.py +13 -3
- angr/analyses/decompiler/ssailification/traversal.py +28 -2
- angr/analyses/decompiler/ssailification/traversal_state.py +6 -1
- angr/analyses/decompiler/structured_codegen/c.py +44 -21
- angr/analyses/decompiler/structuring/phoenix.py +117 -14
- angr/analyses/decompiler/utils.py +113 -8
- angr/analyses/reaching_definitions/function_handler.py +1 -1
- angr/analyses/s_liveness.py +5 -1
- angr/analyses/s_propagator.py +127 -28
- angr/analyses/s_reaching_definitions/s_rda_model.py +2 -1
- angr/analyses/s_reaching_definitions/s_rda_view.py +20 -1
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +11 -1
- angr/analyses/stack_pointer_tracker.py +26 -16
- angr/analyses/variable_recovery/engine_ail.py +19 -7
- angr/analyses/variable_recovery/engine_base.py +16 -14
- angr/analyses/variable_recovery/engine_vex.py +2 -2
- angr/analyses/variable_recovery/variable_recovery_fast.py +22 -1
- angr/block.py +59 -20
- angr/engines/pcode/emulate.py +1 -1
- angr/engines/pcode/lifter.py +31 -18
- angr/engines/soot/expressions/__init__.py +2 -4
- angr/engines/soot/statements/__init__.py +1 -2
- angr/engines/soot/values/__init__.py +1 -2
- angr/engines/successors.py +11 -6
- angr/engines/vex/lifter.py +9 -6
- angr/flirt/build_sig.py +8 -15
- angr/knowledge_plugins/functions/function.py +0 -6
- angr/knowledge_plugins/functions/soot_function.py +5 -8
- angr/knowledge_plugins/variables/variable_manager.py +16 -10
- angr/lib/angr_native.dylib +0 -0
- angr/procedures/glibc/__libc_start_main.py +10 -3
- angr/utils/ssa/__init__.py +14 -1
- {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/METADATA +7 -7
- {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/RECORD +65 -64
- {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/WHEEL +1 -1
- {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/LICENSE +0 -0
- {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/entry_points.txt +0 -0
- {angr-9.2.136.dist-info → angr-9.2.138.dist-info}/top_level.txt +0 -0
|
@@ -17,13 +17,24 @@ class TraversalAnalysis(ForwardAnalysis[TraversalState, ailment.Block, object, t
|
|
|
17
17
|
TraversalAnalysis traverses the AIL graph and collects definitions.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
def __init__(
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
project,
|
|
23
|
+
func,
|
|
24
|
+
ail_graph,
|
|
25
|
+
sp_tracker,
|
|
26
|
+
bp_as_gpr: bool,
|
|
27
|
+
stackvars: bool,
|
|
28
|
+
tmps: bool,
|
|
29
|
+
func_args: set[ailment.Expr.VirtualVariable],
|
|
30
|
+
):
|
|
21
31
|
|
|
22
32
|
self.project = project
|
|
23
33
|
self._stackvars = stackvars
|
|
24
34
|
self._tmps = tmps
|
|
25
35
|
self._function = func
|
|
26
36
|
self._graph_visitor = FunctionGraphVisitor(self._function, ail_graph)
|
|
37
|
+
self._func_args = func_args
|
|
27
38
|
|
|
28
39
|
ForwardAnalysis.__init__(
|
|
29
40
|
self, order_jobs=True, allow_merging=True, allow_widening=False, graph_visitor=self._graph_visitor
|
|
@@ -52,13 +63,28 @@ class TraversalAnalysis(ForwardAnalysis[TraversalState, ailment.Block, object, t
|
|
|
52
63
|
pass
|
|
53
64
|
|
|
54
65
|
def _initial_abstract_state(self, node: ailment.Block) -> TraversalState:
|
|
55
|
-
|
|
66
|
+
state = TraversalState(self.project.arch, self._function)
|
|
67
|
+
# update it with function arguments
|
|
68
|
+
if self._func_args:
|
|
69
|
+
for func_arg in self._func_args:
|
|
70
|
+
if func_arg.oident[0] == ailment.Expr.VirtualVariableCategory.REGISTER:
|
|
71
|
+
reg_offset = func_arg.oident[1]
|
|
72
|
+
reg_size = func_arg.size
|
|
73
|
+
state.live_registers.add(reg_offset)
|
|
74
|
+
# get the full register if needed
|
|
75
|
+
basereg_offset, basereg_size = self.project.arch.get_base_register(reg_offset, size=reg_size)
|
|
76
|
+
if basereg_size != reg_size or basereg_offset != reg_offset:
|
|
77
|
+
state.live_registers.add(basereg_offset)
|
|
78
|
+
elif func_arg.oident[0] == ailment.Expr.VirtualVariableCategory.STACK:
|
|
79
|
+
state.live_stackvars.add((func_arg.oident[1], func_arg.size))
|
|
80
|
+
return state
|
|
56
81
|
|
|
57
82
|
def _merge_states(self, node: ailment.Block, *states: TraversalState) -> tuple[TraversalState, bool]:
|
|
58
83
|
merged_state = TraversalState(
|
|
59
84
|
self.project.arch,
|
|
60
85
|
self._function,
|
|
61
86
|
live_registers=states[0].live_registers.copy(),
|
|
87
|
+
live_stackvars=states[0].live_stackvars.copy(),
|
|
62
88
|
)
|
|
63
89
|
merge_occurred = merged_state.merge(*states[1:])
|
|
64
90
|
return merged_state, not merge_occurred
|
|
@@ -37,7 +37,12 @@ class TraversalState:
|
|
|
37
37
|
merge_occurred = True
|
|
38
38
|
all_regs |= o.live_registers
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
all_stackvars: set[tuple[int, int]] = self.live_stackvars.copy()
|
|
41
|
+
for o in others:
|
|
42
|
+
if o.live_stackvars.difference(all_stackvars):
|
|
43
|
+
merge_occurred = True
|
|
44
|
+
all_stackvars |= o.live_stackvars
|
|
41
45
|
|
|
42
46
|
self.live_registers = all_regs
|
|
47
|
+
self.live_stackvars = all_stackvars
|
|
43
48
|
return merge_occurred
|
|
@@ -679,14 +679,23 @@ class CStatements(CStatement):
|
|
|
679
679
|
Represents a sequence of statements in C.
|
|
680
680
|
"""
|
|
681
681
|
|
|
682
|
-
__slots__ = (
|
|
682
|
+
__slots__ = (
|
|
683
|
+
"addr",
|
|
684
|
+
"statements",
|
|
685
|
+
)
|
|
683
686
|
|
|
684
|
-
def __init__(self, statements, **kwargs):
|
|
687
|
+
def __init__(self, statements, addr=None, **kwargs):
|
|
685
688
|
super().__init__(**kwargs)
|
|
686
689
|
|
|
687
690
|
self.statements = statements
|
|
691
|
+
self.addr = addr
|
|
688
692
|
|
|
689
693
|
def c_repr_chunks(self, indent=0, asexpr=False):
|
|
694
|
+
indent_str = self.indent_str(indent)
|
|
695
|
+
if self.codegen.display_block_addrs:
|
|
696
|
+
yield indent_str, None
|
|
697
|
+
yield f"/* Block {hex(self.addr) if self.addr is not None else 'unknown'} */", None
|
|
698
|
+
yield "\n", None
|
|
690
699
|
for stmt in self.statements:
|
|
691
700
|
yield from stmt.c_repr_chunks(indent=indent, asexpr=asexpr)
|
|
692
701
|
if asexpr:
|
|
@@ -1572,15 +1581,19 @@ class CVariable(CExpression):
|
|
|
1572
1581
|
"unified_variable",
|
|
1573
1582
|
"variable",
|
|
1574
1583
|
"variable_type",
|
|
1584
|
+
"vvar_id",
|
|
1575
1585
|
)
|
|
1576
1586
|
|
|
1577
|
-
def __init__(
|
|
1587
|
+
def __init__(
|
|
1588
|
+
self, variable: SimVariable, unified_variable=None, variable_type=None, tags=None, vvar_id=None, **kwargs
|
|
1589
|
+
):
|
|
1578
1590
|
super().__init__(**kwargs)
|
|
1579
1591
|
|
|
1580
1592
|
self.variable: SimVariable = variable
|
|
1581
1593
|
self.unified_variable: SimVariable | None = unified_variable
|
|
1582
1594
|
self.variable_type: SimType = variable_type.with_arch(self.codegen.project.arch)
|
|
1583
1595
|
self.tags = tags
|
|
1596
|
+
self.vvar_id = vvar_id
|
|
1584
1597
|
|
|
1585
1598
|
@property
|
|
1586
1599
|
def type(self):
|
|
@@ -1598,6 +1611,8 @@ class CVariable(CExpression):
|
|
|
1598
1611
|
|
|
1599
1612
|
def c_repr_chunks(self, indent=0, asexpr=False):
|
|
1600
1613
|
yield self.name, self
|
|
1614
|
+
if self.codegen.display_vvar_ids:
|
|
1615
|
+
yield f"<vvar_{self.vvar_id}>", self
|
|
1601
1616
|
|
|
1602
1617
|
|
|
1603
1618
|
class CIndexedVariable(CExpression):
|
|
@@ -2141,7 +2156,8 @@ class CConstant(CExpression):
|
|
|
2141
2156
|
result = self.fmt.get("neg", None)
|
|
2142
2157
|
if result is None:
|
|
2143
2158
|
result = False
|
|
2144
|
-
|
|
2159
|
+
# guess it
|
|
2160
|
+
if isinstance(self._type, (SimTypeInt, SimTypeChar)) and self._type.signed and isinstance(self.value, int):
|
|
2145
2161
|
value_size = self._type.size if self._type is not None else None
|
|
2146
2162
|
if (value_size == 32 and 0xF000_0000 <= self.value <= 0xFFFF_FFFF) or (
|
|
2147
2163
|
value_size == 64 and 0xF000_0000_0000_0000 <= self.value <= 0xFFFF_FFFF_FFFF_FFFF
|
|
@@ -2486,6 +2502,8 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
2486
2502
|
simplify_else_scope=True,
|
|
2487
2503
|
cstyle_ifs=True,
|
|
2488
2504
|
omit_func_header=False,
|
|
2505
|
+
display_block_addrs=False,
|
|
2506
|
+
display_vvar_ids=False,
|
|
2489
2507
|
):
|
|
2490
2508
|
super().__init__(flavor=flavor)
|
|
2491
2509
|
|
|
@@ -2558,6 +2576,8 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
2558
2576
|
self.simplify_else_scope = simplify_else_scope
|
|
2559
2577
|
self.cstyle_ifs = cstyle_ifs
|
|
2560
2578
|
self.omit_func_header = omit_func_header
|
|
2579
|
+
self.display_block_addrs = display_block_addrs
|
|
2580
|
+
self.display_vvar_ids = display_vvar_ids
|
|
2561
2581
|
self.text = None
|
|
2562
2582
|
self.map_pos_to_node = None
|
|
2563
2583
|
self.map_pos_to_addr = None
|
|
@@ -2740,7 +2760,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
2740
2760
|
return _mapping.get(n)(signed=signed).with_arch(self.project.arch)
|
|
2741
2761
|
return SimTypeNum(n, signed=signed).with_arch(self.project.arch)
|
|
2742
2762
|
|
|
2743
|
-
def _variable(self, variable: SimVariable, fallback_type_size: int | None) -> CVariable:
|
|
2763
|
+
def _variable(self, variable: SimVariable, fallback_type_size: int | None, vvar_id: int | None = None) -> CVariable:
|
|
2744
2764
|
# TODO: we need to fucking make sure that variable recovery and type inference actually generates a size
|
|
2745
2765
|
# TODO: for each variable it links into the fucking ail. then we can remove fallback_type_size.
|
|
2746
2766
|
unified = self._variable_kb.variables[self._func.addr].unified_variable(variable)
|
|
@@ -2751,7 +2771,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
2751
2771
|
variable_type = self.default_simtype_from_bits(
|
|
2752
2772
|
(fallback_type_size or self.project.arch.bytes) * self.project.arch.byte_width
|
|
2753
2773
|
)
|
|
2754
|
-
cvar = CVariable(variable, unified_variable=unified, variable_type=variable_type, codegen=self)
|
|
2774
|
+
cvar = CVariable(variable, unified_variable=unified, variable_type=variable_type, codegen=self, vvar_id=vvar_id)
|
|
2755
2775
|
self._variables_in_use[variable] = cvar
|
|
2756
2776
|
return cvar
|
|
2757
2777
|
|
|
@@ -3106,14 +3126,18 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3106
3126
|
# Handlers
|
|
3107
3127
|
#
|
|
3108
3128
|
|
|
3109
|
-
def _handle(self, node, is_expr: bool = True, lvalue: bool = False):
|
|
3129
|
+
def _handle(self, node, is_expr: bool = True, lvalue: bool = False, likely_signed=False):
|
|
3110
3130
|
if (node, is_expr) in self.ailexpr2cnode:
|
|
3111
3131
|
return self.ailexpr2cnode[(node, is_expr)]
|
|
3112
3132
|
|
|
3113
3133
|
handler: Callable | None = self._handlers.get(node.__class__, None)
|
|
3114
3134
|
if handler is not None:
|
|
3115
3135
|
# special case for Call
|
|
3116
|
-
converted =
|
|
3136
|
+
converted = (
|
|
3137
|
+
handler(node, is_expr=is_expr)
|
|
3138
|
+
if isinstance(node, Stmt.Call)
|
|
3139
|
+
else handler(node, lvalue=lvalue, likely_signed=likely_signed)
|
|
3140
|
+
)
|
|
3117
3141
|
self.ailexpr2cnode[(node, is_expr)] = converted
|
|
3118
3142
|
return converted
|
|
3119
3143
|
raise UnsupportedNodeTypeError(f"Node type {type(node)} is not supported yet.")
|
|
@@ -3127,10 +3151,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3127
3151
|
for node in seq.nodes:
|
|
3128
3152
|
lines.append(self._handle(node, is_expr=False))
|
|
3129
3153
|
|
|
3130
|
-
if
|
|
3131
|
-
return CStatements([], codegen=None)
|
|
3132
|
-
|
|
3133
|
-
return CStatements(lines, codegen=self) if len(lines) > 1 else lines[0]
|
|
3154
|
+
return lines[0] if len(lines) == 1 else CStatements(lines, codegen=self, addr=seq.addr)
|
|
3134
3155
|
|
|
3135
3156
|
def _handle_Loop(self, loop_node, **kwargs):
|
|
3136
3157
|
tags = {"ins_addr": loop_node.addr}
|
|
@@ -3217,7 +3238,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3217
3238
|
r = self._handle(n, is_expr=False)
|
|
3218
3239
|
lines.append(r)
|
|
3219
3240
|
|
|
3220
|
-
return
|
|
3241
|
+
return lines[0] if len(lines) == 1 else CStatements(lines, codegen=self, addr=node.addr)
|
|
3221
3242
|
|
|
3222
3243
|
def _handle_SwitchCase(self, node, **kwargs):
|
|
3223
3244
|
"""
|
|
@@ -3260,7 +3281,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3260
3281
|
cstmt = CUnsupportedStatement(stmt, codegen=self)
|
|
3261
3282
|
cstmts.append(cstmt)
|
|
3262
3283
|
|
|
3263
|
-
return CStatements(cstmts, codegen=self)
|
|
3284
|
+
return CStatements(cstmts, codegen=self, addr=node.addr)
|
|
3264
3285
|
|
|
3265
3286
|
#
|
|
3266
3287
|
# AIL statement handlers
|
|
@@ -3315,9 +3336,9 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3315
3336
|
if stmt.dst.variable is not None:
|
|
3316
3337
|
if "struct_member_info" in stmt.dst.tags:
|
|
3317
3338
|
offset, var, _ = stmt.dst.struct_member_info
|
|
3318
|
-
cvar = self._variable(var, stmt.dst.size)
|
|
3339
|
+
cvar = self._variable(var, stmt.dst.size, vvar_id=stmt.dst.varid)
|
|
3319
3340
|
else:
|
|
3320
|
-
cvar = self._variable(stmt.dst.variable, stmt.dst.size)
|
|
3341
|
+
cvar = self._variable(stmt.dst.variable, stmt.dst.size, vvar_id=stmt.dst.varid)
|
|
3321
3342
|
offset = stmt.dst.variable_offset or 0
|
|
3322
3343
|
assert type(offset) is int # I refuse to deal with the alternative
|
|
3323
3344
|
|
|
@@ -3474,7 +3495,9 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3474
3495
|
l.warning("FIXME: Leftover Tmp expressions are found.")
|
|
3475
3496
|
return self._variable(SimTemporaryVariable(expr.tmp_idx, expr.bits), expr.size)
|
|
3476
3497
|
|
|
3477
|
-
def _handle_Expr_Const(
|
|
3498
|
+
def _handle_Expr_Const(
|
|
3499
|
+
self, expr: Expr.Const, type_=None, reference_values=None, variable=None, likely_signed=True, **kwargs
|
|
3500
|
+
):
|
|
3478
3501
|
inline_string = False
|
|
3479
3502
|
function_pointer = False
|
|
3480
3503
|
|
|
@@ -3550,8 +3573,8 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3550
3573
|
inline_string = True
|
|
3551
3574
|
|
|
3552
3575
|
if type_ is None:
|
|
3553
|
-
# default to int
|
|
3554
|
-
type_ = self.default_simtype_from_bits(expr.bits)
|
|
3576
|
+
# default to int or unsigned int, determined by likely_signed
|
|
3577
|
+
type_ = self.default_simtype_from_bits(expr.bits, signed=likely_signed)
|
|
3555
3578
|
|
|
3556
3579
|
if variable is None and hasattr(expr, "reference_variable") and expr.reference_variable is not None:
|
|
3557
3580
|
variable = expr.reference_variable
|
|
@@ -3583,7 +3606,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3583
3606
|
)
|
|
3584
3607
|
|
|
3585
3608
|
lhs = self._handle(expr.operands[0])
|
|
3586
|
-
rhs = self._handle(expr.operands[1])
|
|
3609
|
+
rhs = self._handle(expr.operands[1], likely_signed=expr.op not in {"And", "Or"})
|
|
3587
3610
|
|
|
3588
3611
|
return CBinaryOp(
|
|
3589
3612
|
expr.op,
|
|
@@ -3679,7 +3702,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
|
|
|
3679
3702
|
|
|
3680
3703
|
def _handle_VirtualVariable(self, expr: Expr.VirtualVariable, **kwargs):
|
|
3681
3704
|
if expr.variable:
|
|
3682
|
-
cvar = self._variable(expr.variable, None)
|
|
3705
|
+
cvar = self._variable(expr.variable, None, vvar_id=expr.varid)
|
|
3683
3706
|
if expr.variable.size != expr.size:
|
|
3684
3707
|
l.warning(
|
|
3685
3708
|
"VirtualVariable size (%d) and variable size (%d) do not match. Force a type cast.",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# pylint:disable=line-too-long,import-outside-toplevel,import-error,multiple-statements,too-many-boolean-expressions
|
|
2
|
+
# ruff: noqa: SIM102
|
|
2
3
|
from __future__ import annotations
|
|
3
4
|
from typing import Any, TYPE_CHECKING
|
|
4
5
|
from collections import defaultdict, OrderedDict
|
|
@@ -26,6 +27,7 @@ from angr.analyses.decompiler.utils import (
|
|
|
26
27
|
is_empty_or_label_only_node,
|
|
27
28
|
has_nonlabel_nonphi_statements,
|
|
28
29
|
first_nonlabel_nonphi_statement,
|
|
30
|
+
switch_extract_bitwiseand_jumptable_info,
|
|
29
31
|
)
|
|
30
32
|
from angr.analyses.decompiler.counters.call_counter import AILCallCounter
|
|
31
33
|
from .structurer_nodes import (
|
|
@@ -1045,6 +1047,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1045
1047
|
if r:
|
|
1046
1048
|
return r
|
|
1047
1049
|
jump_tables = self.kb.cfgs["CFGFast"].jump_tables
|
|
1050
|
+
r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(
|
|
1051
|
+
node, graph, full_graph, jump_tables
|
|
1052
|
+
)
|
|
1053
|
+
if r:
|
|
1054
|
+
return r
|
|
1048
1055
|
r = self._match_acyclic_switch_cases_address_loaded_from_memory(node, graph, full_graph, jump_tables)
|
|
1049
1056
|
if r:
|
|
1050
1057
|
return r
|
|
@@ -1240,6 +1247,93 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1240
1247
|
|
|
1241
1248
|
return True
|
|
1242
1249
|
|
|
1250
|
+
def _match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(
|
|
1251
|
+
self, node, graph, full_graph, jump_tables
|
|
1252
|
+
) -> bool:
|
|
1253
|
+
if node.addr not in jump_tables:
|
|
1254
|
+
return False
|
|
1255
|
+
|
|
1256
|
+
try:
|
|
1257
|
+
last_stmt = self.cond_proc.get_last_statement(node)
|
|
1258
|
+
except EmptyBlockNotice:
|
|
1259
|
+
return False
|
|
1260
|
+
if not (isinstance(last_stmt, Jump) and not isinstance(last_stmt.target, Const)):
|
|
1261
|
+
return False
|
|
1262
|
+
|
|
1263
|
+
jump_table = jump_tables[node.addr]
|
|
1264
|
+
if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
|
|
1265
|
+
return False
|
|
1266
|
+
|
|
1267
|
+
# extract the index expression, lower-, and upper-bounds from the last statement
|
|
1268
|
+
index = switch_extract_bitwiseand_jumptable_info(last_stmt)
|
|
1269
|
+
if not index:
|
|
1270
|
+
return False
|
|
1271
|
+
index_expr, cmp_lb, cmp_ub = index # pylint:disable=unused-variable
|
|
1272
|
+
case_count = cmp_ub - cmp_lb + 1
|
|
1273
|
+
|
|
1274
|
+
# ensure we have the same number of cases
|
|
1275
|
+
if case_count != len(jump_table.jumptable_entries):
|
|
1276
|
+
return False
|
|
1277
|
+
|
|
1278
|
+
# populate whitelist_edges
|
|
1279
|
+
for case_node_addr in jump_table.jumptable_entries:
|
|
1280
|
+
self.whitelist_edges.add((node.addr, case_node_addr))
|
|
1281
|
+
self.switch_case_known_heads.add(node)
|
|
1282
|
+
|
|
1283
|
+
# sanity check: case nodes are successors to node. all case nodes must have at most common one successor
|
|
1284
|
+
node_pred = None
|
|
1285
|
+
if graph.in_degree[node] == 1:
|
|
1286
|
+
node_pred = next(iter(graph.predecessors(node)))
|
|
1287
|
+
|
|
1288
|
+
case_nodes = list(graph.successors(node))
|
|
1289
|
+
|
|
1290
|
+
# case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
|
|
1291
|
+
# optimization)
|
|
1292
|
+
# example: touch_touch_no_switch.o:main
|
|
1293
|
+
r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
|
|
1294
|
+
|
|
1295
|
+
# case 2: the common successor is not directly reachable from node_a. this is a more common case.
|
|
1296
|
+
if not r:
|
|
1297
|
+
r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
|
|
1298
|
+
|
|
1299
|
+
if not r:
|
|
1300
|
+
return False
|
|
1301
|
+
|
|
1302
|
+
case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
|
|
1303
|
+
|
|
1304
|
+
cases, node_default, to_remove = self._switch_build_cases(
|
|
1305
|
+
case_and_entry_addrs,
|
|
1306
|
+
node,
|
|
1307
|
+
node,
|
|
1308
|
+
None,
|
|
1309
|
+
graph,
|
|
1310
|
+
full_graph,
|
|
1311
|
+
)
|
|
1312
|
+
|
|
1313
|
+
assert node_default is None
|
|
1314
|
+
switch_end_addr = None
|
|
1315
|
+
|
|
1316
|
+
r = self._make_switch_cases_core(
|
|
1317
|
+
node,
|
|
1318
|
+
index_expr,
|
|
1319
|
+
cases,
|
|
1320
|
+
None,
|
|
1321
|
+
None,
|
|
1322
|
+
last_stmt.ins_addr,
|
|
1323
|
+
to_remove,
|
|
1324
|
+
graph,
|
|
1325
|
+
full_graph,
|
|
1326
|
+
node_a=None,
|
|
1327
|
+
)
|
|
1328
|
+
if not r:
|
|
1329
|
+
return False
|
|
1330
|
+
|
|
1331
|
+
# fully structured into a switch-case. remove node from switch_case_known_heads
|
|
1332
|
+
self.switch_case_known_heads.remove(node)
|
|
1333
|
+
self._switch_handle_gotos(cases, node_default, switch_end_addr)
|
|
1334
|
+
|
|
1335
|
+
return True
|
|
1336
|
+
|
|
1243
1337
|
def _match_acyclic_switch_cases_address_computed(self, node, graph, full_graph, jump_tables) -> bool:
|
|
1244
1338
|
if node.addr not in jump_tables:
|
|
1245
1339
|
return False
|
|
@@ -1333,7 +1427,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1333
1427
|
case_and_entryaddrs: dict[int, int | tuple[int, int | None]],
|
|
1334
1428
|
head_node,
|
|
1335
1429
|
node_a: BaseNode,
|
|
1336
|
-
node_b_addr,
|
|
1430
|
+
node_b_addr: int | None,
|
|
1337
1431
|
graph,
|
|
1338
1432
|
full_graph,
|
|
1339
1433
|
) -> tuple[OrderedDict, Any, set[Any]]:
|
|
@@ -1343,7 +1437,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1343
1437
|
# it is possible that the default node gets duplicated by other analyses and creates a default node (addr.a)
|
|
1344
1438
|
# and a case node (addr.b). The addr.a node is a successor to the head node while the addr.b node is a
|
|
1345
1439
|
# successor to node_a
|
|
1346
|
-
default_node_candidates =
|
|
1440
|
+
default_node_candidates = (
|
|
1441
|
+
[nn for nn in graph.nodes if nn.addr == node_b_addr] if node_b_addr is not None else []
|
|
1442
|
+
)
|
|
1347
1443
|
node_default: BaseNode | None = next(
|
|
1348
1444
|
iter(nn for nn in default_node_candidates if graph.has_edge(head_node, nn)), None
|
|
1349
1445
|
)
|
|
@@ -1355,7 +1451,6 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1355
1451
|
self.replace_nodes(full_graph, node_default, new_node)
|
|
1356
1452
|
node_default = new_node
|
|
1357
1453
|
|
|
1358
|
-
# entry_addrs_set = set(jumptable_entries)
|
|
1359
1454
|
converted_nodes: dict[tuple[int, int | None], Any] = {}
|
|
1360
1455
|
entry_addr_to_ids: defaultdict[tuple[int, int | None], set[int]] = defaultdict(set)
|
|
1361
1456
|
|
|
@@ -1442,7 +1537,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1442
1537
|
head,
|
|
1443
1538
|
cmp_expr,
|
|
1444
1539
|
cases: OrderedDict,
|
|
1445
|
-
node_default_addr: int,
|
|
1540
|
+
node_default_addr: int | None,
|
|
1446
1541
|
node_default,
|
|
1447
1542
|
addr,
|
|
1448
1543
|
to_remove: set,
|
|
@@ -1491,7 +1586,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1491
1586
|
pass
|
|
1492
1587
|
graph.remove_edge(head, node_default)
|
|
1493
1588
|
full_graph.remove_edge(head, node_default)
|
|
1494
|
-
|
|
1589
|
+
elif node_default_addr is not None:
|
|
1495
1590
|
# the default node is not in the current graph, but it might be in the full graph
|
|
1496
1591
|
node_default_in_full_graph = next(iter(nn for nn in full_graph if nn.addr == node_default_addr), None)
|
|
1497
1592
|
if node_default_in_full_graph is not None and full_graph.has_edge(head, node_default_in_full_graph):
|
|
@@ -1570,12 +1665,22 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1570
1665
|
|
|
1571
1666
|
return case_and_entry_addrs
|
|
1572
1667
|
|
|
1573
|
-
def
|
|
1668
|
+
def _is_node_unstructured_switch_case_head(self, node) -> bool:
|
|
1574
1669
|
jump_tables = self.kb.cfgs["CFGFast"].jump_tables
|
|
1575
1670
|
if node.addr in jump_tables:
|
|
1671
|
+
# maybe it has been structured?
|
|
1672
|
+
try:
|
|
1673
|
+
last_stmts = self.cond_proc.get_last_statements(node)
|
|
1674
|
+
except EmptyBlockNotice:
|
|
1675
|
+
return False
|
|
1676
|
+
return len(last_stmts) == 1 and isinstance(last_stmts[0], Jump)
|
|
1677
|
+
return False
|
|
1678
|
+
|
|
1679
|
+
def _is_switch_cases_address_loaded_from_memory_head_or_jumpnode(self, graph, node) -> bool:
|
|
1680
|
+
if self._is_node_unstructured_switch_case_head(node):
|
|
1576
1681
|
return True
|
|
1577
1682
|
for succ in graph.successors(node):
|
|
1578
|
-
if succ
|
|
1683
|
+
if self._is_node_unstructured_switch_case_head(succ):
|
|
1579
1684
|
return True
|
|
1580
1685
|
return node in self.switch_case_known_heads
|
|
1581
1686
|
|
|
@@ -1634,13 +1739,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1634
1739
|
)
|
|
1635
1740
|
):
|
|
1636
1741
|
# potentially ITE
|
|
1637
|
-
jump_tables = self.kb.cfgs["CFGFast"].jump_tables
|
|
1638
|
-
|
|
1639
1742
|
if (
|
|
1640
1743
|
full_graph.in_degree[left] == 1
|
|
1641
1744
|
and full_graph.in_degree[right] == 1
|
|
1642
|
-
and
|
|
1643
|
-
and
|
|
1745
|
+
and not self._is_node_unstructured_switch_case_head(left)
|
|
1746
|
+
and not self._is_node_unstructured_switch_case_head(right)
|
|
1644
1747
|
):
|
|
1645
1748
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
1646
1749
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
@@ -1681,9 +1784,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1681
1784
|
left_succs, right_succs = right_succs, left_succs
|
|
1682
1785
|
if left in graph and not left_succs and full_graph.in_degree[left] == 1 and right in graph:
|
|
1683
1786
|
# potentially If-Then
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1787
|
+
if not self._is_node_unstructured_switch_case_head(
|
|
1788
|
+
left
|
|
1789
|
+
) and not self._is_node_unstructured_switch_case_head(right):
|
|
1687
1790
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
1688
1791
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
1689
1792
|
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
@@ -40,7 +40,7 @@ def remove_last_statement(node):
|
|
|
40
40
|
elif type(node) is LoopNode:
|
|
41
41
|
stmt = remove_last_statement(node.sequence_node)
|
|
42
42
|
else:
|
|
43
|
-
raise NotImplementedError
|
|
43
|
+
raise NotImplementedError(type(node))
|
|
44
44
|
|
|
45
45
|
return stmt
|
|
46
46
|
|
|
@@ -69,7 +69,7 @@ def remove_last_statements(node) -> bool:
|
|
|
69
69
|
return r
|
|
70
70
|
if type(node) is LoopNode:
|
|
71
71
|
return remove_last_statements(node.sequence_node)
|
|
72
|
-
raise NotImplementedError
|
|
72
|
+
raise NotImplementedError(type(node))
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
def append_statement(node, stmt):
|
|
@@ -83,16 +83,16 @@ def append_statement(node, stmt):
|
|
|
83
83
|
if node.nodes:
|
|
84
84
|
append_statement(node.nodes[-1], stmt)
|
|
85
85
|
else:
|
|
86
|
-
raise NotImplementedError
|
|
86
|
+
raise NotImplementedError("MultiNode without nodes")
|
|
87
87
|
return
|
|
88
88
|
if type(node) is SequenceNode:
|
|
89
89
|
if node.nodes:
|
|
90
90
|
append_statement(node.nodes[-1], stmt)
|
|
91
91
|
else:
|
|
92
|
-
raise NotImplementedError
|
|
92
|
+
raise NotImplementedError("SequenceNode without nodes")
|
|
93
93
|
return
|
|
94
94
|
|
|
95
|
-
raise NotImplementedError
|
|
95
|
+
raise NotImplementedError(type(node))
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
def replace_last_statement(node, old_stmt, new_stmt):
|
|
@@ -118,7 +118,7 @@ def replace_last_statement(node, old_stmt, new_stmt):
|
|
|
118
118
|
replace_last_statement(node.false_node, old_stmt, new_stmt)
|
|
119
119
|
return
|
|
120
120
|
|
|
121
|
-
raise NotImplementedError
|
|
121
|
+
raise NotImplementedError(type(node))
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
def extract_jump_targets(stmt):
|
|
@@ -175,6 +175,111 @@ def switch_extract_cmp_bounds(last_stmt: ailment.Stmt.ConditionalJump) -> tuple[
|
|
|
175
175
|
return None
|
|
176
176
|
|
|
177
177
|
|
|
178
|
+
def switch_extract_bitwiseand_jumptable_info(last_stmt: ailment.Stmt.Jump) -> tuple[Any, int, int] | None:
|
|
179
|
+
"""
|
|
180
|
+
Check the last statement of the switch-case header node (whose address is loaded from a jump table and computed
|
|
181
|
+
using an index) and extract necessary information for rebuilding the switch-case construct.
|
|
182
|
+
|
|
183
|
+
An example of the statement:
|
|
184
|
+
|
|
185
|
+
Goto(Conv(32->s64, (
|
|
186
|
+
Load(addr=(0x4530e4<64> + (Conv(32->64, (Conv(64->32, vvar_287{reg 32}) & 0x3<32>)) * 0x4<64>)),
|
|
187
|
+
size=4, endness=Iend_LE) + 0x4530e4<32>))
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
:param last_stmt: The last statement of the switch-case header node.
|
|
191
|
+
:return: A tuple of (index expression, lower bound, upper bound), or None
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
if not isinstance(last_stmt, ailment.Stmt.Jump):
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
# unpack the target expression
|
|
198
|
+
target = last_stmt.target
|
|
199
|
+
jump_addr_offset = None
|
|
200
|
+
jumptable_load_addr = None
|
|
201
|
+
while True:
|
|
202
|
+
if isinstance(target, ailment.Expr.Convert) and (
|
|
203
|
+
(target.from_bits == 32 and target.to_bits == 64) or (target.from_bits == 16 and target.to_bits == 32)
|
|
204
|
+
):
|
|
205
|
+
target = target.operand
|
|
206
|
+
continue
|
|
207
|
+
if isinstance(target, ailment.Expr.BinaryOp) and target.op == "Add":
|
|
208
|
+
if isinstance(target.operands[0], ailment.Expr.Const) and isinstance(target.operands[1], ailment.Expr.Load):
|
|
209
|
+
jump_addr_offset = target.operands[0]
|
|
210
|
+
jumptable_load_addr = target.operands[1].addr
|
|
211
|
+
break
|
|
212
|
+
if isinstance(target.operands[1], ailment.Expr.Const) and isinstance(target.operands[0], ailment.Expr.Load):
|
|
213
|
+
jump_addr_offset = target.operands[1]
|
|
214
|
+
jumptable_load_addr = target.operands[0].addr
|
|
215
|
+
break
|
|
216
|
+
return None
|
|
217
|
+
if isinstance(target, ailment.Expr.Const):
|
|
218
|
+
return None
|
|
219
|
+
break
|
|
220
|
+
|
|
221
|
+
if jump_addr_offset is None or jumptable_load_addr is None:
|
|
222
|
+
return None
|
|
223
|
+
|
|
224
|
+
# parse jumptable_load_addr
|
|
225
|
+
jumptable_offset = None
|
|
226
|
+
jumptable_base_addr = None
|
|
227
|
+
if isinstance(jumptable_load_addr, ailment.Expr.BinaryOp) and jumptable_load_addr.op == "Add":
|
|
228
|
+
if isinstance(jumptable_load_addr.operands[0], ailment.Expr.Const):
|
|
229
|
+
jumptable_base_addr = jumptable_load_addr.operands[0]
|
|
230
|
+
jumptable_offset = jumptable_load_addr.operands[1]
|
|
231
|
+
elif isinstance(jumptable_load_addr.operands[1], ailment.Expr.Const):
|
|
232
|
+
jumptable_offset = jumptable_load_addr.operands[0]
|
|
233
|
+
jumptable_base_addr = jumptable_load_addr.operands[1]
|
|
234
|
+
|
|
235
|
+
if jumptable_offset is None or jumptable_base_addr is None:
|
|
236
|
+
return None
|
|
237
|
+
|
|
238
|
+
# parse jumptable_offset
|
|
239
|
+
expr = jumptable_offset
|
|
240
|
+
coeff = None
|
|
241
|
+
index_expr = None
|
|
242
|
+
lb = None
|
|
243
|
+
ub = None
|
|
244
|
+
while expr is not None:
|
|
245
|
+
if isinstance(expr, ailment.Expr.BinaryOp):
|
|
246
|
+
if expr.op == "Mul":
|
|
247
|
+
if isinstance(expr.operands[1], ailment.Expr.Const):
|
|
248
|
+
coeff = expr.operands[1].value
|
|
249
|
+
expr = expr.operands[0]
|
|
250
|
+
elif isinstance(expr.operands[0], ailment.Expr.Const):
|
|
251
|
+
coeff = expr.operands[0].value
|
|
252
|
+
expr = expr.operands[1]
|
|
253
|
+
else:
|
|
254
|
+
return None
|
|
255
|
+
elif expr.op == "And":
|
|
256
|
+
masks = {0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF}
|
|
257
|
+
if isinstance(expr.operands[1], ailment.Expr.Const) and expr.operands[1].value in masks:
|
|
258
|
+
lb = 0
|
|
259
|
+
ub = expr.operands[1].value
|
|
260
|
+
index_expr = expr
|
|
261
|
+
break
|
|
262
|
+
if isinstance(expr.operands[0], ailment.Expr.Const) and expr.operands[1].value in masks:
|
|
263
|
+
lb = 0
|
|
264
|
+
ub = expr.operands[0].value
|
|
265
|
+
index_expr = expr
|
|
266
|
+
break
|
|
267
|
+
return None
|
|
268
|
+
else:
|
|
269
|
+
return None
|
|
270
|
+
elif isinstance(expr, ailment.Expr.Convert):
|
|
271
|
+
if expr.is_signed is False:
|
|
272
|
+
expr = expr.operand
|
|
273
|
+
else:
|
|
274
|
+
return None
|
|
275
|
+
else:
|
|
276
|
+
break
|
|
277
|
+
|
|
278
|
+
if coeff is not None and index_expr is not None and lb is not None and ub is not None:
|
|
279
|
+
return index_expr, lb, ub
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
|
|
178
283
|
def get_ast_subexprs(claripy_ast):
|
|
179
284
|
queue = [claripy_ast]
|
|
180
285
|
while queue:
|
|
@@ -267,9 +372,9 @@ def insert_node(parent, insert_location: str, node, node_idx: int | tuple[int] |
|
|
|
267
372
|
parent.sequence_node = SequenceNode(parent.sequence_node.addr, nodes=[parent.sequence_node])
|
|
268
373
|
insert_node(parent.sequence_node, insert_location, node, node_idx)
|
|
269
374
|
else:
|
|
270
|
-
raise NotImplementedError
|
|
375
|
+
raise NotImplementedError(label)
|
|
271
376
|
else:
|
|
272
|
-
raise NotImplementedError
|
|
377
|
+
raise NotImplementedError(type(parent))
|
|
273
378
|
|
|
274
379
|
|
|
275
380
|
def _merge_ail_nodes(graph, node_a: ailment.Block, node_b: ailment.Block) -> ailment.Block:
|
|
@@ -379,7 +379,7 @@ class FunctionHandler:
|
|
|
379
379
|
else hook_libname
|
|
380
380
|
)
|
|
381
381
|
type_collections = []
|
|
382
|
-
if prototype_libname is not None:
|
|
382
|
+
if prototype_libname is not None and prototype_libname in SIM_LIBRARIES:
|
|
383
383
|
prototype_lib = SIM_LIBRARIES[prototype_libname]
|
|
384
384
|
if prototype_lib.type_collection_names:
|
|
385
385
|
for typelib_name in prototype_lib.type_collection_names:
|