angr 9.2.160__cp310-abi3-macosx_11_0_arm64.whl → 9.2.162__cp310-abi3-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.

Files changed (58) hide show
  1. angr/__init__.py +4 -1
  2. angr/analyses/analysis.py +0 -1
  3. angr/analyses/cfg/cfg_base.py +5 -1
  4. angr/analyses/decompiler/ail_simplifier.py +101 -2
  5. angr/analyses/decompiler/block_simplifier.py +13 -8
  6. angr/analyses/decompiler/clinic.py +1 -0
  7. angr/analyses/decompiler/condition_processor.py +24 -0
  8. angr/analyses/decompiler/counters/call_counter.py +11 -1
  9. angr/analyses/decompiler/decompiler.py +3 -1
  10. angr/analyses/decompiler/graph_region.py +11 -2
  11. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +1 -1
  12. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -0
  13. angr/analyses/decompiler/optimization_passes/optimization_pass.py +31 -11
  14. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +2 -0
  15. angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
  16. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +53 -0
  17. angr/analyses/decompiler/peephole_optimizations/modulo_simplifier.py +89 -0
  18. angr/analyses/decompiler/peephole_optimizations/{const_mull_a_shift.py → optimized_div_simplifier.py} +139 -25
  19. angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +18 -9
  20. angr/analyses/decompiler/region_simplifiers/goto.py +3 -3
  21. angr/analyses/decompiler/region_simplifiers/if_.py +2 -2
  22. angr/analyses/decompiler/region_simplifiers/loop.py +2 -2
  23. angr/analyses/decompiler/structured_codegen/c.py +3 -3
  24. angr/analyses/decompiler/structuring/dream.py +1 -1
  25. angr/analyses/decompiler/structuring/phoenix.py +138 -99
  26. angr/analyses/decompiler/structuring/recursive_structurer.py +3 -2
  27. angr/analyses/decompiler/structuring/sailr.py +51 -43
  28. angr/analyses/decompiler/structuring/structurer_base.py +2 -3
  29. angr/analyses/deobfuscator/string_obf_opt_passes.py +1 -1
  30. angr/analyses/disassembly.py +1 -1
  31. angr/analyses/reaching_definitions/function_handler.py +1 -0
  32. angr/analyses/s_propagator.py +2 -2
  33. angr/analyses/s_reaching_definitions/s_rda_model.py +1 -0
  34. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +5 -2
  35. angr/analyses/variable_recovery/engine_base.py +17 -1
  36. angr/analyses/variable_recovery/variable_recovery_base.py +30 -2
  37. angr/analyses/variable_recovery/variable_recovery_fast.py +11 -2
  38. angr/emulator.py +143 -0
  39. angr/engines/concrete.py +66 -0
  40. angr/engines/icicle.py +66 -30
  41. angr/exploration_techniques/driller_core.py +2 -2
  42. angr/knowledge_plugins/functions/function.py +1 -1
  43. angr/knowledge_plugins/functions/function_manager.py +1 -2
  44. angr/project.py +7 -0
  45. angr/rustylib.abi3.so +0 -0
  46. angr/sim_type.py +16 -8
  47. angr/simos/javavm.py +1 -1
  48. angr/unicornlib.dylib +0 -0
  49. angr/utils/graph.py +48 -13
  50. angr/utils/library.py +13 -12
  51. angr/utils/ssa/__init__.py +57 -5
  52. {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/METADATA +5 -5
  53. {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/RECORD +57 -55
  54. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +0 -57
  55. {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/WHEEL +0 -0
  56. {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/entry_points.txt +0 -0
  57. {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/licenses/LICENSE +0 -0
  58. {angr-9.2.160.dist-info → angr-9.2.162.dist-info}/top_level.txt +0 -0
@@ -11,7 +11,7 @@ import networkx
11
11
  import claripy
12
12
  from angr.ailment.block import Block
13
13
  from angr.ailment.statement import Statement, ConditionalJump, Jump, Label, Return
14
- from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression
14
+ from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression, BinaryOp
15
15
 
16
16
  from angr.utils.graph import GraphUtils
17
17
  from angr.utils.ail import is_phi_assignment, is_head_controlled_loop_block
@@ -126,6 +126,12 @@ class PhoenixStructurer(StructurerBase):
126
126
  self._improve_algorithm = improve_algorithm
127
127
  self._edge_virtualization_hints = []
128
128
 
129
+ # node_order keeps a dictionary of nodes and their order in a quasi-topological sort of the region full graph
130
+ # (graph_with_successors). _generate_node_order() initializes this dictionary. we then update this dictionary
131
+ # when new nodes are created. we do not populate this dictionary when working on acyclic graphs because it's
132
+ # not used for acyclic graphs.
133
+ self._node_order: dict[Any, int] | None = None
134
+
129
135
  self._use_multistmtexprs = use_multistmtexprs
130
136
  self._multistmtexpr_stmt_threshold = multistmtexpr_stmt_threshold
131
137
  self._analyze()
@@ -221,8 +227,11 @@ class PhoenixStructurer(StructurerBase):
221
227
 
222
228
  def _analyze_cyclic(self) -> bool:
223
229
  any_matches = False
224
- acyclic_graph = to_acyclic_graph(self._region.graph, loop_heads=[self._region.head])
225
- for node in list(reversed(GraphUtils.quasi_topological_sort_nodes(acyclic_graph))):
230
+
231
+ if self._node_order is None:
232
+ self._generate_node_order()
233
+ acyclic_graph = to_acyclic_graph(self._region.graph, node_order=self._node_order)
234
+ for node in list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, self._region.head)):
226
235
  if node not in self._region.graph:
227
236
  continue
228
237
  matched = self._match_cyclic_schemas(
@@ -323,10 +332,9 @@ class PhoenixStructurer(StructurerBase):
323
332
  # it's a while loop if the conditional jump (or the head block) is at the beginning of node
324
333
  loop_type = "while" if head_block_idx == 0 else "do-while"
325
334
  # otherwise it's a do-while loop
326
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
327
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
328
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
335
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, head_block, left, right):
329
336
  # c = !c
337
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
330
338
  if head_block_idx == 0:
331
339
  self._remove_first_statement_if_jump(head_block)
332
340
  else:
@@ -351,13 +359,12 @@ class PhoenixStructurer(StructurerBase):
351
359
  # possible candidate
352
360
  _, _, head_block = self._find_node_going_to_dst(node, left, condjump_only=True)
353
361
  if head_block is not None:
354
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
355
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
356
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
362
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, head_block, left, right):
357
363
  # c = !c
358
364
  if PhoenixStructurer._is_single_statement_block(node):
359
365
  # the single-statement-block check is to ensure we don't execute any code before the
360
366
  # conditional jump. this way the entire node can be dropped.
367
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
361
368
  new_node = SequenceNode(node.addr, nodes=[left])
362
369
  loop_node = LoopNode("while", edge_cond_left, new_node, addr=node.addr)
363
370
 
@@ -372,6 +379,7 @@ class PhoenixStructurer(StructurerBase):
372
379
 
373
380
  return True, loop_node, right
374
381
  # we generate a while-true loop instead
382
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
375
383
  last_stmt = self._remove_last_statement_if_jump(head_block)
376
384
  assert last_stmt is not None
377
385
  cond_jump = Jump(
@@ -400,10 +408,9 @@ class PhoenixStructurer(StructurerBase):
400
408
  # while (true) { ...; if (...) break; }
401
409
  _, _, head_block = self._find_node_going_to_dst(node, left, condjump_only=True)
402
410
  if head_block is not None:
403
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
404
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
405
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
411
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, head_block, left, right):
406
412
  # c = !c
413
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
407
414
  self._remove_last_statement_if_jump(head_block)
408
415
  cond_break = ConditionalBreakNode(node.addr, edge_cond_right, right.addr)
409
416
  new_node = SequenceNode(node.addr, nodes=[node, cond_break, left])
@@ -501,6 +508,10 @@ class PhoenixStructurer(StructurerBase):
501
508
  self.replace_nodes(full_graph, node, loop_node, self_loop=False)
502
509
  full_graph.add_edge(loop_node, successor_node)
503
510
 
511
+ if self._node_order is not None:
512
+ self._node_order[loop_node] = self._node_order[node]
513
+ self._node_order[successor_node] = self._node_order[loop_node]
514
+
504
515
  return True, loop_node, successor_node
505
516
 
506
517
  def _cyclic_while_with_single_successor_must_return(self, successor_node: SequenceNode) -> bool:
@@ -527,10 +538,9 @@ class PhoenixStructurer(StructurerBase):
527
538
  # possible candidate
528
539
  _, _, succ_block = self._find_node_going_to_dst(succ, out_node, condjump_only=True)
529
540
  if succ_block is not None:
530
- edge_cond_succhead = self.cond_proc.recover_edge_condition(full_graph, succ_block, node)
531
- edge_cond_succout = self.cond_proc.recover_edge_condition(full_graph, succ_block, out_node)
532
- if claripy.is_true(claripy.Not(edge_cond_succhead) == edge_cond_succout): # type: ignore
541
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, succ_block, node, out_node):
533
542
  # c = !c
543
+ edge_cond_succhead = self.cond_proc.recover_edge_condition(full_graph, succ_block, node)
534
544
  self._remove_last_statement_if_jump(succ)
535
545
  drop_succ = False
536
546
 
@@ -570,10 +580,9 @@ class PhoenixStructurer(StructurerBase):
570
580
  succ = succs[0]
571
581
  if not full_graph.has_edge(succ, node):
572
582
  # possible candidate
573
- edge_cond_head = self.cond_proc.recover_edge_condition(full_graph, node, node)
574
- edge_cond_head_succ = self.cond_proc.recover_edge_condition(full_graph, node, succ)
575
- if claripy.is_true(claripy.Not(edge_cond_head) == edge_cond_head_succ): # type: ignore
583
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, node, node, succ):
576
584
  # c = !c
585
+ edge_cond_head = self.cond_proc.recover_edge_condition(full_graph, node, node)
577
586
  self._remove_last_statement_if_jump(node)
578
587
  seq_node = SequenceNode(node.addr, nodes=[node]) if not isinstance(node, SequenceNode) else node
579
588
  loop_node = LoopNode("do-while", edge_cond_head, seq_node, addr=seq_node.addr)
@@ -1039,7 +1048,7 @@ class PhoenixStructurer(StructurerBase):
1039
1048
 
1040
1049
  self._assert_graph_ok(acyclic_graph, "Removed wrong edges")
1041
1050
 
1042
- for node in list(reversed(GraphUtils.quasi_topological_sort_nodes(acyclic_graph))):
1051
+ for node in list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, head)):
1043
1052
  if node not in graph:
1044
1053
  continue
1045
1054
  if graph.has_edge(node, head):
@@ -1145,6 +1154,8 @@ class PhoenixStructurer(StructurerBase):
1145
1154
  node_default = Block(SWITCH_MISSING_DEFAULT_NODE_ADDR, 0, statements=[jmp_to_default_node])
1146
1155
  graph.add_edge(node, node_default)
1147
1156
  full_graph.add_edge(node, node_default)
1157
+ if self._node_order is not None:
1158
+ self._node_order[node_default] = self._node_order[node]
1148
1159
  r = self._make_switch_cases_core(
1149
1160
  node,
1150
1161
  self.cond_proc.claripy_ast_from_ail_condition(last_stmt.switch_variable),
@@ -1250,6 +1261,8 @@ class PhoenixStructurer(StructurerBase):
1250
1261
  # un-structure IncompleteSwitchCaseNode
1251
1262
  if isinstance(node_a, SequenceNode) and node_a.nodes and isinstance(node_a.nodes[0], IncompleteSwitchCaseNode):
1252
1263
  _, new_seq_node = self._unpack_sequencenode_head(graph, node_a)
1264
+ if new_seq_node is not None and self._node_order is not None:
1265
+ self._node_order[new_seq_node] = self._node_order[node_a]
1253
1266
  self._unpack_sequencenode_head(full_graph, node_a, new_seq=new_seq_node)
1254
1267
  # update node_a
1255
1268
  node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
@@ -1260,6 +1273,8 @@ class PhoenixStructurer(StructurerBase):
1260
1273
  self._unpack_incompleteswitchcasenode(full_graph, node_a) # this shall not fail
1261
1274
  # update node_a
1262
1275
  node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
1276
+ if self._node_order is not None:
1277
+ self._generate_node_order()
1263
1278
 
1264
1279
  better_node_a = node_a
1265
1280
  if isinstance(node_a, SequenceNode) and is_empty_or_label_only_node(node_a.nodes[0]) and len(node_a.nodes) == 2:
@@ -1604,6 +1619,8 @@ class PhoenixStructurer(StructurerBase):
1604
1619
  self.replace_nodes(full_graph, node, new_node)
1605
1620
  if out_nodes:
1606
1621
  full_graph.add_edge(new_node, out_nodes[0])
1622
+ if self._node_order:
1623
+ self._node_order[new_node] = self._node_order[node]
1607
1624
  return True
1608
1625
  return False
1609
1626
 
@@ -1785,6 +1802,8 @@ class PhoenixStructurer(StructurerBase):
1785
1802
 
1786
1803
  graph.add_edge(head, scnode)
1787
1804
  full_graph.add_edge(head, scnode)
1805
+ if self._node_order is not None:
1806
+ self._node_order[scnode] = self._node_order[head]
1788
1807
 
1789
1808
  if out_edges:
1790
1809
  # for all out edges going to head, we ensure there is a goto at the end of each corresponding case node
@@ -1934,10 +1953,9 @@ class PhoenixStructurer(StructurerBase):
1934
1953
  and not self._is_node_unstructured_switch_case_head(left)
1935
1954
  and not self._is_node_unstructured_switch_case_head(right)
1936
1955
  ):
1937
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1938
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1939
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
1956
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
1940
1957
  # c = !c
1958
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1941
1959
  last_if_jump = self._remove_last_statement_if_jump(start_node)
1942
1960
  new_cond_node = ConditionNode(
1943
1961
  last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
@@ -1976,10 +1994,9 @@ class PhoenixStructurer(StructurerBase):
1976
1994
  if not self._is_node_unstructured_switch_case_head(
1977
1995
  left
1978
1996
  ) and not self._is_node_unstructured_switch_case_head(right):
1979
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1980
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1981
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
1997
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
1982
1998
  # c = !c
1999
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1983
2000
  last_if_jump = self._remove_last_statement_if_jump(start_node)
1984
2001
  new_cond_node = ConditionNode(
1985
2002
  last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
@@ -2009,10 +2026,9 @@ class PhoenixStructurer(StructurerBase):
2009
2026
  and full_graph.in_degree[left] == 1
2010
2027
  and full_graph.in_degree[right] >= 2
2011
2028
  ):
2012
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2013
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2014
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2029
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2015
2030
  # c = !c
2031
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2016
2032
  last_if_jump = self._remove_last_statement_if_jump(start_node)
2017
2033
  new_cond_node = ConditionNode(
2018
2034
  last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
@@ -2044,10 +2060,9 @@ class PhoenixStructurer(StructurerBase):
2044
2060
  or (full_graph.in_degree[right] == 1 and not left_succs)
2045
2061
  )
2046
2062
  ):
2047
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2048
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2049
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2063
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2050
2064
  # c = !c
2065
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2051
2066
  try:
2052
2067
  last_stmt = self.cond_proc.get_last_statement(start_node)
2053
2068
  except EmptyBlockNotice:
@@ -2169,25 +2184,39 @@ class PhoenixStructurer(StructurerBase):
2169
2184
  #
2170
2185
  # We reduce it into if (cond && next_cond) { body } else { else }
2171
2186
 
2187
+ # fast-path check to reject nodes that definitely do not work
2188
+ if full_graph.out_degree[start_node] != 2:
2189
+ return False
2190
+ next_cond_candidates = list(full_graph.successors(start_node))
2191
+ check_passed = False
2192
+ for next_cond in next_cond_candidates:
2193
+ if full_graph.out_degree[next_cond] != 2:
2194
+ continue
2195
+ for next_cond_succ in full_graph.successors(next_cond):
2196
+ if full_graph.has_edge(start_node, next_cond_succ):
2197
+ check_passed = True
2198
+ break
2199
+ if check_passed:
2200
+ break
2201
+ if not check_passed:
2202
+ return False
2203
+
2172
2204
  r = self._match_acyclic_short_circuit_conditions_type_a(graph, full_graph, start_node)
2173
2205
 
2174
2206
  if r is not None:
2175
2207
  left, left_cond, right, left_right_cond, succ = r
2176
2208
  # create the condition node
2177
- memo = {}
2209
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2210
+ left_cond_expr_neg = UnaryOp(None, "Not", left_cond_expr, ins_addr=start_node.addr)
2211
+ left_right_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_right_cond)
2178
2212
  if not self._is_single_statement_block(left):
2179
2213
  if not self._should_use_multistmtexprs(left):
2180
2214
  return False
2181
2215
  # create a MultiStatementExpression for left_right_cond
2182
2216
  stmts = self._build_multistatementexpr_statements(left)
2183
2217
  assert stmts is not None
2184
- mstmt_expr = MultiStatementExpression(
2185
- None, stmts, self.cond_proc.convert_claripy_bool_ast(left_right_cond), ins_addr=left.addr
2186
- )
2187
- memo[left_right_cond._hash] = mstmt_expr
2188
- cond = self.cond_proc.convert_claripy_bool_ast(
2189
- claripy.Or(claripy.Not(left_cond), left_right_cond), memo=memo
2190
- )
2218
+ left_right_cond_expr = MultiStatementExpression(None, stmts, left_right_cond_expr, ins_addr=left.addr)
2219
+ cond = BinaryOp(None, "LogicalOr", [left_cond_expr_neg, left_right_cond_expr], ins_addr=start_node.addr)
2191
2220
  cond_jump = ConditionalJump(
2192
2221
  None,
2193
2222
  cond,
@@ -2212,18 +2241,16 @@ class PhoenixStructurer(StructurerBase):
2212
2241
  if r is not None:
2213
2242
  left, left_cond, right, right_left_cond, else_node = r
2214
2243
  # create the condition node
2215
- memo = {}
2244
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2245
+ right_left_cond_expr = self.cond_proc.convert_claripy_bool_ast(right_left_cond)
2216
2246
  if not self._is_single_statement_block(right):
2217
2247
  if not self._should_use_multistmtexprs(right):
2218
2248
  return False
2219
2249
  # create a MultiStatementExpression for left_right_cond
2220
2250
  stmts = self._build_multistatementexpr_statements(right)
2221
2251
  assert stmts is not None
2222
- mstmt_expr = MultiStatementExpression(
2223
- None, stmts, self.cond_proc.convert_claripy_bool_ast(right_left_cond), ins_addr=left.addr
2224
- )
2225
- memo[right_left_cond._hash] = mstmt_expr
2226
- cond = self.cond_proc.convert_claripy_bool_ast(claripy.Or(left_cond, right_left_cond), memo=memo)
2252
+ right_left_cond_expr = MultiStatementExpression(None, stmts, right_left_cond_expr, ins_addr=left.addr)
2253
+ cond = BinaryOp(None, "LogicalOr", [left_cond_expr, right_left_cond_expr], ins_addr=start_node.addr)
2227
2254
  cond_jump = ConditionalJump(
2228
2255
  None,
2229
2256
  cond,
@@ -2248,20 +2275,17 @@ class PhoenixStructurer(StructurerBase):
2248
2275
  if r is not None:
2249
2276
  left, left_cond, succ, left_succ_cond, right = r
2250
2277
  # create the condition node
2251
- memo = {}
2278
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2279
+ left_succ_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_succ_cond)
2252
2280
  if not self._is_single_statement_block(left):
2253
2281
  if not self._should_use_multistmtexprs(left):
2254
2282
  return False
2255
2283
  # create a MultiStatementExpression for left_right_cond
2256
2284
  stmts = self._build_multistatementexpr_statements(left)
2257
2285
  assert stmts is not None
2258
- mstmt_expr = MultiStatementExpression(
2259
- None, stmts, self.cond_proc.convert_claripy_bool_ast(left_succ_cond), ins_addr=left.addr
2260
- )
2261
- memo[left_succ_cond._hash] = mstmt_expr
2262
- cond = self.cond_proc.convert_claripy_bool_ast(
2263
- claripy.And(left_cond, claripy.Not(left_succ_cond)), memo=memo
2264
- )
2286
+ left_succ_cond_expr = MultiStatementExpression(None, stmts, left_succ_cond_expr, ins_addr=left.addr)
2287
+ left_succ_cond_expr_neg = UnaryOp(None, "Not", left_succ_cond_expr, ins_addr=start_node.addr)
2288
+ cond = BinaryOp(None, "LogicalAnd", [left_cond_expr, left_succ_cond_expr_neg], ins_addr=start_node.addr)
2265
2289
  cond_jump = ConditionalJump(
2266
2290
  None,
2267
2291
  cond,
@@ -2285,21 +2309,16 @@ class PhoenixStructurer(StructurerBase):
2285
2309
  if r is not None:
2286
2310
  left, left_cond, right, right_left_cond, else_node = r
2287
2311
  # create the condition node
2288
- memo = {}
2312
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2313
+ left_right_cond_expr = self.cond_proc.convert_claripy_bool_ast(right_left_cond)
2289
2314
  if not self._is_single_statement_block(left):
2290
2315
  if not self._should_use_multistmtexprs(left):
2291
2316
  return False
2292
2317
  # create a MultiStatementExpression for left_right_cond
2293
2318
  stmts = self._build_multistatementexpr_statements(left)
2294
2319
  assert stmts is not None
2295
- mstmt_expr = MultiStatementExpression(
2296
- None, stmts, self.cond_proc.convert_claripy_bool_ast(right_left_cond), ins_addr=left.addr
2297
- )
2298
- memo[right_left_cond._hash] = mstmt_expr
2299
- cond = self.cond_proc.convert_claripy_bool_ast(
2300
- claripy.And(left_cond, right_left_cond),
2301
- memo=memo,
2302
- )
2320
+ left_right_cond_expr = MultiStatementExpression(None, stmts, left_right_cond_expr, ins_addr=left.addr)
2321
+ cond = BinaryOp(None, "LogicalAnd", [left_cond_expr, left_right_cond_expr], ins_addr=start_node.addr)
2303
2322
  cond_jump = ConditionalJump(
2304
2323
  None,
2305
2324
  cond,
@@ -2349,21 +2368,17 @@ class PhoenixStructurer(StructurerBase):
2349
2368
  and full_graph.in_degree[left] == 1
2350
2369
  and full_graph.in_degree[right] >= 1
2351
2370
  ):
2352
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2353
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2354
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2371
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2355
2372
  # c0 = !c0
2356
2373
  left_succs = list(full_graph.successors(left))
2357
2374
  if len(left_succs) == 2 and right in left_succs:
2358
2375
  other_succ = next(iter(succ for succ in left_succs if succ is not right))
2359
2376
  if full_graph.out_degree[right] == 1 and full_graph.has_edge(right, other_succ):
2360
2377
  # there must be an edge between right and other_succ
2361
- edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2362
- edge_cond_left_other = self.cond_proc.recover_edge_condition(full_graph, left, other_succ)
2363
- if claripy.is_true(
2364
- claripy.Not(edge_cond_left_right) == edge_cond_left_other # type: ignore
2365
- ):
2378
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, other_succ):
2366
2379
  # c1 = !c1
2380
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2381
+ edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2367
2382
  return left, edge_cond_left, right, edge_cond_left_right, other_succ
2368
2383
  return None
2369
2384
 
@@ -2386,7 +2401,7 @@ class PhoenixStructurer(StructurerBase):
2386
2401
  if len(succs) == 2:
2387
2402
  left, right = succs
2388
2403
 
2389
- if full_graph.in_degree[left] == 1 and full_graph.in_degree[right] == 2:
2404
+ if full_graph.in_degree[left] == 1 and full_graph.in_degree[right] >= 2:
2390
2405
  left, right = right, left
2391
2406
 
2392
2407
  # ensure left and right nodes are not the head of a switch-case construct
@@ -2397,24 +2412,20 @@ class PhoenixStructurer(StructurerBase):
2397
2412
 
2398
2413
  if (
2399
2414
  self._is_sequential_statement_block(right)
2400
- and full_graph.in_degree[left] == 2
2415
+ and full_graph.in_degree[left] >= 2
2401
2416
  and full_graph.in_degree[right] == 1
2402
2417
  ):
2403
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2404
- edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2405
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2418
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2406
2419
  # c0 = !c0
2407
2420
  right_succs = list(full_graph.successors(right))
2408
2421
  left_succs = list(full_graph.successors(left))
2409
2422
  if len(right_succs) == 2 and left in right_succs:
2410
2423
  else_node = next(iter(succ for succ in right_succs if succ is not left))
2411
2424
  if len([succ for succ in left_succs if succ is not else_node]) == 1:
2412
- edge_cond_right_left = self.cond_proc.recover_edge_condition(full_graph, right, left)
2413
- edge_cond_right_else = self.cond_proc.recover_edge_condition(full_graph, right, else_node)
2414
- if claripy.is_true(
2415
- claripy.Not(edge_cond_right_left) == edge_cond_right_else # type: ignore
2416
- ):
2425
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, right, left, else_node):
2417
2426
  # c1 = !c1
2427
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2428
+ edge_cond_right_left = self.cond_proc.recover_edge_condition(full_graph, right, left)
2418
2429
  return left, edge_cond_left, right, edge_cond_right_left, else_node
2419
2430
  return None
2420
2431
 
@@ -2445,23 +2456,19 @@ class PhoenixStructurer(StructurerBase):
2445
2456
  and full_graph.in_degree[left] == 1
2446
2457
  and full_graph.in_degree[successor] >= 1
2447
2458
  ):
2448
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2449
- edge_cond_successor = self.cond_proc.recover_edge_condition(full_graph, start_node, successor)
2450
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_successor): # type: ignore
2459
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, successor):
2451
2460
  # c0 = !c0
2452
2461
  left_succs = list(full_graph.successors(left))
2453
2462
  if len(left_succs) == 2 and successor in left_succs:
2454
2463
  right = next(iter(succ for succ in left_succs if succ is not successor))
2455
2464
  if full_graph.out_degree[right] == 1 and full_graph.has_edge(right, successor):
2456
2465
  # there must be an edge from right to successor
2457
- edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2458
- edge_cond_left_successor = self.cond_proc.recover_edge_condition(
2459
- full_graph, left, successor
2460
- )
2461
- if claripy.is_true(
2462
- claripy.Not(edge_cond_left_right) == edge_cond_left_successor # type: ignore
2463
- ):
2466
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, successor):
2464
2467
  # c1 = !c1
2468
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2469
+ edge_cond_left_successor = self.cond_proc.recover_edge_condition(
2470
+ full_graph, left, successor
2471
+ )
2465
2472
  return left, edge_cond_left, successor, edge_cond_left_successor, right
2466
2473
  return None
2467
2474
 
@@ -2497,17 +2504,15 @@ class PhoenixStructurer(StructurerBase):
2497
2504
  and full_graph.in_degree[left] == 1
2498
2505
  and full_graph.in_degree[else_node] >= 1
2499
2506
  ):
2500
- edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2501
- edge_cond_else = self.cond_proc.recover_edge_condition(full_graph, start_node, else_node)
2502
- if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_else): # type: ignore
2507
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, else_node):
2503
2508
  # c0 = !c0
2504
2509
  left_succs = list(full_graph.successors(left))
2505
2510
  if len(left_succs) == 2 and else_node in left_succs:
2506
2511
  right = next(iter(succ for succ in left_succs if succ is not else_node))
2507
- edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2508
- edge_cond_left_else = self.cond_proc.recover_edge_condition(full_graph, left, else_node)
2509
- if claripy.is_true(claripy.Not(edge_cond_left_right) == edge_cond_left_else): # type: ignore
2512
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, else_node):
2510
2513
  # c1 = !c1
2514
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2515
+ edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2511
2516
  return left, edge_cond_left, right, edge_cond_left_right, else_node
2512
2517
  return None
2513
2518
 
@@ -2529,7 +2534,7 @@ class PhoenixStructurer(StructurerBase):
2529
2534
  if networkx.is_directed_acyclic_graph(full_graph):
2530
2535
  acyclic_graph = full_graph
2531
2536
  else:
2532
- acyclic_graph = to_acyclic_graph(full_graph, loop_heads=[head])
2537
+ acyclic_graph = to_acyclic_graph(full_graph, node_order=self._node_order)
2533
2538
  for src, dst in acyclic_graph.edges:
2534
2539
  if src is dst:
2535
2540
  continue
@@ -2545,7 +2550,20 @@ class PhoenixStructurer(StructurerBase):
2545
2550
  if (src.addr, dst.addr) not in self.whitelist_edges:
2546
2551
  other_edges.append((src, dst))
2547
2552
 
2548
- ordered_nodes = GraphUtils.quasi_topological_sort_nodes(acyclic_graph, loop_heads=[head])
2553
+ # acyclic graph may contain more than one entry node, so we may add a temporary head node to ensure all nodes
2554
+ # are accounted for in node_seq
2555
+ graph_entries = [nn for nn in acyclic_graph if acyclic_graph.in_degree[nn] == 0]
2556
+ postorder_head = head
2557
+ if len(graph_entries) > 1:
2558
+ postorder_head = Block(0, 0)
2559
+ for nn in graph_entries:
2560
+ acyclic_graph.add_edge(postorder_head, nn)
2561
+ ordered_nodes = list(
2562
+ reversed(list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, postorder_head)))
2563
+ )
2564
+ if len(graph_entries) > 1:
2565
+ ordered_nodes.remove(postorder_head)
2566
+ acyclic_graph.remove_node(postorder_head)
2549
2567
  node_seq = {nn: (len(ordered_nodes) - idx) for (idx, nn) in enumerate(ordered_nodes)} # post-order
2550
2568
 
2551
2569
  if all_edges_wo_dominance:
@@ -2625,6 +2643,8 @@ class PhoenixStructurer(StructurerBase):
2625
2643
  self.virtualized_edges.add((src, dst))
2626
2644
  if new_src is not None:
2627
2645
  self.replace_nodes(graph, src, new_src)
2646
+ if self._node_order is not None:
2647
+ self._node_order[new_src] = self._node_order[src]
2628
2648
  if full_graph is not None:
2629
2649
  self.virtualized_edges.add((src, dst))
2630
2650
  full_graph.remove_edge(src, dst)
@@ -2934,10 +2954,29 @@ class PhoenixStructurer(StructurerBase):
2934
2954
  src, dst = edge_
2935
2955
  dst_in_degree = graph.in_degree[dst]
2936
2956
  src_out_degree = graph.out_degree[src]
2937
- return -node_seq.get(dst), dst_in_degree, src_out_degree, -src.addr, -dst.addr # type: ignore
2957
+ return -node_seq[dst], dst_in_degree, src_out_degree, -src.addr, -dst.addr # type: ignore
2938
2958
 
2939
2959
  return sorted(edges, key=_sort_edge, reverse=True)
2940
2960
 
2961
+ def _generate_node_order(self):
2962
+ the_graph = (
2963
+ self._region.graph_with_successors if self._region.graph_with_successors is not None else self._region.graph
2964
+ )
2965
+ the_head = self._region.head
2966
+ ordered_nodes = GraphUtils.quasi_topological_sort_nodes(
2967
+ the_graph,
2968
+ loop_heads=[the_head],
2969
+ )
2970
+ self._node_order = {n: i for i, n in enumerate(ordered_nodes)}
2971
+
2972
+ def replace_nodes(self, graph, old_node_0, new_node, old_node_1=None, self_loop=True):
2973
+ super().replace_nodes(graph, old_node_0, new_node, old_node_1=old_node_1, self_loop=self_loop)
2974
+ if self._node_order is not None and graph is self._region.graph_with_successors:
2975
+ if old_node_1 is not None:
2976
+ self._node_order[new_node] = min(self._node_order[old_node_0], self._node_order[old_node_1])
2977
+ else:
2978
+ self._node_order[new_node] = self._node_order[old_node_0]
2979
+
2941
2980
  @staticmethod
2942
2981
  def _replace_node_in_edge_list(edge_list: list[tuple], old_node, new_node) -> None:
2943
2982
  for idx in range(len(edge_list)): # pylint:disable=consider-using-enumerate
@@ -43,7 +43,7 @@ class RecursiveStructurer(Analysis):
43
43
  self.structurer_cls = structurer_cls if structurer_cls is not None else DreamStructurer
44
44
  self.structurer_options = kwargs
45
45
 
46
- self.result = None
46
+ self.result: BaseNode | None = None
47
47
  self.result_incomplete: bool = False
48
48
 
49
49
  self._analyze()
@@ -161,6 +161,7 @@ class RecursiveStructurer(Analysis):
161
161
  for jump_table_head_addr, jumptable in jump_tables.items():
162
162
  if jump_table_head_addr not in func_block_addrs:
163
163
  continue
164
+ assert jumptable.jumptable_entries is not None
164
165
  for entry_addr in jumptable.jumptable_entries:
165
166
  entries[entry_addr] = jump_table_head_addr
166
167
 
@@ -178,7 +179,7 @@ class RecursiveStructurer(Analysis):
178
179
  continue
179
180
  if node.addr == self.function.addr:
180
181
  return node
181
- if min_node is None or min_node.addr < node.addr:
182
+ if min_node is None or (min_node.addr is not None and node.addr is not None and min_node.addr < node.addr):
182
183
  min_node = node
183
184
 
184
185
  return min_node