angr 9.2.165__cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

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

Potentially problematic release.


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

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