angr 9.2.138__py3-none-manylinux2014_x86_64.whl → 9.2.140__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 +48 -21
- angr/analyses/calling_convention/fact_collector.py +59 -12
- angr/analyses/calling_convention/utils.py +2 -2
- angr/analyses/cfg/cfg_base.py +13 -0
- angr/analyses/cfg/cfg_fast.py +23 -4
- angr/analyses/decompiler/ail_simplifier.py +79 -53
- angr/analyses/decompiler/block_simplifier.py +0 -2
- angr/analyses/decompiler/callsite_maker.py +80 -14
- angr/analyses/decompiler/clinic.py +99 -80
- angr/analyses/decompiler/condition_processor.py +2 -2
- angr/analyses/decompiler/decompiler.py +19 -7
- angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
- angr/analyses/decompiler/expression_narrower.py +1 -1
- angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
- angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
- angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
- angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +21 -12
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +17 -9
- angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
- angr/analyses/decompiler/presets/fast.py +2 -0
- angr/analyses/decompiler/presets/full.py +2 -0
- angr/analyses/decompiler/region_simplifiers/expr_folding.py +259 -108
- angr/analyses/decompiler/region_simplifiers/region_simplifier.py +28 -9
- angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
- angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
- angr/analyses/decompiler/structured_codegen/c.py +10 -3
- angr/analyses/decompiler/structuring/dream.py +28 -19
- angr/analyses/decompiler/structuring/phoenix.py +253 -89
- angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
- angr/analyses/decompiler/structuring/structurer_base.py +121 -46
- angr/analyses/decompiler/structuring/structurer_nodes.py +6 -1
- angr/analyses/decompiler/utils.py +60 -1
- angr/analyses/deobfuscator/api_obf_finder.py +13 -5
- angr/analyses/deobfuscator/api_obf_type2_finder.py +166 -0
- 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 +129 -87
- angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
- angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
- 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 +62 -67
- angr/analyses/variable_recovery/engine_vex.py +1 -1
- angr/analyses/variable_recovery/irsb_scanner.py +2 -2
- angr/block.py +69 -107
- angr/callable.py +14 -7
- angr/calling_conventions.py +81 -10
- angr/distributed/__init__.py +1 -1
- angr/engines/__init__.py +7 -8
- angr/engines/engine.py +3 -138
- angr/engines/failure.py +2 -2
- angr/engines/hook.py +2 -2
- angr/engines/light/engine.py +5 -10
- angr/engines/pcode/emulate.py +2 -2
- angr/engines/pcode/engine.py +2 -14
- angr/engines/pcode/lifter.py +2 -2
- 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 +123 -17
- angr/engines/syscall.py +2 -2
- angr/engines/unicorn.py +3 -3
- angr/engines/vex/heavy/heavy.py +3 -15
- angr/engines/vex/lifter.py +2 -2
- angr/engines/vex/light/light.py +2 -2
- angr/factory.py +4 -19
- angr/knowledge_plugins/cfg/cfg_model.py +3 -2
- angr/knowledge_plugins/key_definitions/atoms.py +8 -4
- angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
- angr/knowledge_plugins/labels.py +2 -2
- angr/knowledge_plugins/obfuscations.py +1 -0
- angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
- angr/sim_type.py +19 -17
- 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-9.2.138.dist-info → angr-9.2.140.dist-info}/METADATA +6 -6
- {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/RECORD +100 -98
- {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/LICENSE +0 -0
- {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/WHEEL +0 -0
- {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/entry_points.txt +0 -0
- {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/top_level.txt +0 -0
|
@@ -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 (
|
|
@@ -241,6 +242,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
241
242
|
def _match_cyclic_schemas(self, node, head, graph, full_graph) -> bool:
|
|
242
243
|
matched, loop_node, successor_node = self._match_cyclic_while(node, head, graph, full_graph)
|
|
243
244
|
if matched:
|
|
245
|
+
assert loop_node is not None and successor_node is not None
|
|
244
246
|
# traverse this node and rewrite all conditional jumps that go outside the loop to breaks
|
|
245
247
|
self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
|
|
246
248
|
# traverse this node and rewrite all jumps that go to the beginning of the loop to continue
|
|
@@ -249,6 +251,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
249
251
|
|
|
250
252
|
matched, loop_node, successor_node = self._match_cyclic_dowhile(node, head, graph, full_graph)
|
|
251
253
|
if matched:
|
|
254
|
+
assert loop_node is not None and successor_node is not None
|
|
252
255
|
# traverse this node and rewrite all conditional jumps that go outside the loop to breaks
|
|
253
256
|
self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
|
|
254
257
|
# traverse this node and rewrite all jumps that go to the beginning of the loop to continue
|
|
@@ -260,6 +263,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
260
263
|
node, head, graph, full_graph
|
|
261
264
|
)
|
|
262
265
|
if matched:
|
|
266
|
+
assert loop_node is not None and successor_node is not None
|
|
263
267
|
# traverse this node and rewrite all conditional jumps that go outside the loop to breaks
|
|
264
268
|
self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
|
|
265
269
|
# traverse this node and rewrite all jumps that go to the beginning of the loop to continue
|
|
@@ -268,6 +272,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
268
272
|
|
|
269
273
|
matched, loop_node = self._match_cyclic_natural_loop(node, head, graph, full_graph)
|
|
270
274
|
if matched:
|
|
275
|
+
assert loop_node is not None
|
|
271
276
|
if self._region.successors is not None and len(self._region.successors) == 1:
|
|
272
277
|
# traverse this node and rewrite all conditional jumps that go outside the loop to breaks
|
|
273
278
|
self._rewrite_conditional_jumps_to_breaks(
|
|
@@ -318,7 +323,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
318
323
|
# otherwise it's a do-while loop
|
|
319
324
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
|
|
320
325
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
|
|
321
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
326
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
322
327
|
# c = !c
|
|
323
328
|
if head_block_idx == 0:
|
|
324
329
|
self._remove_first_statement_if_jump(head_block)
|
|
@@ -346,7 +351,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
346
351
|
if head_block is not None:
|
|
347
352
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
|
|
348
353
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
|
|
349
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
354
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
350
355
|
# c = !c
|
|
351
356
|
if PhoenixStructurer._is_single_statement_block(node):
|
|
352
357
|
# the single-statement-block check is to ensure we don't execute any code before the
|
|
@@ -366,6 +371,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
366
371
|
return True, loop_node, right
|
|
367
372
|
# we generate a while-true loop instead
|
|
368
373
|
last_stmt = self._remove_last_statement_if_jump(head_block)
|
|
374
|
+
assert last_stmt is not None
|
|
369
375
|
cond_jump = Jump(
|
|
370
376
|
None,
|
|
371
377
|
Const(None, None, right.addr, self.project.arch.bits),
|
|
@@ -394,7 +400,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
394
400
|
if head_block is not None:
|
|
395
401
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
|
|
396
402
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
|
|
397
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
403
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
398
404
|
# c = !c
|
|
399
405
|
self._remove_last_statement_if_jump(head_block)
|
|
400
406
|
cond_break = ConditionalBreakNode(node.addr, edge_cond_right, right.addr)
|
|
@@ -426,6 +432,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
426
432
|
return False, None, None
|
|
427
433
|
|
|
428
434
|
loop_cond = None
|
|
435
|
+
successor_node = None
|
|
436
|
+
succ_node_is_true_node = None
|
|
429
437
|
if (
|
|
430
438
|
isinstance(node, SequenceNode)
|
|
431
439
|
and node.nodes
|
|
@@ -436,21 +444,27 @@ class PhoenixStructurer(StructurerBase):
|
|
|
436
444
|
and node.nodes[-1].true_node is not None
|
|
437
445
|
and node.nodes[-1].false_node is not None
|
|
438
446
|
):
|
|
439
|
-
|
|
440
|
-
#
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
except EmptyBlockNotice:
|
|
445
|
-
last_stmt = None
|
|
446
|
-
if last_stmt is not None and isinstance(last_stmt, Return):
|
|
447
|
+
# try both true node and false node; pick the first node with only Returns as last statements as the
|
|
448
|
+
# successor.
|
|
449
|
+
if self._cyclic_while_with_single_successor_must_return(node.nodes[-1].true_node):
|
|
450
|
+
succ_node_is_true_node = True
|
|
451
|
+
successor_node = node.nodes[-1].true_node
|
|
447
452
|
loop_cond = claripy.Not(node.nodes[-1].condition)
|
|
453
|
+
elif self._cyclic_while_with_single_successor_must_return(node.nodes[-1].false_node):
|
|
454
|
+
succ_node_is_true_node = False
|
|
455
|
+
successor_node = node.nodes[-1].false_node
|
|
456
|
+
loop_cond = node.nodes[-1].condition
|
|
457
|
+
else:
|
|
458
|
+
loop_cond = None
|
|
448
459
|
|
|
449
460
|
if loop_cond is None:
|
|
450
461
|
return False, None, None
|
|
451
462
|
|
|
452
463
|
node_copy = node.copy()
|
|
453
|
-
|
|
464
|
+
# replace the last node with the intended successor node
|
|
465
|
+
node_copy.nodes[-1] = (
|
|
466
|
+
node_copy.nodes[-1].false_node if succ_node_is_true_node else node_copy.nodes[-1].true_node
|
|
467
|
+
)
|
|
454
468
|
# check if there is a cycle that starts with node and ends with node
|
|
455
469
|
next_node = node
|
|
456
470
|
seq_node = SequenceNode(node.addr, nodes=[node_copy])
|
|
@@ -487,6 +501,15 @@ class PhoenixStructurer(StructurerBase):
|
|
|
487
501
|
|
|
488
502
|
return True, loop_node, successor_node
|
|
489
503
|
|
|
504
|
+
def _cyclic_while_with_single_successor_must_return(self, successor_node: SequenceNode) -> bool:
|
|
505
|
+
try:
|
|
506
|
+
last_stmts = self.cond_proc.get_last_statements(successor_node)
|
|
507
|
+
except EmptyBlockNotice:
|
|
508
|
+
return False
|
|
509
|
+
if not last_stmts:
|
|
510
|
+
return False
|
|
511
|
+
return all(isinstance(stmt, Return) for stmt in last_stmts)
|
|
512
|
+
|
|
490
513
|
def _match_cyclic_dowhile(self, node, head, graph, full_graph) -> tuple[bool, LoopNode | None, BaseNode | None]:
|
|
491
514
|
preds = list(full_graph.predecessors(node))
|
|
492
515
|
succs = list(full_graph.successors(node))
|
|
@@ -504,7 +527,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
504
527
|
if succ_block is not None:
|
|
505
528
|
edge_cond_succhead = self.cond_proc.recover_edge_condition(full_graph, succ_block, node)
|
|
506
529
|
edge_cond_succout = self.cond_proc.recover_edge_condition(full_graph, succ_block, out_node)
|
|
507
|
-
if claripy.is_true(claripy.Not(edge_cond_succhead) == edge_cond_succout):
|
|
530
|
+
if claripy.is_true(claripy.Not(edge_cond_succhead) == edge_cond_succout): # type: ignore
|
|
508
531
|
# c = !c
|
|
509
532
|
self._remove_last_statement_if_jump(succ)
|
|
510
533
|
drop_succ = False
|
|
@@ -543,7 +566,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
543
566
|
# possible candidate
|
|
544
567
|
edge_cond_head = self.cond_proc.recover_edge_condition(full_graph, node, node)
|
|
545
568
|
edge_cond_head_succ = self.cond_proc.recover_edge_condition(full_graph, node, succ)
|
|
546
|
-
if claripy.is_true(claripy.Not(edge_cond_head) == edge_cond_head_succ):
|
|
569
|
+
if claripy.is_true(claripy.Not(edge_cond_head) == edge_cond_head_succ): # type: ignore
|
|
547
570
|
# c = !c
|
|
548
571
|
self._remove_last_statement_if_jump(node)
|
|
549
572
|
seq_node = SequenceNode(node.addr, nodes=[node]) if not isinstance(node, SequenceNode) else node
|
|
@@ -617,9 +640,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
617
640
|
|
|
618
641
|
def _refine_cyclic_core(self, loop_head) -> bool:
|
|
619
642
|
graph: networkx.DiGraph = self._region.graph
|
|
620
|
-
fullgraph: networkx.DiGraph =
|
|
621
|
-
|
|
622
|
-
|
|
643
|
+
fullgraph: networkx.DiGraph = (
|
|
644
|
+
self._region.graph_with_successors
|
|
645
|
+
if self._region.graph_with_successors is not None
|
|
646
|
+
else networkx.DiGraph(self._region.graph)
|
|
647
|
+
)
|
|
623
648
|
|
|
624
649
|
# check if there is an out-going edge from the loop head
|
|
625
650
|
head_succs = list(fullgraph.successors(loop_head))
|
|
@@ -638,6 +663,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
638
663
|
# for now, we handle the most common case: both successors exist in the graph of the parent region, and
|
|
639
664
|
# one successor has a path to the other successor
|
|
640
665
|
if is_while and is_dowhile and self._parent_region is not None:
|
|
666
|
+
assert result_while is not None and result_dowhile is not None
|
|
641
667
|
succ_while = result_while[-1]
|
|
642
668
|
succ_dowhile = result_dowhile[-1]
|
|
643
669
|
if succ_while in self._parent_region.graph and succ_dowhile in self._parent_region.graph:
|
|
@@ -651,9 +677,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
651
677
|
is_while = False
|
|
652
678
|
|
|
653
679
|
if is_while:
|
|
680
|
+
assert result_while is not None
|
|
654
681
|
loop_type = "while"
|
|
655
682
|
continue_edges, outgoing_edges, continue_node, successor = result_while
|
|
656
683
|
elif is_dowhile:
|
|
684
|
+
assert result_dowhile is not None
|
|
657
685
|
loop_type = "do-while"
|
|
658
686
|
continue_edges, outgoing_edges, continue_node, successor = result_dowhile
|
|
659
687
|
|
|
@@ -736,7 +764,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
736
764
|
# unwanted results, e.g., inserting a break (that's intended to break out of the loop) inside a
|
|
737
765
|
# switch-case that is nested within a loop.
|
|
738
766
|
last_src_stmt = self.cond_proc.get_last_statement(src_block)
|
|
767
|
+
assert last_src_stmt is not None
|
|
739
768
|
break_cond = self.cond_proc.recover_edge_condition(fullgraph, src_block, dst)
|
|
769
|
+
assert successor.addr is not None
|
|
740
770
|
if claripy.is_true(break_cond):
|
|
741
771
|
break_stmt = Jump(
|
|
742
772
|
None,
|
|
@@ -761,7 +791,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
761
791
|
)
|
|
762
792
|
new_node = SequenceNode(src_block.addr, nodes=[src_block, break_node])
|
|
763
793
|
if has_continue:
|
|
764
|
-
if self.is_a_jump_target(
|
|
794
|
+
if continue_node.addr is not None and self.is_a_jump_target(
|
|
795
|
+
last_src_stmt, continue_node.addr
|
|
796
|
+
):
|
|
765
797
|
# instead of a conditional break node, we should insert a condition node instead
|
|
766
798
|
break_stmt = Jump(
|
|
767
799
|
None,
|
|
@@ -817,7 +849,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
817
849
|
if fullgraph.in_degree[dst] == 0:
|
|
818
850
|
# drop this node
|
|
819
851
|
fullgraph.remove_node(dst)
|
|
820
|
-
if dst in self._region.successors:
|
|
852
|
+
if self._region.successors and dst in self._region.successors:
|
|
821
853
|
self._region.successors.remove(dst)
|
|
822
854
|
|
|
823
855
|
if len(continue_edges) > 1:
|
|
@@ -839,7 +871,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
839
871
|
if cont_block is None:
|
|
840
872
|
# cont_block is not found. but it's ok. one possibility is that src is a jump table head with one
|
|
841
873
|
# case being the loop head. in such cases, we can just remove the edge.
|
|
842
|
-
if src.addr not in self.
|
|
874
|
+
if src.addr not in self.jump_tables:
|
|
843
875
|
l.debug(
|
|
844
876
|
"_refine_cyclic_core: Cannot find the block going to loop head for edge %r -> %r. "
|
|
845
877
|
"Remove the edge anyway.",
|
|
@@ -1040,25 +1072,29 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1040
1072
|
# switch cases
|
|
1041
1073
|
|
|
1042
1074
|
def _match_acyclic_switch_cases(self, graph: networkx.DiGraph, full_graph: networkx.DiGraph, node) -> bool:
|
|
1043
|
-
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):
|
|
1044
1083
|
return False
|
|
1045
1084
|
|
|
1046
1085
|
r = self._match_acyclic_switch_cases_incomplete_switch_head(node, graph, full_graph)
|
|
1047
1086
|
if r:
|
|
1048
1087
|
return r
|
|
1049
|
-
|
|
1050
|
-
r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(
|
|
1051
|
-
node, graph, full_graph, jump_tables
|
|
1052
|
-
)
|
|
1088
|
+
r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(node, graph, full_graph)
|
|
1053
1089
|
if r:
|
|
1054
1090
|
return r
|
|
1055
|
-
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)
|
|
1056
1092
|
if r:
|
|
1057
1093
|
return r
|
|
1058
|
-
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)
|
|
1059
1095
|
if r:
|
|
1060
1096
|
return r
|
|
1061
|
-
return self._match_acyclic_incomplete_switch_cases(node, graph, full_graph
|
|
1097
|
+
return self._match_acyclic_incomplete_switch_cases(node, graph, full_graph)
|
|
1062
1098
|
|
|
1063
1099
|
def _match_acyclic_switch_cases_incomplete_switch_head(self, node, graph, full_graph) -> bool:
|
|
1064
1100
|
try:
|
|
@@ -1074,7 +1110,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1074
1110
|
|
|
1075
1111
|
# make a fake jumptable
|
|
1076
1112
|
node_default_addr = None
|
|
1077
|
-
case_entries: dict[int, tuple[int, int | None]] = {}
|
|
1113
|
+
case_entries: dict[int, int | tuple[int, int | None]] = {}
|
|
1078
1114
|
for _, case_value, case_target_addr, case_target_idx, _ in last_stmt.case_addrs:
|
|
1079
1115
|
if isinstance(case_value, str):
|
|
1080
1116
|
if case_value == "default":
|
|
@@ -1127,28 +1163,33 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1127
1163
|
if self._region.graph_with_successors is not None:
|
|
1128
1164
|
self._region.graph_with_successors.remove_node(o)
|
|
1129
1165
|
|
|
1130
|
-
self.
|
|
1166
|
+
switch_end_addr = self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
|
|
1167
|
+
if switch_end_addr is not None:
|
|
1168
|
+
self._switch_handle_gotos(cases, node_default, switch_end_addr)
|
|
1131
1169
|
return True
|
|
1132
1170
|
|
|
1133
|
-
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:
|
|
1134
1172
|
try:
|
|
1135
1173
|
last_stmt = self.cond_proc.get_last_statement(node)
|
|
1136
1174
|
except EmptyBlockNotice:
|
|
1137
1175
|
return False
|
|
1138
1176
|
|
|
1177
|
+
if last_stmt is None:
|
|
1178
|
+
return False
|
|
1179
|
+
|
|
1139
1180
|
successor_addrs = extract_jump_targets(last_stmt)
|
|
1140
1181
|
if len(successor_addrs) != 2:
|
|
1141
1182
|
return False
|
|
1142
1183
|
|
|
1143
1184
|
for t in successor_addrs:
|
|
1144
|
-
if t in jump_tables:
|
|
1185
|
+
if t in self.jump_tables:
|
|
1145
1186
|
# this is a candidate!
|
|
1146
1187
|
target = t
|
|
1147
1188
|
break
|
|
1148
1189
|
else:
|
|
1149
1190
|
return False
|
|
1150
1191
|
|
|
1151
|
-
jump_table = jump_tables[target]
|
|
1192
|
+
jump_table = self.jump_tables[target]
|
|
1152
1193
|
if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
|
|
1153
1194
|
return False
|
|
1154
1195
|
|
|
@@ -1168,6 +1209,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1168
1209
|
return False
|
|
1169
1210
|
|
|
1170
1211
|
# populate whitelist_edges
|
|
1212
|
+
assert jump_table.jumptable_entries is not None
|
|
1171
1213
|
for case_node_addr in jump_table.jumptable_entries:
|
|
1172
1214
|
self.whitelist_edges.add((node_a.addr, case_node_addr))
|
|
1173
1215
|
self.whitelist_edges.add((node.addr, node_b_addr))
|
|
@@ -1193,6 +1235,12 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1193
1235
|
if not r:
|
|
1194
1236
|
return False
|
|
1195
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
|
+
|
|
1196
1244
|
# un-structure IncompleteSwitchCaseNode
|
|
1197
1245
|
if isinstance(node_a, SequenceNode) and node_a.nodes and isinstance(node_a.nodes[0], IncompleteSwitchCaseNode):
|
|
1198
1246
|
_, new_seq_node = self._unpack_sequencenode_head(graph, node_a)
|
|
@@ -1222,7 +1270,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1222
1270
|
switch_end_addr = node_b_addr
|
|
1223
1271
|
else:
|
|
1224
1272
|
# we don't know what the end address of this switch-case structure is. let's figure it out
|
|
1225
|
-
switch_end_addr =
|
|
1273
|
+
switch_end_addr = self._switch_find_switch_end_addr(
|
|
1274
|
+
cases, node_default, {nn.addr for nn in self._region.graph}
|
|
1275
|
+
)
|
|
1226
1276
|
to_remove.add(node_default)
|
|
1227
1277
|
|
|
1228
1278
|
to_remove.add(node_a) # add node_a
|
|
@@ -1243,14 +1293,104 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1243
1293
|
|
|
1244
1294
|
# fully structured into a switch-case. remove node from switch_case_known_heads
|
|
1245
1295
|
self.switch_case_known_heads.remove(node)
|
|
1246
|
-
|
|
1296
|
+
if switch_end_addr is not None:
|
|
1297
|
+
self._switch_handle_gotos(cases, node_default, switch_end_addr)
|
|
1247
1298
|
|
|
1248
1299
|
return True
|
|
1249
1300
|
|
|
1250
|
-
def
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
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:
|
|
1254
1394
|
return False
|
|
1255
1395
|
|
|
1256
1396
|
try:
|
|
@@ -1260,7 +1400,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1260
1400
|
if not (isinstance(last_stmt, Jump) and not isinstance(last_stmt.target, Const)):
|
|
1261
1401
|
return False
|
|
1262
1402
|
|
|
1263
|
-
jump_table = jump_tables[node.addr]
|
|
1403
|
+
jump_table = self.jump_tables[node.addr]
|
|
1264
1404
|
if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
|
|
1265
1405
|
return False
|
|
1266
1406
|
|
|
@@ -1272,6 +1412,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1272
1412
|
case_count = cmp_ub - cmp_lb + 1
|
|
1273
1413
|
|
|
1274
1414
|
# ensure we have the same number of cases
|
|
1415
|
+
assert jump_table.jumptable_entries is not None
|
|
1275
1416
|
if case_count != len(jump_table.jumptable_entries):
|
|
1276
1417
|
return False
|
|
1277
1418
|
|
|
@@ -1311,7 +1452,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1311
1452
|
)
|
|
1312
1453
|
|
|
1313
1454
|
assert node_default is None
|
|
1314
|
-
switch_end_addr =
|
|
1455
|
+
switch_end_addr = self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
|
|
1315
1456
|
|
|
1316
1457
|
r = self._make_switch_cases_core(
|
|
1317
1458
|
node,
|
|
@@ -1330,14 +1471,15 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1330
1471
|
|
|
1331
1472
|
# fully structured into a switch-case. remove node from switch_case_known_heads
|
|
1332
1473
|
self.switch_case_known_heads.remove(node)
|
|
1333
|
-
|
|
1474
|
+
if switch_end_addr is not None:
|
|
1475
|
+
self._switch_handle_gotos(cases, node_default, switch_end_addr)
|
|
1334
1476
|
|
|
1335
1477
|
return True
|
|
1336
1478
|
|
|
1337
|
-
def _match_acyclic_switch_cases_address_computed(self, node, graph, full_graph
|
|
1338
|
-
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:
|
|
1339
1481
|
return False
|
|
1340
|
-
jump_table = jump_tables[node.addr]
|
|
1482
|
+
jump_table = self.jump_tables[node.addr]
|
|
1341
1483
|
if jump_table.type != IndirectJumpType.Jumptable_AddressComputed:
|
|
1342
1484
|
return False
|
|
1343
1485
|
|
|
@@ -1364,9 +1506,16 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1364
1506
|
|
|
1365
1507
|
if isinstance(last_stmt.false_target, Const):
|
|
1366
1508
|
default_addr = last_stmt.false_target.value
|
|
1509
|
+
assert isinstance(default_addr, int)
|
|
1367
1510
|
else:
|
|
1368
1511
|
return False
|
|
1369
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
|
+
|
|
1370
1519
|
case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
|
|
1371
1520
|
|
|
1372
1521
|
cases, node_default, to_remove = self._switch_build_cases(
|
|
@@ -1386,10 +1535,10 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1386
1535
|
)
|
|
1387
1536
|
|
|
1388
1537
|
def _match_acyclic_incomplete_switch_cases(
|
|
1389
|
-
self, node, graph: networkx.DiGraph, full_graph: networkx.DiGraph
|
|
1538
|
+
self, node, graph: networkx.DiGraph, full_graph: networkx.DiGraph
|
|
1390
1539
|
) -> bool:
|
|
1391
1540
|
# sanity checks
|
|
1392
|
-
if node.addr not in jump_tables:
|
|
1541
|
+
if node.addr not in self.jump_tables:
|
|
1393
1542
|
return False
|
|
1394
1543
|
if isinstance(node, IncompleteSwitchCaseNode):
|
|
1395
1544
|
return False
|
|
@@ -1398,9 +1547,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1398
1547
|
|
|
1399
1548
|
successors = list(graph.successors(node))
|
|
1400
1549
|
|
|
1550
|
+
jump_table = self.jump_tables[node.addr]
|
|
1551
|
+
assert jump_table.jumptable_entries is not None
|
|
1401
1552
|
if (
|
|
1402
1553
|
successors
|
|
1403
|
-
and {succ.addr for succ in successors} == set(
|
|
1554
|
+
and {succ.addr for succ in successors} == set(jump_table.jumptable_entries)
|
|
1404
1555
|
and all(graph.in_degree[succ] == 1 for succ in successors)
|
|
1405
1556
|
):
|
|
1406
1557
|
out_nodes = set()
|
|
@@ -1431,19 +1582,15 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1431
1582
|
graph,
|
|
1432
1583
|
full_graph,
|
|
1433
1584
|
) -> tuple[OrderedDict, Any, set[Any]]:
|
|
1434
|
-
cases: OrderedDict[int | tuple[int], SequenceNode] = OrderedDict()
|
|
1585
|
+
cases: OrderedDict[int | tuple[int, ...], SequenceNode] = OrderedDict()
|
|
1435
1586
|
to_remove = set()
|
|
1436
1587
|
|
|
1437
|
-
# it is possible that the default node gets duplicated by other analyses and creates a default node (addr.a)
|
|
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
|
|
1439
|
-
# successor to node_a
|
|
1440
1588
|
default_node_candidates = (
|
|
1441
1589
|
[nn for nn in graph.nodes if nn.addr == node_b_addr] if node_b_addr is not None else []
|
|
1442
1590
|
)
|
|
1443
|
-
node_default
|
|
1444
|
-
|
|
1591
|
+
node_default = (
|
|
1592
|
+
self._switch_find_default_node(graph, head_node, node_b_addr) if node_b_addr is not None else None
|
|
1445
1593
|
)
|
|
1446
|
-
|
|
1447
1594
|
if node_default is not None and not isinstance(node_default, SequenceNode):
|
|
1448
1595
|
# make the default node a SequenceNode so that we can insert Break and Continue nodes into it later
|
|
1449
1596
|
new_node = SequenceNode(node_default.addr, nodes=[node_default])
|
|
@@ -1634,6 +1781,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1634
1781
|
for _out_src, out_dst in out_edges[1:]:
|
|
1635
1782
|
if out_dst in full_graph and out_dst not in graph and full_graph.in_degree[out_dst] == 0:
|
|
1636
1783
|
full_graph.remove_node(out_dst)
|
|
1784
|
+
assert self._region.successors is not None
|
|
1637
1785
|
if out_dst in self._region.successors:
|
|
1638
1786
|
self._region.successors.remove(out_dst)
|
|
1639
1787
|
|
|
@@ -1666,8 +1814,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1666
1814
|
return case_and_entry_addrs
|
|
1667
1815
|
|
|
1668
1816
|
def _is_node_unstructured_switch_case_head(self, node) -> bool:
|
|
1669
|
-
|
|
1670
|
-
if node.addr in jump_tables:
|
|
1817
|
+
if node.addr in self.jump_tables:
|
|
1671
1818
|
# maybe it has been structured?
|
|
1672
1819
|
try:
|
|
1673
1820
|
last_stmts = self.cond_proc.get_last_statements(node)
|
|
@@ -1700,6 +1847,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1700
1847
|
and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, end_node)
|
|
1701
1848
|
and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, start_node)
|
|
1702
1849
|
and end_node not in self.dowhile_known_tail_nodes
|
|
1850
|
+
and not isinstance(end_node, IncompleteSwitchCaseNode)
|
|
1703
1851
|
):
|
|
1704
1852
|
# merge two blocks
|
|
1705
1853
|
new_seq = self._merge_nodes(start_node, end_node)
|
|
@@ -1719,6 +1867,8 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1719
1867
|
succs = list(full_graph.successors(start_node))
|
|
1720
1868
|
if len(succs) == 2:
|
|
1721
1869
|
left, right = succs
|
|
1870
|
+
if left.addr > right.addr:
|
|
1871
|
+
left, right = right, left
|
|
1722
1872
|
if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
|
|
1723
1873
|
full_graph, left
|
|
1724
1874
|
) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, right):
|
|
@@ -1747,7 +1897,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1747
1897
|
):
|
|
1748
1898
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
1749
1899
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
1750
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
1900
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
1751
1901
|
# c = !c
|
|
1752
1902
|
last_if_jump = self._remove_last_statement_if_jump(start_node)
|
|
1753
1903
|
new_cond_node = ConditionNode(
|
|
@@ -1789,7 +1939,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1789
1939
|
) and not self._is_node_unstructured_switch_case_head(right):
|
|
1790
1940
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
1791
1941
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
1792
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
1942
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
1793
1943
|
# c = !c
|
|
1794
1944
|
last_if_jump = self._remove_last_statement_if_jump(start_node)
|
|
1795
1945
|
new_cond_node = ConditionNode(
|
|
@@ -1822,7 +1972,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1822
1972
|
):
|
|
1823
1973
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
1824
1974
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
1825
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
1975
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
1826
1976
|
# c = !c
|
|
1827
1977
|
last_if_jump = self._remove_last_statement_if_jump(start_node)
|
|
1828
1978
|
new_cond_node = ConditionNode(
|
|
@@ -1857,7 +2007,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1857
2007
|
):
|
|
1858
2008
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
1859
2009
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
1860
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
2010
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
1861
2011
|
# c = !c
|
|
1862
2012
|
try:
|
|
1863
2013
|
last_stmt = self.cond_proc.get_last_statement(start_node)
|
|
@@ -1876,7 +2026,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
1876
2026
|
self._remove_last_statement_if_jump(start_node)
|
|
1877
2027
|
# add a goto node at the end
|
|
1878
2028
|
new_jump_node = Block(
|
|
1879
|
-
new_cond_node.addr,
|
|
2029
|
+
new_cond_node.addr if new_cond_node.addr is not None else 0x7EFF_FFFF,
|
|
1880
2030
|
0,
|
|
1881
2031
|
statements=[
|
|
1882
2032
|
Jump(
|
|
@@ -2162,7 +2312,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2162
2312
|
):
|
|
2163
2313
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2164
2314
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
2165
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
2315
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
2166
2316
|
# c0 = !c0
|
|
2167
2317
|
left_succs = list(full_graph.successors(left))
|
|
2168
2318
|
if len(left_succs) == 2 and right in left_succs:
|
|
@@ -2171,7 +2321,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2171
2321
|
# there must be an edge between right and other_succ
|
|
2172
2322
|
edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
|
|
2173
2323
|
edge_cond_left_other = self.cond_proc.recover_edge_condition(full_graph, left, other_succ)
|
|
2174
|
-
if claripy.is_true(
|
|
2324
|
+
if claripy.is_true(
|
|
2325
|
+
claripy.Not(edge_cond_left_right) == edge_cond_left_other # type: ignore
|
|
2326
|
+
):
|
|
2175
2327
|
# c1 = !c1
|
|
2176
2328
|
return left, edge_cond_left, right, edge_cond_left_right, other_succ
|
|
2177
2329
|
return None
|
|
@@ -2211,7 +2363,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2211
2363
|
):
|
|
2212
2364
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2213
2365
|
edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
|
|
2214
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
|
|
2366
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
|
|
2215
2367
|
# c0 = !c0
|
|
2216
2368
|
right_succs = list(full_graph.successors(right))
|
|
2217
2369
|
left_succs = list(full_graph.successors(left))
|
|
@@ -2220,7 +2372,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2220
2372
|
if len([succ for succ in left_succs if succ is not else_node]) == 1:
|
|
2221
2373
|
edge_cond_right_left = self.cond_proc.recover_edge_condition(full_graph, right, left)
|
|
2222
2374
|
edge_cond_right_else = self.cond_proc.recover_edge_condition(full_graph, right, else_node)
|
|
2223
|
-
if claripy.is_true(
|
|
2375
|
+
if claripy.is_true(
|
|
2376
|
+
claripy.Not(edge_cond_right_left) == edge_cond_right_else # type: ignore
|
|
2377
|
+
):
|
|
2224
2378
|
# c1 = !c1
|
|
2225
2379
|
return left, edge_cond_left, right, edge_cond_right_left, else_node
|
|
2226
2380
|
return None
|
|
@@ -2254,7 +2408,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2254
2408
|
):
|
|
2255
2409
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2256
2410
|
edge_cond_successor = self.cond_proc.recover_edge_condition(full_graph, start_node, successor)
|
|
2257
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_successor):
|
|
2411
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_successor): # type: ignore
|
|
2258
2412
|
# c0 = !c0
|
|
2259
2413
|
left_succs = list(full_graph.successors(left))
|
|
2260
2414
|
if len(left_succs) == 2 and successor in left_succs:
|
|
@@ -2265,7 +2419,9 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2265
2419
|
edge_cond_left_successor = self.cond_proc.recover_edge_condition(
|
|
2266
2420
|
full_graph, left, successor
|
|
2267
2421
|
)
|
|
2268
|
-
if claripy.is_true(
|
|
2422
|
+
if claripy.is_true(
|
|
2423
|
+
claripy.Not(edge_cond_left_right) == edge_cond_left_successor # type: ignore
|
|
2424
|
+
):
|
|
2269
2425
|
# c1 = !c1
|
|
2270
2426
|
return left, edge_cond_left, successor, edge_cond_left_successor, right
|
|
2271
2427
|
return None
|
|
@@ -2304,19 +2460,19 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2304
2460
|
):
|
|
2305
2461
|
edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
|
|
2306
2462
|
edge_cond_else = self.cond_proc.recover_edge_condition(full_graph, start_node, else_node)
|
|
2307
|
-
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_else):
|
|
2463
|
+
if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_else): # type: ignore
|
|
2308
2464
|
# c0 = !c0
|
|
2309
2465
|
left_succs = list(full_graph.successors(left))
|
|
2310
2466
|
if len(left_succs) == 2 and else_node in left_succs:
|
|
2311
2467
|
right = next(iter(succ for succ in left_succs if succ is not else_node))
|
|
2312
2468
|
edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
|
|
2313
2469
|
edge_cond_left_else = self.cond_proc.recover_edge_condition(full_graph, left, else_node)
|
|
2314
|
-
if claripy.is_true(claripy.Not(edge_cond_left_right) == edge_cond_left_else):
|
|
2470
|
+
if claripy.is_true(claripy.Not(edge_cond_left_right) == edge_cond_left_else): # type: ignore
|
|
2315
2471
|
# c1 = !c1
|
|
2316
2472
|
return left, edge_cond_left, right, edge_cond_left_right, else_node
|
|
2317
2473
|
return None
|
|
2318
2474
|
|
|
2319
|
-
def _last_resort_refinement(self, head, graph: networkx.DiGraph, full_graph: networkx.DiGraph
|
|
2475
|
+
def _last_resort_refinement(self, head, graph: networkx.DiGraph, full_graph: networkx.DiGraph) -> bool:
|
|
2320
2476
|
if self._improve_algorithm:
|
|
2321
2477
|
while self._edge_virtualization_hints:
|
|
2322
2478
|
src, dst = self._edge_virtualization_hints.pop(0)
|
|
@@ -2396,6 +2552,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2396
2552
|
goto1_target = None
|
|
2397
2553
|
|
|
2398
2554
|
if goto0_condition is not None:
|
|
2555
|
+
assert goto0_target is not None and goto1_target is not None
|
|
2399
2556
|
goto0 = Block(
|
|
2400
2557
|
last_stmt.ins_addr,
|
|
2401
2558
|
0,
|
|
@@ -2461,11 +2618,11 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2461
2618
|
|
|
2462
2619
|
@staticmethod
|
|
2463
2620
|
def _find_node_going_to_dst(
|
|
2464
|
-
node:
|
|
2621
|
+
node: BaseNode,
|
|
2465
2622
|
dst: Block | BaseNode,
|
|
2466
2623
|
last=True,
|
|
2467
2624
|
condjump_only=False,
|
|
2468
|
-
) -> tuple[int | None, BaseNode | None, Block | None]:
|
|
2625
|
+
) -> tuple[int | None, BaseNode | None, Block | MultiNode | BreakNode | None]:
|
|
2469
2626
|
"""
|
|
2470
2627
|
|
|
2471
2628
|
:param node:
|
|
@@ -2477,6 +2634,14 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2477
2634
|
dst_addr = dst.addr
|
|
2478
2635
|
dst_idx = dst.idx if isinstance(dst, Block) else ...
|
|
2479
2636
|
|
|
2637
|
+
class _Holder:
|
|
2638
|
+
"""
|
|
2639
|
+
Holds parent_and_block and is accessible from within the handlers.
|
|
2640
|
+
"""
|
|
2641
|
+
|
|
2642
|
+
parent_and_block: list[tuple[int, Any, Block | MultiNode | BreakNode]] = []
|
|
2643
|
+
block_id: int = -1
|
|
2644
|
+
|
|
2480
2645
|
def _check(last_stmt):
|
|
2481
2646
|
return (
|
|
2482
2647
|
(
|
|
@@ -2512,34 +2677,34 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2512
2677
|
first_stmt = first_nonlabel_nonphi_statement(block)
|
|
2513
2678
|
if first_stmt is not None:
|
|
2514
2679
|
# this block has content. increment the block ID counter
|
|
2515
|
-
|
|
2680
|
+
_Holder.block_id += 1
|
|
2516
2681
|
|
|
2517
2682
|
if _check(first_stmt):
|
|
2518
|
-
|
|
2683
|
+
_Holder.parent_and_block.append((_Holder.block_id, parent, block))
|
|
2519
2684
|
elif len(block.statements) > 1:
|
|
2520
2685
|
last_stmt = block.statements[-1]
|
|
2521
2686
|
if _check(last_stmt) or (
|
|
2522
2687
|
not isinstance(last_stmt, (Jump, ConditionalJump))
|
|
2523
2688
|
and block.addr + block.original_size == dst_addr
|
|
2524
2689
|
):
|
|
2525
|
-
|
|
2690
|
+
_Holder.parent_and_block.append((_Holder.block_id, parent, block))
|
|
2526
2691
|
|
|
2527
2692
|
def _handle_MultiNode(block: MultiNode, parent=None, **kwargs): # pylint:disable=unused-argument
|
|
2528
2693
|
if block.nodes and isinstance(block.nodes[-1], Block) and block.nodes[-1].statements:
|
|
2529
2694
|
first_stmt = first_nonlabel_nonphi_statement(block)
|
|
2530
2695
|
if first_stmt is not None:
|
|
2531
2696
|
# this block has content. increment the block ID counter
|
|
2532
|
-
|
|
2697
|
+
_Holder.block_id += 1
|
|
2533
2698
|
if _check(block.nodes[-1].statements[-1]):
|
|
2534
|
-
|
|
2699
|
+
_Holder.parent_and_block.append((_Holder.block_id, parent, block))
|
|
2535
2700
|
|
|
2536
2701
|
def _handle_BreakNode(break_node: BreakNode, parent=None, **kwargs): # pylint:disable=unused-argument
|
|
2537
|
-
|
|
2702
|
+
_Holder.block_id += 1
|
|
2538
2703
|
if break_node.target == dst_addr or (
|
|
2539
2704
|
isinstance(break_node.target, Const) and break_node.target.value == dst_addr
|
|
2540
2705
|
):
|
|
2541
2706
|
# FIXME: idx is ignored
|
|
2542
|
-
|
|
2707
|
+
_Holder.parent_and_block.append((_Holder.block_id, parent, break_node))
|
|
2543
2708
|
|
|
2544
2709
|
walker = SequenceWalker(
|
|
2545
2710
|
handlers={
|
|
@@ -2550,14 +2715,13 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2550
2715
|
update_seqnode_in_place=False,
|
|
2551
2716
|
force_forward_scan=True,
|
|
2552
2717
|
)
|
|
2553
|
-
|
|
2554
|
-
walker.block_id = -1
|
|
2718
|
+
_Holder.block_id = -1
|
|
2555
2719
|
walker.walk(node)
|
|
2556
|
-
if not
|
|
2720
|
+
if not _Holder.parent_and_block:
|
|
2557
2721
|
return None, None, None
|
|
2558
2722
|
if last:
|
|
2559
|
-
return
|
|
2560
|
-
return
|
|
2723
|
+
return _Holder.parent_and_block[-1]
|
|
2724
|
+
return _Holder.parent_and_block[0]
|
|
2561
2725
|
|
|
2562
2726
|
@staticmethod
|
|
2563
2727
|
def _unpack_sequencenode_head(graph: networkx.DiGraph, seq: SequenceNode, new_seq=None):
|
|
@@ -2626,7 +2790,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2626
2790
|
return True
|
|
2627
2791
|
return all(not isinstance(stmt, (ConditionalJump, Jump)) for stmt in stmts[:-1])
|
|
2628
2792
|
|
|
2629
|
-
def _to_statement_list(node: Block | MultiNode | SequenceNode) -> list[Statement]:
|
|
2793
|
+
def _to_statement_list(node: Block | MultiNode | SequenceNode | BaseNode) -> list[Statement]:
|
|
2630
2794
|
if isinstance(node, Block):
|
|
2631
2795
|
return node.statements
|
|
2632
2796
|
if isinstance(node, MultiNode):
|
|
@@ -2681,7 +2845,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2681
2845
|
graph.remove_edge(src, succ)
|
|
2682
2846
|
|
|
2683
2847
|
@staticmethod
|
|
2684
|
-
def _remove_first_statement_if_jump(node: BaseNode | Block) -> Jump | ConditionalJump | None:
|
|
2848
|
+
def _remove_first_statement_if_jump(node: BaseNode | Block | MultiNode) -> Jump | ConditionalJump | None:
|
|
2685
2849
|
if isinstance(node, Block):
|
|
2686
2850
|
if node.statements:
|
|
2687
2851
|
idx = 0
|
|
@@ -2729,7 +2893,7 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2729
2893
|
src, dst = edge_
|
|
2730
2894
|
dst_in_degree = graph.in_degree[dst]
|
|
2731
2895
|
src_out_degree = graph.out_degree[src]
|
|
2732
|
-
return -node_seq.get(dst), dst_in_degree, src_out_degree, -src.addr, -dst.addr
|
|
2896
|
+
return -node_seq.get(dst), dst_in_degree, src_out_degree, -src.addr, -dst.addr # type: ignore
|
|
2733
2897
|
|
|
2734
2898
|
return sorted(edges, key=_sort_edge, reverse=True)
|
|
2735
2899
|
|