angr 9.2.135__py3-none-manylinux2014_x86_64.whl → 9.2.137__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.

Files changed (198) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/__init__.py +3 -7
  3. angr/analyses/analysis.py +4 -0
  4. angr/analyses/backward_slice.py +1 -2
  5. angr/analyses/binary_optimizer.py +3 -4
  6. angr/analyses/bindiff.py +4 -6
  7. angr/analyses/boyscout.py +1 -3
  8. angr/analyses/callee_cleanup_finder.py +4 -4
  9. angr/analyses/calling_convention/calling_convention.py +6 -4
  10. angr/analyses/calling_convention/fact_collector.py +10 -3
  11. angr/analyses/cdg.py +1 -2
  12. angr/analyses/cfg/cfb.py +1 -3
  13. angr/analyses/cfg/cfg.py +2 -2
  14. angr/analyses/cfg/cfg_base.py +40 -68
  15. angr/analyses/cfg/cfg_emulated.py +1 -104
  16. angr/analyses/cfg/cfg_fast.py +90 -27
  17. angr/analyses/cfg/cfg_fast_soot.py +1 -1
  18. angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
  19. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +46 -10
  20. angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +5 -1
  21. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +65 -14
  22. angr/analyses/cfg/indirect_jump_resolvers/memload_resolver.py +81 -0
  23. angr/analyses/cfg/indirect_jump_resolvers/propagator_utils.py +24 -5
  24. angr/analyses/cfg/indirect_jump_resolvers/x86_pe_iat.py +2 -5
  25. angr/analyses/class_identifier.py +1 -2
  26. angr/analyses/complete_calling_conventions.py +3 -0
  27. angr/analyses/congruency_check.py +2 -3
  28. angr/analyses/data_dep/data_dependency_analysis.py +2 -2
  29. angr/analyses/ddg.py +1 -4
  30. angr/analyses/decompiler/ail_simplifier.py +15 -5
  31. angr/analyses/decompiler/block_simplifier.py +2 -2
  32. angr/analyses/decompiler/ccall_rewriters/__init__.py +2 -0
  33. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
  34. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +69 -0
  35. angr/analyses/decompiler/clinic.py +119 -72
  36. angr/analyses/decompiler/condition_processor.py +2 -0
  37. angr/analyses/decompiler/decompiler.py +1 -0
  38. angr/analyses/decompiler/dephication/dephication_base.py +2 -0
  39. angr/analyses/decompiler/dephication/rewriting_engine.py +8 -6
  40. angr/analyses/decompiler/dephication/seqnode_dephication.py +10 -1
  41. angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +2 -2
  42. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +2 -2
  43. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +1 -1
  44. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +1 -1
  45. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +1 -2
  46. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +1 -1
  47. angr/analyses/decompiler/sequence_walker.py +6 -2
  48. angr/analyses/decompiler/ssailification/rewriting.py +11 -1
  49. angr/analyses/decompiler/ssailification/rewriting_engine.py +56 -19
  50. angr/analyses/decompiler/ssailification/ssailification.py +13 -3
  51. angr/analyses/decompiler/ssailification/traversal.py +28 -2
  52. angr/analyses/decompiler/ssailification/traversal_state.py +6 -1
  53. angr/analyses/decompiler/structured_codegen/c.py +44 -21
  54. angr/analyses/decompiler/structuring/phoenix.py +118 -15
  55. angr/analyses/decompiler/utils.py +113 -8
  56. angr/analyses/disassembly.py +5 -5
  57. angr/analyses/fcp/__init__.py +4 -0
  58. angr/analyses/fcp/fcp.py +429 -0
  59. angr/analyses/identifier/identify.py +1 -3
  60. angr/analyses/loopfinder.py +4 -3
  61. angr/analyses/patchfinder.py +1 -1
  62. angr/analyses/propagator/engine_base.py +4 -3
  63. angr/analyses/propagator/propagator.py +14 -53
  64. angr/analyses/reaching_definitions/function_handler.py +1 -1
  65. angr/analyses/reassembler.py +1 -2
  66. angr/analyses/s_liveness.py +5 -1
  67. angr/analyses/s_propagator.py +26 -7
  68. angr/analyses/s_reaching_definitions/s_rda_model.py +2 -1
  69. angr/analyses/s_reaching_definitions/s_rda_view.py +20 -1
  70. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +11 -1
  71. angr/analyses/soot_class_hierarchy.py +1 -2
  72. angr/analyses/stack_pointer_tracker.py +29 -3
  73. angr/analyses/static_hooker.py +1 -2
  74. angr/analyses/typehoon/simple_solver.py +2 -2
  75. angr/analyses/variable_recovery/engine_ail.py +19 -7
  76. angr/analyses/variable_recovery/engine_base.py +16 -14
  77. angr/analyses/variable_recovery/engine_vex.py +2 -2
  78. angr/analyses/variable_recovery/variable_recovery_fast.py +23 -3
  79. angr/analyses/veritesting.py +4 -7
  80. angr/analyses/vfg.py +1 -1
  81. angr/analyses/vsa_ddg.py +1 -2
  82. angr/block.py +62 -22
  83. angr/callable.py +1 -3
  84. angr/calling_conventions.py +3 -3
  85. angr/codenode.py +5 -1
  86. angr/concretization_strategies/__init__.py +1 -83
  87. angr/concretization_strategies/any.py +2 -1
  88. angr/concretization_strategies/any_named.py +1 -1
  89. angr/concretization_strategies/base.py +81 -0
  90. angr/concretization_strategies/controlled_data.py +2 -1
  91. angr/concretization_strategies/eval.py +2 -1
  92. angr/concretization_strategies/logging.py +3 -1
  93. angr/concretization_strategies/max.py +2 -1
  94. angr/concretization_strategies/nonzero.py +2 -1
  95. angr/concretization_strategies/nonzero_range.py +2 -1
  96. angr/concretization_strategies/norepeats.py +2 -1
  97. angr/concretization_strategies/norepeats_range.py +2 -1
  98. angr/concretization_strategies/range.py +2 -1
  99. angr/concretization_strategies/signed_add.py +2 -1
  100. angr/concretization_strategies/single.py +2 -1
  101. angr/concretization_strategies/solutions.py +2 -1
  102. angr/concretization_strategies/unlimited_range.py +2 -1
  103. angr/engines/__init__.py +8 -5
  104. angr/engines/engine.py +3 -5
  105. angr/engines/failure.py +4 -5
  106. angr/engines/pcode/emulate.py +1 -1
  107. angr/engines/pcode/lifter.py +31 -18
  108. angr/engines/procedure.py +5 -7
  109. angr/engines/soot/expressions/__init__.py +20 -23
  110. angr/engines/soot/expressions/base.py +4 -4
  111. angr/engines/soot/expressions/invoke.py +1 -2
  112. angr/engines/soot/statements/__init__.py +10 -12
  113. angr/engines/soot/values/__init__.py +10 -12
  114. angr/engines/soot/values/arrayref.py +3 -3
  115. angr/engines/soot/values/instancefieldref.py +3 -2
  116. angr/engines/successors.py +18 -12
  117. angr/engines/syscall.py +4 -6
  118. angr/engines/unicorn.py +3 -2
  119. angr/engines/vex/claripy/ccall.py +8 -10
  120. angr/engines/vex/claripy/datalayer.py +4 -5
  121. angr/engines/vex/lifter.py +9 -6
  122. angr/exploration_techniques/__init__.py +0 -2
  123. angr/exploration_techniques/spiller.py +1 -3
  124. angr/exploration_techniques/stochastic.py +2 -3
  125. angr/factory.py +3 -9
  126. angr/flirt/build_sig.py +8 -15
  127. angr/knowledge_plugins/cfg/cfg_model.py +20 -17
  128. angr/knowledge_plugins/functions/function.py +70 -79
  129. angr/knowledge_plugins/functions/function_manager.py +8 -7
  130. angr/knowledge_plugins/functions/function_parser.py +1 -1
  131. angr/knowledge_plugins/functions/soot_function.py +21 -24
  132. angr/knowledge_plugins/propagations/propagation_model.py +4 -5
  133. angr/knowledge_plugins/propagations/states.py +0 -511
  134. angr/knowledge_plugins/variables/variable_manager.py +16 -10
  135. angr/procedures/libc/memcpy.py +4 -4
  136. angr/procedures/procedure_dict.py +3 -2
  137. angr/protos/__init__.py +2 -5
  138. angr/protos/cfg_pb2.py +21 -18
  139. angr/protos/function_pb2.py +17 -14
  140. angr/protos/primitives_pb2.py +44 -39
  141. angr/protos/variables_pb2.py +36 -31
  142. angr/protos/xrefs_pb2.py +15 -12
  143. angr/sim_procedure.py +15 -16
  144. angr/sim_variable.py +13 -1
  145. angr/simos/__init__.py +2 -0
  146. angr/simos/javavm.py +4 -6
  147. angr/simos/xbox.py +32 -0
  148. angr/state_plugins/__init__.py +0 -2
  149. angr/state_plugins/callstack.py +4 -4
  150. angr/state_plugins/cgc.py +3 -2
  151. angr/state_plugins/gdb.py +6 -5
  152. angr/state_plugins/globals.py +1 -2
  153. angr/state_plugins/heap/heap_brk.py +1 -2
  154. angr/state_plugins/history.py +10 -12
  155. angr/state_plugins/inspect.py +3 -5
  156. angr/state_plugins/libc.py +2 -2
  157. angr/state_plugins/log.py +8 -10
  158. angr/state_plugins/loop_data.py +1 -2
  159. angr/state_plugins/posix.py +7 -7
  160. angr/state_plugins/preconstrainer.py +2 -3
  161. angr/state_plugins/scratch.py +5 -8
  162. angr/state_plugins/sim_action.py +3 -3
  163. angr/state_plugins/solver.py +8 -3
  164. angr/state_plugins/symbolizer.py +5 -4
  165. angr/state_plugins/uc_manager.py +3 -3
  166. angr/state_plugins/unicorn_engine.py +5 -1
  167. angr/state_plugins/view.py +3 -5
  168. angr/storage/file.py +3 -5
  169. angr/storage/memory_mixins/address_concretization_mixin.py +2 -2
  170. angr/storage/memory_mixins/bvv_conversion_mixin.py +3 -3
  171. angr/storage/memory_mixins/clouseau_mixin.py +1 -3
  172. angr/storage/memory_mixins/name_resolution_mixin.py +1 -3
  173. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +13 -15
  174. angr/storage/memory_mixins/paged_memory/pages/__init__.py +1 -22
  175. angr/storage/memory_mixins/paged_memory/pages/base.py +31 -0
  176. angr/storage/memory_mixins/paged_memory/pages/list_page.py +1 -1
  177. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +1 -1
  178. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +2 -4
  179. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +3 -4
  180. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +4 -2
  181. angr/storage/memory_mixins/smart_find_mixin.py +1 -1
  182. angr/storage/memory_mixins/underconstrained_mixin.py +1 -1
  183. angr/storage/memory_mixins/unwrapper_mixin.py +1 -3
  184. angr/utils/enums_conv.py +28 -12
  185. angr/utils/segment_list.py +25 -22
  186. angr/utils/timing.py +18 -1
  187. angr/vaults.py +5 -6
  188. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/METADATA +7 -7
  189. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/RECORD +193 -191
  190. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/WHEEL +1 -1
  191. angr/analyses/propagator/outdated_definition_walker.py +0 -159
  192. angr/analyses/propagator/tmpvar_finder.py +0 -18
  193. angr/engines/concrete.py +0 -180
  194. angr/exploration_techniques/symbion.py +0 -80
  195. angr/state_plugins/concrete.py +0 -295
  196. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/LICENSE +0 -0
  197. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/entry_points.txt +0 -0
  198. {angr-9.2.135.dist-info → angr-9.2.137.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  # pylint:disable=line-too-long,import-outside-toplevel,import-error,multiple-statements,too-many-boolean-expressions
2
+ # ruff: noqa: SIM102
2
3
  from __future__ import annotations
3
4
  from typing import Any, TYPE_CHECKING
4
5
  from collections import defaultdict, OrderedDict
@@ -26,6 +27,7 @@ from angr.analyses.decompiler.utils import (
26
27
  is_empty_or_label_only_node,
27
28
  has_nonlabel_nonphi_statements,
28
29
  first_nonlabel_nonphi_statement,
30
+ switch_extract_bitwiseand_jumptable_info,
29
31
  )
30
32
  from angr.analyses.decompiler.counters.call_counter import AILCallCounter
31
33
  from .structurer_nodes import (
@@ -1045,6 +1047,11 @@ class PhoenixStructurer(StructurerBase):
1045
1047
  if r:
1046
1048
  return r
1047
1049
  jump_tables = self.kb.cfgs["CFGFast"].jump_tables
1050
+ r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(
1051
+ node, graph, full_graph, jump_tables
1052
+ )
1053
+ if r:
1054
+ return r
1048
1055
  r = self._match_acyclic_switch_cases_address_loaded_from_memory(node, graph, full_graph, jump_tables)
1049
1056
  if r:
1050
1057
  return r
@@ -1240,6 +1247,93 @@ class PhoenixStructurer(StructurerBase):
1240
1247
 
1241
1248
  return True
1242
1249
 
1250
+ def _match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(
1251
+ self, node, graph, full_graph, jump_tables
1252
+ ) -> bool:
1253
+ if node.addr not in jump_tables:
1254
+ return False
1255
+
1256
+ try:
1257
+ last_stmt = self.cond_proc.get_last_statement(node)
1258
+ except EmptyBlockNotice:
1259
+ return False
1260
+ if not (isinstance(last_stmt, Jump) and not isinstance(last_stmt.target, Const)):
1261
+ return False
1262
+
1263
+ jump_table = jump_tables[node.addr]
1264
+ if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
1265
+ return False
1266
+
1267
+ # extract the index expression, lower-, and upper-bounds from the last statement
1268
+ index = switch_extract_bitwiseand_jumptable_info(last_stmt)
1269
+ if not index:
1270
+ return False
1271
+ index_expr, cmp_lb, cmp_ub = index # pylint:disable=unused-variable
1272
+ case_count = cmp_ub - cmp_lb + 1
1273
+
1274
+ # ensure we have the same number of cases
1275
+ if case_count != len(jump_table.jumptable_entries):
1276
+ return False
1277
+
1278
+ # populate whitelist_edges
1279
+ for case_node_addr in jump_table.jumptable_entries:
1280
+ self.whitelist_edges.add((node.addr, case_node_addr))
1281
+ self.switch_case_known_heads.add(node)
1282
+
1283
+ # sanity check: case nodes are successors to node. all case nodes must have at most common one successor
1284
+ node_pred = None
1285
+ if graph.in_degree[node] == 1:
1286
+ node_pred = next(iter(graph.predecessors(node)))
1287
+
1288
+ case_nodes = list(graph.successors(node))
1289
+
1290
+ # case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
1291
+ # optimization)
1292
+ # example: touch_touch_no_switch.o:main
1293
+ r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
1294
+
1295
+ # case 2: the common successor is not directly reachable from node_a. this is a more common case.
1296
+ if not r:
1297
+ r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
1298
+
1299
+ if not r:
1300
+ return False
1301
+
1302
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
1303
+
1304
+ cases, node_default, to_remove = self._switch_build_cases(
1305
+ case_and_entry_addrs,
1306
+ node,
1307
+ node,
1308
+ None,
1309
+ graph,
1310
+ full_graph,
1311
+ )
1312
+
1313
+ assert node_default is None
1314
+ switch_end_addr = None
1315
+
1316
+ r = self._make_switch_cases_core(
1317
+ node,
1318
+ index_expr,
1319
+ cases,
1320
+ None,
1321
+ None,
1322
+ last_stmt.ins_addr,
1323
+ to_remove,
1324
+ graph,
1325
+ full_graph,
1326
+ node_a=None,
1327
+ )
1328
+ if not r:
1329
+ return False
1330
+
1331
+ # fully structured into a switch-case. remove node from switch_case_known_heads
1332
+ self.switch_case_known_heads.remove(node)
1333
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1334
+
1335
+ return True
1336
+
1243
1337
  def _match_acyclic_switch_cases_address_computed(self, node, graph, full_graph, jump_tables) -> bool:
1244
1338
  if node.addr not in jump_tables:
1245
1339
  return False
@@ -1333,7 +1427,7 @@ class PhoenixStructurer(StructurerBase):
1333
1427
  case_and_entryaddrs: dict[int, int | tuple[int, int | None]],
1334
1428
  head_node,
1335
1429
  node_a: BaseNode,
1336
- node_b_addr,
1430
+ node_b_addr: int | None,
1337
1431
  graph,
1338
1432
  full_graph,
1339
1433
  ) -> tuple[OrderedDict, Any, set[Any]]:
@@ -1343,7 +1437,9 @@ class PhoenixStructurer(StructurerBase):
1343
1437
  # it is possible that the default node gets duplicated by other analyses and creates a default node (addr.a)
1344
1438
  # and a case node (addr.b). The addr.a node is a successor to the head node while the addr.b node is a
1345
1439
  # successor to node_a
1346
- default_node_candidates = [nn for nn in graph.nodes if nn.addr == node_b_addr]
1440
+ default_node_candidates = (
1441
+ [nn for nn in graph.nodes if nn.addr == node_b_addr] if node_b_addr is not None else []
1442
+ )
1347
1443
  node_default: BaseNode | None = next(
1348
1444
  iter(nn for nn in default_node_candidates if graph.has_edge(head_node, nn)), None
1349
1445
  )
@@ -1355,7 +1451,6 @@ class PhoenixStructurer(StructurerBase):
1355
1451
  self.replace_nodes(full_graph, node_default, new_node)
1356
1452
  node_default = new_node
1357
1453
 
1358
- # entry_addrs_set = set(jumptable_entries)
1359
1454
  converted_nodes: dict[tuple[int, int | None], Any] = {}
1360
1455
  entry_addr_to_ids: defaultdict[tuple[int, int | None], set[int]] = defaultdict(set)
1361
1456
 
@@ -1442,7 +1537,7 @@ class PhoenixStructurer(StructurerBase):
1442
1537
  head,
1443
1538
  cmp_expr,
1444
1539
  cases: OrderedDict,
1445
- node_default_addr: int,
1540
+ node_default_addr: int | None,
1446
1541
  node_default,
1447
1542
  addr,
1448
1543
  to_remove: set,
@@ -1491,7 +1586,7 @@ class PhoenixStructurer(StructurerBase):
1491
1586
  pass
1492
1587
  graph.remove_edge(head, node_default)
1493
1588
  full_graph.remove_edge(head, node_default)
1494
- else:
1589
+ elif node_default_addr is not None:
1495
1590
  # the default node is not in the current graph, but it might be in the full graph
1496
1591
  node_default_in_full_graph = next(iter(nn for nn in full_graph if nn.addr == node_default_addr), None)
1497
1592
  if node_default_in_full_graph is not None and full_graph.has_edge(head, node_default_in_full_graph):
@@ -1536,7 +1631,7 @@ class PhoenixStructurer(StructurerBase):
1536
1631
  full_graph.remove_edge(head, out_dst)
1537
1632
 
1538
1633
  # fix full_graph if needed: remove successors that are no longer needed
1539
- for out_src, out_dst in out_edges[1:]:
1634
+ for _out_src, out_dst in out_edges[1:]:
1540
1635
  if out_dst in full_graph and out_dst not in graph and full_graph.in_degree[out_dst] == 0:
1541
1636
  full_graph.remove_node(out_dst)
1542
1637
  if out_dst in self._region.successors:
@@ -1570,12 +1665,22 @@ class PhoenixStructurer(StructurerBase):
1570
1665
 
1571
1666
  return case_and_entry_addrs
1572
1667
 
1573
- def _is_switch_cases_address_loaded_from_memory_head_or_jumpnode(self, graph, node) -> bool:
1668
+ def _is_node_unstructured_switch_case_head(self, node) -> bool:
1574
1669
  jump_tables = self.kb.cfgs["CFGFast"].jump_tables
1575
1670
  if node.addr in jump_tables:
1671
+ # maybe it has been structured?
1672
+ try:
1673
+ last_stmts = self.cond_proc.get_last_statements(node)
1674
+ except EmptyBlockNotice:
1675
+ return False
1676
+ return len(last_stmts) == 1 and isinstance(last_stmts[0], Jump)
1677
+ return False
1678
+
1679
+ def _is_switch_cases_address_loaded_from_memory_head_or_jumpnode(self, graph, node) -> bool:
1680
+ if self._is_node_unstructured_switch_case_head(node):
1576
1681
  return True
1577
1682
  for succ in graph.successors(node):
1578
- if succ.addr in jump_tables:
1683
+ if self._is_node_unstructured_switch_case_head(succ):
1579
1684
  return True
1580
1685
  return node in self.switch_case_known_heads
1581
1686
 
@@ -1634,13 +1739,11 @@ class PhoenixStructurer(StructurerBase):
1634
1739
  )
1635
1740
  ):
1636
1741
  # potentially ITE
1637
- jump_tables = self.kb.cfgs["CFGFast"].jump_tables
1638
-
1639
1742
  if (
1640
1743
  full_graph.in_degree[left] == 1
1641
1744
  and full_graph.in_degree[right] == 1
1642
- and left.addr not in jump_tables
1643
- and right.addr not in jump_tables
1745
+ and not self._is_node_unstructured_switch_case_head(left)
1746
+ and not self._is_node_unstructured_switch_case_head(right)
1644
1747
  ):
1645
1748
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1646
1749
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
@@ -1681,9 +1784,9 @@ class PhoenixStructurer(StructurerBase):
1681
1784
  left_succs, right_succs = right_succs, left_succs
1682
1785
  if left in graph and not left_succs and full_graph.in_degree[left] == 1 and right in graph:
1683
1786
  # potentially If-Then
1684
- jump_tables = self.kb.cfgs["CFGFast"].jump_tables
1685
-
1686
- if left.addr not in jump_tables and right.addr not in jump_tables:
1787
+ if not self._is_node_unstructured_switch_case_head(
1788
+ left
1789
+ ) and not self._is_node_unstructured_switch_case_head(right):
1687
1790
  edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1688
1791
  edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1689
1792
  if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right):
@@ -40,7 +40,7 @@ def remove_last_statement(node):
40
40
  elif type(node) is LoopNode:
41
41
  stmt = remove_last_statement(node.sequence_node)
42
42
  else:
43
- raise NotImplementedError
43
+ raise NotImplementedError(type(node))
44
44
 
45
45
  return stmt
46
46
 
@@ -69,7 +69,7 @@ def remove_last_statements(node) -> bool:
69
69
  return r
70
70
  if type(node) is LoopNode:
71
71
  return remove_last_statements(node.sequence_node)
72
- raise NotImplementedError
72
+ raise NotImplementedError(type(node))
73
73
 
74
74
 
75
75
  def append_statement(node, stmt):
@@ -83,16 +83,16 @@ def append_statement(node, stmt):
83
83
  if node.nodes:
84
84
  append_statement(node.nodes[-1], stmt)
85
85
  else:
86
- raise NotImplementedError
86
+ raise NotImplementedError("MultiNode without nodes")
87
87
  return
88
88
  if type(node) is SequenceNode:
89
89
  if node.nodes:
90
90
  append_statement(node.nodes[-1], stmt)
91
91
  else:
92
- raise NotImplementedError
92
+ raise NotImplementedError("SequenceNode without nodes")
93
93
  return
94
94
 
95
- raise NotImplementedError
95
+ raise NotImplementedError(type(node))
96
96
 
97
97
 
98
98
  def replace_last_statement(node, old_stmt, new_stmt):
@@ -118,7 +118,7 @@ def replace_last_statement(node, old_stmt, new_stmt):
118
118
  replace_last_statement(node.false_node, old_stmt, new_stmt)
119
119
  return
120
120
 
121
- raise NotImplementedError
121
+ raise NotImplementedError(type(node))
122
122
 
123
123
 
124
124
  def extract_jump_targets(stmt):
@@ -175,6 +175,111 @@ def switch_extract_cmp_bounds(last_stmt: ailment.Stmt.ConditionalJump) -> tuple[
175
175
  return None
176
176
 
177
177
 
178
+ def switch_extract_bitwiseand_jumptable_info(last_stmt: ailment.Stmt.Jump) -> tuple[Any, int, int] | None:
179
+ """
180
+ Check the last statement of the switch-case header node (whose address is loaded from a jump table and computed
181
+ using an index) and extract necessary information for rebuilding the switch-case construct.
182
+
183
+ An example of the statement:
184
+
185
+ Goto(Conv(32->s64, (
186
+ Load(addr=(0x4530e4<64> + (Conv(32->64, (Conv(64->32, vvar_287{reg 32}) & 0x3<32>)) * 0x4<64>)),
187
+ size=4, endness=Iend_LE) + 0x4530e4<32>))
188
+ )
189
+
190
+ :param last_stmt: The last statement of the switch-case header node.
191
+ :return: A tuple of (index expression, lower bound, upper bound), or None
192
+ """
193
+
194
+ if not isinstance(last_stmt, ailment.Stmt.Jump):
195
+ return None
196
+
197
+ # unpack the target expression
198
+ target = last_stmt.target
199
+ jump_addr_offset = None
200
+ jumptable_load_addr = None
201
+ while True:
202
+ if isinstance(target, ailment.Expr.Convert) and (
203
+ (target.from_bits == 32 and target.to_bits == 64) or (target.from_bits == 16 and target.to_bits == 32)
204
+ ):
205
+ target = target.operand
206
+ continue
207
+ if isinstance(target, ailment.Expr.BinaryOp) and target.op == "Add":
208
+ if isinstance(target.operands[0], ailment.Expr.Const) and isinstance(target.operands[1], ailment.Expr.Load):
209
+ jump_addr_offset = target.operands[0]
210
+ jumptable_load_addr = target.operands[1].addr
211
+ break
212
+ if isinstance(target.operands[1], ailment.Expr.Const) and isinstance(target.operands[0], ailment.Expr.Load):
213
+ jump_addr_offset = target.operands[1]
214
+ jumptable_load_addr = target.operands[0].addr
215
+ break
216
+ return None
217
+ if isinstance(target, ailment.Expr.Const):
218
+ return None
219
+ break
220
+
221
+ if jump_addr_offset is None or jumptable_load_addr is None:
222
+ return None
223
+
224
+ # parse jumptable_load_addr
225
+ jumptable_offset = None
226
+ jumptable_base_addr = None
227
+ if isinstance(jumptable_load_addr, ailment.Expr.BinaryOp) and jumptable_load_addr.op == "Add":
228
+ if isinstance(jumptable_load_addr.operands[0], ailment.Expr.Const):
229
+ jumptable_base_addr = jumptable_load_addr.operands[0]
230
+ jumptable_offset = jumptable_load_addr.operands[1]
231
+ elif isinstance(jumptable_load_addr.operands[1], ailment.Expr.Const):
232
+ jumptable_offset = jumptable_load_addr.operands[0]
233
+ jumptable_base_addr = jumptable_load_addr.operands[1]
234
+
235
+ if jumptable_offset is None or jumptable_base_addr is None:
236
+ return None
237
+
238
+ # parse jumptable_offset
239
+ expr = jumptable_offset
240
+ coeff = None
241
+ index_expr = None
242
+ lb = None
243
+ ub = None
244
+ while expr is not None:
245
+ if isinstance(expr, ailment.Expr.BinaryOp):
246
+ if expr.op == "Mul":
247
+ if isinstance(expr.operands[1], ailment.Expr.Const):
248
+ coeff = expr.operands[1].value
249
+ expr = expr.operands[0]
250
+ elif isinstance(expr.operands[0], ailment.Expr.Const):
251
+ coeff = expr.operands[0].value
252
+ expr = expr.operands[1]
253
+ else:
254
+ return None
255
+ elif expr.op == "And":
256
+ masks = {0x1, 0x3, 0x7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF}
257
+ if isinstance(expr.operands[1], ailment.Expr.Const) and expr.operands[1].value in masks:
258
+ lb = 0
259
+ ub = expr.operands[1].value
260
+ index_expr = expr
261
+ break
262
+ if isinstance(expr.operands[0], ailment.Expr.Const) and expr.operands[1].value in masks:
263
+ lb = 0
264
+ ub = expr.operands[0].value
265
+ index_expr = expr
266
+ break
267
+ return None
268
+ else:
269
+ return None
270
+ elif isinstance(expr, ailment.Expr.Convert):
271
+ if expr.is_signed is False:
272
+ expr = expr.operand
273
+ else:
274
+ return None
275
+ else:
276
+ break
277
+
278
+ if coeff is not None and index_expr is not None and lb is not None and ub is not None:
279
+ return index_expr, lb, ub
280
+ return None
281
+
282
+
178
283
  def get_ast_subexprs(claripy_ast):
179
284
  queue = [claripy_ast]
180
285
  while queue:
@@ -267,9 +372,9 @@ def insert_node(parent, insert_location: str, node, node_idx: int | tuple[int] |
267
372
  parent.sequence_node = SequenceNode(parent.sequence_node.addr, nodes=[parent.sequence_node])
268
373
  insert_node(parent.sequence_node, insert_location, node, node_idx)
269
374
  else:
270
- raise NotImplementedError
375
+ raise NotImplementedError(label)
271
376
  else:
272
- raise NotImplementedError
377
+ raise NotImplementedError(type(parent))
273
378
 
274
379
 
275
380
  def _merge_ail_nodes(graph, node_a: ailment.Block, node_b: ailment.Block) -> ailment.Block:
@@ -1,22 +1,24 @@
1
1
  from __future__ import annotations
2
+
3
+ import contextlib
2
4
  import logging
3
5
  from collections import defaultdict
4
- from typing import Union, Any
5
6
  from collections.abc import Sequence
7
+ from typing import Union, Any
6
8
 
7
9
  import pyvex
8
10
  import archinfo
9
- from angr.knowledge_plugins import Function
10
11
 
11
12
  from . import Analysis
12
13
 
14
+ from angr.analyses import AnalysesHub
13
15
  from angr.errors import AngrTypeError
16
+ from angr.knowledge_plugins import Function
14
17
  from angr.utils.library import get_cpp_function_name
15
18
  from angr.utils.formatting import ansi_color_enabled, ansi_color, add_edge_to_buffer
16
19
  from angr.block import DisassemblerInsn, CapstoneInsn, SootBlockNode
17
20
  from angr.codenode import BlockNode
18
21
  from .disassembly_utils import decode_instruction
19
- import contextlib
20
22
 
21
23
  try:
22
24
  from angr.engines import pcode
@@ -1295,6 +1297,4 @@ class Disassembly(Analysis):
1295
1297
  return "\n".join(buf)
1296
1298
 
1297
1299
 
1298
- from angr.analyses import AnalysesHub
1299
-
1300
1300
  AnalysesHub.register_default("Disassembly", Disassembly)
@@ -0,0 +1,4 @@
1
+ from __future__ import annotations
2
+ from .fcp import FastConstantPropagation
3
+
4
+ __all__ = ["FastConstantPropagation"]