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