angr 9.2.158__cp310-abi3-manylinux2014_aarch64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (1393) hide show
  1. angr/__init__.py +363 -0
  2. angr/__main__.py +152 -0
  3. angr/analyses/__init__.py +113 -0
  4. angr/analyses/analysis.py +407 -0
  5. angr/analyses/backward_slice.py +686 -0
  6. angr/analyses/binary_optimizer.py +670 -0
  7. angr/analyses/bindiff.py +1512 -0
  8. angr/analyses/boyscout.py +76 -0
  9. angr/analyses/callee_cleanup_finder.py +74 -0
  10. angr/analyses/calling_convention/__init__.py +6 -0
  11. angr/analyses/calling_convention/calling_convention.py +1092 -0
  12. angr/analyses/calling_convention/fact_collector.py +636 -0
  13. angr/analyses/calling_convention/utils.py +60 -0
  14. angr/analyses/cdg.py +189 -0
  15. angr/analyses/cfg/__init__.py +23 -0
  16. angr/analyses/cfg/cfb.py +428 -0
  17. angr/analyses/cfg/cfg.py +74 -0
  18. angr/analyses/cfg/cfg_arch_options.py +95 -0
  19. angr/analyses/cfg/cfg_base.py +2902 -0
  20. angr/analyses/cfg/cfg_emulated.py +3447 -0
  21. angr/analyses/cfg/cfg_fast.py +5278 -0
  22. angr/analyses/cfg/cfg_fast_soot.py +662 -0
  23. angr/analyses/cfg/cfg_job_base.py +203 -0
  24. angr/analyses/cfg/indirect_jump_resolvers/__init__.py +28 -0
  25. angr/analyses/cfg/indirect_jump_resolvers/amd64_elf_got.py +62 -0
  26. angr/analyses/cfg/indirect_jump_resolvers/amd64_pe_iat.py +51 -0
  27. angr/analyses/cfg/indirect_jump_resolvers/arm_elf_fast.py +149 -0
  28. angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +186 -0
  29. angr/analyses/cfg/indirect_jump_resolvers/constant_value_manager.py +107 -0
  30. angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +76 -0
  31. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +2367 -0
  32. angr/analyses/cfg/indirect_jump_resolvers/memload_resolver.py +81 -0
  33. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +286 -0
  34. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_got.py +148 -0
  35. angr/analyses/cfg/indirect_jump_resolvers/propagator_utils.py +46 -0
  36. angr/analyses/cfg/indirect_jump_resolvers/resolver.py +74 -0
  37. angr/analyses/cfg/indirect_jump_resolvers/syscall_resolver.py +92 -0
  38. angr/analyses/cfg/indirect_jump_resolvers/x86_elf_pic_plt.py +88 -0
  39. angr/analyses/cfg/indirect_jump_resolvers/x86_pe_iat.py +47 -0
  40. angr/analyses/cfg_slice_to_sink/__init__.py +11 -0
  41. angr/analyses/cfg_slice_to_sink/cfg_slice_to_sink.py +117 -0
  42. angr/analyses/cfg_slice_to_sink/graph.py +87 -0
  43. angr/analyses/cfg_slice_to_sink/transitions.py +27 -0
  44. angr/analyses/class_identifier.py +63 -0
  45. angr/analyses/code_tagging.py +123 -0
  46. angr/analyses/codecave.py +77 -0
  47. angr/analyses/complete_calling_conventions.py +461 -0
  48. angr/analyses/congruency_check.py +377 -0
  49. angr/analyses/data_dep/__init__.py +16 -0
  50. angr/analyses/data_dep/data_dependency_analysis.py +595 -0
  51. angr/analyses/data_dep/dep_nodes.py +171 -0
  52. angr/analyses/data_dep/sim_act_location.py +49 -0
  53. angr/analyses/datagraph_meta.py +105 -0
  54. angr/analyses/ddg.py +1670 -0
  55. angr/analyses/decompiler/__init__.py +41 -0
  56. angr/analyses/decompiler/ail_simplifier.py +1972 -0
  57. angr/analyses/decompiler/ailgraph_walker.py +49 -0
  58. angr/analyses/decompiler/block_io_finder.py +302 -0
  59. angr/analyses/decompiler/block_similarity.py +196 -0
  60. angr/analyses/decompiler/block_simplifier.py +371 -0
  61. angr/analyses/decompiler/callsite_maker.py +555 -0
  62. angr/analyses/decompiler/ccall_rewriters/__init__.py +9 -0
  63. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +580 -0
  64. angr/analyses/decompiler/ccall_rewriters/rewriter_base.py +20 -0
  65. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +313 -0
  66. angr/analyses/decompiler/clinic.py +3304 -0
  67. angr/analyses/decompiler/condition_processor.py +1256 -0
  68. angr/analyses/decompiler/counters/__init__.py +16 -0
  69. angr/analyses/decompiler/counters/boolean_counter.py +27 -0
  70. angr/analyses/decompiler/counters/call_counter.py +47 -0
  71. angr/analyses/decompiler/counters/expression_counters.py +77 -0
  72. angr/analyses/decompiler/counters/seq_cf_structure_counter.py +63 -0
  73. angr/analyses/decompiler/decompilation_cache.py +46 -0
  74. angr/analyses/decompiler/decompilation_options.py +275 -0
  75. angr/analyses/decompiler/decompiler.py +703 -0
  76. angr/analyses/decompiler/dephication/__init__.py +6 -0
  77. angr/analyses/decompiler/dephication/dephication_base.py +89 -0
  78. angr/analyses/decompiler/dephication/graph_dephication.py +63 -0
  79. angr/analyses/decompiler/dephication/graph_rewriting.py +116 -0
  80. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +326 -0
  81. angr/analyses/decompiler/dephication/rewriting_engine.py +504 -0
  82. angr/analyses/decompiler/dephication/seqnode_dephication.py +134 -0
  83. angr/analyses/decompiler/empty_node_remover.py +212 -0
  84. angr/analyses/decompiler/expression_narrower.py +287 -0
  85. angr/analyses/decompiler/goto_manager.py +112 -0
  86. angr/analyses/decompiler/graph_region.py +398 -0
  87. angr/analyses/decompiler/jump_target_collector.py +37 -0
  88. angr/analyses/decompiler/jumptable_entry_condition_rewriter.py +67 -0
  89. angr/analyses/decompiler/label_collector.py +32 -0
  90. angr/analyses/decompiler/optimization_passes/__init__.py +151 -0
  91. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +157 -0
  92. angr/analyses/decompiler/optimization_passes/call_stmt_rewriter.py +46 -0
  93. angr/analyses/decompiler/optimization_passes/code_motion.py +362 -0
  94. angr/analyses/decompiler/optimization_passes/condition_constprop.py +219 -0
  95. angr/analyses/decompiler/optimization_passes/const_derefs.py +266 -0
  96. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +365 -0
  97. angr/analyses/decompiler/optimization_passes/cross_jump_reverter.py +106 -0
  98. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +82 -0
  99. angr/analyses/decompiler/optimization_passes/determine_load_sizes.py +64 -0
  100. angr/analyses/decompiler/optimization_passes/div_simplifier.py +425 -0
  101. angr/analyses/decompiler/optimization_passes/duplication_reverter/__init__.py +5 -0
  102. angr/analyses/decompiler/optimization_passes/duplication_reverter/ail_merge_graph.py +503 -0
  103. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +1218 -0
  104. angr/analyses/decompiler/optimization_passes/duplication_reverter/errors.py +16 -0
  105. angr/analyses/decompiler/optimization_passes/duplication_reverter/similarity.py +126 -0
  106. angr/analyses/decompiler/optimization_passes/duplication_reverter/utils.py +167 -0
  107. angr/analyses/decompiler/optimization_passes/eager_std_string_concatenation.py +165 -0
  108. angr/analyses/decompiler/optimization_passes/engine_base.py +500 -0
  109. angr/analyses/decompiler/optimization_passes/expr_op_swapper.py +135 -0
  110. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +113 -0
  111. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +615 -0
  112. angr/analyses/decompiler/optimization_passes/ite_expr_converter.py +224 -0
  113. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +335 -0
  114. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +922 -0
  115. angr/analyses/decompiler/optimization_passes/mod_simplifier.py +99 -0
  116. angr/analyses/decompiler/optimization_passes/optimization_pass.py +659 -0
  117. angr/analyses/decompiler/optimization_passes/register_save_area_simplifier.py +221 -0
  118. angr/analyses/decompiler/optimization_passes/ret_addr_save_simplifier.py +171 -0
  119. angr/analyses/decompiler/optimization_passes/ret_deduplicator.py +222 -0
  120. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +640 -0
  121. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +61 -0
  122. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +235 -0
  123. angr/analyses/decompiler/optimization_passes/stack_canary_simplifier.py +333 -0
  124. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +149 -0
  125. angr/analyses/decompiler/optimization_passes/switch_reused_entry_rewriter.py +102 -0
  126. angr/analyses/decompiler/optimization_passes/tag_slicer.py +41 -0
  127. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +421 -0
  128. angr/analyses/decompiler/optimization_passes/x86_gcc_getpc_simplifier.py +88 -0
  129. angr/analyses/decompiler/peephole_optimizations/__init__.py +127 -0
  130. angr/analyses/decompiler/peephole_optimizations/a_div_const_add_a_mul_n_div_const.py +42 -0
  131. angr/analyses/decompiler/peephole_optimizations/a_mul_const_div_shr_const.py +38 -0
  132. angr/analyses/decompiler/peephole_optimizations/a_mul_const_sub_a.py +34 -0
  133. angr/analyses/decompiler/peephole_optimizations/a_shl_const_sub_a.py +34 -0
  134. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div.py +25 -0
  135. angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +57 -0
  136. angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +37 -0
  137. angr/analyses/decompiler/peephole_optimizations/a_sub_a_sub_n.py +23 -0
  138. angr/analyses/decompiler/peephole_optimizations/arm_cmpf.py +236 -0
  139. angr/analyses/decompiler/peephole_optimizations/base.py +157 -0
  140. angr/analyses/decompiler/peephole_optimizations/basepointeroffset_add_n.py +34 -0
  141. angr/analyses/decompiler/peephole_optimizations/basepointeroffset_and_mask.py +36 -0
  142. angr/analyses/decompiler/peephole_optimizations/bitwise_or_to_logical_or.py +34 -0
  143. angr/analyses/decompiler/peephole_optimizations/bool_expr_xor_1.py +27 -0
  144. angr/analyses/decompiler/peephole_optimizations/bswap.py +142 -0
  145. angr/analyses/decompiler/peephole_optimizations/cas_intrinsics.py +115 -0
  146. angr/analyses/decompiler/peephole_optimizations/cmpord_rewriter.py +71 -0
  147. angr/analyses/decompiler/peephole_optimizations/coalesce_adjacent_shrs.py +39 -0
  148. angr/analyses/decompiler/peephole_optimizations/coalesce_same_cascading_ifs.py +28 -0
  149. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +242 -0
  150. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +44 -0
  151. angr/analyses/decompiler/peephole_optimizations/conv_a_sub0_shr_and.py +69 -0
  152. angr/analyses/decompiler/peephole_optimizations/conv_shl_shr.py +52 -0
  153. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +394 -0
  154. angr/analyses/decompiler/peephole_optimizations/extended_byte_and_mask.py +56 -0
  155. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +160 -0
  156. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +109 -0
  157. angr/analyses/decompiler/peephole_optimizations/inlined_wstrcpy.py +170 -0
  158. angr/analyses/decompiler/peephole_optimizations/invert_negated_logical_conjuction_disjunction.py +50 -0
  159. angr/analyses/decompiler/peephole_optimizations/one_sub_bool.py +33 -0
  160. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +45 -0
  161. angr/analyses/decompiler/peephole_optimizations/remove_cxx_destructor_calls.py +32 -0
  162. angr/analyses/decompiler/peephole_optimizations/remove_empty_if_body.py +46 -0
  163. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +47 -0
  164. angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +116 -0
  165. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +273 -0
  166. angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_branch.py +30 -0
  167. angr/analyses/decompiler/peephole_optimizations/remove_redundant_ite_comparisons.py +54 -0
  168. angr/analyses/decompiler/peephole_optimizations/remove_redundant_nots.py +36 -0
  169. angr/analyses/decompiler/peephole_optimizations/remove_redundant_reinterprets.py +44 -0
  170. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +95 -0
  171. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts_around_comparators.py +44 -0
  172. angr/analyses/decompiler/peephole_optimizations/rewrite_bit_extractions.py +85 -0
  173. angr/analyses/decompiler/peephole_optimizations/rewrite_conv_mul.py +40 -0
  174. angr/analyses/decompiler/peephole_optimizations/rewrite_cxx_operator_calls.py +90 -0
  175. angr/analyses/decompiler/peephole_optimizations/rewrite_mips_gp_loads.py +49 -0
  176. angr/analyses/decompiler/peephole_optimizations/rol_ror.py +130 -0
  177. angr/analyses/decompiler/peephole_optimizations/sar_to_signed_div.py +143 -0
  178. angr/analyses/decompiler/peephole_optimizations/shl_to_mul.py +25 -0
  179. angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +51 -0
  180. angr/analyses/decompiler/peephole_optimizations/single_bit_cond_to_boolexpr.py +82 -0
  181. angr/analyses/decompiler/peephole_optimizations/single_bit_xor.py +29 -0
  182. angr/analyses/decompiler/peephole_optimizations/tidy_stack_addr.py +131 -0
  183. angr/analyses/decompiler/peephole_optimizations/utils.py +18 -0
  184. angr/analyses/decompiler/presets/__init__.py +20 -0
  185. angr/analyses/decompiler/presets/basic.py +32 -0
  186. angr/analyses/decompiler/presets/fast.py +58 -0
  187. angr/analyses/decompiler/presets/full.py +68 -0
  188. angr/analyses/decompiler/presets/preset.py +37 -0
  189. angr/analyses/decompiler/redundant_label_remover.py +134 -0
  190. angr/analyses/decompiler/region_identifier.py +1218 -0
  191. angr/analyses/decompiler/region_simplifiers/__init__.py +5 -0
  192. angr/analyses/decompiler/region_simplifiers/cascading_cond_transformer.py +95 -0
  193. angr/analyses/decompiler/region_simplifiers/cascading_ifs.py +82 -0
  194. angr/analyses/decompiler/region_simplifiers/expr_folding.py +789 -0
  195. angr/analyses/decompiler/region_simplifiers/goto.py +178 -0
  196. angr/analyses/decompiler/region_simplifiers/if_.py +135 -0
  197. angr/analyses/decompiler/region_simplifiers/ifelse.py +91 -0
  198. angr/analyses/decompiler/region_simplifiers/loop.py +143 -0
  199. angr/analyses/decompiler/region_simplifiers/node_address_finder.py +24 -0
  200. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +234 -0
  201. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +654 -0
  202. angr/analyses/decompiler/region_simplifiers/switch_expr_simplifier.py +87 -0
  203. angr/analyses/decompiler/region_walker.py +24 -0
  204. angr/analyses/decompiler/return_maker.py +72 -0
  205. angr/analyses/decompiler/seq_to_blocks.py +20 -0
  206. angr/analyses/decompiler/sequence_walker.py +257 -0
  207. angr/analyses/decompiler/ssailification/__init__.py +4 -0
  208. angr/analyses/decompiler/ssailification/rewriting.py +379 -0
  209. angr/analyses/decompiler/ssailification/rewriting_engine.py +1053 -0
  210. angr/analyses/decompiler/ssailification/rewriting_state.py +61 -0
  211. angr/analyses/decompiler/ssailification/ssailification.py +276 -0
  212. angr/analyses/decompiler/ssailification/traversal.py +124 -0
  213. angr/analyses/decompiler/ssailification/traversal_engine.py +297 -0
  214. angr/analyses/decompiler/ssailification/traversal_state.py +48 -0
  215. angr/analyses/decompiler/stack_item.py +36 -0
  216. angr/analyses/decompiler/structured_codegen/__init__.py +25 -0
  217. angr/analyses/decompiler/structured_codegen/base.py +132 -0
  218. angr/analyses/decompiler/structured_codegen/c.py +4082 -0
  219. angr/analyses/decompiler/structured_codegen/dummy.py +15 -0
  220. angr/analyses/decompiler/structured_codegen/dwarf_import.py +190 -0
  221. angr/analyses/decompiler/structuring/__init__.py +30 -0
  222. angr/analyses/decompiler/structuring/dream.py +1217 -0
  223. angr/analyses/decompiler/structuring/phoenix.py +2999 -0
  224. angr/analyses/decompiler/structuring/recursive_structurer.py +187 -0
  225. angr/analyses/decompiler/structuring/sailr.py +112 -0
  226. angr/analyses/decompiler/structuring/structurer_base.py +1067 -0
  227. angr/analyses/decompiler/structuring/structurer_nodes.py +438 -0
  228. angr/analyses/decompiler/utils.py +1106 -0
  229. angr/analyses/deobfuscator/__init__.py +18 -0
  230. angr/analyses/deobfuscator/api_obf_finder.py +325 -0
  231. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +51 -0
  232. angr/analyses/deobfuscator/api_obf_type2_finder.py +166 -0
  233. angr/analyses/deobfuscator/irsb_reg_collector.py +54 -0
  234. angr/analyses/deobfuscator/string_obf_finder.py +861 -0
  235. angr/analyses/deobfuscator/string_obf_opt_passes.py +133 -0
  236. angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +47 -0
  237. angr/analyses/disassembly.py +1295 -0
  238. angr/analyses/disassembly_utils.py +101 -0
  239. angr/analyses/dominance_frontier.py +57 -0
  240. angr/analyses/fcp/__init__.py +4 -0
  241. angr/analyses/fcp/fcp.py +426 -0
  242. angr/analyses/find_objects_static.py +205 -0
  243. angr/analyses/flirt/__init__.py +47 -0
  244. angr/analyses/flirt/consts.py +160 -0
  245. angr/analyses/flirt/flirt.py +244 -0
  246. angr/analyses/flirt/flirt_function.py +20 -0
  247. angr/analyses/flirt/flirt_matcher.py +351 -0
  248. angr/analyses/flirt/flirt_module.py +32 -0
  249. angr/analyses/flirt/flirt_node.py +23 -0
  250. angr/analyses/flirt/flirt_sig.py +356 -0
  251. angr/analyses/flirt/flirt_utils.py +31 -0
  252. angr/analyses/forward_analysis/__init__.py +12 -0
  253. angr/analyses/forward_analysis/forward_analysis.py +530 -0
  254. angr/analyses/forward_analysis/job_info.py +64 -0
  255. angr/analyses/forward_analysis/visitors/__init__.py +14 -0
  256. angr/analyses/forward_analysis/visitors/call_graph.py +29 -0
  257. angr/analyses/forward_analysis/visitors/function_graph.py +86 -0
  258. angr/analyses/forward_analysis/visitors/graph.py +242 -0
  259. angr/analyses/forward_analysis/visitors/loop.py +29 -0
  260. angr/analyses/forward_analysis/visitors/single_node_graph.py +38 -0
  261. angr/analyses/identifier/__init__.py +5 -0
  262. angr/analyses/identifier/custom_callable.py +137 -0
  263. angr/analyses/identifier/errors.py +10 -0
  264. angr/analyses/identifier/func.py +60 -0
  265. angr/analyses/identifier/functions/__init__.py +37 -0
  266. angr/analyses/identifier/functions/atoi.py +73 -0
  267. angr/analyses/identifier/functions/based_atoi.py +125 -0
  268. angr/analyses/identifier/functions/fdprintf.py +123 -0
  269. angr/analyses/identifier/functions/free.py +64 -0
  270. angr/analyses/identifier/functions/int2str.py +287 -0
  271. angr/analyses/identifier/functions/malloc.py +111 -0
  272. angr/analyses/identifier/functions/memcmp.py +67 -0
  273. angr/analyses/identifier/functions/memcpy.py +89 -0
  274. angr/analyses/identifier/functions/memset.py +43 -0
  275. angr/analyses/identifier/functions/printf.py +123 -0
  276. angr/analyses/identifier/functions/recv_until.py +312 -0
  277. angr/analyses/identifier/functions/skip_calloc.py +73 -0
  278. angr/analyses/identifier/functions/skip_realloc.py +97 -0
  279. angr/analyses/identifier/functions/skip_recv_n.py +105 -0
  280. angr/analyses/identifier/functions/snprintf.py +112 -0
  281. angr/analyses/identifier/functions/sprintf.py +116 -0
  282. angr/analyses/identifier/functions/strcasecmp.py +33 -0
  283. angr/analyses/identifier/functions/strcmp.py +113 -0
  284. angr/analyses/identifier/functions/strcpy.py +43 -0
  285. angr/analyses/identifier/functions/strlen.py +27 -0
  286. angr/analyses/identifier/functions/strncmp.py +104 -0
  287. angr/analyses/identifier/functions/strncpy.py +65 -0
  288. angr/analyses/identifier/functions/strtol.py +89 -0
  289. angr/analyses/identifier/identify.py +825 -0
  290. angr/analyses/identifier/runner.py +360 -0
  291. angr/analyses/init_finder.py +289 -0
  292. angr/analyses/loop_analysis.py +349 -0
  293. angr/analyses/loopfinder.py +171 -0
  294. angr/analyses/patchfinder.py +137 -0
  295. angr/analyses/pathfinder.py +282 -0
  296. angr/analyses/propagator/__init__.py +5 -0
  297. angr/analyses/propagator/engine_base.py +62 -0
  298. angr/analyses/propagator/engine_vex.py +297 -0
  299. angr/analyses/propagator/propagator.py +361 -0
  300. angr/analyses/propagator/top_checker_mixin.py +218 -0
  301. angr/analyses/propagator/values.py +117 -0
  302. angr/analyses/propagator/vex_vars.py +68 -0
  303. angr/analyses/proximity_graph.py +444 -0
  304. angr/analyses/reaching_definitions/__init__.py +67 -0
  305. angr/analyses/reaching_definitions/call_trace.py +73 -0
  306. angr/analyses/reaching_definitions/dep_graph.py +433 -0
  307. angr/analyses/reaching_definitions/engine_ail.py +1130 -0
  308. angr/analyses/reaching_definitions/engine_vex.py +1127 -0
  309. angr/analyses/reaching_definitions/external_codeloc.py +0 -0
  310. angr/analyses/reaching_definitions/function_handler.py +637 -0
  311. angr/analyses/reaching_definitions/function_handler_library/__init__.py +12 -0
  312. angr/analyses/reaching_definitions/function_handler_library/stdio.py +268 -0
  313. angr/analyses/reaching_definitions/function_handler_library/stdlib.py +189 -0
  314. angr/analyses/reaching_definitions/function_handler_library/string.py +147 -0
  315. angr/analyses/reaching_definitions/function_handler_library/unistd.py +44 -0
  316. angr/analyses/reaching_definitions/heap_allocator.py +70 -0
  317. angr/analyses/reaching_definitions/rd_initializer.py +237 -0
  318. angr/analyses/reaching_definitions/rd_state.py +579 -0
  319. angr/analyses/reaching_definitions/reaching_definitions.py +581 -0
  320. angr/analyses/reaching_definitions/subject.py +65 -0
  321. angr/analyses/reassembler.py +2900 -0
  322. angr/analyses/s_liveness.py +203 -0
  323. angr/analyses/s_propagator.py +542 -0
  324. angr/analyses/s_reaching_definitions/__init__.py +12 -0
  325. angr/analyses/s_reaching_definitions/s_rda_model.py +135 -0
  326. angr/analyses/s_reaching_definitions/s_rda_view.py +315 -0
  327. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +174 -0
  328. angr/analyses/smc.py +161 -0
  329. angr/analyses/soot_class_hierarchy.py +273 -0
  330. angr/analyses/stack_pointer_tracker.py +953 -0
  331. angr/analyses/static_hooker.py +53 -0
  332. angr/analyses/typehoon/__init__.py +5 -0
  333. angr/analyses/typehoon/dfa.py +118 -0
  334. angr/analyses/typehoon/lifter.py +122 -0
  335. angr/analyses/typehoon/simple_solver.py +1464 -0
  336. angr/analyses/typehoon/translator.py +279 -0
  337. angr/analyses/typehoon/typeconsts.py +336 -0
  338. angr/analyses/typehoon/typehoon.py +311 -0
  339. angr/analyses/typehoon/typevars.py +622 -0
  340. angr/analyses/typehoon/variance.py +11 -0
  341. angr/analyses/unpacker/__init__.py +6 -0
  342. angr/analyses/unpacker/obfuscation_detector.py +103 -0
  343. angr/analyses/unpacker/packing_detector.py +138 -0
  344. angr/analyses/variable_recovery/__init__.py +9 -0
  345. angr/analyses/variable_recovery/annotations.py +58 -0
  346. angr/analyses/variable_recovery/engine_ail.py +897 -0
  347. angr/analyses/variable_recovery/engine_base.py +1185 -0
  348. angr/analyses/variable_recovery/engine_vex.py +593 -0
  349. angr/analyses/variable_recovery/irsb_scanner.py +143 -0
  350. angr/analyses/variable_recovery/variable_recovery.py +574 -0
  351. angr/analyses/variable_recovery/variable_recovery_base.py +461 -0
  352. angr/analyses/variable_recovery/variable_recovery_fast.py +652 -0
  353. angr/analyses/veritesting.py +626 -0
  354. angr/analyses/vfg.py +1898 -0
  355. angr/analyses/vsa_ddg.py +420 -0
  356. angr/analyses/vtable.py +92 -0
  357. angr/analyses/xrefs.py +286 -0
  358. angr/angrdb/__init__.py +14 -0
  359. angr/angrdb/db.py +206 -0
  360. angr/angrdb/models.py +184 -0
  361. angr/angrdb/serializers/__init__.py +10 -0
  362. angr/angrdb/serializers/cfg_model.py +41 -0
  363. angr/angrdb/serializers/comments.py +60 -0
  364. angr/angrdb/serializers/funcs.py +61 -0
  365. angr/angrdb/serializers/kb.py +111 -0
  366. angr/angrdb/serializers/labels.py +59 -0
  367. angr/angrdb/serializers/loader.py +165 -0
  368. angr/angrdb/serializers/structured_code.py +125 -0
  369. angr/angrdb/serializers/variables.py +58 -0
  370. angr/angrdb/serializers/xrefs.py +48 -0
  371. angr/annocfg.py +317 -0
  372. angr/blade.py +426 -0
  373. angr/block.py +509 -0
  374. angr/callable.py +168 -0
  375. angr/calling_conventions.py +2580 -0
  376. angr/code_location.py +163 -0
  377. angr/codenode.py +145 -0
  378. angr/concretization_strategies/__init__.py +32 -0
  379. angr/concretization_strategies/any.py +17 -0
  380. angr/concretization_strategies/any_named.py +35 -0
  381. angr/concretization_strategies/base.py +81 -0
  382. angr/concretization_strategies/controlled_data.py +58 -0
  383. angr/concretization_strategies/eval.py +19 -0
  384. angr/concretization_strategies/logging.py +35 -0
  385. angr/concretization_strategies/max.py +25 -0
  386. angr/concretization_strategies/nonzero.py +16 -0
  387. angr/concretization_strategies/nonzero_range.py +22 -0
  388. angr/concretization_strategies/norepeats.py +37 -0
  389. angr/concretization_strategies/norepeats_range.py +37 -0
  390. angr/concretization_strategies/range.py +19 -0
  391. angr/concretization_strategies/signed_add.py +31 -0
  392. angr/concretization_strategies/single.py +15 -0
  393. angr/concretization_strategies/solutions.py +20 -0
  394. angr/concretization_strategies/unlimited_range.py +17 -0
  395. angr/distributed/__init__.py +9 -0
  396. angr/distributed/server.py +197 -0
  397. angr/distributed/worker.py +185 -0
  398. angr/engines/__init__.py +67 -0
  399. angr/engines/engine.py +29 -0
  400. angr/engines/failure.py +27 -0
  401. angr/engines/hook.py +68 -0
  402. angr/engines/icicle.py +229 -0
  403. angr/engines/light/__init__.py +23 -0
  404. angr/engines/light/data.py +681 -0
  405. angr/engines/light/engine.py +1285 -0
  406. angr/engines/pcode/__init__.py +9 -0
  407. angr/engines/pcode/behavior.py +994 -0
  408. angr/engines/pcode/cc.py +128 -0
  409. angr/engines/pcode/emulate.py +440 -0
  410. angr/engines/pcode/engine.py +242 -0
  411. angr/engines/pcode/lifter.py +1420 -0
  412. angr/engines/procedure.py +70 -0
  413. angr/engines/soot/__init__.py +5 -0
  414. angr/engines/soot/engine.py +410 -0
  415. angr/engines/soot/exceptions.py +17 -0
  416. angr/engines/soot/expressions/__init__.py +87 -0
  417. angr/engines/soot/expressions/arrayref.py +22 -0
  418. angr/engines/soot/expressions/base.py +21 -0
  419. angr/engines/soot/expressions/binop.py +28 -0
  420. angr/engines/soot/expressions/cast.py +22 -0
  421. angr/engines/soot/expressions/condition.py +35 -0
  422. angr/engines/soot/expressions/constants.py +47 -0
  423. angr/engines/soot/expressions/instanceOf.py +15 -0
  424. angr/engines/soot/expressions/instancefieldref.py +8 -0
  425. angr/engines/soot/expressions/invoke.py +114 -0
  426. angr/engines/soot/expressions/length.py +8 -0
  427. angr/engines/soot/expressions/local.py +8 -0
  428. angr/engines/soot/expressions/new.py +16 -0
  429. angr/engines/soot/expressions/newArray.py +54 -0
  430. angr/engines/soot/expressions/newMultiArray.py +86 -0
  431. angr/engines/soot/expressions/paramref.py +8 -0
  432. angr/engines/soot/expressions/phi.py +30 -0
  433. angr/engines/soot/expressions/staticfieldref.py +8 -0
  434. angr/engines/soot/expressions/thisref.py +7 -0
  435. angr/engines/soot/expressions/unsupported.py +7 -0
  436. angr/engines/soot/field_dispatcher.py +46 -0
  437. angr/engines/soot/method_dispatcher.py +46 -0
  438. angr/engines/soot/statements/__init__.py +44 -0
  439. angr/engines/soot/statements/assign.py +30 -0
  440. angr/engines/soot/statements/base.py +79 -0
  441. angr/engines/soot/statements/goto.py +14 -0
  442. angr/engines/soot/statements/identity.py +15 -0
  443. angr/engines/soot/statements/if_.py +19 -0
  444. angr/engines/soot/statements/invoke.py +12 -0
  445. angr/engines/soot/statements/return_.py +20 -0
  446. angr/engines/soot/statements/switch.py +41 -0
  447. angr/engines/soot/statements/throw.py +15 -0
  448. angr/engines/soot/values/__init__.py +38 -0
  449. angr/engines/soot/values/arrayref.py +122 -0
  450. angr/engines/soot/values/base.py +7 -0
  451. angr/engines/soot/values/constants.py +18 -0
  452. angr/engines/soot/values/instancefieldref.py +44 -0
  453. angr/engines/soot/values/local.py +18 -0
  454. angr/engines/soot/values/paramref.py +18 -0
  455. angr/engines/soot/values/staticfieldref.py +38 -0
  456. angr/engines/soot/values/strref.py +38 -0
  457. angr/engines/soot/values/thisref.py +149 -0
  458. angr/engines/successors.py +654 -0
  459. angr/engines/syscall.py +51 -0
  460. angr/engines/unicorn.py +490 -0
  461. angr/engines/vex/__init__.py +20 -0
  462. angr/engines/vex/claripy/__init__.py +5 -0
  463. angr/engines/vex/claripy/ccall.py +2097 -0
  464. angr/engines/vex/claripy/datalayer.py +141 -0
  465. angr/engines/vex/claripy/irop.py +1276 -0
  466. angr/engines/vex/heavy/__init__.py +16 -0
  467. angr/engines/vex/heavy/actions.py +231 -0
  468. angr/engines/vex/heavy/concretizers.py +403 -0
  469. angr/engines/vex/heavy/dirty.py +466 -0
  470. angr/engines/vex/heavy/heavy.py +370 -0
  471. angr/engines/vex/heavy/inspect.py +52 -0
  472. angr/engines/vex/heavy/resilience.py +85 -0
  473. angr/engines/vex/heavy/super_fastpath.py +34 -0
  474. angr/engines/vex/lifter.py +420 -0
  475. angr/engines/vex/light/__init__.py +11 -0
  476. angr/engines/vex/light/light.py +551 -0
  477. angr/engines/vex/light/resilience.py +74 -0
  478. angr/engines/vex/light/slicing.py +52 -0
  479. angr/errors.py +609 -0
  480. angr/exploration_techniques/__init__.py +53 -0
  481. angr/exploration_techniques/base.py +126 -0
  482. angr/exploration_techniques/bucketizer.py +94 -0
  483. angr/exploration_techniques/common.py +56 -0
  484. angr/exploration_techniques/dfs.py +37 -0
  485. angr/exploration_techniques/director.py +520 -0
  486. angr/exploration_techniques/driller_core.py +100 -0
  487. angr/exploration_techniques/explorer.py +152 -0
  488. angr/exploration_techniques/lengthlimiter.py +22 -0
  489. angr/exploration_techniques/local_loop_seer.py +65 -0
  490. angr/exploration_techniques/loop_seer.py +236 -0
  491. angr/exploration_techniques/manual_mergepoint.py +82 -0
  492. angr/exploration_techniques/memory_watcher.py +43 -0
  493. angr/exploration_techniques/oppologist.py +92 -0
  494. angr/exploration_techniques/slicecutor.py +118 -0
  495. angr/exploration_techniques/spiller.py +280 -0
  496. angr/exploration_techniques/spiller_db.py +27 -0
  497. angr/exploration_techniques/stochastic.py +56 -0
  498. angr/exploration_techniques/stub_stasher.py +19 -0
  499. angr/exploration_techniques/suggestions.py +159 -0
  500. angr/exploration_techniques/tech_builder.py +49 -0
  501. angr/exploration_techniques/threading.py +69 -0
  502. angr/exploration_techniques/timeout.py +34 -0
  503. angr/exploration_techniques/tracer.py +1098 -0
  504. angr/exploration_techniques/unique.py +106 -0
  505. angr/exploration_techniques/veritesting.py +37 -0
  506. angr/factory.py +404 -0
  507. angr/flirt/__init__.py +97 -0
  508. angr/flirt/build_sig.py +305 -0
  509. angr/graph_utils.py +0 -0
  510. angr/keyed_region.py +525 -0
  511. angr/knowledge_base.py +143 -0
  512. angr/knowledge_plugins/__init__.py +43 -0
  513. angr/knowledge_plugins/callsite_prototypes.py +53 -0
  514. angr/knowledge_plugins/cfg/__init__.py +18 -0
  515. angr/knowledge_plugins/cfg/cfg_manager.py +95 -0
  516. angr/knowledge_plugins/cfg/cfg_model.py +1045 -0
  517. angr/knowledge_plugins/cfg/cfg_node.py +536 -0
  518. angr/knowledge_plugins/cfg/indirect_jump.py +65 -0
  519. angr/knowledge_plugins/cfg/memory_data.py +156 -0
  520. angr/knowledge_plugins/comments.py +16 -0
  521. angr/knowledge_plugins/custom_strings.py +38 -0
  522. angr/knowledge_plugins/data.py +22 -0
  523. angr/knowledge_plugins/debug_variables.py +216 -0
  524. angr/knowledge_plugins/functions/__init__.py +9 -0
  525. angr/knowledge_plugins/functions/function.py +1780 -0
  526. angr/knowledge_plugins/functions/function_manager.py +589 -0
  527. angr/knowledge_plugins/functions/function_parser.py +299 -0
  528. angr/knowledge_plugins/functions/soot_function.py +128 -0
  529. angr/knowledge_plugins/indirect_jumps.py +35 -0
  530. angr/knowledge_plugins/key_definitions/__init__.py +17 -0
  531. angr/knowledge_plugins/key_definitions/atoms.py +374 -0
  532. angr/knowledge_plugins/key_definitions/constants.py +29 -0
  533. angr/knowledge_plugins/key_definitions/definition.py +214 -0
  534. angr/knowledge_plugins/key_definitions/environment.py +96 -0
  535. angr/knowledge_plugins/key_definitions/heap_address.py +33 -0
  536. angr/knowledge_plugins/key_definitions/key_definition_manager.py +82 -0
  537. angr/knowledge_plugins/key_definitions/live_definitions.py +1010 -0
  538. angr/knowledge_plugins/key_definitions/liveness.py +165 -0
  539. angr/knowledge_plugins/key_definitions/rd_model.py +171 -0
  540. angr/knowledge_plugins/key_definitions/tag.py +78 -0
  541. angr/knowledge_plugins/key_definitions/undefined.py +70 -0
  542. angr/knowledge_plugins/key_definitions/unknown_size.py +86 -0
  543. angr/knowledge_plugins/key_definitions/uses.py +178 -0
  544. angr/knowledge_plugins/labels.py +110 -0
  545. angr/knowledge_plugins/obfuscations.py +37 -0
  546. angr/knowledge_plugins/patches.py +126 -0
  547. angr/knowledge_plugins/plugin.py +24 -0
  548. angr/knowledge_plugins/propagations/__init__.py +10 -0
  549. angr/knowledge_plugins/propagations/prop_value.py +191 -0
  550. angr/knowledge_plugins/propagations/propagation_manager.py +60 -0
  551. angr/knowledge_plugins/propagations/propagation_model.py +73 -0
  552. angr/knowledge_plugins/propagations/states.py +552 -0
  553. angr/knowledge_plugins/structured_code.py +63 -0
  554. angr/knowledge_plugins/types.py +88 -0
  555. angr/knowledge_plugins/variables/__init__.py +8 -0
  556. angr/knowledge_plugins/variables/variable_access.py +113 -0
  557. angr/knowledge_plugins/variables/variable_manager.py +1380 -0
  558. angr/knowledge_plugins/xrefs/__init__.py +12 -0
  559. angr/knowledge_plugins/xrefs/xref.py +150 -0
  560. angr/knowledge_plugins/xrefs/xref_manager.py +127 -0
  561. angr/knowledge_plugins/xrefs/xref_types.py +16 -0
  562. angr/lib/angr_native.so +0 -0
  563. angr/misc/__init__.py +19 -0
  564. angr/misc/ansi.py +47 -0
  565. angr/misc/autoimport.py +90 -0
  566. angr/misc/bug_report.py +117 -0
  567. angr/misc/hookset.py +106 -0
  568. angr/misc/loggers.py +130 -0
  569. angr/misc/picklable_lock.py +46 -0
  570. angr/misc/plugins.py +289 -0
  571. angr/misc/telemetry.py +54 -0
  572. angr/misc/testing.py +24 -0
  573. angr/misc/ux.py +31 -0
  574. angr/procedures/__init__.py +12 -0
  575. angr/procedures/advapi32/__init__.py +0 -0
  576. angr/procedures/cgc/__init__.py +3 -0
  577. angr/procedures/cgc/_terminate.py +11 -0
  578. angr/procedures/cgc/allocate.py +75 -0
  579. angr/procedures/cgc/deallocate.py +67 -0
  580. angr/procedures/cgc/fdwait.py +65 -0
  581. angr/procedures/cgc/random.py +67 -0
  582. angr/procedures/cgc/receive.py +93 -0
  583. angr/procedures/cgc/transmit.py +65 -0
  584. angr/procedures/definitions/__init__.py +779 -0
  585. angr/procedures/definitions/cgc.py +20 -0
  586. angr/procedures/definitions/glibc.py +8372 -0
  587. angr/procedures/definitions/gnulib.py +32 -0
  588. angr/procedures/definitions/libstdcpp.py +21 -0
  589. angr/procedures/definitions/linux_kernel.py +6171 -0
  590. angr/procedures/definitions/linux_loader.py +7 -0
  591. angr/procedures/definitions/msvcr.py +16 -0
  592. angr/procedures/definitions/parse_syscalls_from_local_system.py +50 -0
  593. angr/procedures/definitions/parse_win32json.py +2553 -0
  594. angr/procedures/definitions/types_stl.py +22 -0
  595. angr/procedures/definitions/types_win32.py +34482 -0
  596. angr/procedures/definitions/wdk_api-ms-win-dx-d3dkmt-l1-1-4.py +30 -0
  597. angr/procedures/definitions/wdk_api-ms-win-dx-d3dkmt-l1-1-6.py +26 -0
  598. angr/procedures/definitions/wdk_clfs.py +140 -0
  599. angr/procedures/definitions/wdk_fltmgr.py +556 -0
  600. angr/procedures/definitions/wdk_fwpkclnt.py +30 -0
  601. angr/procedures/definitions/wdk_fwpuclnt.py +316 -0
  602. angr/procedures/definitions/wdk_gdi32.py +366 -0
  603. angr/procedures/definitions/wdk_hal.py +78 -0
  604. angr/procedures/definitions/wdk_ksecdd.py +62 -0
  605. angr/procedures/definitions/wdk_ndis.py +238 -0
  606. angr/procedures/definitions/wdk_ntoskrnl.py +3451 -0
  607. angr/procedures/definitions/wdk_offreg.py +72 -0
  608. angr/procedures/definitions/wdk_pshed.py +36 -0
  609. angr/procedures/definitions/wdk_secur32.py +40 -0
  610. angr/procedures/definitions/wdk_vhfum.py +34 -0
  611. angr/procedures/definitions/win32_aclui.py +30 -0
  612. angr/procedures/definitions/win32_activeds.py +68 -0
  613. angr/procedures/definitions/win32_advapi32.py +1684 -0
  614. angr/procedures/definitions/win32_advpack.py +124 -0
  615. angr/procedures/definitions/win32_amsi.py +38 -0
  616. angr/procedures/definitions/win32_api-ms-win-appmodel-runtime-l1-1-1.py +44 -0
  617. angr/procedures/definitions/win32_api-ms-win-appmodel-runtime-l1-1-3.py +34 -0
  618. angr/procedures/definitions/win32_api-ms-win-appmodel-runtime-l1-1-6.py +26 -0
  619. angr/procedures/definitions/win32_api-ms-win-core-apiquery-l2-1-0.py +26 -0
  620. angr/procedures/definitions/win32_api-ms-win-core-backgroundtask-l1-1-0.py +26 -0
  621. angr/procedures/definitions/win32_api-ms-win-core-comm-l1-1-1.py +26 -0
  622. angr/procedures/definitions/win32_api-ms-win-core-comm-l1-1-2.py +26 -0
  623. angr/procedures/definitions/win32_api-ms-win-core-enclave-l1-1-1.py +30 -0
  624. angr/procedures/definitions/win32_api-ms-win-core-errorhandling-l1-1-3.py +26 -0
  625. angr/procedures/definitions/win32_api-ms-win-core-featurestaging-l1-1-0.py +34 -0
  626. angr/procedures/definitions/win32_api-ms-win-core-featurestaging-l1-1-1.py +26 -0
  627. angr/procedures/definitions/win32_api-ms-win-core-file-fromapp-l1-1-0.py +46 -0
  628. angr/procedures/definitions/win32_api-ms-win-core-handle-l1-1-0.py +26 -0
  629. angr/procedures/definitions/win32_api-ms-win-core-ioring-l1-1-0.py +48 -0
  630. angr/procedures/definitions/win32_api-ms-win-core-marshal-l1-1-0.py +32 -0
  631. angr/procedures/definitions/win32_api-ms-win-core-memory-l1-1-3.py +32 -0
  632. angr/procedures/definitions/win32_api-ms-win-core-memory-l1-1-4.py +26 -0
  633. angr/procedures/definitions/win32_api-ms-win-core-memory-l1-1-5.py +30 -0
  634. angr/procedures/definitions/win32_api-ms-win-core-memory-l1-1-6.py +32 -0
  635. angr/procedures/definitions/win32_api-ms-win-core-memory-l1-1-7.py +28 -0
  636. angr/procedures/definitions/win32_api-ms-win-core-memory-l1-1-8.py +30 -0
  637. angr/procedures/definitions/win32_api-ms-win-core-path-l1-1-0.py +68 -0
  638. angr/procedures/definitions/win32_api-ms-win-core-psm-appnotify-l1-1-0.py +28 -0
  639. angr/procedures/definitions/win32_api-ms-win-core-psm-appnotify-l1-1-1.py +28 -0
  640. angr/procedures/definitions/win32_api-ms-win-core-realtime-l1-1-1.py +30 -0
  641. angr/procedures/definitions/win32_api-ms-win-core-realtime-l1-1-2.py +30 -0
  642. angr/procedures/definitions/win32_api-ms-win-core-slapi-l1-1-0.py +26 -0
  643. angr/procedures/definitions/win32_api-ms-win-core-state-helpers-l1-1-0.py +26 -0
  644. angr/procedures/definitions/win32_api-ms-win-core-synch-l1-2-0.py +30 -0
  645. angr/procedures/definitions/win32_api-ms-win-core-sysinfo-l1-2-0.py +26 -0
  646. angr/procedures/definitions/win32_api-ms-win-core-sysinfo-l1-2-3.py +28 -0
  647. angr/procedures/definitions/win32_api-ms-win-core-sysinfo-l1-2-4.py +28 -0
  648. angr/procedures/definitions/win32_api-ms-win-core-sysinfo-l1-2-6.py +26 -0
  649. angr/procedures/definitions/win32_api-ms-win-core-util-l1-1-1.py +28 -0
  650. angr/procedures/definitions/win32_api-ms-win-core-winrt-error-l1-1-0.py +44 -0
  651. angr/procedures/definitions/win32_api-ms-win-core-winrt-error-l1-1-1.py +38 -0
  652. angr/procedures/definitions/win32_api-ms-win-core-winrt-l1-1-0.py +40 -0
  653. angr/procedures/definitions/win32_api-ms-win-core-winrt-registration-l1-1-0.py +24 -0
  654. angr/procedures/definitions/win32_api-ms-win-core-winrt-robuffer-l1-1-0.py +24 -0
  655. angr/procedures/definitions/win32_api-ms-win-core-winrt-roparameterizediid-l1-1-0.py +28 -0
  656. angr/procedures/definitions/win32_api-ms-win-core-winrt-string-l1-1-0.py +76 -0
  657. angr/procedures/definitions/win32_api-ms-win-core-winrt-string-l1-1-1.py +24 -0
  658. angr/procedures/definitions/win32_api-ms-win-core-wow64-l1-1-1.py +30 -0
  659. angr/procedures/definitions/win32_api-ms-win-devices-query-l1-1-0.py +42 -0
  660. angr/procedures/definitions/win32_api-ms-win-devices-query-l1-1-1.py +34 -0
  661. angr/procedures/definitions/win32_api-ms-win-dx-d3dkmt-l1-1-0.py +26 -0
  662. angr/procedures/definitions/win32_api-ms-win-gaming-deviceinformation-l1-1-0.py +26 -0
  663. angr/procedures/definitions/win32_api-ms-win-gaming-expandedresources-l1-1-0.py +30 -0
  664. angr/procedures/definitions/win32_api-ms-win-gaming-tcui-l1-1-0.py +38 -0
  665. angr/procedures/definitions/win32_api-ms-win-gaming-tcui-l1-1-1.py +28 -0
  666. angr/procedures/definitions/win32_api-ms-win-gaming-tcui-l1-1-2.py +38 -0
  667. angr/procedures/definitions/win32_api-ms-win-gaming-tcui-l1-1-3.py +28 -0
  668. angr/procedures/definitions/win32_api-ms-win-gaming-tcui-l1-1-4.py +40 -0
  669. angr/procedures/definitions/win32_api-ms-win-mm-misc-l1-1-1.py +26 -0
  670. angr/procedures/definitions/win32_api-ms-win-net-isolation-l1-1-0.py +40 -0
  671. angr/procedures/definitions/win32_api-ms-win-security-base-l1-2-2.py +26 -0
  672. angr/procedures/definitions/win32_api-ms-win-security-isolatedcontainer-l1-1-0.py +26 -0
  673. angr/procedures/definitions/win32_api-ms-win-security-isolatedcontainer-l1-1-1.py +26 -0
  674. angr/procedures/definitions/win32_api-ms-win-service-core-l1-1-3.py +26 -0
  675. angr/procedures/definitions/win32_api-ms-win-service-core-l1-1-4.py +26 -0
  676. angr/procedures/definitions/win32_api-ms-win-service-core-l1-1-5.py +28 -0
  677. angr/procedures/definitions/win32_api-ms-win-shcore-scaling-l1-1-0.py +30 -0
  678. angr/procedures/definitions/win32_api-ms-win-shcore-scaling-l1-1-1.py +36 -0
  679. angr/procedures/definitions/win32_api-ms-win-shcore-scaling-l1-1-2.py +26 -0
  680. angr/procedures/definitions/win32_api-ms-win-shcore-stream-winrt-l1-1-0.py +28 -0
  681. angr/procedures/definitions/win32_api-ms-win-wsl-api-l1-1-0.py +38 -0
  682. angr/procedures/definitions/win32_apphelp.py +26 -0
  683. angr/procedures/definitions/win32_authz.py +90 -0
  684. angr/procedures/definitions/win32_avicap32.py +32 -0
  685. angr/procedures/definitions/win32_avifil32.py +144 -0
  686. angr/procedures/definitions/win32_avrt.py +52 -0
  687. angr/procedures/definitions/win32_bcp47mrm.py +28 -0
  688. angr/procedures/definitions/win32_bcrypt.py +130 -0
  689. angr/procedures/definitions/win32_bcryptprimitives.py +28 -0
  690. angr/procedures/definitions/win32_bluetoothapis.py +106 -0
  691. angr/procedures/definitions/win32_bthprops.py +34 -0
  692. angr/procedures/definitions/win32_bthprops_cpl.py +36 -0
  693. angr/procedures/definitions/win32_cabinet.py +68 -0
  694. angr/procedures/definitions/win32_certadm.py +60 -0
  695. angr/procedures/definitions/win32_certpoleng.py +40 -0
  696. angr/procedures/definitions/win32_cfgmgr32.py +502 -0
  697. angr/procedures/definitions/win32_chakra.py +198 -0
  698. angr/procedures/definitions/win32_cldapi.py +96 -0
  699. angr/procedures/definitions/win32_clfsw32.py +142 -0
  700. angr/procedures/definitions/win32_clusapi.py +584 -0
  701. angr/procedures/definitions/win32_comctl32.py +254 -0
  702. angr/procedures/definitions/win32_comdlg32.py +66 -0
  703. angr/procedures/definitions/win32_compstui.py +32 -0
  704. angr/procedures/definitions/win32_computecore.py +132 -0
  705. angr/procedures/definitions/win32_computenetwork.py +110 -0
  706. angr/procedures/definitions/win32_computestorage.py +48 -0
  707. angr/procedures/definitions/win32_comsvcs.py +38 -0
  708. angr/procedures/definitions/win32_coremessaging.py +24 -0
  709. angr/procedures/definitions/win32_credui.py +62 -0
  710. angr/procedures/definitions/win32_crypt32.py +482 -0
  711. angr/procedures/definitions/win32_cryptnet.py +34 -0
  712. angr/procedures/definitions/win32_cryptui.py +44 -0
  713. angr/procedures/definitions/win32_cryptxml.py +62 -0
  714. angr/procedures/definitions/win32_cscapi.py +32 -0
  715. angr/procedures/definitions/win32_d2d1.py +50 -0
  716. angr/procedures/definitions/win32_d3d10.py +78 -0
  717. angr/procedures/definitions/win32_d3d10_1.py +28 -0
  718. angr/procedures/definitions/win32_d3d11.py +30 -0
  719. angr/procedures/definitions/win32_d3d12.py +40 -0
  720. angr/procedures/definitions/win32_d3d9.py +46 -0
  721. angr/procedures/definitions/win32_d3dcompiler_47.py +76 -0
  722. angr/procedures/definitions/win32_d3dcsx.py +42 -0
  723. angr/procedures/definitions/win32_davclnt.py +60 -0
  724. angr/procedures/definitions/win32_dbgeng.py +32 -0
  725. angr/procedures/definitions/win32_dbghelp.py +462 -0
  726. angr/procedures/definitions/win32_dbgmodel.py +26 -0
  727. angr/procedures/definitions/win32_dciman32.py +64 -0
  728. angr/procedures/definitions/win32_dcomp.py +48 -0
  729. angr/procedures/definitions/win32_ddraw.py +38 -0
  730. angr/procedures/definitions/win32_deviceaccess.py +26 -0
  731. angr/procedures/definitions/win32_dflayout.py +26 -0
  732. angr/procedures/definitions/win32_dhcpcsvc.py +54 -0
  733. angr/procedures/definitions/win32_dhcpcsvc6.py +36 -0
  734. angr/procedures/definitions/win32_dhcpsapi.py +416 -0
  735. angr/procedures/definitions/win32_diagnosticdataquery.py +94 -0
  736. angr/procedures/definitions/win32_dinput8.py +26 -0
  737. angr/procedures/definitions/win32_directml.py +28 -0
  738. angr/procedures/definitions/win32_dmprocessxmlfiltered.py +26 -0
  739. angr/procedures/definitions/win32_dnsapi.py +152 -0
  740. angr/procedures/definitions/win32_drt.py +56 -0
  741. angr/procedures/definitions/win32_drtprov.py +42 -0
  742. angr/procedures/definitions/win32_drttransport.py +28 -0
  743. angr/procedures/definitions/win32_dsound.py +44 -0
  744. angr/procedures/definitions/win32_dsparse.py +62 -0
  745. angr/procedures/definitions/win32_dsprop.py +38 -0
  746. angr/procedures/definitions/win32_dssec.py +32 -0
  747. angr/procedures/definitions/win32_dsuiext.py +32 -0
  748. angr/procedures/definitions/win32_dwmapi.py +86 -0
  749. angr/procedures/definitions/win32_dwrite.py +26 -0
  750. angr/procedures/definitions/win32_dxcompiler.py +28 -0
  751. angr/procedures/definitions/win32_dxcore.py +26 -0
  752. angr/procedures/definitions/win32_dxgi.py +36 -0
  753. angr/procedures/definitions/win32_dxva2.py +100 -0
  754. angr/procedures/definitions/win32_eappcfg.py +52 -0
  755. angr/procedures/definitions/win32_eappprxy.py +60 -0
  756. angr/procedures/definitions/win32_efswrt.py +28 -0
  757. angr/procedures/definitions/win32_elscore.py +34 -0
  758. angr/procedures/definitions/win32_esent.py +482 -0
  759. angr/procedures/definitions/win32_evr.py +38 -0
  760. angr/procedures/definitions/win32_faultrep.py +32 -0
  761. angr/procedures/definitions/win32_fhsvcctl.py +38 -0
  762. angr/procedures/definitions/win32_firewallapi.py +30 -0
  763. angr/procedures/definitions/win32_fltlib.py +80 -0
  764. angr/procedures/definitions/win32_fontsub.py +28 -0
  765. angr/procedures/definitions/win32_forceinline.py +30 -0
  766. angr/procedures/definitions/win32_fwpuclnt.py +408 -0
  767. angr/procedures/definitions/win32_fxsutility.py +28 -0
  768. angr/procedures/definitions/win32_gdi32.py +886 -0
  769. angr/procedures/definitions/win32_gdiplus.py +1282 -0
  770. angr/procedures/definitions/win32_glu32.py +128 -0
  771. angr/procedures/definitions/win32_gpedit.py +36 -0
  772. angr/procedures/definitions/win32_hhctrl_ocx.py +28 -0
  773. angr/procedures/definitions/win32_hid.py +114 -0
  774. angr/procedures/definitions/win32_hlink.py +80 -0
  775. angr/procedures/definitions/win32_hrtfapo.py +26 -0
  776. angr/procedures/definitions/win32_httpapi.py +110 -0
  777. angr/procedures/definitions/win32_icm32.py +66 -0
  778. angr/procedures/definitions/win32_icmui.py +28 -0
  779. angr/procedures/definitions/win32_icu.py +2074 -0
  780. angr/procedures/definitions/win32_ieframe.py +82 -0
  781. angr/procedures/definitions/win32_imagehlp.py +76 -0
  782. angr/procedures/definitions/win32_imgutil.py +42 -0
  783. angr/procedures/definitions/win32_imm32.py +188 -0
  784. angr/procedures/definitions/win32_infocardapi.py +58 -0
  785. angr/procedures/definitions/win32_inkobjcore.py +78 -0
  786. angr/procedures/definitions/win32_iphlpapi.py +426 -0
  787. angr/procedures/definitions/win32_iscsidsc.py +182 -0
  788. angr/procedures/definitions/win32_isolatedwindowsenvironmentutils.py +28 -0
  789. angr/procedures/definitions/win32_kernel32.py +3185 -0
  790. angr/procedures/definitions/win32_kernelbase.py +36 -0
  791. angr/procedures/definitions/win32_keycredmgr.py +32 -0
  792. angr/procedures/definitions/win32_ksproxy_ax.py +36 -0
  793. angr/procedures/definitions/win32_ksuser.py +40 -0
  794. angr/procedures/definitions/win32_ktmw32.py +102 -0
  795. angr/procedures/definitions/win32_licenseprotection.py +28 -0
  796. angr/procedures/definitions/win32_loadperf.py +48 -0
  797. angr/procedures/definitions/win32_magnification.py +62 -0
  798. angr/procedures/definitions/win32_mapi32.py +156 -0
  799. angr/procedures/definitions/win32_mdmlocalmanagement.py +30 -0
  800. angr/procedures/definitions/win32_mdmregistration.py +54 -0
  801. angr/procedures/definitions/win32_mf.py +148 -0
  802. angr/procedures/definitions/win32_mfcore.py +28 -0
  803. angr/procedures/definitions/win32_mfplat.py +314 -0
  804. angr/procedures/definitions/win32_mfplay.py +26 -0
  805. angr/procedures/definitions/win32_mfreadwrite.py +34 -0
  806. angr/procedures/definitions/win32_mfsensorgroup.py +44 -0
  807. angr/procedures/definitions/win32_mfsrcsnk.py +28 -0
  808. angr/procedures/definitions/win32_mgmtapi.py +42 -0
  809. angr/procedures/definitions/win32_mi.py +26 -0
  810. angr/procedures/definitions/win32_mmdevapi.py +26 -0
  811. angr/procedures/definitions/win32_mpr.py +118 -0
  812. angr/procedures/definitions/win32_mprapi.py +248 -0
  813. angr/procedures/definitions/win32_mqrt.py +92 -0
  814. angr/procedures/definitions/win32_mrmsupport.py +78 -0
  815. angr/procedures/definitions/win32_msacm32.py +108 -0
  816. angr/procedures/definitions/win32_msajapi.py +1118 -0
  817. angr/procedures/definitions/win32_mscms.py +182 -0
  818. angr/procedures/definitions/win32_mscoree.py +78 -0
  819. angr/procedures/definitions/win32_msctfmonitor.py +30 -0
  820. angr/procedures/definitions/win32_msdelta.py +56 -0
  821. angr/procedures/definitions/win32_msdmo.py +46 -0
  822. angr/procedures/definitions/win32_msdrm.py +192 -0
  823. angr/procedures/definitions/win32_msi.py +552 -0
  824. angr/procedures/definitions/win32_msimg32.py +30 -0
  825. angr/procedures/definitions/win32_mspatcha.py +56 -0
  826. angr/procedures/definitions/win32_mspatchc.py +42 -0
  827. angr/procedures/definitions/win32_msports.py +38 -0
  828. angr/procedures/definitions/win32_msrating.py +62 -0
  829. angr/procedures/definitions/win32_mssign32.py +44 -0
  830. angr/procedures/definitions/win32_mstask.py +28 -0
  831. angr/procedures/definitions/win32_msvfw32.py +110 -0
  832. angr/procedures/definitions/win32_mswsock.py +56 -0
  833. angr/procedures/definitions/win32_mtxdm.py +26 -0
  834. angr/procedures/definitions/win32_ncrypt.py +102 -0
  835. angr/procedures/definitions/win32_ndfapi.py +56 -0
  836. angr/procedures/definitions/win32_netapi32.py +436 -0
  837. angr/procedures/definitions/win32_netsh.py +40 -0
  838. angr/procedures/definitions/win32_netshell.py +28 -0
  839. angr/procedures/definitions/win32_newdev.py +46 -0
  840. angr/procedures/definitions/win32_ninput.py +84 -0
  841. angr/procedures/definitions/win32_normaliz.py +28 -0
  842. angr/procedures/definitions/win32_ntdll.py +171 -0
  843. angr/procedures/definitions/win32_ntdllk.py +26 -0
  844. angr/procedures/definitions/win32_ntdsapi.py +186 -0
  845. angr/procedures/definitions/win32_ntlanman.py +44 -0
  846. angr/procedures/definitions/win32_odbc32.py +392 -0
  847. angr/procedures/definitions/win32_odbcbcp.py +78 -0
  848. angr/procedures/definitions/win32_ole32.py +658 -0
  849. angr/procedures/definitions/win32_oleacc.py +58 -0
  850. angr/procedures/definitions/win32_oleaut32.py +834 -0
  851. angr/procedures/definitions/win32_oledlg.py +70 -0
  852. angr/procedures/definitions/win32_ondemandconnroutehelper.py +34 -0
  853. angr/procedures/definitions/win32_opengl32.py +734 -0
  854. angr/procedures/definitions/win32_opmxbox.py +30 -0
  855. angr/procedures/definitions/win32_p2p.py +240 -0
  856. angr/procedures/definitions/win32_p2pgraph.py +98 -0
  857. angr/procedures/definitions/win32_pdh.py +220 -0
  858. angr/procedures/definitions/win32_peerdist.py +80 -0
  859. angr/procedures/definitions/win32_powrprof.py +192 -0
  860. angr/procedures/definitions/win32_prntvpt.py +46 -0
  861. angr/procedures/definitions/win32_projectedfslib.py +62 -0
  862. angr/procedures/definitions/win32_propsys.py +460 -0
  863. angr/procedures/definitions/win32_psapi.py +78 -0
  864. angr/procedures/definitions/win32_quartz.py +28 -0
  865. angr/procedures/definitions/win32_query.py +32 -0
  866. angr/procedures/definitions/win32_qwave.py +46 -0
  867. angr/procedures/definitions/win32_rasapi32.py +192 -0
  868. angr/procedures/definitions/win32_rasdlg.py +36 -0
  869. angr/procedures/definitions/win32_resutils.py +264 -0
  870. angr/procedures/definitions/win32_rometadata.py +24 -0
  871. angr/procedures/definitions/win32_rpcns4.py +146 -0
  872. angr/procedures/definitions/win32_rpcproxy.py +32 -0
  873. angr/procedures/definitions/win32_rpcrt4.py +918 -0
  874. angr/procedures/definitions/win32_rstrtmgr.py +46 -0
  875. angr/procedures/definitions/win32_rtm.py +176 -0
  876. angr/procedures/definitions/win32_rtutils.py +106 -0
  877. angr/procedures/definitions/win32_rtworkq.py +90 -0
  878. angr/procedures/definitions/win32_sas.py +26 -0
  879. angr/procedures/definitions/win32_scarddlg.py +34 -0
  880. angr/procedures/definitions/win32_schannel.py +42 -0
  881. angr/procedures/definitions/win32_sechost.py +28 -0
  882. angr/procedures/definitions/win32_secur32.py +202 -0
  883. angr/procedures/definitions/win32_sensapi.py +30 -0
  884. angr/procedures/definitions/win32_sensorsutilsv2.py +104 -0
  885. angr/procedures/definitions/win32_setupapi.py +692 -0
  886. angr/procedures/definitions/win32_sfc.py +36 -0
  887. angr/procedures/definitions/win32_shdocvw.py +30 -0
  888. angr/procedures/definitions/win32_shell32.py +512 -0
  889. angr/procedures/definitions/win32_shlwapi.py +744 -0
  890. angr/procedures/definitions/win32_slc.py +88 -0
  891. angr/procedures/definitions/win32_slcext.py +32 -0
  892. angr/procedures/definitions/win32_slwga.py +26 -0
  893. angr/procedures/definitions/win32_snmpapi.py +76 -0
  894. angr/procedures/definitions/win32_spoolss.py +76 -0
  895. angr/procedures/definitions/win32_srclient.py +26 -0
  896. angr/procedures/definitions/win32_srpapi.py +46 -0
  897. angr/procedures/definitions/win32_sspicli.py +38 -0
  898. angr/procedures/definitions/win32_sti.py +26 -0
  899. angr/procedures/definitions/win32_t2embed.py +52 -0
  900. angr/procedures/definitions/win32_tapi32.py +522 -0
  901. angr/procedures/definitions/win32_tbs.py +52 -0
  902. angr/procedures/definitions/win32_tdh.py +78 -0
  903. angr/procedures/definitions/win32_tokenbinding.py +44 -0
  904. angr/procedures/definitions/win32_traffic.py +64 -0
  905. angr/procedures/definitions/win32_txfw32.py +42 -0
  906. angr/procedures/definitions/win32_ualapi.py +32 -0
  907. angr/procedures/definitions/win32_uiautomationcore.py +220 -0
  908. angr/procedures/definitions/win32_urlmon.py +178 -0
  909. angr/procedures/definitions/win32_user32.py +1551 -0
  910. angr/procedures/definitions/win32_userenv.py +112 -0
  911. angr/procedures/definitions/win32_usp10.py +104 -0
  912. angr/procedures/definitions/win32_uxtheme.py +178 -0
  913. angr/procedures/definitions/win32_verifier.py +26 -0
  914. angr/procedures/definitions/win32_version.py +52 -0
  915. angr/procedures/definitions/win32_vertdll.py +38 -0
  916. angr/procedures/definitions/win32_virtdisk.py +82 -0
  917. angr/procedures/definitions/win32_vmdevicehost.py +50 -0
  918. angr/procedures/definitions/win32_vmsavedstatedumpprovider.py +110 -0
  919. angr/procedures/definitions/win32_vssapi.py +26 -0
  920. angr/procedures/definitions/win32_wcmapi.py +34 -0
  921. angr/procedures/definitions/win32_wdsbp.py +38 -0
  922. angr/procedures/definitions/win32_wdsclientapi.py +98 -0
  923. angr/procedures/definitions/win32_wdsmc.py +36 -0
  924. angr/procedures/definitions/win32_wdspxe.py +86 -0
  925. angr/procedures/definitions/win32_wdstptc.py +50 -0
  926. angr/procedures/definitions/win32_webauthn.py +50 -0
  927. angr/procedures/definitions/win32_webservices.py +410 -0
  928. angr/procedures/definitions/win32_websocket.py +50 -0
  929. angr/procedures/definitions/win32_wecapi.py +54 -0
  930. angr/procedures/definitions/win32_wer.py +66 -0
  931. angr/procedures/definitions/win32_wevtapi.py +94 -0
  932. angr/procedures/definitions/win32_winbio.py +132 -0
  933. angr/procedures/definitions/win32_windows_ai_machinelearning.py +26 -0
  934. angr/procedures/definitions/win32_windows_data_pdf.py +24 -0
  935. angr/procedures/definitions/win32_windows_media_mediacontrol.py +40 -0
  936. angr/procedures/definitions/win32_windows_networking.py +26 -0
  937. angr/procedures/definitions/win32_windows_ui_xaml.py +28 -0
  938. angr/procedures/definitions/win32_windowscodecs.py +42 -0
  939. angr/procedures/definitions/win32_winfax.py +136 -0
  940. angr/procedures/definitions/win32_winhttp.py +136 -0
  941. angr/procedures/definitions/win32_winhvemulation.py +32 -0
  942. angr/procedures/definitions/win32_winhvplatform.py +156 -0
  943. angr/procedures/definitions/win32_wininet.py +616 -0
  944. angr/procedures/definitions/win32_winml.py +26 -0
  945. angr/procedures/definitions/win32_winmm.py +376 -0
  946. angr/procedures/definitions/win32_winscard.py +164 -0
  947. angr/procedures/definitions/win32_winspool.py +364 -0
  948. angr/procedures/definitions/win32_winspool_drv.py +368 -0
  949. angr/procedures/definitions/win32_wintrust.py +144 -0
  950. angr/procedures/definitions/win32_winusb.py +92 -0
  951. angr/procedures/definitions/win32_wlanapi.py +144 -0
  952. angr/procedures/definitions/win32_wlanui.py +26 -0
  953. angr/procedures/definitions/win32_wldap32.py +510 -0
  954. angr/procedures/definitions/win32_wldp.py +42 -0
  955. angr/procedures/definitions/win32_wmvcore.py +46 -0
  956. angr/procedures/definitions/win32_wnvapi.py +28 -0
  957. angr/procedures/definitions/win32_wofutil.py +46 -0
  958. angr/procedures/definitions/win32_ws2_32.py +344 -0
  959. angr/procedures/definitions/win32_wscapi.py +36 -0
  960. angr/procedures/definitions/win32_wsclient.py +30 -0
  961. angr/procedures/definitions/win32_wsdapi.py +88 -0
  962. angr/procedures/definitions/win32_wsmsvc.py +90 -0
  963. angr/procedures/definitions/win32_wsnmp32.py +122 -0
  964. angr/procedures/definitions/win32_wtsapi32.py +150 -0
  965. angr/procedures/definitions/win32_xaudio2_8.py +32 -0
  966. angr/procedures/definitions/win32_xinput1_4.py +38 -0
  967. angr/procedures/definitions/win32_xinputuap.py +36 -0
  968. angr/procedures/definitions/win32_xmllite.py +36 -0
  969. angr/procedures/definitions/win32_xolehlp.py +32 -0
  970. angr/procedures/definitions/win32_xpsprint.py +28 -0
  971. angr/procedures/glibc/__ctype_b_loc.py +21 -0
  972. angr/procedures/glibc/__ctype_tolower_loc.py +21 -0
  973. angr/procedures/glibc/__ctype_toupper_loc.py +21 -0
  974. angr/procedures/glibc/__errno_location.py +7 -0
  975. angr/procedures/glibc/__init__.py +3 -0
  976. angr/procedures/glibc/__libc_init.py +37 -0
  977. angr/procedures/glibc/__libc_start_main.py +301 -0
  978. angr/procedures/glibc/dynamic_loading.py +20 -0
  979. angr/procedures/glibc/scanf.py +11 -0
  980. angr/procedures/glibc/sscanf.py +6 -0
  981. angr/procedures/gnulib/__init__.py +3 -0
  982. angr/procedures/gnulib/xalloc_die.py +14 -0
  983. angr/procedures/gnulib/xstrtol_fatal.py +14 -0
  984. angr/procedures/java/__init__.py +42 -0
  985. angr/procedures/java/unconstrained.py +65 -0
  986. angr/procedures/java_io/__init__.py +0 -0
  987. angr/procedures/java_io/read.py +12 -0
  988. angr/procedures/java_io/write.py +17 -0
  989. angr/procedures/java_jni/__init__.py +482 -0
  990. angr/procedures/java_jni/array_operations.py +312 -0
  991. angr/procedures/java_jni/class_and_interface_operations.py +31 -0
  992. angr/procedures/java_jni/field_access.py +173 -0
  993. angr/procedures/java_jni/global_and_local_refs.py +57 -0
  994. angr/procedures/java_jni/method_calls.py +365 -0
  995. angr/procedures/java_jni/not_implemented.py +26 -0
  996. angr/procedures/java_jni/object_operations.py +94 -0
  997. angr/procedures/java_jni/string_operations.py +87 -0
  998. angr/procedures/java_jni/version_information.py +12 -0
  999. angr/procedures/java_lang/__init__.py +0 -0
  1000. angr/procedures/java_lang/character.py +30 -0
  1001. angr/procedures/java_lang/double.py +24 -0
  1002. angr/procedures/java_lang/exit.py +13 -0
  1003. angr/procedures/java_lang/getsimplename.py +18 -0
  1004. angr/procedures/java_lang/integer.py +43 -0
  1005. angr/procedures/java_lang/load_library.py +9 -0
  1006. angr/procedures/java_lang/math.py +15 -0
  1007. angr/procedures/java_lang/string.py +78 -0
  1008. angr/procedures/java_lang/stringbuilder.py +44 -0
  1009. angr/procedures/java_lang/system.py +18 -0
  1010. angr/procedures/java_util/__init__.py +0 -0
  1011. angr/procedures/java_util/collection.py +35 -0
  1012. angr/procedures/java_util/iterator.py +46 -0
  1013. angr/procedures/java_util/list.py +99 -0
  1014. angr/procedures/java_util/map.py +131 -0
  1015. angr/procedures/java_util/random.py +14 -0
  1016. angr/procedures/java_util/scanner_nextline.py +23 -0
  1017. angr/procedures/libc/__init__.py +3 -0
  1018. angr/procedures/libc/abort.py +9 -0
  1019. angr/procedures/libc/access.py +13 -0
  1020. angr/procedures/libc/atoi.py +14 -0
  1021. angr/procedures/libc/atol.py +13 -0
  1022. angr/procedures/libc/calloc.py +8 -0
  1023. angr/procedures/libc/closelog.py +10 -0
  1024. angr/procedures/libc/err.py +14 -0
  1025. angr/procedures/libc/error.py +54 -0
  1026. angr/procedures/libc/exit.py +11 -0
  1027. angr/procedures/libc/fclose.py +19 -0
  1028. angr/procedures/libc/feof.py +21 -0
  1029. angr/procedures/libc/fflush.py +16 -0
  1030. angr/procedures/libc/fgetc.py +27 -0
  1031. angr/procedures/libc/fgets.py +68 -0
  1032. angr/procedures/libc/fopen.py +63 -0
  1033. angr/procedures/libc/fprintf.py +25 -0
  1034. angr/procedures/libc/fputc.py +23 -0
  1035. angr/procedures/libc/fputs.py +24 -0
  1036. angr/procedures/libc/fread.py +24 -0
  1037. angr/procedures/libc/free.py +9 -0
  1038. angr/procedures/libc/fscanf.py +20 -0
  1039. angr/procedures/libc/fseek.py +34 -0
  1040. angr/procedures/libc/ftell.py +22 -0
  1041. angr/procedures/libc/fwrite.py +19 -0
  1042. angr/procedures/libc/getchar.py +13 -0
  1043. angr/procedures/libc/getdelim.py +99 -0
  1044. angr/procedures/libc/getegid.py +8 -0
  1045. angr/procedures/libc/geteuid.py +8 -0
  1046. angr/procedures/libc/getgid.py +8 -0
  1047. angr/procedures/libc/gets.py +68 -0
  1048. angr/procedures/libc/getuid.py +8 -0
  1049. angr/procedures/libc/malloc.py +12 -0
  1050. angr/procedures/libc/memcmp.py +69 -0
  1051. angr/procedures/libc/memcpy.py +38 -0
  1052. angr/procedures/libc/memset.py +72 -0
  1053. angr/procedures/libc/openlog.py +10 -0
  1054. angr/procedures/libc/perror.py +13 -0
  1055. angr/procedures/libc/printf.py +34 -0
  1056. angr/procedures/libc/putchar.py +13 -0
  1057. angr/procedures/libc/puts.py +19 -0
  1058. angr/procedures/libc/rand.py +8 -0
  1059. angr/procedures/libc/realloc.py +8 -0
  1060. angr/procedures/libc/rewind.py +12 -0
  1061. angr/procedures/libc/scanf.py +20 -0
  1062. angr/procedures/libc/setbuf.py +9 -0
  1063. angr/procedures/libc/setvbuf.py +7 -0
  1064. angr/procedures/libc/snprintf.py +36 -0
  1065. angr/procedures/libc/sprintf.py +25 -0
  1066. angr/procedures/libc/srand.py +7 -0
  1067. angr/procedures/libc/sscanf.py +13 -0
  1068. angr/procedures/libc/stpcpy.py +18 -0
  1069. angr/procedures/libc/strcat.py +14 -0
  1070. angr/procedures/libc/strchr.py +48 -0
  1071. angr/procedures/libc/strcmp.py +31 -0
  1072. angr/procedures/libc/strcpy.py +13 -0
  1073. angr/procedures/libc/strlen.py +114 -0
  1074. angr/procedures/libc/strncat.py +19 -0
  1075. angr/procedures/libc/strncmp.py +183 -0
  1076. angr/procedures/libc/strncpy.py +22 -0
  1077. angr/procedures/libc/strnlen.py +13 -0
  1078. angr/procedures/libc/strstr.py +101 -0
  1079. angr/procedures/libc/strtol.py +261 -0
  1080. angr/procedures/libc/strtoul.py +9 -0
  1081. angr/procedures/libc/system.py +13 -0
  1082. angr/procedures/libc/time.py +9 -0
  1083. angr/procedures/libc/tmpnam.py +20 -0
  1084. angr/procedures/libc/tolower.py +10 -0
  1085. angr/procedures/libc/toupper.py +10 -0
  1086. angr/procedures/libc/ungetc.py +20 -0
  1087. angr/procedures/libc/vsnprintf.py +17 -0
  1088. angr/procedures/libc/wchar.py +16 -0
  1089. angr/procedures/libstdcpp/__init__.py +0 -0
  1090. angr/procedures/libstdcpp/_unwind_resume.py +11 -0
  1091. angr/procedures/libstdcpp/std____throw_bad_alloc.py +13 -0
  1092. angr/procedures/libstdcpp/std____throw_bad_cast.py +13 -0
  1093. angr/procedures/libstdcpp/std____throw_length_error.py +13 -0
  1094. angr/procedures/libstdcpp/std____throw_logic_error.py +13 -0
  1095. angr/procedures/libstdcpp/std__terminate.py +13 -0
  1096. angr/procedures/linux_kernel/__init__.py +3 -0
  1097. angr/procedures/linux_kernel/access.py +18 -0
  1098. angr/procedures/linux_kernel/arch_prctl.py +34 -0
  1099. angr/procedures/linux_kernel/arm_user_helpers.py +59 -0
  1100. angr/procedures/linux_kernel/brk.py +18 -0
  1101. angr/procedures/linux_kernel/cwd.py +28 -0
  1102. angr/procedures/linux_kernel/fstat.py +138 -0
  1103. angr/procedures/linux_kernel/fstat64.py +170 -0
  1104. angr/procedures/linux_kernel/futex.py +17 -0
  1105. angr/procedures/linux_kernel/getegid.py +17 -0
  1106. angr/procedures/linux_kernel/geteuid.py +17 -0
  1107. angr/procedures/linux_kernel/getgid.py +17 -0
  1108. angr/procedures/linux_kernel/getpid.py +14 -0
  1109. angr/procedures/linux_kernel/getrlimit.py +24 -0
  1110. angr/procedures/linux_kernel/gettid.py +9 -0
  1111. angr/procedures/linux_kernel/getuid.py +17 -0
  1112. angr/procedures/linux_kernel/iovec.py +47 -0
  1113. angr/procedures/linux_kernel/lseek.py +42 -0
  1114. angr/procedures/linux_kernel/mmap.py +16 -0
  1115. angr/procedures/linux_kernel/mprotect.py +42 -0
  1116. angr/procedures/linux_kernel/munmap.py +8 -0
  1117. angr/procedures/linux_kernel/openat.py +26 -0
  1118. angr/procedures/linux_kernel/set_tid_address.py +8 -0
  1119. angr/procedures/linux_kernel/sigaction.py +19 -0
  1120. angr/procedures/linux_kernel/sigprocmask.py +23 -0
  1121. angr/procedures/linux_kernel/stat.py +23 -0
  1122. angr/procedures/linux_kernel/sysinfo.py +59 -0
  1123. angr/procedures/linux_kernel/tgkill.py +10 -0
  1124. angr/procedures/linux_kernel/time.py +34 -0
  1125. angr/procedures/linux_kernel/uid.py +30 -0
  1126. angr/procedures/linux_kernel/uname.py +29 -0
  1127. angr/procedures/linux_kernel/unlink.py +22 -0
  1128. angr/procedures/linux_kernel/vsyscall.py +16 -0
  1129. angr/procedures/linux_loader/__init__.py +3 -0
  1130. angr/procedures/linux_loader/_dl_initial_error_catch_tsd.py +7 -0
  1131. angr/procedures/linux_loader/_dl_rtld_lock.py +15 -0
  1132. angr/procedures/linux_loader/sim_loader.py +54 -0
  1133. angr/procedures/linux_loader/tls.py +40 -0
  1134. angr/procedures/msvcr/__getmainargs.py +16 -0
  1135. angr/procedures/msvcr/__init__.py +4 -0
  1136. angr/procedures/msvcr/_initterm.py +38 -0
  1137. angr/procedures/msvcr/fmode.py +31 -0
  1138. angr/procedures/ntdll/__init__.py +0 -0
  1139. angr/procedures/ntdll/exceptions.py +60 -0
  1140. angr/procedures/posix/__init__.py +3 -0
  1141. angr/procedures/posix/accept.py +29 -0
  1142. angr/procedures/posix/bind.py +13 -0
  1143. angr/procedures/posix/bzero.py +9 -0
  1144. angr/procedures/posix/chroot.py +27 -0
  1145. angr/procedures/posix/close.py +9 -0
  1146. angr/procedures/posix/closedir.py +7 -0
  1147. angr/procedures/posix/dup.py +56 -0
  1148. angr/procedures/posix/fcntl.py +10 -0
  1149. angr/procedures/posix/fdopen.py +76 -0
  1150. angr/procedures/posix/fileno.py +18 -0
  1151. angr/procedures/posix/fork.py +13 -0
  1152. angr/procedures/posix/getenv.py +35 -0
  1153. angr/procedures/posix/gethostbyname.py +43 -0
  1154. angr/procedures/posix/getpass.py +19 -0
  1155. angr/procedures/posix/getsockopt.py +11 -0
  1156. angr/procedures/posix/htonl.py +11 -0
  1157. angr/procedures/posix/htons.py +11 -0
  1158. angr/procedures/posix/inet_ntoa.py +59 -0
  1159. angr/procedures/posix/listen.py +13 -0
  1160. angr/procedures/posix/mmap.py +144 -0
  1161. angr/procedures/posix/open.py +18 -0
  1162. angr/procedures/posix/opendir.py +10 -0
  1163. angr/procedures/posix/poll.py +55 -0
  1164. angr/procedures/posix/pread64.py +46 -0
  1165. angr/procedures/posix/pthread.py +87 -0
  1166. angr/procedures/posix/pwrite64.py +46 -0
  1167. angr/procedures/posix/read.py +13 -0
  1168. angr/procedures/posix/readdir.py +62 -0
  1169. angr/procedures/posix/recv.py +13 -0
  1170. angr/procedures/posix/recvfrom.py +13 -0
  1171. angr/procedures/posix/select.py +48 -0
  1172. angr/procedures/posix/send.py +23 -0
  1173. angr/procedures/posix/setsockopt.py +9 -0
  1174. angr/procedures/posix/sigaction.py +23 -0
  1175. angr/procedures/posix/sim_time.py +48 -0
  1176. angr/procedures/posix/sleep.py +8 -0
  1177. angr/procedures/posix/socket.py +18 -0
  1178. angr/procedures/posix/strcasecmp.py +26 -0
  1179. angr/procedures/posix/strdup.py +18 -0
  1180. angr/procedures/posix/strtok_r.py +64 -0
  1181. angr/procedures/posix/syslog.py +15 -0
  1182. angr/procedures/posix/tz.py +9 -0
  1183. angr/procedures/posix/unlink.py +11 -0
  1184. angr/procedures/posix/usleep.py +8 -0
  1185. angr/procedures/posix/write.py +13 -0
  1186. angr/procedures/procedure_dict.py +50 -0
  1187. angr/procedures/stubs/CallReturn.py +13 -0
  1188. angr/procedures/stubs/NoReturnUnconstrained.py +13 -0
  1189. angr/procedures/stubs/Nop.py +7 -0
  1190. angr/procedures/stubs/PathTerminator.py +9 -0
  1191. angr/procedures/stubs/Redirect.py +18 -0
  1192. angr/procedures/stubs/ReturnChar.py +11 -0
  1193. angr/procedures/stubs/ReturnUnconstrained.py +24 -0
  1194. angr/procedures/stubs/UnresolvableCallTarget.py +9 -0
  1195. angr/procedures/stubs/UnresolvableJumpTarget.py +9 -0
  1196. angr/procedures/stubs/UserHook.py +18 -0
  1197. angr/procedures/stubs/__init__.py +3 -0
  1198. angr/procedures/stubs/b64_decode.py +15 -0
  1199. angr/procedures/stubs/caller.py +14 -0
  1200. angr/procedures/stubs/crazy_scanf.py +20 -0
  1201. angr/procedures/stubs/format_parser.py +669 -0
  1202. angr/procedures/stubs/syscall_stub.py +24 -0
  1203. angr/procedures/testing/__init__.py +3 -0
  1204. angr/procedures/testing/manyargs.py +9 -0
  1205. angr/procedures/testing/retreg.py +8 -0
  1206. angr/procedures/tracer/__init__.py +4 -0
  1207. angr/procedures/tracer/random.py +9 -0
  1208. angr/procedures/tracer/receive.py +23 -0
  1209. angr/procedures/tracer/transmit.py +26 -0
  1210. angr/procedures/uclibc/__init__.py +3 -0
  1211. angr/procedures/uclibc/__uClibc_main.py +10 -0
  1212. angr/procedures/win32/EncodePointer.py +7 -0
  1213. angr/procedures/win32/ExitProcess.py +9 -0
  1214. angr/procedures/win32/GetCommandLine.py +12 -0
  1215. angr/procedures/win32/GetCurrentProcessId.py +7 -0
  1216. angr/procedures/win32/GetCurrentThreadId.py +7 -0
  1217. angr/procedures/win32/GetLastInputInfo.py +40 -0
  1218. angr/procedures/win32/GetModuleHandle.py +29 -0
  1219. angr/procedures/win32/GetProcessAffinityMask.py +37 -0
  1220. angr/procedures/win32/InterlockedExchange.py +15 -0
  1221. angr/procedures/win32/IsProcessorFeaturePresent.py +7 -0
  1222. angr/procedures/win32/VirtualAlloc.py +114 -0
  1223. angr/procedures/win32/VirtualProtect.py +60 -0
  1224. angr/procedures/win32/__init__.py +3 -0
  1225. angr/procedures/win32/critical_section.py +12 -0
  1226. angr/procedures/win32/dynamic_loading.py +104 -0
  1227. angr/procedures/win32/file_handles.py +47 -0
  1228. angr/procedures/win32/gethostbyname.py +12 -0
  1229. angr/procedures/win32/heap.py +45 -0
  1230. angr/procedures/win32/is_bad_ptr.py +26 -0
  1231. angr/procedures/win32/local_storage.py +88 -0
  1232. angr/procedures/win32/mutex.py +11 -0
  1233. angr/procedures/win32/sim_time.py +135 -0
  1234. angr/procedures/win32/system_paths.py +35 -0
  1235. angr/procedures/win32_kernel/ExAllocatePool.py +13 -0
  1236. angr/procedures/win32_kernel/ExFreePoolWithTag.py +8 -0
  1237. angr/procedures/win32_kernel/__fastfail.py +15 -0
  1238. angr/procedures/win32_kernel/__init__.py +3 -0
  1239. angr/procedures/win_user32/__init__.py +0 -0
  1240. angr/procedures/win_user32/chars.py +15 -0
  1241. angr/procedures/win_user32/keyboard.py +14 -0
  1242. angr/procedures/win_user32/messagebox.py +49 -0
  1243. angr/project.py +837 -0
  1244. angr/protos/__init__.py +19 -0
  1245. angr/protos/cfg_pb2.py +31 -0
  1246. angr/protos/function_pb2.py +27 -0
  1247. angr/protos/primitives_pb2.py +52 -0
  1248. angr/protos/variables_pb2.py +44 -0
  1249. angr/protos/xrefs_pb2.py +25 -0
  1250. angr/py.typed +1 -0
  1251. angr/rustylib.abi3.so +0 -0
  1252. angr/serializable.py +66 -0
  1253. angr/sim_manager.py +971 -0
  1254. angr/sim_options.py +438 -0
  1255. angr/sim_procedure.py +606 -0
  1256. angr/sim_state.py +901 -0
  1257. angr/sim_state_options.py +403 -0
  1258. angr/sim_type.py +3679 -0
  1259. angr/sim_variable.py +465 -0
  1260. angr/simos/__init__.py +47 -0
  1261. angr/simos/cgc.py +153 -0
  1262. angr/simos/javavm.py +458 -0
  1263. angr/simos/linux.py +509 -0
  1264. angr/simos/simos.py +444 -0
  1265. angr/simos/snimmuc_nxp.py +149 -0
  1266. angr/simos/userland.py +163 -0
  1267. angr/simos/windows.py +601 -0
  1268. angr/simos/xbox.py +32 -0
  1269. angr/slicer.py +352 -0
  1270. angr/state_hierarchy.py +262 -0
  1271. angr/state_plugins/__init__.py +84 -0
  1272. angr/state_plugins/callstack.py +398 -0
  1273. angr/state_plugins/cgc.py +155 -0
  1274. angr/state_plugins/debug_variables.py +192 -0
  1275. angr/state_plugins/filesystem.py +463 -0
  1276. angr/state_plugins/gdb.py +148 -0
  1277. angr/state_plugins/globals.py +65 -0
  1278. angr/state_plugins/heap/__init__.py +15 -0
  1279. angr/state_plugins/heap/heap_base.py +128 -0
  1280. angr/state_plugins/heap/heap_brk.py +136 -0
  1281. angr/state_plugins/heap/heap_freelist.py +213 -0
  1282. angr/state_plugins/heap/heap_libc.py +46 -0
  1283. angr/state_plugins/heap/heap_ptmalloc.py +620 -0
  1284. angr/state_plugins/heap/utils.py +22 -0
  1285. angr/state_plugins/history.py +548 -0
  1286. angr/state_plugins/inspect.py +375 -0
  1287. angr/state_plugins/javavm_classloader.py +134 -0
  1288. angr/state_plugins/jni_references.py +95 -0
  1289. angr/state_plugins/libc.py +1263 -0
  1290. angr/state_plugins/light_registers.py +168 -0
  1291. angr/state_plugins/log.py +84 -0
  1292. angr/state_plugins/loop_data.py +92 -0
  1293. angr/state_plugins/plugin.py +170 -0
  1294. angr/state_plugins/posix.py +703 -0
  1295. angr/state_plugins/preconstrainer.py +196 -0
  1296. angr/state_plugins/scratch.py +173 -0
  1297. angr/state_plugins/sim_action.py +326 -0
  1298. angr/state_plugins/sim_action_object.py +271 -0
  1299. angr/state_plugins/sim_event.py +59 -0
  1300. angr/state_plugins/solver.py +1127 -0
  1301. angr/state_plugins/symbolizer.py +291 -0
  1302. angr/state_plugins/trace_additions.py +738 -0
  1303. angr/state_plugins/uc_manager.py +94 -0
  1304. angr/state_plugins/unicorn_engine.py +1886 -0
  1305. angr/state_plugins/view.py +340 -0
  1306. angr/storage/__init__.py +15 -0
  1307. angr/storage/file.py +1210 -0
  1308. angr/storage/memory_mixins/__init__.py +317 -0
  1309. angr/storage/memory_mixins/actions_mixin.py +72 -0
  1310. angr/storage/memory_mixins/address_concretization_mixin.py +384 -0
  1311. angr/storage/memory_mixins/bvv_conversion_mixin.py +73 -0
  1312. angr/storage/memory_mixins/clouseau_mixin.py +137 -0
  1313. angr/storage/memory_mixins/conditional_store_mixin.py +25 -0
  1314. angr/storage/memory_mixins/convenient_mappings_mixin.py +256 -0
  1315. angr/storage/memory_mixins/default_filler_mixin.py +144 -0
  1316. angr/storage/memory_mixins/dirty_addrs_mixin.py +11 -0
  1317. angr/storage/memory_mixins/hex_dumper_mixin.py +82 -0
  1318. angr/storage/memory_mixins/javavm_memory_mixin.py +392 -0
  1319. angr/storage/memory_mixins/keyvalue_memory_mixin.py +42 -0
  1320. angr/storage/memory_mixins/label_merger_mixin.py +31 -0
  1321. angr/storage/memory_mixins/memory_mixin.py +174 -0
  1322. angr/storage/memory_mixins/multi_value_merger_mixin.py +79 -0
  1323. angr/storage/memory_mixins/name_resolution_mixin.py +67 -0
  1324. angr/storage/memory_mixins/paged_memory/__init__.py +0 -0
  1325. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +266 -0
  1326. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +743 -0
  1327. angr/storage/memory_mixins/paged_memory/paged_memory_multivalue_mixin.py +65 -0
  1328. angr/storage/memory_mixins/paged_memory/pages/__init__.py +26 -0
  1329. angr/storage/memory_mixins/paged_memory/pages/base.py +31 -0
  1330. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +341 -0
  1331. angr/storage/memory_mixins/paged_memory/pages/history_tracking_mixin.py +92 -0
  1332. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +55 -0
  1333. angr/storage/memory_mixins/paged_memory/pages/list_page.py +338 -0
  1334. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +324 -0
  1335. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +419 -0
  1336. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +36 -0
  1337. angr/storage/memory_mixins/paged_memory/pages/refcount_mixin.py +52 -0
  1338. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +503 -0
  1339. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +36 -0
  1340. angr/storage/memory_mixins/paged_memory/stack_allocation_mixin.py +74 -0
  1341. angr/storage/memory_mixins/regioned_memory/__init__.py +17 -0
  1342. angr/storage/memory_mixins/regioned_memory/abstract_address_descriptor.py +36 -0
  1343. angr/storage/memory_mixins/regioned_memory/abstract_merger_mixin.py +31 -0
  1344. angr/storage/memory_mixins/regioned_memory/region_category_mixin.py +9 -0
  1345. angr/storage/memory_mixins/regioned_memory/region_data.py +246 -0
  1346. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +241 -0
  1347. angr/storage/memory_mixins/regioned_memory/regioned_address_concretization_mixin.py +119 -0
  1348. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +441 -0
  1349. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +69 -0
  1350. angr/storage/memory_mixins/simple_interface_mixin.py +71 -0
  1351. angr/storage/memory_mixins/simplification_mixin.py +15 -0
  1352. angr/storage/memory_mixins/size_resolution_mixin.py +143 -0
  1353. angr/storage/memory_mixins/slotted_memory.py +140 -0
  1354. angr/storage/memory_mixins/smart_find_mixin.py +161 -0
  1355. angr/storage/memory_mixins/symbolic_merger_mixin.py +16 -0
  1356. angr/storage/memory_mixins/top_merger_mixin.py +25 -0
  1357. angr/storage/memory_mixins/underconstrained_mixin.py +67 -0
  1358. angr/storage/memory_mixins/unwrapper_mixin.py +26 -0
  1359. angr/storage/memory_object.py +195 -0
  1360. angr/tablespecs.py +91 -0
  1361. angr/utils/__init__.py +46 -0
  1362. angr/utils/ail.py +70 -0
  1363. angr/utils/algo.py +34 -0
  1364. angr/utils/bits.py +46 -0
  1365. angr/utils/constants.py +9 -0
  1366. angr/utils/cowdict.py +63 -0
  1367. angr/utils/cpp.py +17 -0
  1368. angr/utils/doms.py +149 -0
  1369. angr/utils/dynamic_dictlist.py +89 -0
  1370. angr/utils/endness.py +18 -0
  1371. angr/utils/enums_conv.py +97 -0
  1372. angr/utils/env.py +12 -0
  1373. angr/utils/formatting.py +128 -0
  1374. angr/utils/funcid.py +159 -0
  1375. angr/utils/graph.py +898 -0
  1376. angr/utils/lazy_import.py +13 -0
  1377. angr/utils/library.py +211 -0
  1378. angr/utils/loader.py +55 -0
  1379. angr/utils/mp.py +66 -0
  1380. angr/utils/orderedset.py +74 -0
  1381. angr/utils/ssa/__init__.py +395 -0
  1382. angr/utils/ssa/tmp_uses_collector.py +23 -0
  1383. angr/utils/ssa/vvar_uses_collector.py +37 -0
  1384. angr/utils/tagged_interval_map.py +112 -0
  1385. angr/utils/timing.py +74 -0
  1386. angr/utils/types.py +151 -0
  1387. angr/vaults.py +367 -0
  1388. angr-9.2.158.dist-info/METADATA +111 -0
  1389. angr-9.2.158.dist-info/RECORD +1393 -0
  1390. angr-9.2.158.dist-info/WHEEL +5 -0
  1391. angr-9.2.158.dist-info/entry_points.txt +2 -0
  1392. angr-9.2.158.dist-info/licenses/LICENSE +27 -0
  1393. angr-9.2.158.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2999 @@
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 ailment.block import Block
13
+ from ailment.statement import Statement, ConditionalJump, Jump, Label, Return
14
+ from ailment.expression import Const, UnaryOp, MultiStatementExpression
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
+ is_empty_or_label_only_node,
28
+ has_nonlabel_nonphi_statements,
29
+ first_nonlabel_nonphi_statement,
30
+ switch_extract_bitwiseand_jumptable_info,
31
+ switch_extract_switch_expr_from_jump_target,
32
+ )
33
+ from angr.analyses.decompiler.counters.call_counter import AILCallCounter
34
+ from .structurer_nodes import (
35
+ ConditionNode,
36
+ SequenceNode,
37
+ LoopNode,
38
+ ConditionalBreakNode,
39
+ BreakNode,
40
+ ContinueNode,
41
+ BaseNode,
42
+ MultiNode,
43
+ SwitchCaseNode,
44
+ IncompleteSwitchCaseNode,
45
+ EmptyBlockNotice,
46
+ IncompleteSwitchCaseHeadStatement,
47
+ )
48
+ from .structurer_base import StructurerBase
49
+
50
+ if TYPE_CHECKING:
51
+ from angr.knowledge_plugins.functions import Function
52
+
53
+ l = logging.getLogger(__name__)
54
+ _DEBUG = False
55
+
56
+
57
+ class GraphChangedNotification(Exception):
58
+ """
59
+ A notification for graph that is currently worked on being changed. Once this notification is caught, the graph
60
+ schema matching process for the current region restarts.
61
+ """
62
+
63
+
64
+ class MultiStmtExprMode(str, Enum):
65
+ """
66
+ Mode of multi-statement expression creation during structuring.
67
+ """
68
+
69
+ NEVER = "Never"
70
+ ALWAYS = "Always"
71
+ MAX_ONE_CALL = "Only when less than one call"
72
+
73
+
74
+ class PhoenixStructurer(StructurerBase):
75
+ """
76
+ Structure a region using a structuring algorithm that is similar to the one in Phoenix decompiler (described in the
77
+ "phoenix decompiler" paper). Note that this implementation has quite a few improvements over the original described
78
+ version and *should not* be used to evaluate the performance of the original algorithm described in that paper.
79
+ """
80
+
81
+ NAME = "phoenix"
82
+
83
+ def __init__(
84
+ self,
85
+ region,
86
+ parent_map=None,
87
+ condition_processor=None,
88
+ func: Function | None = None,
89
+ case_entry_to_switch_head: dict[int, int] | None = None,
90
+ parent_region=None,
91
+ improve_algorithm=False,
92
+ use_multistmtexprs: MultiStmtExprMode = MultiStmtExprMode.MAX_ONE_CALL,
93
+ multistmtexpr_stmt_threshold: int = 5,
94
+ **kwargs,
95
+ ):
96
+ super().__init__(
97
+ region,
98
+ parent_map=parent_map,
99
+ condition_processor=condition_processor,
100
+ func=func,
101
+ case_entry_to_switch_head=case_entry_to_switch_head,
102
+ parent_region=parent_region,
103
+ **kwargs,
104
+ )
105
+
106
+ # whitelist certain edges. removing these edges will destroy critical schemas, which will impact future
107
+ # structuring cycles.
108
+ # the set is populated during the analysis. _last_resort_refinement() will ensure not to remove any edges
109
+ # who fall into these sets.
110
+ self.whitelist_edges: set[tuple[int, int]] = set()
111
+ # also whitelist certain nodes that are definitely header for switch-case constructs. they should not be merged
112
+ # into another node before we successfully structure the entire switch-case.
113
+ self.switch_case_known_heads: set[Block] = set()
114
+
115
+ # whitelist certain nodes that should be treated as a tail node for do-whiles. these nodes should not be
116
+ # absorbed into other SequenceNodes
117
+ self.dowhile_known_tail_nodes: set = set()
118
+
119
+ # in reimplementing the core phoenix algorithm from the phoenix decompiler paper, two types of changes were
120
+ # made to the algorithm:
121
+ # 1. Mandatory fixes to correct flaws we found in the algorithm
122
+ # 2. Optional fixes to improve the results of already correct choices
123
+ #
124
+ # the improve_algorithm flag controls whether the optional fixes are applied. these are disabled by default
125
+ # to be as close to the original algorithm as possible. for best results, enable this flag.
126
+ self._improve_algorithm = improve_algorithm
127
+ self._edge_virtualization_hints = []
128
+
129
+ self._use_multistmtexprs = use_multistmtexprs
130
+ self._multistmtexpr_stmt_threshold = multistmtexpr_stmt_threshold
131
+ self._analyze()
132
+
133
+ @staticmethod
134
+ def _assert_graph_ok(g, msg: str) -> None:
135
+ if _DEBUG:
136
+ if g is None:
137
+ return
138
+ assert (
139
+ len(list(networkx.connected_components(networkx.Graph(g)))) <= 1
140
+ ), f"{msg}: More than one connected component. Please report this."
141
+ assert (
142
+ len([nn for nn in g if g.in_degree[nn] == 0]) <= 1
143
+ ), f"{msg}: More than one graph entrance. Please report this."
144
+
145
+ def _analyze(self):
146
+ # iterate until there is only one node in the region
147
+
148
+ self._assert_graph_ok(self._region.graph, "Incorrect region graph")
149
+
150
+ has_cycle = self._has_cycle()
151
+
152
+ # special handling for single-node loops
153
+ if len(self._region.graph.nodes) == 1 and has_cycle:
154
+ self._analyze_cyclic()
155
+
156
+ # backup the region prior to conducting a cyclic refinement because we may not be able to structure a cycle out
157
+ # of the refined graph. in that case, we restore the original region and return.
158
+ pre_refinement_region = None
159
+
160
+ while len(self._region.graph.nodes) > 1:
161
+ progressed = self._analyze_acyclic()
162
+ if progressed and self._region.head not in self._region.graph:
163
+ # update the head
164
+ self._region.head = next(
165
+ iter(node for node in self._region.graph.nodes if node.addr == self._region.head.addr)
166
+ )
167
+
168
+ if has_cycle:
169
+ progressed |= self._analyze_cyclic()
170
+ if progressed:
171
+ pre_refinement_region = None
172
+ if self._region.head not in self._region.graph:
173
+ # update the loop head
174
+ self._region.head = next(
175
+ iter(node for node in self._region.graph.nodes if node.addr == self._region.head.addr)
176
+ )
177
+ elif pre_refinement_region is None:
178
+ pre_refinement_region = self._region.copy()
179
+ refined = self._refine_cyclic()
180
+ if refined:
181
+ if self._region.head not in self._region.graph:
182
+ # update the loop head
183
+ self._region.head = next(
184
+ iter(node for node in self._region.graph.nodes if node.addr == self._region.head.addr)
185
+ )
186
+ has_cycle = self._has_cycle()
187
+ continue
188
+ has_cycle = self._has_cycle()
189
+
190
+ if not progressed:
191
+ if self._region.cyclic_ancestor and not self._region.cyclic:
192
+ # we prefer directly returning this subgraph in case it can be further restructured within a loop
193
+ # region
194
+ l.debug("No progress is made on this acyclic graph with a cyclic ancestor. Give up.")
195
+ break
196
+
197
+ l.debug("No progress is made. Enter last resort refinement.")
198
+ removed_edge = self._last_resort_refinement(
199
+ self._region.head,
200
+ self._region.graph,
201
+ (
202
+ self._region.graph_with_successors
203
+ if self._region.graph_with_successors is not None
204
+ else networkx.DiGraph(self._region.graph)
205
+ ),
206
+ )
207
+ self._assert_graph_ok(self._region.graph, "Last resort refinement went wrong")
208
+ if not removed_edge:
209
+ # cannot make any progress in this region. return the subgraph directly
210
+ break
211
+
212
+ if len(self._region.graph.nodes) == 1:
213
+ # successfully structured
214
+ self.result = next(iter(self._region.graph.nodes))
215
+ else:
216
+ if pre_refinement_region is not None:
217
+ # we could not make a loop after the last cycle refinement. restore the graph
218
+ self._region = pre_refinement_region
219
+
220
+ self.result = None # the actual result is in self._region.graph and self._region.graph_with_successors
221
+
222
+ def _analyze_cyclic(self) -> bool:
223
+ any_matches = False
224
+ acyclic_graph = to_acyclic_graph(self._region.graph, loop_heads=[self._region.head])
225
+ for node in list(reversed(GraphUtils.quasi_topological_sort_nodes(acyclic_graph))):
226
+ if node not in self._region.graph:
227
+ continue
228
+ matched = self._match_cyclic_schemas(
229
+ node,
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
+ l.debug("... matching cyclic schemas: %s at %r", matched, node)
239
+ any_matches |= matched
240
+ if matched:
241
+ self._assert_graph_ok(self._region.graph, "Removed incorrect edges")
242
+ return any_matches
243
+
244
+ def _match_cyclic_schemas(self, node, head, graph, full_graph) -> bool:
245
+ matched, loop_node, successor_node = self._match_cyclic_while(node, head, graph, full_graph)
246
+ if matched:
247
+ assert loop_node is not None and successor_node is not None
248
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
249
+ self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
250
+ # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
251
+ self._rewrite_jumps_to_continues(loop_node.sequence_node)
252
+ return True
253
+
254
+ matched, loop_node, successor_node = self._match_cyclic_dowhile(node, head, graph, full_graph)
255
+ if matched:
256
+ assert loop_node is not None and successor_node is not None
257
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
258
+ self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
259
+ # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
260
+ self._rewrite_jumps_to_continues(loop_node.sequence_node, loop_node=loop_node)
261
+ return True
262
+
263
+ if self._improve_algorithm:
264
+ matched, loop_node, successor_node = self._match_cyclic_while_with_single_successor(
265
+ node, head, graph, full_graph
266
+ )
267
+ if matched:
268
+ assert loop_node is not None and successor_node is not None
269
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
270
+ self._rewrite_conditional_jumps_to_breaks(loop_node.sequence_node, [successor_node.addr])
271
+ # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
272
+ self._rewrite_jumps_to_continues(loop_node.sequence_node)
273
+ return True
274
+
275
+ matched, loop_node = self._match_cyclic_natural_loop(node, head, graph, full_graph)
276
+ if matched:
277
+ assert loop_node is not None
278
+ if self._region.successors is not None and len(self._region.successors) == 1:
279
+ # traverse this node and rewrite all conditional jumps that go outside the loop to breaks
280
+ self._rewrite_conditional_jumps_to_breaks(
281
+ loop_node.sequence_node, [succ.addr for succ in self._region.successors]
282
+ )
283
+ # traverse this node and rewrite all jumps that go to the beginning of the loop to continue
284
+ self._rewrite_jumps_to_continues(loop_node.sequence_node)
285
+ return matched
286
+
287
+ def _match_cyclic_while(self, node, head, graph, full_graph) -> tuple[bool, LoopNode | None, BaseNode | None]:
288
+ succs = list(full_graph.successors(node))
289
+ if len(succs) == 2:
290
+ left, right = succs
291
+
292
+ if full_graph.has_edge(right, node) and not full_graph.has_edge(left, node):
293
+ left, right = right, left
294
+ if left is node:
295
+ # self loop
296
+ # possible candidate
297
+ head_block_idx, _, head_block = self._find_node_going_to_dst(node, left)
298
+ if head_block is None:
299
+ # it happens. for example:
300
+ # ## Block 4058c8
301
+ # 00 | 0x4058c8 | if ((rcx<8> == 0x0<64>)) { Goto 0x4058ca<64> } else { Goto None }
302
+ # 01 | 0x4058c8 | rcx<8> = (rcx<8> - 0x1<64>)
303
+ # 02 | 0x4058c8 | cc_dep1<8> = Conv(8->64, Load(addr=rsi<8>, size=1, endness=Iend_LE))
304
+ # 03 | 0x4058c8 | cc_dep2<8> = Conv(8->64, Load(addr=rdi<8>, size=1, endness=Iend_LE))
305
+ # 04 | 0x4058c8 | rdi<8> = (rdi<8> + d<8>)
306
+ # 05 | 0x4058c8 | rsi<8> = (rsi<8> + d<8>)
307
+ # 06 | 0x4058c8 | if ((Conv(64->8, cc_dep1<8>) == Conv(64->8, cc_dep2<8>))) { Goto 0x4058c8<64> }
308
+ # else { Goto None }
309
+ # 07 | 0x4058c8 | Goto(0x4058ca<64>)
310
+ head_block_idx, _, head_block = self._find_node_going_to_dst(node, right)
311
+
312
+ if (
313
+ isinstance(head_block, MultiNode)
314
+ and head_block.nodes
315
+ and isinstance(head_block.nodes[0], Block)
316
+ and head_block.nodes[0].statements
317
+ and is_head_controlled_loop_block(head_block.nodes[0])
318
+ ) or (
319
+ isinstance(head_block, Block)
320
+ and head_block.statements
321
+ and is_head_controlled_loop_block(head_block)
322
+ ):
323
+ # it's a while loop if the conditional jump (or the head block) is at the beginning of node
324
+ loop_type = "while" if head_block_idx == 0 else "do-while"
325
+ # otherwise it's a do-while loop
326
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
327
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
328
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
329
+ # c = !c
330
+ if head_block_idx == 0:
331
+ self._remove_first_statement_if_jump(head_block)
332
+ else:
333
+ remove_last_statement(head_block)
334
+ seq_node = SequenceNode(node.addr, nodes=[node]) if not isinstance(node, SequenceNode) else node
335
+ loop_node = LoopNode(loop_type, edge_cond_left, seq_node, addr=seq_node.addr)
336
+ self.replace_nodes(graph, node, loop_node, self_loop=False)
337
+ self.replace_nodes(full_graph, node, loop_node, self_loop=False)
338
+
339
+ # ensure the loop has only one successor: the right node
340
+ self._remove_edges_except(graph, loop_node, right)
341
+ self._remove_edges_except(full_graph, loop_node, right)
342
+
343
+ return True, loop_node, right
344
+ elif (
345
+ full_graph.has_edge(left, node)
346
+ and left is not head
347
+ and full_graph.in_degree[left] == 1
348
+ and full_graph.out_degree[left] == 1
349
+ and not full_graph.has_edge(right, node)
350
+ ):
351
+ # possible candidate
352
+ _, _, head_block = self._find_node_going_to_dst(node, left, condjump_only=True)
353
+ if head_block is not None:
354
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
355
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
356
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
357
+ # c = !c
358
+ if PhoenixStructurer._is_single_statement_block(node):
359
+ # the single-statement-block check is to ensure we don't execute any code before the
360
+ # conditional jump. this way the entire node can be dropped.
361
+ new_node = SequenceNode(node.addr, nodes=[left])
362
+ loop_node = LoopNode("while", edge_cond_left, new_node, addr=node.addr)
363
+
364
+ # on the original graph
365
+ self.replace_nodes(graph, node, loop_node, old_node_1=left, self_loop=False)
366
+ # on the graph with successors
367
+ self.replace_nodes(full_graph, node, loop_node, old_node_1=left, self_loop=False)
368
+
369
+ # ensure the loop has only one successor: the right node
370
+ self._remove_edges_except(graph, loop_node, right)
371
+ self._remove_edges_except(full_graph, loop_node, right)
372
+
373
+ return True, loop_node, right
374
+ # we generate a while-true loop instead
375
+ last_stmt = self._remove_last_statement_if_jump(head_block)
376
+ assert last_stmt is not None
377
+ cond_jump = Jump(
378
+ None,
379
+ Const(None, None, right.addr, self.project.arch.bits),
380
+ None,
381
+ ins_addr=last_stmt.ins_addr,
382
+ )
383
+ jump_node = Block(last_stmt.ins_addr, None, statements=[cond_jump])
384
+ cond_jump_node = ConditionNode(last_stmt.ins_addr, None, edge_cond_right, jump_node)
385
+ new_node = SequenceNode(node.addr, nodes=[node, cond_jump_node, left])
386
+ loop_node = LoopNode("while", claripy.true(), new_node, addr=node.addr)
387
+
388
+ # on the original graph
389
+ self.replace_nodes(graph, node, loop_node, old_node_1=left, self_loop=False)
390
+ # on the graph with successors
391
+ self.replace_nodes(full_graph, node, loop_node, old_node_1=left, self_loop=False)
392
+
393
+ # ensure the loop has only one successor: the right node
394
+ self._remove_edges_except(graph, loop_node, right)
395
+ self._remove_edges_except(full_graph, loop_node, right)
396
+
397
+ return True, loop_node, right
398
+
399
+ if self._improve_algorithm and full_graph.out_degree[node] == 1:
400
+ # while (true) { ...; if (...) break; }
401
+ _, _, head_block = self._find_node_going_to_dst(node, left, condjump_only=True)
402
+ if head_block is not None:
403
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, head_block, left)
404
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, head_block, right)
405
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
406
+ # c = !c
407
+ self._remove_last_statement_if_jump(head_block)
408
+ cond_break = ConditionalBreakNode(node.addr, edge_cond_right, right.addr)
409
+ new_node = SequenceNode(node.addr, nodes=[node, cond_break, left])
410
+ loop_node = LoopNode("while", claripy.true(), new_node, addr=node.addr)
411
+
412
+ # on the original graph
413
+ self.replace_nodes(graph, node, loop_node, old_node_1=left, self_loop=False)
414
+ # on the graph with successors
415
+ self.replace_nodes(full_graph, node, loop_node, old_node_1=left, self_loop=False)
416
+
417
+ # ensure the loop has only one successor: the right node
418
+ self._remove_edges_except(graph, loop_node, right)
419
+ self._remove_edges_except(full_graph, loop_node, right)
420
+
421
+ return True, loop_node, right
422
+
423
+ return False, None, None
424
+
425
+ def _match_cyclic_while_with_single_successor(
426
+ self, node, head, graph, full_graph
427
+ ) -> tuple[bool, LoopNode | None, BaseNode | None]:
428
+ if self._region.successors:
429
+ return False, None, None
430
+ if node is not head:
431
+ return False, None, None
432
+
433
+ if not (node is head or graph.in_degree[node] == 2):
434
+ return False, None, None
435
+
436
+ loop_cond = None
437
+ successor_node = None
438
+ succ_node_is_true_node = None
439
+ if (
440
+ isinstance(node, SequenceNode)
441
+ and node.nodes
442
+ and isinstance(node.nodes[-1], ConditionNode)
443
+ # must ensure the nodes before the condition node are empty. otherwise the condition may use variables that
444
+ # are updated in these nodes leading up to the condition node.
445
+ and all(not self.has_nonlabel_nonphi_statements(nn) for nn in node.nodes[:-1])
446
+ and node.nodes[-1].true_node is not None
447
+ and node.nodes[-1].false_node is not None
448
+ ):
449
+ # try both true node and false node; pick the first node with only Returns as last statements as the
450
+ # successor.
451
+ if self._cyclic_while_with_single_successor_must_return(node.nodes[-1].true_node):
452
+ succ_node_is_true_node = True
453
+ successor_node = node.nodes[-1].true_node
454
+ loop_cond = claripy.Not(node.nodes[-1].condition)
455
+ elif self._cyclic_while_with_single_successor_must_return(node.nodes[-1].false_node):
456
+ succ_node_is_true_node = False
457
+ successor_node = node.nodes[-1].false_node
458
+ loop_cond = node.nodes[-1].condition
459
+ else:
460
+ loop_cond = None
461
+
462
+ if loop_cond is None:
463
+ return False, None, None
464
+
465
+ node_copy = node.copy()
466
+ # replace the last node with the intended successor node
467
+ node_copy.nodes[-1] = (
468
+ node_copy.nodes[-1].false_node if succ_node_is_true_node else node_copy.nodes[-1].true_node
469
+ )
470
+ # check if there is a cycle that starts with node and ends with node
471
+ next_node = node
472
+ seq_node = SequenceNode(node.addr, nodes=[node_copy])
473
+ seen_nodes = set()
474
+ while True:
475
+ succs = list(full_graph.successors(next_node))
476
+ if len(succs) != 1:
477
+ return False, None, None
478
+ next_node = succs[0]
479
+
480
+ if next_node is node:
481
+ break
482
+ if next_node is not node and next_node in seen_nodes:
483
+ return False, None, None
484
+
485
+ seen_nodes.add(next_node)
486
+ seq_node.nodes.append(next_node)
487
+
488
+ loop_node = LoopNode("while", loop_cond, seq_node, addr=node.addr)
489
+
490
+ # on the original graph
491
+ for node_ in seq_node.nodes:
492
+ if node_ is not node_copy:
493
+ graph.remove_node(node_)
494
+ self.replace_nodes(graph, node, loop_node, self_loop=False)
495
+ graph.add_edge(loop_node, successor_node)
496
+
497
+ # on the graph with successors
498
+ for node_ in seq_node.nodes:
499
+ if node_ is not node_copy:
500
+ full_graph.remove_node(node_)
501
+ self.replace_nodes(full_graph, node, loop_node, self_loop=False)
502
+ full_graph.add_edge(loop_node, successor_node)
503
+
504
+ return True, loop_node, successor_node
505
+
506
+ def _cyclic_while_with_single_successor_must_return(self, successor_node: SequenceNode) -> bool:
507
+ try:
508
+ last_stmts = self.cond_proc.get_last_statements(successor_node)
509
+ except EmptyBlockNotice:
510
+ return False
511
+ if not last_stmts:
512
+ return False
513
+ return all(isinstance(stmt, Return) for stmt in last_stmts)
514
+
515
+ def _match_cyclic_dowhile(self, node, head, graph, full_graph) -> tuple[bool, LoopNode | None, BaseNode | None]:
516
+ preds = list(full_graph.predecessors(node))
517
+ succs = list(full_graph.successors(node))
518
+ if ((node is head and len(preds) >= 1) or len(preds) >= 2) and len(succs) == 1:
519
+ succ = succs[0]
520
+ succ_preds = list(full_graph.predecessors(succ))
521
+ succ_succs = list(full_graph.successors(succ))
522
+ if head is not succ and len(succ_succs) == 2 and node in succ_succs and len(succ_preds) == 1:
523
+ succ_succs.remove(node)
524
+ out_node = succ_succs[0]
525
+
526
+ if full_graph.has_edge(succ, node):
527
+ # possible candidate
528
+ _, _, succ_block = self._find_node_going_to_dst(succ, out_node, condjump_only=True)
529
+ if succ_block is not None:
530
+ edge_cond_succhead = self.cond_proc.recover_edge_condition(full_graph, succ_block, node)
531
+ edge_cond_succout = self.cond_proc.recover_edge_condition(full_graph, succ_block, out_node)
532
+ if claripy.is_true(claripy.Not(edge_cond_succhead) == edge_cond_succout): # type: ignore
533
+ # c = !c
534
+ self._remove_last_statement_if_jump(succ)
535
+ drop_succ = False
536
+
537
+ # absorb the entire succ block if possible
538
+ if (
539
+ self._improve_algorithm
540
+ and self._is_sequential_statement_block(succ)
541
+ and self._should_use_multistmtexprs(succ)
542
+ ):
543
+ stmts = self._build_multistatementexpr_statements(succ)
544
+ assert stmts is not None
545
+ if (
546
+ stmts
547
+ and sum(1 for stmt in stmts if not isinstance(stmt, Label))
548
+ <= self._multistmtexpr_stmt_threshold
549
+ ):
550
+ edge_cond_succhead = MultiStatementExpression(
551
+ None,
552
+ stmts,
553
+ self.cond_proc.convert_claripy_bool_ast(edge_cond_succhead),
554
+ ins_addr=succ.addr,
555
+ )
556
+ drop_succ = True
557
+
558
+ new_node = SequenceNode(node.addr, nodes=[node] if drop_succ else [node, succ])
559
+ loop_node = LoopNode("do-while", edge_cond_succhead, new_node, addr=node.addr)
560
+
561
+ # on the original graph
562
+ self.replace_nodes(graph, node, loop_node, old_node_1=succ, self_loop=False)
563
+ # on the graph with successors
564
+ self.replace_nodes(full_graph, node, loop_node, old_node_1=succ, self_loop=False)
565
+
566
+ return True, loop_node, out_node
567
+ elif ((node is head and len(preds) >= 1) or len(preds) >= 2) and len(succs) == 2 and node in succs:
568
+ # head forms a self-loop
569
+ succs.remove(node)
570
+ succ = succs[0]
571
+ if not full_graph.has_edge(succ, node):
572
+ # possible candidate
573
+ edge_cond_head = self.cond_proc.recover_edge_condition(full_graph, node, node)
574
+ edge_cond_head_succ = self.cond_proc.recover_edge_condition(full_graph, node, succ)
575
+ if claripy.is_true(claripy.Not(edge_cond_head) == edge_cond_head_succ): # type: ignore
576
+ # c = !c
577
+ self._remove_last_statement_if_jump(node)
578
+ seq_node = SequenceNode(node.addr, nodes=[node]) if not isinstance(node, SequenceNode) else node
579
+ loop_node = LoopNode("do-while", edge_cond_head, seq_node, addr=seq_node.addr)
580
+
581
+ # on the original graph
582
+ self.replace_nodes(graph, node, loop_node, self_loop=False)
583
+ # on the graph with successors
584
+ self.replace_nodes(full_graph, node, loop_node, self_loop=False)
585
+
586
+ return True, loop_node, succ
587
+ return False, None, None
588
+
589
+ def _match_cyclic_natural_loop(self, node, head, graph, full_graph) -> tuple[bool, LoopNode | None]:
590
+ if not (node is head or graph.in_degree[node] == 2):
591
+ return False, None
592
+
593
+ # check if there is a cycle that starts with node and ends with node
594
+ next_node = node
595
+ seq_node = SequenceNode(node.addr, nodes=[node])
596
+ seen_nodes = set()
597
+ while True:
598
+ succs = list(graph.successors(next_node))
599
+ if len(succs) != 1:
600
+ return False, None
601
+ if full_graph.out_degree[next_node] > 1:
602
+ # all successors in the full graph should have been refined away at this point
603
+ return False, None
604
+ next_node = succs[0]
605
+
606
+ if next_node is node:
607
+ break
608
+ if next_node is head:
609
+ # we don't want a loop with region head not as the first node of the body!
610
+ return False, None
611
+ if next_node is not node and next_node in seen_nodes:
612
+ return False, None
613
+
614
+ seen_nodes.add(next_node)
615
+ seq_node.nodes.append(next_node)
616
+
617
+ loop_node = LoopNode("while", claripy.true(), seq_node, addr=node.addr)
618
+
619
+ # on the original graph
620
+ for node_ in seq_node.nodes:
621
+ if node_ is not node:
622
+ graph.remove_node(node_)
623
+ self.replace_nodes(graph, node, loop_node, self_loop=False)
624
+
625
+ # on the graph with successors
626
+ for node_ in seq_node.nodes:
627
+ if node_ is not node:
628
+ full_graph.remove_node(node_)
629
+ self.replace_nodes(full_graph, node, loop_node, self_loop=False)
630
+
631
+ return True, loop_node
632
+
633
+ def _refine_cyclic(self) -> bool:
634
+ loop_heads = {t for _, t in dfs_back_edges(self._region.graph, self._region.head)}
635
+ sorted_loop_heads = GraphUtils.quasi_topological_sort_nodes(self._region.graph, nodes=list(loop_heads))
636
+
637
+ for head in sorted_loop_heads:
638
+ l.debug("... refining cyclic at %r", head)
639
+ refined = self._refine_cyclic_core(head)
640
+ l.debug("... refined: %s", refined)
641
+ if refined:
642
+ self._assert_graph_ok(self._region.graph, "Refinement went wrong")
643
+ # cyclic refinement may create dangling nodes in the full graph
644
+ return True
645
+ return False
646
+
647
+ def _refine_cyclic_core(self, loop_head) -> bool:
648
+ graph: networkx.DiGraph = self._region.graph
649
+ fullgraph: networkx.DiGraph = (
650
+ self._region.graph_with_successors
651
+ if self._region.graph_with_successors is not None
652
+ else networkx.DiGraph(self._region.graph)
653
+ )
654
+
655
+ # check if there is an out-going edge from the loop head
656
+ head_succs = list(fullgraph.successors(loop_head))
657
+ successor = None # the loop successor
658
+ loop_type = None
659
+ # continue_node either the loop header for while(true) loops or the loop header predecessor for do-while loops
660
+ continue_node = loop_head
661
+
662
+ is_while, result_while = self._refine_cyclic_is_while_loop(graph, fullgraph, loop_head, head_succs)
663
+ is_dowhile, result_dowhile = self._refine_cyclic_is_dowhile_loop(graph, fullgraph, loop_head, head_succs)
664
+
665
+ continue_edges: list[tuple[BaseNode, BaseNode]] = []
666
+ outgoing_edges: list = []
667
+
668
+ # gotta pick one!
669
+ # for now, we handle the most common case: both successors exist in the graph of the parent region, and
670
+ # one successor has a path to the other successor
671
+ if is_while and is_dowhile and self._parent_region is not None:
672
+ assert result_while is not None and result_dowhile is not None
673
+ succ_while = result_while[-1]
674
+ succ_dowhile = result_dowhile[-1]
675
+ if succ_while in self._parent_region.graph and succ_dowhile in self._parent_region.graph:
676
+ sorted_nodes = GraphUtils.quasi_topological_sort_nodes(
677
+ self._parent_region.graph, loop_heads=[self._parent_region.head]
678
+ )
679
+ succ_while_idx = sorted_nodes.index(succ_while)
680
+ succ_dowhile_idx = sorted_nodes.index(succ_dowhile)
681
+ if succ_dowhile_idx < succ_while_idx:
682
+ # pick do-while
683
+ is_while = False
684
+
685
+ if is_while:
686
+ assert result_while is not None
687
+ loop_type = "while"
688
+ continue_edges, outgoing_edges, continue_node, successor = result_while
689
+ elif is_dowhile:
690
+ assert result_dowhile is not None
691
+ loop_type = "do-while"
692
+ continue_edges, outgoing_edges, continue_node, successor = result_dowhile
693
+
694
+ if loop_type is None:
695
+ # natural loop. select *any* exit edge to determine the successor
696
+ # well actually, to maintain determinism, we select the successor with the highest address
697
+ successor_candidates = set()
698
+ for node in networkx.descendants(graph, loop_head):
699
+ for succ in fullgraph.successors(node):
700
+ if succ not in graph:
701
+ successor_candidates.add(succ)
702
+ if loop_head is succ:
703
+ continue_edges.append((node, succ))
704
+ if successor_candidates:
705
+ successor_candidates = sorted(successor_candidates, key=lambda x: x.addr)
706
+ successor = successor_candidates[0]
707
+ # virtualize all other edges
708
+ for succ in successor_candidates:
709
+ for pred in fullgraph.predecessors(succ):
710
+ if pred in graph:
711
+ outgoing_edges.append((pred, succ))
712
+
713
+ if outgoing_edges:
714
+ # if there is a single successor, we convert all out-going edges into breaks;
715
+ # if there are multiple successors, and if the current region does not have a parent region, then we
716
+ # convert all out-going edges into gotos;
717
+ # otherwise we give up.
718
+
719
+ if self._parent_region is not None and len({dst for _, dst in outgoing_edges}) > 1:
720
+ # give up because there is a parent region
721
+ return False
722
+
723
+ if successor is None:
724
+ successor_and_edgecounts = defaultdict(int)
725
+ for _, dst in outgoing_edges:
726
+ successor_and_edgecounts[dst] += 1
727
+
728
+ if len(successor_and_edgecounts) > 1:
729
+ # pick one successor with the highest edge count and (in case there are multiple successors with the
730
+ # same edge count) the lowest address
731
+ max_edgecount = max(successor_and_edgecounts.values())
732
+ successor_candidates = [
733
+ nn for nn, edgecount in successor_and_edgecounts.items() if edgecount == max_edgecount
734
+ ]
735
+ successor = next(iter(sorted(successor_candidates, key=lambda x: x.addr)))
736
+ else:
737
+ successor = next(iter(successor_and_edgecounts.keys()))
738
+
739
+ for src, dst in outgoing_edges:
740
+ if dst is successor:
741
+ # keep in mind that at this point, src might have been structured already. this means the last
742
+ # block in src may not be the actual block that has a direct jump or a conditional jump to dst. as
743
+ # a result, we should walk all blocks in src to find the jump to dst, then extract the condition
744
+ # and augment the corresponding block with a ConditionalBreak.
745
+ _, src_parent, src_block = self._find_node_going_to_dst(src, dst)
746
+ if src_block is None:
747
+ l.warning(
748
+ "Cannot find the source block jumping to the destination block at %#x. "
749
+ "This is likely a bug elsewhere and needs to be addressed.",
750
+ dst.addr,
751
+ )
752
+ # remove the edge anyway
753
+ fullgraph.remove_edge(src, dst)
754
+ elif not isinstance(src_block, (Block, MultiNode)):
755
+ # it has probably been structured into BreakNode or ConditionalBreakNode
756
+ # just remove the edge
757
+ fullgraph.remove_edge(src, dst)
758
+ else:
759
+ has_continue = False
760
+ # at the same time, examine if there is an edge that goes from src to the continue node. if so,
761
+ # we deal with it here as well.
762
+ continue_node_going_edge = src, continue_node
763
+ if continue_node_going_edge in continue_edges:
764
+ has_continue = True
765
+ # do not remove the edge from continue_edges since we want to process them later in this
766
+ # function.
767
+
768
+ # create the "break" node. in fact, we create a jump or a conditional jump, which will be
769
+ # rewritten to break nodes after (if possible). directly creating break nodes may lead to
770
+ # unwanted results, e.g., inserting a break (that's intended to break out of the loop) inside a
771
+ # switch-case that is nested within a loop.
772
+ last_src_stmt = self.cond_proc.get_last_statement(src_block)
773
+ assert last_src_stmt is not None
774
+ break_cond = self.cond_proc.recover_edge_condition(fullgraph, src_block, dst)
775
+ assert successor.addr is not None
776
+ if claripy.is_true(break_cond):
777
+ break_stmt = Jump(
778
+ None,
779
+ Const(None, None, successor.addr, self.project.arch.bits),
780
+ target_idx=successor.idx if isinstance(successor, Block) else None,
781
+ ins_addr=last_src_stmt.ins_addr,
782
+ )
783
+ break_node = Block(last_src_stmt.ins_addr, None, statements=[break_stmt])
784
+ else:
785
+ break_stmt = Jump(
786
+ None,
787
+ Const(None, None, successor.addr, self.project.arch.bits),
788
+ target_idx=successor.idx if isinstance(successor, Block) else None,
789
+ ins_addr=last_src_stmt.ins_addr,
790
+ )
791
+ break_node_inner = Block(last_src_stmt.ins_addr, None, statements=[break_stmt])
792
+ break_node = ConditionNode(
793
+ last_src_stmt.ins_addr,
794
+ None,
795
+ break_cond,
796
+ break_node_inner,
797
+ )
798
+ new_node = SequenceNode(src_block.addr, nodes=[src_block, break_node])
799
+ if has_continue:
800
+ if continue_node.addr is not None and self.is_a_jump_target(
801
+ last_src_stmt, continue_node.addr
802
+ ):
803
+ # instead of a conditional break node, we should insert a condition node instead
804
+ break_stmt = Jump(
805
+ None,
806
+ Const(None, None, successor.addr, self.project.arch.bits),
807
+ target_idx=successor.idx if isinstance(successor, Block) else None,
808
+ ins_addr=last_src_stmt.ins_addr,
809
+ )
810
+ break_node = Block(last_src_stmt.ins_addr, None, statements=[break_stmt])
811
+ cont_node = ContinueNode(
812
+ last_src_stmt.ins_addr,
813
+ Const(None, None, continue_node.addr, self.project.arch.bits),
814
+ )
815
+ cond_node = ConditionNode(
816
+ last_src_stmt.ins_addr,
817
+ None,
818
+ break_cond,
819
+ break_node,
820
+ )
821
+ new_node.nodes[-1] = cond_node
822
+ new_node.nodes.append(cont_node)
823
+
824
+ # we don't remove the edge (src, continue_node) from the graph or full graph. we will
825
+ # process them later in this function.
826
+ else:
827
+ # the last statement in src_block is not the conditional jump whose one branch goes to
828
+ # the loop head. it probably goes to another block that ends up going to the loop head.
829
+ # we don't handle it here.
830
+ pass
831
+
832
+ self._remove_last_statement_if_jump(src_block)
833
+ fullgraph.remove_edge(src, dst)
834
+ if src_parent is not None:
835
+ # replace the node in its parent node
836
+ self.replace_node_in_node(src_parent, src_block, new_node)
837
+ else:
838
+ # directly replace the node in graph
839
+ self.replace_nodes(graph, src, new_node)
840
+ self.replace_nodes(fullgraph, src, new_node)
841
+ if src is loop_head:
842
+ loop_head = new_node
843
+ if src is continue_node:
844
+ continue_node = new_node
845
+
846
+ self._replace_node_in_edge_list(outgoing_edges, src_block, new_node)
847
+ self._replace_node_in_edge_list(continue_edges, src_block, new_node)
848
+
849
+ # remove the last jump or conditional jump in src_block
850
+ self._remove_last_statement_if_jump(src_block)
851
+
852
+ else:
853
+ self.virtualized_edges.add((src, dst))
854
+ fullgraph.remove_edge(src, dst)
855
+ if fullgraph.in_degree[dst] == 0:
856
+ # drop this node
857
+ fullgraph.remove_node(dst)
858
+ if self._region.successors and dst in self._region.successors:
859
+ self._region.successors.remove(dst)
860
+
861
+ if len(continue_edges) > 1:
862
+ # convert all but one (the one that is the farthest from the head, topological-wise) head-going edges into
863
+ # continues
864
+ sorted_nodes = GraphUtils.quasi_topological_sort_nodes(
865
+ fullgraph, nodes=[src for src, _ in continue_edges], loop_heads=[loop_head]
866
+ )
867
+ src_to_ignore = sorted_nodes[-1]
868
+
869
+ for src, _ in continue_edges:
870
+ if src is src_to_ignore:
871
+ # this edge will be handled during loop structuring
872
+ continue
873
+
874
+ # due to prior structuring of sub regions, the continue node may already be a Jump statement deep in
875
+ # src at this point. we need to find the Jump statement and replace it.
876
+ _, _, cont_block = self._find_node_going_to_dst(src, continue_node)
877
+ if cont_block is None:
878
+ # cont_block is not found. but it's ok. one possibility is that src is a jump table head with one
879
+ # case being the loop head. in such cases, we can just remove the edge.
880
+ if src.addr not in self.jump_tables:
881
+ l.debug(
882
+ "_refine_cyclic_core: Cannot find the block going to loop head for edge %r -> %r. "
883
+ "Remove the edge anyway.",
884
+ src,
885
+ continue_node,
886
+ )
887
+ if graph.has_edge(src, continue_node):
888
+ graph.remove_edge(src, continue_node)
889
+ fullgraph.remove_edge(src, continue_node)
890
+ else:
891
+ # remove the edge.
892
+ graph.remove_edge(src, continue_node)
893
+ fullgraph.remove_edge(src, continue_node)
894
+ # replace it with the original node plus the continue node
895
+ try:
896
+ last_stmt = self.cond_proc.get_last_statement(cont_block)
897
+ except EmptyBlockNotice:
898
+ # meh
899
+ last_stmt = None
900
+ if last_stmt is not None:
901
+ new_cont_node = None
902
+ if isinstance(last_stmt, ConditionalJump):
903
+ new_cont_node = ContinueNode(last_stmt.ins_addr, continue_node.addr)
904
+ if (
905
+ isinstance(last_stmt.true_target, Const)
906
+ and last_stmt.true_target.value == continue_node.addr
907
+ ):
908
+ new_cont_node = ConditionNode(
909
+ last_stmt.ins_addr, None, last_stmt.condition, new_cont_node
910
+ )
911
+ else:
912
+ new_cont_node = ConditionNode(
913
+ last_stmt.ins_addr,
914
+ None,
915
+ UnaryOp(None, "Not", last_stmt.condition),
916
+ new_cont_node,
917
+ )
918
+ elif isinstance(last_stmt, Jump):
919
+ new_cont_node = ContinueNode(last_stmt.ins_addr, continue_node.addr)
920
+
921
+ if new_cont_node is not None:
922
+ self._remove_last_statement_if_jump(cont_block)
923
+ new_node = SequenceNode(src.addr, nodes=[src, new_cont_node])
924
+ self.replace_nodes(graph, src, new_node)
925
+ self.replace_nodes(fullgraph, src, new_node)
926
+
927
+ if loop_type == "do-while":
928
+ self.dowhile_known_tail_nodes.add(continue_node)
929
+
930
+ return bool(outgoing_edges or len(continue_edges) > 1)
931
+
932
+ def _refine_cyclic_is_while_loop(
933
+ self, graph, fullgraph, loop_head, head_succs
934
+ ) -> tuple[bool, tuple[list, list, BaseNode, BaseNode] | None]:
935
+ if len(head_succs) == 2 and any(head_succ not in graph for head_succ in head_succs):
936
+ # make sure the head_pred is not already structured
937
+ _, _, head_block_0 = self._find_node_going_to_dst(loop_head, head_succs[0])
938
+ _, _, head_block_1 = self._find_node_going_to_dst(loop_head, head_succs[1])
939
+ if head_block_0 is head_block_1 and head_block_0 is not None:
940
+ # there is an out-going edge from the loop head
941
+ # virtualize all other edges
942
+ continue_edges: list[tuple[BaseNode, BaseNode]] = []
943
+ outgoing_edges = []
944
+ successor = next(iter(head_succ for head_succ in head_succs if head_succ not in graph))
945
+ for node in networkx.descendants(graph, loop_head):
946
+ succs = list(fullgraph.successors(node))
947
+ if loop_head in succs:
948
+ continue_edges.append((node, loop_head))
949
+
950
+ outside_succs = [succ for succ in succs if succ not in graph]
951
+ for outside_succ in outside_succs:
952
+ outgoing_edges.append((node, outside_succ))
953
+
954
+ return True, (continue_edges, outgoing_edges, loop_head, successor)
955
+ return False, None
956
+
957
+ def _refine_cyclic_is_dowhile_loop( # pylint:disable=unused-argument
958
+ self, graph, fullgraph, loop_head, head_succs
959
+ ) -> tuple[bool, tuple[list, list, BaseNode, BaseNode] | None]:
960
+ # check if there is an out-going edge from the loop tail
961
+ head_preds = list(fullgraph.predecessors(loop_head))
962
+ if len(head_preds) == 1:
963
+ head_pred = head_preds[0]
964
+ head_pred_succs = list(fullgraph.successors(head_pred))
965
+ if len(head_pred_succs) == 2 and any(nn not in graph for nn in head_pred_succs):
966
+ # make sure the head_pred is not already structured
967
+ _, _, src_block_0 = self._find_node_going_to_dst(head_pred, head_pred_succs[0])
968
+ _, _, src_block_1 = self._find_node_going_to_dst(head_pred, head_pred_succs[1])
969
+ if src_block_0 is src_block_1 and src_block_0 is not None:
970
+ continue_edges: list[tuple[BaseNode, BaseNode]] = []
971
+ outgoing_edges = []
972
+ # there is an out-going edge from the loop tail
973
+ # virtualize all other edges
974
+ successor = next(iter(nn for nn in head_pred_succs if nn not in graph))
975
+ continue_node = head_pred
976
+ for node in networkx.descendants(graph, loop_head):
977
+ if node is head_pred:
978
+ continue
979
+ succs = list(fullgraph.successors(node))
980
+ if head_pred in succs:
981
+ continue_edges.append((node, head_pred))
982
+
983
+ outside_succs = [succ for succ in succs if succ not in graph]
984
+ for outside_succ in outside_succs:
985
+ outgoing_edges.append((node, outside_succ))
986
+
987
+ return True, (continue_edges, outgoing_edges, continue_node, successor)
988
+ return False, None
989
+
990
+ def _analyze_acyclic(self) -> bool:
991
+ # match against known schemas
992
+ l.debug("Matching acyclic schemas for region %r.", self._region)
993
+
994
+ any_matches = False
995
+ idx = 0
996
+ while True:
997
+ l.debug("_match_acyclic_schemas: Iteration %d", idx)
998
+ idx += 1
999
+
1000
+ try:
1001
+ any_matches_this_iteration = self._match_acyclic_schemas(
1002
+ self._region.graph,
1003
+ (
1004
+ self._region.graph_with_successors
1005
+ if self._region.graph_with_successors is not None
1006
+ else networkx.DiGraph(self._region.graph)
1007
+ ),
1008
+ self._region.head,
1009
+ )
1010
+ except GraphChangedNotification:
1011
+ # restart
1012
+ l.debug("_match_acyclic_schemas: Graph changed. Restart.")
1013
+ idx = 0
1014
+ continue
1015
+ if not any_matches_this_iteration:
1016
+ break
1017
+ any_matches = True
1018
+
1019
+ # update the head if needed
1020
+ if self._region.head not in self._region.graph:
1021
+ # update the head
1022
+ self._region.head = next(
1023
+ iter(node for node in self._region.graph.nodes if node.addr == self._region.head.addr)
1024
+ )
1025
+
1026
+ return any_matches
1027
+
1028
+ def _match_acyclic_schemas(self, graph: networkx.DiGraph, full_graph: networkx.DiGraph, head) -> bool:
1029
+ # traverse the graph in reverse topological order
1030
+ any_matches = False
1031
+
1032
+ self._assert_graph_ok(self._region.graph, "Got a wrong graph to work on")
1033
+
1034
+ if graph.in_degree[head] == 0:
1035
+ acyclic_graph = graph
1036
+ else:
1037
+ acyclic_graph = networkx.DiGraph(graph)
1038
+ acyclic_graph.remove_edges_from(graph.in_edges(head))
1039
+
1040
+ self._assert_graph_ok(acyclic_graph, "Removed wrong edges")
1041
+
1042
+ for node in list(reversed(GraphUtils.quasi_topological_sort_nodes(acyclic_graph))):
1043
+ if node not in graph:
1044
+ continue
1045
+ if graph.has_edge(node, head):
1046
+ # it's a back edge. skip
1047
+ continue
1048
+ l.debug("... matching acyclic switch-case constructs at %r", node)
1049
+ matched = self._match_acyclic_switch_cases(graph, full_graph, node)
1050
+ l.debug("... matched: %s", matched)
1051
+ any_matches |= matched
1052
+ if matched:
1053
+ break
1054
+ l.debug("... matching acyclic sequence at %r", node)
1055
+ matched = self._match_acyclic_sequence(graph, full_graph, node)
1056
+ l.debug("... matched: %s", matched)
1057
+ any_matches |= matched
1058
+ if matched:
1059
+ break
1060
+ l.debug("... matching acyclic ITE at %r", node)
1061
+ matched = self._match_acyclic_ite(graph, full_graph, node)
1062
+ l.debug("... matched: %s", matched)
1063
+ any_matches |= matched
1064
+ if matched:
1065
+ break
1066
+ if self._improve_algorithm:
1067
+ l.debug("... matching acyclic ITE with short-circuit conditions at %r", node)
1068
+ matched = self._match_acyclic_short_circuit_conditions(graph, full_graph, node)
1069
+ l.debug("... matched: %s", matched)
1070
+ any_matches |= matched
1071
+ if matched:
1072
+ break
1073
+
1074
+ self._assert_graph_ok(self._region.graph, "Removed incorrect edges")
1075
+
1076
+ return any_matches
1077
+
1078
+ # switch cases
1079
+
1080
+ def _match_acyclic_switch_cases(self, graph: networkx.DiGraph, full_graph: networkx.DiGraph, node) -> bool:
1081
+ if isinstance(node, SwitchCaseNode):
1082
+ return False
1083
+
1084
+ r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_default_node(node, graph, full_graph)
1085
+ if r:
1086
+ return r
1087
+
1088
+ if isinstance(node, IncompleteSwitchCaseNode):
1089
+ return False
1090
+
1091
+ r = self._match_acyclic_switch_cases_incomplete_switch_head(node, graph, full_graph)
1092
+ if r:
1093
+ return r
1094
+ r = self._match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(node, graph, full_graph)
1095
+ if r:
1096
+ return r
1097
+ r = self._match_acyclic_switch_cases_address_loaded_from_memory(node, graph, full_graph)
1098
+ if r:
1099
+ return r
1100
+ r = self._match_acyclic_switch_cases_address_computed(node, graph, full_graph)
1101
+ if r:
1102
+ return r
1103
+ return self._match_acyclic_incomplete_switch_cases(node, graph, full_graph)
1104
+
1105
+ def _match_acyclic_switch_cases_incomplete_switch_head(self, node, graph, full_graph) -> bool:
1106
+ try:
1107
+ last_stmts = self.cond_proc.get_last_statements(node)
1108
+ except EmptyBlockNotice:
1109
+ return False
1110
+
1111
+ if len(last_stmts) != 1:
1112
+ return False
1113
+ last_stmt = last_stmts[0]
1114
+ if not isinstance(last_stmt, IncompleteSwitchCaseHeadStatement):
1115
+ return False
1116
+
1117
+ # make a fake jumptable
1118
+ node_default_addr = None
1119
+ case_entries: dict[int, int | tuple[int, int | None]] = {}
1120
+ for _, case_value, case_target_addr, case_target_idx, _ in last_stmt.case_addrs:
1121
+ if isinstance(case_value, str):
1122
+ if case_value == "default":
1123
+ node_default_addr = case_target_addr
1124
+ continue
1125
+ raise ValueError(f"Unsupported 'case_value' {case_value}")
1126
+ case_entries[case_value] = (case_target_addr, case_target_idx)
1127
+
1128
+ cases, node_default, to_remove = self._switch_build_cases(
1129
+ case_entries,
1130
+ node,
1131
+ node,
1132
+ node_default_addr,
1133
+ graph,
1134
+ full_graph,
1135
+ )
1136
+ if node_default_addr is not None and node_default is None:
1137
+ # the default node is not found. it's likely the node has been structured and is part of another construct
1138
+ # (e.g., inside another switch-case). we need to create a default node that jumps to the other node
1139
+ jmp_to_default_node = Jump(
1140
+ None,
1141
+ Const(None, None, node_default_addr, self.project.arch.bits),
1142
+ None,
1143
+ ins_addr=SWITCH_MISSING_DEFAULT_NODE_ADDR,
1144
+ )
1145
+ node_default = Block(SWITCH_MISSING_DEFAULT_NODE_ADDR, 0, statements=[jmp_to_default_node])
1146
+ graph.add_edge(node, node_default)
1147
+ full_graph.add_edge(node, node_default)
1148
+ r = self._make_switch_cases_core(
1149
+ node,
1150
+ self.cond_proc.claripy_ast_from_ail_condition(last_stmt.switch_variable),
1151
+ cases,
1152
+ node_default_addr,
1153
+ node_default,
1154
+ last_stmt.ins_addr,
1155
+ to_remove,
1156
+ graph,
1157
+ full_graph,
1158
+ bail_on_nonhead_outedges=True,
1159
+ )
1160
+ if not r:
1161
+ return False
1162
+
1163
+ # special handling of duplicated default nodes
1164
+ if node_default is not None and self._region.graph.out_degree[node] > 1:
1165
+ other_out_nodes = list(self._region.graph.successors(node))
1166
+ for o in other_out_nodes:
1167
+ if o.addr == node_default.addr and o is not node_default:
1168
+ self._region.graph.remove_node(o)
1169
+ if self._region.graph_with_successors is not None:
1170
+ self._region.graph_with_successors.remove_node(o)
1171
+
1172
+ switch_end_addr = self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
1173
+ if switch_end_addr is not None:
1174
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1175
+ return True
1176
+
1177
+ def _match_acyclic_switch_cases_address_loaded_from_memory(self, node, graph, full_graph) -> bool:
1178
+ try:
1179
+ last_stmt = self.cond_proc.get_last_statement(node)
1180
+ except EmptyBlockNotice:
1181
+ return False
1182
+
1183
+ if last_stmt is None:
1184
+ return False
1185
+
1186
+ successor_addrs = extract_jump_targets(last_stmt)
1187
+ if len(successor_addrs) != 2:
1188
+ return False
1189
+
1190
+ for t in successor_addrs:
1191
+ if t in self.jump_tables:
1192
+ # this is a candidate!
1193
+ target = t
1194
+ break
1195
+ else:
1196
+ return False
1197
+
1198
+ jump_table = self.jump_tables[target]
1199
+ if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
1200
+ return False
1201
+
1202
+ # extract the comparison expression, lower-, and upper-bounds from the last statement
1203
+ cmp = switch_extract_cmp_bounds(last_stmt)
1204
+ if not cmp:
1205
+ return False
1206
+ cmp_expr, cmp_lb, cmp_ub = cmp # pylint:disable=unused-variable
1207
+
1208
+ node_a = next(iter(nn for nn in graph.nodes if nn.addr == target), None)
1209
+ if node_a is None:
1210
+ return False
1211
+
1212
+ # the default case
1213
+ node_b_addr = next(iter(t for t in successor_addrs if t != target), None)
1214
+ if node_b_addr is None:
1215
+ return False
1216
+
1217
+ # populate whitelist_edges
1218
+ assert jump_table.jumptable_entries is not None
1219
+ for case_node_addr in jump_table.jumptable_entries:
1220
+ self.whitelist_edges.add((node_a.addr, case_node_addr))
1221
+ self.whitelist_edges.add((node.addr, node_b_addr))
1222
+ self.whitelist_edges.add((node_a.addr, node_b_addr))
1223
+ self.switch_case_known_heads.add(node)
1224
+
1225
+ # sanity check: case nodes are successors to node_a. all case nodes must have at most common one successor
1226
+ node_pred = None
1227
+ if graph.in_degree[node] == 1:
1228
+ node_pred = next(iter(graph.predecessors(node)))
1229
+
1230
+ case_nodes = list(graph.successors(node_a))
1231
+
1232
+ # case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
1233
+ # optimization)
1234
+ # example: touch_touch_no_switch.o:main
1235
+ r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
1236
+
1237
+ # case 2: the common successor is not directly reachable from node_a. this is a more common case.
1238
+ if not r:
1239
+ r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
1240
+
1241
+ if not r:
1242
+ return False
1243
+
1244
+ node_default = self._switch_find_default_node(graph, node, node_b_addr)
1245
+ if node_default is not None:
1246
+ # ensure we have successfully structured node_default
1247
+ if full_graph.out_degree(node_default) > 1:
1248
+ return False
1249
+
1250
+ # un-structure IncompleteSwitchCaseNode
1251
+ if isinstance(node_a, SequenceNode) and node_a.nodes and isinstance(node_a.nodes[0], IncompleteSwitchCaseNode):
1252
+ _, new_seq_node = self._unpack_sequencenode_head(graph, node_a)
1253
+ self._unpack_sequencenode_head(full_graph, node_a, new_seq=new_seq_node)
1254
+ # update node_a
1255
+ node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
1256
+ if isinstance(node_a, IncompleteSwitchCaseNode):
1257
+ r = self._unpack_incompleteswitchcasenode(graph, node_a)
1258
+ if not r:
1259
+ return False
1260
+ self._unpack_incompleteswitchcasenode(full_graph, node_a) # this shall not fail
1261
+ # update node_a
1262
+ node_a = next(iter(nn for nn in graph.nodes if nn.addr == target))
1263
+
1264
+ better_node_a = node_a
1265
+ if isinstance(node_a, SequenceNode) and is_empty_or_label_only_node(node_a.nodes[0]) and len(node_a.nodes) == 2:
1266
+ better_node_a = node_a.nodes[1]
1267
+
1268
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node_a, graph, cmp_lb, jump_table)
1269
+
1270
+ cases, node_default, to_remove = self._switch_build_cases(
1271
+ case_and_entry_addrs,
1272
+ node,
1273
+ node_a,
1274
+ node_b_addr,
1275
+ graph,
1276
+ full_graph,
1277
+ )
1278
+
1279
+ if isinstance(better_node_a, SwitchCaseNode) and better_node_a.default_node is None:
1280
+ # we found a different head for an otherwise complete edge case.
1281
+ # recreate the switch with it.
1282
+ newsc = SwitchCaseNode(better_node_a.switch_expr, better_node_a.cases, node_default, addr=node.addr)
1283
+
1284
+ if node_default is not None and set(graph.succ[node_a]) != set(graph.succ[node_default]):
1285
+ # if node_a and default_node have different successors we need to bail
1286
+ return False
1287
+
1288
+ for pgraph in (graph, full_graph):
1289
+ all_preds = set(pgraph.pred[node])
1290
+ all_succs = set(pgraph.succ[node_a])
1291
+ if node_default is not None:
1292
+ pgraph.remove_node(node_default)
1293
+ pgraph.remove_node(node)
1294
+ pgraph.remove_node(node_a)
1295
+ pgraph.add_node(newsc)
1296
+ for pred in all_preds:
1297
+ pgraph.add_edge(pred, newsc)
1298
+ for succ in all_succs:
1299
+ pgraph.add_edge(newsc, succ)
1300
+
1301
+ return True
1302
+
1303
+ if node_default is None:
1304
+ switch_end_addr = node_b_addr
1305
+ else:
1306
+ # we don't know what the end address of this switch-case structure is. let's figure it out
1307
+ switch_end_addr = self._switch_find_switch_end_addr(
1308
+ cases, node_default, {nn.addr for nn in self._region.graph}
1309
+ )
1310
+ to_remove.add(node_default)
1311
+
1312
+ to_remove.add(node_a) # add node_a
1313
+ r = self._make_switch_cases_core(
1314
+ node,
1315
+ cmp_expr,
1316
+ cases,
1317
+ node_b_addr,
1318
+ node_default,
1319
+ last_stmt.ins_addr,
1320
+ to_remove,
1321
+ graph,
1322
+ full_graph,
1323
+ node_a=node_a,
1324
+ )
1325
+ if not r:
1326
+ return False
1327
+
1328
+ # fully structured into a switch-case. remove node from switch_case_known_heads
1329
+ self.switch_case_known_heads.remove(node)
1330
+ if switch_end_addr is not None:
1331
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1332
+
1333
+ return True
1334
+
1335
+ def _match_acyclic_switch_cases_address_loaded_from_memory_no_default_node(self, node, graph, full_graph) -> bool:
1336
+ # sanity checks
1337
+ if not isinstance(node, IncompleteSwitchCaseNode):
1338
+ return False
1339
+ if node.addr not in self.jump_tables:
1340
+ return False
1341
+ # ensure _match_acyclic_switch_cases_address_load_from_memory cannot structure its predecessor (and this node)
1342
+ preds = list(graph.predecessors(node))
1343
+ if len(preds) != 1:
1344
+ return False
1345
+ pred = preds[0]
1346
+ if full_graph.out_degree[pred] != 1:
1347
+ return False
1348
+ jump_table: IndirectJump = self.jump_tables[node.addr]
1349
+ if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
1350
+ return False
1351
+
1352
+ # extract the comparison expression, lower-, and upper-bounds from the last statement
1353
+ last_stmt = self.cond_proc.get_last_statement(node.head)
1354
+ if not isinstance(last_stmt, Jump):
1355
+ return False
1356
+ cmp_expr = switch_extract_switch_expr_from_jump_target(last_stmt.target)
1357
+ if cmp_expr is None:
1358
+ return False
1359
+ cmp_lb = 0
1360
+
1361
+ # populate whitelist_edges
1362
+ assert jump_table.jumptable_entries is not None
1363
+
1364
+ # sanity check: case nodes are successors to node_a. all case nodes must have at most common one successor
1365
+ node_pred = None
1366
+ if graph.in_degree[node] == 1:
1367
+ node_pred = next(iter(graph.predecessors(node)))
1368
+
1369
+ case_nodes = list(graph.successors(node))
1370
+
1371
+ # case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
1372
+ # optimization)
1373
+ # example: touch_touch_no_switch.o:main
1374
+ r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
1375
+
1376
+ # case 2: the common successor is not directly reachable from node_a. this is a more common case.
1377
+ if not r:
1378
+ r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
1379
+
1380
+ if not r:
1381
+ return False
1382
+
1383
+ # un-structure IncompleteSwitchCaseNode
1384
+ if isinstance(node, IncompleteSwitchCaseNode):
1385
+ r = self._unpack_incompleteswitchcasenode(graph, node)
1386
+ if not r:
1387
+ return False
1388
+ self._unpack_incompleteswitchcasenode(full_graph, node) # this shall not fail
1389
+ # update node
1390
+ node = next(iter(nn for nn in graph.nodes if nn.addr == jump_table.addr))
1391
+
1392
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
1393
+
1394
+ cases, _, to_remove = self._switch_build_cases(
1395
+ case_and_entry_addrs,
1396
+ node,
1397
+ node,
1398
+ None,
1399
+ graph,
1400
+ full_graph,
1401
+ )
1402
+
1403
+ # we don't know what the end address of this switch-case structure is. let's figure it out
1404
+ switch_end_addr = self._switch_find_switch_end_addr(cases, None, {nn.addr for nn in self._region.graph})
1405
+ r = self._make_switch_cases_core(
1406
+ node,
1407
+ cmp_expr,
1408
+ cases,
1409
+ None,
1410
+ None,
1411
+ last_stmt.ins_addr,
1412
+ to_remove,
1413
+ graph,
1414
+ full_graph,
1415
+ node_a=None,
1416
+ )
1417
+ if not r:
1418
+ return False
1419
+
1420
+ # fully structured into a switch-case. remove node from switch_case_known_heads
1421
+ if switch_end_addr is not None:
1422
+ self._switch_handle_gotos(cases, None, switch_end_addr)
1423
+
1424
+ return True
1425
+
1426
+ def _match_acyclic_switch_cases_address_loaded_from_memory_no_ob_check(self, node, graph, full_graph) -> bool:
1427
+ if node.addr not in self.jump_tables:
1428
+ return False
1429
+
1430
+ try:
1431
+ last_stmt = self.cond_proc.get_last_statement(node)
1432
+ except EmptyBlockNotice:
1433
+ return False
1434
+ if not (isinstance(last_stmt, Jump) and not isinstance(last_stmt.target, Const)):
1435
+ return False
1436
+
1437
+ jump_table = self.jump_tables[node.addr]
1438
+ if jump_table.type != IndirectJumpType.Jumptable_AddressLoadedFromMemory:
1439
+ return False
1440
+
1441
+ # extract the index expression, lower-, and upper-bounds from the last statement
1442
+ index = switch_extract_bitwiseand_jumptable_info(last_stmt)
1443
+ if not index:
1444
+ return False
1445
+ index_expr, cmp_lb, cmp_ub = index # pylint:disable=unused-variable
1446
+ case_count = cmp_ub - cmp_lb + 1
1447
+
1448
+ # ensure we have the same number of cases
1449
+ assert jump_table.jumptable_entries is not None
1450
+ if case_count != len(jump_table.jumptable_entries):
1451
+ return False
1452
+
1453
+ # populate whitelist_edges
1454
+ for case_node_addr in jump_table.jumptable_entries:
1455
+ self.whitelist_edges.add((node.addr, case_node_addr))
1456
+ self.switch_case_known_heads.add(node)
1457
+
1458
+ # sanity check: case nodes are successors to node. all case nodes must have at most common one successor
1459
+ node_pred = None
1460
+ if graph.in_degree[node] == 1:
1461
+ node_pred = next(iter(graph.predecessors(node)))
1462
+
1463
+ case_nodes = list(graph.successors(node))
1464
+
1465
+ # case 1: the common successor happens to be directly reachable from node_a (usually as a result of compiler
1466
+ # optimization)
1467
+ # example: touch_touch_no_switch.o:main
1468
+ r = self.switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred)
1469
+
1470
+ # case 2: the common successor is not directly reachable from node_a. this is a more common case.
1471
+ if not r:
1472
+ r |= self.switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred)
1473
+
1474
+ if not r:
1475
+ return False
1476
+
1477
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
1478
+
1479
+ cases, node_default, to_remove = self._switch_build_cases(
1480
+ case_and_entry_addrs,
1481
+ node,
1482
+ node,
1483
+ None,
1484
+ graph,
1485
+ full_graph,
1486
+ )
1487
+
1488
+ assert node_default is None
1489
+ switch_end_addr = self._switch_find_switch_end_addr(cases, node_default, {nn.addr for nn in self._region.graph})
1490
+
1491
+ r = self._make_switch_cases_core(
1492
+ node,
1493
+ index_expr,
1494
+ cases,
1495
+ None,
1496
+ None,
1497
+ last_stmt.ins_addr,
1498
+ to_remove,
1499
+ graph,
1500
+ full_graph,
1501
+ node_a=None,
1502
+ )
1503
+ if not r:
1504
+ return False
1505
+
1506
+ # fully structured into a switch-case. remove node from switch_case_known_heads
1507
+ self.switch_case_known_heads.remove(node)
1508
+ if switch_end_addr is not None:
1509
+ self._switch_handle_gotos(cases, node_default, switch_end_addr)
1510
+
1511
+ return True
1512
+
1513
+ def _match_acyclic_switch_cases_address_computed(self, node, graph, full_graph) -> bool:
1514
+ if node.addr not in self.jump_tables:
1515
+ return False
1516
+ jump_table = self.jump_tables[node.addr]
1517
+ if jump_table.type != IndirectJumpType.Jumptable_AddressComputed:
1518
+ return False
1519
+
1520
+ try:
1521
+ last_stmts = self.cond_proc.get_last_statements(node)
1522
+ except EmptyBlockNotice:
1523
+ return False
1524
+ if len(last_stmts) != 1:
1525
+ return False
1526
+ last_stmt = last_stmts[0]
1527
+
1528
+ if not isinstance(last_stmt, ConditionalJump):
1529
+ return False
1530
+
1531
+ # Typical look:
1532
+ # t2 = (r5<4> - 0x22<32>)
1533
+ # if ((t2 <= 0x1c<32>)) { Goto (0x41d10c<32> + (t2 << 0x2<8>)) } else { Goto 0x41d108<32> }
1534
+ #
1535
+ # extract the comparison expression, lower-, and upper-bounds from the last statement
1536
+ cmp = switch_extract_cmp_bounds(last_stmt)
1537
+ if not cmp:
1538
+ return False
1539
+ cmp_expr, cmp_lb, cmp_ub = cmp # pylint:disable=unused-variable
1540
+
1541
+ if isinstance(last_stmt.false_target, Const):
1542
+ default_addr = last_stmt.false_target.value
1543
+ assert isinstance(default_addr, int)
1544
+ else:
1545
+ return False
1546
+
1547
+ node_default = self._switch_find_default_node(graph, node, default_addr)
1548
+ if node_default is not None:
1549
+ # ensure we have successfully structured node_default
1550
+ if full_graph.out_degree(node_default) > 1:
1551
+ return False
1552
+
1553
+ case_and_entry_addrs = self._find_case_and_entry_addrs(node, graph, cmp_lb, jump_table)
1554
+
1555
+ cases, node_default, to_remove = self._switch_build_cases(
1556
+ case_and_entry_addrs,
1557
+ node,
1558
+ node,
1559
+ default_addr,
1560
+ graph,
1561
+ full_graph,
1562
+ )
1563
+ if node_default is None:
1564
+ # there must be a default case
1565
+ return False
1566
+
1567
+ return self._make_switch_cases_core(
1568
+ node, cmp_expr, cases, default_addr, node_default, node.addr, to_remove, graph, full_graph
1569
+ )
1570
+
1571
+ def _match_acyclic_incomplete_switch_cases(
1572
+ self, node, graph: networkx.DiGraph, full_graph: networkx.DiGraph
1573
+ ) -> bool:
1574
+ # sanity checks
1575
+ if node.addr not in self.jump_tables:
1576
+ return False
1577
+ if isinstance(node, IncompleteSwitchCaseNode):
1578
+ return False
1579
+ if is_empty_or_label_only_node(node):
1580
+ return False
1581
+
1582
+ successors = list(graph.successors(node))
1583
+
1584
+ jump_table = self.jump_tables[node.addr]
1585
+ assert jump_table.jumptable_entries is not None
1586
+ if (
1587
+ successors
1588
+ and {succ.addr for succ in successors} == set(jump_table.jumptable_entries)
1589
+ and all(graph.in_degree[succ] == 1 for succ in successors)
1590
+ ):
1591
+ out_nodes = set()
1592
+ for succ in successors:
1593
+ out_nodes |= {
1594
+ succ for succ in full_graph.successors(succ) if succ is not node and succ not in successors
1595
+ }
1596
+ out_nodes = list(out_nodes)
1597
+ if len(out_nodes) <= 1:
1598
+ new_node = IncompleteSwitchCaseNode(node.addr, node, successors)
1599
+ graph.remove_nodes_from(successors)
1600
+ self.replace_nodes(graph, node, new_node)
1601
+ if out_nodes and out_nodes[0] in graph:
1602
+ graph.add_edge(new_node, out_nodes[0])
1603
+ full_graph.remove_nodes_from(successors)
1604
+ self.replace_nodes(full_graph, node, new_node)
1605
+ if out_nodes:
1606
+ full_graph.add_edge(new_node, out_nodes[0])
1607
+ return True
1608
+ return False
1609
+
1610
+ def _switch_build_cases(
1611
+ self,
1612
+ case_and_entryaddrs: dict[int, int | tuple[int, int | None]],
1613
+ head_node,
1614
+ node_a: BaseNode,
1615
+ node_b_addr: int | None,
1616
+ graph,
1617
+ full_graph,
1618
+ ) -> tuple[OrderedDict, Any, set[Any]]:
1619
+ cases: OrderedDict[int | tuple[int, ...], SequenceNode] = OrderedDict()
1620
+ to_remove = set()
1621
+
1622
+ default_node_candidates = (
1623
+ [nn for nn in graph.nodes if nn.addr == node_b_addr] if node_b_addr is not None else []
1624
+ )
1625
+ node_default = (
1626
+ self._switch_find_default_node(graph, head_node, node_b_addr) if node_b_addr is not None else None
1627
+ )
1628
+ if node_default is not None and not isinstance(node_default, SequenceNode):
1629
+ # make the default node a SequenceNode so that we can insert Break and Continue nodes into it later
1630
+ new_node = SequenceNode(node_default.addr, nodes=[node_default])
1631
+ self.replace_nodes(graph, node_default, new_node)
1632
+ self.replace_nodes(full_graph, node_default, new_node)
1633
+ node_default = new_node
1634
+
1635
+ converted_nodes: dict[tuple[int, int | None], Any] = {}
1636
+ entry_addr_to_ids: defaultdict[tuple[int, int | None], set[int]] = defaultdict(set)
1637
+
1638
+ # the default node might get duplicated (e.g., by EagerReturns). we detect if a duplicate of the default node
1639
+ # (node b) is a successor node of node a. we only skip those entries going to the default node if no duplicate
1640
+ # of default node exists in node a's successors.
1641
+ node_a_successors = list(graph.successors(node_a))
1642
+ if len(default_node_candidates) > 1:
1643
+ node_b_in_node_a_successors = any(nn for nn in node_a_successors if nn in default_node_candidates)
1644
+ else:
1645
+ # the default node is not duplicated
1646
+ node_b_in_node_a_successors = False
1647
+
1648
+ for case_idx, entry_addr in case_and_entryaddrs.items():
1649
+ if isinstance(entry_addr, tuple):
1650
+ entry_addr, entry_idx = entry_addr
1651
+ else:
1652
+ entry_idx = None
1653
+
1654
+ if not node_b_in_node_a_successors and entry_addr == node_b_addr:
1655
+ # jump to default or end of the switch-case structure - ignore this case
1656
+ continue
1657
+
1658
+ entry_addr_to_ids[(entry_addr, entry_idx)].add(case_idx)
1659
+ if (entry_addr, entry_idx) in converted_nodes:
1660
+ continue
1661
+
1662
+ if entry_addr == self._region.head.addr:
1663
+ # do not make the region head part of the switch-case construct (because it will lead to the removal
1664
+ # of the region head node). replace this entry with a goto statement later.
1665
+ entry_node = None
1666
+ else:
1667
+ entry_node = next(
1668
+ iter(
1669
+ nn
1670
+ for nn in node_a_successors
1671
+ if nn.addr == entry_addr and (not isinstance(nn, (Block, MultiNode)) or nn.idx == entry_idx)
1672
+ ),
1673
+ None,
1674
+ )
1675
+ if entry_node is None:
1676
+ # Missing entries. They are probably *after* the entire switch-case construct. Replace it with an empty
1677
+ # Goto node.
1678
+ case_inner_node = Block(
1679
+ 0,
1680
+ 0,
1681
+ statements=[
1682
+ Jump(
1683
+ None,
1684
+ Const(None, None, entry_addr, self.project.arch.bits),
1685
+ target_idx=entry_idx,
1686
+ ins_addr=0,
1687
+ stmt_idx=0,
1688
+ )
1689
+ ],
1690
+ )
1691
+ case_node = SequenceNode(0, nodes=[case_inner_node])
1692
+ converted_nodes[(entry_addr, entry_idx)] = case_node
1693
+ continue
1694
+
1695
+ if isinstance(entry_node, SequenceNode):
1696
+ case_node = entry_node
1697
+ else:
1698
+ case_node = SequenceNode(entry_node.addr, nodes=[entry_node])
1699
+ to_remove.add(entry_node)
1700
+
1701
+ converted_nodes[(entry_addr, entry_idx)] = case_node
1702
+
1703
+ for entry_addr_and_idx, converted_node in converted_nodes.items():
1704
+ assert entry_addr_and_idx in entry_addr_to_ids
1705
+ case_ids = entry_addr_to_ids[entry_addr_and_idx]
1706
+ if len(case_ids) == 1:
1707
+ cases[next(iter(case_ids))] = converted_node
1708
+ else:
1709
+ cases[tuple(sorted(case_ids))] = converted_node
1710
+
1711
+ # reorganize cases to handle fallthroughs
1712
+ cases = self._reorganize_switch_cases(cases)
1713
+
1714
+ return cases, node_default, to_remove
1715
+
1716
+ def _make_switch_cases_core(
1717
+ self,
1718
+ head,
1719
+ cmp_expr,
1720
+ cases: OrderedDict,
1721
+ node_default_addr: int | None,
1722
+ node_default,
1723
+ addr,
1724
+ to_remove: set,
1725
+ graph: networkx.DiGraph,
1726
+ full_graph: networkx.DiGraph,
1727
+ node_a=None,
1728
+ bail_on_nonhead_outedges: bool = False,
1729
+ ) -> bool:
1730
+ scnode = SwitchCaseNode(cmp_expr, cases, node_default, addr=addr)
1731
+
1732
+ # insert the switch-case node to the graph
1733
+ other_nodes_inedges = []
1734
+ out_edges = []
1735
+
1736
+ # remove all those entry nodes
1737
+ if node_default is not None:
1738
+ to_remove.add(node_default)
1739
+
1740
+ for nn in to_remove:
1741
+ if nn is head:
1742
+ continue
1743
+ for src in graph.predecessors(nn):
1744
+ if src not in to_remove:
1745
+ other_nodes_inedges.append((src, nn))
1746
+ for dst in full_graph.successors(nn):
1747
+ if dst not in to_remove:
1748
+ out_edges.append((nn, dst))
1749
+
1750
+ if bail_on_nonhead_outedges:
1751
+ nonhead_out_nodes = {edge[1] for edge in out_edges if edge[1] is not head}
1752
+ if len(nonhead_out_nodes) > 1:
1753
+ # not ready to be structured yet - do it later
1754
+ return False
1755
+
1756
+ # check if structuring will create any dangling nodes
1757
+ for case_node in to_remove:
1758
+ if case_node is not node_default and case_node is not node_a and case_node is not head:
1759
+ for succ in graph.successors(case_node):
1760
+ if (
1761
+ succ is not case_node
1762
+ and succ is not head
1763
+ and succ is not self._region.head
1764
+ and graph.in_degree[succ] == 1
1765
+ ):
1766
+ # succ will be dangling - not ready to be structured yet - do it later
1767
+ return False
1768
+
1769
+ if node_default is not None:
1770
+ # the head no longer goes to the default case
1771
+ if graph.has_edge(head, node_default):
1772
+ pass
1773
+ graph.remove_edge(head, node_default)
1774
+ full_graph.remove_edge(head, node_default)
1775
+ elif node_default_addr is not None:
1776
+ # the default node is not in the current graph, but it might be in the full graph
1777
+ node_default_in_full_graph = next(iter(nn for nn in full_graph if nn.addr == node_default_addr), None)
1778
+ if node_default_in_full_graph is not None and full_graph.has_edge(head, node_default_in_full_graph):
1779
+ # the head no longer jumps to the default node - the switch jumps to it
1780
+ full_graph.remove_edge(head, node_default_in_full_graph)
1781
+
1782
+ for nn in to_remove:
1783
+ graph.remove_node(nn)
1784
+ full_graph.remove_node(nn)
1785
+
1786
+ graph.add_edge(head, scnode)
1787
+ full_graph.add_edge(head, scnode)
1788
+
1789
+ if out_edges:
1790
+ # for all out edges going to head, we ensure there is a goto at the end of each corresponding case node
1791
+ for out_src, out_dst in out_edges:
1792
+ if out_dst is head:
1793
+ all_case_nodes = list(cases.values())
1794
+ if node_default is not None:
1795
+ all_case_nodes.append(node_default)
1796
+ case_node: SequenceNode = next(nn for nn in all_case_nodes if nn.addr == out_src.addr)
1797
+ try:
1798
+ case_node_last_stmt = self.cond_proc.get_last_statement(case_node)
1799
+ except EmptyBlockNotice:
1800
+ case_node_last_stmt = None
1801
+ if not isinstance(case_node_last_stmt, Jump):
1802
+ jump_stmt = Jump(
1803
+ None, Const(None, None, head.addr, self.project.arch.bits), None, ins_addr=out_src.addr
1804
+ )
1805
+ jump_node = Block(out_src.addr, 0, statements=[jump_stmt])
1806
+ case_node.nodes.append(jump_node)
1807
+
1808
+ out_edges = [edge for edge in out_edges if edge[1] is not head]
1809
+ if out_edges:
1810
+ # leave only one out edge and virtualize all other out edges
1811
+ out_edge = out_edges[0]
1812
+ out_dst = out_edge[1]
1813
+ if out_dst in graph:
1814
+ graph.add_edge(scnode, out_dst)
1815
+ full_graph.add_edge(scnode, out_dst)
1816
+ if full_graph.has_edge(head, out_dst):
1817
+ full_graph.remove_edge(head, out_dst)
1818
+
1819
+ # fix full_graph if needed: remove successors that are no longer needed
1820
+ for _out_src, out_dst in out_edges[1:]:
1821
+ if out_dst in full_graph and out_dst not in graph and full_graph.in_degree[out_dst] == 0:
1822
+ full_graph.remove_node(out_dst)
1823
+ assert self._region.successors is not None
1824
+ if out_dst in self._region.successors:
1825
+ self._region.successors.remove(out_dst)
1826
+
1827
+ # remove the last statement (conditional jump) in the head node
1828
+ remove_last_statement(head)
1829
+
1830
+ if node_a is not None:
1831
+ # remove the last statement in node_a
1832
+ remove_last_statements(node_a)
1833
+
1834
+ return True
1835
+
1836
+ @staticmethod
1837
+ def _find_case_and_entry_addrs(
1838
+ jump_head, graph, cmp_lb: int, jump_table
1839
+ ) -> dict[int, int | tuple[int, int | None]]:
1840
+ case_and_entry_addrs = {}
1841
+
1842
+ addr_to_entry_nodes = defaultdict(list)
1843
+ for succ in graph.successors(jump_head):
1844
+ addr_to_entry_nodes[succ.addr].append(succ)
1845
+
1846
+ for i, entry_addr in enumerate(jump_table.jumptable_entries):
1847
+ case_no = cmp_lb + i
1848
+ if entry_addr in addr_to_entry_nodes and isinstance(addr_to_entry_nodes[entry_addr][0], (MultiNode, Block)):
1849
+ case_and_entry_addrs[case_no] = entry_addr, addr_to_entry_nodes[entry_addr][0].idx
1850
+ else:
1851
+ case_and_entry_addrs[case_no] = entry_addr
1852
+
1853
+ return case_and_entry_addrs
1854
+
1855
+ def _is_node_unstructured_switch_case_head(self, node) -> bool:
1856
+ if node.addr in self.jump_tables:
1857
+ # maybe it has been structured?
1858
+ try:
1859
+ last_stmts = self.cond_proc.get_last_statements(node)
1860
+ except EmptyBlockNotice:
1861
+ return False
1862
+ return len(last_stmts) == 1 and isinstance(last_stmts[0], Jump)
1863
+ return False
1864
+
1865
+ def _is_switch_cases_address_loaded_from_memory_head_or_jumpnode(self, graph, node) -> bool:
1866
+ if self._is_node_unstructured_switch_case_head(node):
1867
+ return True
1868
+ for succ in graph.successors(node):
1869
+ if self._is_node_unstructured_switch_case_head(succ):
1870
+ return True
1871
+ return node in self.switch_case_known_heads
1872
+
1873
+ # other acyclic schemas
1874
+
1875
+ def _match_acyclic_sequence(self, graph, full_graph, start_node) -> bool:
1876
+ """
1877
+ Check if there is a sequence of regions, where each region has a single predecessor and a single successor.
1878
+ """
1879
+ succs = list(graph.successors(start_node))
1880
+ if len(succs) == 1:
1881
+ end_node = succs[0]
1882
+ if (
1883
+ full_graph.out_degree[start_node] == 1
1884
+ and full_graph.in_degree[end_node] == 1
1885
+ and not full_graph.has_edge(end_node, start_node)
1886
+ and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, end_node)
1887
+ and not self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, start_node)
1888
+ and end_node not in self.dowhile_known_tail_nodes
1889
+ and not isinstance(end_node, IncompleteSwitchCaseNode)
1890
+ ):
1891
+ # merge two blocks
1892
+ new_seq = self._merge_nodes(start_node, end_node)
1893
+
1894
+ # on the original graph
1895
+ self.replace_nodes(graph, start_node, new_seq, old_node_1=end_node if end_node in graph else None)
1896
+ # on the graph with successors
1897
+ self.replace_nodes(full_graph, start_node, new_seq, old_node_1=end_node)
1898
+ return True
1899
+ return False
1900
+
1901
+ def _match_acyclic_ite(self, graph, full_graph, start_node) -> bool:
1902
+ """
1903
+ Check if start_node is the beginning of an If-Then-Else region. Create a Condition node if it is the case.
1904
+ """
1905
+
1906
+ succs = list(full_graph.successors(start_node))
1907
+ if len(succs) == 2:
1908
+ left, right = succs
1909
+ if left.addr > right.addr:
1910
+ left, right = right, left
1911
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
1912
+ full_graph, left
1913
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, right):
1914
+ # structure the switch-case first before we wrap them into an ITE. give up
1915
+ return False
1916
+
1917
+ left_succs = list(full_graph.successors(left))
1918
+ right_succs = list(full_graph.successors(right))
1919
+
1920
+ if (
1921
+ left in graph
1922
+ and right in graph
1923
+ and (
1924
+ (not left_succs and not right_succs)
1925
+ or (not left_succs and len(right_succs) == 1)
1926
+ or (not right_succs and len(left_succs) == 1)
1927
+ or (len(left_succs) == 1 and left_succs == right_succs)
1928
+ )
1929
+ ):
1930
+ # potentially ITE
1931
+ if (
1932
+ full_graph.in_degree[left] == 1
1933
+ and full_graph.in_degree[right] == 1
1934
+ and not self._is_node_unstructured_switch_case_head(left)
1935
+ and not self._is_node_unstructured_switch_case_head(right)
1936
+ ):
1937
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1938
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1939
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
1940
+ # c = !c
1941
+ last_if_jump = self._remove_last_statement_if_jump(start_node)
1942
+ new_cond_node = ConditionNode(
1943
+ last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
1944
+ None,
1945
+ edge_cond_left,
1946
+ left,
1947
+ false_node=right,
1948
+ )
1949
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
1950
+
1951
+ if not left_succs:
1952
+ # on the original graph
1953
+ if left in graph:
1954
+ graph.remove_node(left)
1955
+ self.replace_nodes(graph, start_node, new_node, old_node_1=right)
1956
+ # on the graph with successors
1957
+ full_graph.remove_node(left)
1958
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=right)
1959
+ else:
1960
+ # on the original graph
1961
+ if right in graph:
1962
+ graph.remove_node(right)
1963
+ self.replace_nodes(graph, start_node, new_node, old_node_1=left)
1964
+ # on the graph with successors
1965
+ full_graph.remove_node(right)
1966
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=left)
1967
+
1968
+ return True
1969
+
1970
+ if right in graph and not right_succs and full_graph.in_degree[right] == 1 and left in graph:
1971
+ # swap them
1972
+ left, right = right, left
1973
+ left_succs, right_succs = right_succs, left_succs
1974
+ if left in graph and not left_succs and full_graph.in_degree[left] == 1 and right in graph:
1975
+ # potentially If-Then
1976
+ if not self._is_node_unstructured_switch_case_head(
1977
+ left
1978
+ ) and not self._is_node_unstructured_switch_case_head(right):
1979
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
1980
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
1981
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
1982
+ # c = !c
1983
+ last_if_jump = self._remove_last_statement_if_jump(start_node)
1984
+ new_cond_node = ConditionNode(
1985
+ last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
1986
+ None,
1987
+ edge_cond_left,
1988
+ left,
1989
+ false_node=None,
1990
+ )
1991
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
1992
+
1993
+ # on the original graph
1994
+ self.replace_nodes(graph, start_node, new_node, old_node_1=left)
1995
+ # on the graph with successors
1996
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=left)
1997
+
1998
+ return True
1999
+
2000
+ if len(right_succs) == 1 and right_succs[0] == left:
2001
+ # swap them
2002
+ left, right = right, left
2003
+ left_succs, right_succs = right_succs, left_succs
2004
+ # potentially If-Then
2005
+ if (
2006
+ left in graph
2007
+ and len(left_succs) == 1
2008
+ and left_succs[0] == right
2009
+ and full_graph.in_degree[left] == 1
2010
+ and full_graph.in_degree[right] >= 2
2011
+ ):
2012
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2013
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2014
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2015
+ # c = !c
2016
+ last_if_jump = self._remove_last_statement_if_jump(start_node)
2017
+ new_cond_node = ConditionNode(
2018
+ last_if_jump.ins_addr if last_if_jump is not None else start_node.addr,
2019
+ None,
2020
+ edge_cond_left,
2021
+ left,
2022
+ false_node=None,
2023
+ )
2024
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2025
+
2026
+ # on the original graph
2027
+ self.replace_nodes(graph, start_node, new_node, old_node_1=left)
2028
+ # on the graph with successors
2029
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=left)
2030
+
2031
+ return True
2032
+
2033
+ if right in graph and left not in graph:
2034
+ # swap them
2035
+ left, right = right, left
2036
+ left_succs, right_succs = right_succs, left_succs # pylint:disable=unused-variable
2037
+ # potentially If-then
2038
+ if (
2039
+ left in graph
2040
+ and right not in graph
2041
+ and full_graph.in_degree[left] == 1
2042
+ and (
2043
+ (full_graph.in_degree[right] == 2 and left_succs == [right])
2044
+ or (full_graph.in_degree[right] == 1 and not left_succs)
2045
+ )
2046
+ ):
2047
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2048
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2049
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2050
+ # c = !c
2051
+ try:
2052
+ last_stmt = self.cond_proc.get_last_statement(start_node)
2053
+ except EmptyBlockNotice:
2054
+ last_stmt = None
2055
+ new_cond_node = ConditionNode(
2056
+ last_stmt.ins_addr if last_stmt is not None else start_node.addr,
2057
+ None,
2058
+ edge_cond_left,
2059
+ left,
2060
+ false_node=None,
2061
+ )
2062
+ new_nodes = [start_node, new_cond_node]
2063
+ if full_graph.in_degree[right] == 1:
2064
+ # only remove the if statement when it will no longer be used later
2065
+ self._remove_last_statement_if_jump(start_node)
2066
+ # add a goto node at the end
2067
+ new_jump_node = Block(
2068
+ new_cond_node.addr if new_cond_node.addr is not None else 0x7EFF_FFFF,
2069
+ 0,
2070
+ statements=[
2071
+ Jump(
2072
+ None,
2073
+ Const(None, None, right.addr, self.project.arch.bits),
2074
+ ins_addr=new_cond_node.addr,
2075
+ )
2076
+ ],
2077
+ )
2078
+ new_nodes.append(new_jump_node)
2079
+ new_node = SequenceNode(start_node.addr, nodes=new_nodes)
2080
+
2081
+ # on the original graph
2082
+ self.replace_nodes(graph, start_node, new_node, old_node_1=left)
2083
+ # on the graph with successors
2084
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=left)
2085
+
2086
+ return True
2087
+
2088
+ return False
2089
+
2090
+ def _match_acyclic_short_circuit_conditions(
2091
+ self, graph: networkx.DiGraph, full_graph: networkx.DiGraph, start_node
2092
+ ) -> bool:
2093
+ """
2094
+ Check if start_node is the beginning of an If-Then-Else region with cascading short-circuit expressions as the
2095
+ condition. Create a Condition node if it is the case.
2096
+ """
2097
+
2098
+ # There are four possible graph schemas.
2099
+ #
2100
+ # Type A: Cascading Or::
2101
+ #
2102
+ # cond_node
2103
+ # | \
2104
+ # | \
2105
+ # next_cond \
2106
+ # ... \ \
2107
+ # \ |
2108
+ # \ |
2109
+ # \ |
2110
+ # ... body
2111
+ # ... /
2112
+ # \ \ \ /
2113
+ # successor
2114
+ #
2115
+ # We reduce it into if (cond || next_cond) { body }
2116
+ #
2117
+ # Type B: Cascading Or with else::
2118
+ #
2119
+ # cond_node
2120
+ # | \
2121
+ # | \
2122
+ # next_cond \
2123
+ # ... \ \
2124
+ # \ |
2125
+ # \ |
2126
+ # \ |
2127
+ # ... body
2128
+ # else /
2129
+ # \ \ \ /
2130
+ # successor
2131
+ #
2132
+ # We reduce it into if (cond || next_cond) { body } else { else }
2133
+ #
2134
+ # Type C: Cascading And::
2135
+ #
2136
+ # cond_node
2137
+ # | \
2138
+ # | \
2139
+ # next_cond \
2140
+ # ... \ \
2141
+ # \ |
2142
+ # \ |
2143
+ # \ \ /
2144
+ # \ |
2145
+ # body |
2146
+ # ... | |
2147
+ # | |
2148
+ # \ \ \ /
2149
+ # successor
2150
+ #
2151
+ # We reduce it into if (cond && next_cond) { body }
2152
+ #
2153
+ # Type D: Cascading And with else::
2154
+ #
2155
+ # cond_node
2156
+ # | \
2157
+ # | \
2158
+ # next_cond \
2159
+ # ... \ \
2160
+ # \ |
2161
+ # \ |
2162
+ # \ \ /
2163
+ # \ |
2164
+ # body |
2165
+ # ... | else
2166
+ # | |
2167
+ # \ \ \ /
2168
+ # successor
2169
+ #
2170
+ # We reduce it into if (cond && next_cond) { body } else { else }
2171
+
2172
+ r = self._match_acyclic_short_circuit_conditions_type_a(graph, full_graph, start_node)
2173
+
2174
+ if r is not None:
2175
+ left, left_cond, right, left_right_cond, succ = r
2176
+ # create the condition node
2177
+ memo = {}
2178
+ if not self._is_single_statement_block(left):
2179
+ if not self._should_use_multistmtexprs(left):
2180
+ return False
2181
+ # create a MultiStatementExpression for left_right_cond
2182
+ stmts = self._build_multistatementexpr_statements(left)
2183
+ assert stmts is not None
2184
+ mstmt_expr = MultiStatementExpression(
2185
+ None, stmts, self.cond_proc.convert_claripy_bool_ast(left_right_cond), ins_addr=left.addr
2186
+ )
2187
+ memo[left_right_cond._hash] = mstmt_expr
2188
+ cond = self.cond_proc.convert_claripy_bool_ast(
2189
+ claripy.Or(claripy.Not(left_cond), left_right_cond), memo=memo
2190
+ )
2191
+ cond_jump = ConditionalJump(
2192
+ None,
2193
+ cond,
2194
+ Const(None, None, right.addr, self.project.arch.bits),
2195
+ Const(None, None, succ.addr, self.project.arch.bits),
2196
+ true_target_idx=right.idx if isinstance(right, (Block, MultiNode)) else None,
2197
+ false_target_idx=succ.idx if isinstance(succ, (Block, MultiNode)) else None,
2198
+ ins_addr=start_node.addr,
2199
+ stmt_idx=0,
2200
+ )
2201
+ new_cond_node = Block(start_node.addr, None, statements=[cond_jump])
2202
+ self._remove_last_statement_if_jump(start_node)
2203
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2204
+
2205
+ self.replace_nodes(graph, start_node, new_node, old_node_1=left if left in graph else None)
2206
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=left)
2207
+
2208
+ return True
2209
+
2210
+ r = self._match_acyclic_short_circuit_conditions_type_b(graph, full_graph, start_node)
2211
+
2212
+ if r is not None:
2213
+ left, left_cond, right, right_left_cond, else_node = r
2214
+ # create the condition node
2215
+ memo = {}
2216
+ if not self._is_single_statement_block(right):
2217
+ if not self._should_use_multistmtexprs(right):
2218
+ return False
2219
+ # create a MultiStatementExpression for left_right_cond
2220
+ stmts = self._build_multistatementexpr_statements(right)
2221
+ assert stmts is not None
2222
+ mstmt_expr = MultiStatementExpression(
2223
+ None, stmts, self.cond_proc.convert_claripy_bool_ast(right_left_cond), ins_addr=left.addr
2224
+ )
2225
+ memo[right_left_cond._hash] = mstmt_expr
2226
+ cond = self.cond_proc.convert_claripy_bool_ast(claripy.Or(left_cond, right_left_cond), memo=memo)
2227
+ cond_jump = ConditionalJump(
2228
+ None,
2229
+ cond,
2230
+ Const(None, None, left.addr, self.project.arch.bits, ins_addr=start_node.addr),
2231
+ Const(None, None, else_node.addr, self.project.arch.bits, ins_addr=start_node.addr),
2232
+ true_target_idx=left.idx if isinstance(left, (Block, MultiNode)) else None,
2233
+ false_target_idx=else_node.idx if isinstance(else_node, (Block, MultiNode)) else None,
2234
+ ins_addr=start_node.addr,
2235
+ stmt_idx=0,
2236
+ )
2237
+ new_cond_node = Block(start_node.addr, None, statements=[cond_jump])
2238
+ self._remove_last_statement_if_jump(start_node)
2239
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2240
+
2241
+ self.replace_nodes(graph, start_node, new_node, old_node_1=right if right in graph else None)
2242
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=right)
2243
+
2244
+ return True
2245
+
2246
+ r = self._match_acyclic_short_circuit_conditions_type_c(graph, full_graph, start_node)
2247
+
2248
+ if r is not None:
2249
+ left, left_cond, succ, left_succ_cond, right = r
2250
+ # create the condition node
2251
+ memo = {}
2252
+ if not self._is_single_statement_block(left):
2253
+ if not self._should_use_multistmtexprs(left):
2254
+ return False
2255
+ # create a MultiStatementExpression for left_right_cond
2256
+ stmts = self._build_multistatementexpr_statements(left)
2257
+ assert stmts is not None
2258
+ mstmt_expr = MultiStatementExpression(
2259
+ None, stmts, self.cond_proc.convert_claripy_bool_ast(left_succ_cond), ins_addr=left.addr
2260
+ )
2261
+ memo[left_succ_cond._hash] = mstmt_expr
2262
+ cond = self.cond_proc.convert_claripy_bool_ast(
2263
+ claripy.And(left_cond, claripy.Not(left_succ_cond)), memo=memo
2264
+ )
2265
+ cond_jump = ConditionalJump(
2266
+ None,
2267
+ cond,
2268
+ Const(None, None, right.addr, self.project.arch.bits),
2269
+ Const(None, None, succ.addr, self.project.arch.bits),
2270
+ true_target_idx=right.idx if isinstance(right, (Block, MultiNode)) else None,
2271
+ false_target_idx=succ.idx if isinstance(succ, (Block, MultiNode)) else None,
2272
+ ins_addr=start_node.addr,
2273
+ stmt_idx=0,
2274
+ )
2275
+ new_cond_node = Block(start_node.addr, None, statements=[cond_jump])
2276
+ self._remove_last_statement_if_jump(start_node)
2277
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2278
+
2279
+ self.replace_nodes(graph, start_node, new_node, old_node_1=left if left in graph else None)
2280
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=left)
2281
+ return True
2282
+
2283
+ r = self._match_acyclic_short_circuit_conditions_type_d(graph, full_graph, start_node)
2284
+
2285
+ if r is not None:
2286
+ left, left_cond, right, right_left_cond, else_node = r
2287
+ # create the condition node
2288
+ memo = {}
2289
+ if not self._is_single_statement_block(left):
2290
+ if not self._should_use_multistmtexprs(left):
2291
+ return False
2292
+ # create a MultiStatementExpression for left_right_cond
2293
+ stmts = self._build_multistatementexpr_statements(left)
2294
+ assert stmts is not None
2295
+ mstmt_expr = MultiStatementExpression(
2296
+ None, stmts, self.cond_proc.convert_claripy_bool_ast(right_left_cond), ins_addr=left.addr
2297
+ )
2298
+ memo[right_left_cond._hash] = mstmt_expr
2299
+ cond = self.cond_proc.convert_claripy_bool_ast(
2300
+ claripy.And(left_cond, right_left_cond),
2301
+ memo=memo,
2302
+ )
2303
+ cond_jump = ConditionalJump(
2304
+ None,
2305
+ cond,
2306
+ Const(None, None, right.addr, self.project.arch.bits),
2307
+ Const(None, None, else_node.addr, self.project.arch.bits),
2308
+ true_target_idx=right.idx if isinstance(right, (Block, MultiNode)) else None,
2309
+ false_target_idx=else_node.idx if isinstance(else_node, (Block, MultiNode)) else None,
2310
+ ins_addr=start_node.addr,
2311
+ stmt_idx=0,
2312
+ )
2313
+ new_cond_node = Block(start_node.addr, None, statements=[cond_jump])
2314
+ self._remove_last_statement_if_jump(start_node)
2315
+ new_node = SequenceNode(start_node.addr, nodes=[start_node, new_cond_node])
2316
+
2317
+ self.replace_nodes(graph, start_node, new_node, old_node_1=left if left in graph else None)
2318
+ self.replace_nodes(full_graph, start_node, new_node, old_node_1=left)
2319
+ return True
2320
+
2321
+ return False
2322
+
2323
+ def _match_acyclic_short_circuit_conditions_type_a( # pylint:disable=unused-argument
2324
+ self, graph, full_graph, start_node
2325
+ ) -> tuple | None:
2326
+ # if (a) goto right
2327
+ # else if (b) goto right
2328
+ # else goto other_succ
2329
+ # right:
2330
+ # ...
2331
+ # goto other_succ
2332
+ # other_succ:
2333
+
2334
+ succs = list(full_graph.successors(start_node))
2335
+ if len(succs) == 2:
2336
+ left, right = succs
2337
+
2338
+ if full_graph.in_degree[left] > 1 and full_graph.in_degree[right] == 1:
2339
+ left, right = right, left
2340
+
2341
+ # ensure left and right nodes are not the head of a switch-case construct
2342
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
2343
+ full_graph, left
2344
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, right):
2345
+ return None
2346
+
2347
+ if (
2348
+ self._is_sequential_statement_block(left)
2349
+ and full_graph.in_degree[left] == 1
2350
+ and full_graph.in_degree[right] >= 1
2351
+ ):
2352
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2353
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2354
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2355
+ # c0 = !c0
2356
+ left_succs = list(full_graph.successors(left))
2357
+ if len(left_succs) == 2 and right in left_succs:
2358
+ other_succ = next(iter(succ for succ in left_succs if succ is not right))
2359
+ if full_graph.out_degree[right] == 1 and full_graph.has_edge(right, other_succ):
2360
+ # there must be an edge between right and other_succ
2361
+ edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2362
+ edge_cond_left_other = self.cond_proc.recover_edge_condition(full_graph, left, other_succ)
2363
+ if claripy.is_true(
2364
+ claripy.Not(edge_cond_left_right) == edge_cond_left_other # type: ignore
2365
+ ):
2366
+ # c1 = !c1
2367
+ return left, edge_cond_left, right, edge_cond_left_right, other_succ
2368
+ return None
2369
+
2370
+ def _match_acyclic_short_circuit_conditions_type_b( # pylint:disable=unused-argument
2371
+ self, graph, full_graph, start_node
2372
+ ) -> tuple | None:
2373
+ # if (a) goto left
2374
+ # right:
2375
+ # else if (b) goto left
2376
+ # else goto else_node
2377
+ # left:
2378
+ # ...
2379
+ # goto succ
2380
+ # else_node:
2381
+ # ...
2382
+ # goto succ
2383
+ # succ:
2384
+
2385
+ succs = list(full_graph.successors(start_node))
2386
+ if len(succs) == 2:
2387
+ left, right = succs
2388
+
2389
+ if full_graph.in_degree[left] == 1 and full_graph.in_degree[right] == 2:
2390
+ left, right = right, left
2391
+
2392
+ # ensure left and right nodes are not the head of a switch-case construct
2393
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
2394
+ full_graph, left
2395
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, right):
2396
+ return None
2397
+
2398
+ if (
2399
+ self._is_sequential_statement_block(right)
2400
+ and full_graph.in_degree[left] == 2
2401
+ and full_graph.in_degree[right] == 1
2402
+ ):
2403
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2404
+ edge_cond_right = self.cond_proc.recover_edge_condition(full_graph, start_node, right)
2405
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_right): # type: ignore
2406
+ # c0 = !c0
2407
+ right_succs = list(full_graph.successors(right))
2408
+ left_succs = list(full_graph.successors(left))
2409
+ if len(right_succs) == 2 and left in right_succs:
2410
+ else_node = next(iter(succ for succ in right_succs if succ is not left))
2411
+ if len([succ for succ in left_succs if succ is not else_node]) == 1:
2412
+ edge_cond_right_left = self.cond_proc.recover_edge_condition(full_graph, right, left)
2413
+ edge_cond_right_else = self.cond_proc.recover_edge_condition(full_graph, right, else_node)
2414
+ if claripy.is_true(
2415
+ claripy.Not(edge_cond_right_left) == edge_cond_right_else # type: ignore
2416
+ ):
2417
+ # c1 = !c1
2418
+ return left, edge_cond_left, right, edge_cond_right_left, else_node
2419
+ return None
2420
+
2421
+ def _match_acyclic_short_circuit_conditions_type_c( # pylint:disable=unused-argument
2422
+ self, graph, full_graph, start_node
2423
+ ) -> tuple | None:
2424
+ # if (a) goto successor
2425
+ # else if (b) goto successor
2426
+ # right:
2427
+ # ...
2428
+ # successor:
2429
+
2430
+ succs = list(full_graph.successors(start_node))
2431
+ if len(succs) == 2:
2432
+ left, successor = succs
2433
+
2434
+ if full_graph.in_degree[left] > 1 and full_graph.in_degree[successor] == 1:
2435
+ left, successor = successor, left
2436
+
2437
+ # ensure left and successor nodes are not the head of a switch-case construct
2438
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
2439
+ full_graph, left
2440
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, successor):
2441
+ return None
2442
+
2443
+ if (
2444
+ self._is_sequential_statement_block(left)
2445
+ and full_graph.in_degree[left] == 1
2446
+ and full_graph.in_degree[successor] >= 1
2447
+ ):
2448
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2449
+ edge_cond_successor = self.cond_proc.recover_edge_condition(full_graph, start_node, successor)
2450
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_successor): # type: ignore
2451
+ # c0 = !c0
2452
+ left_succs = list(full_graph.successors(left))
2453
+ if len(left_succs) == 2 and successor in left_succs:
2454
+ right = next(iter(succ for succ in left_succs if succ is not successor))
2455
+ if full_graph.out_degree[right] == 1 and full_graph.has_edge(right, successor):
2456
+ # there must be an edge from right to successor
2457
+ edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2458
+ edge_cond_left_successor = self.cond_proc.recover_edge_condition(
2459
+ full_graph, left, successor
2460
+ )
2461
+ if claripy.is_true(
2462
+ claripy.Not(edge_cond_left_right) == edge_cond_left_successor # type: ignore
2463
+ ):
2464
+ # c1 = !c1
2465
+ return left, edge_cond_left, successor, edge_cond_left_successor, right
2466
+ return None
2467
+
2468
+ def _match_acyclic_short_circuit_conditions_type_d( # pylint:disable=unused-argument
2469
+ self, graph, full_graph, start_node
2470
+ ) -> tuple | None:
2471
+ # if (a) goto else_node
2472
+ # left:
2473
+ # else if (b) goto else_node
2474
+ # right:
2475
+ # ...
2476
+ # goto successor
2477
+ # else_node:
2478
+ # ...
2479
+ # goto successor
2480
+ # successor:
2481
+
2482
+ succs = list(full_graph.successors(start_node))
2483
+ if len(succs) == 2:
2484
+ left, else_node = succs
2485
+
2486
+ if full_graph.in_degree[left] > 1 and full_graph.in_degree[else_node] == 1:
2487
+ left, else_node = else_node, left
2488
+
2489
+ # ensure left and else nodes are not the head of a switch-case construct
2490
+ if self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(
2491
+ full_graph, left
2492
+ ) or self._is_switch_cases_address_loaded_from_memory_head_or_jumpnode(full_graph, else_node):
2493
+ return None
2494
+
2495
+ if (
2496
+ self._is_sequential_statement_block(left)
2497
+ and full_graph.in_degree[left] == 1
2498
+ and full_graph.in_degree[else_node] >= 1
2499
+ ):
2500
+ edge_cond_left = self.cond_proc.recover_edge_condition(full_graph, start_node, left)
2501
+ edge_cond_else = self.cond_proc.recover_edge_condition(full_graph, start_node, else_node)
2502
+ if claripy.is_true(claripy.Not(edge_cond_left) == edge_cond_else): # type: ignore
2503
+ # c0 = !c0
2504
+ left_succs = list(full_graph.successors(left))
2505
+ if len(left_succs) == 2 and else_node in left_succs:
2506
+ right = next(iter(succ for succ in left_succs if succ is not else_node))
2507
+ edge_cond_left_right = self.cond_proc.recover_edge_condition(full_graph, left, right)
2508
+ edge_cond_left_else = self.cond_proc.recover_edge_condition(full_graph, left, else_node)
2509
+ if claripy.is_true(claripy.Not(edge_cond_left_right) == edge_cond_left_else): # type: ignore
2510
+ # c1 = !c1
2511
+ return left, edge_cond_left, right, edge_cond_left_right, else_node
2512
+ return None
2513
+
2514
+ def _last_resort_refinement(self, head, graph: networkx.DiGraph, full_graph: networkx.DiGraph) -> bool:
2515
+ if self._improve_algorithm:
2516
+ while self._edge_virtualization_hints:
2517
+ src, dst = self._edge_virtualization_hints.pop(0)
2518
+ if graph.has_edge(src, dst):
2519
+ self._virtualize_edge(graph, full_graph, src, dst)
2520
+ l.debug("last_resort: Removed edge %r -> %r (type 3)", src, dst)
2521
+ return True
2522
+
2523
+ # virtualize an edge to allow progressing in structuring
2524
+ all_edges_wo_dominance = [] # to ensure determinism, edges in this list are ordered by a tuple of
2525
+ # (src_addr, dst_addr)
2526
+ secondary_edges = [] # likewise, edges in this list are ordered by a tuple of (src_addr, dst_addr)
2527
+ other_edges = []
2528
+ idoms = networkx.immediate_dominators(full_graph, head)
2529
+ if networkx.is_directed_acyclic_graph(full_graph):
2530
+ acyclic_graph = full_graph
2531
+ else:
2532
+ acyclic_graph = to_acyclic_graph(full_graph, loop_heads=[head])
2533
+ for src, dst in acyclic_graph.edges:
2534
+ if src is dst:
2535
+ continue
2536
+ if src not in graph:
2537
+ continue
2538
+ if not dominates(idoms, src, dst) and not dominates(idoms, dst, src):
2539
+ if (src.addr, dst.addr) not in self.whitelist_edges:
2540
+ all_edges_wo_dominance.append((src, dst))
2541
+ elif not dominates(idoms, src, dst):
2542
+ if (src.addr, dst.addr) not in self.whitelist_edges:
2543
+ secondary_edges.append((src, dst))
2544
+ else:
2545
+ if (src.addr, dst.addr) not in self.whitelist_edges:
2546
+ other_edges.append((src, dst))
2547
+
2548
+ ordered_nodes = GraphUtils.quasi_topological_sort_nodes(acyclic_graph, loop_heads=[head])
2549
+ node_seq = {nn: (len(ordered_nodes) - idx) for (idx, nn) in enumerate(ordered_nodes)} # post-order
2550
+
2551
+ if all_edges_wo_dominance:
2552
+ all_edges_wo_dominance = self._order_virtualizable_edges(full_graph, all_edges_wo_dominance, node_seq)
2553
+ # virtualize the first edge
2554
+ src, dst = all_edges_wo_dominance[0]
2555
+ self._virtualize_edge(graph, full_graph, src, dst)
2556
+ l.debug("last_resort: Removed edge %r -> %r (type 1)", src, dst)
2557
+ return True
2558
+
2559
+ if secondary_edges:
2560
+ secondary_edges = self._order_virtualizable_edges(full_graph, secondary_edges, node_seq)
2561
+ # virtualize the first edge
2562
+ src, dst = secondary_edges[0]
2563
+ self._virtualize_edge(graph, full_graph, src, dst)
2564
+ l.debug("last_resort: Removed edge %r -> %r (type 2)", src, dst)
2565
+ return True
2566
+
2567
+ l.debug("last_resort: No edge to remove")
2568
+ return False
2569
+
2570
+ def _virtualize_edge(self, graph, full_graph, src, dst):
2571
+ # if the last statement of src is a conditional jump, we rewrite it into a Condition(Jump) and a direct jump
2572
+ try:
2573
+ last_stmt = self.cond_proc.get_last_statement(src)
2574
+ except EmptyBlockNotice:
2575
+ last_stmt = None
2576
+ new_src = None
2577
+ remove_src_last_stmt = False
2578
+ if isinstance(last_stmt, ConditionalJump):
2579
+ if isinstance(last_stmt.true_target, Const) and last_stmt.true_target.value == dst.addr:
2580
+ goto0_condition = last_stmt.condition
2581
+ goto0_target = last_stmt.true_target
2582
+ goto1_target = last_stmt.false_target
2583
+ elif isinstance(last_stmt.false_target, Const) and last_stmt.false_target.value == dst.addr:
2584
+ goto0_condition = UnaryOp(None, "Not", last_stmt.condition)
2585
+ goto0_target = last_stmt.false_target
2586
+ goto1_target = last_stmt.true_target
2587
+ else:
2588
+ # this should not really happen...
2589
+ goto0_condition = None
2590
+ goto0_target = None
2591
+ goto1_target = None
2592
+
2593
+ if goto0_condition is not None:
2594
+ assert goto0_target is not None and goto1_target is not None
2595
+ goto0 = Block(
2596
+ last_stmt.ins_addr,
2597
+ 0,
2598
+ statements=[Jump(None, goto0_target, ins_addr=last_stmt.ins_addr, stmt_idx=0)],
2599
+ )
2600
+ cond_node = ConditionNode(last_stmt.ins_addr, None, goto0_condition, goto0)
2601
+ goto1_node = Block(
2602
+ last_stmt.ins_addr,
2603
+ 0,
2604
+ statements=[Jump(None, goto1_target, ins_addr=last_stmt.ins_addr, stmt_idx=0)],
2605
+ )
2606
+ remove_src_last_stmt = True
2607
+ new_src = SequenceNode(src.addr, nodes=[src, cond_node, goto1_node])
2608
+ elif isinstance(last_stmt, Jump):
2609
+ # do nothing
2610
+ pass
2611
+ else:
2612
+ # insert a Jump at the end
2613
+ stmt_addr = src.addr
2614
+ goto_node = Block(
2615
+ stmt_addr,
2616
+ 0,
2617
+ statements=[
2618
+ Jump(None, Const(None, None, dst.addr, self.project.arch.bits), ins_addr=stmt_addr, stmt_idx=0)
2619
+ ],
2620
+ )
2621
+ new_src = SequenceNode(src.addr, nodes=[src, goto_node])
2622
+
2623
+ if graph.has_edge(src, dst):
2624
+ graph.remove_edge(src, dst)
2625
+ self.virtualized_edges.add((src, dst))
2626
+ if new_src is not None:
2627
+ self.replace_nodes(graph, src, new_src)
2628
+ if full_graph is not None:
2629
+ self.virtualized_edges.add((src, dst))
2630
+ full_graph.remove_edge(src, dst)
2631
+ if new_src is not None:
2632
+ self.replace_nodes(full_graph, src, new_src)
2633
+ if remove_src_last_stmt:
2634
+ remove_last_statements(src)
2635
+
2636
+ def _should_use_multistmtexprs(self, node: Block | BaseNode) -> bool:
2637
+ """
2638
+ The original Phoenix algorithm had no support for multi-stmt expressions, such as the following:
2639
+ if ((x = y) && z) { ... }
2640
+
2641
+ There are multiple levels at which multi-stmt expressions can be used. If the Phoenix algorithm is not not
2642
+ set to be in improved mode, then we should not use multi-stmt expressions at all.
2643
+ """
2644
+ if not self._improve_algorithm:
2645
+ return False
2646
+ if self._use_multistmtexprs == MultiStmtExprMode.NEVER:
2647
+ return False
2648
+ if self._use_multistmtexprs == MultiStmtExprMode.ALWAYS:
2649
+ ctr = AILCallCounter()
2650
+ ctr.walk(node)
2651
+ return ctr.non_label_stmts <= self._multistmtexpr_stmt_threshold
2652
+ if self._use_multistmtexprs == MultiStmtExprMode.MAX_ONE_CALL:
2653
+ # count the number of calls
2654
+ ctr = AILCallCounter()
2655
+ ctr.walk(node)
2656
+ return ctr.calls <= 1 and ctr.non_label_stmts <= self._multistmtexpr_stmt_threshold
2657
+ l.warning("Unsupported enum value for _use_multistmtexprs: %s", self._use_multistmtexprs)
2658
+ return False
2659
+
2660
+ @staticmethod
2661
+ def _find_node_going_to_dst(
2662
+ node: BaseNode,
2663
+ dst: Block | BaseNode,
2664
+ last=True,
2665
+ condjump_only=False,
2666
+ ) -> tuple[int | None, BaseNode | None, Block | MultiNode | BreakNode | None]:
2667
+ """
2668
+
2669
+ :param node:
2670
+ :param dst_addr:
2671
+ :param dst_idx:
2672
+ :return: A tuple of (parent node, node who has a successor of dst_addr)
2673
+ """
2674
+
2675
+ dst_addr = dst.addr
2676
+ dst_idx = dst.idx if isinstance(dst, Block) else ...
2677
+
2678
+ class _Holder:
2679
+ """
2680
+ Holds parent_and_block and is accessible from within the handlers.
2681
+ """
2682
+
2683
+ parent_and_block: list[tuple[int, Any, Block | MultiNode | BreakNode]] = []
2684
+ block_id: int = -1
2685
+
2686
+ def _check(last_stmt):
2687
+ return (
2688
+ (
2689
+ not condjump_only
2690
+ and isinstance(last_stmt, Jump)
2691
+ and isinstance(last_stmt.target, Const)
2692
+ and last_stmt.target.value == dst_addr
2693
+ and (dst_idx is ... or last_stmt.target_idx == dst_idx)
2694
+ )
2695
+ or (
2696
+ isinstance(last_stmt, ConditionalJump)
2697
+ and (
2698
+ (
2699
+ isinstance(last_stmt.true_target, Const)
2700
+ and last_stmt.true_target.value == dst_addr
2701
+ and (dst_idx is ... or last_stmt.true_target_idx == dst_idx)
2702
+ )
2703
+ or (
2704
+ isinstance(last_stmt.false_target, Const)
2705
+ and last_stmt.false_target.value == dst_addr
2706
+ and (dst_idx is ... or last_stmt.false_target_idx == dst_idx)
2707
+ )
2708
+ )
2709
+ )
2710
+ or (
2711
+ isinstance(last_stmt, IncompleteSwitchCaseHeadStatement)
2712
+ and any(case_addr == dst_addr for _, _, _, _, case_addr in last_stmt.case_addrs)
2713
+ )
2714
+ )
2715
+
2716
+ def _handle_Block(block: Block, parent=None, **kwargs): # pylint:disable=unused-argument
2717
+ if block.statements:
2718
+ first_stmt = first_nonlabel_nonphi_statement(block)
2719
+ if first_stmt is not None:
2720
+ # this block has content. increment the block ID counter
2721
+ _Holder.block_id += 1
2722
+
2723
+ if _check(first_stmt):
2724
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
2725
+ elif len(block.statements) > 1:
2726
+ last_stmt = block.statements[-1]
2727
+ if _check(last_stmt) or (
2728
+ not isinstance(last_stmt, (Jump, ConditionalJump))
2729
+ and block.addr + block.original_size == dst_addr
2730
+ ):
2731
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
2732
+
2733
+ def _handle_MultiNode(block: MultiNode, parent=None, **kwargs): # pylint:disable=unused-argument
2734
+ if block.nodes and isinstance(block.nodes[-1], Block) and block.nodes[-1].statements:
2735
+ first_stmt = first_nonlabel_nonphi_statement(block)
2736
+ if first_stmt is not None:
2737
+ # this block has content. increment the block ID counter
2738
+ _Holder.block_id += 1
2739
+ if _check(block.nodes[-1].statements[-1]):
2740
+ _Holder.parent_and_block.append((_Holder.block_id, parent, block))
2741
+
2742
+ def _handle_BreakNode(break_node: BreakNode, parent=None, **kwargs): # pylint:disable=unused-argument
2743
+ _Holder.block_id += 1
2744
+ if break_node.target == dst_addr or (
2745
+ isinstance(break_node.target, Const) and break_node.target.value == dst_addr
2746
+ ):
2747
+ # FIXME: idx is ignored
2748
+ _Holder.parent_and_block.append((_Holder.block_id, parent, break_node))
2749
+
2750
+ walker = SequenceWalker(
2751
+ handlers={
2752
+ Block: _handle_Block,
2753
+ MultiNode: _handle_MultiNode,
2754
+ BreakNode: _handle_BreakNode,
2755
+ },
2756
+ update_seqnode_in_place=False,
2757
+ force_forward_scan=True,
2758
+ )
2759
+ _Holder.block_id = -1
2760
+ walker.walk(node)
2761
+ if not _Holder.parent_and_block:
2762
+ return None, None, None
2763
+ if last:
2764
+ return _Holder.parent_and_block[-1]
2765
+ return _Holder.parent_and_block[0]
2766
+
2767
+ @staticmethod
2768
+ def _unpack_sequencenode_head(graph: networkx.DiGraph, seq: SequenceNode, new_seq=None):
2769
+ if not seq.nodes:
2770
+ return False, None
2771
+ node = seq.nodes[0]
2772
+ if new_seq is None:
2773
+ # create the new sequence node if no prior-created sequence node is passed in
2774
+ new_seq = seq.copy()
2775
+ new_seq.nodes = new_seq.nodes[1:]
2776
+ if new_seq.nodes:
2777
+ new_seq.addr = new_seq.nodes[0].addr
2778
+
2779
+ preds = list(graph.predecessors(seq))
2780
+ succs = list(graph.successors(seq))
2781
+ graph.remove_node(seq)
2782
+ for pred in preds:
2783
+ graph.add_edge(pred, node)
2784
+ if new_seq.nodes:
2785
+ graph.add_edge(node, new_seq)
2786
+ for succ in succs:
2787
+ if succ is seq:
2788
+ graph.add_edge(new_seq, new_seq)
2789
+ else:
2790
+ graph.add_edge(new_seq, succ)
2791
+ return True, new_seq
2792
+
2793
+ @staticmethod
2794
+ def _unpack_incompleteswitchcasenode(graph: networkx.DiGraph, incscnode: IncompleteSwitchCaseNode) -> bool:
2795
+ preds = list(graph.predecessors(incscnode))
2796
+ succs = list(graph.successors(incscnode))
2797
+ if len(succs) <= 1:
2798
+ graph.remove_node(incscnode)
2799
+ for pred in preds:
2800
+ graph.add_edge(pred, incscnode.head)
2801
+ for case_node in incscnode.cases:
2802
+ graph.add_edge(incscnode.head, case_node)
2803
+ if succs:
2804
+ graph.add_edge(case_node, succs[0])
2805
+ return True
2806
+ return False
2807
+
2808
+ @staticmethod
2809
+ def _count_statements(node: BaseNode | Block) -> int:
2810
+ if isinstance(node, Block):
2811
+ return sum(1 for stmt in node.statements if not isinstance(stmt, Label) and not is_phi_assignment(stmt))
2812
+ if isinstance(node, (MultiNode, SequenceNode)):
2813
+ return sum(PhoenixStructurer._count_statements(nn) for nn in node.nodes)
2814
+ return 1
2815
+
2816
+ @staticmethod
2817
+ def _is_single_statement_block(node: BaseNode | Block) -> bool:
2818
+ if isinstance(node, (Block, MultiNode, SequenceNode)):
2819
+ return PhoenixStructurer._count_statements(node) == 1
2820
+ return False
2821
+
2822
+ @staticmethod
2823
+ def _is_sequential_statement_block(node: BaseNode | Block) -> bool:
2824
+ """
2825
+ Examine if the node can be converted into a MultiStatementExpression object. The conversion fails if there are
2826
+ any conditional statements or goto statements before the very last statement of the node.
2827
+ """
2828
+
2829
+ def _is_sequential_statement_list(stmts: list[Statement]) -> bool:
2830
+ if not stmts:
2831
+ return True
2832
+ return all(not isinstance(stmt, (ConditionalJump, Jump)) for stmt in stmts[:-1])
2833
+
2834
+ def _to_statement_list(node: Block | MultiNode | SequenceNode | BaseNode) -> list[Statement]:
2835
+ if isinstance(node, Block):
2836
+ return node.statements
2837
+ if isinstance(node, MultiNode):
2838
+ # expand it
2839
+ all_statements = []
2840
+ for nn in node.nodes:
2841
+ all_statements += _to_statement_list(nn)
2842
+ return all_statements
2843
+ if isinstance(node, SequenceNode):
2844
+ all_statements = []
2845
+ for nn in node.nodes:
2846
+ all_statements += _to_statement_list(nn)
2847
+ return all_statements
2848
+ raise TypeError(f"Unsupported node type {type(node)}")
2849
+
2850
+ try:
2851
+ stmt_list = _to_statement_list(node)
2852
+ except TypeError:
2853
+ return False
2854
+ return _is_sequential_statement_list(stmt_list)
2855
+
2856
+ @staticmethod
2857
+ def _build_multistatementexpr_statements(block) -> list[Statement] | None:
2858
+ stmts = []
2859
+ if isinstance(block, (SequenceNode, MultiNode)):
2860
+ for b in block.nodes:
2861
+ stmts_ = PhoenixStructurer._build_multistatementexpr_statements(b)
2862
+ if stmts_ is None:
2863
+ return None
2864
+ stmts += stmts_
2865
+ return stmts
2866
+ if isinstance(block, Block):
2867
+ for idx, stmt in enumerate(block.statements):
2868
+ if isinstance(stmt, Label):
2869
+ continue
2870
+ if is_phi_assignment(stmt):
2871
+ continue
2872
+ if isinstance(stmt, ConditionalJump):
2873
+ if idx == len(block.statements) - 1:
2874
+ continue
2875
+ return None
2876
+ if isinstance(stmt, Jump):
2877
+ return None
2878
+ stmts.append(stmt)
2879
+ return stmts
2880
+ return None
2881
+
2882
+ @staticmethod
2883
+ def _remove_edges_except(graph: networkx.DiGraph, src, dst):
2884
+ for succ in list(graph.successors(src)):
2885
+ if succ is not src and succ is not dst:
2886
+ graph.remove_edge(src, succ)
2887
+
2888
+ @staticmethod
2889
+ def _remove_first_statement_if_jump(node: BaseNode | Block | MultiNode) -> Jump | ConditionalJump | None:
2890
+ if isinstance(node, Block):
2891
+ if node.statements:
2892
+ idx = 0
2893
+ first_stmt = node.statements[idx]
2894
+ while isinstance(first_stmt, Label) or is_phi_assignment(first_stmt):
2895
+ idx += 1
2896
+ if idx >= len(node.statements):
2897
+ first_stmt = None
2898
+ break
2899
+ first_stmt = node.statements[idx]
2900
+
2901
+ if isinstance(first_stmt, (Jump, ConditionalJump)):
2902
+ if idx == 0:
2903
+ node.statements = node.statements[1:]
2904
+ else:
2905
+ node.statements = node.statements[0:idx] + node.statements[idx + 1 :]
2906
+ return first_stmt
2907
+ return None
2908
+ if isinstance(node, MultiNode):
2909
+ for nn in node.nodes:
2910
+ if isinstance(nn, Block):
2911
+ if not has_nonlabel_nonphi_statements(nn):
2912
+ continue
2913
+ return PhoenixStructurer._remove_first_statement_if_jump(nn)
2914
+ break
2915
+ return None
2916
+
2917
+ # pylint: disable=unused-argument,no-self-use
2918
+ def _order_virtualizable_edges(self, graph: networkx.DiGraph, edges: list, node_seq: dict[Any, int]) -> list:
2919
+ """
2920
+ Returns a list of edges that are ordered by the best edges to virtualize first.
2921
+ """
2922
+ return PhoenixStructurer._chick_order_edges(edges, node_seq)
2923
+
2924
+ @staticmethod
2925
+ def _chick_order_edges(edges: list, node_seq: dict[Any, int]) -> list:
2926
+ graph = networkx.DiGraph()
2927
+ graph.add_edges_from(edges)
2928
+
2929
+ def _sort_edge(edge_):
2930
+ # this is a bit complex. we first sort based on the topological order of the destination node; edges with
2931
+ # destination nodes that are closer to the head (as specified in node_seq) should be virtualized first.
2932
+ # then we solve draws by prioritizing edges whose destination nodes are with a lower in-degree (only
2933
+ # consider the sub graph with these edges), and a few other properties.
2934
+ src, dst = edge_
2935
+ dst_in_degree = graph.in_degree[dst]
2936
+ src_out_degree = graph.out_degree[src]
2937
+ return -node_seq.get(dst), dst_in_degree, src_out_degree, -src.addr, -dst.addr # type: ignore
2938
+
2939
+ return sorted(edges, key=_sort_edge, reverse=True)
2940
+
2941
+ @staticmethod
2942
+ def _replace_node_in_edge_list(edge_list: list[tuple], old_node, new_node) -> None:
2943
+ for idx in range(len(edge_list)): # pylint:disable=consider-using-enumerate
2944
+ edge = edge_list[idx]
2945
+ src, dst = edge
2946
+ replace = False
2947
+ if src is old_node:
2948
+ src = new_node
2949
+ replace = True
2950
+ if dst is old_node:
2951
+ dst = new_node
2952
+ replace = True
2953
+ if replace:
2954
+ edge_list[idx] = src, dst
2955
+
2956
+ @staticmethod
2957
+ def dump_graph(graph: networkx.DiGraph, path: str) -> None:
2958
+ graph_with_str = networkx.DiGraph()
2959
+
2960
+ for node in graph:
2961
+ graph_with_str.add_node(f'"{node!r}"')
2962
+
2963
+ for src, dst in graph.edges:
2964
+ graph_with_str.add_edge(f'"{src!r}"', f'"{dst!r}"')
2965
+
2966
+ networkx.drawing.nx_pydot.write_dot(graph_with_str, path)
2967
+
2968
+ @staticmethod
2969
+ def switch_case_entry_node_has_common_successor_case_1(graph, jump_table, case_nodes, node_pred) -> bool:
2970
+ all_succs = set()
2971
+ for case_node in case_nodes:
2972
+ if case_node is node_pred:
2973
+ continue
2974
+ if case_node.addr in jump_table.jumptable_entries:
2975
+ all_succs |= set(graph.successors(case_node))
2976
+
2977
+ case_node_successors = set()
2978
+ for case_node in case_nodes:
2979
+ if case_node is node_pred:
2980
+ continue
2981
+ if case_node in all_succs:
2982
+ continue
2983
+ if case_node.addr in jump_table.jumptable_entries:
2984
+ succs = set(graph.successors(case_node))
2985
+ case_node_successors |= {succ for succ in succs if succ.addr not in jump_table.jumptable_entries}
2986
+
2987
+ return len(case_node_successors) <= 1
2988
+
2989
+ @staticmethod
2990
+ def switch_case_entry_node_has_common_successor_case_2(graph, jump_table, case_nodes, node_pred) -> bool:
2991
+ case_node_successors = set()
2992
+ for case_node in case_nodes:
2993
+ if case_node is node_pred:
2994
+ continue
2995
+ if case_node.addr in jump_table.jumptable_entries:
2996
+ succs = set(graph.successors(case_node))
2997
+ case_node_successors |= {succ for succ in succs if succ.addr not in jump_table.jumptable_entries}
2998
+
2999
+ return len(case_node_successors) <= 1