angr 9.2.103__py3-none-macosx_11_0_arm64.whl

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

Potentially problematic release.


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

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