angr 9.2.87__py3-none-manylinux2014_x86_64.whl → 9.2.89__py3-none-manylinux2014_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of angr might be problematic. Click here for more details.
- angr/__init__.py +4 -1
- angr/analyses/decompiler/clinic.py +16 -0
- angr/analyses/decompiler/decompiler.py +3 -0
- angr/analyses/decompiler/optimization_passes/__init__.py +5 -0
- angr/analyses/decompiler/optimization_passes/cross_jump_reverter.py +108 -0
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +17 -4
- angr/analyses/decompiler/optimization_passes/return_duplicator.py +4 -32
- angr/analyses/decompiler/structured_codegen/c.py +12 -2
- angr/analyses/decompiler/utils.py +13 -0
- angr/analyses/typehoon/dfa.py +108 -0
- angr/analyses/typehoon/lifter.py +34 -2
- angr/analyses/typehoon/simple_solver.py +1043 -503
- angr/analyses/typehoon/translator.py +13 -4
- angr/analyses/typehoon/typeconsts.py +117 -36
- angr/analyses/typehoon/typehoon.py +31 -11
- angr/analyses/typehoon/typevars.py +88 -21
- angr/analyses/typehoon/variance.py +10 -0
- angr/analyses/variable_recovery/engine_ail.py +28 -9
- angr/analyses/variable_recovery/engine_base.py +50 -43
- angr/analyses/variable_recovery/variable_recovery_base.py +16 -3
- angr/analyses/variable_recovery/variable_recovery_fast.py +14 -5
- angr/exploration_techniques/tracer.py +2 -0
- angr/misc/autoimport.py +26 -0
- angr/procedures/definitions/__init__.py +32 -3
- angr/utils/constants.py +1 -0
- angr/utils/graph.py +20 -1
- {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/METADATA +7 -6
- {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/RECORD +32 -244
- angr-9.2.89.dist-info/top_level.txt +1 -0
- angr/procedures/definitions/ntdll.py +0 -12
- angr-9.2.87.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- tests/analyses/__init__.py +0 -0
- tests/analyses/cfg/__init__.py +0 -0
- tests/analyses/cfg/test_cfg_clflush.py +0 -43
- tests/analyses/cfg/test_cfg_get_any_node.py +0 -34
- tests/analyses/cfg/test_cfg_manager.py +0 -32
- tests/analyses/cfg/test_cfg_model.py +0 -55
- tests/analyses/cfg/test_cfg_patching.py +0 -378
- tests/analyses/cfg/test_cfg_rust_got_resolution.py +0 -36
- tests/analyses/cfg/test_cfg_thumb_firmware.py +0 -50
- tests/analyses/cfg/test_cfg_vex_postprocessor.py +0 -27
- tests/analyses/cfg/test_cfgemulated.py +0 -634
- tests/analyses/cfg/test_cfgfast.py +0 -1123
- tests/analyses/cfg/test_cfgfast_soot.py +0 -38
- tests/analyses/cfg/test_const_resolver.py +0 -38
- tests/analyses/cfg/test_iat_resolver.py +0 -37
- tests/analyses/cfg/test_jumptables.py +0 -3008
- tests/analyses/cfg/test_noop_blocks.py +0 -54
- tests/analyses/cfg_slice_to_sink/__init__.py +0 -0
- tests/analyses/cfg_slice_to_sink/test_cfg_slice_to_sink.py +0 -93
- tests/analyses/cfg_slice_to_sink/test_graph.py +0 -114
- tests/analyses/cfg_slice_to_sink/test_transitions.py +0 -28
- tests/analyses/decompiler/__init__.py +0 -0
- tests/analyses/decompiler/test_baseptr_save_simplifier.py +0 -80
- tests/analyses/decompiler/test_decompiler.py +0 -3336
- tests/analyses/decompiler/test_peephole_optimizations.py +0 -48
- tests/analyses/decompiler/test_propagator_loops.py +0 -101
- tests/analyses/decompiler/test_structurer.py +0 -275
- tests/analyses/reaching_definitions/__init__.py +0 -0
- tests/analyses/reaching_definitions/test_dep_graph.py +0 -432
- tests/analyses/reaching_definitions/test_function_handler.py +0 -131
- tests/analyses/reaching_definitions/test_heap_allocator.py +0 -46
- tests/analyses/reaching_definitions/test_rd_state.py +0 -78
- tests/analyses/reaching_definitions/test_reachingdefinitions.py +0 -463
- tests/analyses/reaching_definitions/test_subject.py +0 -76
- tests/analyses/test_bindiff.py +0 -52
- tests/analyses/test_block_simplifier.py +0 -112
- tests/analyses/test_boyscout.py +0 -104
- tests/analyses/test_calling_convention_analysis.py +0 -352
- tests/analyses/test_callsite_maker.py +0 -60
- tests/analyses/test_cdg.py +0 -165
- tests/analyses/test_cfb.py +0 -37
- tests/analyses/test_class_identifier.py +0 -46
- tests/analyses/test_clinic.py +0 -30
- tests/analyses/test_codetagging.py +0 -32
- tests/analyses/test_constantpropagation.py +0 -88
- tests/analyses/test_ddg.py +0 -95
- tests/analyses/test_ddg_global_var_dependencies.py +0 -83
- tests/analyses/test_ddg_memvar_addresses.py +0 -40
- tests/analyses/test_disassembly.py +0 -121
- tests/analyses/test_find_objects_static.py +0 -35
- tests/analyses/test_flirt.py +0 -49
- tests/analyses/test_identifier.py +0 -33
- tests/analyses/test_init_finder.py +0 -38
- tests/analyses/test_proximitygraph.py +0 -31
- tests/analyses/test_reassembler.py +0 -295
- tests/analyses/test_regionidentifier.py +0 -27
- tests/analyses/test_slicing.py +0 -164
- tests/analyses/test_stack_pointer_tracker.py +0 -74
- tests/analyses/test_static_hooker.py +0 -28
- tests/analyses/test_typehoon.py +0 -55
- tests/analyses/test_variablerecovery.py +0 -464
- tests/analyses/test_vfg.py +0 -221
- tests/analyses/test_vtable.py +0 -31
- tests/analyses/test_xrefs.py +0 -77
- tests/common.py +0 -128
- tests/engines/__init__.py +0 -0
- tests/engines/light/__init__.py +0 -0
- tests/engines/light/test_data.py +0 -17
- tests/engines/pcode/__init__.py +0 -0
- tests/engines/pcode/test_emulate.py +0 -607
- tests/engines/pcode/test_pcode.py +0 -84
- tests/engines/test_actions.py +0 -27
- tests/engines/test_hook.py +0 -112
- tests/engines/test_java.py +0 -697
- tests/engines/test_unicorn.py +0 -518
- tests/engines/vex/__init__.py +0 -0
- tests/engines/vex/test_lifter.py +0 -124
- tests/engines/vex/test_vex.py +0 -574
- tests/exploration_techniques/__init__.py +0 -0
- tests/exploration_techniques/test_cacher.py +0 -45
- tests/exploration_techniques/test_director.py +0 -67
- tests/exploration_techniques/test_driller_core.py +0 -48
- tests/exploration_techniques/test_loop_seer.py +0 -158
- tests/exploration_techniques/test_memory_watcher.py +0 -46
- tests/exploration_techniques/test_oppologist.py +0 -65
- tests/exploration_techniques/test_spiller.py +0 -82
- tests/exploration_techniques/test_stochastic.py +0 -40
- tests/exploration_techniques/test_tech_builder.py +0 -61
- tests/exploration_techniques/test_tracer.py +0 -856
- tests/exploration_techniques/test_unique.py +0 -40
- tests/exploration_techniques/test_veritesting.py +0 -120
- tests/factory/__init__.py +0 -0
- tests/factory/block/__init__.py +0 -0
- tests/factory/block/test_block_cache.py +0 -33
- tests/factory/block/test_keystone.py +0 -106
- tests/factory/test_argc.py +0 -101
- tests/factory/test_argc_sym.py +0 -110
- tests/factory/test_argv.py +0 -158
- tests/factory/test_callable.py +0 -266
- tests/factory/test_windows_args.py +0 -36
- tests/knowledge_plugins/__init__.py +0 -0
- tests/knowledge_plugins/cfg/__init__.py +0 -0
- tests/knowledge_plugins/cfg/test_cfg_manager.py +0 -36
- tests/knowledge_plugins/functions/__init__.py +0 -0
- tests/knowledge_plugins/functions/test_function.py +0 -91
- tests/knowledge_plugins/functions/test_function2.py +0 -79
- tests/knowledge_plugins/functions/test_function_manager.py +0 -139
- tests/knowledge_plugins/functions/test_prototypes.py +0 -53
- tests/knowledge_plugins/key_definitions/__init__.py +0 -0
- tests/knowledge_plugins/key_definitions/test_atoms.py +0 -24
- tests/knowledge_plugins/key_definitions/test_environment.py +0 -126
- tests/knowledge_plugins/key_definitions/test_heap_address.py +0 -27
- tests/knowledge_plugins/key_definitions/test_live_definitions.py +0 -72
- tests/knowledge_plugins/test_dwarf_variables.py +0 -240
- tests/knowledge_plugins/test_kb_plugins.py +0 -91
- tests/knowledge_plugins/test_kb_plugins_dwarf.py +0 -36
- tests/knowledge_plugins/test_patches.py +0 -48
- tests/misc/__init__.py +0 -0
- tests/misc/test_hookset.py +0 -57
- tests/perf/__init__.py +0 -0
- tests/perf/perf_cfgemulated.py +0 -19
- tests/perf/perf_cfgfast.py +0 -18
- tests/perf/perf_concrete_execution.py +0 -41
- tests/perf/perf_siminspect_nop.py +0 -36
- tests/perf/perf_state_copy.py +0 -33
- tests/perf/perf_unicorn_0.py +0 -27
- tests/perf/perf_unicorn_1.py +0 -23
- tests/procedures/__init__.py +0 -0
- tests/procedures/glibc/__init__.py +0 -0
- tests/procedures/glibc/test_ctype_locale.py +0 -164
- tests/procedures/libc/__init__.py +0 -0
- tests/procedures/libc/test_fgets.py +0 -53
- tests/procedures/libc/test_scanf.py +0 -205
- tests/procedures/libc/test_sprintf.py +0 -44
- tests/procedures/libc/test_sscanf.py +0 -63
- tests/procedures/libc/test_strcasecmp.py +0 -37
- tests/procedures/libc/test_string.py +0 -1102
- tests/procedures/libc/test_strtol.py +0 -78
- tests/procedures/linux_kernel/__init__.py +0 -0
- tests/procedures/linux_kernel/test_lseek.py +0 -174
- tests/procedures/posix/__init__.py +0 -0
- tests/procedures/posix/test_chroot.py +0 -33
- tests/procedures/posix/test_getenv.py +0 -78
- tests/procedures/posix/test_pwrite_pread.py +0 -57
- tests/procedures/posix/test_sim_time.py +0 -46
- tests/procedures/posix/test_unlink.py +0 -46
- tests/procedures/test_project_resolve_simproc.py +0 -43
- tests/procedures/test_sim_procedure.py +0 -117
- tests/procedures/test_stub_procedure_args.py +0 -53
- tests/serialization/__init__.py +0 -0
- tests/serialization/test_db.py +0 -197
- tests/serialization/test_pickle.py +0 -95
- tests/serialization/test_serialization.py +0 -132
- tests/serialization/test_vault.py +0 -169
- tests/sim/__init__.py +0 -3
- tests/sim/exec_func/__init__.py +0 -0
- tests/sim/exec_func/test_mem_funcs.py +0 -55
- tests/sim/exec_func/test_str_funcs.py +0 -93
- tests/sim/exec_func/test_syscall_result.py +0 -39
- tests/sim/exec_insn/__init__.py +0 -0
- tests/sim/exec_insn/test_adc.py +0 -44
- tests/sim/exec_insn/test_ops.py +0 -83
- tests/sim/exec_insn/test_rcr.py +0 -26
- tests/sim/exec_insn/test_rol.py +0 -51
- tests/sim/exec_insn/test_signed_div.py +0 -34
- tests/sim/exec_insn/test_sqrt.py +0 -56
- tests/sim/options/__init__.py +0 -0
- tests/sim/options/test_0div.py +0 -54
- tests/sim/options/test_symbolic_fd.py +0 -59
- tests/sim/options/test_unsupported.py +0 -34
- tests/sim/test_accuracy.py +0 -137
- tests/sim/test_checkbyte.py +0 -53
- tests/sim/test_echo.py +0 -36
- tests/sim/test_fauxware.py +0 -202
- tests/sim/test_self_modifying_code.py +0 -65
- tests/sim/test_simple_api.py +0 -36
- tests/sim/test_simulation_manager.py +0 -147
- tests/sim/test_stack_alignment.py +0 -65
- tests/sim/test_state.py +0 -303
- tests/sim/test_state_customization.py +0 -54
- tests/sim/test_symbol_hooked_by.py +0 -49
- tests/simos/__init__.py +0 -0
- tests/simos/windows/__init__.py +0 -0
- tests/simos/windows/test_windows_stack_cookie.py +0 -58
- tests/state_plugins/__init__.py +0 -0
- tests/state_plugins/inspect/__init__.py +0 -0
- tests/state_plugins/inspect/test_inspect.py +0 -310
- tests/state_plugins/inspect/test_syscall_override.py +0 -90
- tests/state_plugins/posix/__init__.py +0 -0
- tests/state_plugins/posix/test_file_struct_funcs.py +0 -56
- tests/state_plugins/posix/test_files.py +0 -69
- tests/state_plugins/posix/test_posix.py +0 -72
- tests/state_plugins/solver/__init__.py +0 -0
- tests/state_plugins/solver/test_simsolver.py +0 -58
- tests/state_plugins/solver/test_symbolic.py +0 -153
- tests/state_plugins/solver/test_variable_registration.py +0 -46
- tests/state_plugins/test_callstack.py +0 -54
- tests/state_plugins/test_gdb_plugin.py +0 -35
- tests/state_plugins/test_multi_open_file.py +0 -47
- tests/state_plugins/test_symbolization.py +0 -38
- tests/storage/__init__.py +0 -0
- tests/storage/test_memory.py +0 -960
- tests/storage/test_memory_merge.py +0 -114
- tests/storage/test_memview.py +0 -205
- tests/storage/test_mmap.py +0 -26
- tests/storage/test_multivalues.py +0 -44
- tests/storage/test_permissions.py +0 -32
- tests/storage/test_ptmalloc.py +0 -291
- tests/storage/test_relro_perm.py +0 -49
- tests/test_calling_conventions.py +0 -86
- tests/test_types.py +0 -329
- tests/utils/__init__.py +0 -0
- tests/utils/test_graph.py +0 -41
- {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/LICENSE +0 -0
- {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/WHEEL +0 -0
- {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/entry_points.txt +0 -0
angr/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# pylint: disable=wildcard-import
|
|
2
2
|
# pylint: disable=wrong-import-position
|
|
3
3
|
|
|
4
|
-
__version__ = "9.2.
|
|
4
|
+
__version__ = "9.2.89"
|
|
5
5
|
|
|
6
6
|
if bytes is str:
|
|
7
7
|
raise Exception(
|
|
@@ -88,6 +88,7 @@ from .state_plugins.heap import SimHeapBrk, SimHeapPTMalloc, PTChunk
|
|
|
88
88
|
from . import concretization_strategies
|
|
89
89
|
from .distributed import Server
|
|
90
90
|
from .knowledge_base import KnowledgeBase
|
|
91
|
+
from .procedures.definitions import load_external_definitions
|
|
91
92
|
|
|
92
93
|
# for compatibility reasons
|
|
93
94
|
from . import sim_manager as manager
|
|
@@ -95,6 +96,8 @@ from . import sim_manager as manager
|
|
|
95
96
|
# now that we have everything loaded, re-grab the list of loggers
|
|
96
97
|
loggers.load_all_loggers()
|
|
97
98
|
|
|
99
|
+
load_external_definitions()
|
|
100
|
+
|
|
98
101
|
__all__ = (
|
|
99
102
|
"SimProcedure",
|
|
100
103
|
"SIM_PROCEDURES",
|
|
@@ -638,6 +638,19 @@ class Clinic(Analysis):
|
|
|
638
638
|
return block_node
|
|
639
639
|
|
|
640
640
|
block = self.project.factory.block(block_node.addr, block_node.size, cross_insn_opt=False)
|
|
641
|
+
if block.vex.jumpkind not in {"Ijk_Call", "Ijk_Boring", "Ijk_Ret"} and not block.vex.jumpkind.startswith(
|
|
642
|
+
"Ijk_Sys"
|
|
643
|
+
):
|
|
644
|
+
# we don't support lifting this block. use a dummy block instead
|
|
645
|
+
statements = [
|
|
646
|
+
ailment.Stmt.DirtyStatement(
|
|
647
|
+
self._ail_manager.next_atom(),
|
|
648
|
+
f"Unsupported jumpkind {block.vex.jumpkind} at address {block_node.addr}",
|
|
649
|
+
ins_addr=block_node.addr,
|
|
650
|
+
)
|
|
651
|
+
]
|
|
652
|
+
ail_block = ailment.Block(block_node.addr, block_node.size, statements=statements)
|
|
653
|
+
return ail_block
|
|
641
654
|
|
|
642
655
|
ail_block = ailment.IRSBConverter.convert(block.vex, self._ail_manager)
|
|
643
656
|
return ail_block
|
|
@@ -1140,6 +1153,7 @@ class Clinic(Analysis):
|
|
|
1140
1153
|
try:
|
|
1141
1154
|
tp = self.project.analyses.Typehoon(
|
|
1142
1155
|
vr.type_constraints,
|
|
1156
|
+
vr.func_typevar,
|
|
1143
1157
|
kb=tmp_kb,
|
|
1144
1158
|
var_mapping=vr.var_to_typevars,
|
|
1145
1159
|
must_struct=must_struct,
|
|
@@ -1246,6 +1260,8 @@ class Clinic(Analysis):
|
|
|
1246
1260
|
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, ret_expr)
|
|
1247
1261
|
|
|
1248
1262
|
def _link_variables_on_call(self, variable_manager, global_variables, block, stmt_idx, stmt, is_expr=False):
|
|
1263
|
+
if not isinstance(stmt.target, ailment.Expr.Const):
|
|
1264
|
+
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.target)
|
|
1249
1265
|
if stmt.args:
|
|
1250
1266
|
for arg in stmt.args:
|
|
1251
1267
|
self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, arg)
|
|
@@ -349,6 +349,9 @@ class Decompiler(Analysis):
|
|
|
349
349
|
# only for post region id opts
|
|
350
350
|
if pass_.STAGE != OptimizationPassStage.DURING_REGION_IDENTIFICATION:
|
|
351
351
|
continue
|
|
352
|
+
if pass_.STRUCTURING:
|
|
353
|
+
if self._recursive_structurer_params["structurer_cls"].NAME not in pass_.STRUCTURING:
|
|
354
|
+
continue
|
|
352
355
|
|
|
353
356
|
a = pass_(
|
|
354
357
|
self.func,
|
|
@@ -21,6 +21,7 @@ from .x86_gcc_getpc_simplifier import X86GccGetPcSimplifier
|
|
|
21
21
|
from .flip_boolean_cmp import FlipBooleanCmp
|
|
22
22
|
from .ret_deduplicator import ReturnDeduplicator
|
|
23
23
|
from .win_stack_canary_simplifier import WinStackCanarySimplifier
|
|
24
|
+
from .cross_jump_reverter import CrossJumpReverter
|
|
24
25
|
|
|
25
26
|
# order matters!
|
|
26
27
|
_all_optimization_passes = [
|
|
@@ -40,9 +41,13 @@ _all_optimization_passes = [
|
|
|
40
41
|
(ReturnDuplicator, True),
|
|
41
42
|
(LoweredSwitchSimplifier, False),
|
|
42
43
|
(ReturnDeduplicator, True),
|
|
44
|
+
(CrossJumpReverter, True),
|
|
43
45
|
(FlipBooleanCmp, True),
|
|
44
46
|
]
|
|
45
47
|
|
|
48
|
+
# these passes may duplicate code to remove gotos or improve the structure of the graph
|
|
49
|
+
DUPLICATING_OPTS = [ReturnDuplicator, CrossJumpReverter]
|
|
50
|
+
|
|
46
51
|
|
|
47
52
|
def get_optimization_passes(arch, platform):
|
|
48
53
|
if isinstance(arch, Arch):
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from itertools import count
|
|
3
|
+
import copy
|
|
4
|
+
import logging
|
|
5
|
+
import inspect
|
|
6
|
+
|
|
7
|
+
from .optimization_pass import OptimizationPassStage, StructuringOptimizationPass
|
|
8
|
+
from ..call_counter import AILBlockCallCounter
|
|
9
|
+
|
|
10
|
+
l = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CrossJumpReverter(StructuringOptimizationPass):
|
|
14
|
+
"""
|
|
15
|
+
This is an implementation to revert the compiler optimization Cross Jumping, and ISC optimization discussed
|
|
16
|
+
in the USENIX 2024 paper SAILR. This optimization is somewhat aggressive and as such should be run last in your
|
|
17
|
+
decompiler deoptimization chain. This deoptimization will take any goto it finds and attempt to duplicate its
|
|
18
|
+
target block if its target only has one outgoing edge.
|
|
19
|
+
|
|
20
|
+
There are some heuristics in place to prevent duplication everywhere. First, this deoptimization will only run
|
|
21
|
+
a max of max_opt_iters times. Second, it will not duplicate a block with too many calls.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
ARCHES = None
|
|
25
|
+
PLATFORMS = None
|
|
26
|
+
STAGE = OptimizationPassStage.DURING_REGION_IDENTIFICATION
|
|
27
|
+
NAME = "Duplicate linear blocks with gotos"
|
|
28
|
+
STRUCTURING = ["phoenix"]
|
|
29
|
+
DESCRIPTION = inspect.cleandoc(__doc__).strip()
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
func,
|
|
34
|
+
# internal parameters that should be used by Clinic
|
|
35
|
+
node_idx_start: int = 0,
|
|
36
|
+
# settings
|
|
37
|
+
max_opt_iters: int = 3,
|
|
38
|
+
max_call_duplications: int = 2,
|
|
39
|
+
**kwargs,
|
|
40
|
+
):
|
|
41
|
+
super().__init__(func, max_opt_iters=max_opt_iters, strictly_less_gotos=True, **kwargs)
|
|
42
|
+
|
|
43
|
+
self.node_idx = count(start=node_idx_start)
|
|
44
|
+
self._max_call_dup = max_call_duplications
|
|
45
|
+
self.analyze()
|
|
46
|
+
|
|
47
|
+
def _check(self):
|
|
48
|
+
return True, None
|
|
49
|
+
|
|
50
|
+
def _analyze(self, cache=None):
|
|
51
|
+
to_update = defaultdict(list)
|
|
52
|
+
for node in self.out_graph.nodes:
|
|
53
|
+
gotos = self._goto_manager.gotos_in_block(node)
|
|
54
|
+
# TODO: support if-stmts
|
|
55
|
+
if not gotos or len(gotos) >= 2:
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
# only blocks that have a single outgoing goto are candidates
|
|
59
|
+
# for duplicates
|
|
60
|
+
goto = list(gotos)[0]
|
|
61
|
+
for goto_target in self.out_graph.successors(node):
|
|
62
|
+
if goto_target.addr == goto.dst_addr:
|
|
63
|
+
break
|
|
64
|
+
else:
|
|
65
|
+
goto_target = None
|
|
66
|
+
|
|
67
|
+
# the target goto block should only have a single outgoing edge
|
|
68
|
+
# this prevents duplication of conditions
|
|
69
|
+
if goto_target is None or self.out_graph.out_degree(goto_target) != 1:
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
# minimize the number of calls in the target block that can be duplicated
|
|
73
|
+
# to prevent duplication of big blocks
|
|
74
|
+
counter = AILBlockCallCounter()
|
|
75
|
+
counter.walk(goto_target)
|
|
76
|
+
if counter.calls > self._max_call_dup:
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
# [goto_target] = (pred1, pred2, ...)
|
|
80
|
+
to_update[goto_target].append(node)
|
|
81
|
+
|
|
82
|
+
if not to_update:
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
updates = False
|
|
86
|
+
sorted_targets = sorted(to_update.items(), key=lambda x: x[0].addr)
|
|
87
|
+
for goto_target, pred_to_update in sorted_targets:
|
|
88
|
+
pred_to_update = sorted(pred_to_update, key=lambda x: x.addr)
|
|
89
|
+
# do some sanity checks
|
|
90
|
+
update_edges = [(pred, goto_target) for pred in pred_to_update]
|
|
91
|
+
if not all(self.out_graph.has_edge(*edge) for edge in update_edges):
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
current_preds = list(self.out_graph.predecessors(goto_target))
|
|
95
|
+
delete_original = len(current_preds) == len(pred_to_update)
|
|
96
|
+
|
|
97
|
+
# update the edges
|
|
98
|
+
for src, goto_blk in update_edges:
|
|
99
|
+
cp = copy.deepcopy(goto_blk)
|
|
100
|
+
cp.idx = next(self.node_idx)
|
|
101
|
+
self.out_graph.remove_edge(src, goto_blk)
|
|
102
|
+
self.out_graph.add_edge(src, cp)
|
|
103
|
+
|
|
104
|
+
updates = True
|
|
105
|
+
if delete_original:
|
|
106
|
+
self.out_graph.remove_node(goto_target)
|
|
107
|
+
|
|
108
|
+
return updates
|
|
@@ -249,10 +249,18 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
249
249
|
STAGE = OptimizationPassStage.DURING_REGION_IDENTIFICATION
|
|
250
250
|
|
|
251
251
|
def __init__(
|
|
252
|
-
self,
|
|
252
|
+
self,
|
|
253
|
+
func,
|
|
254
|
+
prevent_new_gotos=True,
|
|
255
|
+
strictly_less_gotos=False,
|
|
256
|
+
recover_structure_fails=True,
|
|
257
|
+
max_opt_iters=1,
|
|
258
|
+
simplify_ail=True,
|
|
259
|
+
**kwargs,
|
|
253
260
|
):
|
|
254
261
|
super().__init__(func, **kwargs)
|
|
255
262
|
self._prevent_new_gotos = prevent_new_gotos
|
|
263
|
+
self._strictly_less_gotos = strictly_less_gotos
|
|
256
264
|
self._recover_structure_fails = recover_structure_fails
|
|
257
265
|
self._max_opt_iters = max_opt_iters
|
|
258
266
|
self._simplify_ail = simplify_ail
|
|
@@ -298,9 +306,14 @@ class StructuringOptimizationPass(OptimizationPass):
|
|
|
298
306
|
# this should not (TM) change the structure of the graph but is needed for later optimizations
|
|
299
307
|
self.out_graph = self._simplify_ail_graph(self.out_graph)
|
|
300
308
|
|
|
301
|
-
if self._prevent_new_gotos
|
|
302
|
-
|
|
303
|
-
|
|
309
|
+
if self._prevent_new_gotos:
|
|
310
|
+
prev_gotos = len(initial_gotos)
|
|
311
|
+
new_gotos = len(self._goto_manager.gotos)
|
|
312
|
+
if (self._strictly_less_gotos and (new_gotos >= prev_gotos)) or (
|
|
313
|
+
not self._strictly_less_gotos and (new_gotos > prev_gotos)
|
|
314
|
+
):
|
|
315
|
+
self.out_graph = None
|
|
316
|
+
return
|
|
304
317
|
|
|
305
318
|
def _fixed_point_analyze(self, cache=None):
|
|
306
319
|
for _ in range(self._max_opt_iters):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Tuple, Dict, List
|
|
1
|
+
from typing import Any, Tuple, Dict, List
|
|
2
2
|
from itertools import count
|
|
3
3
|
import copy
|
|
4
4
|
import logging
|
|
@@ -8,38 +8,18 @@ import ailment.expression
|
|
|
8
8
|
import networkx
|
|
9
9
|
|
|
10
10
|
from ailment import Block
|
|
11
|
-
from ailment.statement import Jump, ConditionalJump, Assignment,
|
|
11
|
+
from ailment.statement import Jump, ConditionalJump, Assignment, Return, Label
|
|
12
12
|
from ailment.expression import Const
|
|
13
|
-
from ailment.block_walker import AILBlockWalkerBase
|
|
14
13
|
|
|
15
14
|
from .optimization_pass import StructuringOptimizationPass
|
|
16
15
|
from ..condition_processor import ConditionProcessor, EmptyBlockNotice
|
|
17
16
|
from ..graph_region import GraphRegion
|
|
18
|
-
from ..utils import remove_labels, to_ail_supergraph
|
|
17
|
+
from ..utils import remove_labels, to_ail_supergraph, calls_in_graph
|
|
19
18
|
from ..structuring.structurer_nodes import MultiNode
|
|
20
19
|
|
|
21
|
-
if TYPE_CHECKING:
|
|
22
|
-
from ailment.statement import Call
|
|
23
|
-
|
|
24
20
|
_l = logging.getLogger(name=__name__)
|
|
25
21
|
|
|
26
22
|
|
|
27
|
-
class AILCallCounter(AILBlockWalkerBase):
|
|
28
|
-
"""
|
|
29
|
-
Helper class to count AIL Calls in a block.
|
|
30
|
-
"""
|
|
31
|
-
|
|
32
|
-
calls = 0
|
|
33
|
-
|
|
34
|
-
def _handle_Call(self, stmt_idx: int, stmt: "Call", block: Optional["Block"]):
|
|
35
|
-
self.calls += 1
|
|
36
|
-
super()._handle_Call(stmt_idx, stmt, block)
|
|
37
|
-
|
|
38
|
-
def _handle_CallExpr(self, expr_idx: int, expr: "Call", stmt_idx: int, stmt: Statement, block: Optional[Block]):
|
|
39
|
-
self.calls += 1
|
|
40
|
-
super()._handle_CallExpr(expr_idx, expr, stmt_idx, stmt, block)
|
|
41
|
-
|
|
42
|
-
|
|
43
23
|
class ReturnDuplicator(StructuringOptimizationPass):
|
|
44
24
|
"""
|
|
45
25
|
An optimization pass that reverts a subset of Irreducible Statement Condensing (ISC) optimizations, as described
|
|
@@ -207,7 +187,7 @@ class ReturnDuplicator(StructuringOptimizationPass):
|
|
|
207
187
|
|
|
208
188
|
# to assure we are not copying like crazy, set a max amount of code (which is estimated in calls)
|
|
209
189
|
# that can be copied in a region
|
|
210
|
-
if
|
|
190
|
+
if calls_in_graph(region) > self._max_calls_in_region:
|
|
211
191
|
continue
|
|
212
192
|
|
|
213
193
|
end_node_regions[region_head] = in_edges, region
|
|
@@ -383,14 +363,6 @@ class ReturnDuplicator(StructuringOptimizationPass):
|
|
|
383
363
|
|
|
384
364
|
return valid_assignment
|
|
385
365
|
|
|
386
|
-
@staticmethod
|
|
387
|
-
def _number_of_calls_in(graph: networkx.DiGraph) -> int:
|
|
388
|
-
counter = AILCallCounter()
|
|
389
|
-
for node in graph.nodes:
|
|
390
|
-
counter.walk(node)
|
|
391
|
-
|
|
392
|
-
return counter.calls
|
|
393
|
-
|
|
394
366
|
@staticmethod
|
|
395
367
|
def _single_entry_region(graph, end_node) -> Tuple[networkx.DiGraph, Any]:
|
|
396
368
|
"""
|
|
@@ -472,7 +472,7 @@ class CFunction(CConstruct): # pylint:disable=abstract-method
|
|
|
472
472
|
yield indent_str, None
|
|
473
473
|
|
|
474
474
|
# pick the first cvariable
|
|
475
|
-
#
|
|
475
|
+
# picking any cvariable is enough since highlighting works on the unified variable
|
|
476
476
|
try:
|
|
477
477
|
cvariable = next(iter(cvar_and_vartypes))[0]
|
|
478
478
|
except StopIteration:
|
|
@@ -486,9 +486,19 @@ class CFunction(CConstruct): # pylint:disable=abstract-method
|
|
|
486
486
|
else:
|
|
487
487
|
name = str(variable)
|
|
488
488
|
|
|
489
|
-
# sort by number of occurrences
|
|
489
|
+
# sort by number of occurrences, with a preference of non-basic types
|
|
490
|
+
# TODO: The type selection should actually happen during variable unification
|
|
490
491
|
vartypes = [x[1] for x in cvar_and_vartypes]
|
|
492
|
+
nonprimitive_vartypes = [
|
|
493
|
+
vt for vt in vartypes if not isinstance(vt, (SimTypeChar, SimTypeInt, SimTypeFloat))
|
|
494
|
+
]
|
|
491
495
|
vartypes = list(dict.fromkeys(sorted(vartypes, key=vartypes.count, reverse=True)))
|
|
496
|
+
if nonprimitive_vartypes:
|
|
497
|
+
nonprimitive_vartypes = list(
|
|
498
|
+
dict.fromkeys(sorted(nonprimitive_vartypes, key=nonprimitive_vartypes.count, reverse=True))
|
|
499
|
+
)
|
|
500
|
+
vartypes.remove(nonprimitive_vartypes[0])
|
|
501
|
+
vartypes.insert(0, nonprimitive_vartypes[0])
|
|
492
502
|
|
|
493
503
|
for i, var_type in enumerate(vartypes):
|
|
494
504
|
if i == 0:
|
|
@@ -7,7 +7,9 @@ import logging
|
|
|
7
7
|
import networkx
|
|
8
8
|
|
|
9
9
|
import ailment
|
|
10
|
+
|
|
10
11
|
import angr
|
|
12
|
+
from .call_counter import AILBlockCallCounter
|
|
11
13
|
|
|
12
14
|
_l = logging.getLogger(__name__)
|
|
13
15
|
|
|
@@ -712,6 +714,17 @@ def decompile_functions(path, functions=None, structurer=None, catch_errors=Fals
|
|
|
712
714
|
return decompilation
|
|
713
715
|
|
|
714
716
|
|
|
717
|
+
def calls_in_graph(graph: networkx.DiGraph) -> int:
|
|
718
|
+
"""
|
|
719
|
+
Counts the number of calls in an graph full of AIL Blocks
|
|
720
|
+
"""
|
|
721
|
+
counter = AILBlockCallCounter()
|
|
722
|
+
for node in graph.nodes:
|
|
723
|
+
counter.walk(node)
|
|
724
|
+
|
|
725
|
+
return counter.calls
|
|
726
|
+
|
|
727
|
+
|
|
715
728
|
# delayed import
|
|
716
729
|
from .structuring.structurer_nodes import (
|
|
717
730
|
MultiNode,
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from typing import Set, List, Tuple, Optional, TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
import networkx
|
|
4
|
+
|
|
5
|
+
# FIXME: Remove the dependency on pyformlang
|
|
6
|
+
from pyformlang.finite_automaton import Epsilon, EpsilonNFA, State, Symbol
|
|
7
|
+
|
|
8
|
+
from angr.errors import AngrError
|
|
9
|
+
from .typevars import BaseLabel, Subtype
|
|
10
|
+
from .variance import Variance
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from pyformlang.finite_automaton import DeterministicFiniteAutomaton
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
START_STATE = State("START")
|
|
17
|
+
END_STATE = State("END")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EmptyEpsilonNFAError(AngrError):
|
|
21
|
+
"""
|
|
22
|
+
A notification exception generated when the epsilon NFA is empty.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DFAConstraintSolver:
|
|
27
|
+
"""
|
|
28
|
+
Implements a DFA-based graph solver.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def graph_to_epsilon_nfa(graph: networkx.DiGraph, starts: Set, ends: Set) -> EpsilonNFA:
|
|
33
|
+
enfa = EpsilonNFA()
|
|
34
|
+
|
|
35
|
+
# print("Converting graph to eNFA")
|
|
36
|
+
|
|
37
|
+
for src, dst, data in graph.edges(data=True):
|
|
38
|
+
if not data:
|
|
39
|
+
symbol = Epsilon()
|
|
40
|
+
else:
|
|
41
|
+
assert "label" in data
|
|
42
|
+
label, kind = data["label"]
|
|
43
|
+
symbol = Symbol((label, kind))
|
|
44
|
+
|
|
45
|
+
# print(src, "-----", symbol, "----->", dst)
|
|
46
|
+
enfa.add_transition(State(src), symbol, State(dst))
|
|
47
|
+
|
|
48
|
+
enfa.add_start_state(START_STATE)
|
|
49
|
+
|
|
50
|
+
for start in starts:
|
|
51
|
+
# print("Start transition", START_STATE, "----->", start)
|
|
52
|
+
enfa.add_transition(START_STATE, Symbol(start), State(start))
|
|
53
|
+
|
|
54
|
+
enfa.add_final_state(END_STATE)
|
|
55
|
+
for end in ends:
|
|
56
|
+
# print("End transition", end, "----->", END_STATE)
|
|
57
|
+
enfa.add_transition(State(end), Symbol(end), END_STATE)
|
|
58
|
+
|
|
59
|
+
# networkx.drawing.nx_pydot.write_dot(graph, "d:/enfa.dot")
|
|
60
|
+
|
|
61
|
+
if enfa.is_empty():
|
|
62
|
+
raise EmptyEpsilonNFAError()
|
|
63
|
+
return enfa
|
|
64
|
+
|
|
65
|
+
def generate_constraints_between(self, graph: networkx.DiGraph, starts: Set, ends: Set) -> Set:
|
|
66
|
+
epsilon_nfa = self.graph_to_epsilon_nfa(graph, starts, ends)
|
|
67
|
+
min_dfa: "DeterministicFiniteAutomaton" = epsilon_nfa.minimize()
|
|
68
|
+
dfa_graph: networkx.MultiDiGraph = min_dfa.to_networkx()
|
|
69
|
+
|
|
70
|
+
constraints = set()
|
|
71
|
+
|
|
72
|
+
for final_state in min_dfa.final_states:
|
|
73
|
+
for path in networkx.all_simple_edge_paths(dfa_graph, min_dfa.start_state, final_state):
|
|
74
|
+
path_labels = []
|
|
75
|
+
for src, dst, index in path:
|
|
76
|
+
d = dfa_graph.get_edge_data(src, dst)[index]
|
|
77
|
+
path_labels.append(d["label"])
|
|
78
|
+
|
|
79
|
+
start_node = path_labels[0]
|
|
80
|
+
end_node = path_labels[-1]
|
|
81
|
+
|
|
82
|
+
constraint = self._check_constraint(start_node, end_node, path_labels[1:-1])
|
|
83
|
+
if constraint is not None:
|
|
84
|
+
constraints.add(constraint)
|
|
85
|
+
|
|
86
|
+
return constraints
|
|
87
|
+
|
|
88
|
+
@staticmethod
|
|
89
|
+
def _check_constraint(src, dst, string: List[Tuple[BaseLabel, str]]) -> Optional[Subtype]:
|
|
90
|
+
forgets = []
|
|
91
|
+
recalls = []
|
|
92
|
+
for label, kind in string:
|
|
93
|
+
if kind == "forget":
|
|
94
|
+
forgets.append(label)
|
|
95
|
+
elif kind == "recall":
|
|
96
|
+
recalls.append(label)
|
|
97
|
+
|
|
98
|
+
lhs = src
|
|
99
|
+
rhs = dst
|
|
100
|
+
for recall in recalls:
|
|
101
|
+
lhs = lhs.recall(recall)
|
|
102
|
+
for forget in reversed(forgets):
|
|
103
|
+
rhs = rhs.recall(forget)
|
|
104
|
+
|
|
105
|
+
if lhs.variance == Variance.COVARIANT and rhs.variance == Variance.COVARIANT:
|
|
106
|
+
if lhs.typevar != rhs.typevar:
|
|
107
|
+
return Subtype(lhs.typevar, rhs.typevar)
|
|
108
|
+
return None
|
angr/analyses/typehoon/lifter.py
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from typing import Union, TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from ...sim_type import (
|
|
4
|
+
SimType,
|
|
5
|
+
SimTypeChar,
|
|
6
|
+
SimTypeShort,
|
|
7
|
+
SimTypeInt,
|
|
8
|
+
SimTypeLong,
|
|
9
|
+
SimTypeLongLong,
|
|
10
|
+
SimTypePointer,
|
|
11
|
+
SimStruct,
|
|
12
|
+
SimTypeArray,
|
|
13
|
+
)
|
|
14
|
+
from .typeconsts import BottomType, Int8, Int16, Int32, Int64, Pointer32, Pointer64, Struct, Array
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from .typeconsts import TypeConstant
|
|
3
18
|
|
|
4
19
|
|
|
5
20
|
class TypeLifter:
|
|
@@ -40,6 +55,21 @@ class TypeLifter:
|
|
|
40
55
|
return Pointer64(self.lift(ty.pts_to))
|
|
41
56
|
raise ValueError("Unsupported bits %s." % self.bits)
|
|
42
57
|
|
|
58
|
+
def _lift_SimStruct(self, ty: SimStruct) -> Union["TypeConstant", BottomType]:
|
|
59
|
+
converted_fields = {}
|
|
60
|
+
field_names = {}
|
|
61
|
+
ty_offsets = ty.offsets
|
|
62
|
+
for field_name, simtype in ty.fields.items():
|
|
63
|
+
if field_name not in ty_offsets:
|
|
64
|
+
return BottomType()
|
|
65
|
+
converted_fields[ty_offsets[field_name]] = self.lift(simtype)
|
|
66
|
+
field_names[ty_offsets[field_name]] = field_name
|
|
67
|
+
return Struct(fields=converted_fields, name=ty.name, field_names=field_names)
|
|
68
|
+
|
|
69
|
+
def _lift_SimTypeArray(self, ty: SimTypeArray) -> Array:
|
|
70
|
+
elem_type = self.lift(ty.elem_type)
|
|
71
|
+
return Array(elem_type, count=ty.length)
|
|
72
|
+
|
|
43
73
|
|
|
44
74
|
_mapping = {
|
|
45
75
|
SimTypeChar: TypeLifter._lift_SimTypeChar,
|
|
@@ -48,4 +78,6 @@ _mapping = {
|
|
|
48
78
|
SimTypeLong: TypeLifter._lift_SimTypeInt,
|
|
49
79
|
SimTypeLongLong: TypeLifter._lift_SimTypeLongLong,
|
|
50
80
|
SimTypePointer: TypeLifter._lift_SimTypePointer,
|
|
81
|
+
SimStruct: TypeLifter._lift_SimStruct,
|
|
82
|
+
SimTypeArray: TypeLifter._lift_SimTypeArray,
|
|
51
83
|
}
|