angr 9.2.192__cp311-cp311-macosx_10_12_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.
Files changed (1442) hide show
  1. angr/__init__.py +366 -0
  2. angr/__main__.py +182 -0
  3. angr/ail_callable.py +79 -0
  4. angr/ailment/__init__.py +83 -0
  5. angr/ailment/block.py +88 -0
  6. angr/ailment/block_walker.py +856 -0
  7. angr/ailment/constant.py +3 -0
  8. angr/ailment/converter_common.py +11 -0
  9. angr/ailment/converter_pcode.py +648 -0
  10. angr/ailment/converter_vex.py +829 -0
  11. angr/ailment/expression.py +1655 -0
  12. angr/ailment/manager.py +34 -0
  13. angr/ailment/statement.py +973 -0
  14. angr/ailment/tagged_object.py +58 -0
  15. angr/ailment/utils.py +114 -0
  16. angr/analyses/__init__.py +117 -0
  17. angr/analyses/analysis.py +429 -0
  18. angr/analyses/backward_slice.py +686 -0
  19. angr/analyses/binary_optimizer.py +670 -0
  20. angr/analyses/bindiff.py +1512 -0
  21. angr/analyses/boyscout.py +76 -0
  22. angr/analyses/callee_cleanup_finder.py +74 -0
  23. angr/analyses/calling_convention/__init__.py +6 -0
  24. angr/analyses/calling_convention/calling_convention.py +1113 -0
  25. angr/analyses/calling_convention/fact_collector.py +647 -0
  26. angr/analyses/calling_convention/utils.py +60 -0
  27. angr/analyses/cdg.py +189 -0
  28. angr/analyses/cfg/__init__.py +23 -0
  29. angr/analyses/cfg/cfb.py +451 -0
  30. angr/analyses/cfg/cfg.py +74 -0
  31. angr/analyses/cfg/cfg_arch_options.py +95 -0
  32. angr/analyses/cfg/cfg_base.py +2954 -0
  33. angr/analyses/cfg/cfg_emulated.py +3451 -0
  34. angr/analyses/cfg/cfg_fast.py +5431 -0
  35. angr/analyses/cfg/cfg_fast_soot.py +662 -0
  36. angr/analyses/cfg/cfg_job_base.py +203 -0
  37. angr/analyses/cfg/indirect_jump_resolvers/__init__.py +30 -0
  38. angr/analyses/cfg/indirect_jump_resolvers/aarch64_macho_got.py +77 -0
  39. angr/analyses/cfg/indirect_jump_resolvers/amd64_elf_got.py +62 -0
  40. angr/analyses/cfg/indirect_jump_resolvers/amd64_pe_iat.py +51 -0
  41. angr/analyses/cfg/indirect_jump_resolvers/arm_elf_fast.py +159 -0
  42. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +339 -0
  43. angr/analyses/cfg/indirect_jump_resolvers/constant_value_manager.py +107 -0
  44. angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +82 -0
  45. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +2490 -0
  46. angr/analyses/cfg/indirect_jump_resolvers/memload_resolver.py +81 -0
  47. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +286 -0
  48. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_got.py +148 -0
  49. angr/analyses/cfg/indirect_jump_resolvers/propagator_utils.py +46 -0
  50. angr/analyses/cfg/indirect_jump_resolvers/resolver.py +74 -0
  51. angr/analyses/cfg/indirect_jump_resolvers/syscall_resolver.py +92 -0
  52. angr/analyses/cfg/indirect_jump_resolvers/x86_elf_pic_plt.py +88 -0
  53. angr/analyses/cfg/indirect_jump_resolvers/x86_pe_iat.py +47 -0
  54. angr/analyses/cfg_slice_to_sink/__init__.py +11 -0
  55. angr/analyses/cfg_slice_to_sink/cfg_slice_to_sink.py +117 -0
  56. angr/analyses/cfg_slice_to_sink/graph.py +87 -0
  57. angr/analyses/cfg_slice_to_sink/transitions.py +27 -0
  58. angr/analyses/class_identifier.py +63 -0
  59. angr/analyses/code_tagging.py +123 -0
  60. angr/analyses/codecave.py +77 -0
  61. angr/analyses/complete_calling_conventions.py +475 -0
  62. angr/analyses/congruency_check.py +377 -0
  63. angr/analyses/data_dep/__init__.py +16 -0
  64. angr/analyses/data_dep/data_dependency_analysis.py +595 -0
  65. angr/analyses/data_dep/dep_nodes.py +171 -0
  66. angr/analyses/data_dep/sim_act_location.py +49 -0
  67. angr/analyses/datagraph_meta.py +105 -0
  68. angr/analyses/ddg.py +1670 -0
  69. angr/analyses/decompiler/__init__.py +41 -0
  70. angr/analyses/decompiler/ail_simplifier.py +2246 -0
  71. angr/analyses/decompiler/ailgraph_walker.py +49 -0
  72. angr/analyses/decompiler/block_io_finder.py +302 -0
  73. angr/analyses/decompiler/block_similarity.py +199 -0
  74. angr/analyses/decompiler/block_simplifier.py +397 -0
  75. angr/analyses/decompiler/callsite_maker.py +579 -0
  76. angr/analyses/decompiler/ccall_rewriters/__init__.py +9 -0
  77. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +618 -0
  78. angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +24 -0
  79. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +354 -0
  80. angr/analyses/decompiler/clinic.py +3662 -0
  81. angr/analyses/decompiler/condition_processor.py +1323 -0
  82. angr/analyses/decompiler/counters/__init__.py +16 -0
  83. angr/analyses/decompiler/counters/boolean_counter.py +27 -0
  84. angr/analyses/decompiler/counters/call_counter.py +77 -0
  85. angr/analyses/decompiler/counters/expression_counters.py +77 -0
  86. angr/analyses/decompiler/counters/seq_cf_structure_counter.py +63 -0
  87. angr/analyses/decompiler/decompilation_cache.py +54 -0
  88. angr/analyses/decompiler/decompilation_options.py +317 -0
  89. angr/analyses/decompiler/decompiler.py +796 -0
  90. angr/analyses/decompiler/dephication/__init__.py +6 -0
  91. angr/analyses/decompiler/dephication/dephication_base.py +100 -0
  92. angr/analyses/decompiler/dephication/graph_dephication.py +70 -0
  93. angr/analyses/decompiler/dephication/graph_rewriting.py +112 -0
  94. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +357 -0
  95. angr/analyses/decompiler/dephication/rewriting_engine.py +528 -0
  96. angr/analyses/decompiler/dephication/seqnode_dephication.py +156 -0
  97. angr/analyses/decompiler/dirty_rewriters/__init__.py +7 -0
  98. angr/analyses/decompiler/dirty_rewriters/amd64_dirty.py +74 -0
  99. angr/analyses/decompiler/dirty_rewriters/rewriter_base.py +27 -0
  100. angr/analyses/decompiler/empty_node_remover.py +212 -0
  101. angr/analyses/decompiler/expression_narrower.py +290 -0
  102. angr/analyses/decompiler/goto_manager.py +112 -0
  103. angr/analyses/decompiler/graph_region.py +441 -0
  104. angr/analyses/decompiler/jump_target_collector.py +37 -0
  105. angr/analyses/decompiler/jumptable_entry_condition_rewriter.py +67 -0
  106. angr/analyses/decompiler/label_collector.py +32 -0
  107. angr/analyses/decompiler/node_replacer.py +42 -0
  108. angr/analyses/decompiler/notes/__init__.py +9 -0
  109. angr/analyses/decompiler/notes/decompilation_note.py +48 -0
  110. angr/analyses/decompiler/notes/deobfuscated_strings.py +56 -0
  111. angr/analyses/decompiler/optimization_passes/__init__.py +164 -0
  112. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +157 -0
  113. angr/analyses/decompiler/optimization_passes/call_stmt_rewriter.py +46 -0
  114. angr/analyses/decompiler/optimization_passes/code_motion.py +362 -0
  115. angr/analyses/decompiler/optimization_passes/condition_constprop.py +211 -0
  116. angr/analyses/decompiler/optimization_passes/const_derefs.py +127 -0
  117. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +365 -0
  118. angr/analyses/decompiler/optimization_passes/cross_jump_reverter.py +106 -0
  119. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +82 -0
  120. angr/analyses/decompiler/optimization_passes/determine_load_sizes.py +64 -0
  121. angr/analyses/decompiler/optimization_passes/div_simplifier.py +425 -0
  122. angr/analyses/decompiler/optimization_passes/duplication_reverter/__init__.py +5 -0
  123. angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +503 -0
  124. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +1221 -0
  125. angr/analyses/decompiler/optimization_passes/duplication_reverter/errors.py +16 -0
  126. angr/analyses/decompiler/optimization_passes/duplication_reverter/similarity.py +126 -0
  127. angr/analyses/decompiler/optimization_passes/duplication_reverter/utils.py +167 -0
  128. angr/analyses/decompiler/optimization_passes/eager_std_string_concatenation.py +236 -0
  129. angr/analyses/decompiler/optimization_passes/eager_std_string_eval.py +186 -0
  130. angr/analyses/decompiler/optimization_passes/engine_base.py +502 -0
  131. angr/analyses/decompiler/optimization_passes/expr_op_swapper.py +138 -0
  132. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +113 -0
  133. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +618 -0
  134. angr/analyses/decompiler/optimization_passes/inlined_strlen_simplifier.py +274 -0
  135. angr/analyses/decompiler/optimization_passes/ite_expr_converter.py +224 -0
  136. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +337 -0
  137. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +939 -0
  138. angr/analyses/decompiler/optimization_passes/mod_simplifier.py +99 -0
  139. angr/analyses/decompiler/optimization_passes/optimization_pass.py +710 -0
  140. angr/analyses/decompiler/optimization_passes/peephole_simplifier.py +75 -0
  141. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +263 -0
  142. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier_adv.py +198 -0
  143. angr/analyses/decompiler/optimization_passes/ret_addr_save_simplifier.py +171 -0
  144. angr/analyses/decompiler/optimization_passes/ret_deduplicator.py +222 -0
  145. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +632 -0
  146. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +61 -0
  147. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +166 -0
  148. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +333 -0
  149. angr/analyses/decompiler/optimization_passes/static_vvar_rewriter.py +336 -0
  150. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +166 -0
  151. angr/analyses/decompiler/optimization_passes/switch_reused_entry_rewriter.py +102 -0
  152. angr/analyses/decompiler/optimization_passes/tag_slicer.py +41 -0
  153. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +477 -0
  154. angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py +88 -0
  155. angr/analyses/decompiler/peephole_optimizations/__init__.py +136 -0
  156. angr/analyses/decompiler/peephole_optimizations/a_div_const_add_a_mul_n_div_const.py +42 -0
  157. angr/analyses/decompiler/peephole_optimizations/a_mul_const_div_shr_const.py +38 -0
  158. angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +34 -0
  159. angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +34 -0
  160. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div.py +25 -0
  161. angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +37 -0
  162. angr/analyses/decompiler/peephole_optimizations/a_sub_a_sub_n.py +23 -0
  163. angr/analyses/decompiler/peephole_optimizations/arm_cmpf.py +236 -0
  164. angr/analyses/decompiler/peephole_optimizations/base.py +157 -0
  165. angr/analyses/decompiler/peephole_optimizations/basepointeroffset_add_n.py +34 -0
  166. angr/analyses/decompiler/peephole_optimizations/basepointeroffset_and_mask.py +36 -0
  167. angr/analyses/decompiler/peephole_optimizations/bitwise_or_to_logical_or.py +34 -0
  168. angr/analyses/decompiler/peephole_optimizations/bool_expr_xor_1.py +27 -0
  169. angr/analyses/decompiler/peephole_optimizations/bswap.py +142 -0
  170. angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +182 -0
  171. angr/analyses/decompiler/peephole_optimizations/cmpord_rewriter.py +71 -0
  172. angr/analyses/decompiler/peephole_optimizations/coalesce_adjacent_shrs.py +39 -0
  173. angr/analyses/decompiler/peephole_optimizations/coalesce_same_cascading_ifs.py +28 -0
  174. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +44 -0
  175. angr/analyses/decompiler/peephole_optimizations/conv_a_sub0_shr_and.py +69 -0
  176. angr/analyses/decompiler/peephole_optimizations/conv_shl_shr.py +52 -0
  177. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +436 -0
  178. angr/analyses/decompiler/peephole_optimizations/extended_byte_and_mask.py +56 -0
  179. angr/analyses/decompiler/peephole_optimizations/inlined_memcpy.py +78 -0
  180. angr/analyses/decompiler/peephole_optimizations/inlined_memset.py +262 -0
  181. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +217 -0
  182. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +106 -0
  183. angr/analyses/decompiler/peephole_optimizations/inlined_wcscpy.py +256 -0
  184. angr/analyses/decompiler/peephole_optimizations/inlined_wcscpy_consolidation.py +296 -0
  185. angr/analyses/decompiler/peephole_optimizations/invert_negated_logical_conjuction_disjunction.py +50 -0
  186. angr/analyses/decompiler/peephole_optimizations/modulo_simplifier.py +89 -0
  187. angr/analyses/decompiler/peephole_optimizations/one_sub_bool.py +33 -0
  188. angr/analyses/decompiler/peephole_optimizations/optimized_div_simplifier.py +356 -0
  189. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +45 -0
  190. angr/analyses/decompiler/peephole_optimizations/remove_cxx_destructor_calls.py +32 -0
  191. angr/analyses/decompiler/peephole_optimizations/remove_empty_if_body.py +46 -0
  192. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +47 -0
  193. angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +125 -0
  194. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +273 -0
  195. angr/analyses/decompiler/peephole_optimizations/remove_redundant_derefs.py +21 -0
  196. angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_branch.py +30 -0
  197. angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_comparisons.py +54 -0
  198. angr/analyses/decompiler/peephole_optimizations/remove_redundant_nots.py +36 -0
  199. angr/analyses/decompiler/peephole_optimizations/remove_redundant_reinterprets.py +44 -0
  200. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +95 -0
  201. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py +115 -0
  202. angr/analyses/decompiler/peephole_optimizations/rewrite_bit_extractions.py +85 -0
  203. angr/analyses/decompiler/peephole_optimizations/rewrite_conv_mul.py +40 -0
  204. angr/analyses/decompiler/peephole_optimizations/rewrite_cxx_operator_calls.py +90 -0
  205. angr/analyses/decompiler/peephole_optimizations/rewrite_mips_gp_loads.py +49 -0
  206. angr/analyses/decompiler/peephole_optimizations/rol_ror.py +130 -0
  207. angr/analyses/decompiler/peephole_optimizations/sar_to_signed_div.py +143 -0
  208. angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +25 -0
  209. angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +51 -0
  210. angr/analyses/decompiler/peephole_optimizations/single_bit_cond_to_boolexpr.py +28 -0
  211. angr/analyses/decompiler/peephole_optimizations/single_bit_xor.py +29 -0
  212. angr/analyses/decompiler/peephole_optimizations/tidy_stack_addr.py +131 -0
  213. angr/analyses/decompiler/peephole_optimizations/utils.py +18 -0
  214. angr/analyses/decompiler/presets/__init__.py +22 -0
  215. angr/analyses/decompiler/presets/basic.py +36 -0
  216. angr/analyses/decompiler/presets/fast.py +66 -0
  217. angr/analyses/decompiler/presets/full.py +76 -0
  218. angr/analyses/decompiler/presets/malware.py +70 -0
  219. angr/analyses/decompiler/presets/preset.py +37 -0
  220. angr/analyses/decompiler/redundant_label_remover.py +141 -0
  221. angr/analyses/decompiler/region_identifier.py +1319 -0
  222. angr/analyses/decompiler/region_simplifiers/__init__.py +5 -0
  223. angr/analyses/decompiler/region_simplifiers/cascading_cond_transformer.py +95 -0
  224. angr/analyses/decompiler/region_simplifiers/cascading_ifs.py +82 -0
  225. angr/analyses/decompiler/region_simplifiers/expr_folding.py +838 -0
  226. angr/analyses/decompiler/region_simplifiers/goto.py +178 -0
  227. angr/analyses/decompiler/region_simplifiers/if_.py +135 -0
  228. angr/analyses/decompiler/region_simplifiers/ifelse.py +91 -0
  229. angr/analyses/decompiler/region_simplifiers/loop.py +143 -0
  230. angr/analyses/decompiler/region_simplifiers/node_address_finder.py +24 -0
  231. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +270 -0
  232. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +654 -0
  233. angr/analyses/decompiler/region_simplifiers/switch_expr_simplifier.py +87 -0
  234. angr/analyses/decompiler/region_walker.py +24 -0
  235. angr/analyses/decompiler/return_maker.py +72 -0
  236. angr/analyses/decompiler/semantic_naming/__init__.py +37 -0
  237. angr/analyses/decompiler/semantic_naming/array_index_naming.py +196 -0
  238. angr/analyses/decompiler/semantic_naming/boolean_naming.py +264 -0
  239. angr/analyses/decompiler/semantic_naming/call_result_naming.py +220 -0
  240. angr/analyses/decompiler/semantic_naming/naming_base.py +166 -0
  241. angr/analyses/decompiler/semantic_naming/orchestrator.py +107 -0
  242. angr/analyses/decompiler/semantic_naming/pointer_naming.py +334 -0
  243. angr/analyses/decompiler/semantic_naming/region_loop_counter_naming.py +246 -0
  244. angr/analyses/decompiler/semantic_naming/size_naming.py +137 -0
  245. angr/analyses/decompiler/seq_to_blocks.py +20 -0
  246. angr/analyses/decompiler/sequence_walker.py +261 -0
  247. angr/analyses/decompiler/ssailification/__init__.py +4 -0
  248. angr/analyses/decompiler/ssailification/rewriting.py +451 -0
  249. angr/analyses/decompiler/ssailification/rewriting_engine.py +1091 -0
  250. angr/analyses/decompiler/ssailification/rewriting_state.py +61 -0
  251. angr/analyses/decompiler/ssailification/ssailification.py +283 -0
  252. angr/analyses/decompiler/ssailification/traversal.py +127 -0
  253. angr/analyses/decompiler/ssailification/traversal_engine.py +323 -0
  254. angr/analyses/decompiler/ssailification/traversal_state.py +48 -0
  255. angr/analyses/decompiler/stack_item.py +36 -0
  256. angr/analyses/decompiler/structured_codegen/__init__.py +25 -0
  257. angr/analyses/decompiler/structured_codegen/base.py +193 -0
  258. angr/analyses/decompiler/structured_codegen/c.py +4257 -0
  259. angr/analyses/decompiler/structured_codegen/dummy.py +15 -0
  260. angr/analyses/decompiler/structured_codegen/dwarf_import.py +190 -0
  261. angr/analyses/decompiler/structuring/__init__.py +30 -0
  262. angr/analyses/decompiler/structuring/dream.py +1217 -0
  263. angr/analyses/decompiler/structuring/phoenix.py +3636 -0
  264. angr/analyses/decompiler/structuring/recursive_structurer.py +187 -0
  265. angr/analyses/decompiler/structuring/sailr.py +120 -0
  266. angr/analyses/decompiler/structuring/structurer_base.py +1140 -0
  267. angr/analyses/decompiler/structuring/structurer_nodes.py +442 -0
  268. angr/analyses/decompiler/utils.py +1224 -0
  269. angr/analyses/deobfuscator/__init__.py +23 -0
  270. angr/analyses/deobfuscator/api_obf_finder.py +333 -0
  271. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +80 -0
  272. angr/analyses/deobfuscator/api_obf_type2_finder.py +166 -0
  273. angr/analyses/deobfuscator/data_transformation_embedder.py +633 -0
  274. angr/analyses/deobfuscator/hash_lookup_api_deobfuscator.py +156 -0
  275. angr/analyses/deobfuscator/irsb_reg_collector.py +54 -0
  276. angr/analyses/deobfuscator/scope_ops_analyzer.py +68 -0
  277. angr/analyses/deobfuscator/string_obf_finder.py +983 -0
  278. angr/analyses/deobfuscator/string_obf_opt_passes.py +136 -0
  279. angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +47 -0
  280. angr/analyses/disassembly.py +1351 -0
  281. angr/analyses/disassembly_utils.py +101 -0
  282. angr/analyses/dominance_frontier.py +57 -0
  283. angr/analyses/fcp/__init__.py +4 -0
  284. angr/analyses/fcp/fcp.py +427 -0
  285. angr/analyses/find_objects_static.py +205 -0
  286. angr/analyses/flirt/__init__.py +47 -0
  287. angr/analyses/flirt/consts.py +160 -0
  288. angr/analyses/flirt/flirt.py +249 -0
  289. angr/analyses/flirt/flirt_function.py +20 -0
  290. angr/analyses/flirt/flirt_matcher.py +352 -0
  291. angr/analyses/flirt/flirt_module.py +32 -0
  292. angr/analyses/flirt/flirt_node.py +23 -0
  293. angr/analyses/flirt/flirt_sig.py +359 -0
  294. angr/analyses/flirt/flirt_utils.py +31 -0
  295. angr/analyses/forward_analysis/__init__.py +12 -0
  296. angr/analyses/forward_analysis/forward_analysis.py +619 -0
  297. angr/analyses/forward_analysis/job_info.py +64 -0
  298. angr/analyses/forward_analysis/visitors/__init__.py +14 -0
  299. angr/analyses/forward_analysis/visitors/call_graph.py +29 -0
  300. angr/analyses/forward_analysis/visitors/function_graph.py +86 -0
  301. angr/analyses/forward_analysis/visitors/graph.py +242 -0
  302. angr/analyses/forward_analysis/visitors/loop.py +29 -0
  303. angr/analyses/forward_analysis/visitors/single_node_graph.py +38 -0
  304. angr/analyses/identifier/__init__.py +5 -0
  305. angr/analyses/identifier/custom_callable.py +137 -0
  306. angr/analyses/identifier/errors.py +10 -0
  307. angr/analyses/identifier/func.py +60 -0
  308. angr/analyses/identifier/functions/__init__.py +37 -0
  309. angr/analyses/identifier/functions/atoi.py +73 -0
  310. angr/analyses/identifier/functions/based_atoi.py +125 -0
  311. angr/analyses/identifier/functions/fdprintf.py +123 -0
  312. angr/analyses/identifier/functions/free.py +64 -0
  313. angr/analyses/identifier/functions/int2str.py +287 -0
  314. angr/analyses/identifier/functions/malloc.py +111 -0
  315. angr/analyses/identifier/functions/memcmp.py +67 -0
  316. angr/analyses/identifier/functions/memcpy.py +89 -0
  317. angr/analyses/identifier/functions/memset.py +43 -0
  318. angr/analyses/identifier/functions/printf.py +123 -0
  319. angr/analyses/identifier/functions/recv_until.py +312 -0
  320. angr/analyses/identifier/functions/skip_calloc.py +73 -0
  321. angr/analyses/identifier/functions/skip_realloc.py +97 -0
  322. angr/analyses/identifier/functions/skip_recv_n.py +105 -0
  323. angr/analyses/identifier/functions/snprintf.py +112 -0
  324. angr/analyses/identifier/functions/sprintf.py +116 -0
  325. angr/analyses/identifier/functions/strcasecmp.py +33 -0
  326. angr/analyses/identifier/functions/strcmp.py +113 -0
  327. angr/analyses/identifier/functions/strcpy.py +43 -0
  328. angr/analyses/identifier/functions/strlen.py +27 -0
  329. angr/analyses/identifier/functions/strncmp.py +104 -0
  330. angr/analyses/identifier/functions/strncpy.py +65 -0
  331. angr/analyses/identifier/functions/strtol.py +89 -0
  332. angr/analyses/identifier/identify.py +825 -0
  333. angr/analyses/identifier/runner.py +360 -0
  334. angr/analyses/init_finder.py +289 -0
  335. angr/analyses/loop_analysis/__init__.py +4 -0
  336. angr/analyses/loop_analysis/loop_analysis.py +464 -0
  337. angr/analyses/loop_analysis.py +349 -0
  338. angr/analyses/loop_unroller/__init__.py +4 -0
  339. angr/analyses/loop_unroller/loop_unroller.py +222 -0
  340. angr/analyses/loopfinder.py +171 -0
  341. angr/analyses/outliner/__init__.py +7 -0
  342. angr/analyses/outliner/outliner.py +402 -0
  343. angr/analyses/patchfinder.py +137 -0
  344. angr/analyses/pathfinder.py +282 -0
  345. angr/analyses/propagator/__init__.py +5 -0
  346. angr/analyses/propagator/engine_base.py +62 -0
  347. angr/analyses/propagator/engine_vex.py +297 -0
  348. angr/analyses/propagator/propagator.py +361 -0
  349. angr/analyses/propagator/top_checker_mixin.py +218 -0
  350. angr/analyses/propagator/values.py +117 -0
  351. angr/analyses/propagator/vex_vars.py +68 -0
  352. angr/analyses/proximity_graph.py +444 -0
  353. angr/analyses/purity/__init__.py +15 -0
  354. angr/analyses/purity/analysis.py +78 -0
  355. angr/analyses/purity/engine.py +593 -0
  356. angr/analyses/reaching_definitions/__init__.py +67 -0
  357. angr/analyses/reaching_definitions/call_trace.py +73 -0
  358. angr/analyses/reaching_definitions/dep_graph.py +433 -0
  359. angr/analyses/reaching_definitions/engine_ail.py +1128 -0
  360. angr/analyses/reaching_definitions/engine_vex.py +1128 -0
  361. angr/analyses/reaching_definitions/external_codeloc.py +0 -0
  362. angr/analyses/reaching_definitions/function_handler.py +639 -0
  363. angr/analyses/reaching_definitions/function_handler_library/__init__.py +12 -0
  364. angr/analyses/reaching_definitions/function_handler_library/stdio.py +269 -0
  365. angr/analyses/reaching_definitions/function_handler_library/stdlib.py +195 -0
  366. angr/analyses/reaching_definitions/function_handler_library/string.py +158 -0
  367. angr/analyses/reaching_definitions/function_handler_library/unistd.py +51 -0
  368. angr/analyses/reaching_definitions/heap_allocator.py +70 -0
  369. angr/analyses/reaching_definitions/rd_initializer.py +237 -0
  370. angr/analyses/reaching_definitions/rd_state.py +579 -0
  371. angr/analyses/reaching_definitions/reaching_definitions.py +581 -0
  372. angr/analyses/reaching_definitions/subject.py +65 -0
  373. angr/analyses/reassembler.py +2900 -0
  374. angr/analyses/s_liveness.py +254 -0
  375. angr/analyses/s_propagator.py +575 -0
  376. angr/analyses/s_reaching_definitions/__init__.py +12 -0
  377. angr/analyses/s_reaching_definitions/s_rda_model.py +145 -0
  378. angr/analyses/s_reaching_definitions/s_rda_view.py +344 -0
  379. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +230 -0
  380. angr/analyses/smc.py +160 -0
  381. angr/analyses/soot_class_hierarchy.py +273 -0
  382. angr/analyses/stack_pointer_tracker.py +954 -0
  383. angr/analyses/static_hooker.py +53 -0
  384. angr/analyses/typehoon/__init__.py +5 -0
  385. angr/analyses/typehoon/dfa.py +118 -0
  386. angr/analyses/typehoon/lifter.py +133 -0
  387. angr/analyses/typehoon/simple_solver.py +2009 -0
  388. angr/analyses/typehoon/translator.py +283 -0
  389. angr/analyses/typehoon/typeconsts.py +439 -0
  390. angr/analyses/typehoon/typehoon.py +338 -0
  391. angr/analyses/typehoon/typevars.py +633 -0
  392. angr/analyses/typehoon/variance.py +11 -0
  393. angr/analyses/unpacker/__init__.py +6 -0
  394. angr/analyses/unpacker/obfuscation_detector.py +103 -0
  395. angr/analyses/unpacker/packing_detector.py +138 -0
  396. angr/analyses/variable_recovery/__init__.py +9 -0
  397. angr/analyses/variable_recovery/annotations.py +58 -0
  398. angr/analyses/variable_recovery/engine_ail.py +978 -0
  399. angr/analyses/variable_recovery/engine_base.py +1256 -0
  400. angr/analyses/variable_recovery/engine_vex.py +594 -0
  401. angr/analyses/variable_recovery/irsb_scanner.py +143 -0
  402. angr/analyses/variable_recovery/variable_recovery.py +574 -0
  403. angr/analyses/variable_recovery/variable_recovery_base.py +489 -0
  404. angr/analyses/variable_recovery/variable_recovery_fast.py +669 -0
  405. angr/analyses/veritesting.py +626 -0
  406. angr/analyses/vfg.py +1898 -0
  407. angr/analyses/vsa_ddg.py +420 -0
  408. angr/analyses/vtable.py +92 -0
  409. angr/analyses/xrefs.py +286 -0
  410. angr/angrdb/__init__.py +14 -0
  411. angr/angrdb/db.py +215 -0
  412. angr/angrdb/models.py +184 -0
  413. angr/angrdb/serializers/__init__.py +10 -0
  414. angr/angrdb/serializers/cfg_model.py +41 -0
  415. angr/angrdb/serializers/comments.py +60 -0
  416. angr/angrdb/serializers/funcs.py +61 -0
  417. angr/angrdb/serializers/kb.py +111 -0
  418. angr/angrdb/serializers/labels.py +59 -0
  419. angr/angrdb/serializers/loader.py +165 -0
  420. angr/angrdb/serializers/structured_code.py +167 -0
  421. angr/angrdb/serializers/variables.py +58 -0
  422. angr/angrdb/serializers/xrefs.py +48 -0
  423. angr/annocfg.py +317 -0
  424. angr/blade.py +431 -0
  425. angr/block.py +509 -0
  426. angr/callable.py +176 -0
  427. angr/calling_conventions.py +2613 -0
  428. angr/code_location.py +249 -0
  429. angr/codenode.py +145 -0
  430. angr/concretization_strategies/__init__.py +32 -0
  431. angr/concretization_strategies/any.py +17 -0
  432. angr/concretization_strategies/any_named.py +35 -0
  433. angr/concretization_strategies/base.py +81 -0
  434. angr/concretization_strategies/controlled_data.py +58 -0
  435. angr/concretization_strategies/eval.py +19 -0
  436. angr/concretization_strategies/logging.py +35 -0
  437. angr/concretization_strategies/max.py +25 -0
  438. angr/concretization_strategies/nonzero.py +16 -0
  439. angr/concretization_strategies/nonzero_range.py +22 -0
  440. angr/concretization_strategies/norepeats.py +37 -0
  441. angr/concretization_strategies/norepeats_range.py +37 -0
  442. angr/concretization_strategies/range.py +19 -0
  443. angr/concretization_strategies/signed_add.py +31 -0
  444. angr/concretization_strategies/single.py +15 -0
  445. angr/concretization_strategies/solutions.py +20 -0
  446. angr/concretization_strategies/unlimited_range.py +17 -0
  447. angr/distributed/__init__.py +9 -0
  448. angr/distributed/server.py +197 -0
  449. angr/distributed/worker.py +185 -0
  450. angr/emulator.py +144 -0
  451. angr/engines/__init__.py +69 -0
  452. angr/engines/ail/__init__.py +16 -0
  453. angr/engines/ail/callstack.py +58 -0
  454. angr/engines/ail/engine_light.py +903 -0
  455. angr/engines/ail/engine_successors.py +24 -0
  456. angr/engines/ail/setup.py +57 -0
  457. angr/engines/concrete.py +66 -0
  458. angr/engines/engine.py +29 -0
  459. angr/engines/failure.py +27 -0
  460. angr/engines/hook.py +93 -0
  461. angr/engines/icicle.py +294 -0
  462. angr/engines/light/__init__.py +23 -0
  463. angr/engines/light/data.py +681 -0
  464. angr/engines/light/engine.py +1297 -0
  465. angr/engines/pcode/__init__.py +9 -0
  466. angr/engines/pcode/behavior.py +998 -0
  467. angr/engines/pcode/cc.py +148 -0
  468. angr/engines/pcode/emulate.py +440 -0
  469. angr/engines/pcode/engine.py +242 -0
  470. angr/engines/pcode/lifter.py +1428 -0
  471. angr/engines/procedure.py +70 -0
  472. angr/engines/soot/__init__.py +5 -0
  473. angr/engines/soot/engine.py +410 -0
  474. angr/engines/soot/exceptions.py +17 -0
  475. angr/engines/soot/expressions/__init__.py +87 -0
  476. angr/engines/soot/expressions/arrayref.py +22 -0
  477. angr/engines/soot/expressions/base.py +21 -0
  478. angr/engines/soot/expressions/binop.py +28 -0
  479. angr/engines/soot/expressions/cast.py +22 -0
  480. angr/engines/soot/expressions/condition.py +35 -0
  481. angr/engines/soot/expressions/constants.py +47 -0
  482. angr/engines/soot/expressions/instanceOf.py +15 -0
  483. angr/engines/soot/expressions/instancefieldref.py +8 -0
  484. angr/engines/soot/expressions/invoke.py +114 -0
  485. angr/engines/soot/expressions/length.py +8 -0
  486. angr/engines/soot/expressions/local.py +8 -0
  487. angr/engines/soot/expressions/new.py +16 -0
  488. angr/engines/soot/expressions/newArray.py +54 -0
  489. angr/engines/soot/expressions/newMultiArray.py +86 -0
  490. angr/engines/soot/expressions/paramref.py +8 -0
  491. angr/engines/soot/expressions/phi.py +30 -0
  492. angr/engines/soot/expressions/staticfieldref.py +8 -0
  493. angr/engines/soot/expressions/thisref.py +7 -0
  494. angr/engines/soot/expressions/unsupported.py +7 -0
  495. angr/engines/soot/field_dispatcher.py +46 -0
  496. angr/engines/soot/method_dispatcher.py +46 -0
  497. angr/engines/soot/statements/__init__.py +44 -0
  498. angr/engines/soot/statements/assign.py +30 -0
  499. angr/engines/soot/statements/base.py +79 -0
  500. angr/engines/soot/statements/goto.py +14 -0
  501. angr/engines/soot/statements/identity.py +15 -0
  502. angr/engines/soot/statements/if_.py +19 -0
  503. angr/engines/soot/statements/invoke.py +12 -0
  504. angr/engines/soot/statements/return_.py +20 -0
  505. angr/engines/soot/statements/switch.py +41 -0
  506. angr/engines/soot/statements/throw.py +15 -0
  507. angr/engines/soot/values/__init__.py +38 -0
  508. angr/engines/soot/values/arrayref.py +122 -0
  509. angr/engines/soot/values/base.py +7 -0
  510. angr/engines/soot/values/constants.py +18 -0
  511. angr/engines/soot/values/instancefieldref.py +44 -0
  512. angr/engines/soot/values/local.py +18 -0
  513. angr/engines/soot/values/paramref.py +18 -0
  514. angr/engines/soot/values/staticfieldref.py +38 -0
  515. angr/engines/soot/values/strref.py +38 -0
  516. angr/engines/soot/values/thisref.py +149 -0
  517. angr/engines/successors.py +608 -0
  518. angr/engines/syscall.py +51 -0
  519. angr/engines/unicorn.py +490 -0
  520. angr/engines/vex/__init__.py +20 -0
  521. angr/engines/vex/claripy/__init__.py +5 -0
  522. angr/engines/vex/claripy/ccall.py +2097 -0
  523. angr/engines/vex/claripy/datalayer.py +141 -0
  524. angr/engines/vex/claripy/irop.py +1276 -0
  525. angr/engines/vex/heavy/__init__.py +16 -0
  526. angr/engines/vex/heavy/actions.py +231 -0
  527. angr/engines/vex/heavy/concretizers.py +403 -0
  528. angr/engines/vex/heavy/dirty.py +466 -0
  529. angr/engines/vex/heavy/heavy.py +370 -0
  530. angr/engines/vex/heavy/inspect.py +52 -0
  531. angr/engines/vex/heavy/resilience.py +85 -0
  532. angr/engines/vex/heavy/super_fastpath.py +34 -0
  533. angr/engines/vex/lifter.py +420 -0
  534. angr/engines/vex/light/__init__.py +11 -0
  535. angr/engines/vex/light/light.py +551 -0
  536. angr/engines/vex/light/resilience.py +74 -0
  537. angr/engines/vex/light/slicing.py +52 -0
  538. angr/errors.py +611 -0
  539. angr/exploration_techniques/__init__.py +53 -0
  540. angr/exploration_techniques/base.py +126 -0
  541. angr/exploration_techniques/bucketizer.py +94 -0
  542. angr/exploration_techniques/common.py +56 -0
  543. angr/exploration_techniques/dfs.py +37 -0
  544. angr/exploration_techniques/director.py +520 -0
  545. angr/exploration_techniques/driller_core.py +100 -0
  546. angr/exploration_techniques/explorer.py +152 -0
  547. angr/exploration_techniques/lengthlimiter.py +22 -0
  548. angr/exploration_techniques/local_loop_seer.py +65 -0
  549. angr/exploration_techniques/loop_seer.py +236 -0
  550. angr/exploration_techniques/manual_mergepoint.py +82 -0
  551. angr/exploration_techniques/memory_watcher.py +43 -0
  552. angr/exploration_techniques/oppologist.py +92 -0
  553. angr/exploration_techniques/slicecutor.py +118 -0
  554. angr/exploration_techniques/spiller.py +280 -0
  555. angr/exploration_techniques/spiller_db.py +27 -0
  556. angr/exploration_techniques/stochastic.py +56 -0
  557. angr/exploration_techniques/stub_stasher.py +19 -0
  558. angr/exploration_techniques/suggestions.py +159 -0
  559. angr/exploration_techniques/tech_builder.py +49 -0
  560. angr/exploration_techniques/threading.py +69 -0
  561. angr/exploration_techniques/timeout.py +34 -0
  562. angr/exploration_techniques/tracer.py +1098 -0
  563. angr/exploration_techniques/unique.py +106 -0
  564. angr/exploration_techniques/veritesting.py +37 -0
  565. angr/factory.py +413 -0
  566. angr/flirt/__init__.py +124 -0
  567. angr/flirt/build_sig.py +305 -0
  568. angr/graph_utils.py +0 -0
  569. angr/keyed_region.py +525 -0
  570. angr/knowledge_base.py +146 -0
  571. angr/knowledge_plugins/__init__.py +43 -0
  572. angr/knowledge_plugins/callsite_prototypes.py +95 -0
  573. angr/knowledge_plugins/cfg/__init__.py +18 -0
  574. angr/knowledge_plugins/cfg/cfg_manager.py +95 -0
  575. angr/knowledge_plugins/cfg/cfg_model.py +1043 -0
  576. angr/knowledge_plugins/cfg/cfg_node.py +536 -0
  577. angr/knowledge_plugins/cfg/indirect_jump.py +131 -0
  578. angr/knowledge_plugins/cfg/memory_data.py +156 -0
  579. angr/knowledge_plugins/comments.py +16 -0
  580. angr/knowledge_plugins/custom_strings.py +38 -0
  581. angr/knowledge_plugins/data.py +22 -0
  582. angr/knowledge_plugins/debug_variables.py +216 -0
  583. angr/knowledge_plugins/functions/__init__.py +9 -0
  584. angr/knowledge_plugins/functions/function.py +1830 -0
  585. angr/knowledge_plugins/functions/function_manager.py +621 -0
  586. angr/knowledge_plugins/functions/function_parser.py +360 -0
  587. angr/knowledge_plugins/functions/soot_function.py +128 -0
  588. angr/knowledge_plugins/indirect_jumps.py +35 -0
  589. angr/knowledge_plugins/key_definitions/__init__.py +17 -0
  590. angr/knowledge_plugins/key_definitions/atoms.py +374 -0
  591. angr/knowledge_plugins/key_definitions/constants.py +29 -0
  592. angr/knowledge_plugins/key_definitions/definition.py +216 -0
  593. angr/knowledge_plugins/key_definitions/environment.py +96 -0
  594. angr/knowledge_plugins/key_definitions/heap_address.py +33 -0
  595. angr/knowledge_plugins/key_definitions/key_definition_manager.py +82 -0
  596. angr/knowledge_plugins/key_definitions/live_definitions.py +1020 -0
  597. angr/knowledge_plugins/key_definitions/liveness.py +165 -0
  598. angr/knowledge_plugins/key_definitions/rd_model.py +171 -0
  599. angr/knowledge_plugins/key_definitions/tag.py +78 -0
  600. angr/knowledge_plugins/key_definitions/undefined.py +70 -0
  601. angr/knowledge_plugins/key_definitions/unknown_size.py +86 -0
  602. angr/knowledge_plugins/key_definitions/uses.py +178 -0
  603. angr/knowledge_plugins/labels.py +110 -0
  604. angr/knowledge_plugins/obfuscations.py +40 -0
  605. angr/knowledge_plugins/patches.py +126 -0
  606. angr/knowledge_plugins/plugin.py +24 -0
  607. angr/knowledge_plugins/propagations/__init__.py +10 -0
  608. angr/knowledge_plugins/propagations/prop_value.py +191 -0
  609. angr/knowledge_plugins/propagations/propagation_manager.py +60 -0
  610. angr/knowledge_plugins/propagations/propagation_model.py +80 -0
  611. angr/knowledge_plugins/propagations/states.py +552 -0
  612. angr/knowledge_plugins/structured_code.py +63 -0
  613. angr/knowledge_plugins/types.py +95 -0
  614. angr/knowledge_plugins/variables/__init__.py +8 -0
  615. angr/knowledge_plugins/variables/variable_access.py +113 -0
  616. angr/knowledge_plugins/variables/variable_manager.py +1375 -0
  617. angr/knowledge_plugins/xrefs/__init__.py +12 -0
  618. angr/knowledge_plugins/xrefs/xref.py +150 -0
  619. angr/knowledge_plugins/xrefs/xref_manager.py +127 -0
  620. angr/knowledge_plugins/xrefs/xref_types.py +16 -0
  621. angr/misc/__init__.py +19 -0
  622. angr/misc/ansi.py +47 -0
  623. angr/misc/autoimport.py +90 -0
  624. angr/misc/bug_report.py +126 -0
  625. angr/misc/hookset.py +106 -0
  626. angr/misc/loggers.py +130 -0
  627. angr/misc/picklable_lock.py +46 -0
  628. angr/misc/plugins.py +289 -0
  629. angr/misc/telemetry.py +54 -0
  630. angr/misc/testing.py +24 -0
  631. angr/misc/ux.py +31 -0
  632. angr/procedures/__init__.py +12 -0
  633. angr/procedures/advapi32/__init__.py +0 -0
  634. angr/procedures/cgc/__init__.py +3 -0
  635. angr/procedures/cgc/_terminate.py +11 -0
  636. angr/procedures/cgc/allocate.py +75 -0
  637. angr/procedures/cgc/deallocate.py +67 -0
  638. angr/procedures/cgc/fdwait.py +65 -0
  639. angr/procedures/cgc/random.py +67 -0
  640. angr/procedures/cgc/receive.py +93 -0
  641. angr/procedures/cgc/transmit.py +65 -0
  642. angr/procedures/definitions/__init__.py +1043 -0
  643. angr/procedures/definitions/cgc.py +23 -0
  644. angr/procedures/definitions/common/glibc.json +3516 -0
  645. angr/procedures/definitions/gnulib.py +41 -0
  646. angr/procedures/definitions/libstdcpp.py +25 -0
  647. angr/procedures/definitions/linux_kernel.py +8382 -0
  648. angr/procedures/definitions/linux_loader.py +7 -0
  649. angr/procedures/definitions/macho_libsystem.py +18 -0
  650. angr/procedures/definitions/msvcr.py +25 -0
  651. angr/procedures/definitions/parse_glibc.py +77 -0
  652. angr/procedures/definitions/parse_syscalls_from_local_system.py +54 -0
  653. angr/procedures/definitions/parse_win32json.py +2540 -0
  654. angr/procedures/definitions/types_stl.py +22 -0
  655. angr/procedures/definitions/wdk/api-ms-win-dx-d3dkmt-l1-1-4.json +24 -0
  656. angr/procedures/definitions/wdk/api-ms-win-dx-d3dkmt-l1-1-6.json +18 -0
  657. angr/procedures/definitions/wdk/clfs.json +189 -0
  658. angr/procedures/definitions/wdk/fltmgr.json +813 -0
  659. angr/procedures/definitions/wdk/fwpkclnt.json +24 -0
  660. angr/procedures/definitions/wdk/fwpuclnt.json +453 -0
  661. angr/procedures/definitions/wdk/gdi32.json +528 -0
  662. angr/procedures/definitions/wdk/hal.json +96 -0
  663. angr/procedures/definitions/wdk/ksecdd.json +72 -0
  664. angr/procedures/definitions/wdk/ndis.json +336 -0
  665. angr/procedures/definitions/wdk/ntoskrnl.json +5158 -0
  666. angr/procedures/definitions/wdk/offreg.json +87 -0
  667. angr/procedures/definitions/wdk/pshed.json +33 -0
  668. angr/procedures/definitions/wdk/secur32.json +39 -0
  669. angr/procedures/definitions/wdk/vhfum.json +30 -0
  670. angr/procedures/definitions/win32/_types_win32.json +34480 -0
  671. angr/procedures/definitions/win32/aclui.json +24 -0
  672. angr/procedures/definitions/win32/activeds.json +81 -0
  673. angr/procedures/definitions/win32/advapi32.json +2505 -0
  674. angr/procedures/definitions/win32/advpack.json +165 -0
  675. angr/procedures/definitions/win32/amsi.json +36 -0
  676. angr/procedures/definitions/win32/api-ms-win-appmodel-runtime-l1-1-1.json +45 -0
  677. angr/procedures/definitions/win32/api-ms-win-appmodel-runtime-l1-1-3.json +30 -0
  678. angr/procedures/definitions/win32/api-ms-win-appmodel-runtime-l1-1-6.json +18 -0
  679. angr/procedures/definitions/win32/api-ms-win-core-apiquery-l2-1-0.json +18 -0
  680. angr/procedures/definitions/win32/api-ms-win-core-backgroundtask-l1-1-0.json +18 -0
  681. angr/procedures/definitions/win32/api-ms-win-core-comm-l1-1-1.json +18 -0
  682. angr/procedures/definitions/win32/api-ms-win-core-comm-l1-1-2.json +18 -0
  683. angr/procedures/definitions/win32/api-ms-win-core-enclave-l1-1-1.json +24 -0
  684. angr/procedures/definitions/win32/api-ms-win-core-errorhandling-l1-1-3.json +18 -0
  685. angr/procedures/definitions/win32/api-ms-win-core-featurestaging-l1-1-0.json +30 -0
  686. angr/procedures/definitions/win32/api-ms-win-core-featurestaging-l1-1-1.json +18 -0
  687. angr/procedures/definitions/win32/api-ms-win-core-file-fromapp-l1-1-0.json +48 -0
  688. angr/procedures/definitions/win32/api-ms-win-core-handle-l1-1-0.json +18 -0
  689. angr/procedures/definitions/win32/api-ms-win-core-ioring-l1-1-0.json +51 -0
  690. angr/procedures/definitions/win32/api-ms-win-core-marshal-l1-1-0.json +27 -0
  691. angr/procedures/definitions/win32/api-ms-win-core-memory-l1-1-3.json +27 -0
  692. angr/procedures/definitions/win32/api-ms-win-core-memory-l1-1-4.json +18 -0
  693. angr/procedures/definitions/win32/api-ms-win-core-memory-l1-1-5.json +24 -0
  694. angr/procedures/definitions/win32/api-ms-win-core-memory-l1-1-6.json +27 -0
  695. angr/procedures/definitions/win32/api-ms-win-core-memory-l1-1-7.json +21 -0
  696. angr/procedures/definitions/win32/api-ms-win-core-memory-l1-1-8.json +24 -0
  697. angr/procedures/definitions/win32/api-ms-win-core-path-l1-1-0.json +81 -0
  698. angr/procedures/definitions/win32/api-ms-win-core-psm-appnotify-l1-1-0.json +21 -0
  699. angr/procedures/definitions/win32/api-ms-win-core-psm-appnotify-l1-1-1.json +21 -0
  700. angr/procedures/definitions/win32/api-ms-win-core-realtime-l1-1-1.json +24 -0
  701. angr/procedures/definitions/win32/api-ms-win-core-realtime-l1-1-2.json +24 -0
  702. angr/procedures/definitions/win32/api-ms-win-core-slapi-l1-1-0.json +18 -0
  703. angr/procedures/definitions/win32/api-ms-win-core-state-helpers-l1-1-0.json +18 -0
  704. angr/procedures/definitions/win32/api-ms-win-core-synch-l1-2-0.json +24 -0
  705. angr/procedures/definitions/win32/api-ms-win-core-sysinfo-l1-2-0.json +18 -0
  706. angr/procedures/definitions/win32/api-ms-win-core-sysinfo-l1-2-3.json +21 -0
  707. angr/procedures/definitions/win32/api-ms-win-core-sysinfo-l1-2-4.json +21 -0
  708. angr/procedures/definitions/win32/api-ms-win-core-sysinfo-l1-2-6.json +18 -0
  709. angr/procedures/definitions/win32/api-ms-win-core-util-l1-1-1.json +21 -0
  710. angr/procedures/definitions/win32/api-ms-win-core-wow64-l1-1-1.json +24 -0
  711. angr/procedures/definitions/win32/api-ms-win-devices-query-l1-1-0.json +42 -0
  712. angr/procedures/definitions/win32/api-ms-win-devices-query-l1-1-1.json +30 -0
  713. angr/procedures/definitions/win32/api-ms-win-dx-d3dkmt-l1-1-0.json +18 -0
  714. angr/procedures/definitions/win32/api-ms-win-gaming-deviceinformation-l1-1-0.json +18 -0
  715. angr/procedures/definitions/win32/api-ms-win-gaming-expandedresources-l1-1-0.json +24 -0
  716. angr/procedures/definitions/win32/api-ms-win-gaming-tcui-l1-1-0.json +36 -0
  717. angr/procedures/definitions/win32/api-ms-win-gaming-tcui-l1-1-1.json +21 -0
  718. angr/procedures/definitions/win32/api-ms-win-gaming-tcui-l1-1-2.json +36 -0
  719. angr/procedures/definitions/win32/api-ms-win-gaming-tcui-l1-1-3.json +21 -0
  720. angr/procedures/definitions/win32/api-ms-win-gaming-tcui-l1-1-4.json +39 -0
  721. angr/procedures/definitions/win32/api-ms-win-mm-misc-l1-1-1.json +18 -0
  722. angr/procedures/definitions/win32/api-ms-win-net-isolation-l1-1-0.json +39 -0
  723. angr/procedures/definitions/win32/api-ms-win-security-base-l1-2-2.json +18 -0
  724. angr/procedures/definitions/win32/api-ms-win-security-isolatedcontainer-l1-1-0.json +18 -0
  725. angr/procedures/definitions/win32/api-ms-win-security-isolatedcontainer-l1-1-1.json +18 -0
  726. angr/procedures/definitions/win32/api-ms-win-service-core-l1-1-3.json +18 -0
  727. angr/procedures/definitions/win32/api-ms-win-service-core-l1-1-4.json +18 -0
  728. angr/procedures/definitions/win32/api-ms-win-service-core-l1-1-5.json +21 -0
  729. angr/procedures/definitions/win32/api-ms-win-shcore-scaling-l1-1-0.json +24 -0
  730. angr/procedures/definitions/win32/api-ms-win-shcore-scaling-l1-1-1.json +33 -0
  731. angr/procedures/definitions/win32/api-ms-win-shcore-scaling-l1-1-2.json +18 -0
  732. angr/procedures/definitions/win32/api-ms-win-wsl-api-l1-1-0.json +36 -0
  733. angr/procedures/definitions/win32/apphelp.json +18 -0
  734. angr/procedures/definitions/win32/authz.json +114 -0
  735. angr/procedures/definitions/win32/avicap32.json +27 -0
  736. angr/procedures/definitions/win32/avifil32.json +195 -0
  737. angr/procedures/definitions/win32/avrt.json +57 -0
  738. angr/procedures/definitions/win32/bcp47mrm.json +21 -0
  739. angr/procedures/definitions/win32/bcrypt.json +174 -0
  740. angr/procedures/definitions/win32/bcryptprimitives.json +21 -0
  741. angr/procedures/definitions/win32/bluetoothapis.json +138 -0
  742. angr/procedures/definitions/win32/bthprops_cpl.json +33 -0
  743. angr/procedures/definitions/win32/cabinet.json +81 -0
  744. angr/procedures/definitions/win32/certadm.json +69 -0
  745. angr/procedures/definitions/win32/certpoleng.json +39 -0
  746. angr/procedures/definitions/win32/cfgmgr32.json +732 -0
  747. angr/procedures/definitions/win32/chakra.json +270 -0
  748. angr/procedures/definitions/win32/cldapi.json +123 -0
  749. angr/procedures/definitions/win32/clfsw32.json +192 -0
  750. angr/procedures/definitions/win32/clusapi.json +855 -0
  751. angr/procedures/definitions/win32/comctl32.json +360 -0
  752. angr/procedures/definitions/win32/comdlg32.json +78 -0
  753. angr/procedures/definitions/win32/compstui.json +27 -0
  754. angr/procedures/definitions/win32/computecore.json +177 -0
  755. angr/procedures/definitions/win32/computenetwork.json +144 -0
  756. angr/procedures/definitions/win32/computestorage.json +51 -0
  757. angr/procedures/definitions/win32/comsvcs.json +36 -0
  758. angr/procedures/definitions/win32/credui.json +72 -0
  759. angr/procedures/definitions/win32/crypt32.json +702 -0
  760. angr/procedures/definitions/win32/cryptnet.json +30 -0
  761. angr/procedures/definitions/win32/cryptui.json +45 -0
  762. angr/procedures/definitions/win32/cryptxml.json +72 -0
  763. angr/procedures/definitions/win32/cscapi.json +27 -0
  764. angr/procedures/definitions/win32/d2d1.json +54 -0
  765. angr/procedures/definitions/win32/d3d10.json +96 -0
  766. angr/procedures/definitions/win32/d3d10_1.json +21 -0
  767. angr/procedures/definitions/win32/d3d11.json +24 -0
  768. angr/procedures/definitions/win32/d3d12.json +39 -0
  769. angr/procedures/definitions/win32/d3d9.json +48 -0
  770. angr/procedures/definitions/win32/d3dcompiler_47.json +93 -0
  771. angr/procedures/definitions/win32/d3dcsx.json +42 -0
  772. angr/procedures/definitions/win32/davclnt.json +69 -0
  773. angr/procedures/definitions/win32/dbgeng.json +27 -0
  774. angr/procedures/definitions/win32/dbghelp.json +663 -0
  775. angr/procedures/definitions/win32/dbgmodel.json +18 -0
  776. angr/procedures/definitions/win32/dciman32.json +75 -0
  777. angr/procedures/definitions/win32/dcomp.json +51 -0
  778. angr/procedures/definitions/win32/ddraw.json +36 -0
  779. angr/procedures/definitions/win32/deviceaccess.json +18 -0
  780. angr/procedures/definitions/win32/dflayout.json +18 -0
  781. angr/procedures/definitions/win32/dhcpcsvc.json +60 -0
  782. angr/procedures/definitions/win32/dhcpcsvc6.json +33 -0
  783. angr/procedures/definitions/win32/dhcpsapi.json +603 -0
  784. angr/procedures/definitions/win32/diagnosticdataquery.json +120 -0
  785. angr/procedures/definitions/win32/dinput8.json +18 -0
  786. angr/procedures/definitions/win32/directml.json +21 -0
  787. angr/procedures/definitions/win32/dmprocessxmlfiltered.json +18 -0
  788. angr/procedures/definitions/win32/dnsapi.json +207 -0
  789. angr/procedures/definitions/win32/drt.json +63 -0
  790. angr/procedures/definitions/win32/drtprov.json +42 -0
  791. angr/procedures/definitions/win32/drttransport.json +21 -0
  792. angr/procedures/definitions/win32/dsound.json +45 -0
  793. angr/procedures/definitions/win32/dsparse.json +72 -0
  794. angr/procedures/definitions/win32/dsprop.json +36 -0
  795. angr/procedures/definitions/win32/dssec.json +27 -0
  796. angr/procedures/definitions/win32/dsuiext.json +27 -0
  797. angr/procedures/definitions/win32/dwmapi.json +108 -0
  798. angr/procedures/definitions/win32/dwrite.json +18 -0
  799. angr/procedures/definitions/win32/dxcompiler.json +21 -0
  800. angr/procedures/definitions/win32/dxcore.json +18 -0
  801. angr/procedures/definitions/win32/dxgi.json +33 -0
  802. angr/procedures/definitions/win32/dxva2.json +129 -0
  803. angr/procedures/definitions/win32/eappcfg.json +57 -0
  804. angr/procedures/definitions/win32/eappprxy.json +69 -0
  805. angr/procedures/definitions/win32/efswrt.json +21 -0
  806. angr/procedures/definitions/win32/elscore.json +30 -0
  807. angr/procedures/definitions/win32/esent.json +702 -0
  808. angr/procedures/definitions/win32/evr.json +36 -0
  809. angr/procedures/definitions/win32/faultrep.json +27 -0
  810. angr/procedures/definitions/win32/fhsvcctl.json +36 -0
  811. angr/procedures/definitions/win32/firewallapi.json +24 -0
  812. angr/procedures/definitions/win32/fltlib.json +99 -0
  813. angr/procedures/definitions/win32/fontsub.json +21 -0
  814. angr/procedures/definitions/win32/forceinline.json +24 -0
  815. angr/procedures/definitions/win32/fwpuclnt.json +591 -0
  816. angr/procedures/definitions/win32/fxsutility.json +21 -0
  817. angr/procedures/definitions/win32/gdi32.json +1308 -0
  818. angr/procedures/definitions/win32/gdiplus.json +1902 -0
  819. angr/procedures/definitions/win32/glu32.json +171 -0
  820. angr/procedures/definitions/win32/gpedit.json +33 -0
  821. angr/procedures/definitions/win32/hhctrl_ocx.json +21 -0
  822. angr/procedures/definitions/win32/hid.json +150 -0
  823. angr/procedures/definitions/win32/hlink.json +99 -0
  824. angr/procedures/definitions/win32/hrtfapo.json +18 -0
  825. angr/procedures/definitions/win32/httpapi.json +144 -0
  826. angr/procedures/definitions/win32/icm32.json +78 -0
  827. angr/procedures/definitions/win32/icmui.json +21 -0
  828. angr/procedures/definitions/win32/icu.json +3090 -0
  829. angr/procedures/definitions/win32/ieframe.json +102 -0
  830. angr/procedures/definitions/win32/imagehlp.json +84 -0
  831. angr/procedures/definitions/win32/imgutil.json +42 -0
  832. angr/procedures/definitions/win32/imm32.json +261 -0
  833. angr/procedures/definitions/win32/infocardapi.json +66 -0
  834. angr/procedures/definitions/win32/inkobjcore.json +96 -0
  835. angr/procedures/definitions/win32/iphlpapi.json +618 -0
  836. angr/procedures/definitions/win32/iscsidsc.json +252 -0
  837. angr/procedures/definitions/win32/isolatedwindowsenvironmentutils.json +21 -0
  838. angr/procedures/definitions/win32/kernel32.json +4566 -0
  839. angr/procedures/definitions/win32/kernelbase.json +33 -0
  840. angr/procedures/definitions/win32/keycredmgr.json +27 -0
  841. angr/procedures/definitions/win32/ksproxy_ax.json +33 -0
  842. angr/procedures/definitions/win32/ksuser.json +39 -0
  843. angr/procedures/definitions/win32/ktmw32.json +132 -0
  844. angr/procedures/definitions/win32/licenseprotection.json +21 -0
  845. angr/procedures/definitions/win32/loadperf.json +51 -0
  846. angr/procedures/definitions/win32/magnification.json +72 -0
  847. angr/procedures/definitions/win32/mapi32.json +213 -0
  848. angr/procedures/definitions/win32/mdmlocalmanagement.json +24 -0
  849. angr/procedures/definitions/win32/mdmregistration.json +60 -0
  850. angr/procedures/definitions/win32/mf.json +201 -0
  851. angr/procedures/definitions/win32/mfcore.json +21 -0
  852. angr/procedures/definitions/win32/mfplat.json +450 -0
  853. angr/procedures/definitions/win32/mfplay.json +18 -0
  854. angr/procedures/definitions/win32/mfreadwrite.json +30 -0
  855. angr/procedures/definitions/win32/mfsensorgroup.json +45 -0
  856. angr/procedures/definitions/win32/mfsrcsnk.json +21 -0
  857. angr/procedures/definitions/win32/mgmtapi.json +42 -0
  858. angr/procedures/definitions/win32/mi.json +18 -0
  859. angr/procedures/definitions/win32/mmdevapi.json +18 -0
  860. angr/procedures/definitions/win32/mpr.json +156 -0
  861. angr/procedures/definitions/win32/mprapi.json +351 -0
  862. angr/procedures/definitions/win32/mqrt.json +117 -0
  863. angr/procedures/definitions/win32/mrmsupport.json +96 -0
  864. angr/procedures/definitions/win32/msacm32.json +141 -0
  865. angr/procedures/definitions/win32/msajapi.json +1656 -0
  866. angr/procedures/definitions/win32/mscms.json +252 -0
  867. angr/procedures/definitions/win32/mscoree.json +96 -0
  868. angr/procedures/definitions/win32/msctfmonitor.json +24 -0
  869. angr/procedures/definitions/win32/msdelta.json +63 -0
  870. angr/procedures/definitions/win32/msdmo.json +48 -0
  871. angr/procedures/definitions/win32/msdrm.json +267 -0
  872. angr/procedures/definitions/win32/msi.json +807 -0
  873. angr/procedures/definitions/win32/msimg32.json +24 -0
  874. angr/procedures/definitions/win32/mspatcha.json +63 -0
  875. angr/procedures/definitions/win32/mspatchc.json +42 -0
  876. angr/procedures/definitions/win32/msports.json +36 -0
  877. angr/procedures/definitions/win32/msrating.json +72 -0
  878. angr/procedures/definitions/win32/mssign32.json +45 -0
  879. angr/procedures/definitions/win32/mstask.json +21 -0
  880. angr/procedures/definitions/win32/msvfw32.json +144 -0
  881. angr/procedures/definitions/win32/mswsock.json +63 -0
  882. angr/procedures/definitions/win32/mtxdm.json +18 -0
  883. angr/procedures/definitions/win32/ncrypt.json +132 -0
  884. angr/procedures/definitions/win32/ndfapi.json +63 -0
  885. angr/procedures/definitions/win32/netapi32.json +633 -0
  886. angr/procedures/definitions/win32/netsh.json +39 -0
  887. angr/procedures/definitions/win32/netshell.json +21 -0
  888. angr/procedures/definitions/win32/newdev.json +48 -0
  889. angr/procedures/definitions/win32/ninput.json +105 -0
  890. angr/procedures/definitions/win32/normaliz.json +21 -0
  891. angr/procedures/definitions/win32/ntdll.json +234 -0
  892. angr/procedures/definitions/win32/ntdllk.json +18 -0
  893. angr/procedures/definitions/win32/ntdsapi.json +258 -0
  894. angr/procedures/definitions/win32/ntlanman.json +45 -0
  895. angr/procedures/definitions/win32/odbc32.json +477 -0
  896. angr/procedures/definitions/win32/odbcbcp.json +96 -0
  897. angr/procedures/definitions/win32/ole32.json +966 -0
  898. angr/procedures/definitions/win32/oleacc.json +66 -0
  899. angr/procedures/definitions/win32/oleaut32.json +1230 -0
  900. angr/procedures/definitions/win32/oledlg.json +84 -0
  901. angr/procedures/definitions/win32/ondemandconnroutehelper.json +30 -0
  902. angr/procedures/definitions/win32/opengl32.json +1080 -0
  903. angr/procedures/definitions/win32/opmxbox.json +24 -0
  904. angr/procedures/definitions/win32/p2p.json +339 -0
  905. angr/procedures/definitions/win32/p2pgraph.json +126 -0
  906. angr/procedures/definitions/win32/pdh.json +309 -0
  907. angr/procedures/definitions/win32/peerdist.json +99 -0
  908. angr/procedures/definitions/win32/powrprof.json +267 -0
  909. angr/procedures/definitions/win32/prntvpt.json +48 -0
  910. angr/procedures/definitions/win32/projectedfslib.json +72 -0
  911. angr/procedures/definitions/win32/propsys.json +669 -0
  912. angr/procedures/definitions/win32/psapi.json +96 -0
  913. angr/procedures/definitions/win32/quartz.json +21 -0
  914. angr/procedures/definitions/win32/query.json +27 -0
  915. angr/procedures/definitions/win32/qwave.json +48 -0
  916. angr/procedures/definitions/win32/rasapi32.json +267 -0
  917. angr/procedures/definitions/win32/rasdlg.json +33 -0
  918. angr/procedures/definitions/win32/resutils.json +375 -0
  919. angr/procedures/definitions/win32/rpcns4.json +198 -0
  920. angr/procedures/definitions/win32/rpcproxy.json +27 -0
  921. angr/procedures/definitions/win32/rpcrt4.json +1356 -0
  922. angr/procedures/definitions/win32/rstrtmgr.json +48 -0
  923. angr/procedures/definitions/win32/rtm.json +243 -0
  924. angr/procedures/definitions/win32/rtutils.json +138 -0
  925. angr/procedures/definitions/win32/rtworkq.json +114 -0
  926. angr/procedures/definitions/win32/sas.json +18 -0
  927. angr/procedures/definitions/win32/scarddlg.json +30 -0
  928. angr/procedures/definitions/win32/schannel.json +42 -0
  929. angr/procedures/definitions/win32/sechost.json +21 -0
  930. angr/procedures/definitions/win32/secur32.json +282 -0
  931. angr/procedures/definitions/win32/sensapi.json +24 -0
  932. angr/procedures/definitions/win32/sensorsutilsv2.json +135 -0
  933. angr/procedures/definitions/win32/setupapi.json +1017 -0
  934. angr/procedures/definitions/win32/sfc.json +33 -0
  935. angr/procedures/definitions/win32/shdocvw.json +24 -0
  936. angr/procedures/definitions/win32/shell32.json +747 -0
  937. angr/procedures/definitions/win32/shlwapi.json +1095 -0
  938. angr/procedures/definitions/win32/slc.json +111 -0
  939. angr/procedures/definitions/win32/slcext.json +27 -0
  940. angr/procedures/definitions/win32/slwga.json +18 -0
  941. angr/procedures/definitions/win32/snmpapi.json +93 -0
  942. angr/procedures/definitions/win32/spoolss.json +93 -0
  943. angr/procedures/definitions/win32/srclient.json +18 -0
  944. angr/procedures/definitions/win32/srpapi.json +48 -0
  945. angr/procedures/definitions/win32/sspicli.json +36 -0
  946. angr/procedures/definitions/win32/sti.json +18 -0
  947. angr/procedures/definitions/win32/t2embed.json +57 -0
  948. angr/procedures/definitions/win32/tapi32.json +762 -0
  949. angr/procedures/definitions/win32/tbs.json +57 -0
  950. angr/procedures/definitions/win32/tdh.json +96 -0
  951. angr/procedures/definitions/win32/tokenbinding.json +45 -0
  952. angr/procedures/definitions/win32/traffic.json +75 -0
  953. angr/procedures/definitions/win32/txfw32.json +42 -0
  954. angr/procedures/definitions/win32/ualapi.json +27 -0
  955. angr/procedures/definitions/win32/uiautomationcore.json +309 -0
  956. angr/procedures/definitions/win32/urlmon.json +246 -0
  957. angr/procedures/definitions/win32/user32.json +2298 -0
  958. angr/procedures/definitions/win32/userenv.json +147 -0
  959. angr/procedures/definitions/win32/usp10.json +135 -0
  960. angr/procedures/definitions/win32/uxtheme.json +246 -0
  961. angr/procedures/definitions/win32/verifier.json +18 -0
  962. angr/procedures/definitions/win32/version.json +57 -0
  963. angr/procedures/definitions/win32/vertdll.json +36 -0
  964. angr/procedures/definitions/win32/virtdisk.json +102 -0
  965. angr/procedures/definitions/win32/vmdevicehost.json +54 -0
  966. angr/procedures/definitions/win32/vmsavedstatedumpprovider.json +144 -0
  967. angr/procedures/definitions/win32/vssapi.json +18 -0
  968. angr/procedures/definitions/win32/wcmapi.json +30 -0
  969. angr/procedures/definitions/win32/wdsbp.json +36 -0
  970. angr/procedures/definitions/win32/wdsclientapi.json +126 -0
  971. angr/procedures/definitions/win32/wdsmc.json +33 -0
  972. angr/procedures/definitions/win32/wdspxe.json +108 -0
  973. angr/procedures/definitions/win32/wdstptc.json +54 -0
  974. angr/procedures/definitions/win32/webauthn.json +54 -0
  975. angr/procedures/definitions/win32/webservices.json +594 -0
  976. angr/procedures/definitions/win32/websocket.json +54 -0
  977. angr/procedures/definitions/win32/wecapi.json +60 -0
  978. angr/procedures/definitions/win32/wer.json +78 -0
  979. angr/procedures/definitions/win32/wevtapi.json +120 -0
  980. angr/procedures/definitions/win32/winbio.json +177 -0
  981. angr/procedures/definitions/win32/windows_ai_machinelearning.json +18 -0
  982. angr/procedures/definitions/win32/windows_media_mediacontrol.json +39 -0
  983. angr/procedures/definitions/win32/windows_networking.json +18 -0
  984. angr/procedures/definitions/win32/windows_ui_xaml.json +21 -0
  985. angr/procedures/definitions/win32/windowscodecs.json +42 -0
  986. angr/procedures/definitions/win32/winfax.json +183 -0
  987. angr/procedures/definitions/win32/winhttp.json +183 -0
  988. angr/procedures/definitions/win32/winhvemulation.json +27 -0
  989. angr/procedures/definitions/win32/winhvplatform.json +213 -0
  990. angr/procedures/definitions/win32/wininet.json +903 -0
  991. angr/procedures/definitions/win32/winml.json +18 -0
  992. angr/procedures/definitions/win32/winmm.json +543 -0
  993. angr/procedures/definitions/win32/winscard.json +225 -0
  994. angr/procedures/definitions/win32/winspool_drv.json +531 -0
  995. angr/procedures/definitions/win32/wintrust.json +195 -0
  996. angr/procedures/definitions/win32/winusb.json +117 -0
  997. angr/procedures/definitions/win32/wlanapi.json +195 -0
  998. angr/procedures/definitions/win32/wlanui.json +18 -0
  999. angr/procedures/definitions/win32/wldap32.json +744 -0
  1000. angr/procedures/definitions/win32/wldp.json +42 -0
  1001. angr/procedures/definitions/win32/wmvcore.json +48 -0
  1002. angr/procedures/definitions/win32/wnvapi.json +21 -0
  1003. angr/procedures/definitions/win32/wofutil.json +48 -0
  1004. angr/procedures/definitions/win32/ws2_32.json +495 -0
  1005. angr/procedures/definitions/win32/wscapi.json +33 -0
  1006. angr/procedures/definitions/win32/wsclient.json +24 -0
  1007. angr/procedures/definitions/win32/wsdapi.json +111 -0
  1008. angr/procedures/definitions/win32/wsmsvc.json +114 -0
  1009. angr/procedures/definitions/win32/wsnmp32.json +162 -0
  1010. angr/procedures/definitions/win32/wtsapi32.json +204 -0
  1011. angr/procedures/definitions/win32/xaudio2_8.json +27 -0
  1012. angr/procedures/definitions/win32/xinput1_4.json +36 -0
  1013. angr/procedures/definitions/win32/xmllite.json +33 -0
  1014. angr/procedures/definitions/win32/xolehlp.json +27 -0
  1015. angr/procedures/definitions/win32/xpsprint.json +21 -0
  1016. angr/procedures/glibc/__ctype_b_loc.py +21 -0
  1017. angr/procedures/glibc/__ctype_tolower_loc.py +21 -0
  1018. angr/procedures/glibc/__ctype_toupper_loc.py +21 -0
  1019. angr/procedures/glibc/__errno_location.py +7 -0
  1020. angr/procedures/glibc/__init__.py +3 -0
  1021. angr/procedures/glibc/__libc_init.py +37 -0
  1022. angr/procedures/glibc/__libc_start_main.py +301 -0
  1023. angr/procedures/glibc/dynamic_loading.py +20 -0
  1024. angr/procedures/glibc/scanf.py +19 -0
  1025. angr/procedures/glibc/sscanf.py +10 -0
  1026. angr/procedures/gnulib/__init__.py +3 -0
  1027. angr/procedures/gnulib/xalloc_die.py +14 -0
  1028. angr/procedures/gnulib/xstrtol_fatal.py +14 -0
  1029. angr/procedures/java/__init__.py +42 -0
  1030. angr/procedures/java/unconstrained.py +65 -0
  1031. angr/procedures/java_io/__init__.py +0 -0
  1032. angr/procedures/java_io/read.py +12 -0
  1033. angr/procedures/java_io/write.py +17 -0
  1034. angr/procedures/java_jni/__init__.py +482 -0
  1035. angr/procedures/java_jni/array_operations.py +312 -0
  1036. angr/procedures/java_jni/class_and_interface_operations.py +31 -0
  1037. angr/procedures/java_jni/field_access.py +173 -0
  1038. angr/procedures/java_jni/global_and_local_refs.py +57 -0
  1039. angr/procedures/java_jni/method_calls.py +365 -0
  1040. angr/procedures/java_jni/not_implemented.py +26 -0
  1041. angr/procedures/java_jni/object_operations.py +94 -0
  1042. angr/procedures/java_jni/string_operations.py +87 -0
  1043. angr/procedures/java_jni/version_information.py +12 -0
  1044. angr/procedures/java_lang/__init__.py +0 -0
  1045. angr/procedures/java_lang/character.py +30 -0
  1046. angr/procedures/java_lang/double.py +24 -0
  1047. angr/procedures/java_lang/exit.py +13 -0
  1048. angr/procedures/java_lang/getsimplename.py +18 -0
  1049. angr/procedures/java_lang/integer.py +43 -0
  1050. angr/procedures/java_lang/load_library.py +9 -0
  1051. angr/procedures/java_lang/math.py +15 -0
  1052. angr/procedures/java_lang/string.py +78 -0
  1053. angr/procedures/java_lang/stringbuilder.py +44 -0
  1054. angr/procedures/java_lang/system.py +18 -0
  1055. angr/procedures/java_util/__init__.py +0 -0
  1056. angr/procedures/java_util/collection.py +35 -0
  1057. angr/procedures/java_util/iterator.py +46 -0
  1058. angr/procedures/java_util/list.py +99 -0
  1059. angr/procedures/java_util/map.py +131 -0
  1060. angr/procedures/java_util/random.py +14 -0
  1061. angr/procedures/java_util/scanner_nextline.py +23 -0
  1062. angr/procedures/libc/__init__.py +3 -0
  1063. angr/procedures/libc/abort.py +9 -0
  1064. angr/procedures/libc/access.py +13 -0
  1065. angr/procedures/libc/atoi.py +14 -0
  1066. angr/procedures/libc/atol.py +13 -0
  1067. angr/procedures/libc/calloc.py +8 -0
  1068. angr/procedures/libc/closelog.py +10 -0
  1069. angr/procedures/libc/err.py +14 -0
  1070. angr/procedures/libc/error.py +54 -0
  1071. angr/procedures/libc/exit.py +11 -0
  1072. angr/procedures/libc/fclose.py +19 -0
  1073. angr/procedures/libc/feof.py +21 -0
  1074. angr/procedures/libc/fflush.py +16 -0
  1075. angr/procedures/libc/fgetc.py +27 -0
  1076. angr/procedures/libc/fgets.py +69 -0
  1077. angr/procedures/libc/fopen.py +63 -0
  1078. angr/procedures/libc/fprintf.py +25 -0
  1079. angr/procedures/libc/fputc.py +23 -0
  1080. angr/procedures/libc/fputs.py +24 -0
  1081. angr/procedures/libc/fread.py +24 -0
  1082. angr/procedures/libc/free.py +9 -0
  1083. angr/procedures/libc/fscanf.py +20 -0
  1084. angr/procedures/libc/fseek.py +34 -0
  1085. angr/procedures/libc/ftell.py +22 -0
  1086. angr/procedures/libc/fwrite.py +19 -0
  1087. angr/procedures/libc/getchar.py +13 -0
  1088. angr/procedures/libc/getdelim.py +99 -0
  1089. angr/procedures/libc/getegid.py +8 -0
  1090. angr/procedures/libc/geteuid.py +8 -0
  1091. angr/procedures/libc/getgid.py +8 -0
  1092. angr/procedures/libc/gets.py +68 -0
  1093. angr/procedures/libc/getuid.py +8 -0
  1094. angr/procedures/libc/malloc.py +12 -0
  1095. angr/procedures/libc/memcmp.py +69 -0
  1096. angr/procedures/libc/memcpy.py +45 -0
  1097. angr/procedures/libc/memset.py +72 -0
  1098. angr/procedures/libc/openlog.py +10 -0
  1099. angr/procedures/libc/perror.py +13 -0
  1100. angr/procedures/libc/printf.py +34 -0
  1101. angr/procedures/libc/putchar.py +13 -0
  1102. angr/procedures/libc/puts.py +19 -0
  1103. angr/procedures/libc/rand.py +8 -0
  1104. angr/procedures/libc/realloc.py +8 -0
  1105. angr/procedures/libc/rewind.py +12 -0
  1106. angr/procedures/libc/scanf.py +20 -0
  1107. angr/procedures/libc/setbuf.py +9 -0
  1108. angr/procedures/libc/setvbuf.py +7 -0
  1109. angr/procedures/libc/snprintf.py +36 -0
  1110. angr/procedures/libc/sprintf.py +25 -0
  1111. angr/procedures/libc/srand.py +7 -0
  1112. angr/procedures/libc/sscanf.py +13 -0
  1113. angr/procedures/libc/stpcpy.py +18 -0
  1114. angr/procedures/libc/strcat.py +14 -0
  1115. angr/procedures/libc/strchr.py +48 -0
  1116. angr/procedures/libc/strcmp.py +31 -0
  1117. angr/procedures/libc/strcpy.py +13 -0
  1118. angr/procedures/libc/strlen.py +114 -0
  1119. angr/procedures/libc/strncat.py +19 -0
  1120. angr/procedures/libc/strncmp.py +183 -0
  1121. angr/procedures/libc/strncpy.py +22 -0
  1122. angr/procedures/libc/strnlen.py +13 -0
  1123. angr/procedures/libc/strstr.py +101 -0
  1124. angr/procedures/libc/strtol.py +261 -0
  1125. angr/procedures/libc/strtoul.py +9 -0
  1126. angr/procedures/libc/system.py +13 -0
  1127. angr/procedures/libc/time.py +9 -0
  1128. angr/procedures/libc/tmpnam.py +20 -0
  1129. angr/procedures/libc/tolower.py +10 -0
  1130. angr/procedures/libc/toupper.py +10 -0
  1131. angr/procedures/libc/ungetc.py +20 -0
  1132. angr/procedures/libc/vsnprintf.py +17 -0
  1133. angr/procedures/libc/wchar.py +16 -0
  1134. angr/procedures/libstdcpp/__init__.py +0 -0
  1135. angr/procedures/libstdcpp/_unwind_resume.py +11 -0
  1136. angr/procedures/libstdcpp/std____throw_bad_alloc.py +13 -0
  1137. angr/procedures/libstdcpp/std____throw_bad_cast.py +13 -0
  1138. angr/procedures/libstdcpp/std____throw_length_error.py +13 -0
  1139. angr/procedures/libstdcpp/std____throw_logic_error.py +13 -0
  1140. angr/procedures/libstdcpp/std__terminate.py +13 -0
  1141. angr/procedures/linux_kernel/__init__.py +3 -0
  1142. angr/procedures/linux_kernel/access.py +18 -0
  1143. angr/procedures/linux_kernel/arch_prctl.py +34 -0
  1144. angr/procedures/linux_kernel/arm_user_helpers.py +59 -0
  1145. angr/procedures/linux_kernel/brk.py +18 -0
  1146. angr/procedures/linux_kernel/cwd.py +28 -0
  1147. angr/procedures/linux_kernel/fstat.py +138 -0
  1148. angr/procedures/linux_kernel/fstat64.py +170 -0
  1149. angr/procedures/linux_kernel/futex.py +17 -0
  1150. angr/procedures/linux_kernel/getegid.py +17 -0
  1151. angr/procedures/linux_kernel/geteuid.py +17 -0
  1152. angr/procedures/linux_kernel/getgid.py +17 -0
  1153. angr/procedures/linux_kernel/getpid.py +14 -0
  1154. angr/procedures/linux_kernel/getrlimit.py +24 -0
  1155. angr/procedures/linux_kernel/gettid.py +9 -0
  1156. angr/procedures/linux_kernel/getuid.py +17 -0
  1157. angr/procedures/linux_kernel/iovec.py +47 -0
  1158. angr/procedures/linux_kernel/lseek.py +42 -0
  1159. angr/procedures/linux_kernel/mmap.py +16 -0
  1160. angr/procedures/linux_kernel/mprotect.py +42 -0
  1161. angr/procedures/linux_kernel/munmap.py +8 -0
  1162. angr/procedures/linux_kernel/openat.py +26 -0
  1163. angr/procedures/linux_kernel/set_tid_address.py +8 -0
  1164. angr/procedures/linux_kernel/sigaction.py +19 -0
  1165. angr/procedures/linux_kernel/sigprocmask.py +23 -0
  1166. angr/procedures/linux_kernel/stat.py +23 -0
  1167. angr/procedures/linux_kernel/sysinfo.py +59 -0
  1168. angr/procedures/linux_kernel/tgkill.py +10 -0
  1169. angr/procedures/linux_kernel/time.py +34 -0
  1170. angr/procedures/linux_kernel/uid.py +30 -0
  1171. angr/procedures/linux_kernel/uname.py +29 -0
  1172. angr/procedures/linux_kernel/unlink.py +22 -0
  1173. angr/procedures/linux_kernel/vsyscall.py +16 -0
  1174. angr/procedures/linux_loader/__init__.py +3 -0
  1175. angr/procedures/linux_loader/_dl_initial_error_catch_tsd.py +7 -0
  1176. angr/procedures/linux_loader/_dl_rtld_lock.py +15 -0
  1177. angr/procedures/linux_loader/sim_loader.py +54 -0
  1178. angr/procedures/linux_loader/tls.py +40 -0
  1179. angr/procedures/msvcr/__getmainargs.py +16 -0
  1180. angr/procedures/msvcr/__init__.py +4 -0
  1181. angr/procedures/msvcr/_initterm.py +38 -0
  1182. angr/procedures/msvcr/fmode.py +31 -0
  1183. angr/procedures/ntdll/__init__.py +0 -0
  1184. angr/procedures/ntdll/exceptions.py +60 -0
  1185. angr/procedures/posix/__init__.py +3 -0
  1186. angr/procedures/posix/accept.py +29 -0
  1187. angr/procedures/posix/bind.py +13 -0
  1188. angr/procedures/posix/bzero.py +9 -0
  1189. angr/procedures/posix/chroot.py +27 -0
  1190. angr/procedures/posix/close.py +9 -0
  1191. angr/procedures/posix/closedir.py +7 -0
  1192. angr/procedures/posix/dup.py +56 -0
  1193. angr/procedures/posix/fcntl.py +10 -0
  1194. angr/procedures/posix/fdopen.py +76 -0
  1195. angr/procedures/posix/fileno.py +18 -0
  1196. angr/procedures/posix/fork.py +13 -0
  1197. angr/procedures/posix/getenv.py +35 -0
  1198. angr/procedures/posix/gethostbyname.py +43 -0
  1199. angr/procedures/posix/getpass.py +19 -0
  1200. angr/procedures/posix/getsockopt.py +11 -0
  1201. angr/procedures/posix/htonl.py +11 -0
  1202. angr/procedures/posix/htons.py +11 -0
  1203. angr/procedures/posix/inet_ntoa.py +59 -0
  1204. angr/procedures/posix/listen.py +13 -0
  1205. angr/procedures/posix/mmap.py +144 -0
  1206. angr/procedures/posix/open.py +18 -0
  1207. angr/procedures/posix/opendir.py +10 -0
  1208. angr/procedures/posix/poll.py +55 -0
  1209. angr/procedures/posix/pread64.py +46 -0
  1210. angr/procedures/posix/pthread.py +87 -0
  1211. angr/procedures/posix/pwrite64.py +46 -0
  1212. angr/procedures/posix/read.py +13 -0
  1213. angr/procedures/posix/readdir.py +62 -0
  1214. angr/procedures/posix/recv.py +13 -0
  1215. angr/procedures/posix/recvfrom.py +13 -0
  1216. angr/procedures/posix/select.py +48 -0
  1217. angr/procedures/posix/send.py +23 -0
  1218. angr/procedures/posix/setsockopt.py +9 -0
  1219. angr/procedures/posix/sigaction.py +23 -0
  1220. angr/procedures/posix/sim_time.py +48 -0
  1221. angr/procedures/posix/sleep.py +8 -0
  1222. angr/procedures/posix/socket.py +18 -0
  1223. angr/procedures/posix/strcasecmp.py +26 -0
  1224. angr/procedures/posix/strdup.py +18 -0
  1225. angr/procedures/posix/strtok_r.py +64 -0
  1226. angr/procedures/posix/syslog.py +15 -0
  1227. angr/procedures/posix/tz.py +9 -0
  1228. angr/procedures/posix/unlink.py +11 -0
  1229. angr/procedures/posix/usleep.py +8 -0
  1230. angr/procedures/posix/write.py +13 -0
  1231. angr/procedures/procedure_dict.py +50 -0
  1232. angr/procedures/stubs/CallReturn.py +13 -0
  1233. angr/procedures/stubs/NoReturnUnconstrained.py +13 -0
  1234. angr/procedures/stubs/Nop.py +7 -0
  1235. angr/procedures/stubs/PathTerminator.py +9 -0
  1236. angr/procedures/stubs/Redirect.py +18 -0
  1237. angr/procedures/stubs/ReturnChar.py +11 -0
  1238. angr/procedures/stubs/ReturnUnconstrained.py +24 -0
  1239. angr/procedures/stubs/UnresolvableCallTarget.py +9 -0
  1240. angr/procedures/stubs/UnresolvableJumpTarget.py +9 -0
  1241. angr/procedures/stubs/UserHook.py +18 -0
  1242. angr/procedures/stubs/__init__.py +3 -0
  1243. angr/procedures/stubs/b64_decode.py +15 -0
  1244. angr/procedures/stubs/caller.py +14 -0
  1245. angr/procedures/stubs/crazy_scanf.py +20 -0
  1246. angr/procedures/stubs/format_parser.py +669 -0
  1247. angr/procedures/stubs/syscall_stub.py +24 -0
  1248. angr/procedures/testing/__init__.py +3 -0
  1249. angr/procedures/testing/manyargs.py +9 -0
  1250. angr/procedures/testing/retreg.py +8 -0
  1251. angr/procedures/tracer/__init__.py +4 -0
  1252. angr/procedures/tracer/random.py +9 -0
  1253. angr/procedures/tracer/receive.py +23 -0
  1254. angr/procedures/tracer/transmit.py +26 -0
  1255. angr/procedures/uclibc/__init__.py +3 -0
  1256. angr/procedures/uclibc/__uClibc_main.py +10 -0
  1257. angr/procedures/win32/EncodePointer.py +7 -0
  1258. angr/procedures/win32/ExitProcess.py +9 -0
  1259. angr/procedures/win32/GetCommandLine.py +12 -0
  1260. angr/procedures/win32/GetCurrentProcessId.py +7 -0
  1261. angr/procedures/win32/GetCurrentThreadId.py +7 -0
  1262. angr/procedures/win32/GetLastInputInfo.py +40 -0
  1263. angr/procedures/win32/GetModuleHandle.py +29 -0
  1264. angr/procedures/win32/GetProcessAffinityMask.py +37 -0
  1265. angr/procedures/win32/InterlockedExchange.py +15 -0
  1266. angr/procedures/win32/IsProcessorFeaturePresent.py +7 -0
  1267. angr/procedures/win32/VirtualAlloc.py +114 -0
  1268. angr/procedures/win32/VirtualProtect.py +60 -0
  1269. angr/procedures/win32/__init__.py +3 -0
  1270. angr/procedures/win32/critical_section.py +12 -0
  1271. angr/procedures/win32/dynamic_loading.py +104 -0
  1272. angr/procedures/win32/file_handles.py +47 -0
  1273. angr/procedures/win32/gethostbyname.py +12 -0
  1274. angr/procedures/win32/heap.py +45 -0
  1275. angr/procedures/win32/is_bad_ptr.py +26 -0
  1276. angr/procedures/win32/local_storage.py +88 -0
  1277. angr/procedures/win32/mutex.py +11 -0
  1278. angr/procedures/win32/sim_time.py +135 -0
  1279. angr/procedures/win32/system_paths.py +35 -0
  1280. angr/procedures/win32_kernel/ExAllocatePool.py +13 -0
  1281. angr/procedures/win32_kernel/ExFreePoolWithTag.py +8 -0
  1282. angr/procedures/win32_kernel/__fastfail.py +15 -0
  1283. angr/procedures/win32_kernel/__init__.py +3 -0
  1284. angr/procedures/win_user32/__init__.py +0 -0
  1285. angr/procedures/win_user32/chars.py +15 -0
  1286. angr/procedures/win_user32/keyboard.py +14 -0
  1287. angr/procedures/win_user32/messagebox.py +49 -0
  1288. angr/project.py +860 -0
  1289. angr/protos/__init__.py +19 -0
  1290. angr/protos/cfg_pb2.py +42 -0
  1291. angr/protos/function_pb2.py +38 -0
  1292. angr/protos/primitives_pb2.py +59 -0
  1293. angr/protos/variables_pb2.py +55 -0
  1294. angr/protos/xrefs_pb2.py +36 -0
  1295. angr/py.typed +1 -0
  1296. angr/rustylib.cpython-311-darwin.so +0 -0
  1297. angr/serializable.py +66 -0
  1298. angr/sim_manager.py +971 -0
  1299. angr/sim_options.py +436 -0
  1300. angr/sim_procedure.py +626 -0
  1301. angr/sim_state.py +926 -0
  1302. angr/sim_state_options.py +403 -0
  1303. angr/sim_type.py +4026 -0
  1304. angr/sim_variable.py +470 -0
  1305. angr/simos/__init__.py +47 -0
  1306. angr/simos/cgc.py +153 -0
  1307. angr/simos/javavm.py +458 -0
  1308. angr/simos/linux.py +509 -0
  1309. angr/simos/simos.py +444 -0
  1310. angr/simos/snimmuc_nxp.py +149 -0
  1311. angr/simos/userland.py +163 -0
  1312. angr/simos/windows.py +615 -0
  1313. angr/simos/xbox.py +32 -0
  1314. angr/slicer.py +352 -0
  1315. angr/state_hierarchy.py +262 -0
  1316. angr/state_plugins/__init__.py +84 -0
  1317. angr/state_plugins/callstack.py +478 -0
  1318. angr/state_plugins/cgc.py +155 -0
  1319. angr/state_plugins/debug_variables.py +192 -0
  1320. angr/state_plugins/filesystem.py +463 -0
  1321. angr/state_plugins/gdb.py +148 -0
  1322. angr/state_plugins/globals.py +65 -0
  1323. angr/state_plugins/heap/__init__.py +15 -0
  1324. angr/state_plugins/heap/heap_base.py +128 -0
  1325. angr/state_plugins/heap/heap_brk.py +136 -0
  1326. angr/state_plugins/heap/heap_freelist.py +213 -0
  1327. angr/state_plugins/heap/heap_libc.py +46 -0
  1328. angr/state_plugins/heap/heap_ptmalloc.py +620 -0
  1329. angr/state_plugins/heap/utils.py +22 -0
  1330. angr/state_plugins/history.py +564 -0
  1331. angr/state_plugins/inspect.py +375 -0
  1332. angr/state_plugins/javavm_classloader.py +134 -0
  1333. angr/state_plugins/jni_references.py +95 -0
  1334. angr/state_plugins/libc.py +1263 -0
  1335. angr/state_plugins/light_registers.py +168 -0
  1336. angr/state_plugins/log.py +84 -0
  1337. angr/state_plugins/loop_data.py +92 -0
  1338. angr/state_plugins/plugin.py +176 -0
  1339. angr/state_plugins/posix.py +703 -0
  1340. angr/state_plugins/preconstrainer.py +196 -0
  1341. angr/state_plugins/scratch.py +173 -0
  1342. angr/state_plugins/sim_action.py +326 -0
  1343. angr/state_plugins/sim_action_object.py +271 -0
  1344. angr/state_plugins/sim_event.py +59 -0
  1345. angr/state_plugins/solver.py +1128 -0
  1346. angr/state_plugins/symbolizer.py +291 -0
  1347. angr/state_plugins/trace_additions.py +738 -0
  1348. angr/state_plugins/uc_manager.py +94 -0
  1349. angr/state_plugins/unicorn_engine.py +1920 -0
  1350. angr/state_plugins/view.py +340 -0
  1351. angr/storage/__init__.py +15 -0
  1352. angr/storage/file.py +1210 -0
  1353. angr/storage/memory_mixins/__init__.py +317 -0
  1354. angr/storage/memory_mixins/actions_mixin.py +72 -0
  1355. angr/storage/memory_mixins/address_concretization_mixin.py +384 -0
  1356. angr/storage/memory_mixins/bvv_conversion_mixin.py +73 -0
  1357. angr/storage/memory_mixins/clouseau_mixin.py +137 -0
  1358. angr/storage/memory_mixins/conditional_store_mixin.py +25 -0
  1359. angr/storage/memory_mixins/convenient_mappings_mixin.py +256 -0
  1360. angr/storage/memory_mixins/default_filler_mixin.py +144 -0
  1361. angr/storage/memory_mixins/dirty_addrs_mixin.py +11 -0
  1362. angr/storage/memory_mixins/hex_dumper_mixin.py +82 -0
  1363. angr/storage/memory_mixins/javavm_memory_mixin.py +392 -0
  1364. angr/storage/memory_mixins/keyvalue_memory_mixin.py +43 -0
  1365. angr/storage/memory_mixins/label_merger_mixin.py +31 -0
  1366. angr/storage/memory_mixins/memory_mixin.py +175 -0
  1367. angr/storage/memory_mixins/multi_value_merger_mixin.py +79 -0
  1368. angr/storage/memory_mixins/name_resolution_mixin.py +67 -0
  1369. angr/storage/memory_mixins/paged_memory/__init__.py +0 -0
  1370. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +266 -0
  1371. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +743 -0
  1372. angr/storage/memory_mixins/paged_memory/paged_memory_multivalue_mixin.py +65 -0
  1373. angr/storage/memory_mixins/paged_memory/pages/__init__.py +26 -0
  1374. angr/storage/memory_mixins/paged_memory/pages/base.py +31 -0
  1375. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +341 -0
  1376. angr/storage/memory_mixins/paged_memory/pages/history_tracking_mixin.py +92 -0
  1377. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +55 -0
  1378. angr/storage/memory_mixins/paged_memory/pages/list_page.py +338 -0
  1379. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +324 -0
  1380. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +419 -0
  1381. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +36 -0
  1382. angr/storage/memory_mixins/paged_memory/pages/refcount_mixin.py +52 -0
  1383. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +529 -0
  1384. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +36 -0
  1385. angr/storage/memory_mixins/paged_memory/stack_allocation_mixin.py +74 -0
  1386. angr/storage/memory_mixins/regioned_memory/__init__.py +17 -0
  1387. angr/storage/memory_mixins/regioned_memory/abstract_address_descriptor.py +36 -0
  1388. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +31 -0
  1389. angr/storage/memory_mixins/regioned_memory/region_category_mixin.py +9 -0
  1390. angr/storage/memory_mixins/regioned_memory/region_data.py +246 -0
  1391. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +241 -0
  1392. angr/storage/memory_mixins/regioned_memory/regioned_address_concretization_mixin.py +119 -0
  1393. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +442 -0
  1394. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +69 -0
  1395. angr/storage/memory_mixins/simple_interface_mixin.py +71 -0
  1396. angr/storage/memory_mixins/simplification_mixin.py +15 -0
  1397. angr/storage/memory_mixins/size_resolution_mixin.py +143 -0
  1398. angr/storage/memory_mixins/slotted_memory.py +140 -0
  1399. angr/storage/memory_mixins/smart_find_mixin.py +161 -0
  1400. angr/storage/memory_mixins/symbolic_merger_mixin.py +16 -0
  1401. angr/storage/memory_mixins/top_merger_mixin.py +25 -0
  1402. angr/storage/memory_mixins/underconstrained_mixin.py +67 -0
  1403. angr/storage/memory_mixins/unwrapper_mixin.py +26 -0
  1404. angr/storage/memory_object.py +195 -0
  1405. angr/tablespecs.py +91 -0
  1406. angr/unicornlib.dylib +0 -0
  1407. angr/utils/__init__.py +46 -0
  1408. angr/utils/ail.py +176 -0
  1409. angr/utils/algo.py +34 -0
  1410. angr/utils/balancer.py +776 -0
  1411. angr/utils/bits.py +46 -0
  1412. angr/utils/constants.py +9 -0
  1413. angr/utils/cowdict.py +63 -0
  1414. angr/utils/cpp.py +17 -0
  1415. angr/utils/doms.py +150 -0
  1416. angr/utils/dynamic_dictlist.py +89 -0
  1417. angr/utils/endness.py +18 -0
  1418. angr/utils/enums_conv.py +97 -0
  1419. angr/utils/env.py +12 -0
  1420. angr/utils/formatting.py +128 -0
  1421. angr/utils/funcid.py +244 -0
  1422. angr/utils/graph.py +981 -0
  1423. angr/utils/lazy_import.py +13 -0
  1424. angr/utils/library.py +236 -0
  1425. angr/utils/loader.py +55 -0
  1426. angr/utils/mp.py +66 -0
  1427. angr/utils/orderedset.py +74 -0
  1428. angr/utils/ssa/__init__.py +455 -0
  1429. angr/utils/ssa/tmp_uses_collector.py +23 -0
  1430. angr/utils/ssa/vvar_uses_collector.py +36 -0
  1431. angr/utils/strings.py +20 -0
  1432. angr/utils/tagged_interval_map.py +112 -0
  1433. angr/utils/timing.py +74 -0
  1434. angr/utils/types.py +193 -0
  1435. angr/utils/vex.py +11 -0
  1436. angr/vaults.py +367 -0
  1437. angr-9.2.192.dist-info/METADATA +112 -0
  1438. angr-9.2.192.dist-info/RECORD +1442 -0
  1439. angr-9.2.192.dist-info/WHEEL +6 -0
  1440. angr-9.2.192.dist-info/entry_points.txt +2 -0
  1441. angr-9.2.192.dist-info/licenses/LICENSE +27 -0
  1442. angr-9.2.192.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3636 @@
1
+ # pylint:disable=line-too-long,import-outside-toplevel,import-error,multiple-statements,too-many-boolean-expressions
2
+ # ruff: noqa: SIM102
3
+ from __future__ import annotations
4
+ from typing import Any, TYPE_CHECKING
5
+ from collections import defaultdict, OrderedDict
6
+ from enum import Enum
7
+ import logging
8
+
9
+ import networkx
10
+
11
+ import claripy
12
+ from angr.ailment.block import Block
13
+ from angr.ailment.statement import Statement, ConditionalJump, Jump, Label, Return
14
+ from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression, BinaryOp
15
+
16
+ from angr.utils.graph import GraphUtils
17
+ from angr.utils.ail import is_phi_assignment, is_head_controlled_loop_block
18
+ from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
19
+ from angr.utils.constants import SWITCH_MISSING_DEFAULT_NODE_ADDR
20
+ from angr.utils.graph import dominates, to_acyclic_graph, dfs_back_edges
21
+ from angr.analyses.decompiler.sequence_walker import SequenceWalker
22
+ from angr.analyses.decompiler.utils import (
23
+ remove_last_statement,
24
+ remove_last_statements,
25
+ extract_jump_targets,
26
+ switch_extract_cmp_bounds,
27
+ switch_extract_cmp_bounds_from_condition,
28
+ is_empty_or_label_only_node,
29
+ has_nonlabel_nonphi_statements,
30
+ first_nonlabel_nonphi_statement,
31
+ switch_extract_bitwiseand_jumptable_info,
32
+ switch_extract_switch_expr_from_jump_target,
33
+ )
34
+ from angr.analyses.decompiler.counters.call_counter import AILCallCounter
35
+ from angr.analyses.decompiler.node_replacer import NodeReplacer
36
+ from .structurer_nodes import (
37
+ ConditionNode,
38
+ SequenceNode,
39
+ LoopNode,
40
+ ConditionalBreakNode,
41
+ BreakNode,
42
+ ContinueNode,
43
+ BaseNode,
44
+ MultiNode,
45
+ SwitchCaseNode,
46
+ IncompleteSwitchCaseNode,
47
+ EmptyBlockNotice,
48
+ IncompleteSwitchCaseHeadStatement,
49
+ )
50
+ from .structurer_base import StructurerBase
51
+
52
+ if TYPE_CHECKING:
53
+ from angr.knowledge_plugins.functions import Function
54
+
55
+ l = logging.getLogger(__name__)
56
+ _DEBUG = False
57
+
58
+
59
+ class GraphChangedNotification(Exception):
60
+ """
61
+ A notification for graph that is currently worked on being changed. Once this notification is caught, the graph
62
+ schema matching process for the current region restarts.
63
+ """
64
+
65
+
66
+ class MultiStmtExprMode(str, Enum):
67
+ """
68
+ Mode of multi-statement expression creation during structuring.
69
+ """
70
+
71
+ NEVER = "Never"
72
+ ALWAYS = "Always"
73
+ MAX_ONE_CALL = "Only when less than one call"
74
+
75
+
76
+ class GraphEdgeFilter:
77
+ """
78
+ Filters away edges in a graph that are marked as deleted (outgoing-edges) during cyclic refinement.
79
+ """
80
+
81
+ def __init__(self, graph: networkx.DiGraph):
82
+ self.graph = graph
83
+
84
+ def __call__(self, src, dst) -> bool:
85
+ d = self.graph[src][dst]
86
+ return not d.get("cyclic_refinement_outgoing", False)
87
+
88
+
89
+ def _f(graph: networkx.DiGraph):
90
+ return networkx.subgraph_view(graph, filter_edge=GraphEdgeFilter(graph))
91
+
92
+
93
+ class PhoenixStructurer(StructurerBase):
94
+ """
95
+ Structure a region using a structuring algorithm that is similar to the one in Phoenix decompiler (described in the
96
+ "phoenix decompiler" paper). Note that this implementation has quite a few improvements over the original described
97
+ version and *should not* be used to evaluate the performance of the original algorithm described in that paper.
98
+ """
99
+
100
+ NAME = "phoenix"
101
+
102
+ def __init__(
103
+ self,
104
+ region,
105
+ parent_map=None,
106
+ condition_processor=None,
107
+ func: Function | None = None,
108
+ case_entry_to_switch_head: dict[int, int] | None = None,
109
+ parent_region=None,
110
+ improve_algorithm=False,
111
+ use_multistmtexprs: MultiStmtExprMode = MultiStmtExprMode.MAX_ONE_CALL,
112
+ multistmtexpr_stmt_threshold: int = 5,
113
+ **kwargs,
114
+ ):
115
+ super().__init__(
116
+ region,
117
+ parent_map=parent_map,
118
+ condition_processor=condition_processor,
119
+ func=func,
120
+ case_entry_to_switch_head=case_entry_to_switch_head,
121
+ parent_region=parent_region,
122
+ **kwargs,
123
+ )
124
+
125
+ # whitelist certain edges. removing these edges will destroy critical schemas, which will impact future
126
+ # structuring cycles.
127
+ # the set is populated during the analysis. _last_resort_refinement() will ensure not to remove any edges
128
+ # who fall into these sets.
129
+ self.whitelist_edges: set[tuple[int, int]] = set()
130
+ # also whitelist certain nodes that are definitely header for switch-case constructs. they should not be merged
131
+ # into another node before we successfully structure the entire switch-case.
132
+ self.switch_case_known_heads: set[Block | BaseNode] = set()
133
+
134
+ # whitelist certain nodes that should be treated as a tail node for do-whiles. these nodes should not be
135
+ # absorbed into other SequenceNodes
136
+ self.dowhile_known_tail_nodes: set = set()
137
+
138
+ # in reimplementing the core phoenix algorithm from the phoenix decompiler paper, two types of changes were
139
+ # made to the algorithm:
140
+ # 1. Mandatory fixes to correct flaws we found in the algorithm
141
+ # 2. Optional fixes to improve the results of already correct choices
142
+ #
143
+ # the improve_algorithm flag controls whether the optional fixes are applied. these are disabled by default
144
+ # to be as close to the original algorithm as possible. for best results, enable this flag.
145
+ self._improve_algorithm = improve_algorithm
146
+ self._edge_virtualization_hints = []
147
+
148
+ # for each region, we only convert a switch-case head into an IncompleteSwitchCaseNode once. this is to avoid
149
+ # loops of creating and unpacking IncompleteSwitchCaseNode (when the entire switch-case construct is not yet
150
+ # ready to be structured, e.g., default node has a successor A and all case nodes have a successor B).
151
+ # TestDecompiler.test_decompiling_abnormal_switch_case_within_a_loop_with_redundant_jump captures this case.
152
+ self._matched_incomplete_switch_case_addrs: set[int] = set()
153
+
154
+ # node_order keeps a dictionary of nodes and their order in a quasi-topological sort of the region full graph
155
+ # (graph_with_successors). _generate_node_order() initializes this dictionary. we then update this dictionary
156
+ # when new nodes are created. we do not populate this dictionary when working on acyclic graphs because it's
157
+ # not used for acyclic graphs.
158
+ self._node_order: dict[Any, int] | None = None
159
+
160
+ self._use_multistmtexprs = use_multistmtexprs
161
+ self._multistmtexpr_stmt_threshold = multistmtexpr_stmt_threshold
162
+ self._analyze()
163
+
164
+ @staticmethod
165
+ def _assert_graph_ok(g, msg: str) -> None:
166
+ if _DEBUG:
167
+ if g is None:
168
+ return
169
+ assert (
170
+ len(list(networkx.connected_components(networkx.Graph(g)))) <= 1
171
+ ), f"{msg}: More than one connected component. Please report this."
172
+ assert (
173
+ len([nn for nn in g if g.in_degree[nn] == 0]) <= 1
174
+ ), f"{msg}: More than one graph entrance. Please report this."
175
+
176
+ def _analyze(self):
177
+ # iterate until there is only one node in the region
178
+
179
+ self._assert_graph_ok(self._region.graph, "Incorrect region graph")
180
+
181
+ has_cycle = self._has_cycle()
182
+
183
+ # special handling for single-node loops
184
+ if len(self._region.graph.nodes) == 1 and has_cycle:
185
+ self._analyze_cyclic()
186
+
187
+ # backup the region prior to conducting a cyclic refinement because we may not be able to structure a cycle out
188
+ # of the refined graph. in that case, we restore the original region and return.
189
+ pre_refinement_region = None
190
+
191
+ while len(self._region.graph.nodes) > 1:
192
+ progressed = self._analyze_acyclic()
193
+ if progressed and self._region.head not in self._region.graph:
194
+ # update the head
195
+ self._region.head = next(
196
+ iter(node for node in self._region.graph.nodes if node.addr == self._region.head.addr)
197
+ )
198
+
199
+ if has_cycle:
200
+ progressed |= self._analyze_cyclic()
201
+ if progressed:
202
+ pre_refinement_region = None
203
+ if self._region.head not in self._region.graph:
204
+ # update the loop head
205
+ self._region.head = next(
206
+ iter(node for node in self._region.graph.nodes if node.addr == self._region.head.addr)
207
+ )
208
+ elif pre_refinement_region is None:
209
+ pre_refinement_region = self._region.copy()
210
+ refined = self._refine_cyclic()
211
+ if refined:
212
+ if self._region.head not in self._region.graph:
213
+ # update the loop head
214
+ self._region.head = next(
215
+ iter(node for node in self._region.graph.nodes if node.addr == self._region.head.addr)
216
+ )
217
+ has_cycle = self._has_cycle()
218
+ continue
219
+ has_cycle = self._has_cycle()
220
+
221
+ if not progressed:
222
+ if self._region.cyclic_ancestor and not self._region.cyclic:
223
+ # we prefer directly returning this subgraph in case it can be further restructured within a loop
224
+ # region
225
+ l.debug("No progress is made on this acyclic graph with a cyclic ancestor. Give up.")
226
+ break
227
+
228
+ l.debug("No progress is made. Enter last resort refinement.")
229
+ removed_edge = self._last_resort_refinement(
230
+ self._region.head,
231
+ self._region.graph,
232
+ (
233
+ self._region.graph_with_successors
234
+ if self._region.graph_with_successors is not None
235
+ else networkx.DiGraph(self._region.graph)
236
+ ),
237
+ )
238
+ self._assert_graph_ok(self._region.graph, "Last resort refinement went wrong")
239
+ if not removed_edge:
240
+ # cannot make any progress in this region. return the subgraph directly
241
+ break
242
+
243
+ if len(self._region.graph.nodes) == 1:
244
+ # successfully structured
245
+ self.result = next(iter(self._region.graph.nodes))
246
+ else:
247
+ if pre_refinement_region is not None:
248
+ # we could not make a loop after the last cycle refinement. restore the graph
249
+ l.debug("Could not structure the cyclic graph. Restoring the region to the pre-refinement state.")
250
+ self._region = pre_refinement_region
251
+
252
+ self.result = None # the actual result is in self._region.graph and self._region.graph_with_successors
253
+
254
+ def _analyze_cyclic(self) -> bool:
255
+ any_matches = False
256
+
257
+ if self._node_order is None:
258
+ self._generate_node_order()
259
+ acyclic_graph = to_acyclic_graph(_f(self._region.graph), node_order=self._node_order)
260
+ for node in list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, self._region.head)):
261
+ if node not in self._region.graph:
262
+ continue
263
+ matched = self._match_cyclic_schemas(
264
+ node,
265
+ self._region.head,
266
+ self._region.graph,
267
+ (
268
+ self._region.graph_with_successors
269
+ if self._region.graph_with_successors is not None
270
+ else networkx.DiGraph(self._region.graph)
271
+ ),
272
+ )
273
+ l.debug("... matching cyclic schemas: %s at %r", matched, node)
274
+ any_matches |= matched
275
+ if matched:
276
+ self._assert_graph_ok(self._region.graph, "Removed incorrect edges")
277
+ return any_matches
278
+
279
+ def _match_cyclic_schemas(self, node, head, graph, full_graph) -> bool:
280
+ matched, loop_node, successor_node = self._match_cyclic_while(node, head, graph, full_graph)
281
+ if matched:
282
+ assert loop_node is not None and successor_node is not None
283
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
284
+ self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
285
+ # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
286
+ self._rewrite_jumps_to_continues(loop_node.sequence_node)
287
+ return True
288
+
289
+ matched, loop_node, successor_node = self._match_cyclic_dowhile(node, head, graph, full_graph)
290
+ if matched:
291
+ assert loop_node is not None and successor_node is not None
292
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
293
+ self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
294
+ # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
295
+ self._rewrite_jumps_to_continues(loop_node.sequence_node, loop_node=loop_node)
296
+ return True
297
+
298
+ if self._improve_algorithm:
299
+ matched, loop_node, successor_node = self._match_cyclic_while_with_single_successor(
300
+ node, head, graph, full_graph
301
+ )
302
+ if matched:
303
+ assert loop_node is not None and successor_node is not None
304
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
305
+ self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
306
+ # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
307
+ self._rewrite_jumps_to_continues(loop_node.sequence_node)
308
+ return True
309
+
310
+ matched, loop_node, successor_node = self._match_cyclic_natural_loop(node, head, graph, full_graph)
311
+ if matched:
312
+ assert loop_node is not None
313
+ if successor_node is not None:
314
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
315
+ self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
316
+ elif self._region.successors is not None and len(self._region.successors) == 1:
317
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
318
+ self._rewrite_conditional_jumps_to_breaks(
319
+ loop_node.sequence_node, [succ.addr for succ in self._region.successors]
320
+ )
321
+ # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
322
+ self._rewrite_jumps_to_continues(loop_node.sequence_node)
323
+ return matched
324
+
325
+ def _match_cyclic_while(
326
+ self, node, head, graph_raw, full_graph_raw
327
+ ) -> tuple[bool, LoopNode | None, BaseNode | None]:
328
+ full_graph = _f(full_graph_raw)
329
+
330
+ succs = list(full_graph_raw.successors(node))
331
+ if len(succs) == 2:
332
+ left, right = succs
333
+
334
+ if full_graph_raw.has_edge(right, node) and not full_graph_raw.has_edge(left, node):
335
+ left, right = right, left
336
+ if left is node:
337
+ # self loop
338
+ # possible candidate
339
+ head_block_idx, _, head_block = self._find_node_going_to_dst(node, left)
340
+ if head_block is None:
341
+ # it happens. for example:
342
+ # ## Block 4058c8
343
+ # 00 | 0x4058c8 | if ((rcx<8> == 0x0<64>)) { Goto 0x4058ca<64> } else { Goto None }
344
+ # 01 | 0x4058c8 | rcx<8> = (rcx<8> - 0x1<64>)
345
+ # 02 | 0x4058c8 | cc_dep1<8> = Conv(8->64, Load(addr=rsi<8>, size=1, endness=Iend_LE))
346
+ # 03 | 0x4058c8 | cc_dep2<8> = Conv(8->64, Load(addr=rdi<8>, size=1, endness=Iend_LE))
347
+ # 04 | 0x4058c8 | rdi<8> = (rdi<8> + d<8>)
348
+ # 05 | 0x4058c8 | rsi<8> = (rsi<8> + d<8>)
349
+ # 06 | 0x4058c8 | if ((Conv(64->8, cc_dep1<8>) == Conv(64->8, cc_dep2<8>))) { Goto 0x4058c8<64> }
350
+ # else { Goto None }
351
+ # 07 | 0x4058c8 | Goto(0x4058ca<64>)
352
+ head_block_idx, _, head_block = self._find_node_going_to_dst(node, right)
353
+
354
+ if (
355
+ isinstance(head_block, MultiNode)
356
+ and head_block.nodes
357
+ and isinstance(head_block.nodes[0], Block)
358
+ and head_block.nodes[0].statements
359
+ and is_head_controlled_loop_block(head_block.nodes[0])
360
+ ) or (
361
+ isinstance(head_block, Block)
362
+ and head_block.statements
363
+ and is_head_controlled_loop_block(head_block)
364
+ ):
365
+ # it's a while loop if the conditional jump (or the head block) is at the beginning of node
366
+ loop_type = "while" if head_block_idx == 0 else "do-while"
367
+ # otherwise it's a do-while loop
368
+ if self.cond_proc.have_opposite_edge_conditions(full_graph_raw, head_block, left, right):
369
+ # c = !c
370
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph_raw, head_block, left)
371
+ if head_block_idx == 0:
372
+ self._remove_first_statement_if_jump(head_block)
373
+ else:
374
+ remove_last_statement(head_block)
375
+ seq_node = SequenceNode(node.addr, nodes=[node]) if not isinstance(node, SequenceNode) else node
376
+ loop_node = LoopNode(loop_type, edge_cond_left, seq_node, addr=seq_node.addr)
377
+ self.replace_nodes(graph_raw, node, loop_node, self_loop=False, drop_refinement_marks=True)
378
+ self.replace_nodes(
379
+ full_graph_raw,
380
+ node,
381
+ loop_node,
382
+ self_loop=False,
383
+ update_node_order=True,
384
+ drop_refinement_marks=True,
385
+ )
386
+
387
+ # ensure the loop has only one successor: the right node
388
+ self._remove_edges_except(graph_raw, loop_node, right)
389
+ self._remove_edges_except(full_graph_raw, loop_node, right)
390
+
391
+ return True, loop_node, right
392
+ elif (
393
+ full_graph_raw.has_edge(left, node)
394
+ and left is not head
395
+ and full_graph_raw.in_degree[left] == 1
396
+ and full_graph.out_degree[left] == 1
397
+ and not full_graph_raw.has_edge(right, node)
398
+ ):
399
+ # possible candidate
400
+ _, _, head_block = self._find_node_going_to_dst(node, left, condjump_only=True)
401
+ if head_block is not None:
402
+ if self.cond_proc.have_opposite_edge_conditions(full_graph_raw, head_block, left, right):
403
+ # c = !c
404
+ if PhoenixStructurer._is_single_statement_block(node):
405
+ # the single-statement-block check is to ensure we don't execute any code before the
406
+ # conditional jump. this way the entire node can be dropped.
407
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph_raw, head_block, left)
408
+ new_node = SequenceNode(node.addr, nodes=[left])
409
+ loop_node = LoopNode("while", edge_cond_left, new_node, addr=node.addr)
410
+
411
+ # on the original graph
412
+ self.replace_nodes(
413
+ graph_raw, node, loop_node, old_node_1=left, self_loop=False, drop_refinement_marks=True
414
+ )
415
+ # on the graph with successors
416
+ self.replace_nodes(
417
+ full_graph_raw,
418
+ node,
419
+ loop_node,
420
+ old_node_1=left,
421
+ self_loop=False,
422
+ update_node_order=True,
423
+ )
424
+
425
+ # ensure the loop has only one successor: the right node
426
+ self._remove_edges_except(graph_raw, loop_node, right)
427
+ self._remove_edges_except(full_graph_raw, loop_node, right)
428
+
429
+ return True, loop_node, right
430
+ # we generate a while-true loop instead
431
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph_raw, head_block, right)
432
+ last_stmt = self._remove_last_statement_if_jump(head_block)
433
+ assert last_stmt is not None
434
+ cond_jump = Jump(
435
+ None,
436
+ Const(None, None, right.addr, self.project.arch.bits),
437
+ None,
438
+ ins_addr=last_stmt.tags["ins_addr"],
439
+ )
440
+ jump_node = Block(last_stmt.tags["ins_addr"], None, statements=[cond_jump])
441
+ cond_jump_node = ConditionNode(last_stmt.tags["ins_addr"], None, edge_cond_right, jump_node)
442
+ new_node = SequenceNode(node.addr, nodes=[node, cond_jump_node, left])
443
+ loop_node = LoopNode("while", claripy.true(), new_node, addr=node.addr)
444
+
445
+ # on the original graph
446
+ self.replace_nodes(
447
+ graph_raw, node, loop_node, old_node_1=left, self_loop=False, drop_refinement_marks=True
448
+ )
449
+ # on the graph with successors
450
+ self.replace_nodes(
451
+ full_graph_raw,
452
+ node,
453
+ loop_node,
454
+ old_node_1=left,
455
+ self_loop=False,
456
+ update_node_order=True,
457
+ drop_refinement_marks=True,
458
+ )
459
+
460
+ # ensure the loop has only one successor: the right node
461
+ self._remove_edges_except(graph_raw, loop_node, right)
462
+ self._remove_edges_except(full_graph_raw, loop_node, right)
463
+
464
+ return True, loop_node, right
465
+
466
+ if self._improve_algorithm and full_graph.out_degree[node] == 1:
467
+ # while (true) { ...; if (...) break; }
468
+ _, _, head_block = self._find_node_going_to_dst(node, left, condjump_only=True)
469
+ if head_block is not None:
470
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, head_block, left, right):
471
+ # c = !c
472
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
473
+ self._remove_last_statement_if_jump(head_block)
474
+ cond_break = ConditionalBreakNode(node.addr, edge_cond_right, right.addr)
475
+ new_node = SequenceNode(node.addr, nodes=[node, cond_break, left])
476
+ loop_node = LoopNode("while", claripy.true(), new_node, addr=node.addr)
477
+
478
+ # on the original graph
479
+ self.replace_nodes(
480
+ graph_raw, node, loop_node, old_node_1=left, self_loop=False, drop_refinement_marks=True
481
+ )
482
+ # on the graph with successors
483
+ self.replace_nodes(
484
+ full_graph_raw,
485
+ node,
486
+ loop_node,
487
+ old_node_1=left,
488
+ self_loop=False,
489
+ update_node_order=True,
490
+ drop_refinement_marks=True,
491
+ )
492
+
493
+ # ensure the loop has only one successor: the right node
494
+ self._remove_edges_except(graph_raw, loop_node, right)
495
+ self._remove_edges_except(full_graph_raw, loop_node, right)
496
+
497
+ return True, loop_node, right
498
+
499
+ return False, None, None
500
+
501
+ def _match_cyclic_while_with_single_successor(
502
+ self, node, head, graph_raw, full_graph_raw
503
+ ) -> tuple[bool, LoopNode | None, BaseNode | None]:
504
+ if self._region.successors:
505
+ return False, None, None
506
+ if node is not head:
507
+ return False, None, None
508
+
509
+ full_graph = full_graph_raw
510
+ graph = graph_raw
511
+
512
+ if not (node is head or graph.in_degree[node] == 2):
513
+ return False, None, None
514
+
515
+ loop_cond = None
516
+ successor_node = None
517
+ succ_node_is_true_node = None
518
+ if (
519
+ isinstance(node, SequenceNode)
520
+ and node.nodes
521
+ and isinstance(node.nodes[-1], ConditionNode)
522
+ # must ensure the nodes before the condition node are empty. otherwise the condition may use variables that
523
+ # are updated in these nodes leading up to the condition node.
524
+ and all(not self.has_nonlabel_nonphi_statements(nn) for nn in node.nodes[:-1])
525
+ and node.nodes[-1].true_node is not None
526
+ and node.nodes[-1].false_node is not None
527
+ ):
528
+ # try both true node and false node; pick the first node with only Returns as last statements as the
529
+ # successor.
530
+ if self._cyclic_while_with_single_successor_must_return(node.nodes[-1].true_node):
531
+ succ_node_is_true_node = True
532
+ successor_node = node.nodes[-1].true_node
533
+ loop_cond = claripy.Not(node.nodes[-1].condition)
534
+ elif self._cyclic_while_with_single_successor_must_return(node.nodes[-1].false_node):
535
+ succ_node_is_true_node = False
536
+ successor_node = node.nodes[-1].false_node
537
+ loop_cond = node.nodes[-1].condition
538
+ else:
539
+ loop_cond = None
540
+
541
+ if loop_cond is None:
542
+ return False, None, None
543
+
544
+ node_copy = node.copy()
545
+ # replace the last node with the intended successor node
546
+ node_copy.nodes[-1] = (
547
+ node_copy.nodes[-1].false_node if succ_node_is_true_node else node_copy.nodes[-1].true_node
548
+ )
549
+ # check if there is a cycle that starts with node and ends with node
550
+ next_node = node
551
+ seq_node = SequenceNode(node.addr, nodes=[node_copy])
552
+ seen_nodes = set()
553
+ while True:
554
+ succs = list(full_graph.successors(next_node))
555
+ if len(succs) != 1:
556
+ return False, None, None
557
+ next_node = succs[0]
558
+
559
+ if next_node is node:
560
+ break
561
+ if next_node is not node and next_node in seen_nodes:
562
+ return False, None, None
563
+
564
+ seen_nodes.add(next_node)
565
+ seq_node.nodes.append(next_node)
566
+
567
+ loop_node = LoopNode("while", loop_cond, seq_node, addr=node.addr)
568
+
569
+ # on the original graph
570
+ for node_ in seq_node.nodes:
571
+ if node_ is not node_copy:
572
+ graph_raw.remove_node(node_)
573
+ self.replace_nodes(graph_raw, node, loop_node, self_loop=False, drop_refinement_marks=True)
574
+ graph_raw.add_edge(loop_node, successor_node)
575
+
576
+ # on the graph with successors
577
+ for node_ in seq_node.nodes:
578
+ if node_ is not node_copy:
579
+ full_graph_raw.remove_node(node_)
580
+ self.replace_nodes(
581
+ full_graph_raw, node, loop_node, self_loop=False, update_node_order=True, drop_refinement_marks=True
582
+ )
583
+ full_graph_raw.add_edge(loop_node, successor_node)
584
+
585
+ if self._node_order is not None:
586
+ self._node_order[loop_node] = self._node_order[node]
587
+ self._node_order[successor_node] = self._node_order[loop_node]
588
+
589
+ return True, loop_node, successor_node
590
+
591
+ def _cyclic_while_with_single_successor_must_return(self, successor_node: SequenceNode) -> bool:
592
+ try:
593
+ last_stmts = self.cond_proc.get_last_statements(successor_node)
594
+ except EmptyBlockNotice:
595
+ return False
596
+ if not last_stmts:
597
+ return False
598
+ return all(isinstance(stmt, Return) for stmt in last_stmts)
599
+
600
+ def _match_cyclic_dowhile(
601
+ self, node, head, graph_raw, full_graph_raw
602
+ ) -> tuple[bool, LoopNode | None, BaseNode | None]:
603
+ full_graph = _f(full_graph_raw)
604
+
605
+ preds_raw = list(full_graph_raw.predecessors(node))
606
+ succs_raw = list(full_graph_raw.successors(node))
607
+ succs = list(full_graph.successors(node))
608
+
609
+ if ((node is head and len(preds_raw) >= 1) or len(preds_raw) >= 2) and len(succs) == 1:
610
+ succ = succs[0]
611
+ succ_preds = list(full_graph.predecessors(succ))
612
+ succ_succs = list(full_graph.successors(succ))
613
+ if head is not succ and len(succ_succs) == 2 and node in succ_succs and len(succ_preds) == 1:
614
+ succ_succs.remove(node)
615
+ out_node = succ_succs[0]
616
+
617
+ if (len(succs_raw) == 1 or (len(succs_raw) == 2 and out_node in succs_raw)) and full_graph.has_edge(
618
+ succ, node
619
+ ):
620
+ # possible candidate
621
+ _, _, succ_block = self._find_node_going_to_dst(succ, out_node, condjump_only=True)
622
+ if succ_block is not None:
623
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, succ_block, node, out_node):
624
+ # c = !c
625
+ edge_cond_succhead = self.cond_proc.recover_edge_condition(full_graph, succ_block, node)
626
+ self._remove_last_statement_if_jump(succ)
627
+ drop_succ = False
628
+
629
+ # absorb the entire succ block if possible
630
+ if (
631
+ self._improve_algorithm
632
+ and self._is_sequential_statement_block(succ)
633
+ and self._should_use_multistmtexprs(succ)
634
+ ):
635
+ stmts = self._build_multistatementexpr_statements(succ)
636
+ assert stmts is not None
637
+ if (
638
+ stmts
639
+ and sum(1 for stmt in stmts if not isinstance(stmt, Label))
640
+ <= self._multistmtexpr_stmt_threshold
641
+ ):
642
+ edge_cond_succhead = MultiStatementExpression(
643
+ None,
644
+ stmts,
645
+ self.cond_proc.convert_claripy_bool_ast(edge_cond_succhead),
646
+ ins_addr=succ.addr,
647
+ )
648
+ drop_succ = True
649
+
650
+ new_node = SequenceNode(node.addr, nodes=[node] if drop_succ else [node, succ])
651
+ loop_node = LoopNode("do-while", edge_cond_succhead, new_node, addr=node.addr)
652
+
653
+ # on the original graph
654
+ self.replace_nodes(
655
+ graph_raw, node, loop_node, old_node_1=succ, self_loop=False, drop_refinement_marks=True
656
+ )
657
+ # on the graph with successors
658
+ self.replace_nodes(
659
+ full_graph_raw,
660
+ node,
661
+ loop_node,
662
+ old_node_1=succ,
663
+ self_loop=False,
664
+ update_node_order=True,
665
+ drop_refinement_marks=True,
666
+ )
667
+
668
+ return True, loop_node, out_node
669
+ elif ((node is head and len(preds_raw) >= 1) or len(preds_raw) >= 2) and len(succs) == 2 and node in succs:
670
+ # head forms a self-loop
671
+ succs.remove(node)
672
+ succ = succs[0]
673
+ if not full_graph.has_edge(succ, node):
674
+ # possible candidate
675
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, node, node, succ):
676
+ # c = !c
677
+ edge_cond_head = self.cond_proc.recover_edge_condition(full_graph, node, node)
678
+ self._remove_last_statement_if_jump(node)
679
+ seq_node = SequenceNode(node.addr, nodes=[node]) if not isinstance(node, SequenceNode) else node
680
+ loop_node = LoopNode("do-while", edge_cond_head, seq_node, addr=seq_node.addr)
681
+
682
+ # on the original graph
683
+ self.replace_nodes(graph_raw, node, loop_node, self_loop=False, drop_refinement_marks=True)
684
+ # on the graph with successors
685
+ self.replace_nodes(
686
+ full_graph_raw,
687
+ node,
688
+ loop_node,
689
+ self_loop=False,
690
+ update_node_order=True,
691
+ drop_refinement_marks=True,
692
+ )
693
+
694
+ return True, loop_node, succ
695
+ return False, None, None
696
+
697
+ def _match_cyclic_natural_loop(
698
+ self, node, head, graph_raw, full_graph_raw
699
+ ) -> tuple[bool, LoopNode | None, BaseNode | None]:
700
+
701
+ full_graph = _f(full_graph_raw)
702
+ graph = _f(graph_raw)
703
+
704
+ if not (node is head or graph.in_degree[node] == 2):
705
+ return False, None, None
706
+
707
+ # check if there is a cycle that starts with node and ends with node
708
+ next_node = node
709
+ seq_node = SequenceNode(node.addr, nodes=[node])
710
+ seen_nodes = set()
711
+ loop_successor_candidates = set()
712
+ while True:
713
+ succs = list(full_graph.successors(next_node))
714
+ if len(succs) != 1:
715
+ return False, None, None
716
+ if full_graph.out_degree[next_node] > 1:
717
+ # all successors in the full graph should have been refined away at this point
718
+ return False, None, None
719
+
720
+ if full_graph_raw.out_degree[next_node] > 1:
721
+ for _, raw_succ, edge_data in full_graph_raw.out_edges(next_node, data=True):
722
+ if raw_succ is succs[0]:
723
+ continue
724
+ if edge_data.get("cyclic_refinement_outgoing", False) is True:
725
+ loop_successor_candidates.add(raw_succ)
726
+ else:
727
+ # bad node found
728
+ return False, None, None
729
+
730
+ next_node = succs[0]
731
+
732
+ if next_node is node:
733
+ break
734
+ if next_node is head:
735
+ # we don't want a loop with region head not as the first node of the body!
736
+ return False, None, None
737
+ if next_node is not node and next_node in seen_nodes:
738
+ return False, None, None
739
+
740
+ seen_nodes.add(next_node)
741
+ seq_node.nodes.append(next_node)
742
+
743
+ if len(loop_successor_candidates) > 1:
744
+ return False, None, None
745
+
746
+ loop_node = LoopNode("while", claripy.true(), seq_node, addr=node.addr)
747
+
748
+ # on the original graph
749
+ for node_ in seq_node.nodes:
750
+ if node_ is not node:
751
+ graph_raw.remove_node(node_)
752
+ self.replace_nodes(graph_raw, node, loop_node, self_loop=False, drop_refinement_marks=True)
753
+
754
+ # on the graph with successors
755
+ for node_ in seq_node.nodes:
756
+ if node_ is not node:
757
+ full_graph_raw.remove_node(node_)
758
+ self.replace_nodes(
759
+ full_graph_raw, node, loop_node, self_loop=False, update_node_order=True, drop_refinement_marks=True
760
+ )
761
+
762
+ successor = None if not loop_successor_candidates else next(iter(loop_successor_candidates))
763
+ if successor is not None:
764
+ if successor in graph:
765
+ graph_raw.add_edge(loop_node, successor)
766
+ if successor in full_graph:
767
+ full_graph_raw.add_edge(loop_node, successor)
768
+
769
+ return True, loop_node, successor
770
+
771
+ def _refine_cyclic(self) -> bool:
772
+ graph = _f(self._region.graph)
773
+ loop_heads = {t for _, t in dfs_back_edges(graph, self._region.head, visit_all_nodes=True)}
774
+ sorted_loop_heads = GraphUtils.quasi_topological_sort_nodes(graph, nodes=list(loop_heads))
775
+
776
+ for head in sorted_loop_heads:
777
+ l.debug("... refining cyclic at %r", head)
778
+ refined = self._refine_cyclic_core(head)
779
+ l.debug("... refined: %s", refined)
780
+ if refined:
781
+ self._assert_graph_ok(self._region.graph, "Refinement went wrong")
782
+ # cyclic refinement may create dangling nodes in the full graph
783
+ return True
784
+ return False
785
+
786
+ def _refine_cyclic_core(self, loop_head) -> bool:
787
+ graph_raw: networkx.DiGraph = self._region.graph
788
+ fullgraph_raw: networkx.DiGraph = (
789
+ self._region.graph_with_successors
790
+ if self._region.graph_with_successors is not None
791
+ else networkx.DiGraph(self._region.graph)
792
+ )
793
+
794
+ graph = _f(graph_raw)
795
+ fullgraph = _f(fullgraph_raw)
796
+
797
+ # check if there is an out-going edge from the loop head
798
+ head_succs = list(fullgraph.successors(loop_head))
799
+ successor = None # the loop successor
800
+ loop_type = None
801
+ # continue_node either the loop header for while(true) loops or the loop header predecessor for do-while loops
802
+ continue_node = loop_head
803
+
804
+ is_while, result_while = self._refine_cyclic_is_while_loop(graph, fullgraph, loop_head, head_succs)
805
+ is_dowhile, result_dowhile = self._refine_cyclic_is_dowhile_loop(graph, fullgraph, loop_head)
806
+
807
+ continue_edges: list[tuple[BaseNode, BaseNode]] = []
808
+ outgoing_edges: list = []
809
+
810
+ # gotta pick one!
811
+ # for now, we handle the most common case: both successors exist in the graph of the parent region, and
812
+ # one successor has a path to the other successor
813
+ if is_while and is_dowhile and self._parent_region is not None:
814
+ assert result_while is not None and result_dowhile is not None
815
+ succ_while = result_while[-1]
816
+ succ_dowhile = result_dowhile[-1]
817
+ if succ_while in self._parent_region.graph and succ_dowhile in self._parent_region.graph:
818
+ sorted_nodes = GraphUtils.quasi_topological_sort_nodes(
819
+ self._parent_region.graph, loop_heads=[self._parent_region.head]
820
+ )
821
+ succ_while_idx = sorted_nodes.index(succ_while)
822
+ succ_dowhile_idx = sorted_nodes.index(succ_dowhile)
823
+ if succ_dowhile_idx < succ_while_idx:
824
+ # pick do-while
825
+ is_while = False
826
+
827
+ if is_while:
828
+ assert result_while is not None
829
+ loop_type = "while"
830
+ continue_edges, outgoing_edges, continue_node, successor = result_while
831
+ elif is_dowhile:
832
+ assert result_dowhile is not None
833
+ loop_type = "do-while"
834
+ continue_edges, outgoing_edges, continue_node, successor = result_dowhile
835
+
836
+ if loop_type is None:
837
+ # natural loop. select *any* exit edge to determine the successor
838
+ is_natural, result_natural = self._refine_cyclic_make_natural_loop(graph, fullgraph, loop_head)
839
+ if not is_natural:
840
+ # cannot refine this loop
841
+ return False
842
+ assert result_natural is not None
843
+ continue_edges, outgoing_edges, successor = result_natural
844
+
845
+ if outgoing_edges:
846
+ # if there is a single successor, we convert all but the first one out-going edges into breaks;
847
+ # if there are multiple successors, and if the current region does not have a parent region, then we
848
+ # convert all but the first successor-targeting out-going edges into gotos;
849
+ # otherwise we give up.
850
+
851
+ if self._parent_region is not None and len({dst for _, dst in outgoing_edges}) > 1:
852
+ # give up because there is a parent region
853
+ return False
854
+
855
+ # sanity check: if removing outgoing edges would create dangling nodes, then it means we are not ready for
856
+ # cyclic refinement yet.
857
+ outgoing_edges_by_dst = defaultdict(list)
858
+ for src, dst in outgoing_edges:
859
+ outgoing_edges_by_dst[dst].append(src)
860
+ for dst, srcs in outgoing_edges_by_dst.items():
861
+ if dst in graph and graph.in_degree[dst] == len(srcs):
862
+ return False
863
+
864
+ outgoing_edges = sorted(outgoing_edges, key=lambda edge: (edge[0].addr, edge[1].addr))
865
+
866
+ if successor is None:
867
+ successor_and_edgecounts = defaultdict(int)
868
+ for _, dst in outgoing_edges:
869
+ successor_and_edgecounts[dst] += 1
870
+
871
+ if len(successor_and_edgecounts) > 1:
872
+ # pick one successor with the highest edge count and (in case there are multiple successors with the
873
+ # same edge count) the lowest address
874
+ max_edgecount = max(successor_and_edgecounts.values())
875
+ successor_candidates = [
876
+ nn for nn, edgecount in successor_and_edgecounts.items() if edgecount == max_edgecount
877
+ ]
878
+ successor = next(iter(sorted(successor_candidates, key=lambda x: x.addr)))
879
+ else:
880
+ successor = next(iter(successor_and_edgecounts.keys()))
881
+
882
+ for src, dst in outgoing_edges:
883
+ if dst is successor:
884
+ # keep in mind that at this point, src might have been structured already. this means the last
885
+ # block in src may not be the actual block that has a direct jump or a conditional jump to dst. as
886
+ # a result, we should walk all blocks in src to find the jump to dst, then extract the condition
887
+ # and augment the corresponding block with a ConditionalBreak.
888
+ #
889
+ # don't enter loops because we can't rewrite a goto edge as break if the jump to the loop head is
890
+ # inside another loop
891
+ _, _, src_block = self._find_node_going_to_dst(src, dst, enter_loops=False)
892
+ if src_block is None:
893
+ # we can't find the source block, which is probably because the source block is within another
894
+ # loop. keep that goto and remove the edge anyway
895
+ fullgraph_raw[src][dst]["cyclic_refinement_outgoing"] = True
896
+ if graph.has_edge(src, dst):
897
+ graph_raw[src][dst]["cyclic_refinement_outgoing"] = True
898
+ elif not isinstance(src_block, (Block, MultiNode)):
899
+ # it has probably been structured into BreakNode or ConditionalBreakNode
900
+ # just remove the edge
901
+ fullgraph_raw[src][dst]["cyclic_refinement_outgoing"] = True
902
+ if graph.has_edge(src, dst):
903
+ graph_raw[src][dst]["cyclic_refinement_outgoing"] = True
904
+ else:
905
+ has_continue = False
906
+ # at the same time, examine if there is an edge that goes from src to the continue node. if so,
907
+ # we deal with it here as well.
908
+ continue_node_going_edge = src, continue_node
909
+ if continue_node_going_edge in continue_edges:
910
+ has_continue = True
911
+ # do not remove the edge from continue_edges since we want to process them later in this
912
+ # function.
913
+
914
+ # create the "break" node. in fact, we create a jump or a conditional jump, which will be
915
+ # rewritten to break nodes after (if possible). directly creating break nodes may lead to
916
+ # unwanted results, e.g., inserting a break (that's intended to break out of the loop) inside a
917
+ # switch-case that is nested within a loop.
918
+ last_src_stmt = self.cond_proc.get_last_statement(src_block)
919
+ assert last_src_stmt is not None
920
+ break_cond = self.cond_proc.recover_edge_condition(fullgraph, src_block, dst)
921
+ assert successor.addr is not None
922
+ if claripy.is_true(break_cond):
923
+ break_stmt = Jump(
924
+ None,
925
+ Const(None, None, successor.addr, self.project.arch.bits),
926
+ target_idx=successor.idx if isinstance(successor, Block) else None,
927
+ ins_addr=last_src_stmt.tags["ins_addr"],
928
+ )
929
+ break_node = Block(last_src_stmt.tags["ins_addr"], None, statements=[break_stmt])
930
+ else:
931
+ fallthrough_node = next(
932
+ iter(succ for succ in fullgraph.successors(src) if succ is not dst), None
933
+ )
934
+ if fallthrough_node is not None:
935
+ # we create a conditional jump that will be converted to a conditional break later
936
+ break_stmt = Jump(
937
+ None,
938
+ Const(None, None, successor.addr, self.project.arch.bits),
939
+ target_idx=successor.idx if isinstance(successor, Block) else None,
940
+ ins_addr=last_src_stmt.tags["ins_addr"],
941
+ )
942
+ break_node_inner = Block(last_src_stmt.tags["ins_addr"], None, statements=[break_stmt])
943
+ fallthrough_stmt = Jump(
944
+ None,
945
+ Const(None, None, fallthrough_node.addr, self.project.arch.bits),
946
+ target_idx=successor.idx if isinstance(successor, Block) else None,
947
+ ins_addr=last_src_stmt.tags["ins_addr"],
948
+ )
949
+ break_node_inner_fallthrough = Block(
950
+ last_src_stmt.tags["ins_addr"], None, statements=[fallthrough_stmt]
951
+ )
952
+ else:
953
+ # the fallthrough node does not exist in the graph. we create a conditional jump that
954
+ # jumps to an address
955
+ if not isinstance(last_src_stmt, ConditionalJump):
956
+ raise TypeError(f"Unexpected last_src_stmt type {type(last_src_stmt)}")
957
+ other_target = (
958
+ last_src_stmt.true_target
959
+ if isinstance(last_src_stmt.false_target, Const)
960
+ and last_src_stmt.false_target.value == successor.addr
961
+ else last_src_stmt.false_target
962
+ )
963
+ assert other_target is not None
964
+ break_stmt = Jump(
965
+ None,
966
+ Const(None, None, successor.addr, self.project.arch.bits),
967
+ target_idx=successor.idx if isinstance(successor, Block) else None,
968
+ ins_addr=last_src_stmt.tags["ins_addr"],
969
+ )
970
+ break_node_inner = Block(last_src_stmt.tags["ins_addr"], None, statements=[break_stmt])
971
+ fallthrough_stmt = Jump(
972
+ None,
973
+ other_target,
974
+ target_idx=successor.idx if isinstance(successor, Block) else None,
975
+ ins_addr=last_src_stmt.tags["ins_addr"],
976
+ )
977
+ break_node_inner_fallthrough = Block(
978
+ last_src_stmt.tags["ins_addr"], None, statements=[fallthrough_stmt]
979
+ )
980
+ break_node = ConditionNode(
981
+ last_src_stmt.tags["ins_addr"],
982
+ None,
983
+ break_cond,
984
+ break_node_inner,
985
+ false_node=break_node_inner_fallthrough,
986
+ )
987
+ new_src_block = self._copy_and_remove_last_statement_if_jump(src_block)
988
+ new_node = SequenceNode(src_block.addr, nodes=[new_src_block, break_node])
989
+ if has_continue:
990
+ assert continue_node is not None
991
+
992
+ if continue_node.addr is not None and self.is_a_jump_target(
993
+ last_src_stmt, continue_node.addr
994
+ ):
995
+ # instead of a conditional break node, we should insert a condition node instead
996
+ break_stmt = Jump(
997
+ None,
998
+ Const(None, None, successor.addr, self.project.arch.bits),
999
+ target_idx=successor.idx if isinstance(successor, Block) else None,
1000
+ ins_addr=last_src_stmt.tags["ins_addr"],
1001
+ )
1002
+ break_node = Block(last_src_stmt.tags["ins_addr"], None, statements=[break_stmt])
1003
+ cont_node = ContinueNode(
1004
+ last_src_stmt.tags["ins_addr"],
1005
+ Const(None, None, continue_node.addr, self.project.arch.bits),
1006
+ )
1007
+ cond_node = ConditionNode(
1008
+ last_src_stmt.tags["ins_addr"],
1009
+ None,
1010
+ break_cond,
1011
+ break_node,
1012
+ )
1013
+ new_node.nodes[-1] = cond_node
1014
+ new_node.nodes.append(cont_node)
1015
+
1016
+ # we don't remove the edge (src, continue_node) from the graph or full graph. we will
1017
+ # process them later in this function.
1018
+ else:
1019
+ # the last statement in src_block is not the conditional jump whose one branch goes to
1020
+ # the loop head. it probably goes to another block that ends up going to the loop head.
1021
+ # we don't handle it here.
1022
+ pass
1023
+
1024
+ # we cannot modify the original src_block because loop refinement may fail and we must restore
1025
+ # the original graph
1026
+ new_src = NodeReplacer(src, {src_block: new_node}).result
1027
+ if graph.has_edge(src, dst):
1028
+ graph_raw[src][dst]["cyclic_refinement_outgoing"] = True
1029
+ self.replace_nodes(graph_raw, src, new_src)
1030
+ fullgraph_raw[src][dst]["cyclic_refinement_outgoing"] = True
1031
+ self.replace_nodes(fullgraph_raw, src, new_src, update_node_order=True)
1032
+ if src is loop_head:
1033
+ loop_head = new_src
1034
+ if src is continue_node:
1035
+ continue_node = new_src
1036
+
1037
+ self._replace_node_in_edge_list(outgoing_edges, src, new_src)
1038
+ self._replace_node_in_edge_list(continue_edges, src, new_src)
1039
+
1040
+ else:
1041
+ self.virtualized_edges.add((src, dst))
1042
+ fullgraph_raw.remove_edge(src, dst)
1043
+ if graph.has_edge(src, dst):
1044
+ graph_raw.remove_edge(src, dst)
1045
+ if fullgraph.in_degree[dst] == 0:
1046
+ # drop this node
1047
+ fullgraph_raw.remove_node(dst)
1048
+ if self._region.successors and dst in self._region.successors:
1049
+ self._region.successors.remove(dst)
1050
+
1051
+ if len(continue_edges) > 1:
1052
+ # convert all but one (the one that is the farthest from the head, topological-wise) head-going edges into
1053
+ # continues
1054
+ sorted_nodes = GraphUtils.quasi_topological_sort_nodes(
1055
+ fullgraph, nodes=[src for src, _ in continue_edges], loop_heads=[loop_head]
1056
+ )
1057
+ src_to_ignore = sorted_nodes[-1]
1058
+ replacements = {}
1059
+
1060
+ for src, _ in continue_edges:
1061
+ if src is src_to_ignore:
1062
+ # this edge will be handled during loop structuring
1063
+ # mark it regardless
1064
+ continue
1065
+
1066
+ # just in case src has been replaced
1067
+ src = replacements.get(src, src)
1068
+
1069
+ # due to prior structuring of sub regions, the continue node may already be a Jump statement deep in
1070
+ # src at this point. we need to find the Jump statement and replace it.
1071
+ assert continue_node is not None
1072
+ # don't enter loops because we can't rewrite a goto edge as continue if the jump to the loop head is
1073
+ # inside another loop
1074
+ _, _, cont_block = self._find_node_going_to_dst(src, continue_node, enter_loops=False)
1075
+ if cont_block is None:
1076
+ # cont_block is not found. but it's ok. one possibility is that src is a jump table head with one
1077
+ # case being the loop head. in such cases, we can just remove the edge.
1078
+ if src.addr not in self.jump_tables:
1079
+ l.debug(
1080
+ "_refine_cyclic_core: Cannot find the block going to loop head for edge %r -> %r. "
1081
+ "Remove the edge anyway.",
1082
+ src,
1083
+ continue_node,
1084
+ )
1085
+ if graph.has_edge(src, continue_node):
1086
+ graph_raw.remove_edge(src, continue_node)
1087
+ fullgraph_raw.remove_edge(src, continue_node)
1088
+ else:
1089
+ # remove the edge.
1090
+ graph_raw.remove_edge(src, continue_node)
1091
+ fullgraph_raw.remove_edge(src, continue_node)
1092
+ # replace it with the original node plus the continue node
1093
+ try:
1094
+ last_stmt = self.cond_proc.get_last_statement(cont_block)
1095
+ except EmptyBlockNotice:
1096
+ # meh
1097
+ last_stmt = None
1098
+ if last_stmt is not None:
1099
+ new_cont_node = None
1100
+ if isinstance(last_stmt, ConditionalJump):
1101
+ new_cont_node = ContinueNode(last_stmt.tags["ins_addr"], continue_node.addr)
1102
+ if (
1103
+ isinstance(last_stmt.true_target, Const)
1104
+ and last_stmt.true_target.value == continue_node.addr
1105
+ ):
1106
+ new_cont_node = ConditionNode(
1107
+ last_stmt.tags["ins_addr"], None, last_stmt.condition, new_cont_node
1108
+ )
1109
+ else:
1110
+ new_cont_node = ConditionNode(
1111
+ last_stmt.tags["ins_addr"],
1112
+ None,
1113
+ UnaryOp(None, "Not", last_stmt.condition),
1114
+ new_cont_node,
1115
+ )
1116
+ elif isinstance(last_stmt, Jump):
1117
+ new_cont_node = ContinueNode(last_stmt.tags["ins_addr"], continue_node.addr)
1118
+
1119
+ if new_cont_node is not None and isinstance(cont_block, (Block, MultiNode)):
1120
+ new_cont_block = self._copy_and_remove_last_statement_if_jump(cont_block)
1121
+ new_node = NodeReplacer(src, {cont_block: new_cont_block}).result
1122
+ new_src = SequenceNode(new_node.addr, nodes=[new_node, new_cont_node])
1123
+ replacements[src] = new_src
1124
+ self.replace_nodes(graph_raw, src, new_src)
1125
+ self.replace_nodes(fullgraph_raw, src, new_src, update_node_order=True)
1126
+
1127
+ if continue_node is src:
1128
+ # set continue_node to the new node
1129
+ continue_node = new_src
1130
+
1131
+ if loop_type == "do-while":
1132
+ self.dowhile_known_tail_nodes.add(continue_node)
1133
+
1134
+ return bool(outgoing_edges or len(continue_edges) > 1)
1135
+
1136
+ @staticmethod
1137
+ def _refine_cyclic_determine_loop_body(graph, fullgraph, loop_head, successor=None) -> set[BaseNode]:
1138
+ # determine the loop body: all nodes that have paths going to loop_head
1139
+ loop_body = {loop_head}
1140
+ for node in networkx.descendants(fullgraph, loop_head):
1141
+ if node in graph and networkx.has_path(graph, node, loop_head):
1142
+ loop_body.add(node)
1143
+
1144
+ # extend the loop body if possible
1145
+ while True:
1146
+ loop_body_updated = False
1147
+ for node in list(loop_body):
1148
+ new_nodes = set()
1149
+ succ_not_in_loop_body = False
1150
+ for succ in fullgraph.successors(node):
1151
+ if successor is not None and succ is successor:
1152
+ continue
1153
+ if succ not in loop_body and succ in graph and fullgraph.out_degree[succ] <= 1:
1154
+ if all(pred in loop_body for pred in fullgraph.predecessors(succ)):
1155
+ new_nodes.add(succ)
1156
+ else:
1157
+ # one of the predecessors of this successor is not in the loop body
1158
+ succ_not_in_loop_body = True
1159
+ if new_nodes and not succ_not_in_loop_body:
1160
+ loop_body |= new_nodes
1161
+ loop_body_updated = True
1162
+ if not loop_body_updated:
1163
+ break
1164
+
1165
+ return loop_body
1166
+
1167
+ @staticmethod
1168
+ def _refine_cyclic_is_while_loop_check_loop_head_successors(graph, head_succs) -> tuple[bool, Any]:
1169
+ assert len(head_succs) == 2
1170
+ a, b = head_succs
1171
+ a_in_graph = a in graph
1172
+ b_in_graph = b in graph
1173
+ if a_in_graph ^ b_in_graph:
1174
+ return True, b if a_in_graph else a
1175
+ return False, None
1176
+
1177
+ def _refine_cyclic_is_while_loop(
1178
+ self, graph, fullgraph, loop_head, head_succs
1179
+ ) -> tuple[bool, tuple[list, list, BaseNode, BaseNode] | None]:
1180
+ if len(head_succs) == 2:
1181
+ r, successor = self._refine_cyclic_is_while_loop_check_loop_head_successors(graph, head_succs)
1182
+ if r:
1183
+ # make sure the head_pred is not already structured
1184
+ _, _, head_block_0 = self._find_node_going_to_dst(loop_head, head_succs[0])
1185
+ _, _, head_block_1 = self._find_node_going_to_dst(loop_head, head_succs[1])
1186
+ if head_block_0 is head_block_1 and head_block_0 is not None:
1187
+ # there is an out-going edge from the loop head
1188
+ # virtualize all other edges
1189
+ continue_edges: list[tuple[BaseNode, BaseNode]] = []
1190
+ outgoing_edges = []
1191
+ # note that because we have determined that the loop is a while loop, outgoing_edges do not contain
1192
+ # edges that go from the loop head to the successor.
1193
+ for node in list(networkx.descendants(graph, loop_head)):
1194
+ succs = list(fullgraph.successors(node))
1195
+ if loop_head in succs:
1196
+ continue_edges.append((node, loop_head))
1197
+ outside_succs = [succ for succ in succs if succ not in graph]
1198
+ for outside_succ in outside_succs:
1199
+ outgoing_edges.append((node, outside_succ))
1200
+
1201
+ continue_edges = sorted(continue_edges, key=lambda edge: (edge[0].addr, edge[1].addr))
1202
+ outgoing_edges = sorted(outgoing_edges, key=lambda edge: (edge[0].addr, edge[1].addr))
1203
+ return True, (continue_edges, outgoing_edges, loop_head, successor)
1204
+ return False, None
1205
+
1206
+ def _refine_cyclic_is_dowhile_loop(
1207
+ self, graph, fullgraph, loop_head
1208
+ ) -> tuple[bool, tuple[list, list, BaseNode, BaseNode] | None]:
1209
+ # check if there is an out-going edge from the loop tail
1210
+ head_preds = list(fullgraph.predecessors(loop_head))
1211
+ if len(head_preds) == 1:
1212
+ head_pred = head_preds[0]
1213
+ head_pred_succs = list(fullgraph.successors(head_pred))
1214
+ if len(head_pred_succs) == 2:
1215
+ successor = next(iter(nn for nn in head_pred_succs if nn is not loop_head))
1216
+ # make sure the head_pred is not already structured
1217
+ _, _, src_block_0 = self._find_node_going_to_dst(head_pred, loop_head)
1218
+ _, _, src_block_1 = self._find_node_going_to_dst(head_pred, successor)
1219
+ if src_block_0 is src_block_1 and src_block_0 is not None:
1220
+ continue_edges: list[tuple[BaseNode, BaseNode]] = []
1221
+ outgoing_edges = []
1222
+ # there is an out-going edge from the loop tail
1223
+ # virtualize all other edges
1224
+ continue_node = head_pred
1225
+ loop_body = PhoenixStructurer._refine_cyclic_determine_loop_body(
1226
+ graph, fullgraph, loop_head, successor=successor
1227
+ )
1228
+ for node in loop_body:
1229
+ if node is head_pred:
1230
+ continue
1231
+ succs = list(fullgraph.successors(node))
1232
+ if (
1233
+ head_pred in succs
1234
+ and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(fullgraph, node)
1235
+ ):
1236
+ # special case: if node is the header of a switch-case, then this is *not* a continue edge
1237
+ continue_edges.append((node, head_pred))
1238
+
1239
+ outside_succs = [succ for succ in succs if succ not in loop_body]
1240
+ for outside_succ in outside_succs:
1241
+ outgoing_edges.append((node, outside_succ))
1242
+
1243
+ continue_edges = sorted(continue_edges, key=lambda edge: (edge[0].addr, edge[1].addr))
1244
+ outgoing_edges = sorted(outgoing_edges, key=lambda edge: (edge[0].addr, edge[1].addr))
1245
+ return True, (continue_edges, outgoing_edges, continue_node, successor)
1246
+ return False, None
1247
+
1248
+ @staticmethod
1249
+ def _refine_cyclic_make_natural_loop(graph, fullgraph, loop_head) -> tuple[bool, tuple[list, list, Any] | None]:
1250
+ continue_edges = []
1251
+ outgoing_edges = []
1252
+
1253
+ loop_body = PhoenixStructurer._refine_cyclic_determine_loop_body(graph, fullgraph, loop_head)
1254
+
1255
+ # determine successor candidates using the loop body
1256
+ successor_candidates = set()
1257
+ for node in loop_body:
1258
+ for succ in fullgraph.successors(node):
1259
+ if succ not in loop_body:
1260
+ successor_candidates.add(succ)
1261
+
1262
+ # traverse the loop body to find all continue edges
1263
+ for node in loop_body:
1264
+ if graph.has_edge(node, loop_head):
1265
+ continue_edges.append((node, loop_head))
1266
+
1267
+ if len(successor_candidates) == 0:
1268
+ successor = None
1269
+ else:
1270
+ # one or multiple successors; try to pick a successor in graph, and prioritize the one with the lowest
1271
+ # address
1272
+ successor_candidates_in_graph = {nn for nn in successor_candidates if nn in graph}
1273
+ if successor_candidates_in_graph:
1274
+ # pick the one with the lowest address
1275
+ successor = next(iter(sorted(successor_candidates_in_graph, key=lambda x: x.addr)))
1276
+ else:
1277
+ successor = next(iter(sorted(successor_candidates, key=lambda x: x.addr)))
1278
+ # mark all edges as outgoing edges so they will be virtualized if they don't lead to the successor
1279
+ for node in successor_candidates:
1280
+ for pred in fullgraph.predecessors(node):
1281
+ if pred in graph:
1282
+ outgoing_edges.append((pred, node))
1283
+
1284
+ continue_edges = sorted(continue_edges, key=lambda edge: (edge[0].addr, edge[1].addr))
1285
+ outgoing_edges = sorted(outgoing_edges, key=lambda edge: (edge[0].addr, edge[1].addr))
1286
+ return True, (continue_edges, outgoing_edges, successor)
1287
+
1288
+ def _analyze_acyclic(self) -> bool:
1289
+ # match against known schemas
1290
+ l.debug("Matching acyclic schemas for region %r.", self._region)
1291
+
1292
+ any_matches = False
1293
+ idx = 0
1294
+ while True:
1295
+ l.debug("_match_acyclic_schemas: Iteration %d", idx)
1296
+ idx += 1
1297
+
1298
+ try:
1299
+ any_matches_this_iteration = self._match_acyclic_schemas(
1300
+ self._region.graph,
1301
+ (
1302
+ self._region.graph_with_successors
1303
+ if self._region.graph_with_successors is not None
1304
+ else networkx.DiGraph(self._region.graph)
1305
+ ),
1306
+ self._region.head,
1307
+ )
1308
+ except GraphChangedNotification:
1309
+ # restart
1310
+ l.debug("_match_acyclic_schemas: Graph changed. Restart.")
1311
+ idx = 0
1312
+ continue
1313
+ if not any_matches_this_iteration:
1314
+ break
1315
+ any_matches = True
1316
+
1317
+ # update the head if needed
1318
+ if self._region.head not in self._region.graph:
1319
+ # update the head
1320
+ self._region.head = next(
1321
+ iter(node for node in self._region.graph.nodes if node.addr == self._region.head.addr)
1322
+ )
1323
+
1324
+ return any_matches
1325
+
1326
+ def _match_acyclic_schemas(self, graph: networkx.DiGraph, full_graph: networkx.DiGraph, head) -> bool:
1327
+ # traverse the graph in reverse topological order
1328
+ any_matches = False
1329
+
1330
+ self._assert_graph_ok(self._region.graph, "Got a wrong graph to work on")
1331
+
1332
+ if graph.in_degree[head] == 0:
1333
+ acyclic_graph = graph
1334
+ else:
1335
+ acyclic_graph = networkx.DiGraph(graph)
1336
+ if len([node for node in acyclic_graph if acyclic_graph.in_degree[node] == 0]) == 0:
1337
+ acyclic_graph.remove_edges_from(graph.in_edges(head))
1338
+ self._assert_graph_ok(acyclic_graph, "Removed wrong edges")
1339
+
1340
+ for node in list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, head)):
1341
+ if node not in graph:
1342
+ continue
1343
+ if graph.has_edge(node, head):
1344
+ # it's a back edge
1345
+ l.debug("... %r -> %r is a back edge", node, head)
1346
+ continue
1347
+ l.debug("... matching acyclic switch-case constructs at %r", node)
1348
+ matched = self._match_acyclic_switch_cases(graph, full_graph, node)
1349
+ l.debug("... matched: %s", matched)
1350
+ any_matches |= matched
1351
+ if matched:
1352
+ break
1353
+ l.debug("... matching acyclic sequence at %r", node)
1354
+ matched = self._match_acyclic_sequence(graph, full_graph, node)
1355
+ l.debug("... matched: %s", matched)
1356
+ any_matches |= matched
1357
+ if matched:
1358
+ break
1359
+ l.debug("... matching acyclic ITE at %r", node)
1360
+ matched = self._match_acyclic_ite(graph, full_graph, node)
1361
+ l.debug("... matched: %s", matched)
1362
+ any_matches |= matched
1363
+ if matched:
1364
+ break
1365
+ if self._improve_algorithm:
1366
+ l.debug("... matching acyclic ITE with short-circuit conditions at %r", node)
1367
+ matched = self._match_acyclic_short_circuit_conditions(graph, full_graph, node)
1368
+ l.debug("... matched: %s", matched)
1369
+ any_matches |= matched
1370
+ if matched:
1371
+ break
1372
+
1373
+ self._assert_graph_ok(self._region.graph, "Removed incorrect edges")
1374
+
1375
+ return any_matches
1376
+
1377
+ # switch cases
1378
+
1379
+ def _match_acyclic_switch_cases(self, graph: networkx.DiGraph, full_graph: networkx.DiGraph, node) -> bool:
1380
+ if isinstance(node, SwitchCaseNode):
1381
+ return False
1382
+
1383
+ r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_default_node(node, graph, full_graph)
1384
+ if r:
1385
+ return r
1386
+
1387
+ if isinstance(node, IncompleteSwitchCaseNode):
1388
+ return False
1389
+
1390
+ r = self._match_acyclic_switch_cases_incomplete_switch_head(node, graph, full_graph)
1391
+ if r:
1392
+ return r
1393
+ r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(node, graph, full_graph)
1394
+ if r:
1395
+ return r
1396
+ r = self._match_acyclic_switch_cases_address_loaded_from_memory(node, graph, full_graph)
1397
+ if r:
1398
+ return r
1399
+ r = self._match_acyclic_switch_cases_address_computed(node, graph, full_graph)
1400
+ if r:
1401
+ return r
1402
+ return self._match_acyclic_incomplete_switch_cases(node, graph, full_graph)
1403
+
1404
+ def _match_acyclic_switch_cases_incomplete_switch_head(
1405
+ self, node, graph_raw: networkx.DiGraph, full_graph_raw: networkx.DiGraph
1406
+ ) -> bool:
1407
+ try:
1408
+ last_stmts = self.cond_proc.get_last_statements(node)
1409
+ except EmptyBlockNotice:
1410
+ return False
1411
+
1412
+ if len(last_stmts) != 1:
1413
+ return False
1414
+ last_stmt = last_stmts[0]
1415
+ if not isinstance(last_stmt, IncompleteSwitchCaseHeadStatement):
1416
+ return False
1417
+
1418
+ # make a fake jumptable
1419
+ node_default_addr = None
1420
+ case_entries: dict[int, int | tuple[int, int | None]] = {}
1421
+ for _, case_value, case_target_addr, case_target_idx, _ in last_stmt.case_addrs:
1422
+ if isinstance(case_value, str):
1423
+ if case_value == "default":
1424
+ node_default_addr = case_target_addr
1425
+ continue
1426
+ raise ValueError(f"Unsupported 'case_value' {case_value}")
1427
+ case_entries[case_value] = (case_target_addr, case_target_idx)
1428
+
1429
+ cases, node_default, to_remove = self._switch_build_cases(
1430
+ case_entries,
1431
+ node,
1432
+ node,
1433
+ node_default_addr,
1434
+ graph_raw,
1435
+ full_graph_raw,
1436
+ )
1437
+ if node_default_addr is not None and node_default is None:
1438
+ # the default node is not found. it's likely the node has been structured and is part of another construct
1439
+ # (e.g., inside another switch-case). we need to create a default node that jumps to the other node
1440
+ jmp_to_default_node = Jump(
1441
+ None,
1442
+ Const(None, None, node_default_addr, self.project.arch.bits),
1443
+ None,
1444
+ ins_addr=SWITCH_MISSING_DEFAULT_NODE_ADDR,
1445
+ )
1446
+ node_default = Block(SWITCH_MISSING_DEFAULT_NODE_ADDR, 0, statements=[jmp_to_default_node])
1447
+ graph_raw.add_edge(node, node_default)
1448
+ full_graph_raw.add_edge(node, node_default)
1449
+ if self._node_order is not None:
1450
+ self._node_order[node_default] = self._node_order[node]
1451
+ r = self._make_switch_cases_core(
1452
+ node,
1453
+ self.cond_proc.claripy_ast_from_ail_condition(last_stmt.switch_variable),
1454
+ cases,
1455
+ node_default_addr,
1456
+ node_default,
1457
+ last_stmt.tags["ins_addr"],
1458
+ to_remove,
1459
+ graph_raw,
1460
+ full_graph_raw,
1461
+ bail_on_nonhead_outedges=True,
1462
+ )
1463
+ if not r:
1464
+ return False
1465
+
1466
+ # special handling of duplicated default nodes
1467
+ if node_default is not None and self._region.graph.out_degree[node] > 1:
1468
+ other_out_nodes = list(self._region.graph.successors(node))
1469
+ for o in other_out_nodes:
1470
+ if o.addr == node_default.addr and o is not node_default:
1471
+ self._region.graph.remove_node(o)
1472
+ if self._region.graph_with_successors is not None:
1473
+ self._region.graph_with_successors.remove_node(o)
1474
+
1475
+ switch_end_addr = self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
1476
+ if switch_end_addr is not None:
1477
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1478
+ return True
1479
+
1480
+ def _match_acyclic_switch_cases_address_loaded_from_memory(self, node, graph_raw, full_graph_raw) -> bool:
1481
+
1482
+ successor_addrs: list[int] = []
1483
+ cmp_expr: int = 0
1484
+ cmp_lb: int = 0
1485
+ switch_head_addr: int = 0
1486
+
1487
+ # case 1: the last block is a ConditionNode with two goto statements
1488
+ cond_expr_or_stmt = None
1489
+ cond_case = None
1490
+ if isinstance(node, SequenceNode) and node.nodes and isinstance(node.nodes[-1], ConditionNode):
1491
+ cond_node = node.nodes[-1]
1492
+ assert isinstance(cond_node, ConditionNode)
1493
+ if (
1494
+ cond_node.true_node is not None
1495
+ and cond_node.false_node is not None
1496
+ and isinstance(cond_node.true_node, Block)
1497
+ and isinstance(cond_node.false_node, Block)
1498
+ ):
1499
+ successor_addrs = [
1500
+ *extract_jump_targets(cond_node.true_node.statements[-1]),
1501
+ *extract_jump_targets(cond_node.false_node.statements[-1]),
1502
+ ]
1503
+ if len(successor_addrs) != 2 or None in successor_addrs:
1504
+ return False
1505
+
1506
+ cond_expr_or_stmt = cond_node.condition
1507
+ cond_case = 1
1508
+ assert cond_node.addr is not None
1509
+ switch_head_addr = cond_node.addr
1510
+
1511
+ # case 2: the last statement is a conditional jump
1512
+ if not successor_addrs:
1513
+ try:
1514
+ last_stmt = self.cond_proc.get_last_statement(node)
1515
+ except EmptyBlockNotice:
1516
+ return False
1517
+
1518
+ if last_stmt is None:
1519
+ return False
1520
+
1521
+ successor_addrs = extract_jump_targets(last_stmt)
1522
+ if len(successor_addrs) != 2:
1523
+ return False
1524
+
1525
+ cond_expr_or_stmt = last_stmt
1526
+ cond_case = 2
1527
+ switch_head_addr = last_stmt.tags["ins_addr"]
1528
+
1529
+ graph = _f(graph_raw)
1530
+ full_graph = _f(full_graph_raw)
1531
+
1532
+ # special fix
1533
+ if (
1534
+ len(successor_addrs) == 2
1535
+ and graph.out_degree[node] == 2
1536
+ and len(set(successor_addrs).intersection({succ.addr for succ in graph.successors(node)})) == 1
1537
+ ):
1538
+ # there is an unmatched successor addr! fix it
1539
+ successor_addrs = [succ.addr for succ in graph.successors(node)]
1540
+
1541
+ for t in successor_addrs:
1542
+ if t in self.jump_tables:
1543
+ # this is a candidate!
1544
+ target = t
1545
+ break
1546
+ else:
1547
+ return False
1548
+
1549
+ # extract the comparison expression, lower-, and upper-bounds from the last statement
1550
+ match cond_case:
1551
+ case 1:
1552
+ cmp = switch_extract_cmp_bounds_from_condition(
1553
+ self.cond_proc.convert_claripy_bool_ast(cond_expr_or_stmt)
1554
+ )
1555
+ if not cmp:
1556
+ return False
1557
+ case 2:
1558
+ # extract the comparison expression, lower-, and upper-bounds from the last statement
1559
+ cmp = switch_extract_cmp_bounds(cond_expr_or_stmt)
1560
+ if not cmp:
1561
+ return False
1562
+ case _:
1563
+ # unreachable!
1564
+ return False
1565
+
1566
+ cmp_expr, cmp_lb, _cmp_ub = cmp # pylint:disable=unused-variable
1567
+
1568
+ jump_table = self.jump_tables[target]
1569
+ if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
1570
+ return False
1571
+
1572
+ node_a = next(iter(nn for nn in graph.nodes if nn.addr == target), None)
1573
+ if node_a is None:
1574
+ return False
1575
+ if node_a is self._region.head:
1576
+ # avoid structuring if node_a is the region head; this means the current node is a duplicated switch-case
1577
+ # head (instead of the original one), which is not something we want to structure
1578
+ return False
1579
+
1580
+ # the default case
1581
+ node_b_addr = next(iter(t for t in successor_addrs if t != target), None)
1582
+ if node_b_addr is None:
1583
+ return False
1584
+
1585
+ # populate whitelist_edges
1586
+ assert jump_table.jumptable_entries is not None
1587
+ assert isinstance(node_a.addr, int)
1588
+ assert isinstance(node.addr, int)
1589
+ for case_node_addr in jump_table.jumptable_entries:
1590
+ self.whitelist_edges.add((node_a.addr, case_node_addr))
1591
+ self.whitelist_edges.add((node.addr, node_b_addr))
1592
+ self.whitelist_edges.add((node_a.addr, node_b_addr))
1593
+ self.switch_case_known_heads.add(node)
1594
+
1595
+ # sanity check: case nodes are successors to node_a. all case nodes must have at most common one successor
1596
+ node_pred = None
1597
+ if graph.in_degree[node] == 1:
1598
+ node_pred = next(iter(graph.predecessors(node)))
1599
+
1600
+ case_nodes = list(graph.successors(node_a))
1601
+
1602
+ # case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
1603
+ # optimization)
1604
+ # example: touch_touch_no_switch.o:main
1605
+ r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
1606
+
1607
+ # case 2: the common successor is not directly reachable from node_a. this is a more common case.
1608
+ if not r:
1609
+ r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
1610
+
1611
+ if not r:
1612
+ return False
1613
+
1614
+ node_default = self._switch_find_default_node(graph, node, node_b_addr)
1615
+ if node_default is not None:
1616
+ # ensure we have successfully structured node_default
1617
+ if full_graph.out_degree[node_default] > 1:
1618
+ return False
1619
+
1620
+ # un-structure IncompleteSwitchCaseNode
1621
+ if isinstance(node_a, SequenceNode) and node_a.nodes and isinstance(node_a.nodes[0], IncompleteSwitchCaseNode):
1622
+ _, new_seq_node = self._unpack_sequencenode_head(graph_raw, node_a)
1623
+ if new_seq_node is not None and self._node_order is not None:
1624
+ self._node_order[new_seq_node] = self._node_order[node_a]
1625
+ self._unpack_sequencenode_head(full_graph_raw, node_a, new_seq=new_seq_node)
1626
+ # update node_a
1627
+ node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
1628
+ if isinstance(node_a, IncompleteSwitchCaseNode):
1629
+ # special case: if node_default is None, node_a has a missing case, and node_a has a successor in the full
1630
+ # graph that is not the default node, then we know
1631
+ # 1. there is a default node (instead of the successor of the entire switch-case construct).
1632
+ # 2. the default node is in a parent region.
1633
+ # as a result, we cannot structure this switch-case right now
1634
+ if (
1635
+ len(node_a.cases) == len(set(jump_table.jumptable_entries)) - 1
1636
+ and node_default is None
1637
+ and len([succ for succ in full_graph.successors(node_a) if succ.addr != node_b_addr]) > 0
1638
+ ):
1639
+ return False
1640
+
1641
+ r = self._unpack_incompleteswitchcasenode(graph_raw, node_a, jump_table.jumptable_entries)
1642
+ if not r:
1643
+ return False
1644
+ self._unpack_incompleteswitchcasenode(
1645
+ full_graph_raw, node_a, jump_table.jumptable_entries
1646
+ ) # this shall not fail
1647
+ # update node_a
1648
+ node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
1649
+ if self._node_order is not None:
1650
+ self._generate_node_order()
1651
+
1652
+ better_node_a = node_a
1653
+ if isinstance(node_a, SequenceNode) and is_empty_or_label_only_node(node_a.nodes[0]) and len(node_a.nodes) == 2:
1654
+ better_node_a = node_a.nodes[1]
1655
+
1656
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node_a, graph, cmp_lb, jump_table)
1657
+
1658
+ cases, node_default, to_remove = self._switch_build_cases(
1659
+ case_and_entry_addrs,
1660
+ node,
1661
+ node_a,
1662
+ node_b_addr,
1663
+ graph_raw,
1664
+ full_graph_raw,
1665
+ )
1666
+
1667
+ if isinstance(better_node_a, SwitchCaseNode) and better_node_a.default_node is None:
1668
+ # we found a different head for an otherwise complete edge case.
1669
+ # recreate the switch with it.
1670
+ newsc = SwitchCaseNode(better_node_a.switch_expr, better_node_a.cases, node_default, addr=node.addr)
1671
+
1672
+ if node_default is not None and set(graph.succ[node_a]) != set(graph.succ[node_default]):
1673
+ # if node_a and default_node have different successors we need to bail
1674
+ return False
1675
+
1676
+ for pgraph in (graph_raw, full_graph_raw):
1677
+ all_preds = set(pgraph.pred[node])
1678
+ all_succs = set(pgraph.succ[node_a])
1679
+ if node_default is not None:
1680
+ pgraph.remove_node(node_default)
1681
+ pgraph.remove_node(node)
1682
+ pgraph.remove_node(node_a)
1683
+ pgraph.add_node(newsc)
1684
+ for pred in all_preds:
1685
+ pgraph.add_edge(pred, newsc)
1686
+ for succ in all_succs:
1687
+ pgraph.add_edge(newsc, succ)
1688
+
1689
+ if self._node_order is not None:
1690
+ self._node_order[newsc] = self._node_order[better_node_a]
1691
+
1692
+ return True
1693
+
1694
+ if node_default is None:
1695
+ switch_end_addr = node_b_addr
1696
+ else:
1697
+ # we don't know what the end address of this switch-case structure is. let's figure it out
1698
+ switch_end_addr = self._switch_find_switch_end_addr(
1699
+ cases, node_default, {nn.addr for nn in self._region.graph}
1700
+ )
1701
+ to_remove.add(node_default)
1702
+
1703
+ to_remove.add(node_a) # add node_a
1704
+ r = self._make_switch_cases_core(
1705
+ node,
1706
+ cmp_expr,
1707
+ cases,
1708
+ node_b_addr,
1709
+ node_default,
1710
+ switch_head_addr,
1711
+ to_remove,
1712
+ graph_raw,
1713
+ full_graph_raw,
1714
+ node_a=node_a,
1715
+ )
1716
+ if not r:
1717
+ return False
1718
+
1719
+ # fully structured into a switch-case. remove node from switch_case_known_heads
1720
+ self.switch_case_known_heads.remove(node)
1721
+ if switch_end_addr is not None:
1722
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1723
+
1724
+ return True
1725
+
1726
+ def _match_acyclic_switch_cases_address_loaded_from_memory_no_default_node(
1727
+ self, node, graph_raw, full_graph_raw
1728
+ ) -> bool:
1729
+ # sanity checks
1730
+ if not isinstance(node, IncompleteSwitchCaseNode):
1731
+ return False
1732
+ if node.addr not in self.jump_tables:
1733
+ return False
1734
+
1735
+ graph = _f(graph_raw)
1736
+ full_graph = _f(full_graph_raw)
1737
+
1738
+ # ensure _match_acyclic_switch_cases_address_load_from_memory cannot structure its predecessor (and this node)
1739
+ preds = list(graph.predecessors(node))
1740
+ if len(preds) != 1:
1741
+ return False
1742
+ pred = preds[0]
1743
+ if full_graph.out_degree[pred] != 1:
1744
+ return False
1745
+ jump_table: IndirectJump = self.jump_tables[node.addr]
1746
+ if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
1747
+ return False
1748
+
1749
+ # extract the comparison expression, lower-, and upper-bounds from the last statement
1750
+ last_stmt = self.cond_proc.get_last_statement(node.head)
1751
+ if not isinstance(last_stmt, Jump):
1752
+ return False
1753
+ cmp_expr = switch_extract_switch_expr_from_jump_target(last_stmt.target)
1754
+ if cmp_expr is None:
1755
+ return False
1756
+ cmp_lb = 0
1757
+
1758
+ # populate whitelist_edges
1759
+ assert jump_table.jumptable_entries is not None
1760
+
1761
+ # sanity check: case nodes are successors to node_a. all case nodes must have at most common one successor
1762
+ node_pred = None
1763
+ if graph.in_degree[node] == 1:
1764
+ node_pred = next(iter(graph.predecessors(node)))
1765
+
1766
+ case_nodes = list(graph.successors(node))
1767
+
1768
+ # case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
1769
+ # optimization)
1770
+ # example: touch_touch_no_switch.o:main
1771
+ r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
1772
+
1773
+ # case 2: the common successor is not directly reachable from node_a. this is a more common case.
1774
+ if not r:
1775
+ r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
1776
+
1777
+ if not r:
1778
+ return False
1779
+
1780
+ # un-structure IncompleteSwitchCaseNode
1781
+ if isinstance(node, IncompleteSwitchCaseNode):
1782
+ if len(set(jump_table.jumptable_entries)) > len(node.cases):
1783
+ # it has a missing default case node! we cannot structure it as a no-default switch-case
1784
+ return False
1785
+
1786
+ r = self._unpack_incompleteswitchcasenode(graph_raw, node, jump_table.jumptable_entries)
1787
+ if not r:
1788
+ return False
1789
+ self._unpack_incompleteswitchcasenode(
1790
+ full_graph_raw, node, jump_table.jumptable_entries
1791
+ ) # this shall not fail
1792
+ # update node
1793
+ node = next(iter(nn for nn in graph.nodes if nn.addr == jump_table.addr))
1794
+
1795
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
1796
+
1797
+ cases, _, to_remove = self._switch_build_cases(
1798
+ case_and_entry_addrs,
1799
+ node,
1800
+ node,
1801
+ None,
1802
+ graph_raw,
1803
+ full_graph_raw,
1804
+ )
1805
+
1806
+ # we don't know what the end address of this switch-case structure is. let's figure it out
1807
+ switch_end_addr = self._switch_find_switch_end_addr(cases, None, {nn.addr for nn in self._region.graph})
1808
+ r = self._make_switch_cases_core(
1809
+ node,
1810
+ cmp_expr,
1811
+ cases,
1812
+ None,
1813
+ None,
1814
+ last_stmt.tags["ins_addr"],
1815
+ to_remove,
1816
+ graph_raw,
1817
+ full_graph_raw,
1818
+ node_a=None,
1819
+ )
1820
+ if not r:
1821
+ return False
1822
+
1823
+ # fully structured into a switch-case. remove node from switch_case_known_heads
1824
+ if switch_end_addr is not None:
1825
+ self._switch_handle_gotos(cases, None, switch_end_addr)
1826
+
1827
+ return True
1828
+
1829
+ def _match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(
1830
+ self, node, graph_raw, full_graph_raw
1831
+ ) -> bool:
1832
+ if node.addr not in self.jump_tables:
1833
+ return False
1834
+
1835
+ try:
1836
+ last_stmt = self.cond_proc.get_last_statement(node)
1837
+ except EmptyBlockNotice:
1838
+ return False
1839
+ if not (isinstance(last_stmt, Jump) and not isinstance(last_stmt.target, Const)):
1840
+ return False
1841
+
1842
+ jump_table = self.jump_tables[node.addr]
1843
+ if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
1844
+ return False
1845
+
1846
+ # extract the index expression, lower-, and upper-bounds from the last statement
1847
+ index = switch_extract_bitwiseand_jumptable_info(last_stmt)
1848
+ if not index:
1849
+ return False
1850
+ index_expr, cmp_lb, cmp_ub = index # pylint:disable=unused-variable
1851
+ case_count = cmp_ub - cmp_lb + 1
1852
+
1853
+ # ensure we have the same number of cases
1854
+ assert jump_table.jumptable_entries is not None
1855
+ if case_count != len(jump_table.jumptable_entries):
1856
+ return False
1857
+
1858
+ # populate whitelist_edges
1859
+ for case_node_addr in jump_table.jumptable_entries:
1860
+ self.whitelist_edges.add((node.addr, case_node_addr))
1861
+ self.switch_case_known_heads.add(node)
1862
+
1863
+ graph = _f(graph_raw)
1864
+
1865
+ # sanity check: case nodes are successors to node. all case nodes must have at most common one successor
1866
+ node_pred = None
1867
+ if graph.in_degree[node] == 1:
1868
+ node_pred = next(iter(graph.predecessors(node)))
1869
+
1870
+ case_nodes = list(graph.successors(node))
1871
+
1872
+ # case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
1873
+ # optimization)
1874
+ # example: touch_touch_no_switch.o:main
1875
+ r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
1876
+
1877
+ # case 2: the common successor is not directly reachable from node_a. this is a more common case.
1878
+ if not r:
1879
+ r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
1880
+
1881
+ if not r:
1882
+ return False
1883
+
1884
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
1885
+
1886
+ cases, node_default, to_remove = self._switch_build_cases(
1887
+ case_and_entry_addrs,
1888
+ node,
1889
+ node,
1890
+ None,
1891
+ graph_raw,
1892
+ full_graph_raw,
1893
+ )
1894
+
1895
+ assert node_default is None
1896
+ switch_end_addr = self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
1897
+
1898
+ r = self._make_switch_cases_core(
1899
+ node,
1900
+ index_expr,
1901
+ cases,
1902
+ None,
1903
+ None,
1904
+ last_stmt.tags["ins_addr"],
1905
+ to_remove,
1906
+ graph_raw,
1907
+ full_graph_raw,
1908
+ node_a=None,
1909
+ )
1910
+ if not r:
1911
+ return False
1912
+
1913
+ # fully structured into a switch-case. remove node from switch_case_known_heads
1914
+ self.switch_case_known_heads.remove(node)
1915
+ if switch_end_addr is not None:
1916
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1917
+
1918
+ return True
1919
+
1920
+ def _match_acyclic_switch_cases_address_computed(
1921
+ self, node, graph_raw: networkx.DiGraph, full_graph_raw: networkx.DiGraph
1922
+ ) -> bool:
1923
+ if node.addr not in self.jump_tables:
1924
+ return False
1925
+ jump_table = self.jump_tables[node.addr]
1926
+ if jump_table.type != IndirectJumpType.Jumptable_AddressComputed:
1927
+ return False
1928
+
1929
+ try:
1930
+ last_stmts = self.cond_proc.get_last_statements(node)
1931
+ except EmptyBlockNotice:
1932
+ return False
1933
+ if len(last_stmts) != 1:
1934
+ return False
1935
+ last_stmt = last_stmts[0]
1936
+
1937
+ if not isinstance(last_stmt, ConditionalJump):
1938
+ return False
1939
+
1940
+ # Typical look:
1941
+ # t2 = (r5<4> - 0x22<32>)
1942
+ # if ((t2 <= 0x1c<32>)) { Goto (0x41d10c<32> + (t2 << 0x2<8>)) } else { Goto 0x41d108<32> }
1943
+ #
1944
+ # extract the comparison expression, lower-, and upper-bounds from the last statement
1945
+ cmp = switch_extract_cmp_bounds(last_stmt)
1946
+ if not cmp:
1947
+ return False
1948
+ cmp_expr, cmp_lb, _cmp_ub = cmp # pylint:disable=unused-variable
1949
+
1950
+ if isinstance(last_stmt.false_target, Const):
1951
+ default_addr = last_stmt.false_target.value
1952
+ assert isinstance(default_addr, int)
1953
+ else:
1954
+ return False
1955
+
1956
+ graph = _f(graph_raw)
1957
+ full_graph = _f(full_graph_raw)
1958
+
1959
+ node_default = self._switch_find_default_node(graph, node, default_addr)
1960
+ if node_default is not None:
1961
+ # ensure we have successfully structured node_default
1962
+ if full_graph.out_degree[node_default] > 1:
1963
+ return False
1964
+
1965
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
1966
+
1967
+ cases, node_default, to_remove = self._switch_build_cases(
1968
+ case_and_entry_addrs,
1969
+ node,
1970
+ node,
1971
+ default_addr,
1972
+ graph_raw,
1973
+ full_graph_raw,
1974
+ )
1975
+ if node_default is None:
1976
+ # there must be a default case
1977
+ return False
1978
+
1979
+ return self._make_switch_cases_core(
1980
+ node, cmp_expr, cases, default_addr, node_default, node.addr, to_remove, graph_raw, full_graph_raw
1981
+ )
1982
+
1983
+ def _match_acyclic_incomplete_switch_cases(
1984
+ self, node, graph_raw: networkx.DiGraph, full_graph_raw: networkx.DiGraph
1985
+ ) -> bool:
1986
+ # sanity checks
1987
+ if node.addr not in self.jump_tables:
1988
+ return False
1989
+ if isinstance(node, IncompleteSwitchCaseNode):
1990
+ return False
1991
+ if is_empty_or_label_only_node(node):
1992
+ return False
1993
+
1994
+ graph = _f(graph_raw)
1995
+ full_graph = _f(full_graph_raw)
1996
+
1997
+ successors = list(graph.successors(node))
1998
+
1999
+ jump_table = self.jump_tables[node.addr]
2000
+ assert jump_table.jumptable_entries is not None
2001
+
2002
+ if successors and all(graph.in_degree[succ] == 1 for succ in successors):
2003
+ succ_addrs = {succ.addr for succ in successors}
2004
+ expected_entry_addrs = set(jump_table.jumptable_entries)
2005
+ # test if we have found all entries or all but one entry (where the one missing entry is likely the default
2006
+ # case).
2007
+ if succ_addrs == expected_entry_addrs or (
2008
+ succ_addrs.issubset(expected_entry_addrs) and len(expected_entry_addrs - succ_addrs) == 1
2009
+ ):
2010
+ # ensure that the successors have been properly structured, which means they either do not have
2011
+ # successors or they have one successor that is the likely default node of the switch-case construct
2012
+ # or the head of the switch-case construct (in which case, it's a loop).
2013
+ out_nodes = set()
2014
+ for succ in successors:
2015
+ out_nodes |= {
2016
+ succ for succ in full_graph.successors(succ) if succ is not node and succ not in successors
2017
+ }
2018
+ out_nodes = list(out_nodes)
2019
+ if (
2020
+ len(out_nodes) == 0
2021
+ or (
2022
+ len(out_nodes) == 1
2023
+ and (
2024
+ self._is_switch_case_address_loaded_from_memory_default_node(full_graph, out_nodes[0])
2025
+ or self._is_switch_cases_address_loaded_from_memory_head(full_graph, out_nodes[0])
2026
+ )
2027
+ )
2028
+ ) and node.addr not in self._matched_incomplete_switch_case_addrs:
2029
+ self._matched_incomplete_switch_case_addrs.add(node.addr)
2030
+ new_node = IncompleteSwitchCaseNode(node.addr, node, successors)
2031
+ graph_raw.remove_nodes_from(successors)
2032
+ self.replace_nodes(graph_raw, node, new_node)
2033
+ if out_nodes and out_nodes[0] in graph:
2034
+ graph_raw.add_edge(new_node, out_nodes[0])
2035
+ full_graph_raw.remove_nodes_from(successors)
2036
+ self.replace_nodes(full_graph_raw, node, new_node, update_node_order=True)
2037
+ if out_nodes:
2038
+ full_graph_raw.add_edge(new_node, out_nodes[0])
2039
+ if self._node_order:
2040
+ self._node_order[new_node] = self._node_order[node]
2041
+ return True
2042
+ return False
2043
+
2044
+ def _switch_build_cases(
2045
+ self,
2046
+ case_and_entryaddrs: dict[int, int | tuple[int, int | None]],
2047
+ head_node,
2048
+ node_a: BaseNode,
2049
+ node_b_addr: int | None,
2050
+ graph_raw: networkx.DiGraph,
2051
+ full_graph_raw: networkx.DiGraph,
2052
+ ) -> tuple[OrderedDict, Any, set[Any]]:
2053
+ cases: OrderedDict[int | tuple[int, ...], SequenceNode] = OrderedDict()
2054
+ to_remove = set()
2055
+
2056
+ graph = _f(graph_raw)
2057
+
2058
+ default_node_candidates = (
2059
+ [nn for nn in graph.nodes if nn.addr == node_b_addr] if node_b_addr is not None else []
2060
+ )
2061
+ node_default = (
2062
+ self._switch_find_default_node(graph, head_node, node_b_addr) if node_b_addr is not None else None
2063
+ )
2064
+ if node_default is not None and not isinstance(node_default, SequenceNode):
2065
+ # make the default node a SequenceNode so that we can insert Break and Continue nodes into it later
2066
+ new_node = SequenceNode(node_default.addr, nodes=[node_default])
2067
+ self.replace_nodes(graph_raw, node_default, new_node)
2068
+ self.replace_nodes(full_graph_raw, node_default, new_node, update_node_order=True)
2069
+ node_default = new_node
2070
+
2071
+ converted_nodes: dict[tuple[int, int | None], Any] = {}
2072
+ entry_addr_to_ids: defaultdict[tuple[int, int | None], set[int]] = defaultdict(set)
2073
+
2074
+ # the default node might get duplicated (e.g., by EagerReturns). we detect if a duplicate of the default node
2075
+ # (node b) is a successor node of node a. we only skip those entries going to the default node if no duplicate
2076
+ # of default node exists in node a's successors.
2077
+ node_a_successors = list(graph.successors(node_a))
2078
+ if len(default_node_candidates) > 1:
2079
+ node_b_in_node_a_successors = any(nn for nn in node_a_successors if nn in default_node_candidates)
2080
+ else:
2081
+ # the default node is not duplicated
2082
+ node_b_in_node_a_successors = False
2083
+
2084
+ for case_idx, entry_addr in case_and_entryaddrs.items():
2085
+ if isinstance(entry_addr, tuple):
2086
+ entry_addr, entry_idx = entry_addr
2087
+ else:
2088
+ entry_idx = None
2089
+
2090
+ if not node_b_in_node_a_successors and entry_addr == node_b_addr:
2091
+ # jump to default or end of the switch-case structure - ignore this case
2092
+ continue
2093
+
2094
+ entry_addr_to_ids[(entry_addr, entry_idx)].add(case_idx)
2095
+ if (entry_addr, entry_idx) in converted_nodes:
2096
+ continue
2097
+
2098
+ if entry_addr == self._region.head.addr:
2099
+ # do not make the region head part of the switch-case construct (because it will lead to the removal
2100
+ # of the region head node). replace this entry with a goto statement later.
2101
+ entry_node = None
2102
+ else:
2103
+ entry_node = next(
2104
+ iter(
2105
+ nn
2106
+ for nn in node_a_successors
2107
+ if nn.addr == entry_addr and (not isinstance(nn, (Block, MultiNode)) or nn.idx == entry_idx)
2108
+ ),
2109
+ None,
2110
+ )
2111
+ if entry_node is None:
2112
+ # Missing entries. They are probably *after* the entire switch-case construct. Replace it with an empty
2113
+ # Goto node.
2114
+ case_inner_node = Block(
2115
+ 0,
2116
+ 0,
2117
+ statements=[
2118
+ Jump(
2119
+ None,
2120
+ Const(None, None, entry_addr, self.project.arch.bits),
2121
+ target_idx=entry_idx,
2122
+ ins_addr=0,
2123
+ stmt_idx=0,
2124
+ )
2125
+ ],
2126
+ )
2127
+ case_node = SequenceNode(0, nodes=[case_inner_node])
2128
+ converted_nodes[(entry_addr, entry_idx)] = case_node
2129
+ continue
2130
+
2131
+ if isinstance(entry_node, SequenceNode):
2132
+ case_node = entry_node
2133
+ else:
2134
+ case_node = SequenceNode(entry_node.addr, nodes=[entry_node])
2135
+ to_remove.add(entry_node)
2136
+
2137
+ converted_nodes[(entry_addr, entry_idx)] = case_node
2138
+
2139
+ for entry_addr_and_idx, converted_node in converted_nodes.items():
2140
+ assert entry_addr_and_idx in entry_addr_to_ids
2141
+ case_ids = entry_addr_to_ids[entry_addr_and_idx]
2142
+ if len(case_ids) == 1:
2143
+ cases[next(iter(case_ids))] = converted_node
2144
+ else:
2145
+ cases[tuple(sorted(case_ids))] = converted_node
2146
+
2147
+ # reorganize cases to handle fallthroughs
2148
+ cases = self._reorganize_switch_cases(cases)
2149
+
2150
+ return cases, node_default, to_remove
2151
+
2152
+ def _make_switch_cases_core(
2153
+ self,
2154
+ head,
2155
+ cmp_expr,
2156
+ cases: OrderedDict,
2157
+ node_default_addr: int | None,
2158
+ node_default,
2159
+ addr,
2160
+ to_remove: set,
2161
+ graph: networkx.DiGraph,
2162
+ full_graph: networkx.DiGraph,
2163
+ node_a=None,
2164
+ bail_on_nonhead_outedges: bool = False,
2165
+ ) -> bool:
2166
+ scnode = SwitchCaseNode(cmp_expr, cases, node_default, addr=addr)
2167
+
2168
+ # insert the switch-case node to the graph
2169
+ other_nodes_inedges = []
2170
+ out_edges = []
2171
+
2172
+ # remove all those entry nodes
2173
+ if node_default is not None:
2174
+ to_remove.add(node_default)
2175
+
2176
+ for nn in to_remove:
2177
+ if nn is head or (node_a is not None and nn is node_a):
2178
+ continue
2179
+ for src in graph.predecessors(nn):
2180
+ if src not in to_remove:
2181
+ other_nodes_inedges.append((src, nn))
2182
+ for dst in full_graph.successors(nn):
2183
+ if dst not in to_remove:
2184
+ out_edges.append((nn, dst))
2185
+
2186
+ if bail_on_nonhead_outedges:
2187
+ nonhead_out_nodes = {edge[1] for edge in out_edges if edge[1] is not head}
2188
+ if len(nonhead_out_nodes) > 1:
2189
+ # not ready to be structured yet - do it later
2190
+ return False
2191
+
2192
+ # check if structuring will create any dangling nodes
2193
+ for case_node in to_remove:
2194
+ if case_node is not node_default and case_node is not node_a and case_node is not head:
2195
+ for succ in graph.successors(case_node):
2196
+ if (
2197
+ succ is not case_node
2198
+ and succ is not head
2199
+ and succ is not self._region.head
2200
+ and graph.in_degree[succ] == 1
2201
+ ):
2202
+ # succ will be dangling - not ready to be structured yet - do it later
2203
+ return False
2204
+ succs = {dst for _, dst in out_edges}
2205
+ dangling_succs = set()
2206
+ if len(succs) > 1:
2207
+ for succ in succs:
2208
+ if succ in graph:
2209
+ non_switch_preds = {pred for pred in graph.predecessors(succ) if pred not in to_remove}
2210
+ if not non_switch_preds:
2211
+ dangling_succs.add(succ)
2212
+ if len(dangling_succs) > 1:
2213
+ # there will definitely be dangling nodes after structuring. it's not ready to be structured yet.
2214
+ return False
2215
+
2216
+ if node_default is not None:
2217
+ # the head no longer goes to the default case
2218
+ if graph.has_edge(head, node_default):
2219
+ pass
2220
+ graph.remove_edge(head, node_default)
2221
+ full_graph.remove_edge(head, node_default)
2222
+ elif node_default_addr is not None:
2223
+ # the default node is not in the current graph, but it might be in the full graph
2224
+ node_default_in_full_graph = next(iter(nn for nn in full_graph if nn.addr == node_default_addr), None)
2225
+ if node_default_in_full_graph is not None and full_graph.has_edge(head, node_default_in_full_graph):
2226
+ # the head no longer jumps to the default node - the switch jumps to it
2227
+ full_graph.remove_edge(head, node_default_in_full_graph)
2228
+
2229
+ for nn in to_remove:
2230
+ graph.remove_node(nn)
2231
+ full_graph.remove_node(nn)
2232
+
2233
+ graph.add_edge(head, scnode)
2234
+ full_graph.add_edge(head, scnode)
2235
+ if self._node_order is not None:
2236
+ self._node_order[scnode] = self._node_order[head]
2237
+
2238
+ if out_edges:
2239
+ # sort out_edges
2240
+ out_edges_to_head = [edge for edge in out_edges if edge[1] is head]
2241
+ other_out_edges = sorted(
2242
+ [edge for edge in out_edges if edge[1] is not head], key=lambda edge: (edge[0].addr, edge[1].addr)
2243
+ )
2244
+
2245
+ # for all out edges going to head, we ensure there is a goto at the end of each corresponding case node
2246
+ for out_src, out_dst in out_edges_to_head:
2247
+ assert out_dst is head
2248
+ all_case_nodes = list(cases.values())
2249
+ if node_default is not None:
2250
+ all_case_nodes.append(node_default)
2251
+ case_node: SequenceNode = next(nn for nn in all_case_nodes if nn.addr == out_src.addr)
2252
+ try:
2253
+ case_node_last_stmt = self.cond_proc.get_last_statement(case_node)
2254
+ except EmptyBlockNotice:
2255
+ case_node_last_stmt = None
2256
+ if not isinstance(case_node_last_stmt, Jump):
2257
+ jump_stmt = Jump(
2258
+ None, Const(None, None, head.addr, self.project.arch.bits), None, ins_addr=out_src.addr
2259
+ )
2260
+ jump_node = Block(out_src.addr, 0, statements=[jump_stmt])
2261
+ case_node.nodes.append(jump_node)
2262
+
2263
+ # out_dst_succ is the successor within the current region
2264
+ # out_dst_succ_fullgraph is the successor outside the current region
2265
+ if out_edges_to_head:
2266
+ # add an edge from SwitchCaseNode to head so that a loop will be structured later
2267
+ out_dst_succ = head
2268
+ out_dst_succ_fullgraph = None
2269
+ else:
2270
+ # add an edge from SwitchCaseNode to its most immediate successor (if there is one)
2271
+ # there might be an in-region successor and an out-of-region successor (especially due to the
2272
+ # introduction of self.dowhile_known_tail_nodes)!
2273
+ # example: 7995a0325b446c462bdb6ae10b692eee2ecadd8e888e9d7729befe4412007afb, function 1400EF820
2274
+ out_dst_succs = []
2275
+ out_dst_succs_fullgraph = []
2276
+ for _, o in other_out_edges:
2277
+ if o in graph:
2278
+ if o not in out_dst_succs:
2279
+ out_dst_succs.append(o)
2280
+ elif o in full_graph:
2281
+ if o not in out_dst_succs_fullgraph:
2282
+ out_dst_succs_fullgraph.append(o)
2283
+ out_dst_succ = sorted(out_dst_succs, key=lambda o: o.addr)[0] if out_dst_succs else None
2284
+ out_dst_succ_fullgraph = (
2285
+ sorted(out_dst_succs_fullgraph, key=lambda o: o.addr)[0] if out_dst_succs_fullgraph else None
2286
+ )
2287
+ if len(out_dst_succs) > 1:
2288
+ assert out_dst_succ is not None
2289
+ l.warning(
2290
+ "Multiple in-region successors detected for switch-case node at %#x. Picking %#x as the "
2291
+ "successor and dropping others.",
2292
+ scnode.addr,
2293
+ out_dst_succ.addr,
2294
+ )
2295
+ if len(out_dst_succs_fullgraph) > 1:
2296
+ assert out_dst_succ_fullgraph is not None
2297
+ l.warning(
2298
+ "Multiple out-of-region successors detected for switch-case node at %#x. Picking %#x as the "
2299
+ "successor and dropping others.",
2300
+ scnode.addr,
2301
+ out_dst_succ_fullgraph.addr,
2302
+ )
2303
+
2304
+ if out_dst_succ is not None:
2305
+ graph.add_edge(scnode, out_dst_succ)
2306
+ full_graph.add_edge(scnode, out_dst_succ)
2307
+ if full_graph.has_edge(head, out_dst_succ):
2308
+ full_graph.remove_edge(head, out_dst_succ)
2309
+ if out_dst_succ_fullgraph is not None:
2310
+ full_graph.add_edge(scnode, out_dst_succ_fullgraph)
2311
+ if full_graph.has_edge(head, out_dst_succ_fullgraph):
2312
+ full_graph.remove_edge(head, out_dst_succ_fullgraph)
2313
+
2314
+ # fix full_graph if needed: remove successors that are no longer needed
2315
+ for _out_src, out_dst in other_out_edges:
2316
+ if (
2317
+ out_dst is not out_dst_succ
2318
+ and out_dst in full_graph
2319
+ and out_dst not in graph
2320
+ and full_graph.in_degree[out_dst] == 0
2321
+ ):
2322
+ full_graph.remove_node(out_dst)
2323
+ assert self._region.successors is not None
2324
+ if out_dst in self._region.successors:
2325
+ self._region.successors.remove(out_dst)
2326
+
2327
+ # remove the last statement (conditional jump) in the head node
2328
+ self._remove_last_statement_if_jump_or_schead(head)
2329
+
2330
+ if node_a is not None:
2331
+ # remove the last statement in node_a
2332
+ remove_last_statements(node_a)
2333
+
2334
+ return True
2335
+
2336
+ @staticmethod
2337
+ def _find_case_and_entry_addrs(
2338
+ jump_head, graph, cmp_lb: int, jump_table
2339
+ ) -> dict[int, int | tuple[int, int | None]]:
2340
+ case_and_entry_addrs = {}
2341
+
2342
+ addr_to_entry_nodes = defaultdict(list)
2343
+ for succ in graph.successors(jump_head):
2344
+ addr_to_entry_nodes[succ.addr].append(succ)
2345
+
2346
+ for i, entry_addr in enumerate(jump_table.jumptable_entries):
2347
+ case_no = cmp_lb + i
2348
+ if entry_addr in addr_to_entry_nodes and isinstance(addr_to_entry_nodes[entry_addr][0], (MultiNode, Block)):
2349
+ case_and_entry_addrs[case_no] = entry_addr, addr_to_entry_nodes[entry_addr][0].idx
2350
+ else:
2351
+ case_and_entry_addrs[case_no] = entry_addr
2352
+
2353
+ return case_and_entry_addrs
2354
+
2355
+ def _is_node_unstructured_switch_case_head(self, graph, node) -> bool:
2356
+ # scan forward by at least one step to see if we can get to a jump table node
2357
+ steps = 0
2358
+ while node.addr not in self.jump_tables and steps < 1:
2359
+ steps += 1
2360
+ nodes = list(graph.successors(node))
2361
+ if len(nodes) != 1:
2362
+ return False
2363
+ node = nodes[0]
2364
+
2365
+ if node.addr in self.jump_tables:
2366
+ # maybe it has been structured?
2367
+ try:
2368
+ last_stmts = self.cond_proc.get_last_statements(node)
2369
+ except EmptyBlockNotice:
2370
+ return False
2371
+ return (
2372
+ len(last_stmts) == 1 and isinstance(last_stmts[0], Jump) and not isinstance(last_stmts[0].target, Const)
2373
+ )
2374
+ return False
2375
+
2376
+ def _is_switch_cases_address_loaded_from_memory_head(self, graph, node) -> bool:
2377
+ for succ in graph.successors(node):
2378
+ if self._is_node_unstructured_switch_case_head(graph, succ):
2379
+ return True
2380
+ return node in self.switch_case_known_heads
2381
+
2382
+ def _is_switch_cases_address_loaded_from_memory_head_or_jumpnode(self, graph, node) -> bool:
2383
+ if self._is_node_unstructured_switch_case_head(graph, node):
2384
+ return True
2385
+ if isinstance(node, IncompleteSwitchCaseNode):
2386
+ return True
2387
+ return self._is_switch_cases_address_loaded_from_memory_head(graph, node)
2388
+
2389
+ def _is_switch_case_address_loaded_from_memory_default_node(self, graph, node) -> bool:
2390
+ # the default node should have a predecessor that is a switch-case head node
2391
+ for pred in graph.predecessors(node):
2392
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(graph, pred):
2393
+ return True
2394
+ return False
2395
+
2396
+ # other acyclic schemas
2397
+
2398
+ def _match_acyclic_sequence(self, graph_raw, full_graph_raw, start_node) -> bool:
2399
+ """
2400
+ Check if there is a sequence of regions, where each region has a single predecessor and a single successor.
2401
+ """
2402
+
2403
+ full_graph = _f(full_graph_raw)
2404
+ graph = _f(graph_raw)
2405
+
2406
+ succs = list(graph.successors(start_node))
2407
+ if len(succs) != 1:
2408
+ return False
2409
+ end_node = succs[0]
2410
+ if (
2411
+ full_graph.out_degree[start_node] == 1
2412
+ and full_graph.in_degree[end_node] == 1
2413
+ and not full_graph.has_edge(end_node, start_node)
2414
+ and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, start_node)
2415
+ and end_node not in self.dowhile_known_tail_nodes
2416
+ ):
2417
+ new_seq = None
2418
+ if not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, end_node):
2419
+ # merge two blocks
2420
+ new_seq = self._merge_nodes(start_node, end_node)
2421
+ elif isinstance(end_node, IncompleteSwitchCaseNode):
2422
+ # a special case where there is a node between the actual switch-case head and the jump table
2423
+ # head
2424
+ # binary 7995a0325b446c462bdb6ae10b692eee2ecadd8e888e9d7729befe4412007afb, function 0x1400326C0
2425
+ # keep the IncompleteSwitchCaseNode, and merge two blocks into the head of the IncompleteSwitchCaseNode.
2426
+ new_seq = self._merge_nodes(start_node, end_node.head)
2427
+ new_seq.addr = end_node.addr
2428
+ end_node.head = new_seq
2429
+ new_seq = end_node
2430
+
2431
+ if new_seq is not None:
2432
+ # on the original graph
2433
+ self.replace_nodes(graph_raw, start_node, new_seq, old_node_1=end_node if end_node in graph else None)
2434
+ # on the graph with successors
2435
+ self.replace_nodes(full_graph_raw, start_node, new_seq, old_node_1=end_node, update_node_order=True)
2436
+ return True
2437
+ return False
2438
+
2439
+ def _match_acyclic_ite(self, graph_raw, full_graph_raw, start_node) -> bool:
2440
+ """
2441
+ Check if start_node is the beginning of an If-Then-Else region. Create a Condition node if it is the case.
2442
+ """
2443
+
2444
+ full_graph = _f(full_graph_raw)
2445
+ graph = _f(graph_raw)
2446
+
2447
+ succs = list(full_graph.successors(start_node))
2448
+ if len(succs) == 2:
2449
+ left, right = succs
2450
+ if left.addr > right.addr:
2451
+ left, right = right, left
2452
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
2453
+ full_graph, left
2454
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, right):
2455
+ # structure the switch-case first before we wrap them into an ITE. give up
2456
+ return False
2457
+
2458
+ left_succs = list(full_graph.successors(left))
2459
+ right_succs = list(full_graph.successors(right))
2460
+
2461
+ if (
2462
+ left in graph
2463
+ and right in graph
2464
+ and (
2465
+ (not left_succs and not right_succs)
2466
+ or (not left_succs and len(right_succs) == 1)
2467
+ or (not right_succs and len(left_succs) == 1)
2468
+ or (len(left_succs) == 1 and left_succs == right_succs)
2469
+ )
2470
+ ):
2471
+ # potentially ITE
2472
+ if (
2473
+ full_graph.in_degree[left] == 1
2474
+ and full_graph.in_degree[right] == 1
2475
+ and not self._is_node_unstructured_switch_case_head(full_graph, left)
2476
+ and not self._is_node_unstructured_switch_case_head(full_graph, right)
2477
+ ):
2478
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2479
+ # c = !c
2480
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2481
+ last_if_jump = self._remove_last_statement_if_jump(start_node)
2482
+ new_cond_node = ConditionNode(
2483
+ last_if_jump.tags["ins_addr"] if last_if_jump is not None else start_node.addr,
2484
+ None,
2485
+ edge_cond_left,
2486
+ left,
2487
+ false_node=right,
2488
+ )
2489
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2490
+
2491
+ if not left_succs:
2492
+ # on the original graph
2493
+ if left in graph:
2494
+ graph_raw.remove_node(left)
2495
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=right)
2496
+ # on the graph with successors
2497
+ full_graph_raw.remove_node(left)
2498
+ self.replace_nodes(
2499
+ full_graph_raw, start_node, new_node, old_node_1=right, update_node_order=True
2500
+ )
2501
+ else:
2502
+ # on the original graph
2503
+ if right in graph:
2504
+ graph_raw.remove_node(right)
2505
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=left)
2506
+ # on the graph with successors
2507
+ full_graph_raw.remove_node(right)
2508
+ self.replace_nodes(
2509
+ full_graph_raw, start_node, new_node, old_node_1=left, update_node_order=True
2510
+ )
2511
+
2512
+ return True
2513
+
2514
+ if right in graph and not right_succs and full_graph.in_degree[right] == 1 and left in graph:
2515
+ # swap them
2516
+ left, right = right, left
2517
+ left_succs, right_succs = right_succs, left_succs
2518
+ if left in graph and not left_succs and full_graph.in_degree[left] == 1 and right in graph:
2519
+ # potentially If-Then
2520
+ if not self._is_node_unstructured_switch_case_head(
2521
+ full_graph, left
2522
+ ) and not self._is_node_unstructured_switch_case_head(full_graph, right):
2523
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2524
+ # c = !c
2525
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2526
+ last_if_jump = self._remove_last_statement_if_jump(start_node)
2527
+ new_cond_node = ConditionNode(
2528
+ last_if_jump.tags["ins_addr"] if last_if_jump is not None else start_node.addr,
2529
+ None,
2530
+ edge_cond_left,
2531
+ left,
2532
+ false_node=None,
2533
+ )
2534
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2535
+
2536
+ # on the original graph
2537
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=left)
2538
+ # on the graph with successors
2539
+ self.replace_nodes(
2540
+ full_graph_raw, start_node, new_node, old_node_1=left, update_node_order=True
2541
+ )
2542
+
2543
+ return True
2544
+
2545
+ if len(right_succs) == 1 and right_succs[0] == left:
2546
+ # swap them
2547
+ left, right = right, left
2548
+ left_succs, right_succs = right_succs, left_succs
2549
+ # potentially If-Then
2550
+ if (
2551
+ left in graph
2552
+ and len(left_succs) == 1
2553
+ and left_succs[0] == right
2554
+ and full_graph.in_degree[left] == 1
2555
+ and full_graph.in_degree[right] >= 2
2556
+ ):
2557
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2558
+ # c = !c
2559
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2560
+ last_if_jump = self._remove_last_statement_if_jump(start_node)
2561
+ new_cond_node = ConditionNode(
2562
+ last_if_jump.tags["ins_addr"] if last_if_jump is not None else start_node.addr,
2563
+ None,
2564
+ edge_cond_left,
2565
+ left,
2566
+ false_node=None,
2567
+ )
2568
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2569
+
2570
+ # on the original graph
2571
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=left)
2572
+ # on the graph with successors
2573
+ self.replace_nodes(full_graph_raw, start_node, new_node, old_node_1=left, update_node_order=True)
2574
+
2575
+ return True
2576
+
2577
+ if right in graph and left not in graph:
2578
+ # swap them
2579
+ left, right = right, left
2580
+ left_succs, right_succs = right_succs, left_succs # pylint:disable=unused-variable
2581
+ # potentially If-then
2582
+ if (
2583
+ left in graph
2584
+ and right not in graph
2585
+ and full_graph.in_degree[left] == 1
2586
+ and (
2587
+ (full_graph.in_degree[right] == 2 and left_succs == [right])
2588
+ or (full_graph.in_degree[right] == 1 and not left_succs)
2589
+ )
2590
+ ):
2591
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2592
+ # c = !c
2593
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2594
+ try:
2595
+ last_stmt = self.cond_proc.get_last_statement(start_node)
2596
+ except EmptyBlockNotice:
2597
+ last_stmt = None
2598
+ new_cond_node = ConditionNode(
2599
+ last_stmt.tags["ins_addr"] if last_stmt is not None else start_node.addr,
2600
+ None,
2601
+ edge_cond_left,
2602
+ left,
2603
+ false_node=None,
2604
+ )
2605
+ new_nodes = [start_node, new_cond_node]
2606
+ if full_graph.in_degree[right] == 1:
2607
+ # only remove the if statement when it will no longer be used later
2608
+ self._remove_last_statement_if_jump(start_node)
2609
+ # add a goto node at the end
2610
+ new_jump_node = Block(
2611
+ new_cond_node.addr if new_cond_node.addr is not None else 0x7EFF_FFFF,
2612
+ 0,
2613
+ statements=[
2614
+ Jump(
2615
+ None,
2616
+ Const(None, None, right.addr, self.project.arch.bits),
2617
+ ins_addr=new_cond_node.addr,
2618
+ )
2619
+ ],
2620
+ )
2621
+ new_nodes.append(new_jump_node)
2622
+ new_node = SequenceNode(start_node.addr, nodes=new_nodes)
2623
+
2624
+ # on the original graph
2625
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=left)
2626
+ # on the graph with successors
2627
+ self.replace_nodes(full_graph_raw, start_node, new_node, old_node_1=left, update_node_order=True)
2628
+
2629
+ return True
2630
+
2631
+ return False
2632
+
2633
+ def _match_acyclic_short_circuit_conditions(
2634
+ self, graph_raw: networkx.DiGraph, full_graph_raw: networkx.DiGraph, start_node
2635
+ ) -> bool:
2636
+ """
2637
+ Check if start_node is the beginning of an If-Then-Else region with cascading short-circuit expressions as the
2638
+ condition. Create a Condition node if it is the case.
2639
+ """
2640
+
2641
+ # There are four possible graph schemas.
2642
+ #
2643
+ # Type A: Cascading Or::
2644
+ #
2645
+ # cond_node
2646
+ # | \
2647
+ # | \
2648
+ # next_cond \
2649
+ # ... \ \
2650
+ # \ |
2651
+ # \ |
2652
+ # \ |
2653
+ # ... body
2654
+ # ... /
2655
+ # \ \ \ /
2656
+ # successor
2657
+ #
2658
+ # We reduce it into if (cond || next_cond) { body }
2659
+ #
2660
+ # Type B: Cascading Or with else::
2661
+ #
2662
+ # cond_node
2663
+ # | \
2664
+ # | \
2665
+ # next_cond \
2666
+ # ... \ \
2667
+ # \ |
2668
+ # \ |
2669
+ # \ |
2670
+ # ... body
2671
+ # else /
2672
+ # \ \ \ /
2673
+ # successor
2674
+ #
2675
+ # We reduce it into if (cond || next_cond) { body } else { else }
2676
+ #
2677
+ # Type C: Cascading And::
2678
+ #
2679
+ # cond_node
2680
+ # | \
2681
+ # | \
2682
+ # next_cond \
2683
+ # ... \ \
2684
+ # \ |
2685
+ # \ |
2686
+ # \ \ /
2687
+ # \ |
2688
+ # body |
2689
+ # ... | |
2690
+ # | |
2691
+ # \ \ \ /
2692
+ # successor
2693
+ #
2694
+ # We reduce it into if (cond && next_cond) { body }
2695
+ #
2696
+ # Type D: Cascading And with else::
2697
+ #
2698
+ # cond_node
2699
+ # | \
2700
+ # | \
2701
+ # next_cond \
2702
+ # ... \ \
2703
+ # \ |
2704
+ # \ |
2705
+ # \ \ /
2706
+ # \ |
2707
+ # body |
2708
+ # ... | else
2709
+ # | |
2710
+ # \ \ \ /
2711
+ # successor
2712
+ #
2713
+ # We reduce it into if (cond && next_cond) { body } else { else }
2714
+
2715
+ graph = _f(graph_raw)
2716
+ full_graph = _f(full_graph_raw)
2717
+
2718
+ # fast-path check to reject nodes that definitely do not work
2719
+ if full_graph.out_degree[start_node] != 2:
2720
+ return False
2721
+ next_cond_candidates = list(full_graph.successors(start_node))
2722
+ check_passed = False
2723
+ for next_cond in next_cond_candidates:
2724
+ if full_graph.out_degree[next_cond] != 2:
2725
+ continue
2726
+ for next_cond_succ in full_graph.successors(next_cond):
2727
+ if full_graph.has_edge(start_node, next_cond_succ):
2728
+ check_passed = True
2729
+ break
2730
+ if check_passed:
2731
+ break
2732
+ if not check_passed:
2733
+ return False
2734
+
2735
+ r = self._match_acyclic_short_circuit_conditions_type_a(graph, full_graph, start_node)
2736
+
2737
+ if r is not None:
2738
+ left, left_cond, right, left_right_cond, succ = r
2739
+ # create the condition node
2740
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2741
+ left_cond_expr_neg = UnaryOp(None, "Not", left_cond_expr, ins_addr=start_node.addr)
2742
+ left_right_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_right_cond)
2743
+ if not self._is_single_statement_block(left):
2744
+ if not self._should_use_multistmtexprs(left):
2745
+ return False
2746
+ # create a MultiStatementExpression for left_right_cond
2747
+ stmts = self._build_multistatementexpr_statements(left)
2748
+ assert stmts is not None
2749
+ left_right_cond_expr = MultiStatementExpression(None, stmts, left_right_cond_expr, ins_addr=left.addr)
2750
+ cond = BinaryOp(None, "LogicalOr", [left_cond_expr_neg, left_right_cond_expr], ins_addr=start_node.addr)
2751
+ cond_jump = ConditionalJump(
2752
+ None,
2753
+ cond,
2754
+ Const(None, None, right.addr, self.project.arch.bits),
2755
+ Const(None, None, succ.addr, self.project.arch.bits),
2756
+ true_target_idx=right.idx if isinstance(right, (Block, MultiNode)) else None,
2757
+ false_target_idx=succ.idx if isinstance(succ, (Block, MultiNode)) else None,
2758
+ ins_addr=start_node.addr,
2759
+ stmt_idx=0,
2760
+ )
2761
+ new_cond_node = Block(start_node.addr, None, statements=[cond_jump])
2762
+ self._remove_last_statement_if_jump(start_node)
2763
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2764
+
2765
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=left if left in graph else None)
2766
+ self.replace_nodes(full_graph_raw, start_node, new_node, old_node_1=left, update_node_order=True)
2767
+
2768
+ return True
2769
+
2770
+ r = self._match_acyclic_short_circuit_conditions_type_b(graph, full_graph, start_node)
2771
+
2772
+ if r is not None:
2773
+ left, left_cond, right, right_left_cond, else_node = r
2774
+ # create the condition node
2775
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2776
+ right_left_cond_expr = self.cond_proc.convert_claripy_bool_ast(right_left_cond)
2777
+ if not self._is_single_statement_block(right):
2778
+ if not self._should_use_multistmtexprs(right):
2779
+ return False
2780
+ # create a MultiStatementExpression for left_right_cond
2781
+ stmts = self._build_multistatementexpr_statements(right)
2782
+ assert stmts is not None
2783
+ right_left_cond_expr = MultiStatementExpression(None, stmts, right_left_cond_expr, ins_addr=left.addr)
2784
+ cond = BinaryOp(None, "LogicalOr", [left_cond_expr, right_left_cond_expr], ins_addr=start_node.addr)
2785
+ cond_jump = ConditionalJump(
2786
+ None,
2787
+ cond,
2788
+ Const(None, None, left.addr, self.project.arch.bits, ins_addr=start_node.addr),
2789
+ Const(None, None, else_node.addr, self.project.arch.bits, ins_addr=start_node.addr),
2790
+ true_target_idx=left.idx if isinstance(left, (Block, MultiNode)) else None,
2791
+ false_target_idx=else_node.idx if isinstance(else_node, (Block, MultiNode)) else None,
2792
+ ins_addr=start_node.addr,
2793
+ stmt_idx=0,
2794
+ )
2795
+ new_cond_node = Block(start_node.addr, None, statements=[cond_jump])
2796
+ self._remove_last_statement_if_jump(start_node)
2797
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2798
+
2799
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=right if right in graph else None)
2800
+ self.replace_nodes(full_graph_raw, start_node, new_node, old_node_1=right, update_node_order=True)
2801
+
2802
+ return True
2803
+
2804
+ r = self._match_acyclic_short_circuit_conditions_type_c(graph, full_graph, start_node)
2805
+
2806
+ if r is not None:
2807
+ left, left_cond, succ, left_succ_cond, right = r
2808
+ # create the condition node
2809
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2810
+ left_succ_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_succ_cond)
2811
+ if not self._is_single_statement_block(left):
2812
+ if not self._should_use_multistmtexprs(left):
2813
+ return False
2814
+ # create a MultiStatementExpression for left_right_cond
2815
+ stmts = self._build_multistatementexpr_statements(left)
2816
+ assert stmts is not None
2817
+ left_succ_cond_expr = MultiStatementExpression(None, stmts, left_succ_cond_expr, ins_addr=left.addr)
2818
+ left_succ_cond_expr_neg = UnaryOp(None, "Not", left_succ_cond_expr, ins_addr=start_node.addr)
2819
+ cond = BinaryOp(None, "LogicalAnd", [left_cond_expr, left_succ_cond_expr_neg], ins_addr=start_node.addr)
2820
+ cond_jump = ConditionalJump(
2821
+ None,
2822
+ cond,
2823
+ Const(None, None, right.addr, self.project.arch.bits),
2824
+ Const(None, None, succ.addr, self.project.arch.bits),
2825
+ true_target_idx=right.idx if isinstance(right, (Block, MultiNode)) else None,
2826
+ false_target_idx=succ.idx if isinstance(succ, (Block, MultiNode)) else None,
2827
+ ins_addr=start_node.addr,
2828
+ stmt_idx=0,
2829
+ )
2830
+ new_cond_node = Block(start_node.addr, None, statements=[cond_jump])
2831
+ self._remove_last_statement_if_jump(start_node)
2832
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2833
+
2834
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=left if left in graph else None)
2835
+ self.replace_nodes(full_graph_raw, start_node, new_node, old_node_1=left, update_node_order=True)
2836
+ return True
2837
+
2838
+ r = self._match_acyclic_short_circuit_conditions_type_d(graph, full_graph, start_node)
2839
+
2840
+ if r is not None:
2841
+ left, left_cond, right, right_left_cond, else_node = r
2842
+ # create the condition node
2843
+ left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
2844
+ left_right_cond_expr = self.cond_proc.convert_claripy_bool_ast(right_left_cond)
2845
+ if not self._is_single_statement_block(left):
2846
+ if not self._should_use_multistmtexprs(left):
2847
+ return False
2848
+ # create a MultiStatementExpression for left_right_cond
2849
+ stmts = self._build_multistatementexpr_statements(left)
2850
+ assert stmts is not None
2851
+ left_right_cond_expr = MultiStatementExpression(None, stmts, left_right_cond_expr, ins_addr=left.addr)
2852
+ cond = BinaryOp(None, "LogicalAnd", [left_cond_expr, left_right_cond_expr], ins_addr=start_node.addr)
2853
+ cond_jump = ConditionalJump(
2854
+ None,
2855
+ cond,
2856
+ Const(None, None, right.addr, self.project.arch.bits),
2857
+ Const(None, None, else_node.addr, self.project.arch.bits),
2858
+ true_target_idx=right.idx if isinstance(right, (Block, MultiNode)) else None,
2859
+ false_target_idx=else_node.idx if isinstance(else_node, (Block, MultiNode)) else None,
2860
+ ins_addr=start_node.addr,
2861
+ stmt_idx=0,
2862
+ )
2863
+ new_cond_node = Block(start_node.addr, None, statements=[cond_jump])
2864
+ self._remove_last_statement_if_jump(start_node)
2865
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2866
+
2867
+ self.replace_nodes(graph_raw, start_node, new_node, old_node_1=left if left in graph else None)
2868
+ self.replace_nodes(full_graph_raw, start_node, new_node, old_node_1=left, update_node_order=True)
2869
+ return True
2870
+
2871
+ return False
2872
+
2873
+ def _match_acyclic_short_circuit_conditions_type_a( # pylint:disable=unused-argument
2874
+ self, graph, full_graph, start_node
2875
+ ) -> tuple | None:
2876
+ # if (a) goto right
2877
+ # else if (b) goto right
2878
+ # else goto other_succ
2879
+ # right:
2880
+ # ...
2881
+ # goto other_succ
2882
+ # other_succ:
2883
+
2884
+ succs = list(full_graph.successors(start_node))
2885
+ if len(succs) == 2:
2886
+ left, right = succs
2887
+
2888
+ if full_graph.in_degree[left] > 1 and full_graph.in_degree[right] == 1:
2889
+ left, right = right, left
2890
+
2891
+ # ensure left and right nodes are not the head of a switch-case construct
2892
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
2893
+ full_graph, left
2894
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, right):
2895
+ return None
2896
+
2897
+ if (
2898
+ self._is_sequential_statement_block(left)
2899
+ and full_graph.in_degree[left] == 1
2900
+ and full_graph.in_degree[right] >= 1
2901
+ ):
2902
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2903
+ # c0 = !c0
2904
+ left_succs = list(full_graph.successors(left))
2905
+ if len(left_succs) == 2 and right in left_succs:
2906
+ other_succ = next(iter(succ for succ in left_succs if succ is not right))
2907
+ if full_graph.out_degree[right] == 1 and full_graph.has_edge(right, other_succ):
2908
+ # there must be an edge between right and other_succ
2909
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, other_succ):
2910
+ # c1 = !c1
2911
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2912
+ edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2913
+ return left, edge_cond_left, right, edge_cond_left_right, other_succ
2914
+ return None
2915
+
2916
+ def _match_acyclic_short_circuit_conditions_type_b( # pylint:disable=unused-argument
2917
+ self, graph, full_graph, start_node
2918
+ ) -> tuple | None:
2919
+ # if (a) goto left
2920
+ # right:
2921
+ # else if (b) goto left
2922
+ # else goto else_node
2923
+ # left:
2924
+ # ...
2925
+ # goto succ
2926
+ # else_node:
2927
+ # ...
2928
+ # goto succ
2929
+ # succ:
2930
+
2931
+ succs = list(full_graph.successors(start_node))
2932
+ if len(succs) == 2:
2933
+ left, right = succs
2934
+
2935
+ if full_graph.in_degree[left] == 1 and full_graph.in_degree[right] >= 2:
2936
+ left, right = right, left
2937
+
2938
+ # ensure left and right nodes are not the head of a switch-case construct
2939
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
2940
+ full_graph, left
2941
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, right):
2942
+ return None
2943
+
2944
+ if (
2945
+ self._is_sequential_statement_block(right)
2946
+ and full_graph.in_degree[left] >= 2
2947
+ and full_graph.in_degree[right] == 1
2948
+ ):
2949
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, right):
2950
+ # c0 = !c0
2951
+ right_succs = list(full_graph.successors(right))
2952
+ left_succs = list(full_graph.successors(left))
2953
+ if len(right_succs) == 2 and left in right_succs:
2954
+ else_node = next(iter(succ for succ in right_succs if succ is not left))
2955
+ if len([succ for succ in left_succs if succ is not else_node]) == 1:
2956
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, right, left, else_node):
2957
+ # c1 = !c1
2958
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2959
+ edge_cond_right_left = self.cond_proc.recover_edge_condition(full_graph, right, left)
2960
+ return left, edge_cond_left, right, edge_cond_right_left, else_node
2961
+ return None
2962
+
2963
+ def _match_acyclic_short_circuit_conditions_type_c( # pylint:disable=unused-argument
2964
+ self, graph, full_graph, start_node
2965
+ ) -> tuple | None:
2966
+ # if (a) goto successor
2967
+ # else if (b) goto successor
2968
+ # right:
2969
+ # ...
2970
+ # successor:
2971
+
2972
+ succs = list(full_graph.successors(start_node))
2973
+ if len(succs) == 2:
2974
+ left, successor = succs
2975
+
2976
+ if full_graph.in_degree[left] > 1 and full_graph.in_degree[successor] == 1:
2977
+ left, successor = successor, left
2978
+
2979
+ # ensure left and successor nodes are not the head of a switch-case construct
2980
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
2981
+ full_graph, left
2982
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, successor):
2983
+ return None
2984
+
2985
+ if (
2986
+ self._is_sequential_statement_block(left)
2987
+ and full_graph.in_degree[left] == 1
2988
+ and full_graph.in_degree[successor] >= 1
2989
+ ):
2990
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, successor):
2991
+ # c0 = !c0
2992
+ left_succs = list(full_graph.successors(left))
2993
+ if len(left_succs) == 2 and successor in left_succs:
2994
+ right = next(iter(succ for succ in left_succs if succ is not successor))
2995
+ if full_graph.out_degree[right] == 1 and full_graph.has_edge(right, successor):
2996
+ # there must be an edge from right to successor
2997
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, successor):
2998
+ # c1 = !c1
2999
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
3000
+ edge_cond_left_successor = self.cond_proc.recover_edge_condition(
3001
+ full_graph, left, successor
3002
+ )
3003
+ return left, edge_cond_left, successor, edge_cond_left_successor, right
3004
+ return None
3005
+
3006
+ def _match_acyclic_short_circuit_conditions_type_d( # pylint:disable=unused-argument
3007
+ self, graph, full_graph, start_node
3008
+ ) -> tuple | None:
3009
+ # if (a) goto else_node
3010
+ # left:
3011
+ # else if (b) goto else_node
3012
+ # right:
3013
+ # ...
3014
+ # goto successor
3015
+ # else_node:
3016
+ # ...
3017
+ # goto successor
3018
+ # successor:
3019
+
3020
+ succs = list(full_graph.successors(start_node))
3021
+ if len(succs) == 2:
3022
+ left, else_node = succs
3023
+
3024
+ if full_graph.in_degree[left] > 1 and full_graph.in_degree[else_node] == 1:
3025
+ left, else_node = else_node, left
3026
+
3027
+ # ensure left and else nodes are not the head of a switch-case construct
3028
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
3029
+ full_graph, left
3030
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, else_node):
3031
+ return None
3032
+
3033
+ if (
3034
+ self._is_sequential_statement_block(left)
3035
+ and full_graph.in_degree[left] == 1
3036
+ and full_graph.in_degree[else_node] >= 1
3037
+ ):
3038
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, start_node, left, else_node):
3039
+ # c0 = !c0
3040
+ left_succs = list(full_graph.successors(left))
3041
+ if len(left_succs) == 2 and else_node in left_succs:
3042
+ right = next(iter(succ for succ in left_succs if succ is not else_node))
3043
+ if self.cond_proc.have_opposite_edge_conditions(full_graph, left, right, else_node):
3044
+ # c1 = !c1
3045
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
3046
+ edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
3047
+ return left, edge_cond_left, right, edge_cond_left_right, else_node
3048
+ return None
3049
+
3050
+ def _last_resort_refinement(self, head, graph_raw: networkx.DiGraph, full_graph_raw: networkx.DiGraph) -> bool:
3051
+ if self._improve_algorithm:
3052
+ while self._edge_virtualization_hints:
3053
+ src, dst = self._edge_virtualization_hints.pop(0)
3054
+ if _f(graph_raw).has_edge(src, dst):
3055
+ self._virtualize_edge(graph_raw, full_graph_raw, src, dst)
3056
+ l.debug("last_resort: Removed edge %r -> %r (type 3)", src, dst)
3057
+ return True
3058
+
3059
+ # virtualize an edge to allow progressing in structuring
3060
+ all_edges_wo_dominance = [] # to ensure determinism, edges in this list are ordered by a tuple of
3061
+ # (src_addr, dst_addr)
3062
+ secondary_edges = [] # likewise, edges in this list are ordered by a tuple of (src_addr, dst_addr)
3063
+ other_edges = []
3064
+
3065
+ full_graph = _f(full_graph_raw)
3066
+ graph = _f(graph_raw)
3067
+
3068
+ idoms = networkx.immediate_dominators(full_graph, head)
3069
+ if networkx.is_directed_acyclic_graph(full_graph):
3070
+ acyclic_graph = networkx.DiGraph(full_graph)
3071
+ else:
3072
+ acyclic_graph = to_acyclic_graph(full_graph, node_order=self._node_order)
3073
+ for src, dst in acyclic_graph.edges:
3074
+ if src is dst:
3075
+ continue
3076
+ if src not in graph:
3077
+ continue
3078
+ if (
3079
+ isinstance(src, Block)
3080
+ and src.statements
3081
+ and isinstance(src.statements[-1], IncompleteSwitchCaseHeadStatement)
3082
+ ):
3083
+ # this is a head of an incomplete switch-case construct (that we will definitely be structuring later),
3084
+ # so we do not want to remove any edges going out of this block
3085
+ continue
3086
+ if not dominates(idoms, src, dst) and not dominates(idoms, dst, src):
3087
+ if (src.addr, dst.addr) not in self.whitelist_edges:
3088
+ all_edges_wo_dominance.append((src, dst))
3089
+ elif not dominates(idoms, src, dst):
3090
+ if (src.addr, dst.addr) not in self.whitelist_edges:
3091
+ secondary_edges.append((src, dst))
3092
+ else:
3093
+ if (src.addr, dst.addr) not in self.whitelist_edges:
3094
+ other_edges.append((src, dst))
3095
+
3096
+ # acyclic graph may contain more than one entry node, so we may add a temporary head node to ensure all nodes
3097
+ # are accounted for in node_seq
3098
+ graph_entries = [nn for nn in acyclic_graph if acyclic_graph.in_degree[nn] == 0]
3099
+ postorder_head = head
3100
+ if len(graph_entries) > 1:
3101
+ postorder_head = Block(0, 0)
3102
+ for nn in graph_entries:
3103
+ acyclic_graph.add_edge(postorder_head, nn)
3104
+ ordered_nodes = list(
3105
+ reversed(list(GraphUtils.dfs_postorder_nodes_deterministic(acyclic_graph, postorder_head)))
3106
+ )
3107
+ if len(graph_entries) > 1:
3108
+ ordered_nodes.remove(postorder_head)
3109
+ acyclic_graph.remove_node(postorder_head)
3110
+ node_seq = {nn: (len(ordered_nodes) - idx) for (idx, nn) in enumerate(ordered_nodes)} # post-order
3111
+ if len(node_seq) < len(acyclic_graph):
3112
+ # some nodes are not reachable from head - add them to node_seq as well
3113
+ # but this is usually the result of incorrect structuring, so we may still fail at a later point
3114
+ l.warning("Adding %d unreachable nodes to node_seq", len(acyclic_graph) - len(node_seq))
3115
+ unreachable_nodes = sorted(
3116
+ (nn for nn in acyclic_graph if nn not in node_seq),
3117
+ key=lambda n: (n.addr, (-1 if n.idx is None else n.idx) if hasattr(n, "idx") else 0),
3118
+ )
3119
+ max_seq = max(node_seq.values(), default=0)
3120
+ for i, nn in enumerate(unreachable_nodes):
3121
+ node_seq[nn] = max_seq + i
3122
+
3123
+ if all_edges_wo_dominance:
3124
+ all_edges_wo_dominance = self._order_virtualizable_edges(full_graph, all_edges_wo_dominance, node_seq)
3125
+ # virtualize the first edge
3126
+ src, dst = all_edges_wo_dominance[0]
3127
+ self._virtualize_edge(graph_raw, full_graph_raw, src, dst)
3128
+ l.debug("last_resort: Removed edge %r -> %r (type 1)", src, dst)
3129
+ return True
3130
+
3131
+ if secondary_edges:
3132
+ secondary_edges = self._order_virtualizable_edges(full_graph, secondary_edges, node_seq)
3133
+ # virtualize the first edge
3134
+ src, dst = secondary_edges[0]
3135
+ self._virtualize_edge(graph_raw, full_graph_raw, src, dst)
3136
+ l.debug("last_resort: Removed edge %r -> %r (type 2)", src, dst)
3137
+ return True
3138
+
3139
+ l.debug("last_resort: No edge to remove")
3140
+ return False
3141
+
3142
+ def _virtualize_edge(self, graph, full_graph, src, dst):
3143
+ # if the last statement of src is a conditional jump, we rewrite it into a Condition(Jump) and a direct jump
3144
+ try:
3145
+ last_stmt = self.cond_proc.get_last_statement(src)
3146
+ except EmptyBlockNotice:
3147
+ last_stmt = None
3148
+ new_src = None
3149
+ remove_src_last_stmt = False
3150
+ if isinstance(last_stmt, ConditionalJump):
3151
+ if isinstance(last_stmt.true_target, Const) and last_stmt.true_target.value == dst.addr:
3152
+ goto0_condition = last_stmt.condition
3153
+ goto0_target = last_stmt.true_target
3154
+ goto1_target = last_stmt.false_target
3155
+ elif isinstance(last_stmt.false_target, Const) and last_stmt.false_target.value == dst.addr:
3156
+ goto0_condition = UnaryOp(None, "Not", last_stmt.condition)
3157
+ goto0_target = last_stmt.false_target
3158
+ goto1_target = last_stmt.true_target
3159
+ else:
3160
+ # this should not really happen...
3161
+ goto0_condition = None
3162
+ goto0_target = None
3163
+ goto1_target = None
3164
+
3165
+ if goto0_condition is not None:
3166
+ assert goto0_target is not None and goto1_target is not None
3167
+ goto0 = Block(
3168
+ last_stmt.tags["ins_addr"],
3169
+ 0,
3170
+ statements=[Jump(None, goto0_target, ins_addr=last_stmt.tags["ins_addr"], stmt_idx=0)],
3171
+ )
3172
+ cond_node = ConditionNode(last_stmt.tags["ins_addr"], None, goto0_condition, goto0)
3173
+ goto1_node = Block(
3174
+ last_stmt.tags["ins_addr"],
3175
+ 0,
3176
+ statements=[Jump(None, goto1_target, ins_addr=last_stmt.tags["ins_addr"], stmt_idx=0)],
3177
+ )
3178
+ remove_src_last_stmt = True
3179
+ new_src = SequenceNode(src.addr, nodes=[src, cond_node, goto1_node])
3180
+ elif isinstance(last_stmt, Jump):
3181
+ # do nothing
3182
+ pass
3183
+ else:
3184
+ # insert a Jump at the end
3185
+ stmt_addr = src.addr
3186
+ goto_node = Block(
3187
+ stmt_addr,
3188
+ 0,
3189
+ statements=[
3190
+ Jump(None, Const(None, None, dst.addr, self.project.arch.bits), ins_addr=stmt_addr, stmt_idx=0)
3191
+ ],
3192
+ )
3193
+ new_src = SequenceNode(src.addr, nodes=[src, goto_node])
3194
+
3195
+ if graph.has_edge(src, dst):
3196
+ graph.remove_edge(src, dst)
3197
+ self.virtualized_edges.add((src, dst))
3198
+ if new_src is not None:
3199
+ self.replace_nodes(graph, src, new_src)
3200
+ if self._node_order is not None:
3201
+ self._node_order[new_src] = self._node_order[src]
3202
+ if full_graph is not None:
3203
+ self.virtualized_edges.add((src, dst))
3204
+ full_graph.remove_edge(src, dst)
3205
+ if new_src is not None:
3206
+ self.replace_nodes(full_graph, src, new_src, update_node_order=True)
3207
+ if remove_src_last_stmt:
3208
+ remove_last_statements(src)
3209
+
3210
+ def _should_use_multistmtexprs(self, node: Block | BaseNode) -> bool:
3211
+ """
3212
+ The original Phoenix algorithm had no support for multi-stmt expressions, such as the following:
3213
+ if ((x = y) && z) { ... }
3214
+
3215
+ There are multiple levels at which multi-stmt expressions can be used. If the Phoenix algorithm is not not
3216
+ set to be in improved mode, then we should not use multi-stmt expressions at all.
3217
+ """
3218
+ if not self._improve_algorithm:
3219
+ return False
3220
+ if self._use_multistmtexprs == MultiStmtExprMode.NEVER:
3221
+ return False
3222
+ if self._use_multistmtexprs == MultiStmtExprMode.ALWAYS:
3223
+ ctr = AILCallCounter()
3224
+ ctr.walk(node)
3225
+ return ctr.non_label_stmts <= self._multistmtexpr_stmt_threshold
3226
+ if self._use_multistmtexprs == MultiStmtExprMode.MAX_ONE_CALL:
3227
+ # count the number of calls
3228
+ ctr = AILCallCounter()
3229
+ ctr.walk(node)
3230
+ return ctr.calls <= 1 and ctr.non_label_stmts <= self._multistmtexpr_stmt_threshold
3231
+ l.warning("Unsupported enum value for _use_multistmtexprs: %s", self._use_multistmtexprs)
3232
+ return False
3233
+
3234
+ @staticmethod
3235
+ def _find_node_going_to_dst(
3236
+ node: BaseNode,
3237
+ dst: Block | BaseNode,
3238
+ last=True,
3239
+ condjump_only=False,
3240
+ enter_loops=True,
3241
+ ) -> tuple[int | None, BaseNode | None, Block | MultiNode | BreakNode | SequenceNode | None]:
3242
+ """
3243
+
3244
+ :param node:
3245
+ :param dst_addr:
3246
+ :param dst_idx:
3247
+ :return: A tuple of (parent node, node who has a successor of dst_addr)
3248
+ """
3249
+
3250
+ dst_addr = dst.addr
3251
+ dst_idx = dst.idx if isinstance(dst, Block) else ...
3252
+
3253
+ class _Holder:
3254
+ """
3255
+ Holds parent_and_block and is accessible from within the handlers.
3256
+ """
3257
+
3258
+ parent_and_block: list[tuple[int, Any, Block | MultiNode | BreakNode | SequenceNode]] = []
3259
+ block_id: int = -1
3260
+
3261
+ def _check(last_stmt, force_condjump: bool = False):
3262
+ return (
3263
+ (
3264
+ (force_condjump or not condjump_only)
3265
+ and isinstance(last_stmt, Jump)
3266
+ and isinstance(last_stmt.target, Const)
3267
+ and last_stmt.target.value == dst_addr
3268
+ and (dst_idx is ... or last_stmt.target_idx == dst_idx)
3269
+ )
3270
+ or (
3271
+ isinstance(last_stmt, ConditionalJump)
3272
+ and (
3273
+ (
3274
+ isinstance(last_stmt.true_target, Const)
3275
+ and last_stmt.true_target.value == dst_addr
3276
+ and (dst_idx is ... or last_stmt.true_target_idx == dst_idx)
3277
+ )
3278
+ or (
3279
+ isinstance(last_stmt.false_target, Const)
3280
+ and last_stmt.false_target.value == dst_addr
3281
+ and (dst_idx is ... or last_stmt.false_target_idx == dst_idx)
3282
+ )
3283
+ )
3284
+ )
3285
+ or (
3286
+ isinstance(last_stmt, IncompleteSwitchCaseHeadStatement)
3287
+ and any(case_addr == dst_addr for _, _, _, _, case_addr in last_stmt.case_addrs)
3288
+ )
3289
+ )
3290
+
3291
+ def _handle_Block(block: Block, parent=None, **kwargs): # pylint:disable=unused-argument
3292
+ if block.statements:
3293
+ first_stmt = first_nonlabel_nonphi_statement(block)
3294
+ if first_stmt is not None:
3295
+ # this block has content. increment the block ID counter
3296
+ _Holder.block_id += 1
3297
+
3298
+ if _check(first_stmt):
3299
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
3300
+ elif len(block.statements) > 1:
3301
+ last_stmt = block.statements[-1]
3302
+ if _check(last_stmt) or (
3303
+ not isinstance(last_stmt, (Jump, ConditionalJump))
3304
+ and block.addr + block.original_size == dst_addr
3305
+ ):
3306
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
3307
+
3308
+ def _handle_MultiNode(block: MultiNode, parent=None, **kwargs): # pylint:disable=unused-argument
3309
+ if block.nodes and isinstance(block.nodes[-1], Block) and block.nodes[-1].statements:
3310
+ first_stmt = first_nonlabel_nonphi_statement(block)
3311
+ if first_stmt is not None:
3312
+ # this block has content. increment the block ID counter
3313
+ _Holder.block_id += 1
3314
+ if _check(block.nodes[-1].statements[-1]):
3315
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
3316
+
3317
+ def _handle_BreakNode(break_node: BreakNode, parent=None, **kwargs): # pylint:disable=unused-argument
3318
+ _Holder.block_id += 1
3319
+ if break_node.target == dst_addr or (
3320
+ isinstance(break_node.target, Const) and break_node.target.value == dst_addr
3321
+ ):
3322
+ # FIXME: idx is ignored
3323
+ _Holder.parent_and_block.append((_Holder.block_id, parent, break_node))
3324
+
3325
+ def _handle_ConditionNode(cond_node: ConditionNode, parent=None, **kwargs): # pylint:disable=unused-argument
3326
+ _Holder.block_id += 1
3327
+ if (
3328
+ isinstance(parent, SequenceNode)
3329
+ and parent.nodes
3330
+ and parent.nodes[-1] is cond_node
3331
+ and isinstance(cond_node.true_node, Block)
3332
+ and cond_node.true_node.statements
3333
+ and isinstance(cond_node.false_node, Block)
3334
+ and cond_node.false_node.statements
3335
+ ):
3336
+ if _check(cond_node.true_node.statements[-1], force_condjump=True) or _check(
3337
+ cond_node.false_node.statements[-1], force_condjump=True
3338
+ ):
3339
+ # we insert the parent node (the SequenceNode) instead
3340
+ _Holder.parent_and_block.append((_Holder.block_id, None, parent))
3341
+
3342
+ def _handle_Loop(loop_node: LoopNode, parent=None, **kwargs): # pylint:disable=unused-argument
3343
+ if enter_loops:
3344
+ walker._handle_Loop(loop_node, parent=parent, **kwargs)
3345
+
3346
+ walker = SequenceWalker(
3347
+ handlers={
3348
+ Block: _handle_Block,
3349
+ MultiNode: _handle_MultiNode,
3350
+ BreakNode: _handle_BreakNode,
3351
+ ConditionNode: _handle_ConditionNode,
3352
+ LoopNode: _handle_Loop,
3353
+ },
3354
+ update_seqnode_in_place=False,
3355
+ force_forward_scan=True,
3356
+ )
3357
+ _Holder.block_id = -1
3358
+ walker.walk(node)
3359
+ if not _Holder.parent_and_block:
3360
+ return None, None, None
3361
+ if last:
3362
+ return _Holder.parent_and_block[-1]
3363
+ return _Holder.parent_and_block[0]
3364
+
3365
+ @staticmethod
3366
+ def _unpack_sequencenode_head(graph: networkx.DiGraph, seq: SequenceNode, new_seq=None):
3367
+ if not seq.nodes:
3368
+ return False, None
3369
+ node = seq.nodes[0]
3370
+ if new_seq is None:
3371
+ # create the new sequence node if no prior-created sequence node is passed in
3372
+ new_seq = seq.copy()
3373
+ new_seq.nodes = new_seq.nodes[1:]
3374
+ if new_seq.nodes:
3375
+ new_seq.addr = new_seq.nodes[0].addr
3376
+
3377
+ preds = list(graph.predecessors(seq))
3378
+ succs = list(graph.successors(seq))
3379
+ graph.remove_node(seq)
3380
+ for pred in preds:
3381
+ graph.add_edge(pred, node)
3382
+ if new_seq.nodes:
3383
+ graph.add_edge(node, new_seq)
3384
+ for succ in succs:
3385
+ if succ is seq:
3386
+ graph.add_edge(new_seq, new_seq)
3387
+ else:
3388
+ graph.add_edge(new_seq, succ)
3389
+ return True, new_seq
3390
+
3391
+ @staticmethod
3392
+ def _unpack_incompleteswitchcasenode(
3393
+ graph: networkx.DiGraph, incscnode: IncompleteSwitchCaseNode, jumptable_entries: list[int]
3394
+ ) -> bool:
3395
+ preds = list(graph.predecessors(incscnode))
3396
+ succs = list(graph.successors(incscnode))
3397
+ non_case_succs = [succ for succ in succs if succ.addr not in jumptable_entries]
3398
+ if len(non_case_succs) <= 1:
3399
+ graph.remove_node(incscnode)
3400
+ for pred in preds:
3401
+ graph.add_edge(pred, incscnode.head)
3402
+ for succ in succs:
3403
+ if succ not in non_case_succs:
3404
+ graph.add_edge(incscnode.head, succ)
3405
+ for case_node in incscnode.cases:
3406
+ graph.add_edge(incscnode.head, case_node)
3407
+ if non_case_succs:
3408
+ graph.add_edge(case_node, non_case_succs[0])
3409
+ return True
3410
+ return False
3411
+
3412
+ @staticmethod
3413
+ def _count_statements(node: BaseNode | Block) -> int:
3414
+ if isinstance(node, Block):
3415
+ return sum(1 for stmt in node.statements if not isinstance(stmt, Label) and not is_phi_assignment(stmt))
3416
+ if isinstance(node, (MultiNode, SequenceNode)):
3417
+ return sum(PhoenixStructurer._count_statements(nn) for nn in node.nodes)
3418
+ return 1
3419
+
3420
+ @staticmethod
3421
+ def _is_single_statement_block(node: BaseNode | Block) -> bool:
3422
+ if isinstance(node, (Block, MultiNode, SequenceNode)):
3423
+ return PhoenixStructurer._count_statements(node) == 1
3424
+ return False
3425
+
3426
+ @staticmethod
3427
+ def _is_sequential_statement_block(node: BaseNode | Block) -> bool:
3428
+ """
3429
+ Examine if the node can be converted into a MultiStatementExpression object. The conversion fails if there are
3430
+ any conditional statements or goto statements before the very last statement of the node.
3431
+ """
3432
+
3433
+ def _is_sequential_statement_list(stmts: list[Statement]) -> bool:
3434
+ if not stmts:
3435
+ return True
3436
+ return all(not isinstance(stmt, (ConditionalJump, Jump)) for stmt in stmts[:-1])
3437
+
3438
+ def _to_statement_list(node: Block | MultiNode | SequenceNode | BaseNode) -> list[Statement]:
3439
+ if isinstance(node, Block):
3440
+ return node.statements
3441
+ if isinstance(node, MultiNode):
3442
+ # expand it
3443
+ all_statements = []
3444
+ for nn in node.nodes:
3445
+ all_statements += _to_statement_list(nn)
3446
+ return all_statements
3447
+ if isinstance(node, SequenceNode):
3448
+ all_statements = []
3449
+ for nn in node.nodes:
3450
+ all_statements += _to_statement_list(nn)
3451
+ return all_statements
3452
+ raise TypeError(f"Unsupported node type {type(node)}")
3453
+
3454
+ try:
3455
+ stmt_list = _to_statement_list(node)
3456
+ except TypeError:
3457
+ return False
3458
+ return _is_sequential_statement_list(stmt_list)
3459
+
3460
+ @staticmethod
3461
+ def _build_multistatementexpr_statements(block) -> list[Statement] | None:
3462
+ stmts = []
3463
+ if isinstance(block, (SequenceNode, MultiNode)):
3464
+ for b in block.nodes:
3465
+ stmts_ = PhoenixStructurer._build_multistatementexpr_statements(b)
3466
+ if stmts_ is None:
3467
+ return None
3468
+ stmts += stmts_
3469
+ return stmts
3470
+ if isinstance(block, Block):
3471
+ for idx, stmt in enumerate(block.statements):
3472
+ if isinstance(stmt, Label):
3473
+ continue
3474
+ if is_phi_assignment(stmt):
3475
+ continue
3476
+ if isinstance(stmt, ConditionalJump):
3477
+ if idx == len(block.statements) - 1:
3478
+ continue
3479
+ return None
3480
+ if isinstance(stmt, Jump):
3481
+ return None
3482
+ stmts.append(stmt)
3483
+ return stmts
3484
+ return None
3485
+
3486
+ @staticmethod
3487
+ def _remove_edges_except(graph: networkx.DiGraph, src, dst):
3488
+ for succ in list(graph.successors(src)):
3489
+ if succ is not src and succ is not dst:
3490
+ graph.remove_edge(src, succ)
3491
+
3492
+ @staticmethod
3493
+ def _remove_first_statement_if_jump(node: BaseNode | Block | MultiNode) -> Jump | ConditionalJump | None:
3494
+ if isinstance(node, Block):
3495
+ if node.statements:
3496
+ idx = 0
3497
+ first_stmt = node.statements[idx]
3498
+ while isinstance(first_stmt, Label) or is_phi_assignment(first_stmt):
3499
+ idx += 1
3500
+ if idx >= len(node.statements):
3501
+ first_stmt = None
3502
+ break
3503
+ first_stmt = node.statements[idx]
3504
+
3505
+ if isinstance(first_stmt, (Jump, ConditionalJump)):
3506
+ if idx == 0:
3507
+ node.statements = node.statements[1:]
3508
+ else:
3509
+ node.statements = node.statements[0:idx] + node.statements[idx + 1 :]
3510
+ return first_stmt
3511
+ return None
3512
+ if isinstance(node, MultiNode):
3513
+ for nn in node.nodes:
3514
+ if isinstance(nn, Block):
3515
+ if not has_nonlabel_nonphi_statements(nn):
3516
+ continue
3517
+ return PhoenixStructurer._remove_first_statement_if_jump(nn)
3518
+ break
3519
+ return None
3520
+
3521
+ # pylint: disable=unused-argument,no-self-use
3522
+ def _order_virtualizable_edges(self, graph: networkx.DiGraph, edges: list, node_seq: dict[Any, int]) -> list:
3523
+ """
3524
+ Returns a list of edges that are ordered by the best edges to virtualize first.
3525
+ """
3526
+ return PhoenixStructurer._chick_order_edges(edges, node_seq)
3527
+
3528
+ @staticmethod
3529
+ def _chick_order_edges(edges: list, node_seq: dict[Any, int]) -> list:
3530
+ graph = networkx.DiGraph()
3531
+ graph.add_edges_from(edges)
3532
+
3533
+ def _sort_edge(edge_):
3534
+ # this is a bit complex. we first sort based on the topological order of the destination node; edges with
3535
+ # destination nodes that are closer to the head (as specified in node_seq) should be virtualized first.
3536
+ # then we solve draws by prioritizing edges whose destination nodes are with a lower in-degree (only
3537
+ # consider the sub graph with these edges), and a few other properties.
3538
+ src, dst = edge_
3539
+ dst_in_degree = graph.in_degree[dst]
3540
+ src_out_degree = graph.out_degree[src]
3541
+ return -node_seq[dst], dst_in_degree, src_out_degree, -src.addr, -dst.addr # type: ignore
3542
+
3543
+ return sorted(edges, key=_sort_edge, reverse=True)
3544
+
3545
+ def _generate_node_order(self):
3546
+ the_graph = (
3547
+ self._region.graph_with_successors if self._region.graph_with_successors is not None else self._region.graph
3548
+ )
3549
+ the_head = self._region.head
3550
+ ordered_nodes = GraphUtils.quasi_topological_sort_nodes(
3551
+ the_graph,
3552
+ loop_heads=[the_head],
3553
+ )
3554
+ self._node_order = {n: i for i, n in enumerate(ordered_nodes)}
3555
+
3556
+ def replace_nodes(
3557
+ self,
3558
+ graph,
3559
+ old_node_0,
3560
+ new_node,
3561
+ old_node_1=None,
3562
+ self_loop=True,
3563
+ update_node_order: bool = False,
3564
+ drop_refinement_marks: bool = False,
3565
+ ):
3566
+ super().replace_nodes(graph, old_node_0, new_node, old_node_1=old_node_1, self_loop=self_loop)
3567
+ if drop_refinement_marks:
3568
+ for _, dst in list(graph.out_edges(new_node)):
3569
+ if "cyclic_refinement_outgoing" in graph[new_node][dst]:
3570
+ del graph[new_node][dst]["cyclic_refinement_outgoing"]
3571
+ if self._node_order is not None and update_node_order:
3572
+ if old_node_1 is not None:
3573
+ self._node_order[new_node] = min(self._node_order[old_node_0], self._node_order[old_node_1])
3574
+ else:
3575
+ self._node_order[new_node] = self._node_order[old_node_0]
3576
+
3577
+ @staticmethod
3578
+ def _replace_node_in_edge_list(edge_list: list[tuple], old_node, new_node) -> None:
3579
+ for idx in range(len(edge_list)): # pylint:disable=consider-using-enumerate
3580
+ edge = edge_list[idx]
3581
+ src, dst = edge
3582
+ replace = False
3583
+ if src is old_node:
3584
+ src = new_node
3585
+ replace = True
3586
+ if dst is old_node:
3587
+ dst = new_node
3588
+ replace = True
3589
+ if replace:
3590
+ edge_list[idx] = src, dst
3591
+
3592
+ @staticmethod
3593
+ def dump_graph(graph: networkx.DiGraph, path: str) -> None:
3594
+ graph_with_str = networkx.DiGraph()
3595
+
3596
+ for node in graph:
3597
+ graph_with_str.add_node(f'"{node!r}"')
3598
+
3599
+ for src, dst, data in graph.edges(data=True):
3600
+ data_dict = {} if data.get("cyclic_refinement_outgoing", False) is False else {"CRO": "True"}
3601
+ graph_with_str.add_edge(f'"{src!r}"', f'"{dst!r}"', **data_dict)
3602
+
3603
+ networkx.drawing.nx_pydot.write_dot(graph_with_str, path)
3604
+
3605
+ @staticmethod
3606
+ def switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred) -> bool:
3607
+ all_succs = set()
3608
+ for case_node in case_nodes:
3609
+ if case_node is node_pred:
3610
+ continue
3611
+ if case_node.addr in jump_table.jumptable_entries:
3612
+ all_succs |= set(graph.successors(case_node))
3613
+
3614
+ case_node_successors = set()
3615
+ for case_node in case_nodes:
3616
+ if case_node is node_pred:
3617
+ continue
3618
+ if case_node in all_succs:
3619
+ continue
3620
+ if case_node.addr in jump_table.jumptable_entries:
3621
+ succs = set(graph.successors(case_node))
3622
+ case_node_successors |= {succ for succ in succs if succ.addr not in jump_table.jumptable_entries}
3623
+
3624
+ return len(case_node_successors) <= 1
3625
+
3626
+ @staticmethod
3627
+ def switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred) -> bool:
3628
+ case_node_successors = set()
3629
+ for case_node in case_nodes:
3630
+ if case_node is node_pred:
3631
+ continue
3632
+ if case_node.addr in jump_table.jumptable_entries:
3633
+ succs = set(graph.successors(case_node))
3634
+ case_node_successors |= {succ for succ in succs if succ.addr not in jump_table.jumptable_entries}
3635
+
3636
+ return len(case_node_successors) <= 1