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,3662 @@
|
|
|
1
|
+
# pylint:disable=too-many-boolean-expressions
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import Any, NamedTuple, TYPE_CHECKING
|
|
4
|
+
import copy
|
|
5
|
+
import logging
|
|
6
|
+
import enum
|
|
7
|
+
from collections import defaultdict, namedtuple
|
|
8
|
+
from collections.abc import Iterable
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
import networkx
|
|
12
|
+
import capstone
|
|
13
|
+
|
|
14
|
+
from angr import ailment
|
|
15
|
+
from angr.ailment.block_walker import AILBlockViewer
|
|
16
|
+
from angr.ailment.expression import VirtualVariable
|
|
17
|
+
from angr.errors import AngrDecompilationError
|
|
18
|
+
from angr.knowledge_base import KnowledgeBase
|
|
19
|
+
from angr.knowledge_plugins.functions import Function
|
|
20
|
+
from angr.knowledge_plugins.cfg.memory_data import MemoryDataSort
|
|
21
|
+
from angr.knowledge_plugins.key_definitions import atoms
|
|
22
|
+
from angr.codenode import BlockNode
|
|
23
|
+
from angr.utils import timethis
|
|
24
|
+
from angr.utils.ssa import is_phi_assignment
|
|
25
|
+
from angr.utils.graph import GraphUtils
|
|
26
|
+
from angr.utils.types import dereference_simtype_by_lib
|
|
27
|
+
from angr.calling_conventions import SimRegArg, SimStackArg, SimFunctionArgument, SimCCUsercall
|
|
28
|
+
from angr.sim_type import (
|
|
29
|
+
SimType,
|
|
30
|
+
SimTypeChar,
|
|
31
|
+
SimTypeInt,
|
|
32
|
+
SimTypeLongLong,
|
|
33
|
+
SimTypeShort,
|
|
34
|
+
SimTypeFunction,
|
|
35
|
+
SimTypeBottom,
|
|
36
|
+
SimTypeFloat,
|
|
37
|
+
SimTypePointer,
|
|
38
|
+
SimStruct,
|
|
39
|
+
SimTypeArray,
|
|
40
|
+
SimCppClass,
|
|
41
|
+
)
|
|
42
|
+
from angr.analyses.stack_pointer_tracker import Register, OffsetVal
|
|
43
|
+
from angr.sim_variable import SimVariable, SimStackVariable, SimRegisterVariable, SimMemoryVariable
|
|
44
|
+
from angr.procedures.stubs.UnresolvableCallTarget import UnresolvableCallTarget
|
|
45
|
+
from angr.procedures.stubs.UnresolvableJumpTarget import UnresolvableJumpTarget
|
|
46
|
+
from angr.analyses import Analysis, register_analysis
|
|
47
|
+
from angr.analyses.cfg.cfg_base import CFGBase
|
|
48
|
+
from angr.analyses.reaching_definitions import ReachingDefinitionsAnalysis
|
|
49
|
+
from angr.analyses.typehoon import Typehoon
|
|
50
|
+
from .ail_simplifier import AILSimplifier
|
|
51
|
+
from .ssailification.ssailification import Ssailification
|
|
52
|
+
from .stack_item import StackItem, StackItemType
|
|
53
|
+
from .return_maker import ReturnMaker
|
|
54
|
+
from .ailgraph_walker import AILGraphWalker, RemoveNodeNotice
|
|
55
|
+
from .optimization_passes import (
|
|
56
|
+
OptimizationPassStage,
|
|
57
|
+
StackCanarySimplifier,
|
|
58
|
+
TagSlicer,
|
|
59
|
+
DUPLICATING_OPTS,
|
|
60
|
+
CONDENSING_OPTS,
|
|
61
|
+
)
|
|
62
|
+
from .semantic_naming import SemanticNamingOrchestrator
|
|
63
|
+
|
|
64
|
+
if TYPE_CHECKING:
|
|
65
|
+
from angr.knowledge_plugins.cfg import CFGModel
|
|
66
|
+
from .notes import DecompilationNote
|
|
67
|
+
from .decompilation_cache import DecompilationCache
|
|
68
|
+
from .peephole_optimizations import PeepholeOptimizationStmtBase, PeepholeOptimizationExprBase
|
|
69
|
+
|
|
70
|
+
l = logging.getLogger(name=__name__)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
BlockCache = namedtuple("BlockCache", ("rd", "prop"))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ClinicMode(enum.Enum):
|
|
77
|
+
"""
|
|
78
|
+
Analysis mode for Clinic.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
DECOMPILE = 1
|
|
82
|
+
COLLECT_DATA_REFS = 2
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class DataRefDesc:
|
|
87
|
+
"""
|
|
88
|
+
The fields of this class is compatible with items inside IRSB.data_refs.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
data_addr: int
|
|
92
|
+
data_size: int
|
|
93
|
+
block_addr: int
|
|
94
|
+
stmt_idx: int
|
|
95
|
+
ins_addr: int
|
|
96
|
+
data_type_str: str
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class ClinicStage(enum.IntEnum):
|
|
100
|
+
"""
|
|
101
|
+
Different stages of treating an ailment.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
INITIALIZATION = 0
|
|
105
|
+
AIL_GRAPH_CONVERSION = 1
|
|
106
|
+
MAKE_RETURN_SITES = 2
|
|
107
|
+
MAKE_ARGUMENT_LIST = 3
|
|
108
|
+
PRE_SSA_LEVEL0_FIXUPS = 4
|
|
109
|
+
SSA_LEVEL0_TRANSFORMATION = 5
|
|
110
|
+
CONSTANT_PROPAGATION = 6
|
|
111
|
+
TRACK_STACK_POINTERS = 7
|
|
112
|
+
PRE_SSA_LEVEL1_SIMPLIFICATIONS = 8
|
|
113
|
+
SSA_LEVEL1_TRANSFORMATION = 9
|
|
114
|
+
POST_SSA_LEVEL1_SIMPLIFICATIONS = 10
|
|
115
|
+
MAKE_CALLSITES = 11
|
|
116
|
+
POST_CALLSITES = 12
|
|
117
|
+
RECOVER_VARIABLES = 13
|
|
118
|
+
SEMANTIC_VARIABLE_NAMING = 14
|
|
119
|
+
COLLECT_EXTERNS = 15
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class Clinic(Analysis):
|
|
123
|
+
"""
|
|
124
|
+
A Clinic deals with AILments.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
_ail_manager: ailment.Manager
|
|
128
|
+
|
|
129
|
+
def __init__(
|
|
130
|
+
self,
|
|
131
|
+
func: Function,
|
|
132
|
+
remove_dead_memdefs=False,
|
|
133
|
+
exception_edges=False,
|
|
134
|
+
sp_tracker_track_memory=True,
|
|
135
|
+
fold_callexprs_into_conditions=False,
|
|
136
|
+
insert_labels=True,
|
|
137
|
+
optimization_passes=None,
|
|
138
|
+
cfg=None,
|
|
139
|
+
peephole_optimizations: None | (
|
|
140
|
+
Iterable[type[PeepholeOptimizationStmtBase] | type[PeepholeOptimizationExprBase]]
|
|
141
|
+
) = None, # pylint:disable=line-too-long
|
|
142
|
+
must_struct: set[str] | None = None,
|
|
143
|
+
variable_kb: KnowledgeBase | None = None,
|
|
144
|
+
reset_variable_names=False,
|
|
145
|
+
rewrite_ites_to_diamonds=True,
|
|
146
|
+
cache: DecompilationCache | None = None,
|
|
147
|
+
mode: ClinicMode = ClinicMode.DECOMPILE,
|
|
148
|
+
sp_shift: int = 0,
|
|
149
|
+
inline_functions: set[Function] | None = None,
|
|
150
|
+
inlined_counts: dict[int, int] | None = None,
|
|
151
|
+
inlining_parents: set[int] | None = None,
|
|
152
|
+
vvar_id_start: int = 0,
|
|
153
|
+
optimization_scratch: dict[str, Any] | None = None,
|
|
154
|
+
desired_variables: set[str] | None = None,
|
|
155
|
+
force_loop_single_exit: bool = True,
|
|
156
|
+
refine_loops_with_single_successor: bool = False,
|
|
157
|
+
complete_successors: bool = False,
|
|
158
|
+
max_type_constraints: int = 100_000,
|
|
159
|
+
type_constraint_set_degradation_threshold: int = 150,
|
|
160
|
+
ail_graph: networkx.DiGraph | None = None,
|
|
161
|
+
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
|
|
162
|
+
start_stage: ClinicStage | None = ClinicStage.INITIALIZATION,
|
|
163
|
+
end_stage: ClinicStage | None = None,
|
|
164
|
+
skip_stages: tuple[ClinicStage, ...] = (),
|
|
165
|
+
notes: dict[str, DecompilationNote] | None = None,
|
|
166
|
+
static_vvars: dict | None = None,
|
|
167
|
+
static_buffers: dict | None = None,
|
|
168
|
+
semvar_naming: bool = True,
|
|
169
|
+
):
|
|
170
|
+
if not func.normalized and mode == ClinicMode.DECOMPILE:
|
|
171
|
+
raise ValueError("Decompilation must work on normalized function graphs.")
|
|
172
|
+
|
|
173
|
+
self.function = func
|
|
174
|
+
|
|
175
|
+
self.graph = None
|
|
176
|
+
self.cc_graph: networkx.DiGraph | None = None
|
|
177
|
+
self.unoptimized_graph: networkx.DiGraph | None = None
|
|
178
|
+
self.arg_list = None
|
|
179
|
+
self.arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None
|
|
180
|
+
self.func_args = None
|
|
181
|
+
self.func_ret_var = SimVariable(0, "__retvar", "__retvar")
|
|
182
|
+
self.variable_kb = variable_kb
|
|
183
|
+
self.externs: set[SimMemoryVariable] = set()
|
|
184
|
+
self.data_refs: dict[int, list[DataRefDesc]] = {} # data address to data reference description
|
|
185
|
+
self.optimization_scratch = optimization_scratch if optimization_scratch is not None else {}
|
|
186
|
+
|
|
187
|
+
self._func_graph: networkx.DiGraph | None = None
|
|
188
|
+
self._init_ail_graph = ail_graph
|
|
189
|
+
self._init_arg_vvars = arg_vvars
|
|
190
|
+
self._start_stage = start_stage if start_stage is not None else ClinicStage.INITIALIZATION
|
|
191
|
+
self._end_stage = end_stage if end_stage is not None else max(ClinicStage.__members__.values())
|
|
192
|
+
self._skip_stages = skip_stages
|
|
193
|
+
|
|
194
|
+
self._blocks_by_addr_and_size = {}
|
|
195
|
+
self.entry_node_addr: tuple[int, int | None] = self.function.addr, None
|
|
196
|
+
|
|
197
|
+
self._fold_callexprs_into_conditions = fold_callexprs_into_conditions
|
|
198
|
+
self._insert_labels = insert_labels
|
|
199
|
+
self._remove_dead_memdefs = remove_dead_memdefs
|
|
200
|
+
self._exception_edges = exception_edges
|
|
201
|
+
self._sp_tracker_track_memory = sp_tracker_track_memory
|
|
202
|
+
self._cfg: CFGModel | None = cfg
|
|
203
|
+
self.peephole_optimizations = peephole_optimizations
|
|
204
|
+
self._must_struct = must_struct
|
|
205
|
+
self._reset_variable_names = reset_variable_names
|
|
206
|
+
self._rewrite_ites_to_diamonds = rewrite_ites_to_diamonds
|
|
207
|
+
self.reaching_definitions: ReachingDefinitionsAnalysis | None = None
|
|
208
|
+
self._cache = cache
|
|
209
|
+
self._mode = mode
|
|
210
|
+
self._max_type_constraints = max_type_constraints
|
|
211
|
+
self._type_constraint_set_degradation_threshold = type_constraint_set_degradation_threshold
|
|
212
|
+
self.vvar_id_start = vvar_id_start
|
|
213
|
+
self.vvar_to_vvar: dict[int, int] | None = None
|
|
214
|
+
# during SSA conversion, we create secondary stack variables because they overlap and are larger than the
|
|
215
|
+
# actual stack variables. these secondary stack variables can be safely eliminated if not used by anything.
|
|
216
|
+
self.secondary_stackvars: set[int] = set()
|
|
217
|
+
|
|
218
|
+
self.notes = notes if notes is not None else {}
|
|
219
|
+
self.static_vvars = static_vvars if static_vvars is not None else {}
|
|
220
|
+
self.static_buffers = static_buffers if static_buffers is not None else {}
|
|
221
|
+
self._semvar_naming = semvar_naming
|
|
222
|
+
|
|
223
|
+
if not semvar_naming and ClinicStage.SEMANTIC_VARIABLE_NAMING not in self._skip_stages:
|
|
224
|
+
self._skip_stages += (ClinicStage.SEMANTIC_VARIABLE_NAMING,)
|
|
225
|
+
|
|
226
|
+
#
|
|
227
|
+
# intermediate variables used during decompilation
|
|
228
|
+
#
|
|
229
|
+
|
|
230
|
+
self._ail_graph: networkx.DiGraph = None # type:ignore
|
|
231
|
+
self._spt = None
|
|
232
|
+
# cached block-level reaching definition analysis results and propagator results
|
|
233
|
+
self._block_simplification_cache: dict[ailment.Block, NamedTuple] | None = {}
|
|
234
|
+
self._preserve_vvar_ids: set[int] = set()
|
|
235
|
+
self._type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] = []
|
|
236
|
+
|
|
237
|
+
# inlining help
|
|
238
|
+
self._sp_shift = sp_shift
|
|
239
|
+
self._max_stack_depth = 0
|
|
240
|
+
self._inline_functions = inline_functions if inline_functions else set()
|
|
241
|
+
self._inlined_counts = {} if inlined_counts is None else inlined_counts
|
|
242
|
+
self._inlining_parents = inlining_parents or ()
|
|
243
|
+
self._desired_variables = desired_variables
|
|
244
|
+
self._force_loop_single_exit = force_loop_single_exit
|
|
245
|
+
self._refine_loops_with_single_successor = refine_loops_with_single_successor
|
|
246
|
+
self._complete_successors = complete_successors
|
|
247
|
+
|
|
248
|
+
self._register_save_areas_removed: bool = False
|
|
249
|
+
self.edges_to_remove: list[tuple[tuple[int, int | None], tuple[int, int | None]]] = []
|
|
250
|
+
self.copied_var_ids: set[int] = set()
|
|
251
|
+
|
|
252
|
+
self._new_block_addrs: set[int] = set()
|
|
253
|
+
|
|
254
|
+
# a reference to the Typehoon type inference engine; useful for debugging and loading stats post decompilation
|
|
255
|
+
self.typehoon: Typehoon | None = None
|
|
256
|
+
|
|
257
|
+
# sanity checks
|
|
258
|
+
if not self.kb.functions:
|
|
259
|
+
l.warning("No function is available in kb.functions. It will lead to a suboptimal conversion result.")
|
|
260
|
+
|
|
261
|
+
if optimization_passes is not None:
|
|
262
|
+
self._optimization_passes = optimization_passes
|
|
263
|
+
else:
|
|
264
|
+
self._optimization_passes = []
|
|
265
|
+
|
|
266
|
+
self.stack_items: dict[int, StackItem] = {}
|
|
267
|
+
if self.project.arch.call_pushes_ret:
|
|
268
|
+
self.stack_items[0] = StackItem(0, self.project.arch.bytes, "ret_addr", StackItemType.RET_ADDR)
|
|
269
|
+
|
|
270
|
+
# Set up the function graph according to configurations
|
|
271
|
+
self._set_function_graph()
|
|
272
|
+
|
|
273
|
+
if self._mode == ClinicMode.DECOMPILE:
|
|
274
|
+
self._analyze_for_decompiling()
|
|
275
|
+
if self._end_stage >= ClinicStage.MAKE_CALLSITES:
|
|
276
|
+
self._constrain_callee_prototypes()
|
|
277
|
+
elif self._mode == ClinicMode.COLLECT_DATA_REFS:
|
|
278
|
+
self._analyze_for_data_refs()
|
|
279
|
+
else:
|
|
280
|
+
raise TypeError(f"Unsupported analysis mode {self._mode}")
|
|
281
|
+
|
|
282
|
+
#
|
|
283
|
+
# Public methods
|
|
284
|
+
#
|
|
285
|
+
|
|
286
|
+
def block(self, addr, size):
|
|
287
|
+
"""
|
|
288
|
+
Get the converted block at the given specific address with the given size.
|
|
289
|
+
|
|
290
|
+
:param int addr:
|
|
291
|
+
:param int size:
|
|
292
|
+
:return:
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
try:
|
|
296
|
+
return self._blocks_by_addr_and_size[(addr, size)] if self._blocks_by_addr_and_size is not None else None
|
|
297
|
+
except KeyError:
|
|
298
|
+
return None
|
|
299
|
+
|
|
300
|
+
def dbg_repr(self):
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
:return:
|
|
304
|
+
"""
|
|
305
|
+
assert self.graph is not None
|
|
306
|
+
|
|
307
|
+
s = ""
|
|
308
|
+
|
|
309
|
+
for block in sorted(self.graph.nodes(), key=lambda x: x.addr):
|
|
310
|
+
s += str(block) + "\n\n"
|
|
311
|
+
|
|
312
|
+
return s
|
|
313
|
+
|
|
314
|
+
#
|
|
315
|
+
# Private methods
|
|
316
|
+
#
|
|
317
|
+
|
|
318
|
+
def _analyze_for_decompiling(self):
|
|
319
|
+
# initialize the AIL conversion manager
|
|
320
|
+
self._ail_manager = ailment.Manager(arch=self.project.arch)
|
|
321
|
+
|
|
322
|
+
ail_graph = self._init_ail_graph if self._init_ail_graph is not None else self._decompilation_graph_recovery()
|
|
323
|
+
if not ail_graph:
|
|
324
|
+
return
|
|
325
|
+
if self._start_stage <= ClinicStage.INITIALIZATION:
|
|
326
|
+
ail_graph = self._decompilation_fixups(ail_graph)
|
|
327
|
+
|
|
328
|
+
if self._inline_functions:
|
|
329
|
+
self._max_stack_depth += self.calculate_stack_depth()
|
|
330
|
+
ail_graph = self._inline_child_functions(ail_graph)
|
|
331
|
+
|
|
332
|
+
ail_graph = self._decompilation_simplifications(ail_graph)
|
|
333
|
+
|
|
334
|
+
if self._desired_variables:
|
|
335
|
+
ail_graph = self._slice_variables(ail_graph)
|
|
336
|
+
self.graph = ail_graph
|
|
337
|
+
|
|
338
|
+
def _decompilation_graph_recovery(self):
|
|
339
|
+
is_pcode_arch = ":" in self.project.arch.name
|
|
340
|
+
|
|
341
|
+
# Remove alignment blocks
|
|
342
|
+
self._update_progress(5.0, text="Removing alignment blocks")
|
|
343
|
+
self._remove_alignment_blocks()
|
|
344
|
+
|
|
345
|
+
# if the graph is empty, don't continue
|
|
346
|
+
if not self._func_graph:
|
|
347
|
+
return None
|
|
348
|
+
|
|
349
|
+
# Make sure calling conventions of all functions that the current function calls have been recovered
|
|
350
|
+
if not is_pcode_arch:
|
|
351
|
+
self._update_progress(10.0, text="Recovering calling conventions")
|
|
352
|
+
self._recover_calling_conventions()
|
|
353
|
+
|
|
354
|
+
# Convert VEX blocks to AIL blocks and then simplify them
|
|
355
|
+
|
|
356
|
+
self._update_progress(20.0, text="Converting VEX to AIL")
|
|
357
|
+
self._convert_all()
|
|
358
|
+
|
|
359
|
+
return self._make_ailgraph()
|
|
360
|
+
|
|
361
|
+
def _decompilation_fixups(self, ail_graph):
|
|
362
|
+
is_pcode_arch = ":" in self.project.arch.name
|
|
363
|
+
|
|
364
|
+
self._remove_redundant_jump_blocks(ail_graph)
|
|
365
|
+
# _fix_abnormal_switch_case_heads may re-lift from VEX blocks, so it should be placed as high up as possible
|
|
366
|
+
self._fix_abnormal_switch_case_heads(ail_graph)
|
|
367
|
+
if self._rewrite_ites_to_diamonds:
|
|
368
|
+
self._rewrite_ite_expressions(ail_graph)
|
|
369
|
+
self._remove_redundant_jump_blocks(ail_graph)
|
|
370
|
+
if self._insert_labels:
|
|
371
|
+
self._insert_block_labels(ail_graph)
|
|
372
|
+
|
|
373
|
+
# Run simplification passes
|
|
374
|
+
self._update_progress(22.0, text="Optimizing fresh ailment graph")
|
|
375
|
+
ail_graph = self._run_simplification_passes(ail_graph, OptimizationPassStage.AFTER_AIL_GRAPH_CREATION)
|
|
376
|
+
|
|
377
|
+
# Fix "fake" indirect jumps and calls
|
|
378
|
+
self._update_progress(25.0, text="Analyzing simple indirect jumps")
|
|
379
|
+
ail_graph = self._replace_single_target_indirect_transitions(ail_graph)
|
|
380
|
+
|
|
381
|
+
# Fix tail calls
|
|
382
|
+
self._update_progress(26.0, text="Analyzing tail calls")
|
|
383
|
+
ail_graph = self._replace_tail_jumps_with_calls(ail_graph)
|
|
384
|
+
|
|
385
|
+
# Fix special calls
|
|
386
|
+
self._update_progress(28.0, text="Analyzing special calls")
|
|
387
|
+
ail_graph = self._fix_special_call_calling_conventions(ail_graph)
|
|
388
|
+
|
|
389
|
+
if is_pcode_arch:
|
|
390
|
+
self._update_progress(29.0, text="Recovering calling conventions (AIL mode)")
|
|
391
|
+
self._recover_calling_conventions(func_graph=ail_graph)
|
|
392
|
+
|
|
393
|
+
return self._apply_callsite_prototype_and_calling_convention(ail_graph)
|
|
394
|
+
|
|
395
|
+
def _slice_variables(self, ail_graph: networkx.DiGraph[ailment.Block]) -> networkx.DiGraph[ailment.Block]:
|
|
396
|
+
assert self.variable_kb is not None and self._desired_variables is not None
|
|
397
|
+
|
|
398
|
+
nodes_index = {(n.addr, n.idx): n for n in ail_graph.nodes()}
|
|
399
|
+
|
|
400
|
+
vfm = self.variable_kb.variables.function_managers[self.function.addr]
|
|
401
|
+
for v_name in self._desired_variables:
|
|
402
|
+
v = next(iter(vv for vv in vfm._unified_variables if vv.name == v_name))
|
|
403
|
+
for va in vfm.get_variable_accesses(v):
|
|
404
|
+
assert va.location.block_addr is not None
|
|
405
|
+
assert va.location.stmt_idx is not None
|
|
406
|
+
nodes_index[(va.location.block_addr, va.location.block_idx)].statements[va.location.stmt_idx].tags[
|
|
407
|
+
"keep_in_slice"
|
|
408
|
+
] = True
|
|
409
|
+
|
|
410
|
+
a = TagSlicer(
|
|
411
|
+
self.function,
|
|
412
|
+
graph=ail_graph,
|
|
413
|
+
variable_kb=self.variable_kb,
|
|
414
|
+
)
|
|
415
|
+
if a.out_graph:
|
|
416
|
+
# use the new graph
|
|
417
|
+
ail_graph = a.out_graph
|
|
418
|
+
return ail_graph
|
|
419
|
+
|
|
420
|
+
def _inline_child_functions(self, ail_graph):
|
|
421
|
+
for blk in ail_graph.nodes():
|
|
422
|
+
for idx, stmt in enumerate(blk.statements):
|
|
423
|
+
if isinstance(stmt, ailment.Stmt.Call) and isinstance(stmt.target, ailment.Expr.Const):
|
|
424
|
+
assert self.function._function_manager is not None
|
|
425
|
+
callee = self.function._function_manager.function(stmt.target.value)
|
|
426
|
+
if (
|
|
427
|
+
callee is None
|
|
428
|
+
or callee.addr == self.function.addr
|
|
429
|
+
or callee.addr in self._inlining_parents
|
|
430
|
+
or callee not in self._inline_functions
|
|
431
|
+
or callee.is_plt
|
|
432
|
+
or callee.is_simprocedure
|
|
433
|
+
):
|
|
434
|
+
continue
|
|
435
|
+
|
|
436
|
+
ail_graph = self._inline_call(ail_graph, blk, idx, callee)
|
|
437
|
+
return ail_graph
|
|
438
|
+
|
|
439
|
+
@staticmethod
|
|
440
|
+
def _inline_fix_block_phi_stmts(block: ailment.Block, new_block_idx: int) -> None:
|
|
441
|
+
# update the source block ID of all phi variables
|
|
442
|
+
for idx, stmt in enumerate(block.statements):
|
|
443
|
+
if is_phi_assignment(stmt):
|
|
444
|
+
new_src_and_vvars: list[tuple[tuple[int, int | None], VirtualVariable | None]] = [
|
|
445
|
+
((src_block_addr, new_block_idx), vvar) for (src_block_addr, _), vvar in stmt.src.src_and_vvars
|
|
446
|
+
]
|
|
447
|
+
new_src = ailment.Expr.Phi(stmt.src.idx, stmt.src.bits, new_src_and_vvars, **stmt.src.tags)
|
|
448
|
+
new_stmt = ailment.Stmt.Assignment(stmt.idx, stmt.dst, new_src, **stmt.tags)
|
|
449
|
+
block.statements[idx] = new_stmt
|
|
450
|
+
|
|
451
|
+
def _inline_call(self, ail_graph: networkx.DiGraph, caller_block: ailment.Block, call_idx: int, callee: Function):
|
|
452
|
+
callee_clinic = self.project.analyses.Clinic(
|
|
453
|
+
callee,
|
|
454
|
+
mode=ClinicMode.DECOMPILE,
|
|
455
|
+
inline_functions=self._inline_functions,
|
|
456
|
+
inlining_parents=(*self._inlining_parents, self.function.addr),
|
|
457
|
+
inlined_counts=self._inlined_counts,
|
|
458
|
+
optimization_passes=[StackCanarySimplifier],
|
|
459
|
+
sp_shift=self._max_stack_depth,
|
|
460
|
+
vvar_id_start=self.vvar_id_start,
|
|
461
|
+
fail_fast=self._fail_fast, # type: ignore
|
|
462
|
+
)
|
|
463
|
+
self.vvar_id_start = callee_clinic.vvar_id_start + 1
|
|
464
|
+
self._max_stack_depth = callee_clinic._max_stack_depth
|
|
465
|
+
callee_graph = callee_clinic.copy_graph()
|
|
466
|
+
|
|
467
|
+
# uniquely mark all the blocks in case of duplicates (e.g., foo(); foo();)
|
|
468
|
+
self._inlined_counts.setdefault(callee.addr, 0)
|
|
469
|
+
block_idx = self._inlined_counts[callee.addr]
|
|
470
|
+
for blk in callee_graph.nodes():
|
|
471
|
+
blk.idx = block_idx
|
|
472
|
+
self._inline_fix_block_phi_stmts(blk, block_idx)
|
|
473
|
+
self._inlined_counts[callee.addr] += 1
|
|
474
|
+
|
|
475
|
+
# figure out where the callee should start at and return to
|
|
476
|
+
callee_start = next(n for n in callee_graph if n.addr == callee.addr)
|
|
477
|
+
caller_successors = list(ail_graph.out_edges(caller_block, data=True))
|
|
478
|
+
assert len(caller_successors) == 1
|
|
479
|
+
caller_successor = caller_successors[0][1]
|
|
480
|
+
ail_graph.remove_edge(caller_block, caller_successor)
|
|
481
|
+
|
|
482
|
+
# update all callee return nodes with caller successor
|
|
483
|
+
ail_graph = networkx.union(ail_graph, callee_graph)
|
|
484
|
+
for blk in callee_graph.nodes():
|
|
485
|
+
for idx, stmt in enumerate(list(blk.statements)):
|
|
486
|
+
if isinstance(stmt, ailment.Stmt.Return):
|
|
487
|
+
# replace the return statement with an assignment to the return register
|
|
488
|
+
blk.statements.pop(idx)
|
|
489
|
+
|
|
490
|
+
if stmt.ret_exprs and self.project.arch.ret_offset is not None:
|
|
491
|
+
assign_to_retreg = ailment.Stmt.Assignment(
|
|
492
|
+
self._ail_manager.next_atom(),
|
|
493
|
+
ailment.Expr.Register(
|
|
494
|
+
self._ail_manager.next_atom(),
|
|
495
|
+
None,
|
|
496
|
+
self.project.arch.ret_offset,
|
|
497
|
+
self.project.arch.bits,
|
|
498
|
+
),
|
|
499
|
+
stmt.ret_exprs[0],
|
|
500
|
+
**stmt.tags,
|
|
501
|
+
)
|
|
502
|
+
blk.statements.insert(idx, assign_to_retreg)
|
|
503
|
+
idx += 1
|
|
504
|
+
ail_graph.add_edge(blk, caller_successor)
|
|
505
|
+
break
|
|
506
|
+
|
|
507
|
+
# update the call edge
|
|
508
|
+
# first, remove the call statement. this is a type error but will be resolved later
|
|
509
|
+
caller_block.statements[call_idx] = None # type: ignore
|
|
510
|
+
if (
|
|
511
|
+
isinstance(caller_block.statements[call_idx - 2], ailment.Stmt.Store)
|
|
512
|
+
and caller_block.statements[call_idx - 2].data.value == caller_successor.addr
|
|
513
|
+
):
|
|
514
|
+
# don't push the return address
|
|
515
|
+
caller_block.statements.pop(call_idx - 5) # t6 = rsp<8>
|
|
516
|
+
caller_block.statements.pop(call_idx - 5) # t5 = (t6 - 0x8<64>)
|
|
517
|
+
caller_block.statements.pop(call_idx - 5) # rsp<8> = t5
|
|
518
|
+
caller_block.statements.pop(
|
|
519
|
+
call_idx - 5
|
|
520
|
+
) # STORE(addr=t5, data=0x40121b<64>, size=8, endness=Iend_LE, guard=None)
|
|
521
|
+
caller_block.statements.pop(call_idx - 5) # t7 = (t5 - 0x80<64>) <- wtf is this??
|
|
522
|
+
elif (
|
|
523
|
+
isinstance(caller_block.statements[call_idx - 1], ailment.Stmt.Store)
|
|
524
|
+
and caller_block.statements[call_idx - 1].addr.base == "stack_base"
|
|
525
|
+
and caller_block.statements[call_idx - 1].data.value == caller_successor.addr
|
|
526
|
+
):
|
|
527
|
+
caller_block.statements.pop(call_idx - 1) # s_10 =L 0x401225<64><8>
|
|
528
|
+
|
|
529
|
+
# update caller_block to setup parameters
|
|
530
|
+
if callee_clinic.arg_vvars:
|
|
531
|
+
for arg_idx in sorted(callee_clinic.arg_vvars.keys()):
|
|
532
|
+
param_vvar, reg_arg = callee_clinic.arg_vvars[arg_idx]
|
|
533
|
+
if isinstance(reg_arg, SimRegisterVariable):
|
|
534
|
+
reg_offset = reg_arg.reg
|
|
535
|
+
stmt = ailment.Stmt.Assignment(
|
|
536
|
+
self._ail_manager.next_atom(),
|
|
537
|
+
param_vvar,
|
|
538
|
+
ailment.Expr.Register(
|
|
539
|
+
self._ail_manager.next_atom(),
|
|
540
|
+
None,
|
|
541
|
+
reg_offset,
|
|
542
|
+
reg_arg.bits,
|
|
543
|
+
ins_addr=caller_block.addr + caller_block.original_size,
|
|
544
|
+
),
|
|
545
|
+
ins_addr=caller_block.addr + caller_block.original_size,
|
|
546
|
+
)
|
|
547
|
+
caller_block.statements.append(stmt)
|
|
548
|
+
else:
|
|
549
|
+
raise NotImplementedError("Unsupported parameter type")
|
|
550
|
+
|
|
551
|
+
caller_block.statements = [s for s in caller_block.statements if s is not None]
|
|
552
|
+
|
|
553
|
+
ail_graph.add_edge(caller_block, callee_start)
|
|
554
|
+
|
|
555
|
+
return ail_graph
|
|
556
|
+
|
|
557
|
+
def calculate_stack_depth(self):
|
|
558
|
+
# we need to reserve space for our own stack
|
|
559
|
+
spt = self._track_stack_pointers()
|
|
560
|
+
stack_offsets = spt.offsets_for(self.project.arch.sp_offset)
|
|
561
|
+
if max(stack_offsets) > 2 ** (self.project.arch.bits - 1):
|
|
562
|
+
# why is this unsigned...
|
|
563
|
+
depth = min(s for s in stack_offsets if s > 2 ** (self.project.arch.bits - 1)) - 2**self.project.arch.bits
|
|
564
|
+
else:
|
|
565
|
+
depth = min(stack_offsets)
|
|
566
|
+
|
|
567
|
+
if spt.inconsistent_for(self.project.arch.sp_offset):
|
|
568
|
+
l.warning("Inconsistency found during stack pointer tracking. Stack depth may be incorrect.")
|
|
569
|
+
depth -= 0x1000
|
|
570
|
+
|
|
571
|
+
return depth
|
|
572
|
+
|
|
573
|
+
def _decompilation_simplifications(self, ail_graph):
|
|
574
|
+
self.arg_vvars = self._init_arg_vvars if self._init_arg_vvars is not None else {}
|
|
575
|
+
self.func_args = {arg_vvar for arg_vvar, _ in self.arg_vvars.values()}
|
|
576
|
+
self._ail_graph = ail_graph
|
|
577
|
+
|
|
578
|
+
stages = {
|
|
579
|
+
ClinicStage.MAKE_RETURN_SITES: self._stage_make_return_sites,
|
|
580
|
+
ClinicStage.MAKE_ARGUMENT_LIST: self._stage_make_function_argument_list,
|
|
581
|
+
ClinicStage.PRE_SSA_LEVEL0_FIXUPS: self._stage_pre_ssa_level0_fixups,
|
|
582
|
+
ClinicStage.SSA_LEVEL0_TRANSFORMATION: self._stage_transform_to_ssa_level0,
|
|
583
|
+
ClinicStage.CONSTANT_PROPAGATION: self._stage_constant_propagation,
|
|
584
|
+
ClinicStage.TRACK_STACK_POINTERS: self._stage_track_stack_pointers,
|
|
585
|
+
ClinicStage.PRE_SSA_LEVEL1_SIMPLIFICATIONS: self._stage_pre_ssa_level1_simplifications,
|
|
586
|
+
ClinicStage.SSA_LEVEL1_TRANSFORMATION: self._stage_transform_to_ssa_level1,
|
|
587
|
+
ClinicStage.POST_SSA_LEVEL1_SIMPLIFICATIONS: self._stage_post_ssa_level1_simplifications,
|
|
588
|
+
ClinicStage.MAKE_CALLSITES: self._stage_make_function_callsites,
|
|
589
|
+
ClinicStage.POST_CALLSITES: self._stage_post_callsite_simplifications,
|
|
590
|
+
ClinicStage.RECOVER_VARIABLES: self._stage_recover_variables,
|
|
591
|
+
ClinicStage.SEMANTIC_VARIABLE_NAMING: self._stage_semantic_variable_naming,
|
|
592
|
+
ClinicStage.COLLECT_EXTERNS: self._stage_collect_externs,
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
for stage in sorted(stages):
|
|
596
|
+
if stage < self._start_stage or stage > self._end_stage or stage in self._skip_stages:
|
|
597
|
+
continue
|
|
598
|
+
stages[stage]()
|
|
599
|
+
|
|
600
|
+
# remove empty nodes from the graph
|
|
601
|
+
self._ail_graph = self.remove_empty_nodes(self._ail_graph)
|
|
602
|
+
# note that there are still edges to remove before we can structure this graph!
|
|
603
|
+
|
|
604
|
+
self.cc_graph = self.copy_graph(self._ail_graph)
|
|
605
|
+
return self._ail_graph
|
|
606
|
+
|
|
607
|
+
def _stage_make_return_sites(self) -> None:
|
|
608
|
+
self._update_progress(30.0, text="Making return sites")
|
|
609
|
+
if self.function.prototype is None or not isinstance(self.function.prototype.returnty, SimTypeBottom):
|
|
610
|
+
self._ail_graph = self._make_returns(self._ail_graph)
|
|
611
|
+
self._ail_graph = self._run_simplification_passes(
|
|
612
|
+
self._ail_graph, stage=OptimizationPassStage.BEFORE_SSA_LEVEL0_TRANSFORMATION
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
def _stage_make_function_argument_list(self) -> None:
|
|
616
|
+
self._update_progress(33.0, text="Making argument list")
|
|
617
|
+
self.arg_list = self._make_argument_list()
|
|
618
|
+
self.arg_vvars = self._create_function_argument_vvars(self.arg_list)
|
|
619
|
+
self.func_args = {arg_vvar for arg_vvar, _ in self.arg_vvars.values()}
|
|
620
|
+
|
|
621
|
+
def _stage_pre_ssa_level0_fixups(self) -> None:
|
|
622
|
+
# duplicate orphaned conditional jump blocks
|
|
623
|
+
self._ail_graph = self._duplicate_orphaned_cond_jumps(self._ail_graph)
|
|
624
|
+
# rewrite jmp_rax function calls
|
|
625
|
+
self._ail_graph = self._rewrite_jump_rax_calls(self._ail_graph)
|
|
626
|
+
|
|
627
|
+
def _stage_transform_to_ssa_level0(self) -> None:
|
|
628
|
+
self._update_progress(35.0, text="Transforming to partial-SSA form (registers)")
|
|
629
|
+
assert self.func_args is not None
|
|
630
|
+
self._ail_graph = self._transform_to_ssa_level0(self._ail_graph, self.func_args)
|
|
631
|
+
|
|
632
|
+
def _stage_constant_propagation(self) -> None:
|
|
633
|
+
# full-function constant-only propagation
|
|
634
|
+
self._update_progress(36.0, text="Constant propagation")
|
|
635
|
+
self._simplify_function(
|
|
636
|
+
self._ail_graph,
|
|
637
|
+
remove_dead_memdefs=False,
|
|
638
|
+
unify_variables=False,
|
|
639
|
+
narrow_expressions=False,
|
|
640
|
+
only_consts=True,
|
|
641
|
+
fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
|
|
642
|
+
max_iterations=1,
|
|
643
|
+
)
|
|
644
|
+
|
|
645
|
+
def _stage_track_stack_pointers(self) -> None:
|
|
646
|
+
self._spt = self._track_stack_pointers()
|
|
647
|
+
|
|
648
|
+
def _stage_transform_to_ssa_level1(self) -> None:
|
|
649
|
+
self._update_progress(37.0, text="Transforming to partial-SSA form (stack variables)")
|
|
650
|
+
# rewrite (qualified) stack variables into SSA form
|
|
651
|
+
assert self.func_args is not None
|
|
652
|
+
self._ail_graph = self._transform_to_ssa_level1(self._ail_graph, self.func_args)
|
|
653
|
+
|
|
654
|
+
# Run simplification passes
|
|
655
|
+
self._update_progress(49.0, text="Running simplifications 1.5")
|
|
656
|
+
self._ail_graph = self._run_simplification_passes(
|
|
657
|
+
self._ail_graph, stage=OptimizationPassStage.AFTER_SSA_LEVEL1_TRANSFORMATION
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
# register save area has been removed at this point - we should no longer use callee-saved registers in RDA
|
|
661
|
+
self._register_save_areas_removed = True
|
|
662
|
+
# clear the cached RDA result
|
|
663
|
+
self.reaching_definitions = None
|
|
664
|
+
|
|
665
|
+
def _stage_pre_ssa_level1_simplifications(self) -> None:
|
|
666
|
+
# Simplify blocks
|
|
667
|
+
# we never remove dead memory definitions before making callsites. otherwise stack arguments may go missing
|
|
668
|
+
# before they are recognized as stack arguments.
|
|
669
|
+
self._update_progress(38.0, text="Simplifying blocks 1")
|
|
670
|
+
self._ail_graph = self._simplify_blocks(
|
|
671
|
+
self._ail_graph,
|
|
672
|
+
stack_pointer_tracker=self._spt,
|
|
673
|
+
cache=self._block_simplification_cache,
|
|
674
|
+
preserve_vvar_ids=self._preserve_vvar_ids,
|
|
675
|
+
type_hints=self._type_hints,
|
|
676
|
+
)
|
|
677
|
+
self._rewrite_alloca(self._ail_graph)
|
|
678
|
+
|
|
679
|
+
# Run simplification passes
|
|
680
|
+
self._update_progress(40.0, text="Running simplifications 1")
|
|
681
|
+
self._ail_graph = self._run_simplification_passes(
|
|
682
|
+
self._ail_graph,
|
|
683
|
+
stack_pointer_tracker=self._spt,
|
|
684
|
+
stack_items=self.stack_items,
|
|
685
|
+
stage=OptimizationPassStage.AFTER_SINGLE_BLOCK_SIMPLIFICATION,
|
|
686
|
+
)
|
|
687
|
+
|
|
688
|
+
# Simplify the entire function for the first time
|
|
689
|
+
self._update_progress(45.0, text="Simplifying function 1")
|
|
690
|
+
self._simplify_function(
|
|
691
|
+
self._ail_graph,
|
|
692
|
+
remove_dead_memdefs=False,
|
|
693
|
+
unify_variables=False,
|
|
694
|
+
narrow_expressions=False,
|
|
695
|
+
fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
|
|
696
|
+
arg_vvars=self.arg_vvars,
|
|
697
|
+
)
|
|
698
|
+
|
|
699
|
+
# Run simplification passes again. there might be more chances for peephole optimizations after function-level
|
|
700
|
+
# simplification
|
|
701
|
+
self._update_progress(48.0, text="Simplifying blocks 2")
|
|
702
|
+
self._ail_graph = self._simplify_blocks(
|
|
703
|
+
self._ail_graph,
|
|
704
|
+
stack_pointer_tracker=self._spt,
|
|
705
|
+
cache=self._block_simplification_cache,
|
|
706
|
+
preserve_vvar_ids=self._preserve_vvar_ids,
|
|
707
|
+
type_hints=self._type_hints,
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
# Run simplification passes
|
|
711
|
+
self._update_progress(49.0, text="Running simplifications 2")
|
|
712
|
+
self._ail_graph = self._run_simplification_passes(
|
|
713
|
+
self._ail_graph, stage=OptimizationPassStage.BEFORE_SSA_LEVEL1_TRANSFORMATION
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
def _stage_post_ssa_level1_simplifications(self) -> None:
|
|
717
|
+
# Rust-specific; only call this on Rust binaries when we can identify language and compiler
|
|
718
|
+
self._ail_graph = self._rewrite_rust_probestack_call(self._ail_graph)
|
|
719
|
+
# Windows-specific
|
|
720
|
+
self._ail_graph = self._rewrite_windows_chkstk_call(self._ail_graph)
|
|
721
|
+
|
|
722
|
+
def _stage_make_function_callsites(self) -> None:
|
|
723
|
+
assert self.func_args is not None
|
|
724
|
+
|
|
725
|
+
# Make call-sites
|
|
726
|
+
self._update_progress(50.0, text="Making callsites")
|
|
727
|
+
_, stackarg_offsets, removed_vvar_ids = self._make_callsites(
|
|
728
|
+
self._ail_graph, self.func_args, stack_pointer_tracker=self._spt, preserve_vvar_ids=self._preserve_vvar_ids
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
# Run simplification passes
|
|
732
|
+
self._update_progress(53.0, text="Running simplifications 2")
|
|
733
|
+
self._ail_graph = self._run_simplification_passes(
|
|
734
|
+
self._ail_graph, stage=OptimizationPassStage.AFTER_MAKING_CALLSITES
|
|
735
|
+
)
|
|
736
|
+
|
|
737
|
+
# Simplify the entire function for the second time
|
|
738
|
+
self._update_progress(55.0, text="Simplifying function 2")
|
|
739
|
+
self._simplify_function(
|
|
740
|
+
self._ail_graph,
|
|
741
|
+
remove_dead_memdefs=self._remove_dead_memdefs,
|
|
742
|
+
stack_arg_offsets=stackarg_offsets,
|
|
743
|
+
unify_variables=True,
|
|
744
|
+
narrow_expressions=True,
|
|
745
|
+
fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
|
|
746
|
+
removed_vvar_ids=removed_vvar_ids,
|
|
747
|
+
arg_vvars=self.arg_vvars,
|
|
748
|
+
preserve_vvar_ids=self._preserve_vvar_ids,
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
# After global optimization, there might be more chances for peephole optimizations.
|
|
752
|
+
# Simplify blocks for the second time
|
|
753
|
+
self._update_progress(60.0, text="Simplifying blocks 3")
|
|
754
|
+
self._ail_graph = self._simplify_blocks(
|
|
755
|
+
self._ail_graph,
|
|
756
|
+
stack_pointer_tracker=self._spt,
|
|
757
|
+
cache=self._block_simplification_cache,
|
|
758
|
+
preserve_vvar_ids=self._preserve_vvar_ids,
|
|
759
|
+
type_hints=self._type_hints,
|
|
760
|
+
)
|
|
761
|
+
|
|
762
|
+
# Run simplification passes
|
|
763
|
+
self._update_progress(65.0, text="Running simplifications 3")
|
|
764
|
+
self._ail_graph = self._run_simplification_passes(
|
|
765
|
+
self._ail_graph,
|
|
766
|
+
stack_items=self.stack_items,
|
|
767
|
+
stage=OptimizationPassStage.AFTER_GLOBAL_SIMPLIFICATION,
|
|
768
|
+
arg_vvars=self.arg_vvars,
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
# Simplify the entire function for the third time
|
|
772
|
+
self._update_progress(70.0, text="Simplifying function 3")
|
|
773
|
+
self._simplify_function(
|
|
774
|
+
self._ail_graph,
|
|
775
|
+
remove_dead_memdefs=self._remove_dead_memdefs,
|
|
776
|
+
stack_arg_offsets=stackarg_offsets,
|
|
777
|
+
unify_variables=True,
|
|
778
|
+
narrow_expressions=True,
|
|
779
|
+
fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
|
|
780
|
+
arg_vvars=self.arg_vvars,
|
|
781
|
+
preserve_vvar_ids=self._preserve_vvar_ids,
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
self._update_progress(75.0, text="Simplifying blocks 4")
|
|
785
|
+
self._ail_graph = self._simplify_blocks(
|
|
786
|
+
self._ail_graph,
|
|
787
|
+
stack_pointer_tracker=self._spt,
|
|
788
|
+
cache=self._block_simplification_cache,
|
|
789
|
+
preserve_vvar_ids=self._preserve_vvar_ids,
|
|
790
|
+
type_hints=self._type_hints,
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
# Simplify the entire function for the fourth time
|
|
794
|
+
self._update_progress(78.0, text="Simplifying function 4")
|
|
795
|
+
self._simplify_function(
|
|
796
|
+
self._ail_graph,
|
|
797
|
+
remove_dead_memdefs=self._remove_dead_memdefs,
|
|
798
|
+
stack_arg_offsets=stackarg_offsets,
|
|
799
|
+
unify_variables=True,
|
|
800
|
+
narrow_expressions=True,
|
|
801
|
+
fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
|
|
802
|
+
arg_vvars=self.arg_vvars,
|
|
803
|
+
preserve_vvar_ids=self._preserve_vvar_ids,
|
|
804
|
+
)
|
|
805
|
+
|
|
806
|
+
def _stage_post_callsite_simplifications(self) -> None:
|
|
807
|
+
self.arg_list = []
|
|
808
|
+
self.vvar_to_vvar = {}
|
|
809
|
+
self.copied_var_ids = set()
|
|
810
|
+
|
|
811
|
+
self._update_progress(79.0, text="Running simplifications 4")
|
|
812
|
+
self._ail_graph = self._run_simplification_passes(
|
|
813
|
+
self._ail_graph, stack_items=self.stack_items, stage=OptimizationPassStage.BEFORE_VARIABLE_RECOVERY
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
assert self.arg_vvars is not None
|
|
817
|
+
|
|
818
|
+
# update arg_list
|
|
819
|
+
for idx in sorted(self.arg_vvars):
|
|
820
|
+
self.arg_list.append(self.arg_vvars[idx][1])
|
|
821
|
+
|
|
822
|
+
# Get virtual variable mapping that can de-phi the SSA representation
|
|
823
|
+
self.vvar_to_vvar, self.copied_var_ids = self._collect_dephi_vvar_mapping_and_rewrite_blocks(
|
|
824
|
+
self._ail_graph, self.arg_vvars
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
def _stage_recover_variables(self) -> None:
|
|
828
|
+
assert self.arg_list is not None and self.arg_vvars is not None and self.vvar_to_vvar is not None
|
|
829
|
+
|
|
830
|
+
# Recover variables on AIL blocks
|
|
831
|
+
self._update_progress(80.0, text="Recovering variables")
|
|
832
|
+
variable_kb = self._recover_and_link_variables(
|
|
833
|
+
self._ail_graph, self.arg_list, self.arg_vvars, self.vvar_to_vvar, self._type_hints
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
# Run simplification passes
|
|
837
|
+
self._update_progress(85.0, text="Running simplifications 4")
|
|
838
|
+
self._ail_graph = self._run_simplification_passes(
|
|
839
|
+
self._ail_graph,
|
|
840
|
+
stage=OptimizationPassStage.AFTER_VARIABLE_RECOVERY,
|
|
841
|
+
avoid_vvar_ids=self.copied_var_ids,
|
|
842
|
+
)
|
|
843
|
+
|
|
844
|
+
# Make function prototype
|
|
845
|
+
self._update_progress(90.0, text="Making function prototype")
|
|
846
|
+
self._make_function_prototype(self.arg_list, variable_kb)
|
|
847
|
+
|
|
848
|
+
self.variable_kb = variable_kb
|
|
849
|
+
|
|
850
|
+
def _stage_semantic_variable_naming(self) -> None:
|
|
851
|
+
"""
|
|
852
|
+
Apply semantic-based variable naming.
|
|
853
|
+
|
|
854
|
+
This stage analyzes the AIL graph for semantic patterns and renames variables accordingly.
|
|
855
|
+
"""
|
|
856
|
+
|
|
857
|
+
if self.variable_kb is None:
|
|
858
|
+
l.debug("variable_kb is None, skipping semantic variable naming")
|
|
859
|
+
return
|
|
860
|
+
|
|
861
|
+
self._update_progress(91.0, text="Applying semantic variable naming")
|
|
862
|
+
|
|
863
|
+
# Get the variable manager for this function
|
|
864
|
+
var_manager = self.variable_kb.variables[self.function.addr]
|
|
865
|
+
|
|
866
|
+
# Find the entry node
|
|
867
|
+
entry_node: ailment.Block | None = None
|
|
868
|
+
for node in self._ail_graph:
|
|
869
|
+
if (node.addr, node.idx) == self.entry_node_addr:
|
|
870
|
+
entry_node = node
|
|
871
|
+
break
|
|
872
|
+
|
|
873
|
+
assert entry_node is not None
|
|
874
|
+
|
|
875
|
+
# Run all semantic naming patterns via the orchestrator
|
|
876
|
+
orchestrator = SemanticNamingOrchestrator(self._ail_graph, var_manager, self.kb.functions, entry_node)
|
|
877
|
+
var_name_mapping = orchestrator.analyze()
|
|
878
|
+
l.debug("Semantic naming renamed %d variables", len(var_name_mapping))
|
|
879
|
+
|
|
880
|
+
def _stage_collect_externs(self) -> None:
|
|
881
|
+
self.externs = self._collect_externs(self._ail_graph, self.variable_kb)
|
|
882
|
+
|
|
883
|
+
def _analyze_for_data_refs(self):
|
|
884
|
+
# Remove alignment blocks
|
|
885
|
+
self._update_progress(5.0, text="Removing alignment blocks")
|
|
886
|
+
self._remove_alignment_blocks()
|
|
887
|
+
|
|
888
|
+
# if the graph is empty, don't continue
|
|
889
|
+
if not self._func_graph:
|
|
890
|
+
return
|
|
891
|
+
|
|
892
|
+
# initialize the AIL conversion manager
|
|
893
|
+
self._ail_manager = ailment.Manager(arch=self.project.arch)
|
|
894
|
+
|
|
895
|
+
# Track stack pointers
|
|
896
|
+
self._update_progress(15.0, text="Tracking stack pointers")
|
|
897
|
+
spt = self._track_stack_pointers()
|
|
898
|
+
|
|
899
|
+
# Convert VEX blocks to AIL blocks and then simplify them
|
|
900
|
+
|
|
901
|
+
self._update_progress(20.0, text="Converting VEX to AIL")
|
|
902
|
+
self._convert_all()
|
|
903
|
+
|
|
904
|
+
# there must be at least one Load or one Store
|
|
905
|
+
assert self._blocks_by_addr_and_size is not None
|
|
906
|
+
found_load_or_store = False
|
|
907
|
+
for ail_block in self._blocks_by_addr_and_size.values():
|
|
908
|
+
for stmt in ail_block.statements:
|
|
909
|
+
if isinstance(stmt, ailment.Stmt.Store):
|
|
910
|
+
found_load_or_store = True
|
|
911
|
+
break
|
|
912
|
+
if isinstance(stmt, ailment.Stmt.Assignment) and isinstance(stmt.src, ailment.Expr.Load):
|
|
913
|
+
found_load_or_store = True
|
|
914
|
+
break
|
|
915
|
+
if not found_load_or_store:
|
|
916
|
+
self.data_refs = {}
|
|
917
|
+
return
|
|
918
|
+
|
|
919
|
+
ail_graph = self._make_ailgraph()
|
|
920
|
+
self._remove_redundant_jump_blocks(ail_graph)
|
|
921
|
+
|
|
922
|
+
# full-function constant-only propagation
|
|
923
|
+
self._update_progress(33.0, text="Constant propagation")
|
|
924
|
+
self._simplify_function(
|
|
925
|
+
ail_graph,
|
|
926
|
+
remove_dead_memdefs=False,
|
|
927
|
+
unify_variables=False,
|
|
928
|
+
narrow_expressions=False,
|
|
929
|
+
only_consts=True,
|
|
930
|
+
fold_callexprs_into_conditions=self._fold_callexprs_into_conditions,
|
|
931
|
+
max_iterations=1,
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
# cached block-level reaching definition analysis results and propagator results
|
|
935
|
+
block_simplification_cache: dict[ailment.Block, NamedTuple] | None = {}
|
|
936
|
+
|
|
937
|
+
# Simplify blocks
|
|
938
|
+
# we never remove dead memory definitions before making callsites. otherwise stack arguments may go missing
|
|
939
|
+
# before they are recognized as stack arguments.
|
|
940
|
+
self._update_progress(35.0, text="Simplifying blocks 1")
|
|
941
|
+
ail_graph = self._simplify_blocks(ail_graph, stack_pointer_tracker=spt, cache=block_simplification_cache)
|
|
942
|
+
|
|
943
|
+
# Simplify the entire function for the first time
|
|
944
|
+
self._update_progress(45.0, text="Simplifying function 1")
|
|
945
|
+
self._simplify_function(
|
|
946
|
+
ail_graph,
|
|
947
|
+
remove_dead_memdefs=False,
|
|
948
|
+
unify_variables=False,
|
|
949
|
+
narrow_expressions=False,
|
|
950
|
+
fold_callexprs_into_conditions=False,
|
|
951
|
+
rewrite_ccalls=False,
|
|
952
|
+
max_iterations=1,
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
# clear _blocks_by_addr_and_size so no one can use it again
|
|
956
|
+
# TODO: Totally remove this dict
|
|
957
|
+
self._blocks_by_addr_and_size = None
|
|
958
|
+
|
|
959
|
+
self.graph = ail_graph
|
|
960
|
+
self.arg_list = None
|
|
961
|
+
self.variable_kb = None
|
|
962
|
+
self.cc_graph = None
|
|
963
|
+
self.externs = set()
|
|
964
|
+
self.data_refs: dict[int, list[DataRefDesc]] = self._collect_data_refs(ail_graph)
|
|
965
|
+
|
|
966
|
+
@staticmethod
|
|
967
|
+
def _copy_graph(graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
968
|
+
"""
|
|
969
|
+
Copy AIL Graph.
|
|
970
|
+
|
|
971
|
+
:return: A copy of the AIl graph.
|
|
972
|
+
"""
|
|
973
|
+
graph_copy = networkx.DiGraph()
|
|
974
|
+
block_mapping = {}
|
|
975
|
+
# copy all blocks
|
|
976
|
+
for block in graph.nodes():
|
|
977
|
+
new_block = copy.copy(block)
|
|
978
|
+
new_stmts = copy.copy(block.statements)
|
|
979
|
+
new_block.statements = new_stmts
|
|
980
|
+
block_mapping[block] = new_block
|
|
981
|
+
graph_copy.add_node(new_block)
|
|
982
|
+
|
|
983
|
+
# copy all edges
|
|
984
|
+
for src, dst, data in graph.edges(data=True):
|
|
985
|
+
new_src = block_mapping[src]
|
|
986
|
+
new_dst = block_mapping[dst]
|
|
987
|
+
graph_copy.add_edge(new_src, new_dst, **data)
|
|
988
|
+
return graph_copy
|
|
989
|
+
|
|
990
|
+
def copy_graph(self, graph=None) -> networkx.DiGraph:
|
|
991
|
+
return self._copy_graph(graph or self.graph) # type:ignore
|
|
992
|
+
|
|
993
|
+
@timethis
|
|
994
|
+
def _set_function_graph(self):
|
|
995
|
+
self._func_graph = self.function.graph_ex(exception_edges=self._exception_edges)
|
|
996
|
+
|
|
997
|
+
@timethis
|
|
998
|
+
def _remove_alignment_blocks(self):
|
|
999
|
+
"""
|
|
1000
|
+
Alignment blocks are basic blocks that only consist of nops. They should not be included in the graph.
|
|
1001
|
+
"""
|
|
1002
|
+
assert self._func_graph is not None
|
|
1003
|
+
for node in list(self._func_graph.nodes()):
|
|
1004
|
+
if self._func_graph.in_degree(node) == 0 and CFGBase._is_noop_block(
|
|
1005
|
+
self.project.arch, self.project.factory.block(node.addr, node.size)
|
|
1006
|
+
):
|
|
1007
|
+
if (node.addr, None) == self.entry_node_addr:
|
|
1008
|
+
# this is the entry node. after removing this node, the new entry node will be its successor
|
|
1009
|
+
if self._func_graph.out_degree[node] == 1:
|
|
1010
|
+
succ = next(iter(self._func_graph.successors(node)))
|
|
1011
|
+
self.entry_node_addr = succ.addr, None
|
|
1012
|
+
else:
|
|
1013
|
+
# we just don't remove this node...
|
|
1014
|
+
continue
|
|
1015
|
+
self._func_graph.remove_node(node)
|
|
1016
|
+
|
|
1017
|
+
@timethis
|
|
1018
|
+
def _recover_calling_conventions(self, func_graph=None) -> None:
|
|
1019
|
+
"""
|
|
1020
|
+
Examine the calling convention and function prototype for each function called. For functions with missing
|
|
1021
|
+
calling conventions or function prototypes, analyze each *call site* and recover the calling convention and
|
|
1022
|
+
function prototype of the callee function.
|
|
1023
|
+
|
|
1024
|
+
:return: None
|
|
1025
|
+
"""
|
|
1026
|
+
|
|
1027
|
+
attempted_funcs: set[int] = set()
|
|
1028
|
+
|
|
1029
|
+
for node in self.function.transition_graph:
|
|
1030
|
+
if (
|
|
1031
|
+
isinstance(node, BlockNode)
|
|
1032
|
+
and node.addr != self.function.addr
|
|
1033
|
+
and self.kb.functions.contains_addr(node.addr)
|
|
1034
|
+
):
|
|
1035
|
+
# tail jumps
|
|
1036
|
+
target_func = self.kb.functions.get_by_addr(node.addr)
|
|
1037
|
+
elif isinstance(node, Function):
|
|
1038
|
+
target_func = node
|
|
1039
|
+
else:
|
|
1040
|
+
# TODO: Enable call-site analysis for indirect calls
|
|
1041
|
+
continue
|
|
1042
|
+
|
|
1043
|
+
if target_func.addr in attempted_funcs:
|
|
1044
|
+
continue
|
|
1045
|
+
attempted_funcs.add(target_func.addr)
|
|
1046
|
+
|
|
1047
|
+
# case 0: the calling convention and prototype are available
|
|
1048
|
+
if target_func.calling_convention is not None and target_func.prototype is not None:
|
|
1049
|
+
continue
|
|
1050
|
+
|
|
1051
|
+
call_sites = []
|
|
1052
|
+
for pred, _, data in self.function.transition_graph.in_edges(node, data=True):
|
|
1053
|
+
if data.get("type", None) != "return":
|
|
1054
|
+
call_sites.append(pred)
|
|
1055
|
+
# case 1: calling conventions and prototypes are available at every single call site
|
|
1056
|
+
if call_sites and all(self.kb.callsite_prototypes.has_prototype(callsite.addr) for callsite in call_sites):
|
|
1057
|
+
continue
|
|
1058
|
+
|
|
1059
|
+
# case 2: the callee is a SimProcedure
|
|
1060
|
+
if target_func.is_simprocedure:
|
|
1061
|
+
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast) # type: ignore
|
|
1062
|
+
if cc.cc is not None and cc.prototype is not None:
|
|
1063
|
+
target_func.calling_convention = cc.cc
|
|
1064
|
+
target_func.prototype = cc.prototype
|
|
1065
|
+
target_func.prototype_libname = cc.prototype_libname
|
|
1066
|
+
continue
|
|
1067
|
+
|
|
1068
|
+
# case 3: the callee is a PLT function
|
|
1069
|
+
if target_func.is_plt:
|
|
1070
|
+
cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast) # type: ignore
|
|
1071
|
+
if cc.cc is not None and cc.prototype is not None:
|
|
1072
|
+
target_func.calling_convention = cc.cc
|
|
1073
|
+
target_func.prototype = cc.prototype
|
|
1074
|
+
target_func.prototype_libname = cc.prototype_libname
|
|
1075
|
+
continue
|
|
1076
|
+
|
|
1077
|
+
# case 4: fall back to call site analysis
|
|
1078
|
+
for callsite in call_sites:
|
|
1079
|
+
if self.kb.callsite_prototypes.has_prototype(callsite.addr):
|
|
1080
|
+
continue
|
|
1081
|
+
|
|
1082
|
+
# parse the call instruction address from the edge
|
|
1083
|
+
callsite_ins_addr = None
|
|
1084
|
+
edge_data = [
|
|
1085
|
+
data
|
|
1086
|
+
for src, dst, data in self.function.transition_graph.in_edges(node, data=True)
|
|
1087
|
+
if src is callsite
|
|
1088
|
+
]
|
|
1089
|
+
if len(edge_data) == 1:
|
|
1090
|
+
callsite_ins_addr = edge_data[0].get("ins_addr", None)
|
|
1091
|
+
if callsite_ins_addr is None:
|
|
1092
|
+
# parse the block...
|
|
1093
|
+
callsite_block = self.project.factory.block(callsite.addr, size=callsite.size)
|
|
1094
|
+
if self.project.arch.branch_delay_slot:
|
|
1095
|
+
if callsite_block.instructions < 2:
|
|
1096
|
+
continue
|
|
1097
|
+
callsite_ins_addr = callsite_block.instruction_addrs[-2]
|
|
1098
|
+
else:
|
|
1099
|
+
if callsite_block.instructions == 0:
|
|
1100
|
+
continue
|
|
1101
|
+
callsite_ins_addr = callsite_block.instruction_addrs[-1]
|
|
1102
|
+
|
|
1103
|
+
cc = self.project.analyses.CallingConvention(
|
|
1104
|
+
None,
|
|
1105
|
+
analyze_callsites=True,
|
|
1106
|
+
caller_func_addr=self.function.addr,
|
|
1107
|
+
callsite_block_addr=callsite.addr,
|
|
1108
|
+
callsite_insn_addr=callsite_ins_addr,
|
|
1109
|
+
func_graph=func_graph,
|
|
1110
|
+
fail_fast=self._fail_fast, # type:ignore
|
|
1111
|
+
)
|
|
1112
|
+
|
|
1113
|
+
if cc.cc is not None and cc.prototype is not None:
|
|
1114
|
+
self.kb.callsite_prototypes.set_prototype(callsite.addr, cc.cc, cc.prototype)
|
|
1115
|
+
if func_graph is not None and cc.prototype.returnty is not None:
|
|
1116
|
+
# patch the AIL call statement if we can find one
|
|
1117
|
+
callsite_ail_block: ailment.Block | None = next(
|
|
1118
|
+
iter(bb for bb in func_graph if bb.addr == callsite.addr), None
|
|
1119
|
+
)
|
|
1120
|
+
if callsite_ail_block is not None and callsite_ail_block.statements:
|
|
1121
|
+
last_stmt = callsite_ail_block.statements[-1]
|
|
1122
|
+
if (
|
|
1123
|
+
isinstance(last_stmt, ailment.Stmt.Call)
|
|
1124
|
+
and last_stmt.ret_expr is None
|
|
1125
|
+
and isinstance(cc.cc.RETURN_VAL, SimRegArg)
|
|
1126
|
+
):
|
|
1127
|
+
reg_offset, reg_size = self.project.arch.registers[cc.cc.RETURN_VAL.reg_name]
|
|
1128
|
+
last_stmt.ret_expr = ailment.Expr.Register(
|
|
1129
|
+
None,
|
|
1130
|
+
None,
|
|
1131
|
+
reg_offset,
|
|
1132
|
+
reg_size * 8,
|
|
1133
|
+
ins_addr=callsite_ins_addr,
|
|
1134
|
+
reg_name=cc.cc.RETURN_VAL.reg_name,
|
|
1135
|
+
)
|
|
1136
|
+
last_stmt.bits = reg_size * 8
|
|
1137
|
+
|
|
1138
|
+
# finally, recover the calling convention of the current function
|
|
1139
|
+
if self.function.prototype is None or self.function.calling_convention is None:
|
|
1140
|
+
self.project.analyses.CompleteCallingConventions(
|
|
1141
|
+
fail_fast=self._fail_fast, # type: ignore
|
|
1142
|
+
recover_variables=True,
|
|
1143
|
+
prioritize_func_addrs=[self.function.addr],
|
|
1144
|
+
skip_other_funcs=True,
|
|
1145
|
+
skip_signature_matched_functions=False,
|
|
1146
|
+
func_graphs={self.function.addr: func_graph} if func_graph is not None else None,
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
@timethis
|
|
1150
|
+
def _track_stack_pointers(self):
|
|
1151
|
+
"""
|
|
1152
|
+
For each instruction, track its stack pointer offset and stack base pointer offset.
|
|
1153
|
+
|
|
1154
|
+
:return: None
|
|
1155
|
+
"""
|
|
1156
|
+
|
|
1157
|
+
regs = {self.project.arch.sp_offset}
|
|
1158
|
+
initial_reg_values = {
|
|
1159
|
+
self.project.arch.sp_offset: OffsetVal(
|
|
1160
|
+
Register(self.project.arch.sp_offset, self.project.arch.bits), self._sp_shift
|
|
1161
|
+
)
|
|
1162
|
+
}
|
|
1163
|
+
if hasattr(self.project.arch, "bp_offset") and self.project.arch.bp_offset is not None:
|
|
1164
|
+
regs.add(self.project.arch.bp_offset)
|
|
1165
|
+
initial_reg_values[self.project.arch.bp_offset] = OffsetVal(
|
|
1166
|
+
Register(self.project.arch.bp_offset, self.project.arch.bits), self._sp_shift
|
|
1167
|
+
)
|
|
1168
|
+
|
|
1169
|
+
regs |= self._find_regs_compared_against_sp(self._func_graph)
|
|
1170
|
+
|
|
1171
|
+
spt = self.project.analyses.StackPointerTracker(
|
|
1172
|
+
self.function,
|
|
1173
|
+
regs,
|
|
1174
|
+
fail_fast=self._fail_fast,
|
|
1175
|
+
track_memory=self._sp_tracker_track_memory,
|
|
1176
|
+
cross_insn_opt=False,
|
|
1177
|
+
initial_reg_values=initial_reg_values,
|
|
1178
|
+
)
|
|
1179
|
+
|
|
1180
|
+
if spt.inconsistent_for(self.project.arch.sp_offset):
|
|
1181
|
+
l.warning("Inconsistency found during stack pointer tracking. Decompilation results might be incorrect.")
|
|
1182
|
+
return spt
|
|
1183
|
+
|
|
1184
|
+
@timethis
|
|
1185
|
+
def _convert_all(self):
|
|
1186
|
+
"""
|
|
1187
|
+
Convert all VEX blocks in the function graph to AIL blocks, and fill self._blocks.
|
|
1188
|
+
|
|
1189
|
+
:return: None
|
|
1190
|
+
"""
|
|
1191
|
+
assert self._func_graph is not None
|
|
1192
|
+
assert self._blocks_by_addr_and_size is not None
|
|
1193
|
+
|
|
1194
|
+
for block_node in self._func_graph.nodes():
|
|
1195
|
+
ail_block = self._convert(block_node)
|
|
1196
|
+
|
|
1197
|
+
if type(ail_block) is ailment.Block:
|
|
1198
|
+
# remove constant pc assignments
|
|
1199
|
+
ail_block.statements = [
|
|
1200
|
+
stmt
|
|
1201
|
+
for stmt in ail_block.statements
|
|
1202
|
+
if not (
|
|
1203
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
1204
|
+
and isinstance(stmt.dst, ailment.Expr.Register)
|
|
1205
|
+
and stmt.dst.reg_offset == self.project.arch.ip_offset
|
|
1206
|
+
and isinstance(stmt.src, ailment.Expr.Const)
|
|
1207
|
+
)
|
|
1208
|
+
]
|
|
1209
|
+
|
|
1210
|
+
self._blocks_by_addr_and_size[(block_node.addr, block_node.size)] = ail_block
|
|
1211
|
+
|
|
1212
|
+
def _convert(self, block_node):
|
|
1213
|
+
"""
|
|
1214
|
+
Convert a BlockNode to an AIL block.
|
|
1215
|
+
|
|
1216
|
+
:param block_node: A BlockNode instance.
|
|
1217
|
+
:return: A converted AIL block.
|
|
1218
|
+
:rtype: ailment.Block
|
|
1219
|
+
"""
|
|
1220
|
+
|
|
1221
|
+
if type(block_node) is not BlockNode:
|
|
1222
|
+
return block_node
|
|
1223
|
+
|
|
1224
|
+
if block_node.size == 0:
|
|
1225
|
+
return ailment.Block(block_node.addr, 0, statements=[])
|
|
1226
|
+
|
|
1227
|
+
block = self.project.factory.block(block_node.addr, block_node.size, cross_insn_opt=False)
|
|
1228
|
+
converted = self._convert_vex(block)
|
|
1229
|
+
|
|
1230
|
+
# architecture-specific setup
|
|
1231
|
+
if block.addr == self.function.addr and self.project.arch.name in {"X86", "AMD64"}:
|
|
1232
|
+
# setup dflag; this is a hack for most sane ABIs. we may move this logic elsewhere if there are adversarial
|
|
1233
|
+
# binaries that mess with dflags and pass them across functions
|
|
1234
|
+
dflag_offset, dflag_size = self.project.arch.registers["d"]
|
|
1235
|
+
dflag = ailment.Expr.Register(
|
|
1236
|
+
self._ail_manager.next_atom(),
|
|
1237
|
+
None,
|
|
1238
|
+
dflag_offset,
|
|
1239
|
+
dflag_size * self.project.arch.byte_width,
|
|
1240
|
+
ins_addr=block.addr,
|
|
1241
|
+
)
|
|
1242
|
+
forward = ailment.Expr.Const(
|
|
1243
|
+
self._ail_manager.next_atom(), None, 1, dflag_size * self.project.arch.byte_width, ins_addr=block.addr
|
|
1244
|
+
)
|
|
1245
|
+
dflag_assignment = ailment.Stmt.Assignment(
|
|
1246
|
+
self._ail_manager.next_atom(), dflag, forward, ins_addr=block.addr
|
|
1247
|
+
)
|
|
1248
|
+
converted.statements.insert(0, dflag_assignment)
|
|
1249
|
+
|
|
1250
|
+
return converted
|
|
1251
|
+
|
|
1252
|
+
def _convert_vex(self, block):
|
|
1253
|
+
if block.vex.jumpkind not in {"Ijk_Call", "Ijk_Boring", "Ijk_Ret"} and not block.vex.jumpkind.startswith(
|
|
1254
|
+
"Ijk_Sys"
|
|
1255
|
+
):
|
|
1256
|
+
# we don't support lifting this block. use a dummy block instead
|
|
1257
|
+
dirty_expr = ailment.Expr.DirtyExpression(
|
|
1258
|
+
self._ail_manager.next_atom,
|
|
1259
|
+
f"Unsupported jumpkind {block.vex.jumpkind} at address {block.addr}",
|
|
1260
|
+
[],
|
|
1261
|
+
bits=0,
|
|
1262
|
+
)
|
|
1263
|
+
statements: list[ailment.Statement] = [
|
|
1264
|
+
ailment.Stmt.DirtyStatement(
|
|
1265
|
+
self._ail_manager.next_atom(),
|
|
1266
|
+
dirty_expr,
|
|
1267
|
+
ins_addr=block.addr,
|
|
1268
|
+
)
|
|
1269
|
+
]
|
|
1270
|
+
return ailment.Block(block.addr, block.size, statements=statements)
|
|
1271
|
+
|
|
1272
|
+
return ailment.IRSBConverter.convert(block.vex, self._ail_manager)
|
|
1273
|
+
|
|
1274
|
+
@timethis
|
|
1275
|
+
def _replace_single_target_indirect_transitions(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
1276
|
+
"""
|
|
1277
|
+
Remove single-target indirect jumps and calls and replace them with direct jumps or calls.
|
|
1278
|
+
"""
|
|
1279
|
+
if self._cfg is None:
|
|
1280
|
+
return ail_graph
|
|
1281
|
+
|
|
1282
|
+
for block in ail_graph.nodes():
|
|
1283
|
+
if not block.statements:
|
|
1284
|
+
continue
|
|
1285
|
+
last_stmt = block.statements[-1]
|
|
1286
|
+
if isinstance(last_stmt, ailment.Stmt.Call) and not isinstance(last_stmt.target, ailment.Expr.Const):
|
|
1287
|
+
# indirect call
|
|
1288
|
+
# consult CFG to see if this is a call with a single successor
|
|
1289
|
+
node = self._cfg.get_any_node(block.addr)
|
|
1290
|
+
if node is None:
|
|
1291
|
+
continue
|
|
1292
|
+
successors = [
|
|
1293
|
+
node
|
|
1294
|
+
for node, jk in self._cfg.get_successors_and_jumpkinds(node)
|
|
1295
|
+
if jk == "Ijk_Call" or jk.startswith("Ijk_Sys")
|
|
1296
|
+
]
|
|
1297
|
+
if len(successors) == 1:
|
|
1298
|
+
succ_addr = successors[0].addr
|
|
1299
|
+
if not self.project.is_hooked(succ_addr) or not isinstance(
|
|
1300
|
+
self.project.hooked_by(successors[0].addr), UnresolvableCallTarget
|
|
1301
|
+
):
|
|
1302
|
+
# found a single successor - replace the last statement
|
|
1303
|
+
assert isinstance(last_stmt.target, ailment.Expr.Expression) # not a string
|
|
1304
|
+
new_last_stmt = last_stmt.copy()
|
|
1305
|
+
assert isinstance(successors[0].addr, int)
|
|
1306
|
+
new_last_stmt.target = ailment.Expr.Const(None, None, successors[0].addr, last_stmt.target.bits)
|
|
1307
|
+
block.statements[-1] = new_last_stmt
|
|
1308
|
+
|
|
1309
|
+
elif isinstance(last_stmt, ailment.Stmt.Jump) and not isinstance(last_stmt.target, ailment.Expr.Const):
|
|
1310
|
+
# indirect jump
|
|
1311
|
+
# consult CFG to see if there is a jump with a single successor
|
|
1312
|
+
node = self._cfg.get_any_node(block.addr)
|
|
1313
|
+
if node is None:
|
|
1314
|
+
continue
|
|
1315
|
+
successors = self._cfg.get_successors(node, excluding_fakeret=True, jumpkind="Ijk_Boring")
|
|
1316
|
+
if len(successors) == 1 and not isinstance(
|
|
1317
|
+
self.project.hooked_by(successors[0].addr), UnresolvableJumpTarget
|
|
1318
|
+
):
|
|
1319
|
+
# found a single successor - replace the last statement
|
|
1320
|
+
new_last_stmt = last_stmt.copy()
|
|
1321
|
+
assert isinstance(successors[0].addr, int)
|
|
1322
|
+
new_last_stmt.target = ailment.Expr.Const(None, None, successors[0].addr, last_stmt.target.bits)
|
|
1323
|
+
block.statements[-1] = new_last_stmt
|
|
1324
|
+
|
|
1325
|
+
return ail_graph
|
|
1326
|
+
|
|
1327
|
+
@timethis
|
|
1328
|
+
def _replace_tail_jumps_with_calls(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
1329
|
+
"""
|
|
1330
|
+
Rewrite tail jumps to functions as call statements.
|
|
1331
|
+
"""
|
|
1332
|
+
for block in list(ail_graph.nodes()):
|
|
1333
|
+
if ail_graph.out_degree[block] > 1:
|
|
1334
|
+
continue
|
|
1335
|
+
|
|
1336
|
+
last_stmt = block.statements[-1]
|
|
1337
|
+
if isinstance(last_stmt, ailment.Stmt.Jump):
|
|
1338
|
+
targets = [last_stmt.target]
|
|
1339
|
+
replace_last_stmt = True
|
|
1340
|
+
elif isinstance(last_stmt, ailment.Stmt.ConditionalJump):
|
|
1341
|
+
targets = [last_stmt.true_target, last_stmt.false_target]
|
|
1342
|
+
replace_last_stmt = False
|
|
1343
|
+
else:
|
|
1344
|
+
continue
|
|
1345
|
+
|
|
1346
|
+
for target in targets:
|
|
1347
|
+
if not isinstance(target, ailment.Const) or not self.kb.functions.contains_addr(target.value):
|
|
1348
|
+
continue
|
|
1349
|
+
|
|
1350
|
+
target_func = self.kb.functions.get_by_addr(target.value)
|
|
1351
|
+
|
|
1352
|
+
ret_reg_offset = self.project.arch.ret_offset
|
|
1353
|
+
if target_func.returning and ret_reg_offset is not None:
|
|
1354
|
+
ret_expr = ailment.Expr.Register(
|
|
1355
|
+
None,
|
|
1356
|
+
None,
|
|
1357
|
+
ret_reg_offset,
|
|
1358
|
+
self.project.arch.bits,
|
|
1359
|
+
reg_name=self.project.arch.translate_register_name(ret_reg_offset, size=self.project.arch.bits),
|
|
1360
|
+
**target.tags,
|
|
1361
|
+
)
|
|
1362
|
+
else:
|
|
1363
|
+
ret_expr = None
|
|
1364
|
+
|
|
1365
|
+
call_stmt = ailment.Stmt.Call(
|
|
1366
|
+
None,
|
|
1367
|
+
target.copy(),
|
|
1368
|
+
calling_convention=None, # target_func.calling_convention,
|
|
1369
|
+
prototype=None, # target_func.prototype,
|
|
1370
|
+
ret_expr=ret_expr,
|
|
1371
|
+
**last_stmt.tags,
|
|
1372
|
+
)
|
|
1373
|
+
|
|
1374
|
+
if replace_last_stmt:
|
|
1375
|
+
call_block = block
|
|
1376
|
+
block.statements[-1] = call_stmt
|
|
1377
|
+
else:
|
|
1378
|
+
call_block = ailment.Block(self.new_block_addr(), 1, statements=[call_stmt])
|
|
1379
|
+
ail_graph.add_edge(block, call_block)
|
|
1380
|
+
target.value = call_block.addr
|
|
1381
|
+
|
|
1382
|
+
if target_func.returning:
|
|
1383
|
+
ret_stmt = ailment.Stmt.Return(None, [], **last_stmt.tags)
|
|
1384
|
+
ret_block = ailment.Block(self.new_block_addr(), 1, statements=[ret_stmt])
|
|
1385
|
+
ail_graph.add_edge(call_block, ret_block, type="fake_return")
|
|
1386
|
+
|
|
1387
|
+
return ail_graph
|
|
1388
|
+
|
|
1389
|
+
@timethis
|
|
1390
|
+
def _fix_special_call_calling_conventions(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
1391
|
+
"""
|
|
1392
|
+
Fix the calling convention for special function calls.
|
|
1393
|
+
"""
|
|
1394
|
+
for block in list(ail_graph.nodes()):
|
|
1395
|
+
last_stmt = block.statements[-1]
|
|
1396
|
+
if isinstance(last_stmt, ailment.Stmt.Call) and isinstance(last_stmt.target, ailment.Expr.Const):
|
|
1397
|
+
target = last_stmt.target.value
|
|
1398
|
+
else:
|
|
1399
|
+
continue
|
|
1400
|
+
|
|
1401
|
+
if not self.kb.functions.contains_addr(target):
|
|
1402
|
+
continue
|
|
1403
|
+
target_func = self.kb.functions.get_by_addr(target)
|
|
1404
|
+
if target_func.name == "_security_check_cookie" and self.project.arch.name in {"X86", "AMD64"}:
|
|
1405
|
+
arg = SimRegArg("ecx", 32) if self.project.arch.bits == 32 else SimRegArg("rcx", 64)
|
|
1406
|
+
arg_offset, arg_bits = self.project.arch.registers[arg.reg_name]
|
|
1407
|
+
arg_expr = ailment.Expr.Register(
|
|
1408
|
+
self._ail_manager.next_atom(),
|
|
1409
|
+
None,
|
|
1410
|
+
arg_offset,
|
|
1411
|
+
arg_bits * self.project.arch.byte_width,
|
|
1412
|
+
**last_stmt.tags,
|
|
1413
|
+
)
|
|
1414
|
+
IntCls = SimTypeInt if self.project.arch.bits == 32 else SimTypeLongLong
|
|
1415
|
+
call_stmt = ailment.Stmt.Call(
|
|
1416
|
+
None,
|
|
1417
|
+
last_stmt.target.copy(),
|
|
1418
|
+
calling_convention=SimCCUsercall(self.project.arch, [arg], []),
|
|
1419
|
+
prototype=SimTypeFunction([IntCls(signed=False)], SimTypeBottom(label="void")).with_arch(
|
|
1420
|
+
self.project.arch
|
|
1421
|
+
),
|
|
1422
|
+
args=[arg_expr],
|
|
1423
|
+
ret_expr=None,
|
|
1424
|
+
is_prototype_guessed=False,
|
|
1425
|
+
**last_stmt.tags,
|
|
1426
|
+
)
|
|
1427
|
+
block.statements[-1] = call_stmt
|
|
1428
|
+
|
|
1429
|
+
return ail_graph
|
|
1430
|
+
|
|
1431
|
+
def _apply_callsite_prototype_and_calling_convention(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
1432
|
+
for block in ail_graph.nodes():
|
|
1433
|
+
if not block.statements:
|
|
1434
|
+
continue
|
|
1435
|
+
|
|
1436
|
+
last_stmt = block.statements[-1]
|
|
1437
|
+
if not isinstance(last_stmt, ailment.Stmt.Call):
|
|
1438
|
+
continue
|
|
1439
|
+
|
|
1440
|
+
cc = last_stmt.calling_convention
|
|
1441
|
+
prototype = last_stmt.prototype
|
|
1442
|
+
if cc and prototype:
|
|
1443
|
+
continue
|
|
1444
|
+
|
|
1445
|
+
# manually-specified call-site prototype
|
|
1446
|
+
has_callsite_prototype = self.kb.callsite_prototypes.has_prototype(block.addr)
|
|
1447
|
+
if has_callsite_prototype:
|
|
1448
|
+
manually_specified = self.kb.callsite_prototypes.is_prototype_manual(block.addr)
|
|
1449
|
+
if manually_specified:
|
|
1450
|
+
cc = self.kb.callsite_prototypes.get_cc(block.addr)
|
|
1451
|
+
prototype = self.kb.callsite_prototypes.get_prototype(block.addr)
|
|
1452
|
+
|
|
1453
|
+
# function-specific prototype
|
|
1454
|
+
func = None
|
|
1455
|
+
if cc is None or prototype is None:
|
|
1456
|
+
target = None
|
|
1457
|
+
if isinstance(last_stmt.target, ailment.Expr.Const):
|
|
1458
|
+
target = last_stmt.target.value
|
|
1459
|
+
|
|
1460
|
+
if target is not None and target in self.kb.functions:
|
|
1461
|
+
# function-specific logic when the calling target is known
|
|
1462
|
+
func = self.kb.functions[target]
|
|
1463
|
+
if func.prototype is None:
|
|
1464
|
+
func.find_declaration()
|
|
1465
|
+
cc = func.calling_convention
|
|
1466
|
+
prototype = func.prototype
|
|
1467
|
+
|
|
1468
|
+
# automatically recovered call-site prototype
|
|
1469
|
+
if (cc is None or prototype is None) and has_callsite_prototype:
|
|
1470
|
+
cc = self.kb.callsite_prototypes.get_cc(block.addr)
|
|
1471
|
+
prototype = self.kb.callsite_prototypes.get_prototype(block.addr)
|
|
1472
|
+
|
|
1473
|
+
# ensure the prototype has been resolved
|
|
1474
|
+
if prototype is not None and func is not None:
|
|
1475
|
+
# make sure the function prototype is resolved.
|
|
1476
|
+
# TODO: Cache resolved function prototypes globally
|
|
1477
|
+
prototype_libname = func.prototype_libname
|
|
1478
|
+
if prototype_libname is not None:
|
|
1479
|
+
prototype = dereference_simtype_by_lib(prototype, prototype_libname)
|
|
1480
|
+
|
|
1481
|
+
if cc is None:
|
|
1482
|
+
l.warning("Call site %#x (callee %s) has an unknown calling convention.", block.addr, repr(func))
|
|
1483
|
+
|
|
1484
|
+
new_last_stmt = last_stmt.copy()
|
|
1485
|
+
new_last_stmt.calling_convention = cc
|
|
1486
|
+
new_last_stmt.prototype = prototype
|
|
1487
|
+
new_last_stmt.tags["is_prototype_guessed"] = True
|
|
1488
|
+
if func is not None:
|
|
1489
|
+
new_last_stmt.tags["is_prototype_guessed"] = func.is_prototype_guessed
|
|
1490
|
+
block.statements[-1] = new_last_stmt
|
|
1491
|
+
|
|
1492
|
+
return ail_graph
|
|
1493
|
+
|
|
1494
|
+
@timethis
|
|
1495
|
+
def _make_ailgraph(self) -> networkx.DiGraph:
|
|
1496
|
+
return self._function_graph_to_ail_graph(self._func_graph)
|
|
1497
|
+
|
|
1498
|
+
@timethis
|
|
1499
|
+
def _simplify_blocks(
|
|
1500
|
+
self,
|
|
1501
|
+
ail_graph: networkx.DiGraph,
|
|
1502
|
+
stack_pointer_tracker=None,
|
|
1503
|
+
cache: dict[ailment.Block, NamedTuple] | None = None,
|
|
1504
|
+
preserve_vvar_ids: set[int] | None = None,
|
|
1505
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] | None = None,
|
|
1506
|
+
):
|
|
1507
|
+
"""
|
|
1508
|
+
Simplify all blocks in self._blocks.
|
|
1509
|
+
|
|
1510
|
+
:param ail_graph: The AIL function graph.
|
|
1511
|
+
:param stack_pointer_tracker: The RegisterDeltaTracker analysis instance.
|
|
1512
|
+
:param cache: A block-level cache that stores reaching definition analysis results and
|
|
1513
|
+
propagation results.
|
|
1514
|
+
:return: None
|
|
1515
|
+
"""
|
|
1516
|
+
|
|
1517
|
+
blocks_by_addr_and_idx: dict[tuple[int, int | None], ailment.Block] = {}
|
|
1518
|
+
|
|
1519
|
+
for ail_block in ail_graph.nodes():
|
|
1520
|
+
simplified = self._simplify_block(
|
|
1521
|
+
ail_block,
|
|
1522
|
+
stack_pointer_tracker=stack_pointer_tracker,
|
|
1523
|
+
cache=cache,
|
|
1524
|
+
preserve_vvar_ids=preserve_vvar_ids,
|
|
1525
|
+
type_hints=type_hints,
|
|
1526
|
+
)
|
|
1527
|
+
key = ail_block.addr, ail_block.idx
|
|
1528
|
+
blocks_by_addr_and_idx[key] = simplified
|
|
1529
|
+
|
|
1530
|
+
# update blocks_map to allow node_addr to node lookup
|
|
1531
|
+
def _replace_node_handler(node):
|
|
1532
|
+
key = node.addr, node.idx
|
|
1533
|
+
if key in blocks_by_addr_and_idx:
|
|
1534
|
+
return blocks_by_addr_and_idx[key]
|
|
1535
|
+
return None
|
|
1536
|
+
|
|
1537
|
+
AILGraphWalker(ail_graph, _replace_node_handler, replace_nodes=True).walk()
|
|
1538
|
+
|
|
1539
|
+
return ail_graph
|
|
1540
|
+
|
|
1541
|
+
def _simplify_block(
|
|
1542
|
+
self,
|
|
1543
|
+
ail_block,
|
|
1544
|
+
stack_pointer_tracker=None,
|
|
1545
|
+
cache=None,
|
|
1546
|
+
preserve_vvar_ids: set[int] | None = None,
|
|
1547
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] | None = None,
|
|
1548
|
+
):
|
|
1549
|
+
"""
|
|
1550
|
+
Simplify a single AIL block.
|
|
1551
|
+
|
|
1552
|
+
:param ailment.Block ail_block: The AIL block to simplify.
|
|
1553
|
+
:param stack_pointer_tracker: The RegisterDeltaTracker analysis instance.
|
|
1554
|
+
:return: A simplified AIL block.
|
|
1555
|
+
"""
|
|
1556
|
+
|
|
1557
|
+
cached_rd, cached_prop = None, None
|
|
1558
|
+
cache_item = None
|
|
1559
|
+
cache_key = ail_block.addr, ail_block.idx
|
|
1560
|
+
if cache:
|
|
1561
|
+
cache_item = cache.get(cache_key, None)
|
|
1562
|
+
if cache_item:
|
|
1563
|
+
# cache hit
|
|
1564
|
+
cached_rd = cache_item.rd
|
|
1565
|
+
cached_prop = cache_item.prop
|
|
1566
|
+
|
|
1567
|
+
simp = self.project.analyses.AILBlockSimplifier(
|
|
1568
|
+
ail_block,
|
|
1569
|
+
self.function.addr,
|
|
1570
|
+
fail_fast=self._fail_fast,
|
|
1571
|
+
stack_pointer_tracker=stack_pointer_tracker,
|
|
1572
|
+
peephole_optimizations=self.peephole_optimizations,
|
|
1573
|
+
cached_reaching_definitions=cached_rd,
|
|
1574
|
+
cached_propagator=cached_prop,
|
|
1575
|
+
preserve_vvar_ids=preserve_vvar_ids,
|
|
1576
|
+
type_hints=type_hints,
|
|
1577
|
+
)
|
|
1578
|
+
# update the cache
|
|
1579
|
+
if cache is not None:
|
|
1580
|
+
if cache_item:
|
|
1581
|
+
del cache[cache_key]
|
|
1582
|
+
cache[cache_key] = BlockCache(simp._reaching_definitions, simp._propagator)
|
|
1583
|
+
return simp.result_block
|
|
1584
|
+
|
|
1585
|
+
@timethis
|
|
1586
|
+
def _simplify_function(
|
|
1587
|
+
self,
|
|
1588
|
+
ail_graph,
|
|
1589
|
+
remove_dead_memdefs=False,
|
|
1590
|
+
stack_arg_offsets=None,
|
|
1591
|
+
unify_variables=False,
|
|
1592
|
+
max_iterations: int = 8,
|
|
1593
|
+
narrow_expressions=False,
|
|
1594
|
+
only_consts=False,
|
|
1595
|
+
fold_callexprs_into_conditions=False,
|
|
1596
|
+
rewrite_ccalls=True,
|
|
1597
|
+
rename_ccalls=True,
|
|
1598
|
+
removed_vvar_ids: set[int] | None = None,
|
|
1599
|
+
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
|
|
1600
|
+
preserve_vvar_ids: set[int] | None = None,
|
|
1601
|
+
) -> None:
|
|
1602
|
+
"""
|
|
1603
|
+
Simplify the entire function until it reaches a fixed point.
|
|
1604
|
+
"""
|
|
1605
|
+
|
|
1606
|
+
for idx in range(max_iterations):
|
|
1607
|
+
simplified = self._simplify_function_once(
|
|
1608
|
+
ail_graph,
|
|
1609
|
+
remove_dead_memdefs=remove_dead_memdefs,
|
|
1610
|
+
unify_variables=unify_variables,
|
|
1611
|
+
stack_arg_offsets=stack_arg_offsets,
|
|
1612
|
+
# only narrow once
|
|
1613
|
+
narrow_expressions=narrow_expressions and idx == 0,
|
|
1614
|
+
only_consts=only_consts,
|
|
1615
|
+
fold_callexprs_into_conditions=fold_callexprs_into_conditions,
|
|
1616
|
+
rewrite_ccalls=rewrite_ccalls,
|
|
1617
|
+
rename_ccalls=rename_ccalls,
|
|
1618
|
+
removed_vvar_ids=removed_vvar_ids,
|
|
1619
|
+
arg_vvars=arg_vvars,
|
|
1620
|
+
preserve_vvar_ids=preserve_vvar_ids,
|
|
1621
|
+
)
|
|
1622
|
+
if not simplified:
|
|
1623
|
+
break
|
|
1624
|
+
|
|
1625
|
+
@timethis
|
|
1626
|
+
def _simplify_function_once(
|
|
1627
|
+
self,
|
|
1628
|
+
ail_graph,
|
|
1629
|
+
remove_dead_memdefs=False,
|
|
1630
|
+
stack_arg_offsets=None,
|
|
1631
|
+
unify_variables=False,
|
|
1632
|
+
narrow_expressions=False,
|
|
1633
|
+
only_consts=False,
|
|
1634
|
+
fold_callexprs_into_conditions=False,
|
|
1635
|
+
rewrite_ccalls=True,
|
|
1636
|
+
rename_ccalls=True,
|
|
1637
|
+
removed_vvar_ids: set[int] | None = None,
|
|
1638
|
+
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]] | None = None,
|
|
1639
|
+
preserve_vvar_ids: set[int] | None = None,
|
|
1640
|
+
):
|
|
1641
|
+
"""
|
|
1642
|
+
Simplify the entire function once.
|
|
1643
|
+
|
|
1644
|
+
:return: None
|
|
1645
|
+
"""
|
|
1646
|
+
|
|
1647
|
+
simp = self.project.analyses[AILSimplifier].prep(
|
|
1648
|
+
fail_fast=self._fail_fast,
|
|
1649
|
+
)(
|
|
1650
|
+
self.function,
|
|
1651
|
+
func_graph=ail_graph,
|
|
1652
|
+
remove_dead_memdefs=remove_dead_memdefs,
|
|
1653
|
+
unify_variables=unify_variables,
|
|
1654
|
+
stack_arg_offsets=stack_arg_offsets,
|
|
1655
|
+
ail_manager=self._ail_manager,
|
|
1656
|
+
gp=self.function.info.get("gp", None) if self.project.arch.name in {"MIPS32", "MIPS64"} else None,
|
|
1657
|
+
narrow_expressions=narrow_expressions,
|
|
1658
|
+
only_consts=only_consts,
|
|
1659
|
+
fold_callexprs_into_conditions=fold_callexprs_into_conditions,
|
|
1660
|
+
use_callee_saved_regs_at_return=not self._register_save_areas_removed,
|
|
1661
|
+
rewrite_ccalls=rewrite_ccalls,
|
|
1662
|
+
rename_ccalls=rename_ccalls,
|
|
1663
|
+
removed_vvar_ids=removed_vvar_ids,
|
|
1664
|
+
arg_vvars=arg_vvars,
|
|
1665
|
+
secondary_stackvars=self.secondary_stackvars,
|
|
1666
|
+
avoid_vvar_ids=preserve_vvar_ids,
|
|
1667
|
+
)
|
|
1668
|
+
# cache the simplifier's RDA analysis
|
|
1669
|
+
self.reaching_definitions = simp._reaching_definitions
|
|
1670
|
+
|
|
1671
|
+
# the function graph has been updated at this point
|
|
1672
|
+
return simp.simplified
|
|
1673
|
+
|
|
1674
|
+
@timethis
|
|
1675
|
+
def _run_simplification_passes(
|
|
1676
|
+
self,
|
|
1677
|
+
ail_graph,
|
|
1678
|
+
stage: OptimizationPassStage = OptimizationPassStage.AFTER_GLOBAL_SIMPLIFICATION,
|
|
1679
|
+
variable_kb=None,
|
|
1680
|
+
stack_items: dict[int, StackItem] | None = None,
|
|
1681
|
+
stack_pointer_tracker=None,
|
|
1682
|
+
**kwargs,
|
|
1683
|
+
):
|
|
1684
|
+
addr_and_idx_to_blocks: dict[tuple[int, int | None], ailment.Block] = {}
|
|
1685
|
+
addr_to_blocks: dict[int, set[ailment.Block]] = defaultdict(set)
|
|
1686
|
+
|
|
1687
|
+
# update blocks_map to allow node_addr to node lookup
|
|
1688
|
+
def _updatedict_handler(node):
|
|
1689
|
+
addr_and_idx_to_blocks[(node.addr, node.idx)] = node
|
|
1690
|
+
addr_to_blocks[node.addr].add(node)
|
|
1691
|
+
|
|
1692
|
+
AILGraphWalker(ail_graph, _updatedict_handler).walk()
|
|
1693
|
+
|
|
1694
|
+
# Run each pass
|
|
1695
|
+
for pass_ in self._optimization_passes:
|
|
1696
|
+
if stage != pass_.STAGE:
|
|
1697
|
+
continue
|
|
1698
|
+
|
|
1699
|
+
if pass_ in DUPLICATING_OPTS + CONDENSING_OPTS and self.unoptimized_graph is None:
|
|
1700
|
+
# we should save a copy at the first time any optimization that could alter the structure
|
|
1701
|
+
# of the graph is applied
|
|
1702
|
+
self.unoptimized_graph = self._copy_graph(ail_graph)
|
|
1703
|
+
|
|
1704
|
+
pass_ = timethis(pass_)
|
|
1705
|
+
a = pass_(
|
|
1706
|
+
self.function,
|
|
1707
|
+
blocks_by_addr=addr_to_blocks,
|
|
1708
|
+
blocks_by_addr_and_idx=addr_and_idx_to_blocks,
|
|
1709
|
+
graph=ail_graph,
|
|
1710
|
+
variable_kb=variable_kb,
|
|
1711
|
+
vvar_id_start=self.vvar_id_start,
|
|
1712
|
+
entry_node_addr=self.entry_node_addr,
|
|
1713
|
+
scratch=self.optimization_scratch,
|
|
1714
|
+
force_loop_single_exit=self._force_loop_single_exit,
|
|
1715
|
+
refine_loops_with_single_successor=self._refine_loops_with_single_successor,
|
|
1716
|
+
complete_successors=self._complete_successors,
|
|
1717
|
+
stack_pointer_tracker=stack_pointer_tracker,
|
|
1718
|
+
notes=self.notes,
|
|
1719
|
+
static_vvars=self.static_vvars,
|
|
1720
|
+
static_buffers=self.static_buffers,
|
|
1721
|
+
**kwargs,
|
|
1722
|
+
)
|
|
1723
|
+
if a.out_graph:
|
|
1724
|
+
# use the new graph
|
|
1725
|
+
ail_graph = a.out_graph
|
|
1726
|
+
self.vvar_id_start = a.vvar_id_start
|
|
1727
|
+
if stack_items is not None and a.stack_items:
|
|
1728
|
+
stack_items.update(a.stack_items)
|
|
1729
|
+
|
|
1730
|
+
return ail_graph
|
|
1731
|
+
|
|
1732
|
+
@timethis
|
|
1733
|
+
def _create_function_argument_vvars(self, arg_list) -> dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]]:
|
|
1734
|
+
arg_vvars = {}
|
|
1735
|
+
for arg in arg_list:
|
|
1736
|
+
if isinstance(arg, SimRegisterVariable):
|
|
1737
|
+
# get the full register if needed
|
|
1738
|
+
arg_vvar = ailment.Expr.VirtualVariable(
|
|
1739
|
+
self._ail_manager.next_atom(),
|
|
1740
|
+
self.vvar_id_start,
|
|
1741
|
+
arg.bits,
|
|
1742
|
+
ailment.Expr.VirtualVariableCategory.PARAMETER,
|
|
1743
|
+
oident=(ailment.Expr.VirtualVariableCategory.REGISTER, arg.reg),
|
|
1744
|
+
ins_addr=self.function.addr,
|
|
1745
|
+
vex_block_addr=self.function.addr,
|
|
1746
|
+
)
|
|
1747
|
+
self.vvar_id_start += 1
|
|
1748
|
+
arg_vvars[arg_vvar.varid] = arg_vvar, arg
|
|
1749
|
+
elif isinstance(arg, SimStackVariable):
|
|
1750
|
+
arg_vvar = ailment.Expr.VirtualVariable(
|
|
1751
|
+
self._ail_manager.next_atom(),
|
|
1752
|
+
self.vvar_id_start,
|
|
1753
|
+
arg.bits,
|
|
1754
|
+
ailment.Expr.VirtualVariableCategory.PARAMETER,
|
|
1755
|
+
oident=(ailment.Expr.VirtualVariableCategory.STACK, arg.offset),
|
|
1756
|
+
ins_addr=self.function.addr,
|
|
1757
|
+
vex_block_addr=self.function.addr,
|
|
1758
|
+
)
|
|
1759
|
+
self.vvar_id_start += 1
|
|
1760
|
+
arg_vvars[arg_vvar.varid] = arg_vvar, arg
|
|
1761
|
+
|
|
1762
|
+
return arg_vvars
|
|
1763
|
+
|
|
1764
|
+
@timethis
|
|
1765
|
+
def _transform_to_ssa_level0(
|
|
1766
|
+
self, ail_graph: networkx.DiGraph, func_args: set[ailment.Expr.VirtualVariable]
|
|
1767
|
+
) -> networkx.DiGraph:
|
|
1768
|
+
ssailification = self.project.analyses[Ssailification].prep(fail_fast=self._fail_fast)(
|
|
1769
|
+
self.function,
|
|
1770
|
+
ail_graph,
|
|
1771
|
+
entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
|
|
1772
|
+
ail_manager=self._ail_manager,
|
|
1773
|
+
ssa_stackvars=False,
|
|
1774
|
+
func_args=func_args,
|
|
1775
|
+
vvar_id_start=self.vvar_id_start,
|
|
1776
|
+
)
|
|
1777
|
+
self.vvar_id_start = ssailification.max_vvar_id + 1
|
|
1778
|
+
assert ssailification.out_graph is not None
|
|
1779
|
+
return ssailification.out_graph
|
|
1780
|
+
|
|
1781
|
+
@timethis
|
|
1782
|
+
def _transform_to_ssa_level1(
|
|
1783
|
+
self, ail_graph: networkx.DiGraph, func_args: set[ailment.Expr.VirtualVariable]
|
|
1784
|
+
) -> networkx.DiGraph:
|
|
1785
|
+
ssailification = self.project.analyses.Ssailification(
|
|
1786
|
+
self.function,
|
|
1787
|
+
ail_graph,
|
|
1788
|
+
fail_fast=self._fail_fast,
|
|
1789
|
+
entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
|
|
1790
|
+
ail_manager=self._ail_manager,
|
|
1791
|
+
ssa_tmps=True,
|
|
1792
|
+
ssa_stackvars=True,
|
|
1793
|
+
func_args=func_args,
|
|
1794
|
+
vvar_id_start=self.vvar_id_start,
|
|
1795
|
+
)
|
|
1796
|
+
self.vvar_id_start = ssailification.max_vvar_id + 1
|
|
1797
|
+
self.secondary_stackvars = ssailification.secondary_stackvars
|
|
1798
|
+
return ssailification.out_graph
|
|
1799
|
+
|
|
1800
|
+
@timethis
|
|
1801
|
+
def _collect_dephi_vvar_mapping_and_rewrite_blocks(
|
|
1802
|
+
self, ail_graph: networkx.DiGraph, arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]]
|
|
1803
|
+
) -> tuple[dict[int, int], set[int]]:
|
|
1804
|
+
dephication = self.project.analyses.GraphDephicationVVarMapping(
|
|
1805
|
+
self.function,
|
|
1806
|
+
ail_graph,
|
|
1807
|
+
fail_fast=self._fail_fast,
|
|
1808
|
+
entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
|
|
1809
|
+
vvar_id_start=self.vvar_id_start,
|
|
1810
|
+
arg_vvars=[arg_vvar for arg_vvar, _ in arg_vvars.values()],
|
|
1811
|
+
)
|
|
1812
|
+
self.vvar_id_start = dephication.vvar_id_start + 1
|
|
1813
|
+
return dephication.vvar_to_vvar_mapping, dephication.copied_vvar_ids
|
|
1814
|
+
|
|
1815
|
+
@timethis
|
|
1816
|
+
def _make_argument_list(self) -> list[SimVariable]:
|
|
1817
|
+
if self.function.calling_convention is not None and self.function.prototype is not None:
|
|
1818
|
+
proto = (
|
|
1819
|
+
dereference_simtype_by_lib(self.function.prototype, self.function.prototype_libname)
|
|
1820
|
+
if self.function.prototype_libname
|
|
1821
|
+
else self.function.prototype
|
|
1822
|
+
)
|
|
1823
|
+
args: list[SimFunctionArgument] = self.function.calling_convention.arg_locs(proto)
|
|
1824
|
+
arg_vars: list[SimVariable] = []
|
|
1825
|
+
if args:
|
|
1826
|
+
arg_names = self.function.prototype.arg_names or [f"a{i}" for i in range(len(args))]
|
|
1827
|
+
for idx, arg in enumerate(args):
|
|
1828
|
+
if isinstance(arg, SimRegArg):
|
|
1829
|
+
argvar = SimRegisterVariable(
|
|
1830
|
+
self.project.arch.registers[arg.reg_name][0],
|
|
1831
|
+
arg.size,
|
|
1832
|
+
ident=f"arg_{idx}",
|
|
1833
|
+
name=arg_names[idx],
|
|
1834
|
+
region=self.function.addr,
|
|
1835
|
+
)
|
|
1836
|
+
elif isinstance(arg, SimStackArg):
|
|
1837
|
+
argvar = SimStackVariable(
|
|
1838
|
+
arg.stack_offset,
|
|
1839
|
+
arg.size,
|
|
1840
|
+
base="bp",
|
|
1841
|
+
ident=f"arg_{idx}",
|
|
1842
|
+
name=arg_names[idx],
|
|
1843
|
+
region=self.function.addr,
|
|
1844
|
+
)
|
|
1845
|
+
else:
|
|
1846
|
+
argvar = SimVariable(
|
|
1847
|
+
ident=f"arg_{idx}",
|
|
1848
|
+
name=arg_names[idx],
|
|
1849
|
+
region=self.function.addr,
|
|
1850
|
+
size=arg.size,
|
|
1851
|
+
)
|
|
1852
|
+
arg_vars.append(argvar)
|
|
1853
|
+
return arg_vars
|
|
1854
|
+
return []
|
|
1855
|
+
|
|
1856
|
+
@timethis
|
|
1857
|
+
def _make_callsites(
|
|
1858
|
+
self,
|
|
1859
|
+
ail_graph,
|
|
1860
|
+
func_args: set[ailment.Expr.VirtualVariable],
|
|
1861
|
+
stack_pointer_tracker=None,
|
|
1862
|
+
preserve_vvar_ids: set[int] | None = None,
|
|
1863
|
+
):
|
|
1864
|
+
"""
|
|
1865
|
+
Simplify all function call statements.
|
|
1866
|
+
"""
|
|
1867
|
+
|
|
1868
|
+
# Computing reaching definitions
|
|
1869
|
+
rd = self.project.analyses.SReachingDefinitions(
|
|
1870
|
+
subject=self.function,
|
|
1871
|
+
func_graph=ail_graph,
|
|
1872
|
+
func_args=func_args,
|
|
1873
|
+
fail_fast=self._fail_fast,
|
|
1874
|
+
use_callee_saved_regs_at_return=not self._register_save_areas_removed,
|
|
1875
|
+
)
|
|
1876
|
+
|
|
1877
|
+
class TempClass: # pylint:disable=missing-class-docstring
|
|
1878
|
+
stack_arg_offsets = set()
|
|
1879
|
+
removed_vvar_ids = set()
|
|
1880
|
+
|
|
1881
|
+
def _handler(block):
|
|
1882
|
+
csm = self.project.analyses.AILCallSiteMaker(
|
|
1883
|
+
block,
|
|
1884
|
+
fail_fast=self._fail_fast,
|
|
1885
|
+
reaching_definitions=rd,
|
|
1886
|
+
stack_pointer_tracker=stack_pointer_tracker,
|
|
1887
|
+
ail_manager=self._ail_manager,
|
|
1888
|
+
)
|
|
1889
|
+
if csm.stack_arg_offsets is not None:
|
|
1890
|
+
TempClass.stack_arg_offsets |= csm.stack_arg_offsets
|
|
1891
|
+
if csm.removed_vvar_ids:
|
|
1892
|
+
TempClass.removed_vvar_ids |= csm.removed_vvar_ids
|
|
1893
|
+
if csm.result_block and csm.result_block != block:
|
|
1894
|
+
ail_block = csm.result_block
|
|
1895
|
+
simp = self.project.analyses.AILBlockSimplifier(
|
|
1896
|
+
ail_block,
|
|
1897
|
+
self.function.addr,
|
|
1898
|
+
fail_fast=self._fail_fast,
|
|
1899
|
+
stack_pointer_tracker=stack_pointer_tracker,
|
|
1900
|
+
peephole_optimizations=self.peephole_optimizations,
|
|
1901
|
+
preserve_vvar_ids=preserve_vvar_ids,
|
|
1902
|
+
)
|
|
1903
|
+
return simp.result_block
|
|
1904
|
+
return None
|
|
1905
|
+
|
|
1906
|
+
# rewriting call-sites at this point, pre-inlining, causes issues with incorrect call signatures
|
|
1907
|
+
if not self._inlining_parents:
|
|
1908
|
+
AILGraphWalker(ail_graph, _handler, replace_nodes=True).walk()
|
|
1909
|
+
|
|
1910
|
+
return ail_graph, TempClass.stack_arg_offsets, TempClass.removed_vvar_ids
|
|
1911
|
+
|
|
1912
|
+
@timethis
|
|
1913
|
+
def _make_returns(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
1914
|
+
"""
|
|
1915
|
+
Work on each return statement and fill in its return expressions.
|
|
1916
|
+
"""
|
|
1917
|
+
if self.function.calling_convention is None:
|
|
1918
|
+
# unknown calling convention. cannot do much about return expressions.
|
|
1919
|
+
return ail_graph
|
|
1920
|
+
|
|
1921
|
+
ReturnMaker(self._ail_manager, self.project.arch, self.function, ail_graph)
|
|
1922
|
+
|
|
1923
|
+
return ail_graph
|
|
1924
|
+
|
|
1925
|
+
@timethis
|
|
1926
|
+
def _make_function_prototype(self, arg_list: list[SimVariable], variable_kb):
|
|
1927
|
+
if self.function.prototype is not None:
|
|
1928
|
+
if not self.function.is_prototype_guessed:
|
|
1929
|
+
# do not overwrite an existing function prototype
|
|
1930
|
+
# if you want to re-generate the prototype, clear the existing one first
|
|
1931
|
+
return
|
|
1932
|
+
if isinstance(self.function.prototype.returnty, SimTypeFloat) or any(
|
|
1933
|
+
isinstance(arg, SimTypeFloat) for arg in self.function.prototype.args
|
|
1934
|
+
):
|
|
1935
|
+
# Type inference does not yet support floating point variables, but calling convention analysis does
|
|
1936
|
+
# FIXME: remove this branch once type inference supports floating point variables
|
|
1937
|
+
return
|
|
1938
|
+
|
|
1939
|
+
variables = variable_kb.variables[self.function.addr]
|
|
1940
|
+
func_args = []
|
|
1941
|
+
for arg in arg_list:
|
|
1942
|
+
func_arg = None
|
|
1943
|
+
arg_ty = variables.get_variable_type(arg)
|
|
1944
|
+
if arg_ty is None:
|
|
1945
|
+
# determine type based on size
|
|
1946
|
+
if isinstance(arg, (SimRegisterVariable, SimStackVariable)):
|
|
1947
|
+
if arg.size == 1:
|
|
1948
|
+
func_arg = SimTypeChar()
|
|
1949
|
+
elif arg.size == 2:
|
|
1950
|
+
func_arg = SimTypeShort()
|
|
1951
|
+
elif arg.size == 4:
|
|
1952
|
+
func_arg = SimTypeInt()
|
|
1953
|
+
elif arg.size == 8:
|
|
1954
|
+
func_arg = SimTypeLongLong()
|
|
1955
|
+
else:
|
|
1956
|
+
l.warning("Unsupported argument size %d.", arg.size)
|
|
1957
|
+
else:
|
|
1958
|
+
func_arg = arg_ty
|
|
1959
|
+
|
|
1960
|
+
func_args.append(func_arg)
|
|
1961
|
+
|
|
1962
|
+
returnty = variables.get_variable_type(self.func_ret_var)
|
|
1963
|
+
if returnty is None or isinstance(returnty, SimTypeBottom):
|
|
1964
|
+
if self.function.prototype is not None and self.function.prototype.returnty is not None:
|
|
1965
|
+
returnty = self.function.prototype.returnty
|
|
1966
|
+
else:
|
|
1967
|
+
returnty = SimTypeInt()
|
|
1968
|
+
|
|
1969
|
+
self.function.prototype = SimTypeFunction(func_args, returnty).with_arch(self.project.arch)
|
|
1970
|
+
self.function.is_prototype_guessed = False
|
|
1971
|
+
|
|
1972
|
+
@timethis
|
|
1973
|
+
def _recover_and_link_variables(
|
|
1974
|
+
self,
|
|
1975
|
+
ail_graph,
|
|
1976
|
+
arg_list: list,
|
|
1977
|
+
arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]],
|
|
1978
|
+
vvar2vvar: dict[int, int],
|
|
1979
|
+
type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]],
|
|
1980
|
+
):
|
|
1981
|
+
# variable recovery
|
|
1982
|
+
tmp_kb = KnowledgeBase(self.project) if self.variable_kb is None else self.variable_kb
|
|
1983
|
+
tmp_kb.functions = self.kb.functions
|
|
1984
|
+
vr = self.project.analyses.VariableRecoveryFast(
|
|
1985
|
+
self.function, # pylint:disable=unused-variable
|
|
1986
|
+
fail_fast=self._fail_fast, # type:ignore
|
|
1987
|
+
func_graph=ail_graph,
|
|
1988
|
+
entry_node_addr=self.entry_node_addr,
|
|
1989
|
+
kb=tmp_kb, # type:ignore
|
|
1990
|
+
track_sp=False,
|
|
1991
|
+
func_args=arg_list,
|
|
1992
|
+
func_ret_var=self.func_ret_var,
|
|
1993
|
+
unify_variables=False,
|
|
1994
|
+
func_arg_vvars=arg_vvars,
|
|
1995
|
+
vvar_to_vvar=vvar2vvar,
|
|
1996
|
+
type_hints=type_hints,
|
|
1997
|
+
)
|
|
1998
|
+
# get ground-truth types
|
|
1999
|
+
var_manager = tmp_kb.variables[self.function.addr]
|
|
2000
|
+
groundtruth = {}
|
|
2001
|
+
for variable in var_manager.variables_with_manual_types:
|
|
2002
|
+
vartype = var_manager.variable_to_types.get(variable, None)
|
|
2003
|
+
if vartype is not None:
|
|
2004
|
+
for tv in vr.var_to_typevars[variable]:
|
|
2005
|
+
groundtruth[tv] = vartype
|
|
2006
|
+
|
|
2007
|
+
if self.function.prototype is not None and not self.function.is_prototype_guessed:
|
|
2008
|
+
for arg_i, (_, variable) in arg_vvars.items():
|
|
2009
|
+
if arg_i < len(self.function.prototype.args):
|
|
2010
|
+
for tv in vr.var_to_typevars[variable]:
|
|
2011
|
+
groundtruth[tv] = self.function.prototype.args[arg_i]
|
|
2012
|
+
|
|
2013
|
+
# get maximum sizes of each stack variable, regardless of its original type
|
|
2014
|
+
stackvar_max_sizes = var_manager.get_stackvar_max_sizes(self.stack_items)
|
|
2015
|
+
tv_max_sizes = {}
|
|
2016
|
+
for v, s in stackvar_max_sizes.items():
|
|
2017
|
+
assert isinstance(v, SimStackVariable)
|
|
2018
|
+
if v in vr.var_to_typevars:
|
|
2019
|
+
for tv in vr.var_to_typevars[v]:
|
|
2020
|
+
tv_max_sizes[tv] = s
|
|
2021
|
+
if v.offset in vr.stack_offset_typevars:
|
|
2022
|
+
tv = vr.stack_offset_typevars[v.offset]
|
|
2023
|
+
tv_max_sizes[tv] = s
|
|
2024
|
+
# TODO: Type inference for global variables
|
|
2025
|
+
# run type inference
|
|
2026
|
+
if self._must_struct:
|
|
2027
|
+
must_struct = set()
|
|
2028
|
+
for var, typevars in vr.var_to_typevars.items():
|
|
2029
|
+
if var.ident in self._must_struct:
|
|
2030
|
+
must_struct |= typevars
|
|
2031
|
+
else:
|
|
2032
|
+
must_struct = None
|
|
2033
|
+
total_type_constraints = sum(len(tc) for tc in vr.type_constraints.values()) if vr.type_constraints else 0
|
|
2034
|
+
if total_type_constraints > self._max_type_constraints:
|
|
2035
|
+
l.warning(
|
|
2036
|
+
"The number of type constraints (%d) is greater than the threshold (%d). Skipping type inference.",
|
|
2037
|
+
total_type_constraints,
|
|
2038
|
+
self._max_type_constraints,
|
|
2039
|
+
)
|
|
2040
|
+
else:
|
|
2041
|
+
try:
|
|
2042
|
+
tp = self.project.analyses[Typehoon].prep(
|
|
2043
|
+
kb=tmp_kb,
|
|
2044
|
+
fail_fast=self._fail_fast,
|
|
2045
|
+
)(
|
|
2046
|
+
vr.type_constraints,
|
|
2047
|
+
vr.func_typevar,
|
|
2048
|
+
var_mapping=vr.var_to_typevars,
|
|
2049
|
+
stack_offset_tvs=vr.stack_offset_typevars,
|
|
2050
|
+
must_struct=must_struct,
|
|
2051
|
+
ground_truth=groundtruth,
|
|
2052
|
+
stackvar_max_sizes=tv_max_sizes,
|
|
2053
|
+
constraint_set_degradation_threshold=self._type_constraint_set_degradation_threshold,
|
|
2054
|
+
)
|
|
2055
|
+
# tp.pp_constraints()
|
|
2056
|
+
# tp.pp_solution()
|
|
2057
|
+
tp.update_variable_types(
|
|
2058
|
+
self.function.addr,
|
|
2059
|
+
{
|
|
2060
|
+
v: t
|
|
2061
|
+
for v, t in vr.var_to_typevars.items()
|
|
2062
|
+
if isinstance(v, (SimRegisterVariable, SimStackVariable)) or v is self.func_ret_var
|
|
2063
|
+
},
|
|
2064
|
+
vr.stack_offset_typevars,
|
|
2065
|
+
)
|
|
2066
|
+
tp.update_variable_types(
|
|
2067
|
+
"global",
|
|
2068
|
+
{
|
|
2069
|
+
v: t
|
|
2070
|
+
for v, t in vr.var_to_typevars.items()
|
|
2071
|
+
if isinstance(v, SimMemoryVariable) and not isinstance(v, SimStackVariable)
|
|
2072
|
+
},
|
|
2073
|
+
)
|
|
2074
|
+
self.typehoon = tp
|
|
2075
|
+
except Exception: # pylint:disable=broad-except
|
|
2076
|
+
if self._fail_fast:
|
|
2077
|
+
raise
|
|
2078
|
+
l.warning(
|
|
2079
|
+
"Typehoon analysis failed. Variables will not have types. Please report to GitHub.", exc_info=True
|
|
2080
|
+
)
|
|
2081
|
+
|
|
2082
|
+
# for any left-over variables, assign Bottom type (which will get "corrected" into a default type in
|
|
2083
|
+
# VariableManager)
|
|
2084
|
+
bottype = SimTypeBottom().with_arch(self.project.arch)
|
|
2085
|
+
for var in var_manager._variables:
|
|
2086
|
+
if var not in var_manager.variable_to_types:
|
|
2087
|
+
var_manager.set_variable_type(var, bottype)
|
|
2088
|
+
|
|
2089
|
+
# Unify SSA variables
|
|
2090
|
+
tmp_kb.variables.global_manager.assign_variable_names(labels=self.kb.labels, types={SimMemoryVariable})
|
|
2091
|
+
liveness = self.project.analyses.SLiveness(
|
|
2092
|
+
self.function,
|
|
2093
|
+
func_graph=ail_graph,
|
|
2094
|
+
entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
|
|
2095
|
+
arg_vvars=[vvar for vvar, _ in arg_vvars.values()],
|
|
2096
|
+
)
|
|
2097
|
+
var_manager.unify_variables(interference=liveness.interference_graph())
|
|
2098
|
+
var_manager.assign_unified_variable_names(
|
|
2099
|
+
labels=self.kb.labels,
|
|
2100
|
+
arg_names=list(self.function.prototype.arg_names) if self.function.prototype else None,
|
|
2101
|
+
reset=self._reset_variable_names,
|
|
2102
|
+
func_blocks=list(ail_graph),
|
|
2103
|
+
)
|
|
2104
|
+
|
|
2105
|
+
# Link variables and struct member information to every statement and expression
|
|
2106
|
+
for block in ail_graph.nodes():
|
|
2107
|
+
self._link_variables_on_block(block, tmp_kb)
|
|
2108
|
+
|
|
2109
|
+
if self._cache is not None:
|
|
2110
|
+
self._cache.arg_vvars = arg_vvars
|
|
2111
|
+
self._cache.type_constraints = vr.type_constraints
|
|
2112
|
+
self._cache.func_typevar = vr.func_typevar
|
|
2113
|
+
self._cache.var_to_typevar = vr.var_to_typevars
|
|
2114
|
+
self._cache.stack_offset_typevars = vr.stack_offset_typevars
|
|
2115
|
+
self._cache.stackvar_max_sizes = stackvar_max_sizes
|
|
2116
|
+
|
|
2117
|
+
return tmp_kb
|
|
2118
|
+
|
|
2119
|
+
def _link_variables_on_block(self, block, kb):
|
|
2120
|
+
"""
|
|
2121
|
+
Link atoms (AIL expressions) in the given block to corresponding variables identified previously.
|
|
2122
|
+
|
|
2123
|
+
:param ailment.Block block: The AIL block to work on.
|
|
2124
|
+
:return: None
|
|
2125
|
+
"""
|
|
2126
|
+
|
|
2127
|
+
variable_manager = kb.variables[self.function.addr]
|
|
2128
|
+
global_variables = kb.variables["global"]
|
|
2129
|
+
|
|
2130
|
+
for stmt_idx, stmt in enumerate(block.statements):
|
|
2131
|
+
stmt_type = type(stmt)
|
|
2132
|
+
if stmt_type is ailment.Stmt.Store:
|
|
2133
|
+
# find a memory variable
|
|
2134
|
+
mem_vars = variable_manager.find_variables_by_atom(block.addr, stmt_idx, stmt, block_idx=block.idx)
|
|
2135
|
+
if len(mem_vars) == 1:
|
|
2136
|
+
stmt.variable, stmt.offset = next(iter(mem_vars))
|
|
2137
|
+
else:
|
|
2138
|
+
# check if the dest address is a variable
|
|
2139
|
+
stmt: ailment.Stmt.Store
|
|
2140
|
+
# special handling for constant addresses
|
|
2141
|
+
if isinstance(stmt.addr, ailment.Expr.Const):
|
|
2142
|
+
# global variable?
|
|
2143
|
+
variables = global_variables.get_global_variables(stmt.addr.value)
|
|
2144
|
+
if variables:
|
|
2145
|
+
var = next(iter(variables))
|
|
2146
|
+
stmt.variable = var
|
|
2147
|
+
stmt.offset = 0
|
|
2148
|
+
else:
|
|
2149
|
+
self._link_variables_on_expr(
|
|
2150
|
+
variable_manager, global_variables, block, stmt_idx, stmt, stmt.addr
|
|
2151
|
+
)
|
|
2152
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.data)
|
|
2153
|
+
|
|
2154
|
+
# link struct member info
|
|
2155
|
+
if isinstance(stmt.variable, SimStackVariable):
|
|
2156
|
+
self._map_stackvar_to_struct_member(variable_manager, stmt, stmt.variable.offset)
|
|
2157
|
+
|
|
2158
|
+
elif stmt_type is ailment.Stmt.Assignment or stmt_type is ailment.Stmt.WeakAssignment:
|
|
2159
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.dst)
|
|
2160
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.src)
|
|
2161
|
+
|
|
2162
|
+
elif stmt_type is ailment.Stmt.CAS:
|
|
2163
|
+
for expr in [
|
|
2164
|
+
stmt.addr,
|
|
2165
|
+
stmt.data_lo,
|
|
2166
|
+
stmt.data_hi,
|
|
2167
|
+
stmt.expd_lo,
|
|
2168
|
+
stmt.expd_hi,
|
|
2169
|
+
stmt.old_lo,
|
|
2170
|
+
stmt.old_hi,
|
|
2171
|
+
]:
|
|
2172
|
+
if expr is not None:
|
|
2173
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr)
|
|
2174
|
+
|
|
2175
|
+
elif stmt_type is ailment.Stmt.ConditionalJump:
|
|
2176
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.condition)
|
|
2177
|
+
|
|
2178
|
+
elif stmt_type is ailment.Stmt.Jump and not isinstance(stmt.target, ailment.Expr.Const):
|
|
2179
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.target)
|
|
2180
|
+
|
|
2181
|
+
elif stmt_type is ailment.Stmt.Call:
|
|
2182
|
+
self._link_variables_on_call(variable_manager, global_variables, block, stmt_idx, stmt, is_expr=False)
|
|
2183
|
+
|
|
2184
|
+
elif stmt_type is ailment.Stmt.Return:
|
|
2185
|
+
assert isinstance(stmt, ailment.Stmt.Return)
|
|
2186
|
+
self._link_variables_on_return(variable_manager, global_variables, block, stmt_idx, stmt)
|
|
2187
|
+
|
|
2188
|
+
def _link_variables_on_return(
|
|
2189
|
+
self, variable_manager, global_variables, block: ailment.Block, stmt_idx: int, stmt: ailment.Stmt.Return
|
|
2190
|
+
):
|
|
2191
|
+
if stmt.ret_exprs:
|
|
2192
|
+
for ret_expr in stmt.ret_exprs:
|
|
2193
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, ret_expr)
|
|
2194
|
+
|
|
2195
|
+
def _link_variables_on_call(self, variable_manager, global_variables, block, stmt_idx, stmt, is_expr=False):
|
|
2196
|
+
if not isinstance(stmt.target, ailment.Expr.Const):
|
|
2197
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.target)
|
|
2198
|
+
if stmt.args:
|
|
2199
|
+
for arg in stmt.args:
|
|
2200
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, arg)
|
|
2201
|
+
if not is_expr and stmt.ret_expr:
|
|
2202
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.ret_expr)
|
|
2203
|
+
|
|
2204
|
+
def _link_variables_on_expr(self, variable_manager, global_variables, block, stmt_idx, stmt, expr):
|
|
2205
|
+
"""
|
|
2206
|
+
Link atoms (AIL expressions) in the given expression to corresponding variables identified previously.
|
|
2207
|
+
|
|
2208
|
+
:param variable_manager: Variable manager of the function.
|
|
2209
|
+
:param ailment.Block block: AIL block.
|
|
2210
|
+
:param int stmt_idx: ID of the statement.
|
|
2211
|
+
:param stmt: The AIL statement that `expr` belongs to.
|
|
2212
|
+
:param expr: The AIl expression to work on.
|
|
2213
|
+
:return: None
|
|
2214
|
+
"""
|
|
2215
|
+
|
|
2216
|
+
if type(expr) is ailment.Expr.Register:
|
|
2217
|
+
# find a register variable
|
|
2218
|
+
reg_vars = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
|
|
2219
|
+
final_reg_vars = set()
|
|
2220
|
+
if len(reg_vars) > 1:
|
|
2221
|
+
# take phi variables
|
|
2222
|
+
for reg_var in reg_vars:
|
|
2223
|
+
if variable_manager.is_phi_variable(reg_var[0]):
|
|
2224
|
+
final_reg_vars.add(reg_var)
|
|
2225
|
+
else:
|
|
2226
|
+
final_reg_vars = reg_vars
|
|
2227
|
+
if len(final_reg_vars) >= 1:
|
|
2228
|
+
reg_var, offset = next(iter(final_reg_vars))
|
|
2229
|
+
expr.variable = reg_var
|
|
2230
|
+
expr.variable_offset = offset
|
|
2231
|
+
|
|
2232
|
+
elif type(expr) is ailment.Expr.VirtualVariable:
|
|
2233
|
+
vars_ = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
|
|
2234
|
+
if len(vars_) >= 1:
|
|
2235
|
+
var, offset = next(iter(vars_))
|
|
2236
|
+
expr.variable = var
|
|
2237
|
+
expr.variable_offset = offset
|
|
2238
|
+
|
|
2239
|
+
if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_stack:
|
|
2240
|
+
self._map_stackvar_to_struct_member(variable_manager, expr, expr.stack_offset)
|
|
2241
|
+
|
|
2242
|
+
elif type(expr) is ailment.Expr.Load:
|
|
2243
|
+
variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
|
|
2244
|
+
if len(variables) == 0:
|
|
2245
|
+
# if it's a constant addr, maybe it's referencing an extern location
|
|
2246
|
+
base_addr, offset = self.parse_variable_addr(expr.addr)
|
|
2247
|
+
if offset is not None and isinstance(offset, ailment.Expr.Expression):
|
|
2248
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, offset)
|
|
2249
|
+
if base_addr is not None:
|
|
2250
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, base_addr)
|
|
2251
|
+
|
|
2252
|
+
# if we are accessing the variable directly (offset == 0), we link the variable onto this expression
|
|
2253
|
+
if (
|
|
2254
|
+
offset == 0 or (isinstance(offset, ailment.Expr.Const) and offset.value == 0)
|
|
2255
|
+
) and "reference_variable" in base_addr.tags:
|
|
2256
|
+
expr.variable = base_addr.tags["reference_variable"]
|
|
2257
|
+
expr.variable_offset = base_addr.tags["reference_variable_offset"]
|
|
2258
|
+
|
|
2259
|
+
if base_addr is None and offset is None:
|
|
2260
|
+
# this is a local variable
|
|
2261
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.addr)
|
|
2262
|
+
if "reference_variable" in expr.addr.tags and expr.addr.tags["reference_variable"] is not None:
|
|
2263
|
+
# copy over the variable to this expr since the variable on a constant is supposed to be a
|
|
2264
|
+
# reference variable.
|
|
2265
|
+
expr.variable = expr.addr.tags["reference_variable"]
|
|
2266
|
+
expr.variable_offset = expr.addr.tags["reference_variable_offset"]
|
|
2267
|
+
else:
|
|
2268
|
+
if len(variables) > 1:
|
|
2269
|
+
l.error(
|
|
2270
|
+
"More than one variable are available for atom %s. Consider fixing it using phi nodes.", expr
|
|
2271
|
+
)
|
|
2272
|
+
var, offset = next(iter(variables))
|
|
2273
|
+
expr.variable = var
|
|
2274
|
+
expr.variable_offset = offset
|
|
2275
|
+
|
|
2276
|
+
if isinstance(var, SimStackVariable):
|
|
2277
|
+
self._map_stackvar_to_struct_member(variable_manager, expr, var.offset)
|
|
2278
|
+
|
|
2279
|
+
elif type(expr) is ailment.Expr.BinaryOp:
|
|
2280
|
+
variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
|
|
2281
|
+
if len(variables) >= 1:
|
|
2282
|
+
var, offset = next(iter(variables))
|
|
2283
|
+
expr.variable = var
|
|
2284
|
+
expr.variable_offset = offset
|
|
2285
|
+
else:
|
|
2286
|
+
self._link_variables_on_expr(
|
|
2287
|
+
variable_manager, global_variables, block, stmt_idx, stmt, expr.operands[0]
|
|
2288
|
+
)
|
|
2289
|
+
self._link_variables_on_expr(
|
|
2290
|
+
variable_manager, global_variables, block, stmt_idx, stmt, expr.operands[1]
|
|
2291
|
+
)
|
|
2292
|
+
|
|
2293
|
+
elif type(expr) is ailment.Expr.UnaryOp:
|
|
2294
|
+
variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
|
|
2295
|
+
if len(variables) >= 1:
|
|
2296
|
+
var, offset = next(iter(variables))
|
|
2297
|
+
expr.variable = var
|
|
2298
|
+
expr.variable_offset = offset
|
|
2299
|
+
else:
|
|
2300
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.operand)
|
|
2301
|
+
|
|
2302
|
+
elif type(expr) in {ailment.Expr.Convert, ailment.Expr.Reinterpret}:
|
|
2303
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.operand)
|
|
2304
|
+
|
|
2305
|
+
elif type(expr) is ailment.Expr.ITE:
|
|
2306
|
+
variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
|
|
2307
|
+
if len(variables) >= 1:
|
|
2308
|
+
var, offset = next(iter(variables))
|
|
2309
|
+
expr.variable = var
|
|
2310
|
+
expr.variable_offset = offset
|
|
2311
|
+
else:
|
|
2312
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.cond)
|
|
2313
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.iftrue)
|
|
2314
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.iffalse)
|
|
2315
|
+
|
|
2316
|
+
elif isinstance(expr, ailment.Expr.BasePointerOffset):
|
|
2317
|
+
variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
|
|
2318
|
+
if len(variables) >= 1:
|
|
2319
|
+
var, offset = next(iter(variables))
|
|
2320
|
+
expr.variable = var
|
|
2321
|
+
expr.variable_offset = offset
|
|
2322
|
+
|
|
2323
|
+
elif isinstance(expr, ailment.Expr.Const) and expr.is_int:
|
|
2324
|
+
# custom string?
|
|
2325
|
+
if expr.tags.get("custom_string", False):
|
|
2326
|
+
s = self.kb.custom_strings[expr.value]
|
|
2327
|
+
ty = (
|
|
2328
|
+
expr.tags["type"]
|
|
2329
|
+
if "type" in expr.tags
|
|
2330
|
+
else SimTypePointer(SimTypeChar()).with_arch(self.project.arch)
|
|
2331
|
+
)
|
|
2332
|
+
expr.tags["reference_values"] = {
|
|
2333
|
+
ty: s,
|
|
2334
|
+
}
|
|
2335
|
+
else:
|
|
2336
|
+
# global variable?
|
|
2337
|
+
global_vars = global_variables.get_global_variables(expr.value_int)
|
|
2338
|
+
# detect if there is a related symbol
|
|
2339
|
+
if not global_vars and self.project.loader.find_object_containing(expr.value_int):
|
|
2340
|
+
symbol = self.project.loader.find_symbol(expr.value)
|
|
2341
|
+
if symbol is not None:
|
|
2342
|
+
# Create a new global variable if there isn't one already
|
|
2343
|
+
global_vars = global_variables.get_global_variables(symbol.rebased_addr)
|
|
2344
|
+
if not global_vars:
|
|
2345
|
+
global_var = SimMemoryVariable(symbol.rebased_addr, symbol.size, name=symbol.name)
|
|
2346
|
+
global_variables.add_variable("global", global_var.addr, global_var)
|
|
2347
|
+
global_vars = {global_var}
|
|
2348
|
+
if global_vars:
|
|
2349
|
+
global_var = next(iter(global_vars))
|
|
2350
|
+
expr.tags["reference_variable"] = global_var
|
|
2351
|
+
expr.tags["reference_variable_offset"] = 0
|
|
2352
|
+
|
|
2353
|
+
elif isinstance(expr, ailment.Stmt.Call):
|
|
2354
|
+
self._link_variables_on_call(variable_manager, global_variables, block, stmt_idx, expr, is_expr=True)
|
|
2355
|
+
|
|
2356
|
+
elif isinstance(expr, ailment.Expr.VEXCCallExpression):
|
|
2357
|
+
for operand in expr.operands:
|
|
2358
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, operand)
|
|
2359
|
+
|
|
2360
|
+
elif isinstance(expr, ailment.Expr.DirtyExpression):
|
|
2361
|
+
for operand in expr.operands:
|
|
2362
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, operand)
|
|
2363
|
+
if expr.maddr:
|
|
2364
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.maddr)
|
|
2365
|
+
if expr.guard:
|
|
2366
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.guard)
|
|
2367
|
+
|
|
2368
|
+
elif isinstance(expr, ailment.Expr.Phi):
|
|
2369
|
+
for _, vvar in expr.src_and_vvars:
|
|
2370
|
+
if vvar is not None:
|
|
2371
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, vvar)
|
|
2372
|
+
|
|
2373
|
+
def _map_stackvar_to_struct_member(
|
|
2374
|
+
self,
|
|
2375
|
+
variable_manager,
|
|
2376
|
+
expr_or_stmt: ailment.expression.Expression | ailment.statement.Statement,
|
|
2377
|
+
the_stack_offset: int,
|
|
2378
|
+
) -> bool:
|
|
2379
|
+
any_struct_found = False
|
|
2380
|
+
off = the_stack_offset
|
|
2381
|
+
for stack_off in variable_manager.stack_offset_to_struct.irange(maximum=off, reverse=True):
|
|
2382
|
+
the_var, vartype = variable_manager.stack_offset_to_struct[stack_off]
|
|
2383
|
+
if stack_off <= off < stack_off + vartype.size // self.project.arch.byte_width:
|
|
2384
|
+
expr_or_stmt.tags["struct_member_info"] = off - stack_off, the_var, vartype
|
|
2385
|
+
any_struct_found = True
|
|
2386
|
+
break
|
|
2387
|
+
if stack_off + vartype.size // self.project.arch.byte_width <= off:
|
|
2388
|
+
break
|
|
2389
|
+
return any_struct_found
|
|
2390
|
+
|
|
2391
|
+
def _function_graph_to_ail_graph(self, func_graph, blocks_by_addr_and_size=None):
|
|
2392
|
+
if blocks_by_addr_and_size is None:
|
|
2393
|
+
blocks_by_addr_and_size = self._blocks_by_addr_and_size
|
|
2394
|
+
assert blocks_by_addr_and_size is not None
|
|
2395
|
+
|
|
2396
|
+
graph = networkx.DiGraph()
|
|
2397
|
+
|
|
2398
|
+
entry_node = next(iter(node for node in func_graph if node.addr == self.entry_node_addr[0]), None)
|
|
2399
|
+
if entry_node is None:
|
|
2400
|
+
raise AngrDecompilationError(
|
|
2401
|
+
f"Entry node with address {self.entry_node_addr[0]:#x} not found in the function graph"
|
|
2402
|
+
)
|
|
2403
|
+
|
|
2404
|
+
# add the entry node into the graph
|
|
2405
|
+
ail_block = blocks_by_addr_and_size.get((entry_node.addr, entry_node.size))
|
|
2406
|
+
if ail_block is None:
|
|
2407
|
+
raise AngrDecompilationError(f"AIL block at address {entry_node.addr:#x} not found")
|
|
2408
|
+
graph.add_node(ail_block)
|
|
2409
|
+
|
|
2410
|
+
# get all descendants and only include them in the AIL graph.
|
|
2411
|
+
# this way all unreachable blocks will be excluded from the AIL graph.
|
|
2412
|
+
descendants = networkx.descendants(func_graph, entry_node) | {entry_node}
|
|
2413
|
+
for src_node, dst_node, data in networkx.subgraph_view(
|
|
2414
|
+
func_graph, filter_node=lambda n: n in descendants
|
|
2415
|
+
).edges(data=True):
|
|
2416
|
+
src = blocks_by_addr_and_size.get((src_node.addr, src_node.size))
|
|
2417
|
+
dst = blocks_by_addr_and_size.get((dst_node.addr, dst_node.size))
|
|
2418
|
+
|
|
2419
|
+
if src is not None and dst is not None:
|
|
2420
|
+
graph.add_edge(src, dst, **data)
|
|
2421
|
+
|
|
2422
|
+
return graph
|
|
2423
|
+
|
|
2424
|
+
@staticmethod
|
|
2425
|
+
def _duplicate_orphaned_cond_jumps(ail_graph) -> networkx.DiGraph:
|
|
2426
|
+
"""
|
|
2427
|
+
Find conditional jumps that are orphaned (e.g., being the only instruction of the block). If these blocks have
|
|
2428
|
+
multiple predecessors, duplicate them to all predecessors. This is a workaround for cases where these
|
|
2429
|
+
conditional jumps rely on comparisons in more than one predecessor and we cannot resolve ccalls into
|
|
2430
|
+
comparisons.
|
|
2431
|
+
|
|
2432
|
+
This pass runs before any SSA transformations.
|
|
2433
|
+
|
|
2434
|
+
# 140017162 jz short 1400171e1
|
|
2435
|
+
"""
|
|
2436
|
+
|
|
2437
|
+
for block in list(ail_graph):
|
|
2438
|
+
if (
|
|
2439
|
+
len(block.statements) > 1
|
|
2440
|
+
and block.statements[0].tags["ins_addr"] == block.statements[-1].tags["ins_addr"]
|
|
2441
|
+
):
|
|
2442
|
+
preds = list(ail_graph.predecessors(block))
|
|
2443
|
+
if len(preds) > 1 and block not in preds:
|
|
2444
|
+
has_ccall = any(
|
|
2445
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
2446
|
+
and isinstance(stmt.src, ailment.Expr.VEXCCallExpression)
|
|
2447
|
+
for stmt in block.statements
|
|
2448
|
+
)
|
|
2449
|
+
if has_ccall:
|
|
2450
|
+
# duplicate this block to its predecessors!
|
|
2451
|
+
preds = sorted(preds, key=lambda x: x.addr)
|
|
2452
|
+
succs = sorted(ail_graph.successors(block), key=lambda x: x.addr)
|
|
2453
|
+
# FIXME: We should track block IDs globally and ensure block IDs do not collide
|
|
2454
|
+
block_idx_start = block.idx + 1 if block.idx is not None else 1
|
|
2455
|
+
for pred in preds[1:]:
|
|
2456
|
+
ail_graph.remove_edge(pred, block)
|
|
2457
|
+
new_block = block.copy()
|
|
2458
|
+
new_block.idx = block_idx_start
|
|
2459
|
+
block_idx_start += 1
|
|
2460
|
+
ail_graph.add_edge(pred, new_block)
|
|
2461
|
+
for succ in succs:
|
|
2462
|
+
ail_graph.add_edge(new_block, succ if succ is not block else new_block)
|
|
2463
|
+
|
|
2464
|
+
return ail_graph
|
|
2465
|
+
|
|
2466
|
+
def _rewrite_jump_rax_calls(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
2467
|
+
"""
|
|
2468
|
+
Rewrite calls to special functions (e.g., guard_dispatch_icall_nop) into `call rax`.
|
|
2469
|
+
"""
|
|
2470
|
+
|
|
2471
|
+
if self.project.arch.name != "AMD64":
|
|
2472
|
+
return ail_graph
|
|
2473
|
+
if self._cfg is None:
|
|
2474
|
+
return ail_graph
|
|
2475
|
+
|
|
2476
|
+
for block in ail_graph:
|
|
2477
|
+
if not block.statements:
|
|
2478
|
+
continue
|
|
2479
|
+
assert block.addr is not None
|
|
2480
|
+
last_stmt = block.statements[-1]
|
|
2481
|
+
if isinstance(last_stmt, ailment.Stmt.Call):
|
|
2482
|
+
# we can't examine the call target at this point because constant propagation hasn't run yet; we consult
|
|
2483
|
+
# the CFG instead
|
|
2484
|
+
callsite_node = self._cfg.get_any_node(block.addr, anyaddr=True)
|
|
2485
|
+
if callsite_node is None:
|
|
2486
|
+
break
|
|
2487
|
+
callees = self._cfg.get_successors(callsite_node, jumpkind="Ijk_Call")
|
|
2488
|
+
if len(callees) != 1:
|
|
2489
|
+
break
|
|
2490
|
+
callee = callees[0].addr
|
|
2491
|
+
if self.kb.functions.contains_addr(callee):
|
|
2492
|
+
callee_func = self.kb.functions.get_by_addr(callee)
|
|
2493
|
+
if callee_func.info.get("jmp_rax", False) is True:
|
|
2494
|
+
# rewrite this statement into Call(rax)
|
|
2495
|
+
call_stmt = last_stmt.copy()
|
|
2496
|
+
call_stmt.target = ailment.Expr.Register(
|
|
2497
|
+
self._ail_manager.next_atom(),
|
|
2498
|
+
None,
|
|
2499
|
+
self.project.arch.registers["rax"][0],
|
|
2500
|
+
64,
|
|
2501
|
+
ins_addr=call_stmt.tags["ins_addr"],
|
|
2502
|
+
)
|
|
2503
|
+
block.statements[-1] = call_stmt
|
|
2504
|
+
|
|
2505
|
+
return ail_graph
|
|
2506
|
+
|
|
2507
|
+
def _rewrite_ite_expressions(self, ail_graph):
|
|
2508
|
+
cfg = self._cfg
|
|
2509
|
+
for block in list(ail_graph):
|
|
2510
|
+
if cfg is not None and block.addr in cfg.jump_tables:
|
|
2511
|
+
continue
|
|
2512
|
+
|
|
2513
|
+
ite_ins_addrs = []
|
|
2514
|
+
cas_ins_addrs = set()
|
|
2515
|
+
for stmt in block.statements:
|
|
2516
|
+
if isinstance(stmt, ailment.Stmt.CAS):
|
|
2517
|
+
# we do not rewrite ITE statements that are caused by CAS statements
|
|
2518
|
+
cas_ins_addrs.add(stmt.tags["ins_addr"])
|
|
2519
|
+
elif (
|
|
2520
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
2521
|
+
and isinstance(stmt.src, ailment.Expr.ITE)
|
|
2522
|
+
and stmt.tags["ins_addr"] not in ite_ins_addrs
|
|
2523
|
+
and stmt.tags["ins_addr"] not in cas_ins_addrs
|
|
2524
|
+
):
|
|
2525
|
+
ite_ins_addrs.append(stmt.tags["ins_addr"])
|
|
2526
|
+
|
|
2527
|
+
if ite_ins_addrs:
|
|
2528
|
+
block_addr = block.addr
|
|
2529
|
+
for ite_ins_addr in ite_ins_addrs:
|
|
2530
|
+
block_addr = self._create_triangle_for_ite_expression(ail_graph, block_addr, ite_ins_addr)
|
|
2531
|
+
if block_addr is None or block_addr >= block.addr + block.original_size:
|
|
2532
|
+
break
|
|
2533
|
+
|
|
2534
|
+
def _create_triangle_for_ite_expression(self, ail_graph, block_addr: int, ite_ins_addr: int):
|
|
2535
|
+
ite_insn_only_block = self.project.factory.block(ite_ins_addr, num_inst=1)
|
|
2536
|
+
ite_insn_size = ite_insn_only_block.size
|
|
2537
|
+
assert ite_insn_size is not None
|
|
2538
|
+
if ite_insn_size <= 2: # we need an address for true_block and another address for false_block
|
|
2539
|
+
return None
|
|
2540
|
+
if ite_insn_only_block.vex.exit_statements:
|
|
2541
|
+
return None
|
|
2542
|
+
|
|
2543
|
+
# relift the head and the ITE instruction
|
|
2544
|
+
new_head = self.project.factory.block(
|
|
2545
|
+
block_addr, size=ite_ins_addr - block_addr + ite_insn_size, cross_insn_opt=False
|
|
2546
|
+
)
|
|
2547
|
+
new_head_ail = ailment.IRSBConverter.convert(new_head.vex, self._ail_manager)
|
|
2548
|
+
# remove all statements between the ITE expression and the very end of the block
|
|
2549
|
+
ite_expr_stmt_idx = None
|
|
2550
|
+
ite_expr_stmt = None
|
|
2551
|
+
for idx, stmt in enumerate(new_head_ail.statements):
|
|
2552
|
+
if isinstance(stmt, ailment.Stmt.Assignment) and isinstance(stmt.src, ailment.Expr.ITE):
|
|
2553
|
+
ite_expr_stmt_idx = idx
|
|
2554
|
+
ite_expr_stmt = stmt
|
|
2555
|
+
break
|
|
2556
|
+
if ite_expr_stmt_idx is None:
|
|
2557
|
+
return None
|
|
2558
|
+
assert ite_expr_stmt is not None
|
|
2559
|
+
|
|
2560
|
+
ite_expr: ailment.Expr.ITE = ite_expr_stmt.src # type: ignore
|
|
2561
|
+
new_head_ail.statements = new_head_ail.statements[:ite_expr_stmt_idx]
|
|
2562
|
+
# build the conditional jump
|
|
2563
|
+
true_block_addr = ite_ins_addr + 1
|
|
2564
|
+
false_block_addr = ite_ins_addr + 2
|
|
2565
|
+
cond_jump_stmt = ailment.Stmt.ConditionalJump(
|
|
2566
|
+
ite_expr_stmt.idx,
|
|
2567
|
+
ite_expr.cond,
|
|
2568
|
+
ailment.Expr.Const(None, None, true_block_addr, self.project.arch.bits, **ite_expr_stmt.tags),
|
|
2569
|
+
ailment.Expr.Const(None, None, false_block_addr, self.project.arch.bits, **ite_expr_stmt.tags),
|
|
2570
|
+
**ite_expr_stmt.tags,
|
|
2571
|
+
)
|
|
2572
|
+
new_head_ail.statements.append(cond_jump_stmt)
|
|
2573
|
+
|
|
2574
|
+
# build the true block
|
|
2575
|
+
true_block = self.project.factory.block(ite_ins_addr, num_inst=1)
|
|
2576
|
+
true_block_ail = ailment.IRSBConverter.convert(true_block.vex, self._ail_manager)
|
|
2577
|
+
true_block_ail.addr = true_block_addr
|
|
2578
|
+
|
|
2579
|
+
ite_expr_stmt_idx = None
|
|
2580
|
+
ite_expr_stmt = None
|
|
2581
|
+
for idx, stmt in enumerate(true_block_ail.statements):
|
|
2582
|
+
if isinstance(stmt, ailment.Stmt.Assignment) and isinstance(stmt.src, ailment.Expr.ITE):
|
|
2583
|
+
ite_expr_stmt_idx = idx
|
|
2584
|
+
ite_expr_stmt = stmt
|
|
2585
|
+
break
|
|
2586
|
+
if ite_expr_stmt_idx is None:
|
|
2587
|
+
return None
|
|
2588
|
+
assert ite_expr_stmt is not None
|
|
2589
|
+
|
|
2590
|
+
true_block_ail.statements[ite_expr_stmt_idx] = ailment.Stmt.Assignment(
|
|
2591
|
+
ite_expr_stmt.idx, ite_expr_stmt.dst, ite_expr_stmt.src.iftrue, **ite_expr_stmt.tags
|
|
2592
|
+
)
|
|
2593
|
+
|
|
2594
|
+
# build the false block
|
|
2595
|
+
false_block = self.project.factory.block(ite_ins_addr, num_inst=1)
|
|
2596
|
+
false_block_ail = ailment.IRSBConverter.convert(false_block.vex, self._ail_manager)
|
|
2597
|
+
false_block_ail.addr = false_block_addr
|
|
2598
|
+
|
|
2599
|
+
ite_expr_stmt_idx = None
|
|
2600
|
+
ite_expr_stmt = None
|
|
2601
|
+
for idx, stmt in enumerate(false_block_ail.statements):
|
|
2602
|
+
if isinstance(stmt, ailment.Stmt.Assignment) and isinstance(stmt.src, ailment.Expr.ITE):
|
|
2603
|
+
ite_expr_stmt_idx = idx
|
|
2604
|
+
ite_expr_stmt = stmt
|
|
2605
|
+
break
|
|
2606
|
+
if ite_expr_stmt_idx is None:
|
|
2607
|
+
return None
|
|
2608
|
+
assert ite_expr_stmt is not None
|
|
2609
|
+
|
|
2610
|
+
false_block_ail.statements[ite_expr_stmt_idx] = ailment.Stmt.Assignment(
|
|
2611
|
+
ite_expr_stmt.idx, ite_expr_stmt.dst, ite_expr_stmt.src.iffalse, **ite_expr_stmt.tags
|
|
2612
|
+
)
|
|
2613
|
+
|
|
2614
|
+
original_block = next(iter(b for b in ail_graph if b.addr == block_addr))
|
|
2615
|
+
|
|
2616
|
+
original_block_in_edges = list(ail_graph.in_edges(original_block, data=True))
|
|
2617
|
+
original_block_out_edges = list(ail_graph.out_edges(original_block, data=True))
|
|
2618
|
+
|
|
2619
|
+
# build the target block if the target block does not exist in the current function
|
|
2620
|
+
end_block_addr = ite_ins_addr + ite_insn_size
|
|
2621
|
+
if block_addr < end_block_addr < block_addr + original_block.original_size:
|
|
2622
|
+
end_block = self.project.factory.block(
|
|
2623
|
+
ite_ins_addr + ite_insn_size,
|
|
2624
|
+
size=block_addr + original_block.original_size - (ite_ins_addr + ite_insn_size),
|
|
2625
|
+
cross_insn_opt=False,
|
|
2626
|
+
)
|
|
2627
|
+
end_block_ail = ailment.IRSBConverter.convert(end_block.vex, self._ail_manager)
|
|
2628
|
+
else:
|
|
2629
|
+
try:
|
|
2630
|
+
end_block_ail = next(iter(b for b in ail_graph if b.addr == end_block_addr))
|
|
2631
|
+
except StopIteration:
|
|
2632
|
+
return None
|
|
2633
|
+
|
|
2634
|
+
# last check: if the first instruction of the end block has Sar, then we bail (due to the peephole optimization
|
|
2635
|
+
# SarToSignedDiv)
|
|
2636
|
+
for stmt in end_block_ail.statements:
|
|
2637
|
+
if stmt.tags["ins_addr"] > end_block_ail.addr:
|
|
2638
|
+
break
|
|
2639
|
+
if ( # pylint:disable=no-member
|
|
2640
|
+
isinstance(stmt, ailment.Stmt.Assignment)
|
|
2641
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
2642
|
+
and stmt.src.op == "Sar"
|
|
2643
|
+
):
|
|
2644
|
+
return None
|
|
2645
|
+
|
|
2646
|
+
# corner-case: the last statement of original_block might have been patched by _remove_redundant_jump_blocks.
|
|
2647
|
+
# we detect such case and fix it in new_head_ail
|
|
2648
|
+
self._remove_redundant_jump_blocks_repatch_relifted_block(original_block, end_block_ail)
|
|
2649
|
+
|
|
2650
|
+
ail_graph.remove_node(original_block)
|
|
2651
|
+
|
|
2652
|
+
if end_block_ail not in ail_graph:
|
|
2653
|
+
# newly created. add it and the necessary edges into the graph
|
|
2654
|
+
for _, dst, data in original_block_out_edges:
|
|
2655
|
+
if dst is original_block:
|
|
2656
|
+
ail_graph.add_edge(end_block_ail, new_head_ail, **data)
|
|
2657
|
+
else:
|
|
2658
|
+
ail_graph.add_edge(end_block_ail, dst, **data)
|
|
2659
|
+
|
|
2660
|
+
# in edges
|
|
2661
|
+
for src, _, data in original_block_in_edges:
|
|
2662
|
+
if src is original_block:
|
|
2663
|
+
# loop
|
|
2664
|
+
ail_graph.add_edge(end_block_ail, new_head_ail, **data)
|
|
2665
|
+
else:
|
|
2666
|
+
ail_graph.add_edge(src, new_head_ail, **data)
|
|
2667
|
+
|
|
2668
|
+
# triangle
|
|
2669
|
+
ail_graph.add_edge(new_head_ail, true_block_ail)
|
|
2670
|
+
ail_graph.add_edge(new_head_ail, false_block_ail)
|
|
2671
|
+
ail_graph.add_edge(true_block_ail, end_block_ail)
|
|
2672
|
+
ail_graph.add_edge(false_block_ail, end_block_ail)
|
|
2673
|
+
|
|
2674
|
+
return end_block_ail.addr
|
|
2675
|
+
|
|
2676
|
+
def _fix_abnormal_switch_case_heads(self, ail_graph: networkx.DiGraph) -> None:
|
|
2677
|
+
"""
|
|
2678
|
+
Detect the existence of switch-case heads whose indirect jump node has more than one predecessor, and attempt
|
|
2679
|
+
to fix those cases by altering the graph.
|
|
2680
|
+
"""
|
|
2681
|
+
|
|
2682
|
+
if self._cfg is None:
|
|
2683
|
+
return
|
|
2684
|
+
|
|
2685
|
+
if not self._cfg.jump_tables:
|
|
2686
|
+
return
|
|
2687
|
+
|
|
2688
|
+
node_dict: defaultdict[int, list[ailment.Block]] = defaultdict(list)
|
|
2689
|
+
for node in ail_graph:
|
|
2690
|
+
node_dict[node.addr].append(node)
|
|
2691
|
+
|
|
2692
|
+
candidates = []
|
|
2693
|
+
for block_addr in self._cfg.jump_tables:
|
|
2694
|
+
block_nodes = node_dict[block_addr]
|
|
2695
|
+
for block_node in block_nodes:
|
|
2696
|
+
if ail_graph.in_degree[block_node] > 1:
|
|
2697
|
+
# found it
|
|
2698
|
+
candidates.append(block_node)
|
|
2699
|
+
|
|
2700
|
+
if not candidates:
|
|
2701
|
+
return
|
|
2702
|
+
|
|
2703
|
+
sorted_nodes = GraphUtils.quasi_topological_sort_nodes(ail_graph)
|
|
2704
|
+
node_to_rank = {node: rank for rank, node in enumerate(sorted_nodes)}
|
|
2705
|
+
for candidate in candidates:
|
|
2706
|
+
# determine the "intended" switch-case head using topological order
|
|
2707
|
+
preds = list(ail_graph.predecessors(candidate))
|
|
2708
|
+
preds = sorted(preds, key=lambda n_: node_to_rank[n_])
|
|
2709
|
+
intended_head = preds[0]
|
|
2710
|
+
other_heads = preds[1:]
|
|
2711
|
+
|
|
2712
|
+
# I've seen cases where there is one more block between the actual intended head and the candidate.
|
|
2713
|
+
# binary 7995a0325b446c462bdb6ae10b692eee2ecadd8e888e9d7729befe4412007afb, block 0x140032760
|
|
2714
|
+
while ail_graph.out_degree[intended_head] == 1 and ail_graph.in_degree[intended_head] == 1:
|
|
2715
|
+
intended_head = next(iter(ail_graph.predecessors(intended_head)))
|
|
2716
|
+
|
|
2717
|
+
# now here is the tricky part. there are two cases:
|
|
2718
|
+
# Case 1: the intended head and the other heads share the same suffix (of instructions)
|
|
2719
|
+
# Example:
|
|
2720
|
+
# ; binary 736cb27201273f6c4f83da362c9595b50d12333362e02bc7a77dd327cc6b045a
|
|
2721
|
+
# 0041DA97 mov ecx, [esp+2Ch+var_18] ; this is the intended head
|
|
2722
|
+
# 0041DA9B mov ecx, [ecx]
|
|
2723
|
+
# 0041DA9D cmp ecx, 9
|
|
2724
|
+
# 0041DAA0 jbe loc_41D5A8
|
|
2725
|
+
#
|
|
2726
|
+
# 0041D599 mov ecx, [ecx] ; this is the other head
|
|
2727
|
+
# 0041D59B mov [esp+2Ch+var_10], eax
|
|
2728
|
+
# 0041D59F cmp ecx, 9
|
|
2729
|
+
# 0041D5A2 ja loc_41DAA6 ; fallthrough to 0x41d5a8
|
|
2730
|
+
# given the overlap of two instructions at the end of both blocks, we will alter the second block to remove
|
|
2731
|
+
# the overlapped instructions and add an unconditional jump so that it jumps to 0x41da9d.
|
|
2732
|
+
# this is the most common case created by jump threading optimization in compilers. it's easy to handle.
|
|
2733
|
+
|
|
2734
|
+
# Case 2 & 3: the intended head and the other heads do not share the same suffix of instructions. in this
|
|
2735
|
+
# case, we have two choices:
|
|
2736
|
+
# Case 2: The intended head has two successors, but at least one unintended head has only one successor.
|
|
2737
|
+
# we cannot reliably convert the blocks into a properly structured switch-case construct. we will
|
|
2738
|
+
# last instruction of all other heads to jump to the cmp instruction in the intended head, but do
|
|
2739
|
+
# not remove any other instructions in these other heads. this is unsound, but is the best we can
|
|
2740
|
+
# do in this case.
|
|
2741
|
+
# Case 3: The intended head has only one successor (which is the indirect jump node). during structuring,
|
|
2742
|
+
# we expect it will be structured as a no-default-node switch-case construct. in this case, we
|
|
2743
|
+
# can simply remove the edges from all other heads to the jump node and only leave the edge from
|
|
2744
|
+
# the intended head to the jump node. we will see goto statements in the output, but this will
|
|
2745
|
+
# lead to correct structuring result.
|
|
2746
|
+
|
|
2747
|
+
overlaps = [self._get_overlapping_suffix_instructions(intended_head, head) for head in other_heads]
|
|
2748
|
+
if overlaps and (overlap := min(overlaps)) > 0:
|
|
2749
|
+
# Case 1
|
|
2750
|
+
self._fix_abnormal_switch_case_heads_case1(ail_graph, candidate, intended_head, other_heads, overlap)
|
|
2751
|
+
elif ail_graph.out_degree[intended_head] == 2:
|
|
2752
|
+
# Case 2
|
|
2753
|
+
l.warning("Switch-case at %#x has multiple head nodes but cannot be fixed soundly.", candidate.addr)
|
|
2754
|
+
# find the comparison instruction in the intended head
|
|
2755
|
+
comparison_stmt = None
|
|
2756
|
+
if "cc_op" in self.project.arch.registers:
|
|
2757
|
+
comparison_stmt = next(
|
|
2758
|
+
iter(
|
|
2759
|
+
stmt
|
|
2760
|
+
for stmt in intended_head.statements
|
|
2761
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2762
|
+
and isinstance(stmt.dst, ailment.Expr.Register)
|
|
2763
|
+
and stmt.dst.reg_offset == self.project.arch.registers["cc_op"][0]
|
|
2764
|
+
),
|
|
2765
|
+
None,
|
|
2766
|
+
)
|
|
2767
|
+
intended_head_block = self.project.factory.block(intended_head.addr, size=intended_head.original_size)
|
|
2768
|
+
if comparison_stmt is not None:
|
|
2769
|
+
cmp_rpos = len(intended_head_block.instruction_addrs) - intended_head_block.instruction_addrs.index(
|
|
2770
|
+
comparison_stmt.tags["ins_addr"]
|
|
2771
|
+
)
|
|
2772
|
+
else:
|
|
2773
|
+
cmp_rpos = min(len(intended_head_block.instruction_addrs), 2)
|
|
2774
|
+
self._fix_abnormal_switch_case_heads_case2(
|
|
2775
|
+
ail_graph,
|
|
2776
|
+
candidate,
|
|
2777
|
+
intended_head,
|
|
2778
|
+
other_heads,
|
|
2779
|
+
intended_head_split_insns=cmp_rpos,
|
|
2780
|
+
other_head_split_insns=0,
|
|
2781
|
+
)
|
|
2782
|
+
else:
|
|
2783
|
+
# Case 3
|
|
2784
|
+
self._fix_abnormal_switch_case_heads_case3(
|
|
2785
|
+
candidate,
|
|
2786
|
+
other_heads,
|
|
2787
|
+
)
|
|
2788
|
+
|
|
2789
|
+
def _get_overlapping_suffix_instructions(self, ailblock_0: ailment.Block, ailblock_1: ailment.Block) -> int:
|
|
2790
|
+
# we first compare their ending conditional jumps
|
|
2791
|
+
if not self._get_overlapping_suffix_instructions_compare_conditional_jumps(ailblock_0, ailblock_1):
|
|
2792
|
+
return 0
|
|
2793
|
+
|
|
2794
|
+
# we re-lift the blocks and compare the instructions
|
|
2795
|
+
block_0 = self.project.factory.block(ailblock_0.addr, size=ailblock_0.original_size)
|
|
2796
|
+
block_1 = self.project.factory.block(ailblock_1.addr, size=ailblock_1.original_size)
|
|
2797
|
+
|
|
2798
|
+
i0 = len(block_0.capstone.insns) - 2
|
|
2799
|
+
i1 = len(block_1.capstone.insns) - 2
|
|
2800
|
+
overlap = 1
|
|
2801
|
+
while i0 >= 0 and i1 >= 0:
|
|
2802
|
+
same = self._get_overlapping_suffix_instructions_compare_instructions(
|
|
2803
|
+
block_0.capstone.insns[i0], block_1.capstone.insns[i1]
|
|
2804
|
+
)
|
|
2805
|
+
if not same:
|
|
2806
|
+
break
|
|
2807
|
+
overlap += 1
|
|
2808
|
+
i0 -= 1
|
|
2809
|
+
i1 -= 1
|
|
2810
|
+
|
|
2811
|
+
return overlap
|
|
2812
|
+
|
|
2813
|
+
@staticmethod
|
|
2814
|
+
def _get_overlapping_suffix_instructions_compare_instructions(insn_0, insn_1) -> bool:
|
|
2815
|
+
return insn_0.mnemonic == insn_1.mnemonic and insn_0.op_str == insn_1.op_str
|
|
2816
|
+
|
|
2817
|
+
@staticmethod
|
|
2818
|
+
def _get_overlapping_suffix_instructions_compare_conditional_jumps(
|
|
2819
|
+
ailblock_0: ailment.Block, ailblock_1: ailment.Block
|
|
2820
|
+
) -> bool:
|
|
2821
|
+
if len(ailblock_0.statements) == 0 or len(ailblock_1.statements) == 0:
|
|
2822
|
+
return False
|
|
2823
|
+
|
|
2824
|
+
# 12 | 0x41d5a2 | t17 = (t4 <= 0x9<32>)
|
|
2825
|
+
# 13 | 0x41d5a2 | t16 = Conv(1->32, t17)
|
|
2826
|
+
# 14 | 0x41d5a2 | t14 = t16
|
|
2827
|
+
# 15 | 0x41d5a2 | t18 = Conv(32->1, t14)
|
|
2828
|
+
# 16 | 0x41d5a2 | t9 = t18
|
|
2829
|
+
# 17 | 0x41d5a2 | if (t9) { Goto 0x41d5a8<32> } else { Goto 0x41daa6<32> }
|
|
2830
|
+
|
|
2831
|
+
last_stmt_0 = ailblock_0.statements[-1]
|
|
2832
|
+
last_stmt_1 = ailblock_1.statements[-1]
|
|
2833
|
+
if not (
|
|
2834
|
+
isinstance(last_stmt_0, ailment.Stmt.ConditionalJump)
|
|
2835
|
+
and isinstance(last_stmt_1, ailment.Stmt.ConditionalJump)
|
|
2836
|
+
):
|
|
2837
|
+
return False
|
|
2838
|
+
|
|
2839
|
+
last_cmp_stmt_0 = next(
|
|
2840
|
+
iter(
|
|
2841
|
+
stmt
|
|
2842
|
+
for stmt in reversed(ailblock_0.statements)
|
|
2843
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2844
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
2845
|
+
and stmt.src.op in ailment.Expr.BinaryOp.COMPARISON_NEGATION
|
|
2846
|
+
and stmt.tags["ins_addr"] == last_stmt_0.tags["ins_addr"]
|
|
2847
|
+
),
|
|
2848
|
+
None,
|
|
2849
|
+
)
|
|
2850
|
+
last_cmp_stmt_1 = next(
|
|
2851
|
+
iter(
|
|
2852
|
+
stmt
|
|
2853
|
+
for stmt in reversed(ailblock_1.statements)
|
|
2854
|
+
if isinstance(stmt, ailment.Stmt.Assignment)
|
|
2855
|
+
and isinstance(stmt.src, ailment.Expr.BinaryOp)
|
|
2856
|
+
and stmt.src.op in ailment.Expr.BinaryOp.COMPARISON_NEGATION
|
|
2857
|
+
and stmt.tags["ins_addr"] == last_stmt_1.tags["ins_addr"]
|
|
2858
|
+
),
|
|
2859
|
+
None,
|
|
2860
|
+
)
|
|
2861
|
+
return (
|
|
2862
|
+
last_cmp_stmt_0 is not None
|
|
2863
|
+
and last_cmp_stmt_1 is not None
|
|
2864
|
+
and last_cmp_stmt_0.src.op == last_cmp_stmt_1.src.op
|
|
2865
|
+
and last_cmp_stmt_0.src.operands[1].likes(last_cmp_stmt_1.src.operands[1])
|
|
2866
|
+
)
|
|
2867
|
+
|
|
2868
|
+
def _fix_abnormal_switch_case_heads_case1(
|
|
2869
|
+
self,
|
|
2870
|
+
ail_graph: networkx.DiGraph,
|
|
2871
|
+
indirect_jump_node: ailment.Block,
|
|
2872
|
+
intended_head: ailment.Block,
|
|
2873
|
+
other_heads: list[ailment.Block],
|
|
2874
|
+
overlap: int,
|
|
2875
|
+
) -> None:
|
|
2876
|
+
self._fix_abnormal_switch_case_heads_case2(
|
|
2877
|
+
ail_graph,
|
|
2878
|
+
indirect_jump_node,
|
|
2879
|
+
intended_head,
|
|
2880
|
+
other_heads,
|
|
2881
|
+
intended_head_split_insns=overlap,
|
|
2882
|
+
other_head_split_insns=overlap,
|
|
2883
|
+
)
|
|
2884
|
+
|
|
2885
|
+
def _fix_abnormal_switch_case_heads_case2(
|
|
2886
|
+
self,
|
|
2887
|
+
ail_graph: networkx.DiGraph,
|
|
2888
|
+
indirect_jump_node: ailment.Block,
|
|
2889
|
+
intended_head: ailment.Block,
|
|
2890
|
+
other_heads: list[ailment.Block],
|
|
2891
|
+
intended_head_split_insns: int = 1,
|
|
2892
|
+
other_head_split_insns: int = 0,
|
|
2893
|
+
) -> None:
|
|
2894
|
+
|
|
2895
|
+
# split the intended head into two
|
|
2896
|
+
intended_head_block = self.project.factory.block(intended_head.addr, size=intended_head.original_size)
|
|
2897
|
+
split_ins_addr = intended_head_block.instruction_addrs[-intended_head_split_insns]
|
|
2898
|
+
# note that the two blocks can be fully overlapping, so block_0 will be empty...
|
|
2899
|
+
intended_head_block_0 = (
|
|
2900
|
+
self.project.factory.block(intended_head.addr, size=split_ins_addr - intended_head.addr)
|
|
2901
|
+
if split_ins_addr != intended_head.addr
|
|
2902
|
+
else None
|
|
2903
|
+
)
|
|
2904
|
+
intended_head_block_1 = self.project.factory.block(
|
|
2905
|
+
split_ins_addr, size=intended_head.addr + intended_head.original_size - split_ins_addr
|
|
2906
|
+
)
|
|
2907
|
+
intended_head_0 = self._convert_vex(intended_head_block_0) if intended_head_block_0 is not None else None
|
|
2908
|
+
intended_head_1 = self._convert_vex(intended_head_block_1)
|
|
2909
|
+
|
|
2910
|
+
# corner-case: the last statement of intended_head might have been patched by _remove_redundant_jump_blocks. we
|
|
2911
|
+
# detect such case and fix it in intended_head_1
|
|
2912
|
+
self._remove_redundant_jump_blocks_repatch_relifted_block(intended_head, intended_head_1)
|
|
2913
|
+
|
|
2914
|
+
# adjust the graph accordingly
|
|
2915
|
+
preds = list(ail_graph.predecessors(intended_head))
|
|
2916
|
+
succs = list(ail_graph.successors(intended_head))
|
|
2917
|
+
ail_graph.remove_node(intended_head)
|
|
2918
|
+
|
|
2919
|
+
if intended_head_0 is None:
|
|
2920
|
+
# perfect overlap; the first block is empty
|
|
2921
|
+
for pred in preds:
|
|
2922
|
+
if pred is intended_head:
|
|
2923
|
+
ail_graph.add_edge(intended_head_1, intended_head_1)
|
|
2924
|
+
else:
|
|
2925
|
+
ail_graph.add_edge(pred, intended_head_1)
|
|
2926
|
+
for succ in succs:
|
|
2927
|
+
if succ is intended_head:
|
|
2928
|
+
ail_graph.add_edge(intended_head_1, intended_head_1)
|
|
2929
|
+
else:
|
|
2930
|
+
ail_graph.add_edge(intended_head_1, succ)
|
|
2931
|
+
else:
|
|
2932
|
+
ail_graph.add_edge(intended_head_0, intended_head_1)
|
|
2933
|
+
for pred in preds:
|
|
2934
|
+
if pred is intended_head:
|
|
2935
|
+
ail_graph.add_edge(intended_head_1, intended_head_0)
|
|
2936
|
+
else:
|
|
2937
|
+
ail_graph.add_edge(pred, intended_head_0)
|
|
2938
|
+
for succ in succs:
|
|
2939
|
+
if succ is intended_head:
|
|
2940
|
+
ail_graph.add_edge(intended_head_1, intended_head_0)
|
|
2941
|
+
else:
|
|
2942
|
+
ail_graph.add_edge(intended_head_1, succ)
|
|
2943
|
+
|
|
2944
|
+
# split other heads
|
|
2945
|
+
for o in other_heads:
|
|
2946
|
+
if other_head_split_insns > 0:
|
|
2947
|
+
o_block = self.project.factory.block(o.addr, size=o.original_size)
|
|
2948
|
+
o_split_addr = o_block.instruction_addrs[-other_head_split_insns]
|
|
2949
|
+
new_o_block = (
|
|
2950
|
+
self.project.factory.block(o.addr, size=o_split_addr - o.addr) if o_split_addr != o.addr else None
|
|
2951
|
+
)
|
|
2952
|
+
new_head = self._convert_vex(new_o_block) if new_o_block is not None else None
|
|
2953
|
+
else:
|
|
2954
|
+
new_head = o
|
|
2955
|
+
|
|
2956
|
+
if new_head is None:
|
|
2957
|
+
# the head is removed - let's replace it with a jump to the target
|
|
2958
|
+
jump_stmt = ailment.Stmt.Jump(
|
|
2959
|
+
None,
|
|
2960
|
+
ailment.Expr.Const(None, None, intended_head_1.addr, self.project.arch.bits),
|
|
2961
|
+
target_idx=intended_head_1.idx,
|
|
2962
|
+
ins_addr=o.addr,
|
|
2963
|
+
)
|
|
2964
|
+
new_head = ailment.Block(o.addr, 1, statements=[jump_stmt], idx=o.idx)
|
|
2965
|
+
else:
|
|
2966
|
+
if (
|
|
2967
|
+
new_head.statements
|
|
2968
|
+
and isinstance(new_head.statements[-1], ailment.Stmt.Jump)
|
|
2969
|
+
and isinstance(new_head.statements[-1].target, ailment.Expr.Const)
|
|
2970
|
+
):
|
|
2971
|
+
# update the jump target
|
|
2972
|
+
new_head.statements[-1] = ailment.Stmt.Jump(
|
|
2973
|
+
new_head.statements[-1].idx,
|
|
2974
|
+
ailment.Expr.Const(None, None, intended_head_1.addr, self.project.arch.bits),
|
|
2975
|
+
target_idx=intended_head_1.idx,
|
|
2976
|
+
**new_head.statements[-1].tags,
|
|
2977
|
+
)
|
|
2978
|
+
|
|
2979
|
+
# adjust the graph accordingly
|
|
2980
|
+
preds = list(ail_graph.predecessors(o))
|
|
2981
|
+
succs = list(ail_graph.successors(o))
|
|
2982
|
+
ail_graph.remove_node(o)
|
|
2983
|
+
for pred in preds:
|
|
2984
|
+
if pred is o:
|
|
2985
|
+
ail_graph.add_edge(new_head, new_head)
|
|
2986
|
+
else:
|
|
2987
|
+
ail_graph.add_edge(pred, new_head)
|
|
2988
|
+
for succ in succs:
|
|
2989
|
+
if succ is o:
|
|
2990
|
+
ail_graph.add_edge(new_head, new_head)
|
|
2991
|
+
elif succ is indirect_jump_node:
|
|
2992
|
+
ail_graph.add_edge(new_head, intended_head_1)
|
|
2993
|
+
else:
|
|
2994
|
+
# it should be going to the default node. ignore it
|
|
2995
|
+
pass
|
|
2996
|
+
|
|
2997
|
+
def _fix_abnormal_switch_case_heads_case3(
|
|
2998
|
+
self, indirect_jump_node: ailment.Block, other_heads: list[ailment.Block]
|
|
2999
|
+
) -> None:
|
|
3000
|
+
# remove all edges from other_heads to the indirect jump node
|
|
3001
|
+
for other_head in other_heads:
|
|
3002
|
+
# delay the edge removal so that we don't mess up the SSA analysis
|
|
3003
|
+
self.edges_to_remove.append(
|
|
3004
|
+
((other_head.addr, other_head.idx), (indirect_jump_node.addr, indirect_jump_node.idx))
|
|
3005
|
+
)
|
|
3006
|
+
|
|
3007
|
+
@staticmethod
|
|
3008
|
+
def _remove_redundant_jump_blocks(ail_graph):
|
|
3009
|
+
def first_conditional_jump(block: ailment.Block) -> ailment.Stmt.ConditionalJump | None:
|
|
3010
|
+
for stmt in block.statements:
|
|
3011
|
+
if isinstance(stmt, ailment.Stmt.ConditionalJump):
|
|
3012
|
+
return stmt
|
|
3013
|
+
return None
|
|
3014
|
+
|
|
3015
|
+
def patch_conditional_jump_target(cond_jump_stmt: ailment.Stmt.ConditionalJump, old_addr: int, new_addr: int):
|
|
3016
|
+
if (
|
|
3017
|
+
isinstance(cond_jump_stmt.true_target, ailment.Expr.Const)
|
|
3018
|
+
and cond_jump_stmt.true_target.value == old_addr
|
|
3019
|
+
):
|
|
3020
|
+
cond_jump_stmt.true_target.value = new_addr
|
|
3021
|
+
if (
|
|
3022
|
+
isinstance(cond_jump_stmt.false_target, ailment.Expr.Const)
|
|
3023
|
+
and cond_jump_stmt.false_target.value == old_addr
|
|
3024
|
+
):
|
|
3025
|
+
cond_jump_stmt.false_target.value = new_addr
|
|
3026
|
+
|
|
3027
|
+
# note that blocks don't have labels inserted at this point
|
|
3028
|
+
for node in list(ail_graph.nodes):
|
|
3029
|
+
if (
|
|
3030
|
+
len(node.statements) == 1
|
|
3031
|
+
and isinstance(node.statements[0], ailment.Stmt.Jump)
|
|
3032
|
+
and isinstance(node.statements[0].target, ailment.Expr.Const)
|
|
3033
|
+
):
|
|
3034
|
+
jump_target = node.statements[0].target.value
|
|
3035
|
+
succs = list(ail_graph.successors(node))
|
|
3036
|
+
if len(succs) == 1 and succs[0].addr == jump_target:
|
|
3037
|
+
preds = list(ail_graph.predecessors(node))
|
|
3038
|
+
if len(preds) == 1 and ail_graph.out_degree[preds[0]] == 2:
|
|
3039
|
+
# remove this node
|
|
3040
|
+
for pred in preds:
|
|
3041
|
+
if pred.statements:
|
|
3042
|
+
last_stmt = pred.statements[-1]
|
|
3043
|
+
if (
|
|
3044
|
+
isinstance(last_stmt, ailment.Stmt.Jump)
|
|
3045
|
+
and isinstance(last_stmt.target, ailment.Expr.Const)
|
|
3046
|
+
and last_stmt.target.value == node.addr
|
|
3047
|
+
):
|
|
3048
|
+
last_stmt.target.value = succs[0].addr
|
|
3049
|
+
elif isinstance(last_stmt, ailment.Stmt.ConditionalJump):
|
|
3050
|
+
patch_conditional_jump_target(last_stmt, node.addr, succs[0].addr)
|
|
3051
|
+
# if both branches jump to the same location, we replace it with a jump
|
|
3052
|
+
if (
|
|
3053
|
+
isinstance(last_stmt.true_target, ailment.Expr.Const)
|
|
3054
|
+
and isinstance(last_stmt.false_target, ailment.Expr.Const)
|
|
3055
|
+
and last_stmt.true_target.value == last_stmt.false_target.value
|
|
3056
|
+
):
|
|
3057
|
+
last_stmt = ailment.Stmt.Jump(
|
|
3058
|
+
last_stmt.idx,
|
|
3059
|
+
last_stmt.true_target,
|
|
3060
|
+
target_idx=last_stmt.true_target.idx,
|
|
3061
|
+
ins_addr=last_stmt.tags["ins_addr"],
|
|
3062
|
+
)
|
|
3063
|
+
pred.statements[-1] = last_stmt
|
|
3064
|
+
first_cond_jump = first_conditional_jump(pred)
|
|
3065
|
+
if first_cond_jump is not None and first_cond_jump is not last_stmt:
|
|
3066
|
+
patch_conditional_jump_target(first_cond_jump, node.addr, succs[0].addr)
|
|
3067
|
+
ail_graph.add_edge(pred, succs[0])
|
|
3068
|
+
ail_graph.remove_node(node)
|
|
3069
|
+
|
|
3070
|
+
@staticmethod
|
|
3071
|
+
def _remove_redundant_jump_blocks_repatch_relifted_block(
|
|
3072
|
+
patched_block: ailment.Block, new_block: ailment.Block
|
|
3073
|
+
) -> None:
|
|
3074
|
+
"""
|
|
3075
|
+
The last statement of patched_block might have been patched by _remove_redundant_jump_blocks. In this case, we
|
|
3076
|
+
fix the last instruction for new_block, which is a newly lifted (from VEX) block that ends at the same address
|
|
3077
|
+
as patched_block.
|
|
3078
|
+
|
|
3079
|
+
:param patched_block: Previously patched block.
|
|
3080
|
+
:param new_block: Newly lifted block.
|
|
3081
|
+
"""
|
|
3082
|
+
|
|
3083
|
+
if (
|
|
3084
|
+
isinstance(patched_block.statements[-1], ailment.Stmt.Jump)
|
|
3085
|
+
and isinstance(patched_block.statements[-1].target, ailment.Expr.Const)
|
|
3086
|
+
and isinstance(new_block.statements[-1], ailment.Stmt.Jump)
|
|
3087
|
+
and isinstance(new_block.statements[-1].target, ailment.Expr.Const)
|
|
3088
|
+
and not patched_block.statements[-1].likes(new_block.statements[-1])
|
|
3089
|
+
):
|
|
3090
|
+
new_block.statements[-1].target = patched_block.statements[-1].target
|
|
3091
|
+
if (
|
|
3092
|
+
isinstance(patched_block.statements[-1], ailment.Stmt.ConditionalJump)
|
|
3093
|
+
and isinstance(patched_block.statements[-1].true_target, ailment.Expr.Const)
|
|
3094
|
+
and isinstance(patched_block.statements[-1].false_target, ailment.Expr.Const)
|
|
3095
|
+
and isinstance(new_block.statements[-1], ailment.Stmt.ConditionalJump)
|
|
3096
|
+
and isinstance(new_block.statements[-1].true_target, ailment.Expr.Const)
|
|
3097
|
+
and isinstance(new_block.statements[-1].false_target, ailment.Expr.Const)
|
|
3098
|
+
and not patched_block.statements[-1].likes(new_block.statements[-1])
|
|
3099
|
+
):
|
|
3100
|
+
new_block.statements[-1].true_target = patched_block.statements[-1].true_target
|
|
3101
|
+
new_block.statements[-1].false_target = patched_block.statements[-1].false_target
|
|
3102
|
+
|
|
3103
|
+
@staticmethod
|
|
3104
|
+
def _insert_block_labels(ail_graph):
|
|
3105
|
+
for node in ail_graph.nodes:
|
|
3106
|
+
node: ailment.Block
|
|
3107
|
+
lbl = ailment.Stmt.Label(None, f"LABEL_{node.addr:x}", ins_addr=node.addr, block_idx=node.idx)
|
|
3108
|
+
node.statements.insert(0, lbl)
|
|
3109
|
+
|
|
3110
|
+
@staticmethod
|
|
3111
|
+
def _collect_externs(ail_graph, variable_kb):
|
|
3112
|
+
global_vars = variable_kb.variables.global_manager.get_variables()
|
|
3113
|
+
walker = ailment.AILBlockRewriter()
|
|
3114
|
+
variables = set()
|
|
3115
|
+
|
|
3116
|
+
def handle_expr(
|
|
3117
|
+
expr_idx: int,
|
|
3118
|
+
expr: ailment.expression.Expression,
|
|
3119
|
+
stmt_idx: int,
|
|
3120
|
+
stmt: ailment.statement.Statement | None,
|
|
3121
|
+
block: ailment.Block | None,
|
|
3122
|
+
):
|
|
3123
|
+
for v in [
|
|
3124
|
+
getattr(expr, "variable", None),
|
|
3125
|
+
expr.tags.get("reference_variable", None) if hasattr(expr, "tags") else None,
|
|
3126
|
+
]:
|
|
3127
|
+
if v and v in global_vars:
|
|
3128
|
+
variables.add(v)
|
|
3129
|
+
return ailment.AILBlockRewriter._handle_expr(walker, expr_idx, expr, stmt_idx, stmt, block)
|
|
3130
|
+
|
|
3131
|
+
def handle_Store(stmt_idx: int, stmt: ailment.statement.Store, block: ailment.Block | None):
|
|
3132
|
+
if stmt.variable and stmt.variable in global_vars:
|
|
3133
|
+
variables.add(stmt.variable)
|
|
3134
|
+
return ailment.AILBlockRewriter._handle_Store(walker, stmt_idx, stmt, block)
|
|
3135
|
+
|
|
3136
|
+
walker.stmt_handlers[ailment.statement.Store] = handle_Store
|
|
3137
|
+
walker._handle_expr = handle_expr
|
|
3138
|
+
AILGraphWalker(ail_graph, walker.walk).walk()
|
|
3139
|
+
return variables
|
|
3140
|
+
|
|
3141
|
+
@staticmethod
|
|
3142
|
+
def _collect_data_refs(ail_graph) -> dict[int, list[DataRefDesc]]:
|
|
3143
|
+
# pylint:disable=unused-argument
|
|
3144
|
+
walker = ailment.AILBlockRewriter()
|
|
3145
|
+
data_refs: dict[int, list[DataRefDesc]] = defaultdict(list)
|
|
3146
|
+
|
|
3147
|
+
def handle_Const(
|
|
3148
|
+
expr_idx: int,
|
|
3149
|
+
expr: ailment.expression.Const,
|
|
3150
|
+
stmt_idx: int,
|
|
3151
|
+
stmt: ailment.statement.Statement | None,
|
|
3152
|
+
block: ailment.Block | None,
|
|
3153
|
+
):
|
|
3154
|
+
assert block is not None
|
|
3155
|
+
if isinstance(expr.value, int) and "ins_addr" in expr.tags:
|
|
3156
|
+
data_refs[block.addr].append(
|
|
3157
|
+
DataRefDesc(expr.value, 1, block.addr, stmt_idx, expr.tags["ins_addr"], MemoryDataSort.Unknown)
|
|
3158
|
+
)
|
|
3159
|
+
if "deref_src_addr" in expr.tags:
|
|
3160
|
+
data_refs[block.addr].append(
|
|
3161
|
+
DataRefDesc(
|
|
3162
|
+
expr.tags["deref_src_addr"],
|
|
3163
|
+
expr.size,
|
|
3164
|
+
block.addr,
|
|
3165
|
+
stmt_idx,
|
|
3166
|
+
expr.tags["ins_addr"],
|
|
3167
|
+
MemoryDataSort.Unknown,
|
|
3168
|
+
)
|
|
3169
|
+
)
|
|
3170
|
+
return expr
|
|
3171
|
+
|
|
3172
|
+
def handle_Load(
|
|
3173
|
+
expr_idx: int,
|
|
3174
|
+
expr: ailment.expression.Load,
|
|
3175
|
+
stmt_idx: int,
|
|
3176
|
+
stmt: ailment.statement.Statement | None,
|
|
3177
|
+
block: ailment.Block | None,
|
|
3178
|
+
):
|
|
3179
|
+
assert block is not None
|
|
3180
|
+
if isinstance(expr.addr, ailment.expression.Const):
|
|
3181
|
+
addr = expr.addr
|
|
3182
|
+
if isinstance(addr.value, int) and "ins_addr" in addr.tags:
|
|
3183
|
+
data_refs[block.addr].append(
|
|
3184
|
+
DataRefDesc(
|
|
3185
|
+
addr.value,
|
|
3186
|
+
expr.size,
|
|
3187
|
+
block.addr,
|
|
3188
|
+
stmt_idx,
|
|
3189
|
+
addr.tags["ins_addr"],
|
|
3190
|
+
MemoryDataSort.Integer if expr.size == 4 else MemoryDataSort.Unknown,
|
|
3191
|
+
)
|
|
3192
|
+
)
|
|
3193
|
+
if "deref_src_addr" in addr.tags:
|
|
3194
|
+
data_refs[block.addr].append(
|
|
3195
|
+
DataRefDesc(
|
|
3196
|
+
addr.tags["deref_src_addr"],
|
|
3197
|
+
expr.size,
|
|
3198
|
+
block.addr,
|
|
3199
|
+
stmt_idx,
|
|
3200
|
+
addr.tags["ins_addr"],
|
|
3201
|
+
MemoryDataSort.Integer if expr.size == 4 else MemoryDataSort.Unknown,
|
|
3202
|
+
)
|
|
3203
|
+
)
|
|
3204
|
+
return expr
|
|
3205
|
+
|
|
3206
|
+
return ailment.AILBlockRewriter._handle_Load(walker, expr_idx, expr, stmt_idx, stmt, block)
|
|
3207
|
+
|
|
3208
|
+
def handle_Store(stmt_idx: int, stmt: ailment.statement.Store, block: ailment.Block | None):
|
|
3209
|
+
assert block is not None
|
|
3210
|
+
if isinstance(stmt.addr, ailment.expression.Const):
|
|
3211
|
+
addr = stmt.addr
|
|
3212
|
+
if isinstance(addr.value, int) and "ins_addr" in addr.tags:
|
|
3213
|
+
data_refs[block.addr].append(
|
|
3214
|
+
DataRefDesc(
|
|
3215
|
+
addr.value,
|
|
3216
|
+
stmt.size,
|
|
3217
|
+
block.addr,
|
|
3218
|
+
stmt_idx,
|
|
3219
|
+
addr.tags["ins_addr"],
|
|
3220
|
+
MemoryDataSort.Integer if stmt.size == 4 else MemoryDataSort.Unknown,
|
|
3221
|
+
)
|
|
3222
|
+
)
|
|
3223
|
+
if "deref_src_addr" in addr.tags:
|
|
3224
|
+
data_refs[block.addr].append(
|
|
3225
|
+
DataRefDesc(
|
|
3226
|
+
addr.tags["deref_src_addr"],
|
|
3227
|
+
stmt.size,
|
|
3228
|
+
block.addr,
|
|
3229
|
+
stmt_idx,
|
|
3230
|
+
addr.tags["ins_addr"],
|
|
3231
|
+
MemoryDataSort.Integer if stmt.size == 4 else MemoryDataSort.Unknown,
|
|
3232
|
+
)
|
|
3233
|
+
)
|
|
3234
|
+
return stmt
|
|
3235
|
+
|
|
3236
|
+
return ailment.AILBlockRewriter._handle_Store(walker, stmt_idx, stmt, block)
|
|
3237
|
+
|
|
3238
|
+
walker.stmt_handlers[ailment.statement.Store] = handle_Store
|
|
3239
|
+
walker.expr_handlers[ailment.expression.Load] = handle_Load
|
|
3240
|
+
walker.expr_handlers[ailment.expression.Const] = handle_Const
|
|
3241
|
+
AILGraphWalker(ail_graph, walker.walk).walk()
|
|
3242
|
+
return data_refs
|
|
3243
|
+
|
|
3244
|
+
def _next_atom(self) -> int:
|
|
3245
|
+
return self._ail_manager.next_atom()
|
|
3246
|
+
|
|
3247
|
+
def parse_variable_addr(self, addr: ailment.Expr.Expression) -> tuple[Any, Any]:
|
|
3248
|
+
if isinstance(addr, ailment.Expr.Const):
|
|
3249
|
+
return addr, 0
|
|
3250
|
+
if isinstance(addr, ailment.Expr.BinaryOp) and addr.op == "Add":
|
|
3251
|
+
op0, op1 = addr.operands
|
|
3252
|
+
if (
|
|
3253
|
+
isinstance(op0, ailment.Expr.Const)
|
|
3254
|
+
and self.project.loader.find_object_containing(op0.value_int) is not None
|
|
3255
|
+
):
|
|
3256
|
+
return op0, op1
|
|
3257
|
+
if (
|
|
3258
|
+
isinstance(op1, ailment.Expr.Const)
|
|
3259
|
+
and self.project.loader.find_object_containing(op1.value_int) is not None
|
|
3260
|
+
):
|
|
3261
|
+
return op1, op0
|
|
3262
|
+
return op0, op1 # best-effort guess
|
|
3263
|
+
return None, None
|
|
3264
|
+
|
|
3265
|
+
def new_block_addr(self) -> int:
|
|
3266
|
+
"""
|
|
3267
|
+
Return a block address that does not conflict with any existing blocks.
|
|
3268
|
+
|
|
3269
|
+
:return: The block address.
|
|
3270
|
+
"""
|
|
3271
|
+
if self._new_block_addrs:
|
|
3272
|
+
new_addr = max(self._new_block_addrs) + 1
|
|
3273
|
+
else:
|
|
3274
|
+
new_addr = max(self.function.block_addrs_set) + 2048
|
|
3275
|
+
self._new_block_addrs.add(new_addr)
|
|
3276
|
+
return new_addr
|
|
3277
|
+
|
|
3278
|
+
@staticmethod
|
|
3279
|
+
@timethis
|
|
3280
|
+
def remove_empty_nodes(graph: networkx.DiGraph) -> networkx.DiGraph:
|
|
3281
|
+
def handle_node(node: ailment.Block):
|
|
3282
|
+
if not node.statements:
|
|
3283
|
+
preds = [pred for pred in graph.predecessors(node) if pred is not node]
|
|
3284
|
+
succs = [succ for succ in graph.successors(node) if succ is not node]
|
|
3285
|
+
if len(preds) == 1 and len(succs) == 1:
|
|
3286
|
+
pred = preds[0]
|
|
3287
|
+
succ = succs[0]
|
|
3288
|
+
value_updated = False
|
|
3289
|
+
# update the last statement of pred
|
|
3290
|
+
if pred.statements and isinstance(pred.statements[-1], ailment.Stmt.ConditionalJump):
|
|
3291
|
+
last_stmt = pred.statements[-1]
|
|
3292
|
+
if (
|
|
3293
|
+
isinstance(last_stmt.true_target, ailment.Expr.Const)
|
|
3294
|
+
and last_stmt.true_target.value == node.addr
|
|
3295
|
+
):
|
|
3296
|
+
last_stmt.true_target.value = succ.addr
|
|
3297
|
+
value_updated = True
|
|
3298
|
+
if (
|
|
3299
|
+
isinstance(last_stmt.false_target, ailment.Expr.Const)
|
|
3300
|
+
and last_stmt.false_target.value == node.addr
|
|
3301
|
+
):
|
|
3302
|
+
last_stmt.false_target.value = succ.addr
|
|
3303
|
+
value_updated = True
|
|
3304
|
+
|
|
3305
|
+
if value_updated:
|
|
3306
|
+
graph.add_edge(pred, succ)
|
|
3307
|
+
raise RemoveNodeNotice
|
|
3308
|
+
elif len(preds) >= 1 and len(succs) == 1:
|
|
3309
|
+
succ = succs[0]
|
|
3310
|
+
branch_updates = 0
|
|
3311
|
+
for pred in preds:
|
|
3312
|
+
# test how many last statements of pred can potentially be updated
|
|
3313
|
+
if pred.statements and isinstance(pred.statements[-1], ailment.Stmt.ConditionalJump):
|
|
3314
|
+
last_stmt = pred.statements[-1]
|
|
3315
|
+
if (
|
|
3316
|
+
isinstance(last_stmt.true_target, ailment.Expr.Const)
|
|
3317
|
+
and last_stmt.true_target.value == node.addr
|
|
3318
|
+
):
|
|
3319
|
+
branch_updates += 1
|
|
3320
|
+
if (
|
|
3321
|
+
isinstance(last_stmt.false_target, ailment.Expr.Const)
|
|
3322
|
+
and last_stmt.false_target.value == node.addr
|
|
3323
|
+
):
|
|
3324
|
+
branch_updates += 1
|
|
3325
|
+
|
|
3326
|
+
if branch_updates == len(preds):
|
|
3327
|
+
# actually do the update
|
|
3328
|
+
for pred in preds:
|
|
3329
|
+
graph.add_edge(pred, succ)
|
|
3330
|
+
if pred.statements and isinstance(pred.statements[-1], ailment.Stmt.ConditionalJump):
|
|
3331
|
+
last_stmt = pred.statements[-1]
|
|
3332
|
+
if (
|
|
3333
|
+
isinstance(last_stmt.true_target, ailment.Expr.Const)
|
|
3334
|
+
and last_stmt.true_target.value == node.addr
|
|
3335
|
+
):
|
|
3336
|
+
last_stmt.true_target.value = succ.addr
|
|
3337
|
+
if (
|
|
3338
|
+
isinstance(last_stmt.false_target, ailment.Expr.Const)
|
|
3339
|
+
and last_stmt.false_target.value == node.addr
|
|
3340
|
+
):
|
|
3341
|
+
last_stmt.false_target.value = succ.addr
|
|
3342
|
+
raise RemoveNodeNotice
|
|
3343
|
+
elif not preds or not succs:
|
|
3344
|
+
raise RemoveNodeNotice
|
|
3345
|
+
|
|
3346
|
+
AILGraphWalker(graph, handle_node, replace_nodes=True).walk()
|
|
3347
|
+
return graph
|
|
3348
|
+
|
|
3349
|
+
def _find_regs_compared_against_sp(self, func_graph):
|
|
3350
|
+
# TODO: Implement this function for architectures beyond amd64
|
|
3351
|
+
extra_regs = set()
|
|
3352
|
+
if self.project.arch.name == "AMD64":
|
|
3353
|
+
for node in func_graph.nodes:
|
|
3354
|
+
block = self.project.factory.block(node.addr, size=node.size).capstone
|
|
3355
|
+
for insn in block.insns:
|
|
3356
|
+
if insn.mnemonic == "cmp":
|
|
3357
|
+
capstone_reg_offset = None
|
|
3358
|
+
if (
|
|
3359
|
+
insn.operands[0].type == capstone.x86.X86_OP_REG
|
|
3360
|
+
and insn.operands[0].reg == capstone.x86.X86_REG_RSP
|
|
3361
|
+
and insn.operands[1].type == capstone.x86.X86_OP_REG
|
|
3362
|
+
):
|
|
3363
|
+
capstone_reg_offset = insn.operands[1].reg
|
|
3364
|
+
elif (
|
|
3365
|
+
insn.operands[1].type == capstone.x86.X86_OP_REG
|
|
3366
|
+
and insn.operands[1].reg == capstone.x86.X86_REG_RSP
|
|
3367
|
+
and insn.operands[0].type == capstone.x86.X86_OP_REG
|
|
3368
|
+
):
|
|
3369
|
+
capstone_reg_offset = insn.operands[0].reg
|
|
3370
|
+
|
|
3371
|
+
if capstone_reg_offset is not None:
|
|
3372
|
+
reg_name = insn.reg_name(capstone_reg_offset)
|
|
3373
|
+
extra_regs.add(self.project.arch.registers[reg_name][0])
|
|
3374
|
+
|
|
3375
|
+
return extra_regs
|
|
3376
|
+
|
|
3377
|
+
def _rewrite_rust_probestack_call(self, ail_graph):
|
|
3378
|
+
for node in ail_graph:
|
|
3379
|
+
if not node.statements or ail_graph.out_degree[node] != 1:
|
|
3380
|
+
continue
|
|
3381
|
+
last_stmt = node.statements[-1]
|
|
3382
|
+
if isinstance(last_stmt, ailment.Stmt.Call) and isinstance(last_stmt.target, ailment.Expr.Const):
|
|
3383
|
+
func = (
|
|
3384
|
+
self.project.kb.functions.get_by_addr(last_stmt.target.value)
|
|
3385
|
+
if self.project.kb.functions.contains_addr(last_stmt.target.value)
|
|
3386
|
+
else None
|
|
3387
|
+
)
|
|
3388
|
+
if func is not None and func.info.get("is_rust_probestack", False) is True:
|
|
3389
|
+
# get rid of this call
|
|
3390
|
+
node.statements = node.statements[:-1]
|
|
3391
|
+
if self.project.arch.call_pushes_ret and node.statements:
|
|
3392
|
+
last_stmt = node.statements[-1]
|
|
3393
|
+
succ = next(iter(ail_graph.successors(node)))
|
|
3394
|
+
if (
|
|
3395
|
+
isinstance(last_stmt, ailment.Stmt.Store)
|
|
3396
|
+
and isinstance(last_stmt.addr, ailment.Expr.StackBaseOffset)
|
|
3397
|
+
and isinstance(last_stmt.addr.offset, int)
|
|
3398
|
+
and last_stmt.addr.offset < 0
|
|
3399
|
+
and isinstance(last_stmt.data, ailment.Expr.Const)
|
|
3400
|
+
and last_stmt.data.value == succ.addr
|
|
3401
|
+
) or (
|
|
3402
|
+
isinstance(last_stmt, ailment.Stmt.Assignment)
|
|
3403
|
+
and last_stmt.dst.was_stack
|
|
3404
|
+
and last_stmt.dst.stack_offset < 0
|
|
3405
|
+
and isinstance(last_stmt.src, ailment.Expr.Const)
|
|
3406
|
+
and last_stmt.src.value == succ.addr
|
|
3407
|
+
):
|
|
3408
|
+
# remove the statement that pushes the return address
|
|
3409
|
+
node.statements = node.statements[:-1]
|
|
3410
|
+
break
|
|
3411
|
+
return ail_graph
|
|
3412
|
+
|
|
3413
|
+
def _rewrite_windows_chkstk_call(self, ail_graph) -> networkx.DiGraph:
|
|
3414
|
+
if not (self.project.simos is not None and self.project.simos.name == "Win32"):
|
|
3415
|
+
return ail_graph
|
|
3416
|
+
|
|
3417
|
+
for node in ail_graph:
|
|
3418
|
+
if not node.statements or ail_graph.out_degree[node] != 1:
|
|
3419
|
+
continue
|
|
3420
|
+
last_stmt = node.statements[-1]
|
|
3421
|
+
if isinstance(last_stmt, ailment.Stmt.Call) and isinstance(last_stmt.target, ailment.Expr.Const):
|
|
3422
|
+
func = (
|
|
3423
|
+
self.project.kb.functions.get_by_addr(last_stmt.target.value)
|
|
3424
|
+
if self.project.kb.functions.contains_addr(last_stmt.target.value)
|
|
3425
|
+
else None
|
|
3426
|
+
)
|
|
3427
|
+
if func is not None and (func.name == "__chkstk" or func.info.get("is_alloca_probe", False) is True):
|
|
3428
|
+
# get rid of this call
|
|
3429
|
+
node.statements = node.statements[:-1]
|
|
3430
|
+
if self.project.arch.call_pushes_ret and node.statements:
|
|
3431
|
+
last_stmt = node.statements[-1]
|
|
3432
|
+
succ = next(iter(ail_graph.successors(node)))
|
|
3433
|
+
if (
|
|
3434
|
+
isinstance(last_stmt, ailment.Stmt.Store)
|
|
3435
|
+
and isinstance(last_stmt.addr, ailment.Expr.StackBaseOffset)
|
|
3436
|
+
and isinstance(last_stmt.addr.offset, int)
|
|
3437
|
+
and last_stmt.addr.offset < 0
|
|
3438
|
+
and isinstance(last_stmt.data, ailment.Expr.Const)
|
|
3439
|
+
and last_stmt.data.value == succ.addr
|
|
3440
|
+
) or (
|
|
3441
|
+
isinstance(last_stmt, ailment.Stmt.Assignment)
|
|
3442
|
+
and last_stmt.dst.was_stack
|
|
3443
|
+
and last_stmt.dst.stack_offset < 0
|
|
3444
|
+
and isinstance(last_stmt.src, ailment.Expr.Const)
|
|
3445
|
+
and last_stmt.src.value == succ.addr
|
|
3446
|
+
):
|
|
3447
|
+
# remove the statement that pushes the return address
|
|
3448
|
+
node.statements = node.statements[:-1]
|
|
3449
|
+
break
|
|
3450
|
+
return ail_graph
|
|
3451
|
+
|
|
3452
|
+
def _rewrite_alloca(self, ail_graph):
|
|
3453
|
+
# pylint:disable=too-many-boolean-expressions
|
|
3454
|
+
alloca_node = None
|
|
3455
|
+
sp_equal_to = None
|
|
3456
|
+
|
|
3457
|
+
for node in ail_graph:
|
|
3458
|
+
if ail_graph.in_degree[node] == 2 and ail_graph.out_degree[node] == 2:
|
|
3459
|
+
succs = ail_graph.successors(node)
|
|
3460
|
+
if node in succs and len(node.statements) >= 6:
|
|
3461
|
+
# self loop!
|
|
3462
|
+
stmt0 = node.statements[1] # skip the LABEL statement
|
|
3463
|
+
stmt1 = node.statements[2]
|
|
3464
|
+
last_stmt = node.statements[-1]
|
|
3465
|
+
if (
|
|
3466
|
+
(
|
|
3467
|
+
isinstance(stmt0, ailment.Stmt.Assignment)
|
|
3468
|
+
and isinstance(stmt0.dst, ailment.Expr.Register)
|
|
3469
|
+
and isinstance(stmt0.src, ailment.Expr.StackBaseOffset)
|
|
3470
|
+
and stmt0.src.offset == -0x1000
|
|
3471
|
+
)
|
|
3472
|
+
and (
|
|
3473
|
+
isinstance(stmt1, ailment.Stmt.Store)
|
|
3474
|
+
and isinstance(stmt1.addr, ailment.Expr.StackBaseOffset)
|
|
3475
|
+
and stmt1.addr.offset == -0x1000
|
|
3476
|
+
and isinstance(stmt1.data, ailment.Expr.Load)
|
|
3477
|
+
and isinstance(stmt1.data.addr, ailment.Expr.StackBaseOffset)
|
|
3478
|
+
and stmt1.data.addr.offset == -0x1000
|
|
3479
|
+
)
|
|
3480
|
+
and (
|
|
3481
|
+
isinstance(last_stmt, ailment.Stmt.ConditionalJump)
|
|
3482
|
+
and isinstance(last_stmt.condition, ailment.Expr.BinaryOp)
|
|
3483
|
+
and last_stmt.condition.op == "CmpEQ"
|
|
3484
|
+
and isinstance(last_stmt.condition.operands[0], ailment.Expr.StackBaseOffset)
|
|
3485
|
+
and last_stmt.condition.operands[0].offset == -0x1000
|
|
3486
|
+
and isinstance(last_stmt.condition.operands[1], ailment.Expr.Register)
|
|
3487
|
+
and isinstance(last_stmt.false_target, ailment.Expr.Const)
|
|
3488
|
+
and last_stmt.false_target.value == node.addr
|
|
3489
|
+
)
|
|
3490
|
+
):
|
|
3491
|
+
# found it!
|
|
3492
|
+
assert self.project.arch.sp_offset is not None
|
|
3493
|
+
alloca_node = node
|
|
3494
|
+
sp_equal_to = ailment.Expr.BinaryOp(
|
|
3495
|
+
None,
|
|
3496
|
+
"Sub",
|
|
3497
|
+
[
|
|
3498
|
+
ailment.Expr.Register(None, None, self.project.arch.sp_offset, self.project.arch.bits),
|
|
3499
|
+
last_stmt.condition.operands[1],
|
|
3500
|
+
],
|
|
3501
|
+
False,
|
|
3502
|
+
)
|
|
3503
|
+
break
|
|
3504
|
+
|
|
3505
|
+
if alloca_node is not None and sp_equal_to is not None:
|
|
3506
|
+
stmt0 = alloca_node.statements[1]
|
|
3507
|
+
statements: list[ailment.Statement] = [
|
|
3508
|
+
ailment.Stmt.Call(stmt0.idx, "alloca", args=[sp_equal_to], **stmt0.tags)
|
|
3509
|
+
]
|
|
3510
|
+
new_node = ailment.Block(alloca_node.addr, alloca_node.original_size, statements=statements)
|
|
3511
|
+
# replace the node
|
|
3512
|
+
preds = [pred for pred in ail_graph.predecessors(alloca_node) if pred is not alloca_node]
|
|
3513
|
+
succs = [succ for succ in ail_graph.successors(alloca_node) if succ is not alloca_node]
|
|
3514
|
+
ail_graph.remove_node(alloca_node)
|
|
3515
|
+
for pred in preds:
|
|
3516
|
+
ail_graph.add_edge(pred, new_node)
|
|
3517
|
+
for succ in succs:
|
|
3518
|
+
ail_graph.add_edge(new_node, succ)
|
|
3519
|
+
|
|
3520
|
+
def _collect_callsite_prototypes(self) -> dict[int, list[tuple[list[SimType | None], SimType | None]]]:
|
|
3521
|
+
|
|
3522
|
+
assert self.variable_kb is not None
|
|
3523
|
+
|
|
3524
|
+
variables = self.variable_kb.variables[self.function.addr]
|
|
3525
|
+
func_proto_candidates: defaultdict[int, list[tuple[list[SimType | None], SimType | None]]] = defaultdict(list)
|
|
3526
|
+
|
|
3527
|
+
def _handle_Call_stmt_or_expr(call_: ailment.Stmt.Call):
|
|
3528
|
+
assert self.arg_vvars is not None
|
|
3529
|
+
|
|
3530
|
+
if (
|
|
3531
|
+
isinstance(call_.target, ailment.Expr.Const)
|
|
3532
|
+
and call_.tags.get("is_prototype_guessed", True)
|
|
3533
|
+
and call_.args is not None
|
|
3534
|
+
):
|
|
3535
|
+
# derive the actual prototype
|
|
3536
|
+
arg_types = []
|
|
3537
|
+
for arg_expr in call_.args:
|
|
3538
|
+
arg_type = None
|
|
3539
|
+
if hasattr(arg_expr, "variable") and arg_expr.variable is not None:
|
|
3540
|
+
# the type is type(a)
|
|
3541
|
+
t = None
|
|
3542
|
+
if isinstance(arg_expr, ailment.Expr.VirtualVariable):
|
|
3543
|
+
# a function arg
|
|
3544
|
+
for func_arg_vvar, func_arg_simvar in self.arg_vvars.values():
|
|
3545
|
+
if (
|
|
3546
|
+
arg_expr.likes(func_arg_vvar)
|
|
3547
|
+
and func_arg_simvar is not None
|
|
3548
|
+
and func_arg_simvar.ident.startswith("arg_")
|
|
3549
|
+
):
|
|
3550
|
+
# FIXME: Parsing arg_idx out of argument ident is hacky
|
|
3551
|
+
arg_idx = int(func_arg_simvar.ident[4:])
|
|
3552
|
+
assert self.function.prototype is not None
|
|
3553
|
+
if arg_idx < len(self.function.prototype.args):
|
|
3554
|
+
t = self.function.prototype.args[arg_idx]
|
|
3555
|
+
break
|
|
3556
|
+
|
|
3557
|
+
if t is None:
|
|
3558
|
+
# maybe not a function arg
|
|
3559
|
+
v = arg_expr.variable
|
|
3560
|
+
if v is not None:
|
|
3561
|
+
t = variables.get_variable_type(v)
|
|
3562
|
+
|
|
3563
|
+
if t is not None:
|
|
3564
|
+
arg_type = t
|
|
3565
|
+
elif isinstance(arg_expr, ailment.Expr.UnaryOp) and arg_expr.op == "Reference":
|
|
3566
|
+
# &a; the type becomes a pointer to type(a)
|
|
3567
|
+
inner = arg_expr.operand
|
|
3568
|
+
v = inner.variable
|
|
3569
|
+
if v is not None:
|
|
3570
|
+
t = variables.get_variable_type(v)
|
|
3571
|
+
if t is not None:
|
|
3572
|
+
arg_type = SimTypePointer(t)
|
|
3573
|
+
|
|
3574
|
+
arg_types.append(arg_type)
|
|
3575
|
+
|
|
3576
|
+
func_proto_candidates[call_.target.value_int].append((arg_types, None))
|
|
3577
|
+
|
|
3578
|
+
# pylint:disable=unused-argument
|
|
3579
|
+
def _handle_Call(stmt_idx: int, stmt: ailment.Stmt.Call, block: ailment.Block | None):
|
|
3580
|
+
_handle_Call_stmt_or_expr(stmt)
|
|
3581
|
+
|
|
3582
|
+
# pylint:disable=unused-argument
|
|
3583
|
+
def _handle_CallExpr(
|
|
3584
|
+
expr_idx: int,
|
|
3585
|
+
expr: ailment.Stmt.Call,
|
|
3586
|
+
stmt_idx: int,
|
|
3587
|
+
stmt: ailment.Stmt.Statement | None,
|
|
3588
|
+
block: ailment.Block | None,
|
|
3589
|
+
):
|
|
3590
|
+
_handle_Call_stmt_or_expr(expr)
|
|
3591
|
+
|
|
3592
|
+
def _visit_ail_node(node: ailment.Block):
|
|
3593
|
+
w = AILBlockViewer()
|
|
3594
|
+
w.stmt_handlers[ailment.Stmt.Call] = _handle_Call
|
|
3595
|
+
w.expr_handlers[ailment.Stmt.Call] = _handle_CallExpr
|
|
3596
|
+
w.walk(node)
|
|
3597
|
+
|
|
3598
|
+
AILGraphWalker(self._ail_graph, _visit_ail_node).walk()
|
|
3599
|
+
|
|
3600
|
+
return dict(func_proto_candidates)
|
|
3601
|
+
|
|
3602
|
+
def _constrain_callee_prototypes(self):
|
|
3603
|
+
func_proto_candidates = self._collect_callsite_prototypes()
|
|
3604
|
+
|
|
3605
|
+
default_arg_type = SimTypeLongLong if self.project.arch.bits == 64 else SimTypeInt
|
|
3606
|
+
|
|
3607
|
+
for func_addr, protos in func_proto_candidates.items():
|
|
3608
|
+
if not self.kb.functions.contains_addr(func_addr):
|
|
3609
|
+
continue
|
|
3610
|
+
func = self.kb.functions.get_by_addr(func_addr)
|
|
3611
|
+
if func.prototype is not None and func.is_prototype_guessed is False:
|
|
3612
|
+
# already has a "good" prototype; don't overwrite it
|
|
3613
|
+
continue
|
|
3614
|
+
|
|
3615
|
+
# TODO: merge the return type
|
|
3616
|
+
# ret_types = [proto[1] for proto in protos]
|
|
3617
|
+
args_list = [proto[0] for proto in protos]
|
|
3618
|
+
|
|
3619
|
+
# for each argument, we find the most precise type
|
|
3620
|
+
arg_count = min(len(args) for args in args_list)
|
|
3621
|
+
arg_result = {}
|
|
3622
|
+
|
|
3623
|
+
for arg_i in range(arg_count): # pylint:disable=consider-using-enumerate
|
|
3624
|
+
all_args: list[SimType] = [ # type: ignore
|
|
3625
|
+
args_list[i][arg_i] for i in range(len(args_list)) if args_list[i][arg_i] is not None
|
|
3626
|
+
]
|
|
3627
|
+
if not all_args:
|
|
3628
|
+
continue
|
|
3629
|
+
# TODO: Implement a better logic to find the precise type
|
|
3630
|
+
precise_types = []
|
|
3631
|
+
for a in all_args:
|
|
3632
|
+
if isinstance(a, (SimTypePointer, SimStruct, SimTypeArray, SimCppClass)):
|
|
3633
|
+
precise_types.append(a)
|
|
3634
|
+
if len(precise_types) == 1:
|
|
3635
|
+
arg_result[arg_i] = precise_types[0]
|
|
3636
|
+
|
|
3637
|
+
if arg_result:
|
|
3638
|
+
# build a new function prototype
|
|
3639
|
+
new_arg_types = []
|
|
3640
|
+
func_arg_count = (
|
|
3641
|
+
len(func.prototype.args) if func.prototype is not None and func.prototype.args else max(arg_result)
|
|
3642
|
+
)
|
|
3643
|
+
for i in range(func_arg_count):
|
|
3644
|
+
if i in arg_result:
|
|
3645
|
+
new_arg_types.append(arg_result[i])
|
|
3646
|
+
else:
|
|
3647
|
+
if func.prototype is not None:
|
|
3648
|
+
new_arg_types.append(func.prototype.args[i])
|
|
3649
|
+
else:
|
|
3650
|
+
new_arg_types.append(default_arg_type())
|
|
3651
|
+
new_type = SimTypeFunction(
|
|
3652
|
+
new_arg_types,
|
|
3653
|
+
func.prototype.returnty if func.prototype is not None else default_arg_type(),
|
|
3654
|
+
label=func.prototype.label if func.prototype is not None else None,
|
|
3655
|
+
arg_names=func.prototype.arg_names if func.prototype is not None else None,
|
|
3656
|
+
variadic=func.prototype.variadic if func.prototype is not None else False,
|
|
3657
|
+
).with_arch(self.project.arch)
|
|
3658
|
+
func.prototype = new_type
|
|
3659
|
+
func.is_prototype_guessed = False
|
|
3660
|
+
|
|
3661
|
+
|
|
3662
|
+
register_analysis(Clinic, "Clinic")
|