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,2954 @@
|
|
|
1
|
+
# pylint:disable=line-too-long,multiple-statements
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
import logging
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
|
|
7
|
+
import networkx
|
|
8
|
+
from sortedcontainers import SortedDict
|
|
9
|
+
|
|
10
|
+
import pyvex
|
|
11
|
+
from cle import (
|
|
12
|
+
ELF,
|
|
13
|
+
PE,
|
|
14
|
+
Blob,
|
|
15
|
+
PEStubs,
|
|
16
|
+
TLSObject,
|
|
17
|
+
MachO,
|
|
18
|
+
ExternObject,
|
|
19
|
+
KernelObject,
|
|
20
|
+
FunctionHintSource,
|
|
21
|
+
Hex,
|
|
22
|
+
Coff,
|
|
23
|
+
SRec,
|
|
24
|
+
XBE,
|
|
25
|
+
)
|
|
26
|
+
from cle.backends import NamedRegion
|
|
27
|
+
import archinfo
|
|
28
|
+
from archinfo.arch_soot import SootAddressDescriptor, SootMethodDescriptor
|
|
29
|
+
from archinfo.arch_arm import is_arm_arch, get_real_address_if_arm
|
|
30
|
+
|
|
31
|
+
from angr.knowledge_plugins.functions.function_manager import FunctionManager
|
|
32
|
+
from angr.knowledge_plugins.functions.function import Function
|
|
33
|
+
from angr.knowledge_plugins.cfg import IndirectJump, CFGNode, CFGENode, CFGModel # pylint:disable=unused-import
|
|
34
|
+
from angr.procedures.stubs.UnresolvableJumpTarget import UnresolvableJumpTarget
|
|
35
|
+
from angr.utils.constants import DEFAULT_STATEMENT
|
|
36
|
+
from angr.procedures.procedure_dict import SIM_PROCEDURES
|
|
37
|
+
from angr.errors import (
|
|
38
|
+
AngrCFGError,
|
|
39
|
+
SimTranslationError,
|
|
40
|
+
SimMemoryError,
|
|
41
|
+
SimIRSBError,
|
|
42
|
+
SimEngineError,
|
|
43
|
+
AngrUnsupportedSyscallError,
|
|
44
|
+
SimError,
|
|
45
|
+
)
|
|
46
|
+
from angr.codenode import HookNode, BlockNode
|
|
47
|
+
from angr.engines.vex.lifter import VEX_IRSB_MAX_SIZE, VEX_IRSB_MAX_INST
|
|
48
|
+
from angr.analyses import Analysis
|
|
49
|
+
from angr.analyses.stack_pointer_tracker import StackPointerTracker
|
|
50
|
+
from angr.utils.orderedset import OrderedSet
|
|
51
|
+
from .indirect_jump_resolvers.default_resolvers import default_indirect_jump_resolvers
|
|
52
|
+
|
|
53
|
+
if TYPE_CHECKING:
|
|
54
|
+
from angr.sim_state import SimState
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
l = logging.getLogger(name=__name__)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class CFGBase(Analysis):
|
|
61
|
+
"""
|
|
62
|
+
The base class for control flow graphs.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
tag: str | None = None
|
|
66
|
+
_cle_pseudo_objects = (ExternObject, KernelObject, TLSObject)
|
|
67
|
+
|
|
68
|
+
def __init__(
|
|
69
|
+
self,
|
|
70
|
+
sort,
|
|
71
|
+
context_sensitivity_level,
|
|
72
|
+
normalize=False,
|
|
73
|
+
binary=None,
|
|
74
|
+
objects=None,
|
|
75
|
+
regions=None,
|
|
76
|
+
exclude_sparse_regions=True,
|
|
77
|
+
skip_specific_regions=True,
|
|
78
|
+
force_segment=False,
|
|
79
|
+
base_state=None,
|
|
80
|
+
resolve_indirect_jumps=True,
|
|
81
|
+
indirect_jump_resolvers=None,
|
|
82
|
+
indirect_jump_target_limit=100000,
|
|
83
|
+
detect_tail_calls=False,
|
|
84
|
+
low_priority=False,
|
|
85
|
+
skip_unmapped_addrs=True,
|
|
86
|
+
sp_tracking_track_memory=True,
|
|
87
|
+
model=None,
|
|
88
|
+
):
|
|
89
|
+
"""
|
|
90
|
+
:param str sort: 'fast' or 'emulated'.
|
|
91
|
+
:param int context_sensitivity_level: The level of context-sensitivity of this CFG (see documentation for
|
|
92
|
+
further details). It ranges from 0 to infinity.
|
|
93
|
+
:param bool normalize: Whether the CFG as well as all Function graphs should be normalized.
|
|
94
|
+
:param cle.backends.Backend binary: The binary to recover CFG on. By default, the main binary is used.
|
|
95
|
+
:param objects: A list of objects to recover the CFG on. By default, it will recover
|
|
96
|
+
the CFG of all loaded objects.
|
|
97
|
+
:param iterable regions: A list of tuples in the form of (start address, end address)
|
|
98
|
+
describing memory regions that the CFG should cover.
|
|
99
|
+
:param bool force_segment: Force CFGFast to rely on binary segments instead of sections.
|
|
100
|
+
:param angr.SimState base_state: A state to use as a backer for all memory loads.
|
|
101
|
+
:param bool resolve_indirect_jumps: Whether to try to resolve indirect jumps.
|
|
102
|
+
This is necessary to resolve jump targets from jump tables, etc.
|
|
103
|
+
:param list indirect_jump_resolvers: A custom list of indirect jump resolvers.
|
|
104
|
+
If this list is None or empty, default indirect jump resolvers
|
|
105
|
+
specific to this architecture and binary types will be loaded.
|
|
106
|
+
:param int indirect_jump_target_limit: Maximum indirect jump targets to be recovered.
|
|
107
|
+
:param skip_unmapped_addrs: Ignore all branches into unmapped regions. True by default. You may
|
|
108
|
+
want to set it to False if you are analyzing manually patched
|
|
109
|
+
binaries or malware samples.
|
|
110
|
+
:param bool detect_tail_calls: Aggressive tail-call optimization detection. This option is only
|
|
111
|
+
respected in make_functions().
|
|
112
|
+
:param bool sp_tracking_track_memory: Whether or not to track memory writes if tracking the stack pointer.
|
|
113
|
+
This increases the accuracy of stack pointer tracking,
|
|
114
|
+
especially for architectures without a base pointer.
|
|
115
|
+
Only used if detect_tail_calls is enabled.
|
|
116
|
+
:param None or CFGModel model: The CFGModel instance to write to. A new CFGModel instance will be
|
|
117
|
+
created and registered with the knowledge base if `model` is None.
|
|
118
|
+
|
|
119
|
+
:return: None
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
self.sort = sort
|
|
123
|
+
self._context_sensitivity_level = context_sensitivity_level
|
|
124
|
+
|
|
125
|
+
# Sanity checks
|
|
126
|
+
if context_sensitivity_level < 0:
|
|
127
|
+
raise ValueError(f"Unsupported context sensitivity level {context_sensitivity_level}")
|
|
128
|
+
|
|
129
|
+
self._binary = binary if binary is not None else self.project.loader.main_object
|
|
130
|
+
self._force_segment = force_segment
|
|
131
|
+
self._base_state = base_state
|
|
132
|
+
self._detect_tail_calls = detect_tail_calls
|
|
133
|
+
self._low_priority = low_priority
|
|
134
|
+
self._skip_unmapped_addrs = skip_unmapped_addrs
|
|
135
|
+
|
|
136
|
+
# Initialization
|
|
137
|
+
self._edge_map = None
|
|
138
|
+
self._loop_back_edges = None
|
|
139
|
+
self._overlapped_loop_headers = None
|
|
140
|
+
self._thumb_addrs = set()
|
|
141
|
+
self._tail_calls = set()
|
|
142
|
+
|
|
143
|
+
# Store all the functions analyzed before the set is cleared
|
|
144
|
+
# Used for performance optimization
|
|
145
|
+
self._updated_nonreturning_functions: set[int | SootMethodDescriptor] | None = None
|
|
146
|
+
|
|
147
|
+
self._normalize = normalize
|
|
148
|
+
|
|
149
|
+
# Flag, whether to track memory writes in stack pointer tracking
|
|
150
|
+
self._sp_tracking_track_memory = sp_tracking_track_memory
|
|
151
|
+
|
|
152
|
+
# IndirectJump object that describe all indirect exits found in the binary
|
|
153
|
+
# stores as a map between addresses and IndirectJump objects
|
|
154
|
+
self.indirect_jumps: dict[int, IndirectJump] = {}
|
|
155
|
+
self._indirect_jumps_to_resolve = set()
|
|
156
|
+
|
|
157
|
+
# Indirect jump resolvers
|
|
158
|
+
self._indirect_jump_target_limit = indirect_jump_target_limit
|
|
159
|
+
self._resolve_indirect_jumps = resolve_indirect_jumps
|
|
160
|
+
self.timeless_indirect_jump_resolvers = []
|
|
161
|
+
self.indirect_jump_resolvers = []
|
|
162
|
+
if not indirect_jump_resolvers:
|
|
163
|
+
indirect_jump_resolvers = default_indirect_jump_resolvers(self._binary, self.project)
|
|
164
|
+
if self._resolve_indirect_jumps and indirect_jump_resolvers:
|
|
165
|
+
# split them into different groups for the sake of speed
|
|
166
|
+
for ijr in indirect_jump_resolvers:
|
|
167
|
+
if ijr.timeless:
|
|
168
|
+
self.timeless_indirect_jump_resolvers.append(ijr)
|
|
169
|
+
else:
|
|
170
|
+
self.indirect_jump_resolvers.append(ijr)
|
|
171
|
+
|
|
172
|
+
l.info(
|
|
173
|
+
"Loaded %d indirect jump resolvers (%d timeless, %d generic).",
|
|
174
|
+
len(self.timeless_indirect_jump_resolvers) + len(self.indirect_jump_resolvers),
|
|
175
|
+
len(self.timeless_indirect_jump_resolvers),
|
|
176
|
+
len(self.indirect_jump_resolvers),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Get all executable memory regions
|
|
180
|
+
self._exec_mem_regions = self._executable_memory_regions(None, self._force_segment)
|
|
181
|
+
self._exec_mem_region_size = sum((end - start) for start, end in self._exec_mem_regions)
|
|
182
|
+
|
|
183
|
+
# initialize UnresolvableJumpTarget and UnresolvableCallTarget SimProcedure
|
|
184
|
+
# but we do not want to hook the same symbol multiple times
|
|
185
|
+
ut_jump_addr = self.project.loader.extern_object.get_pseudo_addr("UnresolvableJumpTarget")
|
|
186
|
+
if not self.project.is_hooked(ut_jump_addr):
|
|
187
|
+
self.project.hook(ut_jump_addr, SIM_PROCEDURES["stubs"]["UnresolvableJumpTarget"]())
|
|
188
|
+
self._unresolvable_jump_target_addr = ut_jump_addr
|
|
189
|
+
ut_call_addr = self.project.loader.extern_object.get_pseudo_addr("UnresolvableCallTarget")
|
|
190
|
+
if not self.project.is_hooked(ut_call_addr):
|
|
191
|
+
self.project.hook(ut_call_addr, SIM_PROCEDURES["stubs"]["UnresolvableCallTarget"]())
|
|
192
|
+
self._unresolvable_call_target_addr = ut_call_addr
|
|
193
|
+
|
|
194
|
+
# partially and fully analyzed functions
|
|
195
|
+
# this is implemented as a state machine: jobs (CFGJob instances) of each function are put into
|
|
196
|
+
# _jobs_to_analyze_per_function, which essentially makes the keys of this dict being all partially analyzed
|
|
197
|
+
# functions so far. And then when a function does not have any more job to analyze in the future, it will be
|
|
198
|
+
# put in to _completed_functions.
|
|
199
|
+
|
|
200
|
+
# a dict of mapping between function addresses and sets of jobs (include both future jobs and pending jobs)
|
|
201
|
+
# a set is used to speed up the job removal procedure
|
|
202
|
+
self._jobs_to_analyze_per_function = defaultdict(set)
|
|
203
|
+
# addresses of functions that have been completely recovered (i.e. all of its blocks are identified) so far
|
|
204
|
+
self._completed_functions = set()
|
|
205
|
+
|
|
206
|
+
# TODO: A segment tree to speed up CFG node lookups
|
|
207
|
+
self._node_lookup_index = None
|
|
208
|
+
self._node_lookup_index_warned = False
|
|
209
|
+
|
|
210
|
+
self._function_addresses_from_symbols = self._load_func_addrs_from_symbols()
|
|
211
|
+
self._function_addresses_from_eh_frame = self._load_func_addrs_from_eh_frame()
|
|
212
|
+
|
|
213
|
+
# Cache if an object has executable sections or not
|
|
214
|
+
self._object_to_executable_sections = {}
|
|
215
|
+
# Cache if an object has executable segments or not
|
|
216
|
+
self._object_to_executable_segments = {}
|
|
217
|
+
|
|
218
|
+
if model is not None:
|
|
219
|
+
self._model = model
|
|
220
|
+
else:
|
|
221
|
+
self._model: CFGModel = self.kb.cfgs.new_model(self.tag)
|
|
222
|
+
|
|
223
|
+
# necessary warnings
|
|
224
|
+
regions_not_specified = regions is None and binary is None and not objects
|
|
225
|
+
if regions_not_specified and self.project.loader._auto_load_libs and len(self.project.loader.all_objects) > 3:
|
|
226
|
+
l.warning(
|
|
227
|
+
'"auto_load_libs" is enabled. With libraries loaded in project, CFG will cover libraries, '
|
|
228
|
+
"which may take significantly more time than expected. You may reload the binary with "
|
|
229
|
+
'"auto_load_libs" disabled, or specify "regions" to limit the scope of CFG recovery.'
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if regions is None:
|
|
233
|
+
if self._skip_unmapped_addrs:
|
|
234
|
+
regions = self._executable_memory_regions(objects=objects, force_segment=force_segment)
|
|
235
|
+
else:
|
|
236
|
+
if not objects:
|
|
237
|
+
objects = self.project.loader.all_objects
|
|
238
|
+
regions = self._executable_memory_regions(objects=objects, force_segment=force_segment)
|
|
239
|
+
if not regions:
|
|
240
|
+
# fall back to using min and max addresses of all objects
|
|
241
|
+
regions = [(obj.min_addr, obj.max_addr) for obj in objects]
|
|
242
|
+
|
|
243
|
+
for start, end in regions:
|
|
244
|
+
if end < start:
|
|
245
|
+
raise AngrCFGError("Invalid region bounds (end precedes start)")
|
|
246
|
+
|
|
247
|
+
# Block factory returns patched state by default, so ensure we are also analyzing the patched state
|
|
248
|
+
if self._base_state is None and self.project.kb.patches.values():
|
|
249
|
+
self._base_state = self.project.kb.patches.patched_entry_state
|
|
250
|
+
|
|
251
|
+
if exclude_sparse_regions:
|
|
252
|
+
regions = [r for r in regions if not self._is_region_extremely_sparse(*r, base_state=self._base_state)]
|
|
253
|
+
|
|
254
|
+
if skip_specific_regions:
|
|
255
|
+
if base_state is not None:
|
|
256
|
+
l.warning("You specified both base_state and skip_specific_regions. They may conflict with each other.")
|
|
257
|
+
regions = [r for r in regions if not self._should_skip_region(r[0])]
|
|
258
|
+
|
|
259
|
+
if not regions and self.project.arch.name != "Soot":
|
|
260
|
+
raise AngrCFGError(
|
|
261
|
+
"Regions are empty, or all regions are skipped. You may want to manually specify regions."
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
self._regions_size = sum((end - start) for start, end in regions)
|
|
265
|
+
self._regions: SortedDict = SortedDict(regions)
|
|
266
|
+
|
|
267
|
+
l.debug("CFG recovery covers %d regions:", len(self._regions))
|
|
268
|
+
for start, end in self._regions.items():
|
|
269
|
+
l.debug("... %#x - %#x", start, end)
|
|
270
|
+
|
|
271
|
+
def __contains__(self, cfg_node):
|
|
272
|
+
return cfg_node in self.graph
|
|
273
|
+
|
|
274
|
+
#
|
|
275
|
+
# Properties
|
|
276
|
+
#
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def _nodes(self):
|
|
280
|
+
return self._model._nodes
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def _nodes_by_addr(self):
|
|
284
|
+
return self._model._nodes_by_addr
|
|
285
|
+
|
|
286
|
+
@property
|
|
287
|
+
def model(self) -> CFGModel:
|
|
288
|
+
"""
|
|
289
|
+
Get the CFGModel instance.
|
|
290
|
+
:return: The CFGModel instance that this analysis currently uses.
|
|
291
|
+
"""
|
|
292
|
+
return self._model
|
|
293
|
+
|
|
294
|
+
@property
|
|
295
|
+
def normalized(self):
|
|
296
|
+
return self._model.normalized
|
|
297
|
+
|
|
298
|
+
@normalized.setter
|
|
299
|
+
def normalized(self, v):
|
|
300
|
+
self._model.normalized = v
|
|
301
|
+
|
|
302
|
+
@property
|
|
303
|
+
def context_sensitivity_level(self):
|
|
304
|
+
return self._context_sensitivity_level
|
|
305
|
+
|
|
306
|
+
@property
|
|
307
|
+
def functions(self):
|
|
308
|
+
"""
|
|
309
|
+
A reference to the FunctionManager in the current knowledge base.
|
|
310
|
+
|
|
311
|
+
:return: FunctionManager with all functions
|
|
312
|
+
:rtype: angr.knowledge_plugins.FunctionManager
|
|
313
|
+
"""
|
|
314
|
+
return self.kb.functions
|
|
315
|
+
|
|
316
|
+
#
|
|
317
|
+
# Methods
|
|
318
|
+
#
|
|
319
|
+
|
|
320
|
+
def _initialize_cfg(self):
|
|
321
|
+
"""
|
|
322
|
+
Re-create the DiGraph
|
|
323
|
+
"""
|
|
324
|
+
|
|
325
|
+
self._jobs_to_analyze_per_function = defaultdict(set)
|
|
326
|
+
self._completed_functions = set()
|
|
327
|
+
|
|
328
|
+
def _function_completed(self, func_addr: int):
|
|
329
|
+
pass
|
|
330
|
+
|
|
331
|
+
def _post_analysis(self):
|
|
332
|
+
if self._normalize:
|
|
333
|
+
if not self.normalized:
|
|
334
|
+
self.normalize()
|
|
335
|
+
|
|
336
|
+
# Call normalize() on each function
|
|
337
|
+
for f in self.kb.functions.values():
|
|
338
|
+
if not self.project.is_hooked(f.addr):
|
|
339
|
+
f.normalize()
|
|
340
|
+
|
|
341
|
+
# drop all propagation results that start with "cfg_intermediate"
|
|
342
|
+
self.kb.propagations.discard_by_prefix("cfg_intermediate")
|
|
343
|
+
|
|
344
|
+
def make_copy(self, copy_to):
|
|
345
|
+
"""
|
|
346
|
+
Copy self attributes to the new object.
|
|
347
|
+
|
|
348
|
+
:param CFGBase copy_to: The target to copy to.
|
|
349
|
+
:return: None
|
|
350
|
+
"""
|
|
351
|
+
|
|
352
|
+
for attr, value in self.__dict__.items():
|
|
353
|
+
if attr.startswith("__") and attr.endswith("__"):
|
|
354
|
+
continue
|
|
355
|
+
setattr(copy_to, attr, value)
|
|
356
|
+
|
|
357
|
+
# pylint: disable=no-self-use
|
|
358
|
+
def copy(self):
|
|
359
|
+
raise NotImplementedError
|
|
360
|
+
|
|
361
|
+
def output(self):
|
|
362
|
+
raise NotImplementedError
|
|
363
|
+
|
|
364
|
+
def generate_index(self):
|
|
365
|
+
"""
|
|
366
|
+
Generate an index of all nodes in the graph in order to speed up get_any_node() with anyaddr=True.
|
|
367
|
+
|
|
368
|
+
:return: None
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
raise NotImplementedError("I'm too lazy to implement it right now")
|
|
372
|
+
|
|
373
|
+
def get_loop_back_edges(self):
|
|
374
|
+
return self._loop_back_edges
|
|
375
|
+
|
|
376
|
+
@property
|
|
377
|
+
def graph(self) -> networkx.DiGraph[CFGNode]:
|
|
378
|
+
raise NotImplementedError
|
|
379
|
+
|
|
380
|
+
def remove_edge(self, block_from, block_to):
|
|
381
|
+
if self.graph is None:
|
|
382
|
+
raise TypeError("self.graph does not exist.")
|
|
383
|
+
|
|
384
|
+
if block_from not in self.graph:
|
|
385
|
+
raise ValueError(f"{block_from!r} is not in CFG.")
|
|
386
|
+
|
|
387
|
+
if block_to not in self.graph:
|
|
388
|
+
raise ValueError(f"{block_to!r} is not in CFG.")
|
|
389
|
+
|
|
390
|
+
if block_to not in self.graph[block_from]:
|
|
391
|
+
raise ValueError(f"Edge {block_from!r}->{block_to!r} does not exist.")
|
|
392
|
+
|
|
393
|
+
self.graph.remove_edge(block_from, block_to)
|
|
394
|
+
|
|
395
|
+
def _merge_cfgnodes(self, cfgnode_0, cfgnode_1):
|
|
396
|
+
"""
|
|
397
|
+
Merge two adjacent CFGNodes into one.
|
|
398
|
+
|
|
399
|
+
:param CFGNode cfgnode_0: The first CFGNode.
|
|
400
|
+
:param CFGNode cfgnode_1: The second CFGNode.
|
|
401
|
+
:return: None
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
assert cfgnode_0.addr + cfgnode_0.size == cfgnode_1.addr
|
|
405
|
+
new_node = cfgnode_0.merge(cfgnode_1)
|
|
406
|
+
|
|
407
|
+
# Update the graph and the nodes dict accordingly
|
|
408
|
+
self._model.remove_node(cfgnode_1.block_id, cfgnode_1)
|
|
409
|
+
self._model.remove_node(cfgnode_0.block_id, cfgnode_0)
|
|
410
|
+
|
|
411
|
+
in_edges = list(self.graph.in_edges(cfgnode_0, data=True))
|
|
412
|
+
out_edges = list(self.graph.out_edges(cfgnode_1, data=True))
|
|
413
|
+
|
|
414
|
+
self.graph.remove_node(cfgnode_0)
|
|
415
|
+
self.graph.remove_node(cfgnode_1)
|
|
416
|
+
|
|
417
|
+
self.graph.add_node(new_node)
|
|
418
|
+
for src, _, data in in_edges:
|
|
419
|
+
self.graph.add_edge(src, new_node, **data)
|
|
420
|
+
for _, dst, data in out_edges:
|
|
421
|
+
self.graph.add_edge(new_node, dst, **data)
|
|
422
|
+
|
|
423
|
+
# Put the new node into node dicts
|
|
424
|
+
self._model.add_node(new_node.block_id, new_node)
|
|
425
|
+
|
|
426
|
+
def _to_snippet(self, cfg_node=None, addr=None, size=None, thumb=False, jumpkind=None, base_state=None):
|
|
427
|
+
"""
|
|
428
|
+
Convert a CFGNode instance to a CodeNode object.
|
|
429
|
+
|
|
430
|
+
:param angr.analyses.CFGNode cfg_node: The CFGNode instance.
|
|
431
|
+
:param int addr: Address of the node. Only used when `cfg_node` is None.
|
|
432
|
+
:param bool thumb: Whether this is in THUMB mode or not. Only used for ARM code and when `cfg_node` is None.
|
|
433
|
+
:param str or None jumpkind: Jumpkind of this node.
|
|
434
|
+
:param SimState or None base_state: The state where BlockNode should be created from.
|
|
435
|
+
:return: A converted CodeNode instance.
|
|
436
|
+
:rtype: CodeNode
|
|
437
|
+
"""
|
|
438
|
+
|
|
439
|
+
if cfg_node is not None:
|
|
440
|
+
addr = cfg_node.addr
|
|
441
|
+
size = cfg_node.size
|
|
442
|
+
thumb = cfg_node.thumb
|
|
443
|
+
|
|
444
|
+
if addr is None:
|
|
445
|
+
raise ValueError("_to_snippet(): Either cfg_node or addr must be provided.")
|
|
446
|
+
|
|
447
|
+
if self.project.is_hooked(addr) and jumpkind != "Ijk_NoHook":
|
|
448
|
+
hooker = self.project._sim_procedures[addr]
|
|
449
|
+
size = hooker.kwargs.get("length", 0)
|
|
450
|
+
return HookNode(addr, size, type(hooker))
|
|
451
|
+
|
|
452
|
+
if cfg_node is not None:
|
|
453
|
+
return BlockNode(addr, size, thumb=thumb, bytestr=cfg_node.byte_string) # pylint: disable=no-member
|
|
454
|
+
return self.project.factory.snippet(addr, size=size, jumpkind=jumpkind, thumb=thumb, backup_state=base_state)
|
|
455
|
+
|
|
456
|
+
def is_thumb_addr(self, addr):
|
|
457
|
+
return addr in self._thumb_addrs
|
|
458
|
+
|
|
459
|
+
def _arm_thumb_filter_jump_successors(self, irsb, successors, get_ins_addr, get_exit_stmt_idx, get_jumpkind):
|
|
460
|
+
"""
|
|
461
|
+
Filter successors for THUMB mode basic blocks, and remove those successors that won't be taken normally.
|
|
462
|
+
|
|
463
|
+
:param irsb: The IRSB object.
|
|
464
|
+
:param list successors: A list of successors.
|
|
465
|
+
:param func get_ins_addr: A callable that returns the source instruction address for a successor.
|
|
466
|
+
:param func get_exit_stmt_idx: A callable that returns the source statement ID for a successor.
|
|
467
|
+
:param func get_jumpkind: A callable that returns the jumpkind of a successor.
|
|
468
|
+
:return: A new list of successors after filtering.
|
|
469
|
+
:rtype: list
|
|
470
|
+
"""
|
|
471
|
+
|
|
472
|
+
if not successors:
|
|
473
|
+
return []
|
|
474
|
+
|
|
475
|
+
if len(successors) == 1 and get_exit_stmt_idx(successors[0]) == DEFAULT_STATEMENT:
|
|
476
|
+
# only have a default exit. no need to filter
|
|
477
|
+
return successors
|
|
478
|
+
|
|
479
|
+
if irsb.instruction_addresses and all(
|
|
480
|
+
get_ins_addr(suc) == irsb.instruction_addresses[-1] for suc in successors
|
|
481
|
+
):
|
|
482
|
+
# check if all exits are produced by the last instruction
|
|
483
|
+
# only takes the following jump kinds: Boring, FakeRet, Call, Syscall, Ret
|
|
484
|
+
allowed_jumpkinds = {"Ijk_Boring", "Ijk_FakeRet", "Ijk_Call", "Ijk_Ret"}
|
|
485
|
+
successors = [
|
|
486
|
+
suc
|
|
487
|
+
for suc in successors
|
|
488
|
+
if get_jumpkind(suc) in allowed_jumpkinds or get_jumpkind(suc).startswith("Ijk_Sys")
|
|
489
|
+
]
|
|
490
|
+
if len(successors) == 1:
|
|
491
|
+
return successors
|
|
492
|
+
|
|
493
|
+
can_produce_exits = set() # addresses of instructions that can produce exits
|
|
494
|
+
bb = self._lift(irsb.addr, size=irsb.size, thumb=True)
|
|
495
|
+
|
|
496
|
+
# step A: filter exits using capstone (since it's faster than re-lifting the entire block to VEX)
|
|
497
|
+
THUMB_BRANCH_INSTRUCTIONS = {
|
|
498
|
+
"beq",
|
|
499
|
+
"bne",
|
|
500
|
+
"bcs",
|
|
501
|
+
"bhs",
|
|
502
|
+
"bcc",
|
|
503
|
+
"blo",
|
|
504
|
+
"bmi",
|
|
505
|
+
"bpl",
|
|
506
|
+
"bvs",
|
|
507
|
+
"bvc",
|
|
508
|
+
"bhi",
|
|
509
|
+
"bls",
|
|
510
|
+
"bge",
|
|
511
|
+
"blt",
|
|
512
|
+
"bgt",
|
|
513
|
+
"ble",
|
|
514
|
+
"cbz",
|
|
515
|
+
"cbnz",
|
|
516
|
+
}
|
|
517
|
+
for cs_insn in bb.capstone.insns:
|
|
518
|
+
if cs_insn.mnemonic.split(".")[0] in THUMB_BRANCH_INSTRUCTIONS:
|
|
519
|
+
can_produce_exits.add(cs_insn.address)
|
|
520
|
+
|
|
521
|
+
if all(
|
|
522
|
+
get_ins_addr(suc) in can_produce_exits or get_exit_stmt_idx(suc) == DEFAULT_STATEMENT for suc in successors
|
|
523
|
+
):
|
|
524
|
+
# nothing will be filtered.
|
|
525
|
+
return successors
|
|
526
|
+
|
|
527
|
+
# step B: consider VEX statements
|
|
528
|
+
it_counter = 0
|
|
529
|
+
conc_temps = {}
|
|
530
|
+
|
|
531
|
+
for stmt in bb.vex.statements:
|
|
532
|
+
if stmt.tag == "Ist_IMark":
|
|
533
|
+
if it_counter > 0:
|
|
534
|
+
it_counter -= 1
|
|
535
|
+
can_produce_exits.add(stmt.addr + stmt.delta)
|
|
536
|
+
elif stmt.tag == "Ist_WrTmp":
|
|
537
|
+
val = stmt.data
|
|
538
|
+
if val.tag == "Iex_Const":
|
|
539
|
+
conc_temps[stmt.tmp] = val.con.value
|
|
540
|
+
elif stmt.tag == "Ist_Put" and stmt.offset == self.project.arch.registers["itstate"][0]:
|
|
541
|
+
val = stmt.data
|
|
542
|
+
if val.tag == "Iex_RdTmp":
|
|
543
|
+
if val.tmp in conc_temps:
|
|
544
|
+
# We found an IT instruction!!
|
|
545
|
+
# Determine how many instructions are conditional
|
|
546
|
+
it_counter = 0
|
|
547
|
+
itstate = conc_temps[val.tmp]
|
|
548
|
+
while itstate != 0:
|
|
549
|
+
it_counter += 1
|
|
550
|
+
itstate >>= 8
|
|
551
|
+
elif val.tag == "Iex_Const":
|
|
552
|
+
it_counter = 0
|
|
553
|
+
itstate = val.con.value
|
|
554
|
+
while itstate != 0:
|
|
555
|
+
it_counter += 1
|
|
556
|
+
itstate >>= 8
|
|
557
|
+
|
|
558
|
+
if it_counter != 0:
|
|
559
|
+
l.debug("Basic block ends before calculated IT block (%#x)", irsb.addr)
|
|
560
|
+
|
|
561
|
+
return [
|
|
562
|
+
suc
|
|
563
|
+
for suc in successors
|
|
564
|
+
if get_ins_addr(suc) in can_produce_exits or get_exit_stmt_idx(suc) == DEFAULT_STATEMENT
|
|
565
|
+
]
|
|
566
|
+
|
|
567
|
+
# Methods for determining scanning scope
|
|
568
|
+
|
|
569
|
+
def _inside_regions(self, address: int | None) -> bool:
|
|
570
|
+
"""
|
|
571
|
+
Check if the address is inside any existing region.
|
|
572
|
+
|
|
573
|
+
:param int address: Address to check.
|
|
574
|
+
:return: True if the address is within one of the memory regions, False otherwise.
|
|
575
|
+
"""
|
|
576
|
+
|
|
577
|
+
try:
|
|
578
|
+
start_addr = next(self._regions.irange(maximum=address, reverse=True))
|
|
579
|
+
except StopIteration:
|
|
580
|
+
return False
|
|
581
|
+
else:
|
|
582
|
+
return address < self._regions[start_addr]
|
|
583
|
+
|
|
584
|
+
def _get_min_addr(self) -> int | None:
|
|
585
|
+
"""
|
|
586
|
+
Get the minimum address out of all regions. We assume self._regions is sorted.
|
|
587
|
+
|
|
588
|
+
:return: The minimum address, or None if there is no such address.
|
|
589
|
+
"""
|
|
590
|
+
|
|
591
|
+
if not self._regions:
|
|
592
|
+
if self.project.arch.name != "Soot":
|
|
593
|
+
l.error("self._regions is empty or not properly set.")
|
|
594
|
+
return None
|
|
595
|
+
|
|
596
|
+
return next(self._regions.irange())
|
|
597
|
+
|
|
598
|
+
def _next_address_in_regions(self, address: int | None) -> int | None:
|
|
599
|
+
"""
|
|
600
|
+
Return the next immediate address that is inside any of the regions.
|
|
601
|
+
|
|
602
|
+
:param address: The address to start scanning.
|
|
603
|
+
:return: The next address that is inside one of the memory regions, or None if there is no such address.
|
|
604
|
+
"""
|
|
605
|
+
|
|
606
|
+
if self._inside_regions(address):
|
|
607
|
+
return address
|
|
608
|
+
|
|
609
|
+
try:
|
|
610
|
+
return next(self._regions.irange(minimum=address, reverse=False))
|
|
611
|
+
except StopIteration:
|
|
612
|
+
return None
|
|
613
|
+
|
|
614
|
+
def _is_region_extremely_sparse(self, start: int, end: int, base_state: SimState | None = None) -> bool:
|
|
615
|
+
"""
|
|
616
|
+
Check whether the given memory region is extremely sparse, i.e., all bytes are the same value.
|
|
617
|
+
|
|
618
|
+
:param start: The beginning of the region.
|
|
619
|
+
:param end: The end of the region (exclusive).
|
|
620
|
+
:param base_state: The base state (optional).
|
|
621
|
+
:return: True if the region is extremely sparse, False otherwise.
|
|
622
|
+
"""
|
|
623
|
+
|
|
624
|
+
all_bytes = None
|
|
625
|
+
|
|
626
|
+
if base_state is not None:
|
|
627
|
+
all_bytes = base_state.memory.load(start, end - start)
|
|
628
|
+
try:
|
|
629
|
+
all_bytes = base_state.solver.eval(all_bytes, cast_to=bytes)
|
|
630
|
+
except SimError:
|
|
631
|
+
all_bytes = None
|
|
632
|
+
|
|
633
|
+
size = end - start
|
|
634
|
+
|
|
635
|
+
if all_bytes is None:
|
|
636
|
+
# load from the binary
|
|
637
|
+
all_bytes = self._fast_memory_load_bytes(start, size)
|
|
638
|
+
|
|
639
|
+
if all_bytes is None:
|
|
640
|
+
# failed to load bytes in this region. it might be because the region is not fully mapped (i.e., there are
|
|
641
|
+
# holes). we assume this region is good for analysis.
|
|
642
|
+
return False
|
|
643
|
+
|
|
644
|
+
if len(all_bytes) < size:
|
|
645
|
+
l.warning(
|
|
646
|
+
"_is_region_extremely_sparse: The given region %#x-%#x is not a continuous memory region in the "
|
|
647
|
+
"memory space. Only the first %d bytes (%#x-%#x) are processed.",
|
|
648
|
+
start,
|
|
649
|
+
end,
|
|
650
|
+
len(all_bytes),
|
|
651
|
+
start,
|
|
652
|
+
start + len(all_bytes),
|
|
653
|
+
)
|
|
654
|
+
|
|
655
|
+
the_byte_value = None
|
|
656
|
+
for b in all_bytes:
|
|
657
|
+
if the_byte_value is None:
|
|
658
|
+
the_byte_value = b
|
|
659
|
+
else:
|
|
660
|
+
if the_byte_value != b:
|
|
661
|
+
return False
|
|
662
|
+
|
|
663
|
+
return True
|
|
664
|
+
|
|
665
|
+
def _should_skip_region(self, region_start):
|
|
666
|
+
"""
|
|
667
|
+
Some regions usually do not contain any executable code, but are still marked as executable. We should skip
|
|
668
|
+
those regions by default.
|
|
669
|
+
|
|
670
|
+
:param int region_start: Address of the beginning of the region.
|
|
671
|
+
:return: True/False
|
|
672
|
+
:rtype: bool
|
|
673
|
+
"""
|
|
674
|
+
|
|
675
|
+
obj = self.project.loader.find_object_containing(region_start, membership_check=False)
|
|
676
|
+
if obj is None:
|
|
677
|
+
return False
|
|
678
|
+
if isinstance(obj, PE):
|
|
679
|
+
section = obj.find_section_containing(region_start)
|
|
680
|
+
if section is None:
|
|
681
|
+
return False
|
|
682
|
+
if section.name in {".textbss"}:
|
|
683
|
+
return True
|
|
684
|
+
|
|
685
|
+
return False
|
|
686
|
+
|
|
687
|
+
def _executable_memory_regions(self, objects=None, force_segment=False):
|
|
688
|
+
"""
|
|
689
|
+
Get all executable memory regions from the binaries
|
|
690
|
+
|
|
691
|
+
:param objects: A collection of binary objects to collect regions from. If None, regions from all project
|
|
692
|
+
binary objects are used.
|
|
693
|
+
:param bool force_segment: Rely on binary segments instead of sections.
|
|
694
|
+
:return: A sorted list of tuples (beginning_address, end_address)
|
|
695
|
+
"""
|
|
696
|
+
|
|
697
|
+
binaries = self.project.loader.all_objects if objects is None else objects
|
|
698
|
+
|
|
699
|
+
memory_regions = []
|
|
700
|
+
|
|
701
|
+
for b in binaries:
|
|
702
|
+
if not b.has_memory:
|
|
703
|
+
continue
|
|
704
|
+
|
|
705
|
+
if isinstance(b, ELF):
|
|
706
|
+
# If we have sections, we get result from sections
|
|
707
|
+
sections = []
|
|
708
|
+
if not force_segment and b.sections:
|
|
709
|
+
# Get all executable sections
|
|
710
|
+
for section in b.sections:
|
|
711
|
+
if section.is_executable:
|
|
712
|
+
max_mapped_addr = section.min_addr + min(section.memsize, section.filesize)
|
|
713
|
+
tpl = (section.min_addr, max_mapped_addr)
|
|
714
|
+
sections.append(tpl)
|
|
715
|
+
memory_regions += sections
|
|
716
|
+
|
|
717
|
+
segments = []
|
|
718
|
+
# Get all executable segments
|
|
719
|
+
for segment in b.segments:
|
|
720
|
+
if segment.is_executable:
|
|
721
|
+
max_mapped_addr = segment.min_addr + min(segment.memsize, segment.filesize)
|
|
722
|
+
tpl = (segment.min_addr, max_mapped_addr)
|
|
723
|
+
segments.append(tpl)
|
|
724
|
+
if sections and segments:
|
|
725
|
+
# are there executable segments with no sections inside?
|
|
726
|
+
for segment in segments:
|
|
727
|
+
for section in sections:
|
|
728
|
+
if segment[0] <= section[0] < segment[1]:
|
|
729
|
+
break
|
|
730
|
+
else:
|
|
731
|
+
memory_regions.append(segment)
|
|
732
|
+
|
|
733
|
+
elif isinstance(b, (Coff, PE)):
|
|
734
|
+
for section in b.sections:
|
|
735
|
+
if section.is_executable:
|
|
736
|
+
max_mapped_addr = section.min_addr + min(section.memsize, section.filesize)
|
|
737
|
+
tpl = (section.min_addr, max_mapped_addr)
|
|
738
|
+
memory_regions.append(tpl)
|
|
739
|
+
|
|
740
|
+
elif isinstance(b, XBE):
|
|
741
|
+
# some XBE files will mark the data sections as executable
|
|
742
|
+
for section in b.sections:
|
|
743
|
+
if (
|
|
744
|
+
section.is_executable
|
|
745
|
+
and not section.is_writable
|
|
746
|
+
and section.name not in {".data", ".rdata", ".rodata"}
|
|
747
|
+
):
|
|
748
|
+
tpl = (section.min_addr, section.max_addr)
|
|
749
|
+
memory_regions.append(tpl)
|
|
750
|
+
|
|
751
|
+
elif isinstance(b, MachO):
|
|
752
|
+
if b.segments:
|
|
753
|
+
# Get all executable segments
|
|
754
|
+
for seg in b.segments:
|
|
755
|
+
if seg.is_executable:
|
|
756
|
+
# Take all sections from this segment (MachO style)
|
|
757
|
+
for section in seg.sections:
|
|
758
|
+
max_mapped_addr = section.min_addr + min(section.memsize, section.filesize)
|
|
759
|
+
tpl = (section.min_addr, max_mapped_addr)
|
|
760
|
+
memory_regions.append(tpl)
|
|
761
|
+
|
|
762
|
+
elif isinstance(b, (Hex, SRec)):
|
|
763
|
+
if b.regions:
|
|
764
|
+
for region_addr, region_size in b.regions:
|
|
765
|
+
memory_regions.append((region_addr, region_addr + region_size))
|
|
766
|
+
|
|
767
|
+
elif isinstance(b, Blob):
|
|
768
|
+
# a blob is entirely executable
|
|
769
|
+
tpl = (b.min_addr, b.max_addr + 1)
|
|
770
|
+
memory_regions.append(tpl)
|
|
771
|
+
|
|
772
|
+
elif isinstance(b, NamedRegion):
|
|
773
|
+
# NamedRegions have no content! Ignore
|
|
774
|
+
pass
|
|
775
|
+
|
|
776
|
+
elif isinstance(b, PEStubs):
|
|
777
|
+
# no code here
|
|
778
|
+
pass
|
|
779
|
+
|
|
780
|
+
elif isinstance(b, self._cle_pseudo_objects):
|
|
781
|
+
pass
|
|
782
|
+
|
|
783
|
+
elif hasattr(b, "sections") and b.sections:
|
|
784
|
+
sections = []
|
|
785
|
+
# Get all executable sections
|
|
786
|
+
for section in b.sections:
|
|
787
|
+
if section.is_executable:
|
|
788
|
+
max_mapped_addr = section.min_addr + min(section.memsize, section.filesize)
|
|
789
|
+
tpl = (section.min_addr, max_mapped_addr)
|
|
790
|
+
sections.append(tpl)
|
|
791
|
+
memory_regions += sections
|
|
792
|
+
|
|
793
|
+
elif hasattr(b, "segments") and b.segments:
|
|
794
|
+
segments = []
|
|
795
|
+
for segment in b.segments:
|
|
796
|
+
if segment.is_executable:
|
|
797
|
+
max_mapped_addr = segment.min_addr + min(segment.memsize, segment.filesize)
|
|
798
|
+
tpl = (segment.min_addr, max_mapped_addr)
|
|
799
|
+
segments.append(tpl)
|
|
800
|
+
memory_regions += segments
|
|
801
|
+
|
|
802
|
+
else:
|
|
803
|
+
l.warning('Unsupported object format "%s". Treat it as an executable.', b.__class__.__name__)
|
|
804
|
+
|
|
805
|
+
tpl = (b.min_addr, b.max_addr + 1)
|
|
806
|
+
memory_regions.append(tpl)
|
|
807
|
+
|
|
808
|
+
if not memory_regions:
|
|
809
|
+
memory_regions = [(start, start + len(backer)) for start, backer in self.project.loader.memory.backers()]
|
|
810
|
+
|
|
811
|
+
return sorted(memory_regions, key=lambda x: x[0])
|
|
812
|
+
|
|
813
|
+
def _addr_in_exec_memory_regions(self, addr):
|
|
814
|
+
"""
|
|
815
|
+
Test if the address belongs to an executable memory region.
|
|
816
|
+
|
|
817
|
+
:param int addr: The address to test
|
|
818
|
+
:return: True if the address belongs to an exectubale memory region, False otherwise
|
|
819
|
+
:rtype: bool
|
|
820
|
+
"""
|
|
821
|
+
|
|
822
|
+
return any(start <= addr < end for start, end in self._exec_mem_regions)
|
|
823
|
+
|
|
824
|
+
def _addrs_belong_to_same_section(self, addr_a, addr_b):
|
|
825
|
+
"""
|
|
826
|
+
Test if two addresses belong to the same section.
|
|
827
|
+
|
|
828
|
+
:param int addr_a: The first address to test.
|
|
829
|
+
:param int addr_b: The second address to test.
|
|
830
|
+
:return: True if the two addresses belong to the same section or both of them do not belong to any
|
|
831
|
+
section, False otherwise.
|
|
832
|
+
:rtype: bool
|
|
833
|
+
"""
|
|
834
|
+
|
|
835
|
+
obj = self.project.loader.find_object_containing(addr_a, membership_check=False)
|
|
836
|
+
|
|
837
|
+
if obj is None:
|
|
838
|
+
# test if addr_b also does not belong to any object
|
|
839
|
+
obj_b = self.project.loader.find_object_containing(addr_b, membership_check=False)
|
|
840
|
+
return obj_b is None
|
|
841
|
+
|
|
842
|
+
src_section = obj.find_section_containing(addr_a)
|
|
843
|
+
if src_section is None:
|
|
844
|
+
# test if addr_b also does not belong to any section
|
|
845
|
+
dst_section = obj.find_section_containing(addr_b)
|
|
846
|
+
if dst_section is None:
|
|
847
|
+
return self._addrs_belong_to_same_segment(addr_a, addr_b)
|
|
848
|
+
return False
|
|
849
|
+
|
|
850
|
+
return src_section.contains_addr(addr_b)
|
|
851
|
+
|
|
852
|
+
def _addrs_belong_to_same_segment(self, addr_a, addr_b):
|
|
853
|
+
"""
|
|
854
|
+
Test if two addresses belong to the same segment.
|
|
855
|
+
|
|
856
|
+
:param int addr_a: The first address to test.
|
|
857
|
+
:param int addr_b: The second address to test.
|
|
858
|
+
:return: True if the two addresses belong to the same segment or both of them do not belong to any
|
|
859
|
+
section, False otherwise.
|
|
860
|
+
:rtype: bool
|
|
861
|
+
"""
|
|
862
|
+
|
|
863
|
+
obj = self.project.loader.find_object_containing(addr_a, membership_check=False)
|
|
864
|
+
|
|
865
|
+
if obj is None:
|
|
866
|
+
# test if addr_b also does not belong to any object
|
|
867
|
+
obj_b = self.project.loader.find_object_containing(addr_b, membership_check=False)
|
|
868
|
+
return obj_b is None
|
|
869
|
+
|
|
870
|
+
src_segment = obj.find_segment_containing(addr_a)
|
|
871
|
+
if src_segment is None:
|
|
872
|
+
# test if addr_b also does not belong to any section
|
|
873
|
+
dst_segment = obj.find_segment_containing(addr_b)
|
|
874
|
+
return dst_segment is None
|
|
875
|
+
|
|
876
|
+
return src_segment.contains_addr(addr_b)
|
|
877
|
+
|
|
878
|
+
def _object_has_executable_sections(self, obj):
|
|
879
|
+
"""
|
|
880
|
+
Check whether an object has at least one executable section.
|
|
881
|
+
|
|
882
|
+
:param cle.Backend obj: The object to test.
|
|
883
|
+
:return: None
|
|
884
|
+
"""
|
|
885
|
+
|
|
886
|
+
if obj in self._object_to_executable_sections:
|
|
887
|
+
return self._object_to_executable_sections[obj]
|
|
888
|
+
r = any(sec.is_executable for sec in obj.sections)
|
|
889
|
+
self._object_to_executable_sections[obj] = r
|
|
890
|
+
return r
|
|
891
|
+
|
|
892
|
+
def _object_has_executable_segments(self, obj):
|
|
893
|
+
"""
|
|
894
|
+
Check whether an object has at least one executable segment.
|
|
895
|
+
|
|
896
|
+
:param cle.Backend obj: The object to test.
|
|
897
|
+
:return: None
|
|
898
|
+
"""
|
|
899
|
+
|
|
900
|
+
if obj in self._object_to_executable_segments:
|
|
901
|
+
return self._object_to_executable_segments[obj]
|
|
902
|
+
r = any(seg.is_executable for seg in obj.segments)
|
|
903
|
+
self._object_to_executable_segments[obj] = r
|
|
904
|
+
return r
|
|
905
|
+
|
|
906
|
+
def _addr_hooked_or_syscall(self, addr):
|
|
907
|
+
"""
|
|
908
|
+
Check whether the address belongs to a hook or a syscall.
|
|
909
|
+
|
|
910
|
+
:param int addr: The address to check.
|
|
911
|
+
:return: True if the address is hooked or belongs to a syscall. False otherwise.
|
|
912
|
+
:rtype: bool
|
|
913
|
+
"""
|
|
914
|
+
|
|
915
|
+
return self.project.is_hooked(addr) or self.project.simos.is_syscall_addr(addr)
|
|
916
|
+
|
|
917
|
+
def _fast_memory_load_byte(self, addr):
|
|
918
|
+
"""
|
|
919
|
+
Perform a fast memory loading of a byte.
|
|
920
|
+
|
|
921
|
+
:param int addr: Address to read from.
|
|
922
|
+
:return: A char or None if the address does not exist.
|
|
923
|
+
:rtype: int or None
|
|
924
|
+
"""
|
|
925
|
+
|
|
926
|
+
try:
|
|
927
|
+
return self.project.loader.memory[addr]
|
|
928
|
+
except KeyError:
|
|
929
|
+
return None
|
|
930
|
+
|
|
931
|
+
def _fast_memory_load_bytes(self, addr, length):
|
|
932
|
+
"""
|
|
933
|
+
Perform a fast memory loading of some data.
|
|
934
|
+
|
|
935
|
+
:param int addr: Address to read from.
|
|
936
|
+
:param int length: Size of the string to load.
|
|
937
|
+
:return: A string or None if the address does not exist.
|
|
938
|
+
:rtype: bytes or None
|
|
939
|
+
"""
|
|
940
|
+
|
|
941
|
+
try:
|
|
942
|
+
return self.project.loader.memory.load(addr, length)
|
|
943
|
+
except KeyError:
|
|
944
|
+
return None
|
|
945
|
+
|
|
946
|
+
def _fast_memory_load_pointer(self, addr, size=None) -> int | None:
|
|
947
|
+
"""
|
|
948
|
+
Perform a fast memory loading of a pointer.
|
|
949
|
+
|
|
950
|
+
:param int addr: Address to read from.
|
|
951
|
+
:param int size: Size of the pointer. Default to machine-word size.
|
|
952
|
+
:return: A pointer or None if the address does not exist.
|
|
953
|
+
:rtype: int
|
|
954
|
+
"""
|
|
955
|
+
|
|
956
|
+
try:
|
|
957
|
+
return self.project.loader.memory.unpack_word(addr, size=size)
|
|
958
|
+
except KeyError:
|
|
959
|
+
return None
|
|
960
|
+
|
|
961
|
+
def _load_func_addrs_from_symbols(self):
|
|
962
|
+
"""
|
|
963
|
+
Get all possible function addresses that are specified by the symbols in the binary
|
|
964
|
+
|
|
965
|
+
:return: A set of addresses that are probably functions
|
|
966
|
+
:rtype: set
|
|
967
|
+
"""
|
|
968
|
+
|
|
969
|
+
return {sym.rebased_addr for sym in self._binary.symbols if sym.is_function}
|
|
970
|
+
|
|
971
|
+
def _load_func_addrs_from_eh_frame(self):
|
|
972
|
+
"""
|
|
973
|
+
Get possible function addresses from .eh_frame.
|
|
974
|
+
|
|
975
|
+
:return: A set of addresses that are probably functions.
|
|
976
|
+
:rtype: set
|
|
977
|
+
"""
|
|
978
|
+
|
|
979
|
+
addrs = set()
|
|
980
|
+
if (isinstance(self._binary, ELF) and self._binary.has_dwarf_info) or isinstance(self._binary, PE):
|
|
981
|
+
for function_hint in self._binary.function_hints:
|
|
982
|
+
if function_hint.source == FunctionHintSource.EH_FRAME:
|
|
983
|
+
addrs.add(function_hint.addr)
|
|
984
|
+
return addrs
|
|
985
|
+
|
|
986
|
+
#
|
|
987
|
+
# Analyze function features
|
|
988
|
+
#
|
|
989
|
+
|
|
990
|
+
def _determine_function_returning(self, func, all_funcs_completed=False):
|
|
991
|
+
"""
|
|
992
|
+
Determine if a function returns or not.
|
|
993
|
+
|
|
994
|
+
A function does not return if
|
|
995
|
+
a) it is a SimProcedure that has NO_RET being True,
|
|
996
|
+
or
|
|
997
|
+
b) it is completely recovered (i.e. every block of this function has been recovered, and no future block will
|
|
998
|
+
be added to it), and it does not have a ret or any equivalent instruction.
|
|
999
|
+
|
|
1000
|
+
A function returns if any of its block contains a ret instruction or any equivalence.
|
|
1001
|
+
|
|
1002
|
+
:param Function func: The function to work on.
|
|
1003
|
+
:param bool all_funcs_completed: Whether we treat all functions as completed functions or not.
|
|
1004
|
+
:return: True if the function returns, False if the function does not return, or None if it is
|
|
1005
|
+
not yet determinable with the information available at the moment.
|
|
1006
|
+
:rtype: bool or None
|
|
1007
|
+
"""
|
|
1008
|
+
|
|
1009
|
+
if not self._inside_regions(func.addr):
|
|
1010
|
+
# we don't have a full view of this function. assume it returns
|
|
1011
|
+
return True
|
|
1012
|
+
|
|
1013
|
+
# If there is at least one return site, then this function is definitely returning
|
|
1014
|
+
if func.has_return:
|
|
1015
|
+
return True
|
|
1016
|
+
|
|
1017
|
+
# Let's first see if it's a known SimProcedure that does not return
|
|
1018
|
+
if self.project.is_hooked(func.addr):
|
|
1019
|
+
procedure = self.project.hooked_by(func.addr)
|
|
1020
|
+
else:
|
|
1021
|
+
try:
|
|
1022
|
+
procedure = self.project.simos.syscall_from_addr(func.addr, allow_unsupported=False)
|
|
1023
|
+
except AngrUnsupportedSyscallError:
|
|
1024
|
+
procedure = None
|
|
1025
|
+
|
|
1026
|
+
if procedure is not None and hasattr(procedure, "NO_RET"):
|
|
1027
|
+
return not procedure.NO_RET
|
|
1028
|
+
|
|
1029
|
+
# did we finish analyzing this function?
|
|
1030
|
+
if not all_funcs_completed and func.addr not in self._completed_functions:
|
|
1031
|
+
return None
|
|
1032
|
+
|
|
1033
|
+
if not func.block_addrs_set:
|
|
1034
|
+
# there is no block inside this function
|
|
1035
|
+
# it might happen if the function has been incorrectly identified as part of another function
|
|
1036
|
+
# the error will be corrected during post-processing. In fact at this moment we cannot say anything
|
|
1037
|
+
# about whether this function returns or not. We always assume it returns.
|
|
1038
|
+
return True
|
|
1039
|
+
|
|
1040
|
+
bail_out = False
|
|
1041
|
+
|
|
1042
|
+
# if this function has jump-out sites or ret-out sites, it returns as long as any of the target function
|
|
1043
|
+
# returns
|
|
1044
|
+
for goout_site, type_ in [(site, "jumpout") for site in func.jumpout_sites] + [
|
|
1045
|
+
(site, "retout") for site in func.retout_sites
|
|
1046
|
+
]:
|
|
1047
|
+
# determine where it jumps/returns to
|
|
1048
|
+
goout_site_successors = goout_site.successors()
|
|
1049
|
+
# Filter out UnresolvableJumpTarget because those don't mean that we actually know where it jumps to
|
|
1050
|
+
known_successors = [
|
|
1051
|
+
n
|
|
1052
|
+
for n in goout_site_successors
|
|
1053
|
+
if not (isinstance(n, HookNode) and n.sim_procedure == UnresolvableJumpTarget)
|
|
1054
|
+
]
|
|
1055
|
+
|
|
1056
|
+
if not known_successors:
|
|
1057
|
+
# not sure where it jumps to. bail out
|
|
1058
|
+
bail_out = True
|
|
1059
|
+
continue
|
|
1060
|
+
|
|
1061
|
+
# for ret-out sites, determine what function it calls
|
|
1062
|
+
if type_ == "retout":
|
|
1063
|
+
# see whether the function being called returns or not
|
|
1064
|
+
func_successors = [succ for succ in goout_site_successors if isinstance(succ, Function)]
|
|
1065
|
+
if func_successors and all(
|
|
1066
|
+
func_successor.returning in (None, False) for func_successor in func_successors
|
|
1067
|
+
):
|
|
1068
|
+
# the returning of all possible function calls are undetermined, or they do not return
|
|
1069
|
+
# ignore this site
|
|
1070
|
+
continue
|
|
1071
|
+
|
|
1072
|
+
if type_ == "retout":
|
|
1073
|
+
goout_target = next((succ for succ in goout_site_successors if not isinstance(succ, Function)), None)
|
|
1074
|
+
else:
|
|
1075
|
+
goout_target = next((succ for succ in goout_site_successors), None)
|
|
1076
|
+
if goout_target is None:
|
|
1077
|
+
# there is no jumpout site, which is weird, but what can we do...
|
|
1078
|
+
continue
|
|
1079
|
+
if not self.kb.functions.contains_addr(goout_target.addr):
|
|
1080
|
+
# wait it does not jump to a function?
|
|
1081
|
+
bail_out = True
|
|
1082
|
+
continue
|
|
1083
|
+
|
|
1084
|
+
target_func = self.kb.functions[goout_target.addr]
|
|
1085
|
+
if target_func.returning is True:
|
|
1086
|
+
return True
|
|
1087
|
+
if target_func.returning is None:
|
|
1088
|
+
# the returning status of at least one of the target functions is not decided yet.
|
|
1089
|
+
bail_out = True
|
|
1090
|
+
|
|
1091
|
+
if bail_out:
|
|
1092
|
+
# We cannot determine at this point. bail out
|
|
1093
|
+
return None
|
|
1094
|
+
|
|
1095
|
+
# well this function does not return then
|
|
1096
|
+
return False
|
|
1097
|
+
|
|
1098
|
+
def _analyze_function_features(self, all_funcs_completed=False):
|
|
1099
|
+
"""
|
|
1100
|
+
For each function in the function_manager, try to determine if it returns or not. A function does not return if
|
|
1101
|
+
it calls another function that is known to be not returning, and this function does not have other exits.
|
|
1102
|
+
|
|
1103
|
+
We might as well analyze other features of functions in the future.
|
|
1104
|
+
|
|
1105
|
+
:param bool all_funcs_completed: Ignore _completed_functions set and treat all functions as completed. This
|
|
1106
|
+
can be set to True after the entire CFG is built and _post_analysis() is
|
|
1107
|
+
called (at which point analysis on all functions must be completed).
|
|
1108
|
+
"""
|
|
1109
|
+
|
|
1110
|
+
changes = {"functions_return": [], "functions_do_not_return": []}
|
|
1111
|
+
|
|
1112
|
+
if self._updated_nonreturning_functions is not None:
|
|
1113
|
+
all_func_addrs = self._updated_nonreturning_functions
|
|
1114
|
+
|
|
1115
|
+
# Convert addresses to objects
|
|
1116
|
+
all_functions = [
|
|
1117
|
+
self.kb.functions.get_by_addr(f) for f in all_func_addrs if self.kb.functions.contains_addr(f)
|
|
1118
|
+
]
|
|
1119
|
+
|
|
1120
|
+
else:
|
|
1121
|
+
all_functions = list(self.kb.functions.values())
|
|
1122
|
+
|
|
1123
|
+
analyzed_functions = set()
|
|
1124
|
+
# short-hand
|
|
1125
|
+
functions: FunctionManager = self.kb.functions
|
|
1126
|
+
|
|
1127
|
+
while all_functions:
|
|
1128
|
+
func: Function = all_functions.pop(-1)
|
|
1129
|
+
analyzed_functions.add(func.addr)
|
|
1130
|
+
|
|
1131
|
+
if func.returning is not None:
|
|
1132
|
+
# It has been determined before. Skip it
|
|
1133
|
+
continue
|
|
1134
|
+
|
|
1135
|
+
returning = self._determine_function_returning(func, all_funcs_completed=all_funcs_completed)
|
|
1136
|
+
|
|
1137
|
+
if returning:
|
|
1138
|
+
func.returning = True
|
|
1139
|
+
changes["functions_return"].append(func)
|
|
1140
|
+
elif returning is False:
|
|
1141
|
+
func.returning = False
|
|
1142
|
+
changes["functions_do_not_return"].append(func)
|
|
1143
|
+
|
|
1144
|
+
if returning is not None and func.addr in functions.callgraph:
|
|
1145
|
+
# Add all callers of this function to all_functions list
|
|
1146
|
+
callers = functions.callgraph.predecessors(func.addr)
|
|
1147
|
+
for caller in callers:
|
|
1148
|
+
if caller in analyzed_functions:
|
|
1149
|
+
continue
|
|
1150
|
+
if functions.contains_addr(caller):
|
|
1151
|
+
all_functions.append(functions.get_by_addr(caller))
|
|
1152
|
+
|
|
1153
|
+
return changes
|
|
1154
|
+
|
|
1155
|
+
def _iteratively_analyze_function_features(self, all_funcs_completed=False):
|
|
1156
|
+
"""
|
|
1157
|
+
Iteratively analyze function features until a fixed point is reached.
|
|
1158
|
+
|
|
1159
|
+
:return: the "changes" dict
|
|
1160
|
+
:rtype: dict
|
|
1161
|
+
"""
|
|
1162
|
+
|
|
1163
|
+
changes = {"functions_do_not_return": set(), "functions_return": set()}
|
|
1164
|
+
|
|
1165
|
+
while True:
|
|
1166
|
+
new_changes = self._analyze_function_features(all_funcs_completed=all_funcs_completed)
|
|
1167
|
+
|
|
1168
|
+
changes["functions_do_not_return"] |= set(new_changes["functions_do_not_return"])
|
|
1169
|
+
changes["functions_return"] |= set(new_changes["functions_return"])
|
|
1170
|
+
|
|
1171
|
+
if not new_changes["functions_do_not_return"] and not new_changes["functions_return"]:
|
|
1172
|
+
# a fixed point is reached
|
|
1173
|
+
break
|
|
1174
|
+
|
|
1175
|
+
return changes
|
|
1176
|
+
|
|
1177
|
+
def normalize(self):
|
|
1178
|
+
"""
|
|
1179
|
+
Normalize the CFG, making sure that there are no overlapping basic blocks.
|
|
1180
|
+
|
|
1181
|
+
Note that this method will not alter transition graphs of each function in self.kb.functions. You may call
|
|
1182
|
+
normalize() on each Function object to normalize their transition graphs.
|
|
1183
|
+
|
|
1184
|
+
:return: None
|
|
1185
|
+
"""
|
|
1186
|
+
|
|
1187
|
+
graph = self.graph
|
|
1188
|
+
|
|
1189
|
+
smallest_nodes = {} # indexed by end address of the node
|
|
1190
|
+
end_addr_to_node = {} # a dictionary from node key to node *if* only one node exists for the key
|
|
1191
|
+
end_addr_to_nodes = defaultdict(list) # a dictionary from node key to nodes *if* more than one node exist
|
|
1192
|
+
|
|
1193
|
+
for n in graph.nodes():
|
|
1194
|
+
if n.is_simprocedure:
|
|
1195
|
+
continue
|
|
1196
|
+
end_addr = n.addr + n.size
|
|
1197
|
+
key = (end_addr, n.callstack_key)
|
|
1198
|
+
# add the new item
|
|
1199
|
+
if key not in end_addr_to_node:
|
|
1200
|
+
# this is the first node of this key
|
|
1201
|
+
end_addr_to_node[key] = n
|
|
1202
|
+
else:
|
|
1203
|
+
# this is the 2nd+ node of this key
|
|
1204
|
+
if key not in end_addr_to_nodes:
|
|
1205
|
+
end_addr_to_nodes[key].append(end_addr_to_node[key])
|
|
1206
|
+
end_addr_to_nodes[key].append(n)
|
|
1207
|
+
|
|
1208
|
+
# update smallest_nodes
|
|
1209
|
+
for key, node in end_addr_to_node.items():
|
|
1210
|
+
if key in end_addr_to_nodes:
|
|
1211
|
+
continue
|
|
1212
|
+
smallest_nodes[key] = node
|
|
1213
|
+
del end_addr_to_node # micro memory optimization
|
|
1214
|
+
|
|
1215
|
+
while end_addr_to_nodes:
|
|
1216
|
+
key_to_find = (None, None)
|
|
1217
|
+
for tpl in list(end_addr_to_nodes):
|
|
1218
|
+
x = end_addr_to_nodes[tpl]
|
|
1219
|
+
if len(x) <= 1:
|
|
1220
|
+
continue
|
|
1221
|
+
key_to_find = tpl
|
|
1222
|
+
|
|
1223
|
+
end_addr, callstack_key = key_to_find
|
|
1224
|
+
all_nodes = end_addr_to_nodes[key_to_find]
|
|
1225
|
+
|
|
1226
|
+
all_nodes = sorted(all_nodes, key=lambda node: node.addr, reverse=True)
|
|
1227
|
+
smallest_node = all_nodes[0] # take the one that has the highest address
|
|
1228
|
+
other_nodes = all_nodes[1:]
|
|
1229
|
+
|
|
1230
|
+
self._normalize_core(
|
|
1231
|
+
graph, callstack_key, smallest_node, other_nodes, smallest_nodes, end_addr_to_nodes
|
|
1232
|
+
)
|
|
1233
|
+
|
|
1234
|
+
del end_addr_to_nodes[key_to_find]
|
|
1235
|
+
# make sure the smallest node is stored in end_addresses
|
|
1236
|
+
smallest_nodes[key_to_find] = smallest_node
|
|
1237
|
+
|
|
1238
|
+
# corner case
|
|
1239
|
+
# sometimes two overlapping blocks may not end at the instruction. this might happen when one of the
|
|
1240
|
+
# blocks (the bigger one) hits the instruction count limit or bytes limit before reaching the end
|
|
1241
|
+
# address of the smaller block. in this case we manually pick up those blocks.
|
|
1242
|
+
if not end_addr_to_nodes:
|
|
1243
|
+
# find if there are still overlapping blocks
|
|
1244
|
+
sorted_smallest_nodes = defaultdict(list) # callstack_key is the key of this dict
|
|
1245
|
+
for k, node in smallest_nodes.items():
|
|
1246
|
+
_, callstack_key = k
|
|
1247
|
+
sorted_smallest_nodes[callstack_key].append(node)
|
|
1248
|
+
for k in sorted_smallest_nodes:
|
|
1249
|
+
sorted_smallest_nodes[k] = sorted(sorted_smallest_nodes[k], key=lambda node: node.addr)
|
|
1250
|
+
|
|
1251
|
+
for callstack_key, lst in sorted_smallest_nodes.items():
|
|
1252
|
+
lst_len = len(lst)
|
|
1253
|
+
for i, node in enumerate(lst):
|
|
1254
|
+
if i == lst_len - 1:
|
|
1255
|
+
break
|
|
1256
|
+
next_node = lst[i + 1]
|
|
1257
|
+
if node is not next_node and node.addr <= next_node.addr < node.addr + node.size:
|
|
1258
|
+
# umm, those nodes are overlapping, but they must have different end addresses
|
|
1259
|
+
nodekey_a = node.addr + node.size, callstack_key
|
|
1260
|
+
nodekey_b = next_node.addr + next_node.size, callstack_key
|
|
1261
|
+
if nodekey_a == nodekey_b:
|
|
1262
|
+
# error handling: this will only happen if we have completely overlapping nodes
|
|
1263
|
+
# caused by different jumps (one of the jumps is probably incorrect), which usually
|
|
1264
|
+
# indicates an error in CFG recovery. we print a warning and skip this node
|
|
1265
|
+
l.warning(
|
|
1266
|
+
"Found completely overlapping nodes %s. It usually indicates an error in CFG "
|
|
1267
|
+
"recovery. Skip.",
|
|
1268
|
+
node,
|
|
1269
|
+
)
|
|
1270
|
+
continue
|
|
1271
|
+
|
|
1272
|
+
if nodekey_a in smallest_nodes and nodekey_b in smallest_nodes:
|
|
1273
|
+
# misuse end_addresses_to_nodes
|
|
1274
|
+
key = node.addr + node.size, callstack_key
|
|
1275
|
+
if node not in end_addr_to_nodes[key]:
|
|
1276
|
+
end_addr_to_nodes[key].append(node)
|
|
1277
|
+
if next_node not in end_addr_to_nodes[key]:
|
|
1278
|
+
end_addr_to_nodes[key].append(next_node)
|
|
1279
|
+
|
|
1280
|
+
smallest_nodes.pop(nodekey_a, None)
|
|
1281
|
+
smallest_nodes.pop(nodekey_b, None)
|
|
1282
|
+
|
|
1283
|
+
self.normalized = True
|
|
1284
|
+
|
|
1285
|
+
def _normalize_core(
|
|
1286
|
+
self,
|
|
1287
|
+
graph: networkx.DiGraph[CFGNode],
|
|
1288
|
+
callstack_key,
|
|
1289
|
+
smallest_node,
|
|
1290
|
+
other_nodes,
|
|
1291
|
+
smallest_nodes,
|
|
1292
|
+
end_addr_to_nodes,
|
|
1293
|
+
):
|
|
1294
|
+
# Break other nodes
|
|
1295
|
+
for n in other_nodes:
|
|
1296
|
+
new_size = get_real_address_if_arm(self.project.arch, smallest_node.addr) - get_real_address_if_arm(
|
|
1297
|
+
self.project.arch, n.addr
|
|
1298
|
+
)
|
|
1299
|
+
if new_size == 0:
|
|
1300
|
+
# This node has the same size as the smallest one. Don't touch it.
|
|
1301
|
+
continue
|
|
1302
|
+
|
|
1303
|
+
new_end_addr = n.addr + new_size
|
|
1304
|
+
|
|
1305
|
+
# Does it already exist?
|
|
1306
|
+
new_node = None
|
|
1307
|
+
key = (new_end_addr, n.callstack_key)
|
|
1308
|
+
# the logic below is a little convoluted. we check if key exists in either end_address_to_nodes or
|
|
1309
|
+
# smallest_nodes, since we don't always add the new node back to end_addresses_to_nodes dict - we only do so
|
|
1310
|
+
# when there are more than one node with that key.
|
|
1311
|
+
if key in end_addr_to_nodes:
|
|
1312
|
+
new_node = next((i for i in end_addr_to_nodes[key] if i.addr == n.addr), None)
|
|
1313
|
+
if new_node is None and key in smallest_nodes and smallest_nodes[key].addr == n.addr:
|
|
1314
|
+
new_node = smallest_nodes[key]
|
|
1315
|
+
|
|
1316
|
+
if new_node is None:
|
|
1317
|
+
# Create a new one
|
|
1318
|
+
|
|
1319
|
+
instruction_addrs = []
|
|
1320
|
+
for ins_addr in n.instruction_addrs:
|
|
1321
|
+
if n.addr <= ins_addr < n.addr + new_size:
|
|
1322
|
+
instruction_addrs.append(ins_addr)
|
|
1323
|
+
elif ins_addr == n.addr + new_size:
|
|
1324
|
+
break
|
|
1325
|
+
elif ins_addr > n.addr + new_size:
|
|
1326
|
+
# the immediate next instruction does not start right after the new node. this means we break
|
|
1327
|
+
# an existing instruction in the middle! we need to drop the last instruction address from
|
|
1328
|
+
# instruction_addrs.
|
|
1329
|
+
instruction_addrs.pop()
|
|
1330
|
+
break
|
|
1331
|
+
else:
|
|
1332
|
+
# should not happen if the instruction_addrs list is normal...
|
|
1333
|
+
break
|
|
1334
|
+
|
|
1335
|
+
if self.tag == "CFGFast":
|
|
1336
|
+
new_node = CFGNode(
|
|
1337
|
+
n.addr,
|
|
1338
|
+
new_size,
|
|
1339
|
+
self.model,
|
|
1340
|
+
function_address=n.function_address,
|
|
1341
|
+
block_id=n.block_id,
|
|
1342
|
+
instruction_addrs=instruction_addrs,
|
|
1343
|
+
thumb=n.thumb,
|
|
1344
|
+
)
|
|
1345
|
+
elif self.tag == "CFGEmulated":
|
|
1346
|
+
new_node = CFGENode(
|
|
1347
|
+
n.addr,
|
|
1348
|
+
new_size,
|
|
1349
|
+
self.model,
|
|
1350
|
+
callstack_key=callstack_key,
|
|
1351
|
+
function_address=n.function_address,
|
|
1352
|
+
block_id=n.block_id,
|
|
1353
|
+
instruction_addrs=instruction_addrs,
|
|
1354
|
+
thumb=n.thumb,
|
|
1355
|
+
)
|
|
1356
|
+
else:
|
|
1357
|
+
raise ValueError(f"Unknown tag {self.tag}.")
|
|
1358
|
+
|
|
1359
|
+
# Put the new node into end_addresses list
|
|
1360
|
+
if key in smallest_nodes:
|
|
1361
|
+
if smallest_nodes[key] not in end_addr_to_nodes[key]:
|
|
1362
|
+
end_addr_to_nodes[key].append(smallest_nodes[key])
|
|
1363
|
+
if new_node not in end_addr_to_nodes[key]:
|
|
1364
|
+
end_addr_to_nodes[key].append(new_node)
|
|
1365
|
+
else:
|
|
1366
|
+
smallest_nodes[key] = new_node
|
|
1367
|
+
|
|
1368
|
+
# Modify the CFG
|
|
1369
|
+
original_predecessors = list(graph.in_edges([n], data=True))
|
|
1370
|
+
original_successors = list(graph.out_edges([n], data=True))
|
|
1371
|
+
|
|
1372
|
+
if smallest_node not in graph:
|
|
1373
|
+
continue
|
|
1374
|
+
|
|
1375
|
+
for _s, d, data in original_successors:
|
|
1376
|
+
ins_addr = data.get("ins_addr", None) # ins_addr might be None for FakeRet edges
|
|
1377
|
+
if ins_addr is None and data.get("jumpkind", None) != "Ijk_FakeRet":
|
|
1378
|
+
l.warning(
|
|
1379
|
+
"Unexpected edge with ins_addr being None: %s -> %s, data = %s.",
|
|
1380
|
+
_s,
|
|
1381
|
+
d,
|
|
1382
|
+
str(data),
|
|
1383
|
+
)
|
|
1384
|
+
if ins_addr is not None and ins_addr < smallest_node.addr:
|
|
1385
|
+
continue
|
|
1386
|
+
if d not in graph[smallest_node]:
|
|
1387
|
+
if d is n:
|
|
1388
|
+
graph.add_edge(smallest_node, new_node, **data)
|
|
1389
|
+
else:
|
|
1390
|
+
graph.add_edge(smallest_node, d, **data)
|
|
1391
|
+
|
|
1392
|
+
for p, _, _ in original_predecessors:
|
|
1393
|
+
graph.remove_edge(p, n)
|
|
1394
|
+
if n in graph:
|
|
1395
|
+
graph.remove_node(n)
|
|
1396
|
+
|
|
1397
|
+
# Update nodes dict
|
|
1398
|
+
self._model.remove_node(n.block_id, n)
|
|
1399
|
+
self._model.add_node(n.block_id, new_node)
|
|
1400
|
+
|
|
1401
|
+
for p, _, data in original_predecessors:
|
|
1402
|
+
# Consider the following case: two basic blocks ending at the same position, where A is larger, and
|
|
1403
|
+
# B is smaller. Suppose there is an edge going from the end of A to A itself, and apparently there
|
|
1404
|
+
# is another edge from B to A as well. After splitting A into A' and B, we DO NOT want to add A back
|
|
1405
|
+
# in, otherwise there will be an edge from A to A`, while A should totally be got rid of in the new
|
|
1406
|
+
# graph.
|
|
1407
|
+
if p not in other_nodes:
|
|
1408
|
+
graph.add_edge(p, new_node, **data)
|
|
1409
|
+
|
|
1410
|
+
# We should find the correct successor
|
|
1411
|
+
new_successors = [i for i in [smallest_node, *other_nodes] if i.addr == smallest_node.addr]
|
|
1412
|
+
if new_successors:
|
|
1413
|
+
new_successor = new_successors[0]
|
|
1414
|
+
graph.add_edge(
|
|
1415
|
+
new_node,
|
|
1416
|
+
new_successor,
|
|
1417
|
+
jumpkind="Ijk_Boring",
|
|
1418
|
+
ins_addr=new_node.instruction_addrs[-1] if new_node.instruction_addrs else new_node.addr,
|
|
1419
|
+
)
|
|
1420
|
+
else:
|
|
1421
|
+
# We gotta create a new one
|
|
1422
|
+
l.error("normalize(): Please report it to Fish.")
|
|
1423
|
+
|
|
1424
|
+
# update the jump tables dict and the indirect jumps dict
|
|
1425
|
+
if smallest_node.addr not in self.model.jump_tables:
|
|
1426
|
+
for n in other_nodes:
|
|
1427
|
+
if n.addr in self.model.jump_tables:
|
|
1428
|
+
self.model.jump_tables[n.addr].addr = smallest_node.addr
|
|
1429
|
+
self.model.jump_tables[smallest_node.addr] = self.model.jump_tables[n.addr]
|
|
1430
|
+
break
|
|
1431
|
+
if smallest_node.addr not in self.indirect_jumps:
|
|
1432
|
+
for n in other_nodes:
|
|
1433
|
+
if n.addr in self.indirect_jumps:
|
|
1434
|
+
self.indirect_jumps[n.addr].addr = smallest_node.addr
|
|
1435
|
+
self.indirect_jumps[smallest_node.addr] = self.indirect_jumps[n.addr]
|
|
1436
|
+
break
|
|
1437
|
+
# deal with duplicated entries in self.jump_tables and self.indirect_jumps
|
|
1438
|
+
if smallest_node.addr in self.model.jump_tables:
|
|
1439
|
+
for n in other_nodes:
|
|
1440
|
+
if n.addr in self.model.jump_tables:
|
|
1441
|
+
del self.model.jump_tables[n.addr]
|
|
1442
|
+
if smallest_node.addr in self.indirect_jumps:
|
|
1443
|
+
for n in other_nodes:
|
|
1444
|
+
if n.addr in self.indirect_jumps:
|
|
1445
|
+
del self.indirect_jumps[n.addr]
|
|
1446
|
+
|
|
1447
|
+
#
|
|
1448
|
+
# Job management
|
|
1449
|
+
#
|
|
1450
|
+
|
|
1451
|
+
def _register_analysis_job(self, func_addr, job):
|
|
1452
|
+
"""
|
|
1453
|
+
Register an analysis job of a function to job manager. This allows us to track whether we have finished
|
|
1454
|
+
analyzing/recovering a function or not.
|
|
1455
|
+
|
|
1456
|
+
:param int func_addr: Address of the function that this job belongs to.
|
|
1457
|
+
:param job: The job to register. Note that it does not necessarily be the a CFGJob instance. There
|
|
1458
|
+
can be PendingJob or PendingJob or other instances, too.
|
|
1459
|
+
:return: None
|
|
1460
|
+
"""
|
|
1461
|
+
|
|
1462
|
+
self._jobs_to_analyze_per_function[func_addr].add(job)
|
|
1463
|
+
|
|
1464
|
+
def _deregister_analysis_job(self, func_addr, job):
|
|
1465
|
+
"""
|
|
1466
|
+
Deregister/Remove an analysis job of a function from job manager.
|
|
1467
|
+
|
|
1468
|
+
:param int func_addr: Address of the function that this job belongs to.
|
|
1469
|
+
:param job: The job to deregister.
|
|
1470
|
+
:return: None
|
|
1471
|
+
"""
|
|
1472
|
+
|
|
1473
|
+
self._jobs_to_analyze_per_function[func_addr].discard(job)
|
|
1474
|
+
|
|
1475
|
+
def _get_finished_functions(self):
|
|
1476
|
+
"""
|
|
1477
|
+
Obtain all functions of which we have finished analyzing. As _jobs_to_analyze_per_function is a defaultdict(),
|
|
1478
|
+
if a function address shows up in it with an empty job list, we consider we have exhausted all jobs of this
|
|
1479
|
+
function (both current jobs and pending jobs), thus the analysis of this function is done.
|
|
1480
|
+
|
|
1481
|
+
:return: a list of function addresses of that we have finished analysis.
|
|
1482
|
+
:rtype: list
|
|
1483
|
+
"""
|
|
1484
|
+
|
|
1485
|
+
finished_func_addrs = []
|
|
1486
|
+
for func_addr, all_jobs in self._jobs_to_analyze_per_function.items():
|
|
1487
|
+
if not all_jobs:
|
|
1488
|
+
# great! we have finished analyzing this function!
|
|
1489
|
+
finished_func_addrs.append(func_addr)
|
|
1490
|
+
|
|
1491
|
+
return finished_func_addrs
|
|
1492
|
+
|
|
1493
|
+
def _cleanup_analysis_jobs(self, finished_func_addrs=None):
|
|
1494
|
+
"""
|
|
1495
|
+
From job manager, remove all functions of which we have finished analysis.
|
|
1496
|
+
|
|
1497
|
+
:param list or None finished_func_addrs: A list of addresses of functions of which we have finished analysis.
|
|
1498
|
+
A new list of function addresses will be obtained by calling
|
|
1499
|
+
_get_finished_functions() if this parameter is None.
|
|
1500
|
+
:return: None
|
|
1501
|
+
"""
|
|
1502
|
+
|
|
1503
|
+
if finished_func_addrs is None:
|
|
1504
|
+
finished_func_addrs = self._get_finished_functions()
|
|
1505
|
+
|
|
1506
|
+
for func_addr in finished_func_addrs:
|
|
1507
|
+
if func_addr in self._jobs_to_analyze_per_function:
|
|
1508
|
+
del self._jobs_to_analyze_per_function[func_addr]
|
|
1509
|
+
|
|
1510
|
+
def _make_completed_functions(self):
|
|
1511
|
+
"""
|
|
1512
|
+
Fill in self._completed_functions list and clean up job manager.
|
|
1513
|
+
|
|
1514
|
+
:return: None
|
|
1515
|
+
"""
|
|
1516
|
+
|
|
1517
|
+
finished = self._get_finished_functions()
|
|
1518
|
+
for func_addr in finished:
|
|
1519
|
+
if func_addr not in self._completed_functions:
|
|
1520
|
+
self._function_completed(func_addr)
|
|
1521
|
+
self._completed_functions.add(func_addr)
|
|
1522
|
+
self._cleanup_analysis_jobs(finished_func_addrs=finished)
|
|
1523
|
+
|
|
1524
|
+
#
|
|
1525
|
+
# Function identification and such
|
|
1526
|
+
#
|
|
1527
|
+
|
|
1528
|
+
def mark_function_alignments(self):
|
|
1529
|
+
"""
|
|
1530
|
+
Find all potential function alignments and mark them.
|
|
1531
|
+
|
|
1532
|
+
Note that it is not always correct to simply remove them, because these functions may not be actual alignments
|
|
1533
|
+
but part of an actual function, and is incorrectly marked as an individual function because of failures in
|
|
1534
|
+
resolving indirect jumps. An example is in the test binary ``x86_64/dir_gcc_-O0`` 0x40541d (indirect jump at
|
|
1535
|
+
0x4051b0). If the indirect jump cannot be correctly resolved, removing function 0x40541d will cause a missing
|
|
1536
|
+
label failure in reassembler.
|
|
1537
|
+
|
|
1538
|
+
:return: None
|
|
1539
|
+
"""
|
|
1540
|
+
|
|
1541
|
+
# This function requires Capstone engine support
|
|
1542
|
+
if not self.project.arch.capstone_support:
|
|
1543
|
+
return
|
|
1544
|
+
|
|
1545
|
+
for func_addr in self.kb.functions:
|
|
1546
|
+
function = self.kb.functions[func_addr]
|
|
1547
|
+
if function.is_simprocedure or function.is_syscall:
|
|
1548
|
+
continue
|
|
1549
|
+
if len(function.block_addrs_set) == 1:
|
|
1550
|
+
block = next((b for b in function.blocks), None)
|
|
1551
|
+
if block is None:
|
|
1552
|
+
continue
|
|
1553
|
+
if self._is_noop_block(self.project.arch, block) or all(
|
|
1554
|
+
self._is_noop_insn(insn) for insn in block.capstone.insns
|
|
1555
|
+
):
|
|
1556
|
+
# all nops. mark this function as a function alignment
|
|
1557
|
+
l.debug("Function chunk %#x is probably used as a function alignment (all nops).", func_addr)
|
|
1558
|
+
self.kb.functions[func_addr].is_alignment = True
|
|
1559
|
+
continue
|
|
1560
|
+
node = function.get_node(block.addr)
|
|
1561
|
+
assert node is not None
|
|
1562
|
+
successors = list(function.graph.successors(node))
|
|
1563
|
+
if len(successors) == 1 and successors[0].addr == node.addr:
|
|
1564
|
+
# self loop. mark this function as a function alignment
|
|
1565
|
+
l.debug("Function chunk %#x is probably used as a function alignment (self-loop).", func_addr)
|
|
1566
|
+
self.kb.functions[func_addr].is_alignment = True
|
|
1567
|
+
continue
|
|
1568
|
+
|
|
1569
|
+
def make_functions(self):
|
|
1570
|
+
"""
|
|
1571
|
+
Revisit the entire control flow graph, create Function instances accordingly, and correctly put blocks into
|
|
1572
|
+
each function.
|
|
1573
|
+
|
|
1574
|
+
Although Function objects are created during the CFG recovery, they are neither sound nor accurate. With a
|
|
1575
|
+
pre-constructed CFG, this method rebuilds all functions bearing the following rules:
|
|
1576
|
+
|
|
1577
|
+
- A block may only belong to one function.
|
|
1578
|
+
- Small functions lying inside the startpoint and the endpoint of another function will be merged with the
|
|
1579
|
+
other function
|
|
1580
|
+
- Tail call optimizations are detected.
|
|
1581
|
+
- PLT stubs are aligned by 16.
|
|
1582
|
+
|
|
1583
|
+
:return: None
|
|
1584
|
+
"""
|
|
1585
|
+
|
|
1586
|
+
# TODO: Is it required that PLT stubs are always aligned by 16? If so, on what architectures and platforms is it
|
|
1587
|
+
# TODO: enforced?
|
|
1588
|
+
|
|
1589
|
+
tmp_functions = self.kb.functions
|
|
1590
|
+
|
|
1591
|
+
for function in tmp_functions.values():
|
|
1592
|
+
function.mark_nonreturning_calls_endpoints()
|
|
1593
|
+
if function.returning is False:
|
|
1594
|
+
# remove all FakeRet edges that are related to this function
|
|
1595
|
+
func_node = self.model.get_any_node(function.addr, force_fastpath=True)
|
|
1596
|
+
if func_node is not None:
|
|
1597
|
+
callsite_nodes = [
|
|
1598
|
+
src
|
|
1599
|
+
for src, _, data in self.graph.in_edges(func_node, data=True)
|
|
1600
|
+
if data.get("jumpkind", None) == "Ijk_Call"
|
|
1601
|
+
]
|
|
1602
|
+
for callsite_node in callsite_nodes:
|
|
1603
|
+
for _, dst, data in list(self.graph.out_edges(callsite_node, data=True)):
|
|
1604
|
+
if data.get("jumpkind", None) == "Ijk_FakeRet":
|
|
1605
|
+
self.graph.remove_edge(callsite_node, dst)
|
|
1606
|
+
|
|
1607
|
+
# Clear old functions dict by creating a new function manager
|
|
1608
|
+
self.kb.functions = FunctionManager(self.kb)
|
|
1609
|
+
|
|
1610
|
+
blockaddr_to_function = {}
|
|
1611
|
+
traversed_cfg_nodes = set()
|
|
1612
|
+
|
|
1613
|
+
function_nodes = set()
|
|
1614
|
+
|
|
1615
|
+
# Find nodes for beginnings of all functions
|
|
1616
|
+
for _, dst, data in self.graph.edges(data=True):
|
|
1617
|
+
jumpkind = data.get("jumpkind", "")
|
|
1618
|
+
if jumpkind == "Ijk_Call" or jumpkind.startswith("Ijk_Sys"):
|
|
1619
|
+
function_nodes.add(dst)
|
|
1620
|
+
|
|
1621
|
+
entry_node = self.model.get_any_node(self._binary.entry, force_fastpath=True)
|
|
1622
|
+
if entry_node is not None:
|
|
1623
|
+
function_nodes.add(entry_node)
|
|
1624
|
+
|
|
1625
|
+
# aggressively remove and merge functions
|
|
1626
|
+
# For any function, if there is a call to it, it won't be removed
|
|
1627
|
+
called_function_addrs = {n.addr for n in function_nodes}
|
|
1628
|
+
# Any function addresses that appear as symbols won't be removed
|
|
1629
|
+
predetermined_function_addrs = called_function_addrs | self._function_addresses_from_symbols
|
|
1630
|
+
|
|
1631
|
+
removed_functions_a = self._process_irrational_functions(
|
|
1632
|
+
tmp_functions, predetermined_function_addrs, blockaddr_to_function
|
|
1633
|
+
)
|
|
1634
|
+
removed_functions_b, adjusted_cfgnodes = self._process_irrational_function_starts(
|
|
1635
|
+
tmp_functions, predetermined_function_addrs, blockaddr_to_function
|
|
1636
|
+
)
|
|
1637
|
+
self._process_jump_table_targeted_functions(
|
|
1638
|
+
tmp_functions,
|
|
1639
|
+
predetermined_function_addrs,
|
|
1640
|
+
blockaddr_to_function,
|
|
1641
|
+
)
|
|
1642
|
+
removed_functions = removed_functions_a | removed_functions_b
|
|
1643
|
+
|
|
1644
|
+
# Remove all nodes that are adjusted
|
|
1645
|
+
function_nodes.difference_update(adjusted_cfgnodes)
|
|
1646
|
+
for n in self.graph.nodes():
|
|
1647
|
+
if n.addr in tmp_functions or n.addr in removed_functions:
|
|
1648
|
+
function_nodes.add(n)
|
|
1649
|
+
|
|
1650
|
+
# traverse the graph starting from each node, not following call edges
|
|
1651
|
+
# it's important that we traverse all functions in order so that we have a greater chance to come across
|
|
1652
|
+
# rational functions before its irrational counterparts (e.g. due to failed jump table resolution)
|
|
1653
|
+
|
|
1654
|
+
min_stage_2_progress = 50.0
|
|
1655
|
+
max_stage_2_progress = 90.0
|
|
1656
|
+
nodes_count = len(function_nodes)
|
|
1657
|
+
for i, fn in enumerate(sorted(function_nodes, key=lambda n: n.addr)):
|
|
1658
|
+
if self._low_priority:
|
|
1659
|
+
self._release_gil(i, 800, 0.000001)
|
|
1660
|
+
|
|
1661
|
+
if self._show_progressbar or self._progress_callback:
|
|
1662
|
+
progress = min_stage_2_progress + (max_stage_2_progress - min_stage_2_progress) * (
|
|
1663
|
+
i * 1.0 / nodes_count
|
|
1664
|
+
)
|
|
1665
|
+
self._update_progress(progress)
|
|
1666
|
+
|
|
1667
|
+
self._graph_bfs_custom(
|
|
1668
|
+
self.graph,
|
|
1669
|
+
[fn],
|
|
1670
|
+
self._graph_traversal_handler,
|
|
1671
|
+
blockaddr_to_function,
|
|
1672
|
+
tmp_functions,
|
|
1673
|
+
traversed_cfg_nodes,
|
|
1674
|
+
)
|
|
1675
|
+
|
|
1676
|
+
# Don't forget those small function chunks that are not called by anything.
|
|
1677
|
+
# There might be references to them from data, or simply references that we cannot find via static analysis
|
|
1678
|
+
|
|
1679
|
+
secondary_function_nodes = set()
|
|
1680
|
+
# add all function chunks ("functions" that are not called from anywhere)
|
|
1681
|
+
for func_addr in tmp_functions:
|
|
1682
|
+
node = self.model.get_any_node(func_addr, force_fastpath=True)
|
|
1683
|
+
if node is None:
|
|
1684
|
+
continue
|
|
1685
|
+
if node.addr not in blockaddr_to_function:
|
|
1686
|
+
secondary_function_nodes.add(node)
|
|
1687
|
+
|
|
1688
|
+
missing_cfg_nodes = set(self.graph.nodes()) - traversed_cfg_nodes
|
|
1689
|
+
missing_cfg_nodes = {node for node in missing_cfg_nodes if node.function_address is not None}
|
|
1690
|
+
if missing_cfg_nodes:
|
|
1691
|
+
l.debug("%d CFGNodes are missing in the first traversal.", len(missing_cfg_nodes))
|
|
1692
|
+
secondary_function_nodes |= missing_cfg_nodes
|
|
1693
|
+
|
|
1694
|
+
min_stage_3_progress = 90.0
|
|
1695
|
+
max_stage_3_progress = 99.9
|
|
1696
|
+
|
|
1697
|
+
nodes_count = len(secondary_function_nodes)
|
|
1698
|
+
for i, fn in enumerate(sorted(secondary_function_nodes, key=lambda n: n.addr)):
|
|
1699
|
+
if self._show_progressbar or self._progress_callback:
|
|
1700
|
+
progress = min_stage_3_progress + (max_stage_3_progress - min_stage_3_progress) * (
|
|
1701
|
+
i * 1.0 / nodes_count
|
|
1702
|
+
)
|
|
1703
|
+
self._update_progress(progress)
|
|
1704
|
+
|
|
1705
|
+
self._graph_bfs_custom(
|
|
1706
|
+
self.graph,
|
|
1707
|
+
[fn],
|
|
1708
|
+
self._graph_traversal_handler,
|
|
1709
|
+
blockaddr_to_function,
|
|
1710
|
+
tmp_functions,
|
|
1711
|
+
traversed_cfg_nodes,
|
|
1712
|
+
)
|
|
1713
|
+
|
|
1714
|
+
to_remove = set()
|
|
1715
|
+
|
|
1716
|
+
# Remove all stubs after PLT entries
|
|
1717
|
+
if not is_arm_arch(self.project.arch):
|
|
1718
|
+
to_remove |= self._remove_dummy_plt_stubs(self.kb.functions)
|
|
1719
|
+
|
|
1720
|
+
# remove empty functions
|
|
1721
|
+
for func in self.kb.functions.values():
|
|
1722
|
+
if func.startpoint is None:
|
|
1723
|
+
to_remove.add(func.addr)
|
|
1724
|
+
|
|
1725
|
+
for addr in to_remove:
|
|
1726
|
+
del self.kb.functions[addr]
|
|
1727
|
+
|
|
1728
|
+
# Update CFGNode.function_address
|
|
1729
|
+
for node in self._nodes.values():
|
|
1730
|
+
if node.addr in blockaddr_to_function:
|
|
1731
|
+
node.function_address = blockaddr_to_function[node.addr].addr
|
|
1732
|
+
|
|
1733
|
+
# Update function.info
|
|
1734
|
+
for func in self.kb.functions.values():
|
|
1735
|
+
if func.addr in tmp_functions:
|
|
1736
|
+
func.info = tmp_functions[func.addr].info
|
|
1737
|
+
|
|
1738
|
+
def _remove_dummy_plt_stubs(self, functions):
|
|
1739
|
+
def _is_function_a_plt_stub(arch_, func):
|
|
1740
|
+
if len(func.block_addrs_set) != 1:
|
|
1741
|
+
# multiple blocks? no idea what this is...
|
|
1742
|
+
return False
|
|
1743
|
+
block = next(func.blocks)
|
|
1744
|
+
if self._is_noop_block(arch_, block):
|
|
1745
|
+
# alignments
|
|
1746
|
+
return False
|
|
1747
|
+
|
|
1748
|
+
# TODO: We may want to add support for filtering dummy PLT stubs for other architectures, but I haven't
|
|
1749
|
+
# TODO: seen any need for those.
|
|
1750
|
+
return not (
|
|
1751
|
+
arch_.name in {"X86", "AMD64"}
|
|
1752
|
+
and len(block.vex.instruction_addresses) == 2
|
|
1753
|
+
and block.vex.jumpkind == "Ijk_Boring"
|
|
1754
|
+
)
|
|
1755
|
+
|
|
1756
|
+
to_remove = set()
|
|
1757
|
+
|
|
1758
|
+
met_plts = False
|
|
1759
|
+
non_plt_funcs = 0
|
|
1760
|
+
sorted_func_addrs = sorted(functions.keys())
|
|
1761
|
+
arch = self.project.arch
|
|
1762
|
+
|
|
1763
|
+
# we assume all PLT entries are all located at the same region. the moment we come across the end of it, we
|
|
1764
|
+
# stop looping.
|
|
1765
|
+
for fn_addr in sorted_func_addrs:
|
|
1766
|
+
fn = functions.get_by_addr(fn_addr)
|
|
1767
|
+
addr = fn.addr - (fn.addr % 16)
|
|
1768
|
+
if (
|
|
1769
|
+
addr != fn.addr
|
|
1770
|
+
and addr in functions
|
|
1771
|
+
and functions[addr].is_plt
|
|
1772
|
+
and not _is_function_a_plt_stub(arch, fn)
|
|
1773
|
+
):
|
|
1774
|
+
to_remove.add(fn.addr)
|
|
1775
|
+
continue
|
|
1776
|
+
|
|
1777
|
+
if fn.is_plt:
|
|
1778
|
+
met_plts = True
|
|
1779
|
+
non_plt_funcs = 0
|
|
1780
|
+
if met_plts and not fn.is_plt:
|
|
1781
|
+
non_plt_funcs += 1
|
|
1782
|
+
if non_plt_funcs >= 2:
|
|
1783
|
+
break
|
|
1784
|
+
|
|
1785
|
+
return to_remove
|
|
1786
|
+
|
|
1787
|
+
def _process_irrational_functions(self, functions, predetermined_function_addrs, blockaddr_to_function):
|
|
1788
|
+
"""
|
|
1789
|
+
When force_complete_scan is enabled, for unresolveable indirect jumps, angr will find jump targets and mark
|
|
1790
|
+
them as individual functions. For example, usually the following pattern is seen:
|
|
1791
|
+
|
|
1792
|
+
sub_0x400010:
|
|
1793
|
+
push ebp
|
|
1794
|
+
mov esp, ebp
|
|
1795
|
+
...
|
|
1796
|
+
cmp eax, 10
|
|
1797
|
+
ja end
|
|
1798
|
+
mov eax, jumptable[eax]
|
|
1799
|
+
jmp eax
|
|
1800
|
+
|
|
1801
|
+
sub_0x400080:
|
|
1802
|
+
# do something here
|
|
1803
|
+
jmp end
|
|
1804
|
+
|
|
1805
|
+
end (0x400e00):
|
|
1806
|
+
pop ebp
|
|
1807
|
+
ret
|
|
1808
|
+
|
|
1809
|
+
In the example above, `process_irrational_functions` will remove function 0x400080, and merge it with function
|
|
1810
|
+
0x400010.
|
|
1811
|
+
|
|
1812
|
+
:param angr.knowledge_plugins.FunctionManager functions: all functions that angr recovers, including those ones
|
|
1813
|
+
that are misidentified as functions.
|
|
1814
|
+
:param dict blockaddr_to_function: A mapping between block addresses and Function instances.
|
|
1815
|
+
:return: A set of addresses of all removed functions
|
|
1816
|
+
:rtype: set
|
|
1817
|
+
"""
|
|
1818
|
+
|
|
1819
|
+
functions_to_remove = {}
|
|
1820
|
+
|
|
1821
|
+
all_func_addrs = sorted(set(functions.keys()))
|
|
1822
|
+
|
|
1823
|
+
for func_pos, (func_addr, function) in enumerate(functions.items()):
|
|
1824
|
+
if func_addr in functions_to_remove:
|
|
1825
|
+
continue
|
|
1826
|
+
|
|
1827
|
+
# check all blocks and see if any block ends with an indirect jump and is not resolved
|
|
1828
|
+
has_unresolved_jumps = False
|
|
1829
|
+
# the functions to merge with must be locating between the unresolved basic block address and the endpoint
|
|
1830
|
+
# of the current function
|
|
1831
|
+
max_unresolved_jump_addr = 0
|
|
1832
|
+
for block_addr in function.block_addrs_set:
|
|
1833
|
+
if (
|
|
1834
|
+
block_addr in self.indirect_jumps
|
|
1835
|
+
and self.indirect_jumps[block_addr].jumpkind == "Ijk_Boring"
|
|
1836
|
+
and not self.indirect_jumps[block_addr].resolved_targets
|
|
1837
|
+
):
|
|
1838
|
+
# it's not resolved
|
|
1839
|
+
# we should also make sure it's a jump, not a call
|
|
1840
|
+
has_unresolved_jumps = True
|
|
1841
|
+
max_unresolved_jump_addr = max(max_unresolved_jump_addr, block_addr)
|
|
1842
|
+
|
|
1843
|
+
if not has_unresolved_jumps:
|
|
1844
|
+
continue
|
|
1845
|
+
|
|
1846
|
+
if function.startpoint is None:
|
|
1847
|
+
continue
|
|
1848
|
+
|
|
1849
|
+
startpoint_addr = function.startpoint.addr
|
|
1850
|
+
if not function.endpoints:
|
|
1851
|
+
# Function should have at least one endpoint
|
|
1852
|
+
continue
|
|
1853
|
+
endpoint_addr = max(a.addr for a in function.endpoints)
|
|
1854
|
+
the_endpoint = next(a for a in function.endpoints if a.addr == endpoint_addr)
|
|
1855
|
+
endpoint_addr += the_endpoint.size
|
|
1856
|
+
|
|
1857
|
+
# sanity check: startpoint of the function should be greater than its endpoint
|
|
1858
|
+
if startpoint_addr >= endpoint_addr:
|
|
1859
|
+
continue
|
|
1860
|
+
if max_unresolved_jump_addr <= startpoint_addr or max_unresolved_jump_addr >= endpoint_addr:
|
|
1861
|
+
continue
|
|
1862
|
+
|
|
1863
|
+
# scan forward from the endpoint to include any function-tail jumps
|
|
1864
|
+
# Here is an example:
|
|
1865
|
+
# loc_8049562:
|
|
1866
|
+
# mov eax, ebp
|
|
1867
|
+
# add esp, 3ch
|
|
1868
|
+
# ...
|
|
1869
|
+
# ret
|
|
1870
|
+
# loc_804956c:
|
|
1871
|
+
# mov ebp, 3
|
|
1872
|
+
# jmp loc_8049562
|
|
1873
|
+
# loc_8049573:
|
|
1874
|
+
# mov ebp, 4
|
|
1875
|
+
# jmp loc_8049562
|
|
1876
|
+
#
|
|
1877
|
+
last_addr = endpoint_addr
|
|
1878
|
+
while True:
|
|
1879
|
+
try:
|
|
1880
|
+
# do not follow hooked addresses (such as SimProcedures)
|
|
1881
|
+
if self.project.is_hooked(last_addr):
|
|
1882
|
+
break
|
|
1883
|
+
|
|
1884
|
+
next_block = self._lift(last_addr)
|
|
1885
|
+
next_block_irsb = next_block.vex_nostmt
|
|
1886
|
+
if next_block_irsb.jumpkind not in ("Ijk_Boring", "Ijk_InvalICache"):
|
|
1887
|
+
break
|
|
1888
|
+
if not isinstance(next_block_irsb.next, pyvex.IRExpr.Const):
|
|
1889
|
+
break
|
|
1890
|
+
suc_addr = next_block_irsb.next.con.value
|
|
1891
|
+
if (
|
|
1892
|
+
max(startpoint_addr, the_endpoint.addr - 0x40)
|
|
1893
|
+
<= suc_addr
|
|
1894
|
+
< the_endpoint.addr + the_endpoint.size
|
|
1895
|
+
):
|
|
1896
|
+
# increment the endpoint_addr
|
|
1897
|
+
endpoint_addr = next_block.addr + next_block.size
|
|
1898
|
+
else:
|
|
1899
|
+
break
|
|
1900
|
+
|
|
1901
|
+
last_addr = next_block.addr + next_block.size
|
|
1902
|
+
|
|
1903
|
+
except (SimTranslationError, SimMemoryError, SimIRSBError, SimEngineError):
|
|
1904
|
+
break
|
|
1905
|
+
|
|
1906
|
+
# find all functions that are between [ startpoint, endpoint ]
|
|
1907
|
+
|
|
1908
|
+
should_merge = True
|
|
1909
|
+
functions_to_merge = set()
|
|
1910
|
+
i = func_pos + 1
|
|
1911
|
+
while i < len(all_func_addrs) and all_func_addrs[i] < endpoint_addr:
|
|
1912
|
+
f_addr = all_func_addrs[i]
|
|
1913
|
+
i += 1
|
|
1914
|
+
f = functions[f_addr]
|
|
1915
|
+
if f_addr == func_addr:
|
|
1916
|
+
continue
|
|
1917
|
+
if max_unresolved_jump_addr < f_addr < endpoint_addr and all(
|
|
1918
|
+
max_unresolved_jump_addr < b_addr < endpoint_addr for b_addr in f.block_addrs
|
|
1919
|
+
):
|
|
1920
|
+
if f_addr in functions_to_remove:
|
|
1921
|
+
# this function has already been merged with other functions before... it cannot be merged with
|
|
1922
|
+
# this function anymore
|
|
1923
|
+
should_merge = False
|
|
1924
|
+
break
|
|
1925
|
+
if f_addr in predetermined_function_addrs:
|
|
1926
|
+
# this function is a legit one. it shouldn't be removed/merged
|
|
1927
|
+
should_merge = False
|
|
1928
|
+
break
|
|
1929
|
+
functions_to_merge.add(f_addr)
|
|
1930
|
+
|
|
1931
|
+
if not should_merge:
|
|
1932
|
+
# we shouldn't merge...
|
|
1933
|
+
continue
|
|
1934
|
+
|
|
1935
|
+
for f_addr in functions_to_merge:
|
|
1936
|
+
functions_to_remove[f_addr] = func_addr
|
|
1937
|
+
|
|
1938
|
+
# merge all functions
|
|
1939
|
+
for to_remove, merge_with in functions_to_remove.items():
|
|
1940
|
+
func_merge_with = self._addr_to_function(merge_with, blockaddr_to_function, functions)
|
|
1941
|
+
|
|
1942
|
+
for block_addr in functions[to_remove].block_addrs:
|
|
1943
|
+
blockaddr_to_function[block_addr] = func_merge_with
|
|
1944
|
+
|
|
1945
|
+
del functions[to_remove]
|
|
1946
|
+
|
|
1947
|
+
return set(functions_to_remove.keys())
|
|
1948
|
+
|
|
1949
|
+
def _process_irrational_function_starts(self, functions, predetermined_function_addrs, blockaddr_to_function):
|
|
1950
|
+
"""
|
|
1951
|
+
Functions that are identified via function prologues can be starting after the actual beginning of the function.
|
|
1952
|
+
For example, the following function (with an incorrect start) might exist after a CFG recovery:
|
|
1953
|
+
|
|
1954
|
+
sub_8049f70:
|
|
1955
|
+
push esi
|
|
1956
|
+
|
|
1957
|
+
sub_8049f71:
|
|
1958
|
+
sub esp, 0A8h
|
|
1959
|
+
mov esi, [esp+0ACh+arg_0]
|
|
1960
|
+
mov [esp+0ACh+var_88], 0
|
|
1961
|
+
|
|
1962
|
+
If the following conditions are met, we will remove the second function and merge it into the first function:
|
|
1963
|
+
- The second function is not called by other code.
|
|
1964
|
+
- The first function has only one non-empty jumpout site (e.g., the first function contains more than just nops
|
|
1965
|
+
and jumps), which points to the second function.
|
|
1966
|
+
|
|
1967
|
+
:param FunctionManager functions: All functions that angr recovers.
|
|
1968
|
+
:return: A set of addresses of all removed functions.
|
|
1969
|
+
:rtype: set
|
|
1970
|
+
"""
|
|
1971
|
+
|
|
1972
|
+
addrs = sorted(
|
|
1973
|
+
k for k in functions if not self.project.is_hooked(k) and not self.project.simos.is_syscall_addr(k)
|
|
1974
|
+
)
|
|
1975
|
+
functions_to_remove = set()
|
|
1976
|
+
adjusted_cfgnodes = set()
|
|
1977
|
+
|
|
1978
|
+
for addr_0, addr_1 in zip(addrs[:-1], addrs[1:]):
|
|
1979
|
+
if addr_1 in predetermined_function_addrs:
|
|
1980
|
+
continue
|
|
1981
|
+
if addr_0 in functions_to_remove:
|
|
1982
|
+
continue
|
|
1983
|
+
|
|
1984
|
+
func_0 = functions[addr_0]
|
|
1985
|
+
|
|
1986
|
+
if len(func_0.block_addrs_set) >= 1:
|
|
1987
|
+
if len(func_0.jumpout_sites) != 1:
|
|
1988
|
+
continue
|
|
1989
|
+
block_node = func_0.jumpout_sites[0]
|
|
1990
|
+
if block_node is None:
|
|
1991
|
+
continue
|
|
1992
|
+
if block_node.size == 0:
|
|
1993
|
+
# skip empty blocks (that are usually caused by lifting failures)
|
|
1994
|
+
continue
|
|
1995
|
+
block = func_0.get_block(block_node.addr, block_node.size)
|
|
1996
|
+
# Skip alignment blocks
|
|
1997
|
+
if self._is_noop_block(self.project.arch, block):
|
|
1998
|
+
continue
|
|
1999
|
+
if block.vex_nostmt.jumpkind not in ("Ijk_Boring", "Ijk_InvalICache"):
|
|
2000
|
+
continue
|
|
2001
|
+
if all(isinstance(stmt, pyvex.stmt.IMark) for stmt in block.vex.statements):
|
|
2002
|
+
# the first block is empty
|
|
2003
|
+
continue
|
|
2004
|
+
|
|
2005
|
+
# does the first block transition to the next function?
|
|
2006
|
+
transition_found = False
|
|
2007
|
+
out_edges = list(func_0.transition_graph.out_edges(block_node, data=True))
|
|
2008
|
+
for _, dst_node, data in out_edges:
|
|
2009
|
+
if (
|
|
2010
|
+
dst_node.addr == addr_1
|
|
2011
|
+
and data.get("type", None) == "transition"
|
|
2012
|
+
and data.get("outside", False) is True
|
|
2013
|
+
):
|
|
2014
|
+
transition_found = True
|
|
2015
|
+
break
|
|
2016
|
+
|
|
2017
|
+
if not transition_found:
|
|
2018
|
+
continue
|
|
2019
|
+
|
|
2020
|
+
cfgnode_0 = self.model.get_any_node(block_node.addr, force_fastpath=True)
|
|
2021
|
+
cfgnode_1 = self.model.get_any_node(addr_1, force_fastpath=True)
|
|
2022
|
+
|
|
2023
|
+
if cfgnode_0 is None or cfgnode_1 is None:
|
|
2024
|
+
continue
|
|
2025
|
+
|
|
2026
|
+
# who's jumping to or calling cfgnode_1?
|
|
2027
|
+
cfgnode_1_preds = self.model.get_predecessors_and_jumpkinds(cfgnode_1, excluding_fakeret=True)
|
|
2028
|
+
func_1 = functions[addr_1]
|
|
2029
|
+
abort = False
|
|
2030
|
+
for pred, jumpkind in cfgnode_1_preds:
|
|
2031
|
+
if pred.addr in func_0.block_addrs_set and jumpkind == "Ijk_Boring":
|
|
2032
|
+
# this is the transition from function 0
|
|
2033
|
+
continue
|
|
2034
|
+
if pred.addr in func_1.block_addrs_set:
|
|
2035
|
+
# this is a transition from function 1 itself
|
|
2036
|
+
continue
|
|
2037
|
+
# found an unexpected edge. give up
|
|
2038
|
+
abort = True
|
|
2039
|
+
break
|
|
2040
|
+
if abort:
|
|
2041
|
+
continue
|
|
2042
|
+
|
|
2043
|
+
# Merge functions addr_0 and addr_1
|
|
2044
|
+
l.debug("Merging function %#x into %#x.", addr_1, addr_0)
|
|
2045
|
+
|
|
2046
|
+
cfgnode_1_merged = False
|
|
2047
|
+
# we only merge two CFG nodes if the first one does not end with a branch instruction and there are no
|
|
2048
|
+
# other predecessors to CFG node addr_1
|
|
2049
|
+
if len(func_0.block_addrs_set) == 1 and len(out_edges) == 1 and len(cfgnode_1_preds) <= 1:
|
|
2050
|
+
outedge_src, outedge_dst, outedge_data = out_edges[0]
|
|
2051
|
+
if (
|
|
2052
|
+
outedge_src.addr == cfgnode_0.addr
|
|
2053
|
+
and outedge_src.size == cfgnode_0.size
|
|
2054
|
+
and outedge_dst.addr == cfgnode_1.addr
|
|
2055
|
+
and cfgnode_0.addr + cfgnode_0.size == cfgnode_1.addr
|
|
2056
|
+
and outedge_data.get("type", None) == "transition"
|
|
2057
|
+
and outedge_data.get("stmt_idx", None) == DEFAULT_STATEMENT
|
|
2058
|
+
):
|
|
2059
|
+
cfgnode_1_merged = True
|
|
2060
|
+
self._merge_cfgnodes(cfgnode_0, cfgnode_1)
|
|
2061
|
+
adjusted_cfgnodes.add(cfgnode_0)
|
|
2062
|
+
adjusted_cfgnodes.add(cfgnode_1)
|
|
2063
|
+
|
|
2064
|
+
# Merge the two functions
|
|
2065
|
+
func_1 = functions[addr_1]
|
|
2066
|
+
for block_addr in func_1.block_addrs:
|
|
2067
|
+
if block_addr == addr_1 and cfgnode_1_merged:
|
|
2068
|
+
# Skip addr_1 (since it has been merged to the preceding block)
|
|
2069
|
+
continue
|
|
2070
|
+
merge_with = self._addr_to_function(addr_0, blockaddr_to_function, functions)
|
|
2071
|
+
blockaddr_to_function[block_addr] = merge_with
|
|
2072
|
+
|
|
2073
|
+
functions_to_remove.add(addr_1)
|
|
2074
|
+
|
|
2075
|
+
for to_remove in functions_to_remove:
|
|
2076
|
+
del functions[to_remove]
|
|
2077
|
+
|
|
2078
|
+
return functions_to_remove, adjusted_cfgnodes
|
|
2079
|
+
|
|
2080
|
+
def _process_jump_table_targeted_functions(
|
|
2081
|
+
self, functions, predetermined_function_addrs, blockaddr_to_function
|
|
2082
|
+
) -> set[int]:
|
|
2083
|
+
"""
|
|
2084
|
+
Sometimes compilers will optimize "cold" code regions, make them separate functions, mark them as cold, which
|
|
2085
|
+
conflicts with how angr handles jumps to these functions (because they weren't functions to start with). Here
|
|
2086
|
+
is an example (in function version_etc_arn() from gllib)::
|
|
2087
|
+
|
|
2088
|
+
switch (n_authors) {
|
|
2089
|
+
case 0:
|
|
2090
|
+
abort();
|
|
2091
|
+
case 1:
|
|
2092
|
+
...
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
GCC may decide to move the `abort();` block under case 0 into a separate function (usually named
|
|
2096
|
+
"version_etc_arn_cold") and mark it as "cold." When loading function hints from eh frame is enabled, this
|
|
2097
|
+
function will be identified, and the recovered switch-case structure will have a jump to a function. It's
|
|
2098
|
+
usually not a problem until we need to decompile this function, where (at least for now) angr decompiler
|
|
2099
|
+
requires all switch-case entry blocks must belong to the same function.
|
|
2100
|
+
|
|
2101
|
+
The temporary solution is identifying functions that (a) have no call predecessors, and (b) are used as
|
|
2102
|
+
jump targets for identified jump tables. Remove these functions so that they can be treated as part of the
|
|
2103
|
+
source function where the corresponding jump table belongs.
|
|
2104
|
+
"""
|
|
2105
|
+
|
|
2106
|
+
jumptable_entries: set[int] = set()
|
|
2107
|
+
for jt in self.model.jump_tables.values():
|
|
2108
|
+
assert jt.jumptable_entries is not None
|
|
2109
|
+
jumptable_entries |= set(jt.jumptable_entries)
|
|
2110
|
+
|
|
2111
|
+
if not jumptable_entries:
|
|
2112
|
+
return set()
|
|
2113
|
+
|
|
2114
|
+
functions_to_remove = set()
|
|
2115
|
+
|
|
2116
|
+
for func_addr in functions:
|
|
2117
|
+
if func_addr in predetermined_function_addrs:
|
|
2118
|
+
continue
|
|
2119
|
+
if func_addr in jumptable_entries:
|
|
2120
|
+
# is there any call edge pointing to it?
|
|
2121
|
+
func_node = self.model.get_any_node(func_addr, force_fastpath=True)
|
|
2122
|
+
if func_node is not None:
|
|
2123
|
+
in_edges = self.graph.in_edges(func_node, data=True)
|
|
2124
|
+
has_transition_pred = None
|
|
2125
|
+
has_non_transition_pred = None
|
|
2126
|
+
for _, _, data in in_edges:
|
|
2127
|
+
if data.get("jumpkind", None) == "Ijk_Boring":
|
|
2128
|
+
has_transition_pred = True
|
|
2129
|
+
else:
|
|
2130
|
+
has_non_transition_pred = True
|
|
2131
|
+
if has_transition_pred is True and not has_non_transition_pred:
|
|
2132
|
+
# all predecessors are transition-only
|
|
2133
|
+
# remove this function
|
|
2134
|
+
functions_to_remove.add(func_addr)
|
|
2135
|
+
|
|
2136
|
+
for to_remove in functions_to_remove:
|
|
2137
|
+
del functions[to_remove]
|
|
2138
|
+
if to_remove in blockaddr_to_function:
|
|
2139
|
+
del blockaddr_to_function[to_remove]
|
|
2140
|
+
|
|
2141
|
+
return functions_to_remove
|
|
2142
|
+
|
|
2143
|
+
def _addr_to_function(self, addr, blockaddr_to_function, known_functions):
|
|
2144
|
+
"""
|
|
2145
|
+
Convert an address to a Function object, and store the mapping in a dict. If the block is known to be part of a
|
|
2146
|
+
function, just return that function.
|
|
2147
|
+
|
|
2148
|
+
:param int addr: Address to convert
|
|
2149
|
+
:param dict blockaddr_to_function: A mapping between block addresses to Function instances.
|
|
2150
|
+
:param angr.knowledge_plugins.FunctionManager known_functions: Recovered functions.
|
|
2151
|
+
:return: a Function object
|
|
2152
|
+
:rtype: angr.knowledge.Function
|
|
2153
|
+
"""
|
|
2154
|
+
|
|
2155
|
+
if addr in blockaddr_to_function:
|
|
2156
|
+
f = blockaddr_to_function[addr]
|
|
2157
|
+
else:
|
|
2158
|
+
is_syscall = self.project.simos.is_syscall_addr(addr)
|
|
2159
|
+
|
|
2160
|
+
n = self.model.get_any_node(addr, is_syscall=is_syscall, force_fastpath=True)
|
|
2161
|
+
node = addr if n is None else self._to_snippet(n)
|
|
2162
|
+
|
|
2163
|
+
if isinstance(addr, SootAddressDescriptor):
|
|
2164
|
+
addr = addr.method
|
|
2165
|
+
|
|
2166
|
+
self.kb.functions._add_node(addr, node, syscall=is_syscall)
|
|
2167
|
+
f = self.kb.functions.function(addr=addr)
|
|
2168
|
+
assert f is not None
|
|
2169
|
+
|
|
2170
|
+
# copy over existing metadata
|
|
2171
|
+
if known_functions.contains_addr(addr):
|
|
2172
|
+
kf = known_functions.get_by_addr(addr)
|
|
2173
|
+
f.is_plt = kf.is_plt
|
|
2174
|
+
|
|
2175
|
+
blockaddr_to_function[addr] = f
|
|
2176
|
+
|
|
2177
|
+
function_is_returning = False
|
|
2178
|
+
if addr in known_functions and known_functions.function(addr).returning:
|
|
2179
|
+
f.returning = True
|
|
2180
|
+
function_is_returning = True
|
|
2181
|
+
|
|
2182
|
+
if not function_is_returning and self._updated_nonreturning_functions is not None:
|
|
2183
|
+
# We will rerun function feature analysis on this function later. Add it to
|
|
2184
|
+
# self._updated_nonreturning_functions so it can be picked up by function feature analysis later.
|
|
2185
|
+
self._updated_nonreturning_functions.add(addr)
|
|
2186
|
+
|
|
2187
|
+
return f
|
|
2188
|
+
|
|
2189
|
+
def _is_tail_call_optimization(
|
|
2190
|
+
self,
|
|
2191
|
+
g: networkx.DiGraph[CFGNode],
|
|
2192
|
+
src_addr,
|
|
2193
|
+
dst_addr,
|
|
2194
|
+
src_function,
|
|
2195
|
+
all_edges: list[tuple[CFGNode, CFGNode, Any]],
|
|
2196
|
+
known_functions,
|
|
2197
|
+
blockaddr_to_function,
|
|
2198
|
+
):
|
|
2199
|
+
"""
|
|
2200
|
+
If source and destination belong to the same function, and the following criteria apply:
|
|
2201
|
+
- source node has only one default exit
|
|
2202
|
+
- destination is not one of the known functions
|
|
2203
|
+
- destination does not belong to another function, or destination belongs to the same function that
|
|
2204
|
+
source belongs to
|
|
2205
|
+
- at the end of the block, the SP offset is 0
|
|
2206
|
+
- for all other edges that are pointing to the destination node, their source nodes must only have one default
|
|
2207
|
+
exit, too
|
|
2208
|
+
|
|
2209
|
+
:return: True if it is a tail-call optimization. False otherwise.
|
|
2210
|
+
:rtype: bool
|
|
2211
|
+
"""
|
|
2212
|
+
|
|
2213
|
+
def _has_more_than_one_exit(node_):
|
|
2214
|
+
# Do not consider FakeRets as counting as multiple exits here.
|
|
2215
|
+
out_edges = [e for e in g.out_edges(node_) if g.get_edge_data(*e)["jumpkind"] != "Ijk_FakeRet"]
|
|
2216
|
+
return len(out_edges) > 1
|
|
2217
|
+
|
|
2218
|
+
if len(all_edges) == 1 and dst_addr != src_addr:
|
|
2219
|
+
the_edge = next(iter(all_edges))
|
|
2220
|
+
_, dst, data = the_edge
|
|
2221
|
+
if data.get("stmt_idx", None) != DEFAULT_STATEMENT:
|
|
2222
|
+
return False
|
|
2223
|
+
|
|
2224
|
+
# relift the source node to make sure it's not a fall-through target
|
|
2225
|
+
full_src_node = self.project.factory.block(src_addr)
|
|
2226
|
+
if full_src_node.size >= VEX_IRSB_MAX_SIZE or full_src_node.instructions >= VEX_IRSB_MAX_INST:
|
|
2227
|
+
# we are probably hitting the max-block limit in VEX
|
|
2228
|
+
return False
|
|
2229
|
+
if full_src_node.addr <= dst_addr < full_src_node.addr + full_src_node.size:
|
|
2230
|
+
return False
|
|
2231
|
+
|
|
2232
|
+
dst_in_edges = g.in_edges(dst, data=True)
|
|
2233
|
+
if len(dst_in_edges) > 1:
|
|
2234
|
+
# there are other edges going to the destination node. check all edges to make sure all source nodes
|
|
2235
|
+
# only have one default exit
|
|
2236
|
+
if any(data.get("stmt_idx", None) != DEFAULT_STATEMENT for _, _, data in dst_in_edges):
|
|
2237
|
+
# some nodes are jumping to the destination node via non-default edges. skip.
|
|
2238
|
+
return False
|
|
2239
|
+
if any(_has_more_than_one_exit(src_) for src_, _, _ in dst_in_edges):
|
|
2240
|
+
# at least one source node has more than just the default exit. skip.
|
|
2241
|
+
return False
|
|
2242
|
+
|
|
2243
|
+
candidate = False
|
|
2244
|
+
if dst_addr in known_functions:
|
|
2245
|
+
# dst_addr cannot be the same as src_function.addr. Pass
|
|
2246
|
+
pass
|
|
2247
|
+
elif dst_addr in blockaddr_to_function:
|
|
2248
|
+
# it seems that we already know where this function should belong to. Pass.
|
|
2249
|
+
dst_func = blockaddr_to_function[dst_addr]
|
|
2250
|
+
if dst_func is src_function:
|
|
2251
|
+
# they belong to the same function right now, but they'd better not
|
|
2252
|
+
candidate = True
|
|
2253
|
+
# treat it as a tail-call optimization
|
|
2254
|
+
else:
|
|
2255
|
+
# we don't know where it belongs to
|
|
2256
|
+
# treat it as a tail-call optimization
|
|
2257
|
+
candidate = True
|
|
2258
|
+
|
|
2259
|
+
if candidate:
|
|
2260
|
+
# we have two strategies; for small functions, we run SPTracker on the entire function and see if the
|
|
2261
|
+
# stack pointer changes or not; for large functions, we simply detect how far away we jump as well as
|
|
2262
|
+
# if there are any other functions identified between the source and the destination.
|
|
2263
|
+
if len(src_function.block_addrs_set) <= 10:
|
|
2264
|
+
regs = {self.project.arch.sp_offset}
|
|
2265
|
+
if hasattr(self.project.arch, "bp_offset") and self.project.arch.bp_offset is not None:
|
|
2266
|
+
regs.add(self.project.arch.bp_offset)
|
|
2267
|
+
sptracker = self.project.analyses[StackPointerTracker].prep()(
|
|
2268
|
+
src_function, regs, track_memory=self._sp_tracking_track_memory
|
|
2269
|
+
)
|
|
2270
|
+
sp_delta = sptracker.offset_after_block(src_addr, self.project.arch.sp_offset)
|
|
2271
|
+
if sp_delta == 0:
|
|
2272
|
+
return True
|
|
2273
|
+
else:
|
|
2274
|
+
# large function; to speed things up, we don't track sp
|
|
2275
|
+
minaddr, maxaddr = None, None
|
|
2276
|
+
if dst_addr - src_addr >= 0x100:
|
|
2277
|
+
minaddr = src_addr
|
|
2278
|
+
maxaddr = dst_addr
|
|
2279
|
+
elif dst_addr < src_addr:
|
|
2280
|
+
# jumping back; is it jumping beyond the function header?
|
|
2281
|
+
src_func = blockaddr_to_function[src_addr]
|
|
2282
|
+
if dst_addr < src_func.addr and src_func.addr - dst_addr >= 0x100:
|
|
2283
|
+
minaddr = dst_addr
|
|
2284
|
+
maxaddr = src_func.addr
|
|
2285
|
+
|
|
2286
|
+
if minaddr is not None and maxaddr is not None:
|
|
2287
|
+
# are there other function in between?
|
|
2288
|
+
funcaddrs_in_between = list(
|
|
2289
|
+
known_functions._function_map.irange(minimum=minaddr + 1, maximum=maxaddr - 1)
|
|
2290
|
+
)
|
|
2291
|
+
funcs_in_between = [known_functions.get_by_addr(a) for a in funcaddrs_in_between]
|
|
2292
|
+
funcs_in_between = [func for func in funcs_in_between if not func.is_alignment]
|
|
2293
|
+
if len(funcs_in_between) >= 3:
|
|
2294
|
+
return True
|
|
2295
|
+
|
|
2296
|
+
return False
|
|
2297
|
+
|
|
2298
|
+
def _graph_bfs_custom(self, g, starts, callback, blockaddr_to_function, known_functions, traversed_cfg_nodes=None):
|
|
2299
|
+
"""
|
|
2300
|
+
A customized control flow graph BFS implementation with the following rules:
|
|
2301
|
+
- Call edges are not followed.
|
|
2302
|
+
- Syscall edges are not followed.
|
|
2303
|
+
|
|
2304
|
+
:param networkx.DiGraph g: The graph.
|
|
2305
|
+
:param list starts: A collection of beginning nodes to start graph traversal.
|
|
2306
|
+
:param func callback: Callback function for each edge and node.
|
|
2307
|
+
:param dict blockaddr_to_function: A mapping between block addresses to Function instances.
|
|
2308
|
+
:param angr.knowledge_plugins.FunctionManager known_functions: Already recovered functions.
|
|
2309
|
+
:param set traversed_cfg_nodes: A set of CFGNodes that are traversed before.
|
|
2310
|
+
:return: None
|
|
2311
|
+
"""
|
|
2312
|
+
|
|
2313
|
+
stack = OrderedSet(starts)
|
|
2314
|
+
traversed = set() if traversed_cfg_nodes is None else traversed_cfg_nodes
|
|
2315
|
+
|
|
2316
|
+
while stack:
|
|
2317
|
+
n: CFGNode = stack.pop(last=False)
|
|
2318
|
+
|
|
2319
|
+
if n in traversed:
|
|
2320
|
+
continue
|
|
2321
|
+
|
|
2322
|
+
traversed.add(n)
|
|
2323
|
+
|
|
2324
|
+
if n.has_return:
|
|
2325
|
+
callback(g, n, None, {"jumpkind": "Ijk_Ret"}, blockaddr_to_function, known_functions, None)
|
|
2326
|
+
# NOTE: A block that has_return CAN have successors that aren't the return.
|
|
2327
|
+
# This is particularly the case for ARM conditional instructions. Yes, conditional rets are a thing.
|
|
2328
|
+
|
|
2329
|
+
if g.out_degree(n) == 0:
|
|
2330
|
+
# it's a single node
|
|
2331
|
+
callback(g, n, None, None, blockaddr_to_function, known_functions, None)
|
|
2332
|
+
|
|
2333
|
+
else:
|
|
2334
|
+
all_out_edges = g.out_edges(n, data=True)
|
|
2335
|
+
for src, dst, data in all_out_edges:
|
|
2336
|
+
callback(g, src, dst, data, blockaddr_to_function, known_functions, all_out_edges)
|
|
2337
|
+
|
|
2338
|
+
jumpkind = data.get("jumpkind", "")
|
|
2339
|
+
if (not (jumpkind in ("Ijk_Call", "Ijk_Ret") or jumpkind.startswith("Ijk_Sys"))) and (
|
|
2340
|
+
dst not in stack and dst not in traversed
|
|
2341
|
+
):
|
|
2342
|
+
stack.add(dst)
|
|
2343
|
+
|
|
2344
|
+
def _graph_traversal_handler(self, g, src, dst, data, blockaddr_to_function, known_functions, all_edges):
|
|
2345
|
+
"""
|
|
2346
|
+
Graph traversal handler. It takes in a node or an edge, and create new functions or add nodes to existing
|
|
2347
|
+
functions accordingly. Oh, it also create edges on the transition map of functions.
|
|
2348
|
+
|
|
2349
|
+
:param g: The control flow graph that is currently being traversed.
|
|
2350
|
+
:param CFGNode src: Beginning of the edge, or a single node when dst is None.
|
|
2351
|
+
:param CFGNode dst: Destination of the edge. For processing a single node, `dst` is None.
|
|
2352
|
+
:param dict data: Edge data in the CFG. 'jumpkind' should be there if it's not None.
|
|
2353
|
+
:param dict blockaddr_to_function: A mapping between block addresses to Function instances.
|
|
2354
|
+
:param angr.knowledge_plugins.FunctionManager known_functions: Already recovered functions.
|
|
2355
|
+
:param list or None all_edges: All edges going out from src.
|
|
2356
|
+
:return: None
|
|
2357
|
+
"""
|
|
2358
|
+
|
|
2359
|
+
src_addr = src.addr
|
|
2360
|
+
src_function = self._addr_to_function(src_addr, blockaddr_to_function, known_functions)
|
|
2361
|
+
|
|
2362
|
+
if src_addr not in src_function.block_addrs_set:
|
|
2363
|
+
n = self.model.get_any_node(src_addr, force_fastpath=True)
|
|
2364
|
+
node = src_addr if n is None else self._to_snippet(n)
|
|
2365
|
+
self.kb.functions._add_node(src_function.addr, node)
|
|
2366
|
+
|
|
2367
|
+
if data is None:
|
|
2368
|
+
# it's a single node only
|
|
2369
|
+
return
|
|
2370
|
+
|
|
2371
|
+
jumpkind = data["jumpkind"]
|
|
2372
|
+
|
|
2373
|
+
if jumpkind == "Ijk_Ret":
|
|
2374
|
+
n = self.model.get_any_node(src_addr, force_fastpath=True)
|
|
2375
|
+
from_node = src_addr if n is None else self._to_snippet(n)
|
|
2376
|
+
self.kb.functions._add_return_from(src_function.addr, from_node, None)
|
|
2377
|
+
|
|
2378
|
+
if dst is None:
|
|
2379
|
+
return
|
|
2380
|
+
|
|
2381
|
+
dst_addr = dst.addr
|
|
2382
|
+
|
|
2383
|
+
# get instruction address and statement index
|
|
2384
|
+
ins_addr = data.get("ins_addr", None)
|
|
2385
|
+
stmt_idx = data.get("stmt_idx", None)
|
|
2386
|
+
|
|
2387
|
+
if jumpkind == "Ijk_Call" or jumpkind.startswith("Ijk_Sys"):
|
|
2388
|
+
is_syscall = jumpkind.startswith("Ijk_Sys")
|
|
2389
|
+
|
|
2390
|
+
# It must be calling a function
|
|
2391
|
+
dst_function = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
|
|
2392
|
+
|
|
2393
|
+
n = self.model.get_any_node(src_addr, force_fastpath=True)
|
|
2394
|
+
if n is None:
|
|
2395
|
+
src_snippet = self._to_snippet(addr=src_addr, base_state=self._base_state)
|
|
2396
|
+
else:
|
|
2397
|
+
src_snippet = self._to_snippet(cfg_node=n)
|
|
2398
|
+
|
|
2399
|
+
# HACK: FIXME: We need a better way of representing unresolved calls and whether they return.
|
|
2400
|
+
# For now, assume UnresolvedTarget returns if we're calling to it
|
|
2401
|
+
|
|
2402
|
+
# If the function doesn't return, don't add a fakeret!
|
|
2403
|
+
if not all_edges or (dst_function.returning is False and dst_function.name != "UnresolvableCallTarget"):
|
|
2404
|
+
fakeret_node = None
|
|
2405
|
+
else:
|
|
2406
|
+
fakeret_node = self._one_fakeret_node(all_edges)
|
|
2407
|
+
|
|
2408
|
+
if isinstance(dst_addr, SootAddressDescriptor):
|
|
2409
|
+
dst_addr = dst_addr.method
|
|
2410
|
+
|
|
2411
|
+
# determining the returning target
|
|
2412
|
+
return_to_outside = False
|
|
2413
|
+
returning_snippet = None
|
|
2414
|
+
if dst_function.returning and fakeret_node is not None:
|
|
2415
|
+
returning_target = src.addr + src.size
|
|
2416
|
+
if returning_target not in blockaddr_to_function:
|
|
2417
|
+
if returning_target not in known_functions:
|
|
2418
|
+
blockaddr_to_function[returning_target] = src_function
|
|
2419
|
+
else:
|
|
2420
|
+
self._addr_to_function(returning_target, blockaddr_to_function, known_functions)
|
|
2421
|
+
|
|
2422
|
+
return_to_outside = blockaddr_to_function[returning_target] is not src_function
|
|
2423
|
+
|
|
2424
|
+
n = self.model.get_any_node(returning_target, force_fastpath=True)
|
|
2425
|
+
if n is None:
|
|
2426
|
+
try:
|
|
2427
|
+
returning_snippet = self._to_snippet(addr=returning_target, base_state=self._base_state)
|
|
2428
|
+
except SimEngineError:
|
|
2429
|
+
# it may not exist
|
|
2430
|
+
returning_snippet = None
|
|
2431
|
+
else:
|
|
2432
|
+
returning_snippet = self._to_snippet(cfg_node=n)
|
|
2433
|
+
|
|
2434
|
+
self.kb.functions._add_call_to(
|
|
2435
|
+
src_function.addr,
|
|
2436
|
+
src_snippet,
|
|
2437
|
+
dst_addr,
|
|
2438
|
+
retn_node=returning_snippet,
|
|
2439
|
+
syscall=is_syscall,
|
|
2440
|
+
ins_addr=ins_addr,
|
|
2441
|
+
stmt_idx=stmt_idx,
|
|
2442
|
+
return_to_outside=return_to_outside,
|
|
2443
|
+
)
|
|
2444
|
+
|
|
2445
|
+
if returning_snippet is not None:
|
|
2446
|
+
self.kb.functions._add_fakeret_to(
|
|
2447
|
+
src_function.addr, src_snippet, returning_snippet, confirmed=True, to_outside=return_to_outside
|
|
2448
|
+
)
|
|
2449
|
+
|
|
2450
|
+
elif jumpkind in ("Ijk_Boring", "Ijk_InvalICache", "Ijk_Exception"):
|
|
2451
|
+
# convert src_addr and dst_addr to CodeNodes
|
|
2452
|
+
n = self.model.get_any_node(src_addr, force_fastpath=True)
|
|
2453
|
+
src_node = src_addr if n is None else self._to_snippet(cfg_node=n)
|
|
2454
|
+
|
|
2455
|
+
n = self.model.get_any_node(dst_addr, force_fastpath=True)
|
|
2456
|
+
dst_node = dst_addr if n is None else self._to_snippet(cfg_node=n)
|
|
2457
|
+
|
|
2458
|
+
if self._skip_unmapped_addrs:
|
|
2459
|
+
# pre-check: if source and destination do not belong to the same section, it must be jumping to another
|
|
2460
|
+
# function
|
|
2461
|
+
belong_to_same_section = self._addrs_belong_to_same_section(src_addr, dst_addr)
|
|
2462
|
+
if not belong_to_same_section:
|
|
2463
|
+
_ = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
|
|
2464
|
+
|
|
2465
|
+
if self._detect_tail_calls and self._is_tail_call_optimization(
|
|
2466
|
+
g, src_addr, dst_addr, src_function, all_edges, known_functions, blockaddr_to_function
|
|
2467
|
+
):
|
|
2468
|
+
l.debug("Possible tail-call optimization detected at function %#x.", dst_addr)
|
|
2469
|
+
# it's (probably) a tail-call optimization. we should make the destination node a new function
|
|
2470
|
+
# instead.
|
|
2471
|
+
blockaddr_to_function.pop(dst_addr, None)
|
|
2472
|
+
_ = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
|
|
2473
|
+
self.kb.functions._add_outside_transition_to(
|
|
2474
|
+
src_function.addr, src_node, dst_node, to_function_addr=dst_addr
|
|
2475
|
+
)
|
|
2476
|
+
self._tail_calls.add(dst_addr)
|
|
2477
|
+
|
|
2478
|
+
# is it a jump to another function?
|
|
2479
|
+
if isinstance(dst_addr, SootAddressDescriptor):
|
|
2480
|
+
is_known_function_addr = dst_addr.method in known_functions and dst_addr.method.addr == dst_addr
|
|
2481
|
+
else:
|
|
2482
|
+
is_known_function_addr = dst_addr in known_functions
|
|
2483
|
+
|
|
2484
|
+
if (is_known_function_addr and dst_addr != src_function.addr) or (
|
|
2485
|
+
dst_addr in blockaddr_to_function and blockaddr_to_function[dst_addr] is not src_function
|
|
2486
|
+
):
|
|
2487
|
+
# yes it is
|
|
2488
|
+
dst_function_addr = (
|
|
2489
|
+
blockaddr_to_function[dst_addr].addr if dst_addr in blockaddr_to_function else dst_addr
|
|
2490
|
+
)
|
|
2491
|
+
|
|
2492
|
+
self.kb.functions._add_outside_transition_to(
|
|
2493
|
+
src_function.addr,
|
|
2494
|
+
src_node,
|
|
2495
|
+
dst_node,
|
|
2496
|
+
ins_addr=ins_addr,
|
|
2497
|
+
stmt_idx=stmt_idx,
|
|
2498
|
+
to_function_addr=dst_function_addr,
|
|
2499
|
+
is_exception=jumpkind == "Ijk_Exception",
|
|
2500
|
+
)
|
|
2501
|
+
|
|
2502
|
+
_ = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
|
|
2503
|
+
else:
|
|
2504
|
+
# no it's not
|
|
2505
|
+
# add the transition code
|
|
2506
|
+
|
|
2507
|
+
if dst_addr not in blockaddr_to_function:
|
|
2508
|
+
blockaddr_to_function[dst_addr] = src_function
|
|
2509
|
+
|
|
2510
|
+
self.kb.functions._add_transition_to(
|
|
2511
|
+
src_function.addr,
|
|
2512
|
+
src_node,
|
|
2513
|
+
dst_node,
|
|
2514
|
+
ins_addr=ins_addr,
|
|
2515
|
+
stmt_idx=stmt_idx,
|
|
2516
|
+
is_exception=jumpkind == "Ijk_Exception",
|
|
2517
|
+
)
|
|
2518
|
+
|
|
2519
|
+
elif jumpkind == "Ijk_FakeRet":
|
|
2520
|
+
# convert src_addr and dst_addr to CodeNodes
|
|
2521
|
+
n = self.model.get_any_node(src_addr, force_fastpath=True)
|
|
2522
|
+
src_node = src_addr if n is None else self._to_snippet(n)
|
|
2523
|
+
|
|
2524
|
+
n = self.model.get_any_node(dst_addr, force_fastpath=True)
|
|
2525
|
+
dst_node = dst_addr if n is None else self._to_snippet(n)
|
|
2526
|
+
|
|
2527
|
+
if dst_addr not in blockaddr_to_function:
|
|
2528
|
+
if isinstance(dst_addr, SootAddressDescriptor):
|
|
2529
|
+
if dst_addr.method not in known_functions:
|
|
2530
|
+
blockaddr_to_function[dst_addr] = src_function
|
|
2531
|
+
target_function = src_function
|
|
2532
|
+
else:
|
|
2533
|
+
target_function = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
|
|
2534
|
+
else:
|
|
2535
|
+
if dst_addr not in known_functions:
|
|
2536
|
+
blockaddr_to_function[dst_addr] = src_function
|
|
2537
|
+
target_function = src_function
|
|
2538
|
+
else:
|
|
2539
|
+
target_function = self._addr_to_function(dst_addr, blockaddr_to_function, known_functions)
|
|
2540
|
+
else:
|
|
2541
|
+
target_function = blockaddr_to_function[dst_addr]
|
|
2542
|
+
|
|
2543
|
+
# Figure out if the function called (not the function returned to) returns.
|
|
2544
|
+
# We may have determined that this does not happen, since the time this path
|
|
2545
|
+
# was scheduled for exploration
|
|
2546
|
+
called_function = None
|
|
2547
|
+
called_function_addr = None
|
|
2548
|
+
# Try to find the call that this fakeret goes with
|
|
2549
|
+
for _, d, e in all_edges:
|
|
2550
|
+
if e["jumpkind"] == "Ijk_Call" and d.addr in blockaddr_to_function:
|
|
2551
|
+
called_function = blockaddr_to_function[d.addr]
|
|
2552
|
+
called_function_addr = d.addr
|
|
2553
|
+
break
|
|
2554
|
+
# We may have since figured out that the called function doesn't ret.
|
|
2555
|
+
# It's important to assume that all unresolved targets do return
|
|
2556
|
+
if called_function is not None and called_function.returning is False:
|
|
2557
|
+
return
|
|
2558
|
+
|
|
2559
|
+
to_outside = target_function is not src_function
|
|
2560
|
+
|
|
2561
|
+
confirmed = called_function is None or called_function.returning is True
|
|
2562
|
+
self.kb.functions._add_fakeret_to(
|
|
2563
|
+
src_function.addr,
|
|
2564
|
+
src_node,
|
|
2565
|
+
dst_node,
|
|
2566
|
+
confirmed=confirmed,
|
|
2567
|
+
to_outside=to_outside,
|
|
2568
|
+
to_function_addr=called_function_addr,
|
|
2569
|
+
)
|
|
2570
|
+
|
|
2571
|
+
else:
|
|
2572
|
+
l.debug("Ignored jumpkind %s", jumpkind)
|
|
2573
|
+
|
|
2574
|
+
#
|
|
2575
|
+
# Other functions
|
|
2576
|
+
#
|
|
2577
|
+
|
|
2578
|
+
@staticmethod
|
|
2579
|
+
def _is_noop_jump_block(block) -> bool:
|
|
2580
|
+
"""
|
|
2581
|
+
Check if the block does nothing but jumping to a constant address.
|
|
2582
|
+
|
|
2583
|
+
:param block: The block instance. We assume the block is already optimized.
|
|
2584
|
+
:return: True if the entire block is a jump to a constant address, False otherwise.
|
|
2585
|
+
"""
|
|
2586
|
+
|
|
2587
|
+
vex = block.vex
|
|
2588
|
+
if vex.jumpkind != "Ijk_Boring":
|
|
2589
|
+
return False
|
|
2590
|
+
if isinstance(vex.next, pyvex.expr.Const):
|
|
2591
|
+
return all(isinstance(stmt, pyvex.stmt.IMark) for stmt in vex.statements)
|
|
2592
|
+
if isinstance(vex.next, pyvex.expr.RdTmp):
|
|
2593
|
+
next_tmp = vex.next.tmp
|
|
2594
|
+
return all(
|
|
2595
|
+
isinstance(stmt, pyvex.stmt.IMark)
|
|
2596
|
+
or (
|
|
2597
|
+
isinstance(stmt, pyvex.stmt.WrTmp)
|
|
2598
|
+
and stmt.tmp == next_tmp
|
|
2599
|
+
and isinstance(stmt.data, pyvex.expr.Load)
|
|
2600
|
+
and isinstance(stmt.data.addr, pyvex.expr.Const)
|
|
2601
|
+
)
|
|
2602
|
+
for stmt in vex.statements
|
|
2603
|
+
)
|
|
2604
|
+
return False
|
|
2605
|
+
|
|
2606
|
+
@staticmethod
|
|
2607
|
+
def _is_noop_block(arch: archinfo.Arch, block) -> bool:
|
|
2608
|
+
"""
|
|
2609
|
+
Check if the block is a no-op block by checking VEX statements.
|
|
2610
|
+
|
|
2611
|
+
:param arch: An architecture descriptor.
|
|
2612
|
+
:param block: The VEX block instance.
|
|
2613
|
+
:return: True if the entire block is a single-byte or multi-byte nop instruction, False otherwise.
|
|
2614
|
+
"""
|
|
2615
|
+
|
|
2616
|
+
if arch.name == "X86" or arch.name == "AMD64":
|
|
2617
|
+
block_bytes_set = set(block.bytes)
|
|
2618
|
+
if block_bytes_set == {0x90}:
|
|
2619
|
+
return True
|
|
2620
|
+
if block_bytes_set == {0xCC}:
|
|
2621
|
+
# technically this is not a no-op, but for our purposes we can settle for now
|
|
2622
|
+
return True
|
|
2623
|
+
elif arch.name == "MIPS32":
|
|
2624
|
+
if arch.memory_endness == "Iend_BE":
|
|
2625
|
+
MIPS32_BE_NOOPS = {
|
|
2626
|
+
b"\x00\x20\x08\x25", # move $at, $at
|
|
2627
|
+
}
|
|
2628
|
+
insns = {block.bytes[i : i + 4] for i in range(0, block.size, 4)}
|
|
2629
|
+
if MIPS32_BE_NOOPS.issuperset(insns):
|
|
2630
|
+
return True
|
|
2631
|
+
|
|
2632
|
+
elif is_arm_arch(arch):
|
|
2633
|
+
if block.addr & 1 == 0:
|
|
2634
|
+
# ARM mode
|
|
2635
|
+
if arch.memory_endness == archinfo.Endness.LE:
|
|
2636
|
+
ARM_NOOPS = {
|
|
2637
|
+
b"\x00\x00\x00\x00", # andeq r0, r0, r0
|
|
2638
|
+
b"\x00\x00\xa0\xe1", # mov r0, r0
|
|
2639
|
+
}
|
|
2640
|
+
else: # if arch.memory_endness == archinfo.Endness.BE:
|
|
2641
|
+
ARM_NOOPS = {
|
|
2642
|
+
b"\x00\x00\x00\x00", # andeq r0, r0, r0
|
|
2643
|
+
b"\xe1\xa0\x00\x00", # mov r0, r0
|
|
2644
|
+
}
|
|
2645
|
+
insns = {block.bytes[i : i + 4] for i in range(0, block.size, 4)}
|
|
2646
|
+
if ARM_NOOPS.issuperset(insns):
|
|
2647
|
+
return True
|
|
2648
|
+
|
|
2649
|
+
else:
|
|
2650
|
+
# THUMB mode, 2-byte instructions
|
|
2651
|
+
if arch.memory_endness == archinfo.Endness.LE:
|
|
2652
|
+
THUMB_NOOPS = {
|
|
2653
|
+
b"\xc0\x46", # mov r8, r8
|
|
2654
|
+
b"\xb0\x00", # add sp, #0
|
|
2655
|
+
b"\x00\xbf", # nop
|
|
2656
|
+
}
|
|
2657
|
+
else:
|
|
2658
|
+
THUMB_NOOPS = {
|
|
2659
|
+
b"\x46\xc0", # mov r8, r8
|
|
2660
|
+
b"\x00\xb0", # add sp, #0
|
|
2661
|
+
b"\xbf\x00", # nop
|
|
2662
|
+
}
|
|
2663
|
+
insns = {block.bytes[i : i + 2] for i in range(0, block.size, 2)}
|
|
2664
|
+
if THUMB_NOOPS.issuperset(insns):
|
|
2665
|
+
return True
|
|
2666
|
+
|
|
2667
|
+
return block.vex_nostmt.is_noop_block
|
|
2668
|
+
|
|
2669
|
+
@staticmethod
|
|
2670
|
+
def _is_noop_insn(insn):
|
|
2671
|
+
"""
|
|
2672
|
+
Check if the instruction does nothing.
|
|
2673
|
+
|
|
2674
|
+
:param insn: The capstone insn object.
|
|
2675
|
+
:return: True if the instruction does no-op, False otherwise.
|
|
2676
|
+
"""
|
|
2677
|
+
|
|
2678
|
+
insn_name = insn.mnemonic
|
|
2679
|
+
|
|
2680
|
+
if insn_name == "nop":
|
|
2681
|
+
# nops
|
|
2682
|
+
return True
|
|
2683
|
+
if insn_name == "lea":
|
|
2684
|
+
# lea reg, [reg + 0]
|
|
2685
|
+
op0, op1 = insn.operands
|
|
2686
|
+
# reg and mem
|
|
2687
|
+
if op0.type == 1 and op1.type == 3 and op0.reg == op1.mem.base and op1.mem.index == 0 and op1.mem.disp == 0:
|
|
2688
|
+
return True
|
|
2689
|
+
elif insn_name == "mov":
|
|
2690
|
+
if len(insn.operands) > 2:
|
|
2691
|
+
# mov reg_a, imm1, shift imm2
|
|
2692
|
+
# This is not a NOP
|
|
2693
|
+
return False
|
|
2694
|
+
# mov reg_a, reg_a
|
|
2695
|
+
op0, op1 = insn.operands
|
|
2696
|
+
# reg and reg
|
|
2697
|
+
if op0.type == 1 and op1.type == 1 and op0.reg == op1.reg:
|
|
2698
|
+
return True
|
|
2699
|
+
elif insn_name in {"ud0", "ud1", "ud2"}:
|
|
2700
|
+
return True
|
|
2701
|
+
|
|
2702
|
+
# add more types of no-op instructions here :-)
|
|
2703
|
+
|
|
2704
|
+
return False
|
|
2705
|
+
|
|
2706
|
+
@classmethod
|
|
2707
|
+
def _get_nop_length(cls, insns):
|
|
2708
|
+
"""
|
|
2709
|
+
Calculate the total size of leading nop instructions.
|
|
2710
|
+
|
|
2711
|
+
:param insns: A list of capstone insn objects.
|
|
2712
|
+
:return: Number of bytes of leading nop instructions.
|
|
2713
|
+
:rtype: int
|
|
2714
|
+
"""
|
|
2715
|
+
|
|
2716
|
+
nop_length = 0
|
|
2717
|
+
|
|
2718
|
+
if insns and cls._is_noop_insn(insns[0]):
|
|
2719
|
+
# see where those nop instructions terminate
|
|
2720
|
+
for insn in insns:
|
|
2721
|
+
if cls._is_noop_insn(insn):
|
|
2722
|
+
nop_length += insn.size
|
|
2723
|
+
else:
|
|
2724
|
+
break
|
|
2725
|
+
|
|
2726
|
+
return nop_length
|
|
2727
|
+
|
|
2728
|
+
@staticmethod
|
|
2729
|
+
def _one_fakeret_node(all_edges):
|
|
2730
|
+
"""
|
|
2731
|
+
Pick the first Ijk_FakeRet edge from all_edges, and return the destination node.
|
|
2732
|
+
|
|
2733
|
+
:param list all_edges: A list of networkx.Graph edges with data.
|
|
2734
|
+
:return: The first FakeRet node, or None if nothing is found.
|
|
2735
|
+
:rtype: CFGNode or None
|
|
2736
|
+
"""
|
|
2737
|
+
|
|
2738
|
+
for _, dst, data in all_edges:
|
|
2739
|
+
if data.get("jumpkind", None) == "Ijk_FakeRet":
|
|
2740
|
+
return dst
|
|
2741
|
+
return None
|
|
2742
|
+
|
|
2743
|
+
def _lift(self, addr, *args, opt_level=1, cross_insn_opt=False, **kwargs):
|
|
2744
|
+
"""
|
|
2745
|
+
Lift a basic block of code. Will use the base state as a source of bytes if possible.
|
|
2746
|
+
"""
|
|
2747
|
+
if "backup_state" not in kwargs:
|
|
2748
|
+
kwargs["backup_state"] = self._base_state
|
|
2749
|
+
return self.project.factory.block(addr, *args, opt_level=opt_level, cross_insn_opt=cross_insn_opt, **kwargs)
|
|
2750
|
+
|
|
2751
|
+
#
|
|
2752
|
+
# Indirect jumps processing
|
|
2753
|
+
#
|
|
2754
|
+
|
|
2755
|
+
def _resolve_indirect_jump_timelessly(self, addr, block, func_addr, jumpkind):
|
|
2756
|
+
"""
|
|
2757
|
+
Attempt to quickly resolve an indirect jump.
|
|
2758
|
+
|
|
2759
|
+
:param int addr: Basic block address of this indirect jump.
|
|
2760
|
+
:param block: The basic block. The type is determined by the backend being used. It's pyvex.IRSB if
|
|
2761
|
+
pyvex is used as the backend.
|
|
2762
|
+
:param int func_addr: Address of the function that this indirect jump belongs to.
|
|
2763
|
+
:param str jumpkind: The jumpkind.
|
|
2764
|
+
:return: A tuple of a boolean indicating whether the resolution is successful or not, and a list
|
|
2765
|
+
of resolved targets (ints).
|
|
2766
|
+
:rtype: tuple
|
|
2767
|
+
"""
|
|
2768
|
+
|
|
2769
|
+
# pre-check: if re-lifting the block with full optimization (cross-instruction-optimization enabled) gives us
|
|
2770
|
+
# a constant next expression, we don't need to resolve it
|
|
2771
|
+
try:
|
|
2772
|
+
relifted = self.project.factory.block(block.addr, size=block.size, opt_level=1, cross_insn_opt=True).vex
|
|
2773
|
+
except SimError:
|
|
2774
|
+
return False, []
|
|
2775
|
+
if not relifted.jumpkind.startswith("Ijk_Sys") and isinstance(relifted.next, pyvex.IRExpr.Const):
|
|
2776
|
+
# yes!
|
|
2777
|
+
return True, [relifted.next.con.value]
|
|
2778
|
+
|
|
2779
|
+
if block.statements is None:
|
|
2780
|
+
# make sure there are statements
|
|
2781
|
+
block = self.project.factory.block(block.addr, size=block.size).vex
|
|
2782
|
+
|
|
2783
|
+
for res in self.timeless_indirect_jump_resolvers:
|
|
2784
|
+
if res.filter(self, addr, func_addr, block, jumpkind):
|
|
2785
|
+
r, resolved_targets = res.resolve(self, addr, func_addr, block, jumpkind)
|
|
2786
|
+
if r:
|
|
2787
|
+
return True, resolved_targets
|
|
2788
|
+
return False, []
|
|
2789
|
+
|
|
2790
|
+
def _indirect_jump_resolved(self, jump, jump_addr, resolved_by, targets):
|
|
2791
|
+
"""
|
|
2792
|
+
Called when an indirect jump is successfully resolved.
|
|
2793
|
+
|
|
2794
|
+
:param IndirectJump jump: The resolved indirect jump, or None if an IndirectJump instance is
|
|
2795
|
+
not available.
|
|
2796
|
+
:param int jump_addr: Address of the resolved indirect jump.
|
|
2797
|
+
:param IndirectJumpResolver resolved_by: The resolver used to resolve this indirect jump.
|
|
2798
|
+
:param list targets: List of indirect jump targets.
|
|
2799
|
+
:param CFGJob job: The job at the start of the block containing the indirect jump.
|
|
2800
|
+
|
|
2801
|
+
:return: None
|
|
2802
|
+
"""
|
|
2803
|
+
|
|
2804
|
+
addr = jump.addr if jump is not None else jump_addr
|
|
2805
|
+
l.debug(
|
|
2806
|
+
"The indirect jump at %#x is successfully resolved by %s. It has %d targets.",
|
|
2807
|
+
addr,
|
|
2808
|
+
resolved_by,
|
|
2809
|
+
len(targets),
|
|
2810
|
+
)
|
|
2811
|
+
self.kb.indirect_jumps.update_resolved_addrs(addr, targets)
|
|
2812
|
+
|
|
2813
|
+
def _indirect_jump_unresolved(self, jump):
|
|
2814
|
+
"""
|
|
2815
|
+
Called when we cannot resolve an indirect jump.
|
|
2816
|
+
|
|
2817
|
+
:param IndirectJump jump: The unresolved indirect jump.
|
|
2818
|
+
|
|
2819
|
+
:return: None
|
|
2820
|
+
"""
|
|
2821
|
+
|
|
2822
|
+
l.debug("Failed to resolve the indirect jump at %#x.", jump.addr)
|
|
2823
|
+
# tell KnowledgeBase that it's not resolved
|
|
2824
|
+
# TODO: self.kb._unresolved_indirect_jumps is not processed during normalization. Fix it.
|
|
2825
|
+
self.kb.unresolved_indirect_jumps.add(jump.addr)
|
|
2826
|
+
|
|
2827
|
+
def _indirect_jump_encountered(
|
|
2828
|
+
self,
|
|
2829
|
+
addr: int,
|
|
2830
|
+
cfg_node: CFGNode,
|
|
2831
|
+
irsb: pyvex.IRSB,
|
|
2832
|
+
func_addr: int,
|
|
2833
|
+
stmt_idx: int = DEFAULT_STATEMENT,
|
|
2834
|
+
) -> tuple[bool, set[int], IndirectJump | None]:
|
|
2835
|
+
"""
|
|
2836
|
+
Called when we encounter an indirect jump. We will try to resolve this indirect jump using timeless (fast)
|
|
2837
|
+
indirect jump resolvers. If it cannot be resolved, we will see if this indirect jump has been resolved before.
|
|
2838
|
+
|
|
2839
|
+
:param addr: Address of the block containing the indirect jump.
|
|
2840
|
+
:param cfg_node: The CFGNode instance of the block that contains the indirect jump.
|
|
2841
|
+
:param irsb: The IRSB instance of the block that contains the indirect jump. It must be lifted with
|
|
2842
|
+
cross-instruction optimization disabled (cross_insn_opt=True when opt_level=1, or
|
|
2843
|
+
opt_level=0).
|
|
2844
|
+
:param func_addr: Address of the current function.
|
|
2845
|
+
:param stmt_idx: ID of the source statement.
|
|
2846
|
+
|
|
2847
|
+
:return: A 3-tuple of (whether it is resolved or not, all resolved targets, an IndirectJump object
|
|
2848
|
+
if there is one or None otherwise)
|
|
2849
|
+
"""
|
|
2850
|
+
|
|
2851
|
+
jumpkind = irsb.jumpkind
|
|
2852
|
+
l.debug("IRSB %#x has an indirect jump (%s) as its default exit.", addr, jumpkind)
|
|
2853
|
+
|
|
2854
|
+
# try resolving it fast
|
|
2855
|
+
resolved, resolved_targets = self._resolve_indirect_jump_timelessly(addr, irsb, func_addr, jumpkind)
|
|
2856
|
+
if resolved:
|
|
2857
|
+
l.debug(
|
|
2858
|
+
"Indirect jump at block %#x is resolved by a timeless indirect jump resolver. %d targets found.",
|
|
2859
|
+
addr,
|
|
2860
|
+
len(resolved_targets),
|
|
2861
|
+
)
|
|
2862
|
+
return True, set(resolved_targets), None
|
|
2863
|
+
|
|
2864
|
+
l.debug("Indirect jump at block %#x cannot be resolved by a timeless indirect jump resolver.", addr)
|
|
2865
|
+
|
|
2866
|
+
# Add it to our set. Will process it later if user allows.
|
|
2867
|
+
# Create an IndirectJump instance
|
|
2868
|
+
if addr not in self.indirect_jumps:
|
|
2869
|
+
if self.project.arch.branch_delay_slot:
|
|
2870
|
+
if len(cfg_node.instruction_addrs) < 2:
|
|
2871
|
+
# sanity check
|
|
2872
|
+
# decoding failed when decoding the second instruction (or even the first instruction)
|
|
2873
|
+
return False, set(), None
|
|
2874
|
+
ins_addr = cfg_node.instruction_addrs[-2]
|
|
2875
|
+
elif cfg_node.instruction_addrs:
|
|
2876
|
+
ins_addr = cfg_node.instruction_addrs[-1]
|
|
2877
|
+
else:
|
|
2878
|
+
# fallback
|
|
2879
|
+
ins_addr = addr
|
|
2880
|
+
assert jumpkind is not None
|
|
2881
|
+
ij = IndirectJump(addr, ins_addr, func_addr, jumpkind, stmt_idx, resolved_targets=[])
|
|
2882
|
+
self.indirect_jumps[addr] = ij
|
|
2883
|
+
resolved = False
|
|
2884
|
+
else:
|
|
2885
|
+
ij: IndirectJump = self.indirect_jumps[addr]
|
|
2886
|
+
resolved = len(ij.resolved_targets) > 0
|
|
2887
|
+
|
|
2888
|
+
return resolved, ij.resolved_targets, ij
|
|
2889
|
+
|
|
2890
|
+
def _process_unresolved_indirect_jumps(self):
|
|
2891
|
+
"""
|
|
2892
|
+
Resolve all unresolved indirect jumps found in previous scanning.
|
|
2893
|
+
|
|
2894
|
+
Currently we support resolving the following types of indirect jumps:
|
|
2895
|
+
- Ijk_Call: indirect calls where the function address is passed in from a proceeding basic block
|
|
2896
|
+
- Ijk_Boring: jump tables
|
|
2897
|
+
- For an up-to-date list, see analyses/cfg/indirect_jump_resolvers
|
|
2898
|
+
|
|
2899
|
+
:return: A set of concrete indirect jump targets (ints).
|
|
2900
|
+
:rtype: set
|
|
2901
|
+
"""
|
|
2902
|
+
|
|
2903
|
+
l.info("%d indirect jumps to resolve.", len(self._indirect_jumps_to_resolve))
|
|
2904
|
+
|
|
2905
|
+
all_targets = set()
|
|
2906
|
+
idx: int
|
|
2907
|
+
jump: IndirectJump
|
|
2908
|
+
for idx, jump in enumerate(self._indirect_jumps_to_resolve):
|
|
2909
|
+
if self._low_priority:
|
|
2910
|
+
self._release_gil(idx, 50, 0.000001)
|
|
2911
|
+
all_targets |= self._process_one_indirect_jump(jump)
|
|
2912
|
+
|
|
2913
|
+
self._indirect_jumps_to_resolve.clear()
|
|
2914
|
+
|
|
2915
|
+
return all_targets
|
|
2916
|
+
|
|
2917
|
+
def _process_one_indirect_jump(self, jump: IndirectJump, func_graph_complete: bool = True) -> set:
|
|
2918
|
+
"""
|
|
2919
|
+
Resolve a given indirect jump.
|
|
2920
|
+
|
|
2921
|
+
:param jump: The IndirectJump instance.
|
|
2922
|
+
:param func_graph_complete: True if the function graph is complete at this point (except for this indirect jump
|
|
2923
|
+
and all nodes that it dominates). Indirect jump resolvers may use the current
|
|
2924
|
+
function graph to perform sanity checks. CFGEmulated sets func_graph_complete to
|
|
2925
|
+
False while CFGFast sets it to True (because in CFGFast, indirect jumps are always
|
|
2926
|
+
resolved after direct jump jobs are processed).
|
|
2927
|
+
:return: A set of resolved indirect jump targets (ints).
|
|
2928
|
+
"""
|
|
2929
|
+
|
|
2930
|
+
resolved = False
|
|
2931
|
+
resolved_by = None
|
|
2932
|
+
targets = None
|
|
2933
|
+
|
|
2934
|
+
block = self._lift(jump.addr)
|
|
2935
|
+
|
|
2936
|
+
for resolver in self.indirect_jump_resolvers:
|
|
2937
|
+
resolver.base_state = self._base_state
|
|
2938
|
+
|
|
2939
|
+
if not resolver.filter(self, jump.addr, jump.func_addr, block, jump.jumpkind):
|
|
2940
|
+
continue
|
|
2941
|
+
|
|
2942
|
+
resolved, targets = resolver.resolve(
|
|
2943
|
+
self, jump.addr, jump.func_addr, block, jump.jumpkind, func_graph_complete=func_graph_complete
|
|
2944
|
+
)
|
|
2945
|
+
if resolved:
|
|
2946
|
+
resolved_by = resolver
|
|
2947
|
+
break
|
|
2948
|
+
|
|
2949
|
+
if resolved:
|
|
2950
|
+
self._indirect_jump_resolved(jump, jump.addr, resolved_by, targets)
|
|
2951
|
+
else:
|
|
2952
|
+
self._indirect_jump_unresolved(jump)
|
|
2953
|
+
|
|
2954
|
+
return set() if targets is None else set(targets)
|