angr 9.2.139__py3-none-manylinux2014_x86_64.whl → 9.2.141__py3-none-manylinux2014_x86_64.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 +136 -53
- angr/analyses/calling_convention/fact_collector.py +44 -18
- angr/analyses/calling_convention/utils.py +3 -1
- angr/analyses/cfg/cfg_base.py +13 -0
- angr/analyses/cfg/cfg_fast.py +11 -0
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
- angr/analyses/decompiler/ail_simplifier.py +115 -72
- angr/analyses/decompiler/callsite_maker.py +24 -11
- angr/analyses/decompiler/clinic.py +78 -43
- angr/analyses/decompiler/decompiler.py +18 -7
- angr/analyses/decompiler/expression_narrower.py +1 -1
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
- angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
- angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
- angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +92 -11
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +53 -9
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
- angr/analyses/decompiler/region_identifier.py +6 -4
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +287 -122
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +31 -13
- angr/analyses/decompiler/ssailification/rewriting.py +23 -15
- angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
- angr/analyses/decompiler/ssailification/ssailification.py +22 -14
- angr/analyses/decompiler/structured_codegen/c.py +73 -137
- angr/analyses/decompiler/structuring/dream.py +22 -18
- angr/analyses/decompiler/structuring/phoenix.py +158 -41
- angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
- angr/analyses/decompiler/structuring/structurer_base.py +37 -10
- angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
- angr/analyses/decompiler/utils.py +106 -21
- angr/analyses/deobfuscator/api_obf_finder.py +8 -5
- angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
- angr/analyses/deobfuscator/string_obf_finder.py +105 -18
- angr/analyses/forward_analysis/forward_analysis.py +1 -1
- angr/analyses/propagator/top_checker_mixin.py +6 -6
- angr/analyses/reaching_definitions/__init__.py +2 -1
- angr/analyses/reaching_definitions/dep_graph.py +1 -12
- angr/analyses/reaching_definitions/engine_vex.py +36 -31
- angr/analyses/reaching_definitions/function_handler.py +15 -2
- angr/analyses/reaching_definitions/rd_state.py +1 -37
- angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
- angr/analyses/s_propagator.py +6 -41
- angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
- angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
- angr/analyses/stack_pointer_tracker.py +36 -22
- angr/analyses/typehoon/simple_solver.py +45 -7
- angr/analyses/typehoon/typeconsts.py +18 -5
- angr/analyses/variable_recovery/engine_ail.py +1 -1
- angr/analyses/variable_recovery/engine_base.py +7 -5
- angr/analyses/variable_recovery/engine_vex.py +20 -4
- angr/block.py +69 -107
- angr/callable.py +14 -7
- angr/calling_conventions.py +30 -11
- angr/distributed/__init__.py +1 -1
- angr/engines/__init__.py +7 -8
- angr/engines/engine.py +1 -120
- angr/engines/failure.py +2 -2
- angr/engines/hook.py +2 -2
- angr/engines/light/engine.py +2 -2
- angr/engines/pcode/engine.py +2 -14
- angr/engines/procedure.py +2 -2
- angr/engines/soot/engine.py +2 -2
- angr/engines/soot/statements/switch.py +1 -1
- angr/engines/successors.py +124 -11
- angr/engines/syscall.py +2 -2
- angr/engines/unicorn.py +3 -3
- angr/engines/vex/heavy/heavy.py +3 -15
- angr/factory.py +12 -22
- angr/knowledge_plugins/key_definitions/atoms.py +8 -4
- angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
- angr/knowledge_plugins/variables/variable_manager.py +7 -5
- angr/sim_type.py +19 -17
- angr/simos/simos.py +3 -1
- angr/state_plugins/plugin.py +19 -4
- angr/storage/memory_mixins/memory_mixin.py +1 -1
- angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
- angr/utils/ssa/__init__.py +119 -4
- angr/utils/types.py +48 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/RECORD +87 -86
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
- {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
|
@@ -10,7 +10,7 @@ import claripy
|
|
|
10
10
|
import ailment
|
|
11
11
|
|
|
12
12
|
from angr.utils.graph import GraphUtils
|
|
13
|
-
from angr.knowledge_plugins.cfg import
|
|
13
|
+
from angr.knowledge_plugins.cfg import IndirectJumpType
|
|
14
14
|
from angr.analyses.decompiler.graph_region import GraphRegion
|
|
15
15
|
from angr.analyses.decompiler.empty_node_remover import EmptyNodeRemover
|
|
16
16
|
from angr.analyses.decompiler.jumptable_entry_condition_rewriter import JumpTableEntryConditionRewriter
|
|
@@ -112,6 +112,7 @@ class DreamStructurer(StructurerBase):
|
|
|
112
112
|
|
|
113
113
|
loop_subgraph = self._region.graph
|
|
114
114
|
successors = self._region.successors
|
|
115
|
+
assert successors is not None
|
|
115
116
|
|
|
116
117
|
assert len(successors) <= 1
|
|
117
118
|
|
|
@@ -267,6 +268,7 @@ class DreamStructurer(StructurerBase):
|
|
|
267
268
|
|
|
268
269
|
def _to_loop_body_sequence(self, loop_head, loop_subgraph, loop_successors):
|
|
269
270
|
graph = self._region.graph_with_successors
|
|
271
|
+
assert graph is not None
|
|
270
272
|
loop_region_graph = networkx.DiGraph()
|
|
271
273
|
|
|
272
274
|
# TODO: Make sure the loop body has been structured
|
|
@@ -312,7 +314,7 @@ class DreamStructurer(StructurerBase):
|
|
|
312
314
|
loop_head, loop_region_graph, successors=None, graph_with_successors=None, cyclic=False, full_graph=None
|
|
313
315
|
)
|
|
314
316
|
structurer = self.project.analyses[DreamStructurer].prep()(
|
|
315
|
-
region, condition_processor=self.cond_proc, func=self.function
|
|
317
|
+
region, condition_processor=self.cond_proc, func=self.function, jump_tables=self.jump_tables
|
|
316
318
|
)
|
|
317
319
|
seq = structurer.result
|
|
318
320
|
|
|
@@ -372,7 +374,7 @@ class DreamStructurer(StructurerBase):
|
|
|
372
374
|
node.condition_and_nodes = new_cond_and_nodes
|
|
373
375
|
|
|
374
376
|
if node.else_node is not None:
|
|
375
|
-
node.else_node = walker._handle(node.else_node)
|
|
377
|
+
node.else_node = walker._handle(node.else_node) # type: ignore
|
|
376
378
|
return node
|
|
377
379
|
|
|
378
380
|
def _handle_SwitchCaseNode(node, **kwargs): # pylint:disable=unused-argument
|
|
@@ -480,8 +482,6 @@ class DreamStructurer(StructurerBase):
|
|
|
480
482
|
:return: None
|
|
481
483
|
"""
|
|
482
484
|
|
|
483
|
-
jump_tables = self.kb.cfgs["CFGFast"].jump_tables
|
|
484
|
-
|
|
485
485
|
addr2nodes: dict[int, set[CodeNode]] = defaultdict(set)
|
|
486
486
|
for node in seq.nodes:
|
|
487
487
|
addr2nodes[node.addr].add(node)
|
|
@@ -491,14 +491,14 @@ class DreamStructurer(StructurerBase):
|
|
|
491
491
|
node = seq.nodes[i]
|
|
492
492
|
|
|
493
493
|
# Jumptable_AddressLoadedFromMemory
|
|
494
|
-
r = self._make_switch_cases_address_loaded_from_memory(seq, i, node, addr2nodes
|
|
494
|
+
r = self._make_switch_cases_address_loaded_from_memory(seq, i, node, addr2nodes)
|
|
495
495
|
if r:
|
|
496
496
|
# we found a node that looks like a switch-case. seq.nodes are changed. resume to find the next such
|
|
497
497
|
# case
|
|
498
498
|
break
|
|
499
499
|
|
|
500
500
|
# Jumptable_AddressComputed
|
|
501
|
-
r = self._make_switch_cases_address_computed(seq, i, node, addr2nodes
|
|
501
|
+
r = self._make_switch_cases_address_computed(seq, i, node, addr2nodes)
|
|
502
502
|
if r:
|
|
503
503
|
break
|
|
504
504
|
|
|
@@ -506,9 +506,7 @@ class DreamStructurer(StructurerBase):
|
|
|
506
506
|
# we did not find any node that looks like a switch-case. exit.
|
|
507
507
|
break
|
|
508
508
|
|
|
509
|
-
def _make_switch_cases_address_loaded_from_memory(
|
|
510
|
-
self, seq, i, node, addr2nodes: dict[int, set[CodeNode]], jump_tables: dict[int, IndirectJump]
|
|
511
|
-
) -> bool:
|
|
509
|
+
def _make_switch_cases_address_loaded_from_memory(self, seq, i, node, addr2nodes: dict[int, set[CodeNode]]) -> bool:
|
|
512
510
|
"""
|
|
513
511
|
A typical jump table involves multiple nodes, which look like the following:
|
|
514
512
|
|
|
@@ -526,6 +524,8 @@ class DreamStructurer(StructurerBase):
|
|
|
526
524
|
|
|
527
525
|
try:
|
|
528
526
|
last_stmt = self.cond_proc.get_last_statement(node)
|
|
527
|
+
if not isinstance(last_stmt, ailment.Stmt.ConditionalJump):
|
|
528
|
+
return False
|
|
529
529
|
except EmptyBlockNotice:
|
|
530
530
|
return False
|
|
531
531
|
successor_addrs = extract_jump_targets(last_stmt)
|
|
@@ -533,14 +533,14 @@ class DreamStructurer(StructurerBase):
|
|
|
533
533
|
return False
|
|
534
534
|
|
|
535
535
|
for t in successor_addrs:
|
|
536
|
-
if t in addr2nodes and t in jump_tables:
|
|
536
|
+
if t in addr2nodes and t in self.jump_tables:
|
|
537
537
|
# this is a candidate!
|
|
538
538
|
target = t
|
|
539
539
|
break
|
|
540
540
|
else:
|
|
541
541
|
return False
|
|
542
542
|
|
|
543
|
-
jump_table = jump_tables[target]
|
|
543
|
+
jump_table = self.jump_tables[target]
|
|
544
544
|
if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
|
|
545
545
|
return False
|
|
546
546
|
|
|
@@ -563,6 +563,7 @@ class DreamStructurer(StructurerBase):
|
|
|
563
563
|
return False
|
|
564
564
|
|
|
565
565
|
# build switch-cases
|
|
566
|
+
assert jump_table.jumptable_entries is not None
|
|
566
567
|
cases, node_default, to_remove = self._switch_build_cases(
|
|
567
568
|
seq, cmp_lb, jump_table.jumptable_entries, i, node_b_addr, addr2nodes
|
|
568
569
|
)
|
|
@@ -575,6 +576,7 @@ class DreamStructurer(StructurerBase):
|
|
|
575
576
|
if switch_end_addr is not None:
|
|
576
577
|
self._switch_handle_gotos(cases, node_default, switch_end_addr)
|
|
577
578
|
|
|
579
|
+
assert last_stmt.ins_addr is not None
|
|
578
580
|
self._make_switch_cases_core(
|
|
579
581
|
seq,
|
|
580
582
|
i,
|
|
@@ -591,12 +593,10 @@ class DreamStructurer(StructurerBase):
|
|
|
591
593
|
|
|
592
594
|
return True
|
|
593
595
|
|
|
594
|
-
def _make_switch_cases_address_computed(
|
|
595
|
-
|
|
596
|
-
) -> bool:
|
|
597
|
-
if node.addr not in jump_tables:
|
|
596
|
+
def _make_switch_cases_address_computed(self, seq, i, node, addr2nodes: dict[int, set[CodeNode]]) -> bool:
|
|
597
|
+
if node.addr not in self.jump_tables:
|
|
598
598
|
return False
|
|
599
|
-
jump_table = jump_tables[node.addr]
|
|
599
|
+
jump_table = self.jump_tables[node.addr]
|
|
600
600
|
if jump_table.type != IndirectJumpType.Jumptable_AddressComputed:
|
|
601
601
|
return False
|
|
602
602
|
|
|
@@ -622,9 +622,11 @@ class DreamStructurer(StructurerBase):
|
|
|
622
622
|
cmp_expr, cmp_lb, cmp_ub = cmp # pylint:disable=unused-variable
|
|
623
623
|
|
|
624
624
|
jumptable_entries = jump_table.jumptable_entries
|
|
625
|
+
assert jumptable_entries is not None
|
|
625
626
|
|
|
626
627
|
if isinstance(last_stmt.false_target, ailment.Expr.Const):
|
|
627
628
|
default_addr = last_stmt.false_target.value
|
|
629
|
+
assert isinstance(default_addr, int)
|
|
628
630
|
else:
|
|
629
631
|
return False
|
|
630
632
|
|
|
@@ -661,8 +663,9 @@ class DreamStructurer(StructurerBase):
|
|
|
661
663
|
addr,
|
|
662
664
|
addr2nodes,
|
|
663
665
|
to_remove,
|
|
666
|
+
*,
|
|
667
|
+
jumptable_addr: int,
|
|
664
668
|
node_a=None,
|
|
665
|
-
jumptable_addr=None,
|
|
666
669
|
):
|
|
667
670
|
scnode = SwitchCaseNode(cmp_expr, cases, node_default, addr=addr)
|
|
668
671
|
scnode = CodeNode(scnode, node.reaching_condition)
|
|
@@ -720,6 +723,7 @@ class DreamStructurer(StructurerBase):
|
|
|
720
723
|
):
|
|
721
724
|
# unpacking is needed
|
|
722
725
|
for n in node_a.node.nodes:
|
|
726
|
+
assert n.addr is not None
|
|
723
727
|
if isinstance(n, ConditionNode):
|
|
724
728
|
unpacked = self._switch_unpack_condition_node(n, jumptable)
|
|
725
729
|
if unpacked is None:
|
|
@@ -15,7 +15,7 @@ from ailment.expression import Const, UnaryOp, MultiStatementExpression
|
|
|
15
15
|
|
|
16
16
|
from angr.utils.graph import GraphUtils
|
|
17
17
|
from angr.utils.ail import is_phi_assignment
|
|
18
|
-
from angr.knowledge_plugins.cfg import IndirectJumpType
|
|
18
|
+
from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
|
|
19
19
|
from angr.utils.constants import SWITCH_MISSING_DEFAULT_NODE_ADDR
|
|
20
20
|
from angr.utils.graph import dominates, to_acyclic_graph, dfs_back_edges
|
|
21
21
|
from angr.analyses.decompiler.sequence_walker import SequenceWalker
|
|
@@ -28,6 +28,7 @@ from angr.analyses.decompiler.utils import (
|
|
|
28
28
|
has_nonlabel_nonphi_statements,
|
|
29
29
|
first_nonlabel_nonphi_statement,
|
|
30
30
|
switch_extract_bitwiseand_jumptable_info,
|
|
31
|
+
switch_extract_switch_expr_from_jump_target,
|
|
31
32
|
)
|
|
32
33
|
from angr.analyses.decompiler.counters.call_counter import AILCallCounter
|
|
33
34
|
from .structurer_nodes import (
|
|
@@ -765,6 +766,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
765
766
|
last_src_stmt = self.cond_proc.get_last_statement(src_block)
|
|
766
767
|
assert last_src_stmt is not None
|
|
767
768
|
break_cond = self.cond_proc.recover_edge_condition(fullgraph, src_block, dst)
|
|
769
|
+
assert successor.addr is not None
|
|
768
770
|
if claripy.is_true(break_cond):
|
|
769
771
|
break_stmt = Jump(
|
|
770
772
|
None,
|
|
@@ -869,7 +871,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
869
871
|
if cont_block is None:
|
|
870
872
|
# cont_block is not found. but it's ok. one possibility is that src is a jump table head with one
|
|
871
873
|
# case being the loop head. in such cases, we can just remove the edge.
|
|
872
|
-
if src.addr not in self.
|
|
874
|
+
if src.addr not in self.jump_tables:
|
|
873
875
|
l.debug(
|
|
874
876
|
"_refine_cyclic_core: Cannot find the block going to loop head for edge %r -> %r. "
|
|
875
877
|
"Remove the edge anyway.",
|
|
@@ -1070,25 +1072,29 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1070
1072
|
# switch cases
|
|
1071
1073
|
|
|
1072
1074
|
def _match_acyclic_switch_cases(self, graph: networkx.DiGraph, full_graph: networkx.DiGraph, node) -> bool:
|
|
1073
|
-
if isinstance(node,
|
|
1075
|
+
if isinstance(node, SwitchCaseNode):
|
|
1076
|
+
return False
|
|
1077
|
+
|
|
1078
|
+
r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_default_node(node, graph, full_graph)
|
|
1079
|
+
if r:
|
|
1080
|
+
return r
|
|
1081
|
+
|
|
1082
|
+
if isinstance(node, IncompleteSwitchCaseNode):
|
|
1074
1083
|
return False
|
|
1075
1084
|
|
|
1076
1085
|
r = self._match_acyclic_switch_cases_incomplete_switch_head(node, graph, full_graph)
|
|
1077
1086
|
if r:
|
|
1078
1087
|
return r
|
|
1079
|
-
|
|
1080
|
-
r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(
|
|
1081
|
-
node, graph, full_graph, jump_tables
|
|
1082
|
-
)
|
|
1088
|
+
r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(node, graph, full_graph)
|
|
1083
1089
|
if r:
|
|
1084
1090
|
return r
|
|
1085
|
-
r = self._match_acyclic_switch_cases_address_loaded_from_memory(node, graph, full_graph
|
|
1091
|
+
r = self._match_acyclic_switch_cases_address_loaded_from_memory(node, graph, full_graph)
|
|
1086
1092
|
if r:
|
|
1087
1093
|
return r
|
|
1088
|
-
r = self._match_acyclic_switch_cases_address_computed(node, graph, full_graph
|
|
1094
|
+
r = self._match_acyclic_switch_cases_address_computed(node, graph, full_graph)
|
|
1089
1095
|
if r:
|
|
1090
1096
|
return r
|
|
1091
|
-
return self._match_acyclic_incomplete_switch_cases(node, graph, full_graph
|
|
1097
|
+
return self._match_acyclic_incomplete_switch_cases(node, graph, full_graph)
|
|
1092
1098
|
|
|
1093
1099
|
def _match_acyclic_switch_cases_incomplete_switch_head(self, node, graph, full_graph) -> bool:
|
|
1094
1100
|
try:
|
|
@@ -1162,7 +1168,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1162
1168
|
self._switch_handle_gotos(cases, node_default, switch_end_addr)
|
|
1163
1169
|
return True
|
|
1164
1170
|
|
|
1165
|
-
def _match_acyclic_switch_cases_address_loaded_from_memory(self, node, graph, full_graph
|
|
1171
|
+
def _match_acyclic_switch_cases_address_loaded_from_memory(self, node, graph, full_graph) -> bool:
|
|
1166
1172
|
try:
|
|
1167
1173
|
last_stmt = self.cond_proc.get_last_statement(node)
|
|
1168
1174
|
except EmptyBlockNotice:
|
|
@@ -1176,14 +1182,14 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1176
1182
|
return False
|
|
1177
1183
|
|
|
1178
1184
|
for t in successor_addrs:
|
|
1179
|
-
if t in jump_tables:
|
|
1185
|
+
if t in self.jump_tables:
|
|
1180
1186
|
# this is a candidate!
|
|
1181
1187
|
target = t
|
|
1182
1188
|
break
|
|
1183
1189
|
else:
|
|
1184
1190
|
return False
|
|
1185
1191
|
|
|
1186
|
-
jump_table = jump_tables[target]
|
|
1192
|
+
jump_table = self.jump_tables[target]
|
|
1187
1193
|
if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
|
|
1188
1194
|
return False
|
|
1189
1195
|
|
|
@@ -1203,6 +1209,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1203
1209
|
return False
|
|
1204
1210
|
|
|
1205
1211
|
# populate whitelist_edges
|
|
1212
|
+
assert jump_table.jumptable_entries is not None
|
|
1206
1213
|
for case_node_addr in jump_table.jumptable_entries:
|
|
1207
1214
|
self.whitelist_edges.add((node_a.addr, case_node_addr))
|
|
1208
1215
|
self.whitelist_edges.add((node.addr, node_b_addr))
|
|
@@ -1228,6 +1235,12 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1228
1235
|
if not r:
|
|
1229
1236
|
return False
|
|
1230
1237
|
|
|
1238
|
+
node_default = self._switch_find_default_node(graph, node, node_b_addr)
|
|
1239
|
+
if node_default is not None:
|
|
1240
|
+
# ensure we have successfully structured node_default
|
|
1241
|
+
if full_graph.out_degree(node_default) > 1:
|
|
1242
|
+
return False
|
|
1243
|
+
|
|
1231
1244
|
# un-structure IncompleteSwitchCaseNode
|
|
1232
1245
|
if isinstance(node_a, SequenceNode) and node_a.nodes and isinstance(node_a.nodes[0], IncompleteSwitchCaseNode):
|
|
1233
1246
|
_, new_seq_node = self._unpack_sequencenode_head(graph, node_a)
|
|
@@ -1285,10 +1298,99 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1285
1298
|
|
|
1286
1299
|
return True
|
|
1287
1300
|
|
|
1288
|
-
def
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1301
|
+
def _match_acyclic_switch_cases_address_loaded_from_memory_no_default_node(self, node, graph, full_graph) -> bool:
|
|
1302
|
+
# sanity checks
|
|
1303
|
+
if not isinstance(node, IncompleteSwitchCaseNode):
|
|
1304
|
+
return False
|
|
1305
|
+
if node.addr not in self.jump_tables:
|
|
1306
|
+
return False
|
|
1307
|
+
# ensure _match_acyclic_switch_cases_address_load_from_memory cannot structure its predecessor (and this node)
|
|
1308
|
+
preds = list(graph.predecessors(node))
|
|
1309
|
+
if len(preds) != 1:
|
|
1310
|
+
return False
|
|
1311
|
+
pred = preds[0]
|
|
1312
|
+
if full_graph.out_degree[pred] != 1:
|
|
1313
|
+
return False
|
|
1314
|
+
jump_table: IndirectJump = self.jump_tables[node.addr]
|
|
1315
|
+
if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
|
|
1316
|
+
return False
|
|
1317
|
+
|
|
1318
|
+
# extract the comparison expression, lower-, and upper-bounds from the last statement
|
|
1319
|
+
last_stmt = self.cond_proc.get_last_statement(node.head)
|
|
1320
|
+
if not isinstance(last_stmt, Jump):
|
|
1321
|
+
return False
|
|
1322
|
+
cmp_expr = switch_extract_switch_expr_from_jump_target(last_stmt.target)
|
|
1323
|
+
if cmp_expr is None:
|
|
1324
|
+
return False
|
|
1325
|
+
cmp_lb = 0
|
|
1326
|
+
|
|
1327
|
+
# populate whitelist_edges
|
|
1328
|
+
assert jump_table.jumptable_entries is not None
|
|
1329
|
+
|
|
1330
|
+
# sanity check: case nodes are successors to node_a. all case nodes must have at most common one successor
|
|
1331
|
+
node_pred = None
|
|
1332
|
+
if graph.in_degree[node] == 1:
|
|
1333
|
+
node_pred = next(iter(graph.predecessors(node)))
|
|
1334
|
+
|
|
1335
|
+
case_nodes = list(graph.successors(node))
|
|
1336
|
+
|
|
1337
|
+
# case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
|
|
1338
|
+
# optimization)
|
|
1339
|
+
# example: touch_touch_no_switch.o:main
|
|
1340
|
+
r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
|
|
1341
|
+
|
|
1342
|
+
# case 2: the common successor is not directly reachable from node_a. this is a more common case.
|
|
1343
|
+
if not r:
|
|
1344
|
+
r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
|
|
1345
|
+
|
|
1346
|
+
if not r:
|
|
1347
|
+
return False
|
|
1348
|
+
|
|
1349
|
+
# un-structure IncompleteSwitchCaseNode
|
|
1350
|
+
if isinstance(node, IncompleteSwitchCaseNode):
|
|
1351
|
+
r = self._unpack_incompleteswitchcasenode(graph, node)
|
|
1352
|
+
if not r:
|
|
1353
|
+
return False
|
|
1354
|
+
self._unpack_incompleteswitchcasenode(full_graph, node) # this shall not fail
|
|
1355
|
+
# update node
|
|
1356
|
+
node = next(iter(nn for nn in graph.nodes if nn.addr == jump_table.addr))
|
|
1357
|
+
|
|
1358
|
+
case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
|
|
1359
|
+
|
|
1360
|
+
cases, _, to_remove = self._switch_build_cases(
|
|
1361
|
+
case_and_entry_addrs,
|
|
1362
|
+
node,
|
|
1363
|
+
node,
|
|
1364
|
+
None,
|
|
1365
|
+
graph,
|
|
1366
|
+
full_graph,
|
|
1367
|
+
)
|
|
1368
|
+
|
|
1369
|
+
# we don't know what the end address of this switch-case structure is. let's figure it out
|
|
1370
|
+
switch_end_addr = self._switch_find_switch_end_addr(cases, None, {nn.addr for nn in self._region.graph})
|
|
1371
|
+
r = self._make_switch_cases_core(
|
|
1372
|
+
node,
|
|
1373
|
+
cmp_expr,
|
|
1374
|
+
cases,
|
|
1375
|
+
None,
|
|
1376
|
+
None,
|
|
1377
|
+
last_stmt.ins_addr,
|
|
1378
|
+
to_remove,
|
|
1379
|
+
graph,
|
|
1380
|
+
full_graph,
|
|
1381
|
+
node_a=None,
|
|
1382
|
+
)
|
|
1383
|
+
if not r:
|
|
1384
|
+
return False
|
|
1385
|
+
|
|
1386
|
+
# fully structured into a switch-case. remove node from switch_case_known_heads
|
|
1387
|
+
if switch_end_addr is not None:
|
|
1388
|
+
self._switch_handle_gotos(cases, None, switch_end_addr)
|
|
1389
|
+
|
|
1390
|
+
return True
|
|
1391
|
+
|
|
1392
|
+
def _match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(self, node, graph, full_graph) -> bool:
|
|
1393
|
+
if node.addr not in self.jump_tables:
|
|
1292
1394
|
return False
|
|
1293
1395
|
|
|
1294
1396
|
try:
|
|
@@ -1298,7 +1400,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1298
1400
|
if not (isinstance(last_stmt, Jump) and not isinstance(last_stmt.target, Const)):
|
|
1299
1401
|
return False
|
|
1300
1402
|
|
|
1301
|
-
jump_table = jump_tables[node.addr]
|
|
1403
|
+
jump_table = self.jump_tables[node.addr]
|
|
1302
1404
|
if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
|
|
1303
1405
|
return False
|
|
1304
1406
|
|
|
@@ -1310,6 +1412,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1310
1412
|
case_count = cmp_ub - cmp_lb + 1
|
|
1311
1413
|
|
|
1312
1414
|
# ensure we have the same number of cases
|
|
1415
|
+
assert jump_table.jumptable_entries is not None
|
|
1313
1416
|
if case_count != len(jump_table.jumptable_entries):
|
|
1314
1417
|
return False
|
|
1315
1418
|
|
|
@@ -1373,10 +1476,10 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1373
1476
|
|
|
1374
1477
|
return True
|
|
1375
1478
|
|
|
1376
|
-
def _match_acyclic_switch_cases_address_computed(self, node, graph, full_graph
|
|
1377
|
-
if node.addr not in jump_tables:
|
|
1479
|
+
def _match_acyclic_switch_cases_address_computed(self, node, graph, full_graph) -> bool:
|
|
1480
|
+
if node.addr not in self.jump_tables:
|
|
1378
1481
|
return False
|
|
1379
|
-
jump_table = jump_tables[node.addr]
|
|
1482
|
+
jump_table = self.jump_tables[node.addr]
|
|
1380
1483
|
if jump_table.type != IndirectJumpType.Jumptable_AddressComputed:
|
|
1381
1484
|
return False
|
|
1382
1485
|
|
|
@@ -1403,9 +1506,16 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1403
1506
|
|
|
1404
1507
|
if isinstance(last_stmt.false_target, Const):
|
|
1405
1508
|
default_addr = last_stmt.false_target.value
|
|
1509
|
+
assert isinstance(default_addr, int)
|
|
1406
1510
|
else:
|
|
1407
1511
|
return False
|
|
1408
1512
|
|
|
1513
|
+
node_default = self._switch_find_default_node(graph, node, default_addr)
|
|
1514
|
+
if node_default is not None:
|
|
1515
|
+
# ensure we have successfully structured node_default
|
|
1516
|
+
if full_graph.out_degree(node_default) > 1:
|
|
1517
|
+
return False
|
|
1518
|
+
|
|
1409
1519
|
case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
|
|
1410
1520
|
|
|
1411
1521
|
cases, node_default, to_remove = self._switch_build_cases(
|
|
@@ -1425,10 +1535,10 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1425
1535
|
)
|
|
1426
1536
|
|
|
1427
1537
|
def _match_acyclic_incomplete_switch_cases(
|
|
1428
|
-
self, node, graph: networkx.DiGraph, full_graph: networkx.DiGraph
|
|
1538
|
+
self, node, graph: networkx.DiGraph, full_graph: networkx.DiGraph
|
|
1429
1539
|
) -> bool:
|
|
1430
1540
|
# sanity checks
|
|
1431
|
-
if node.addr not in jump_tables:
|
|
1541
|
+
if node.addr not in self.jump_tables:
|
|
1432
1542
|
return False
|
|
1433
1543
|
if isinstance(node, IncompleteSwitchCaseNode):
|
|
1434
1544
|
return False
|
|
@@ -1437,9 +1547,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1437
1547
|
|
|
1438
1548
|
successors = list(graph.successors(node))
|
|
1439
1549
|
|
|
1550
|
+
jump_table = self.jump_tables[node.addr]
|
|
1551
|
+
assert jump_table.jumptable_entries is not None
|
|
1440
1552
|
if (
|
|
1441
1553
|
successors
|
|
1442
|
-
and {succ.addr for succ in successors} == set(
|
|
1554
|
+
and {succ.addr for succ in successors} == set(jump_table.jumptable_entries)
|
|
1443
1555
|
and all(graph.in_degree[succ] == 1 for succ in successors)
|
|
1444
1556
|
):
|
|
1445
1557
|
out_nodes = set()
|
|
@@ -1473,16 +1585,12 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1473
1585
|
cases: OrderedDict[int | tuple[int, ...], SequenceNode] = OrderedDict()
|
|
1474
1586
|
to_remove = set()
|
|
1475
1587
|
|
|
1476
|
-
# it is possible that the default node gets duplicated by other analyses and creates a default node (addr.a)
|
|
1477
|
-
# and a case node (addr.b). The addr.a node is a successor to the head node while the addr.b node is a
|
|
1478
|
-
# successor to node_a
|
|
1479
1588
|
default_node_candidates = (
|
|
1480
1589
|
[nn for nn in graph.nodes if nn.addr == node_b_addr] if node_b_addr is not None else []
|
|
1481
1590
|
)
|
|
1482
|
-
node_default
|
|
1483
|
-
|
|
1591
|
+
node_default = (
|
|
1592
|
+
self._switch_find_default_node(graph, head_node, node_b_addr) if node_b_addr is not None else None
|
|
1484
1593
|
)
|
|
1485
|
-
|
|
1486
1594
|
if node_default is not None and not isinstance(node_default, SequenceNode):
|
|
1487
1595
|
# make the default node a SequenceNode so that we can insert Break and Continue nodes into it later
|
|
1488
1596
|
new_node = SequenceNode(node_default.addr, nodes=[node_default])
|
|
@@ -1615,7 +1723,12 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1615
1723
|
for case_node in to_remove:
|
|
1616
1724
|
if case_node is not node_default and case_node is not node_a and case_node is not head:
|
|
1617
1725
|
for succ in graph.successors(case_node):
|
|
1618
|
-
if
|
|
1726
|
+
if (
|
|
1727
|
+
succ is not case_node
|
|
1728
|
+
and succ is not head
|
|
1729
|
+
and succ is not self._region.head
|
|
1730
|
+
and graph.in_degree[succ] == 1
|
|
1731
|
+
):
|
|
1619
1732
|
# succ will be dangling - not ready to be structured yet - do it later
|
|
1620
1733
|
return False
|
|
1621
1734
|
|
|
@@ -1673,6 +1786,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1673
1786
|
for _out_src, out_dst in out_edges[1:]:
|
|
1674
1787
|
if out_dst in full_graph and out_dst not in graph and full_graph.in_degree[out_dst] == 0:
|
|
1675
1788
|
full_graph.remove_node(out_dst)
|
|
1789
|
+
assert self._region.successors is not None
|
|
1676
1790
|
if out_dst in self._region.successors:
|
|
1677
1791
|
self._region.successors.remove(out_dst)
|
|
1678
1792
|
|
|
@@ -1705,8 +1819,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1705
1819
|
return case_and_entry_addrs
|
|
1706
1820
|
|
|
1707
1821
|
def _is_node_unstructured_switch_case_head(self, node) -> bool:
|
|
1708
|
-
|
|
1709
|
-
if node.addr in jump_tables:
|
|
1822
|
+
if node.addr in self.jump_tables:
|
|
1710
1823
|
# maybe it has been structured?
|
|
1711
1824
|
try:
|
|
1712
1825
|
last_stmts = self.cond_proc.get_last_statements(node)
|
|
@@ -1739,6 +1852,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1739
1852
|
and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, end_node)
|
|
1740
1853
|
and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, start_node)
|
|
1741
1854
|
and end_node not in self.dowhile_known_tail_nodes
|
|
1855
|
+
and not isinstance(end_node, IncompleteSwitchCaseNode)
|
|
1742
1856
|
):
|
|
1743
1857
|
# merge two blocks
|
|
1744
1858
|
new_seq = self._merge_nodes(start_node, end_node)
|
|
@@ -2299,7 +2413,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2299
2413
|
):
|
|
2300
2414
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2301
2415
|
edge_cond_successor = self.cond_proc.recover_edge_condition(full_graph, start_node, successor)
|
|
2302
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_successor):
|
|
2416
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_successor): # type: ignore
|
|
2303
2417
|
# c0 = !c0
|
|
2304
2418
|
left_succs = list(full_graph.successors(left))
|
|
2305
2419
|
if len(left_succs) == 2 and successor in left_succs:
|
|
@@ -2310,7 +2424,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2310
2424
|
edge_cond_left_successor = self.cond_proc.recover_edge_condition(
|
|
2311
2425
|
full_graph, left, successor
|
|
2312
2426
|
)
|
|
2313
|
-
if claripy.is_true(
|
|
2427
|
+
if claripy.is_true(
|
|
2428
|
+
claripy.Not(edge_cond_left_right) == edge_cond_left_successor # type: ignore
|
|
2429
|
+
):
|
|
2314
2430
|
# c1 = !c1
|
|
2315
2431
|
return left, edge_cond_left, successor, edge_cond_left_successor, right
|
|
2316
2432
|
return None
|
|
@@ -2349,14 +2465,14 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2349
2465
|
):
|
|
2350
2466
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2351
2467
|
edge_cond_else = self.cond_proc.recover_edge_condition(full_graph, start_node, else_node)
|
|
2352
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_else):
|
|
2468
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_else): # type: ignore
|
|
2353
2469
|
# c0 = !c0
|
|
2354
2470
|
left_succs = list(full_graph.successors(left))
|
|
2355
2471
|
if len(left_succs) == 2 and else_node in left_succs:
|
|
2356
2472
|
right = next(iter(succ for succ in left_succs if succ is not else_node))
|
|
2357
2473
|
edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
|
|
2358
2474
|
edge_cond_left_else = self.cond_proc.recover_edge_condition(full_graph, left, else_node)
|
|
2359
|
-
if claripy.is_true(claripy.Not(edge_cond_left_right) == edge_cond_left_else):
|
|
2475
|
+
if claripy.is_true(claripy.Not(edge_cond_left_right) == edge_cond_left_else): # type: ignore
|
|
2360
2476
|
# c1 = !c1
|
|
2361
2477
|
return left, edge_cond_left, right, edge_cond_left_right, else_node
|
|
2362
2478
|
return None
|
|
@@ -2441,6 +2557,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2441
2557
|
goto1_target = None
|
|
2442
2558
|
|
|
2443
2559
|
if goto0_condition is not None:
|
|
2560
|
+
assert goto0_target is not None and goto1_target is not None
|
|
2444
2561
|
goto0 = Block(
|
|
2445
2562
|
last_stmt.ins_addr,
|
|
2446
2563
|
0,
|
|
@@ -2510,7 +2627,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2510
2627
|
dst: Block | BaseNode,
|
|
2511
2628
|
last=True,
|
|
2512
2629
|
condjump_only=False,
|
|
2513
|
-
) -> tuple[int | None, BaseNode | None, Block | None]:
|
|
2630
|
+
) -> tuple[int | None, BaseNode | None, Block | MultiNode | BreakNode | None]:
|
|
2514
2631
|
"""
|
|
2515
2632
|
|
|
2516
2633
|
:param node:
|
|
@@ -2678,7 +2795,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2678
2795
|
return True
|
|
2679
2796
|
return all(not isinstance(stmt, (ConditionalJump, Jump)) for stmt in stmts[:-1])
|
|
2680
2797
|
|
|
2681
|
-
def _to_statement_list(node: Block | MultiNode | SequenceNode) -> list[Statement]:
|
|
2798
|
+
def _to_statement_list(node: Block | MultiNode | SequenceNode | BaseNode) -> list[Statement]:
|
|
2682
2799
|
if isinstance(node, Block):
|
|
2683
2800
|
return node.statements
|
|
2684
2801
|
if isinstance(node, MultiNode):
|
|
@@ -2733,7 +2850,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2733
2850
|
graph.remove_edge(src, succ)
|
|
2734
2851
|
|
|
2735
2852
|
@staticmethod
|
|
2736
|
-
def _remove_first_statement_if_jump(node: BaseNode | Block) -> Jump | ConditionalJump | None:
|
|
2853
|
+
def _remove_first_statement_if_jump(node: BaseNode | Block | MultiNode) -> Jump | ConditionalJump | None:
|
|
2737
2854
|
if isinstance(node, Block):
|
|
2738
2855
|
if node.statements:
|
|
2739
2856
|
idx = 0
|
|
@@ -2781,7 +2898,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2781
2898
|
src, dst = edge_
|
|
2782
2899
|
dst_in_degree = graph.in_degree[dst]
|
|
2783
2900
|
src_out_degree = graph.out_degree[src]
|
|
2784
|
-
return -node_seq.get(dst), dst_in_degree, src_out_degree, -src.addr, -dst.addr
|
|
2901
|
+
return -node_seq.get(dst), dst_in_degree, src_out_degree, -src.addr, -dst.addr # type: ignore
|
|
2785
2902
|
|
|
2786
2903
|
return sorted(edges, key=_sort_edge, reverse=True)
|
|
2787
2904
|
|
|
@@ -92,6 +92,7 @@ class RecursiveStructurer(Analysis):
|
|
|
92
92
|
case_entry_to_switch_head=self._case_entry_to_switch_head,
|
|
93
93
|
func=self.function,
|
|
94
94
|
parent_region=parent_region,
|
|
95
|
+
jump_tables=self.kb.cfgs["CFGFast"].jump_tables,
|
|
95
96
|
**self.structurer_options,
|
|
96
97
|
)
|
|
97
98
|
# replace this region with the resulting node in its parent region... if it's not an orphan
|