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
|
@@ -1,3336 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# pylint: disable=missing-class-docstring,no-self-use,line-too-long
|
|
3
|
-
__package__ = __package__ or "tests.analyses.decompiler" # pylint:disable=redefined-builtin
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
import os
|
|
7
|
-
import re
|
|
8
|
-
import unittest
|
|
9
|
-
from functools import wraps
|
|
10
|
-
|
|
11
|
-
from typing import List, Tuple
|
|
12
|
-
|
|
13
|
-
import angr
|
|
14
|
-
from angr.knowledge_plugins.variables.variable_manager import VariableManagerInternal
|
|
15
|
-
from angr.sim_type import SimTypeInt, SimTypePointer
|
|
16
|
-
from angr.analyses import (
|
|
17
|
-
VariableRecoveryFast,
|
|
18
|
-
CallingConventionAnalysis,
|
|
19
|
-
CompleteCallingConventionsAnalysis,
|
|
20
|
-
CFGFast,
|
|
21
|
-
Decompiler,
|
|
22
|
-
)
|
|
23
|
-
from angr.analyses.decompiler.optimization_passes.expr_op_swapper import OpDescriptor
|
|
24
|
-
from angr.analyses.decompiler.decompilation_options import get_structurer_option, PARAM_TO_OPTION
|
|
25
|
-
from angr.analyses.decompiler.structuring import STRUCTURER_CLASSES
|
|
26
|
-
from angr.analyses.decompiler.structuring.phoenix import MultiStmtExprMode
|
|
27
|
-
from angr.misc.testing import is_testing
|
|
28
|
-
from angr.utils.library import convert_cproto_to_py
|
|
29
|
-
|
|
30
|
-
from ...common import bin_location, slow_test
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
test_location = os.path.join(bin_location, "tests")
|
|
34
|
-
|
|
35
|
-
l = logging.Logger(__name__)
|
|
36
|
-
|
|
37
|
-
WORKER = is_testing or bool(
|
|
38
|
-
os.environ.get("WORKER", False)
|
|
39
|
-
) # this variable controls whether we print the decompilation code or not
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def set_decompiler_option(decompiler_options: List[Tuple], params: List[Tuple]) -> List[Tuple]:
|
|
43
|
-
if decompiler_options is None:
|
|
44
|
-
decompiler_options = []
|
|
45
|
-
|
|
46
|
-
for param, value in params:
|
|
47
|
-
for option in angr.analyses.decompiler.decompilation_options.options:
|
|
48
|
-
if param == option.param:
|
|
49
|
-
decompiler_options.append((option, value))
|
|
50
|
-
|
|
51
|
-
return decompiler_options
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def for_all_structuring_algos(func):
|
|
55
|
-
"""
|
|
56
|
-
A helper wrapper that wraps a unittest function that has an option for 'decompiler_options'.
|
|
57
|
-
This option MUST be used when calling the Decompiler interface for the effects of using all
|
|
58
|
-
structuring algorithms.
|
|
59
|
-
|
|
60
|
-
In the function its best to call your decompiler like so:
|
|
61
|
-
angr.analyses.Decompiler(f, cfg=..., options=decompiler_options)
|
|
62
|
-
"""
|
|
63
|
-
|
|
64
|
-
@wraps(func)
|
|
65
|
-
def _for_all_structuring_algos(*args, **kwargs):
|
|
66
|
-
orig_opts = kwargs.pop("decompiler_options", None) or []
|
|
67
|
-
ret_vals = []
|
|
68
|
-
structurer_option = get_structurer_option()
|
|
69
|
-
for structurer in STRUCTURER_CLASSES:
|
|
70
|
-
new_opts = orig_opts + [(structurer_option, structurer)]
|
|
71
|
-
ret_vals.append(func(*args, decompiler_options=new_opts, **kwargs))
|
|
72
|
-
|
|
73
|
-
return ret_vals
|
|
74
|
-
|
|
75
|
-
return _for_all_structuring_algos
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
def structuring_algo(algo: str):
|
|
79
|
-
def _structuring_algo(func):
|
|
80
|
-
@wraps(func)
|
|
81
|
-
def inner(*args, **kwargs):
|
|
82
|
-
orig_opts = kwargs.pop("decompiler_options", None) or []
|
|
83
|
-
ret_vals = []
|
|
84
|
-
structurer_option = get_structurer_option()
|
|
85
|
-
new_opts = orig_opts + [(structurer_option, algo)]
|
|
86
|
-
ret_vals.append(func(*args, decompiler_options=new_opts, **kwargs))
|
|
87
|
-
return ret_vals
|
|
88
|
-
|
|
89
|
-
return inner
|
|
90
|
-
|
|
91
|
-
return _structuring_algo
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
class TestDecompiler(unittest.TestCase):
|
|
95
|
-
def _print_decompilation_result(self, dec):
|
|
96
|
-
if not WORKER:
|
|
97
|
-
print("Decompilation result:")
|
|
98
|
-
print(dec.codegen.text)
|
|
99
|
-
|
|
100
|
-
@for_all_structuring_algos
|
|
101
|
-
def test_decompiling_all_x86_64(self, decompiler_options=None):
|
|
102
|
-
bin_path = os.path.join(test_location, "x86_64", "all")
|
|
103
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
104
|
-
|
|
105
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
106
|
-
for f in cfg.functions.values():
|
|
107
|
-
if f.is_simprocedure:
|
|
108
|
-
l.debug("Skipping SimProcedure %s.", repr(f))
|
|
109
|
-
continue
|
|
110
|
-
p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
111
|
-
# FIXME: This test does not pass
|
|
112
|
-
# assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
113
|
-
# self._print_decompilation_result(dec)
|
|
114
|
-
|
|
115
|
-
@for_all_structuring_algos
|
|
116
|
-
def test_decompiling_babypwn_i386(self, decompiler_options=None):
|
|
117
|
-
bin_path = os.path.join(test_location, "i386", "decompiler", "codegate2017_babypwn")
|
|
118
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
119
|
-
|
|
120
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
121
|
-
p.analyses[CompleteCallingConventionsAnalysis].prep()(recover_variables=True)
|
|
122
|
-
for f in cfg.functions.values():
|
|
123
|
-
if f.is_simprocedure:
|
|
124
|
-
l.debug("Skipping SimProcedure %s.", repr(f))
|
|
125
|
-
continue
|
|
126
|
-
if f.addr not in (0x8048A71, 0x8048C6B):
|
|
127
|
-
continue
|
|
128
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
129
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
130
|
-
self._print_decompilation_result(dec)
|
|
131
|
-
|
|
132
|
-
@structuring_algo("dream")
|
|
133
|
-
def test_decompiling_loop_x86_64(self, decompiler_options=None):
|
|
134
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "loop")
|
|
135
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
136
|
-
|
|
137
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
138
|
-
f = cfg.functions["loop"]
|
|
139
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
140
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
141
|
-
self._print_decompilation_result(dec)
|
|
142
|
-
# it should be properly structured to a while loop with conditional breaks.
|
|
143
|
-
assert "break" in dec.codegen.text
|
|
144
|
-
|
|
145
|
-
@for_all_structuring_algos
|
|
146
|
-
def test_decompiling_all_i386(self, decompiler_options=None):
|
|
147
|
-
bin_path = os.path.join(test_location, "i386", "all")
|
|
148
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
149
|
-
|
|
150
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
151
|
-
|
|
152
|
-
f = cfg.functions["main"]
|
|
153
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
154
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
155
|
-
self._print_decompilation_result(dec)
|
|
156
|
-
|
|
157
|
-
@for_all_structuring_algos
|
|
158
|
-
def test_decompiling_aes_armel(self, decompiler_options=None):
|
|
159
|
-
# EDG Says: This binary is invalid.
|
|
160
|
-
# Consider replacing with some real firmware
|
|
161
|
-
bin_path = os.path.join(test_location, "armel", "aes")
|
|
162
|
-
# TODO: FIXME: EDG says: This binary is actually CortexM
|
|
163
|
-
# It is incorrectly linked. We override this here
|
|
164
|
-
p = angr.Project(bin_path, arch="ARMEL", auto_load_libs=False, load_debug_info=True)
|
|
165
|
-
|
|
166
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
167
|
-
|
|
168
|
-
f = cfg.functions["main"]
|
|
169
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
170
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
171
|
-
self._print_decompilation_result(dec)
|
|
172
|
-
|
|
173
|
-
@for_all_structuring_algos
|
|
174
|
-
def test_decompiling_mips_allcmps(self, decompiler_options=None):
|
|
175
|
-
bin_path = os.path.join(test_location, "mips", "allcmps")
|
|
176
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
177
|
-
|
|
178
|
-
cfg = p.analyses[CFGFast].prep()(collect_data_references=True, normalize=True)
|
|
179
|
-
|
|
180
|
-
f = cfg.functions["main"]
|
|
181
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
182
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
183
|
-
self._print_decompilation_result(dec)
|
|
184
|
-
|
|
185
|
-
@for_all_structuring_algos
|
|
186
|
-
def test_decompiling_linked_list(self, decompiler_options=None):
|
|
187
|
-
bin_path = os.path.join(test_location, "x86_64", "linked_list")
|
|
188
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
189
|
-
|
|
190
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
191
|
-
|
|
192
|
-
f = cfg.functions["sum"]
|
|
193
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
194
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
195
|
-
self._print_decompilation_result(dec)
|
|
196
|
-
|
|
197
|
-
@for_all_structuring_algos
|
|
198
|
-
def test_decompiling_dir_gcc_O0_free_ent(self, decompiler_options=None):
|
|
199
|
-
bin_path = os.path.join(test_location, "x86_64", "dir_gcc_-O0")
|
|
200
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
201
|
-
|
|
202
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True)
|
|
203
|
-
|
|
204
|
-
f = cfg.functions["free_ent"]
|
|
205
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
206
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
207
|
-
self._print_decompilation_result(dec)
|
|
208
|
-
|
|
209
|
-
@for_all_structuring_algos
|
|
210
|
-
def test_decompiling_dir_gcc_O0_main(self, decompiler_options=None):
|
|
211
|
-
# tests loop structuring
|
|
212
|
-
bin_path = os.path.join(test_location, "x86_64", "dir_gcc_-O0")
|
|
213
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
214
|
-
|
|
215
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True)
|
|
216
|
-
|
|
217
|
-
f = cfg.functions["main"]
|
|
218
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
219
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
220
|
-
self._print_decompilation_result(dec)
|
|
221
|
-
|
|
222
|
-
@for_all_structuring_algos
|
|
223
|
-
def test_decompiling_dir_gcc_O0_emit_ancillary_info(self, decompiler_options=None):
|
|
224
|
-
bin_path = os.path.join(test_location, "x86_64", "dir_gcc_-O0")
|
|
225
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
226
|
-
|
|
227
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True)
|
|
228
|
-
|
|
229
|
-
f = cfg.functions["emit_ancillary_info"]
|
|
230
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
231
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
232
|
-
self._print_decompilation_result(dec)
|
|
233
|
-
|
|
234
|
-
@for_all_structuring_algos
|
|
235
|
-
def test_decompiling_switch0_x86_64(self, decompiler_options=None):
|
|
236
|
-
bin_path = os.path.join(test_location, "x86_64", "switch_0")
|
|
237
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
238
|
-
|
|
239
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
240
|
-
|
|
241
|
-
f = cfg.functions["main"]
|
|
242
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
243
|
-
|
|
244
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
245
|
-
self._print_decompilation_result(dec)
|
|
246
|
-
code = dec.codegen.text
|
|
247
|
-
assert "switch" in code
|
|
248
|
-
assert "case 1:" in code
|
|
249
|
-
assert "case 2:" in code
|
|
250
|
-
assert "case 3:" in code
|
|
251
|
-
assert "case 4:" in code
|
|
252
|
-
assert "case 5:" in code
|
|
253
|
-
assert "case 6:" in code
|
|
254
|
-
assert "case 7:" in code
|
|
255
|
-
assert "default:" in code
|
|
256
|
-
|
|
257
|
-
@for_all_structuring_algos
|
|
258
|
-
def test_decompiling_switch1_x86_64(self, decompiler_options=None):
|
|
259
|
-
bin_path = os.path.join(test_location, "x86_64", "switch_1")
|
|
260
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
261
|
-
|
|
262
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
263
|
-
|
|
264
|
-
# disable eager returns simplifier
|
|
265
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
266
|
-
"AMD64", "linux"
|
|
267
|
-
)
|
|
268
|
-
all_optimization_passes = [
|
|
269
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
270
|
-
]
|
|
271
|
-
|
|
272
|
-
f = cfg.functions["main"]
|
|
273
|
-
dec = p.analyses[Decompiler].prep()(
|
|
274
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
275
|
-
)
|
|
276
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
277
|
-
self._print_decompilation_result(dec)
|
|
278
|
-
code = dec.codegen.text
|
|
279
|
-
assert "switch" in code
|
|
280
|
-
assert "case 1:" in code
|
|
281
|
-
assert "case 2:" in code
|
|
282
|
-
assert "case 3:" in code
|
|
283
|
-
assert "case 4:" in code
|
|
284
|
-
assert "case 5:" in code
|
|
285
|
-
assert "case 6:" in code
|
|
286
|
-
assert "case 7:" in code
|
|
287
|
-
assert "case 8:" in code
|
|
288
|
-
assert "default:" not in code
|
|
289
|
-
|
|
290
|
-
@for_all_structuring_algos
|
|
291
|
-
def test_decompiling_switch2_x86_64(self, decompiler_options=None):
|
|
292
|
-
bin_path = os.path.join(test_location, "x86_64", "switch_2")
|
|
293
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
294
|
-
|
|
295
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
296
|
-
|
|
297
|
-
# disable eager returns simplifier
|
|
298
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
299
|
-
"AMD64", "linux"
|
|
300
|
-
)
|
|
301
|
-
all_optimization_passes = [
|
|
302
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
303
|
-
]
|
|
304
|
-
|
|
305
|
-
f = cfg.functions["main"]
|
|
306
|
-
dec = p.analyses[Decompiler].prep()(
|
|
307
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
308
|
-
)
|
|
309
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
310
|
-
self._print_decompilation_result(dec)
|
|
311
|
-
code = dec.codegen.text
|
|
312
|
-
assert "switch" in code
|
|
313
|
-
assert "case 1:" in code
|
|
314
|
-
assert "case 2:" in code
|
|
315
|
-
assert "case 3:" in code
|
|
316
|
-
assert "case 4:" in code
|
|
317
|
-
assert "case 5:" in code
|
|
318
|
-
assert "case 6:" in code
|
|
319
|
-
assert "case 7:" in code
|
|
320
|
-
assert "case 8:" not in code
|
|
321
|
-
assert "default:" in code
|
|
322
|
-
|
|
323
|
-
assert code.count("break;") == 4
|
|
324
|
-
|
|
325
|
-
@for_all_structuring_algos
|
|
326
|
-
def test_decompiling_true_x86_64_0(self, decompiler_options=None):
|
|
327
|
-
# in fact this test case tests if CFGBase._process_jump_table_targeted_functions successfully removes "function"
|
|
328
|
-
# 0x402543, which is an artificial function that the compiler (GCC) created for identified "cold" functions.
|
|
329
|
-
|
|
330
|
-
bin_path = os.path.join(test_location, "x86_64", "true_ubuntu_2004")
|
|
331
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
332
|
-
|
|
333
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
334
|
-
|
|
335
|
-
# disable eager returns simplifier
|
|
336
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
337
|
-
"AMD64", "linux"
|
|
338
|
-
)
|
|
339
|
-
all_optimization_passes = [
|
|
340
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
341
|
-
]
|
|
342
|
-
|
|
343
|
-
f = cfg.functions[0x4048C0]
|
|
344
|
-
dec = p.analyses[Decompiler].prep()(
|
|
345
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
346
|
-
)
|
|
347
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
348
|
-
self._print_decompilation_result(dec)
|
|
349
|
-
code = dec.codegen.text
|
|
350
|
-
assert "switch" in code
|
|
351
|
-
assert "case" in code
|
|
352
|
-
|
|
353
|
-
@for_all_structuring_algos
|
|
354
|
-
def test_decompiling_true_x86_64_1(self, decompiler_options=None):
|
|
355
|
-
bin_path = os.path.join(test_location, "x86_64", "true_ubuntu_2004")
|
|
356
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
357
|
-
|
|
358
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
359
|
-
|
|
360
|
-
# disable eager returns simplifier
|
|
361
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
362
|
-
"AMD64", "linux"
|
|
363
|
-
)
|
|
364
|
-
all_optimization_passes = [
|
|
365
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
366
|
-
]
|
|
367
|
-
|
|
368
|
-
f = cfg.functions[0x404DC0]
|
|
369
|
-
dec = p.analyses[Decompiler].prep()(
|
|
370
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
371
|
-
)
|
|
372
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
373
|
-
self._print_decompilation_result(dec)
|
|
374
|
-
code: str = dec.codegen.text
|
|
375
|
-
|
|
376
|
-
# constant propagation was failing. see https://github.com/angr/angr/issues/2659
|
|
377
|
-
assert (
|
|
378
|
-
code.count("32 <=") == 0
|
|
379
|
-
and code.count("32 >") == 0
|
|
380
|
-
and code.count("((int)32) <=") == 0
|
|
381
|
-
and code.count("((int)32) >") == 0
|
|
382
|
-
)
|
|
383
|
-
if "*(&stack_base-56:32)" in code:
|
|
384
|
-
assert code.count("32") == 3
|
|
385
|
-
else:
|
|
386
|
-
assert code.count("32") == 2
|
|
387
|
-
|
|
388
|
-
@slow_test
|
|
389
|
-
@for_all_structuring_algos
|
|
390
|
-
def test_decompiling_true_a_x86_64_0(self, decompiler_options=None):
|
|
391
|
-
bin_path = os.path.join(test_location, "x86_64", "true_a")
|
|
392
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
393
|
-
|
|
394
|
-
cfg = p.analyses[CFGFast].prep(show_progressbar=not WORKER)(normalize=True, data_references=True)
|
|
395
|
-
|
|
396
|
-
# disable eager returns simplifier
|
|
397
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
398
|
-
"AMD64", "linux"
|
|
399
|
-
)
|
|
400
|
-
all_optimization_passes = [
|
|
401
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
402
|
-
]
|
|
403
|
-
|
|
404
|
-
f = cfg.functions[0x401E60]
|
|
405
|
-
dec = p.analyses[Decompiler].prep(show_progressbar=not WORKER)(
|
|
406
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
407
|
-
)
|
|
408
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
409
|
-
self._print_decompilation_result(dec)
|
|
410
|
-
|
|
411
|
-
assert dec.codegen.text.count("switch (") == 3 # there are three switch-cases in total
|
|
412
|
-
|
|
413
|
-
@for_all_structuring_algos
|
|
414
|
-
def test_decompiling_true_a_x86_64_1(self, decompiler_options=None):
|
|
415
|
-
bin_path = os.path.join(test_location, "x86_64", "true_a")
|
|
416
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=True)
|
|
417
|
-
|
|
418
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
419
|
-
|
|
420
|
-
# disable eager returns simplifier
|
|
421
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
422
|
-
"AMD64", "linux"
|
|
423
|
-
)
|
|
424
|
-
all_optimization_passes = [
|
|
425
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
426
|
-
]
|
|
427
|
-
|
|
428
|
-
f = cfg.functions[0x404410]
|
|
429
|
-
|
|
430
|
-
dec = p.analyses[Decompiler].prep()(
|
|
431
|
-
f,
|
|
432
|
-
cfg=cfg.model,
|
|
433
|
-
options=set_decompiler_option(decompiler_options, [("cstyle_ifs", False)]),
|
|
434
|
-
optimization_passes=all_optimization_passes,
|
|
435
|
-
)
|
|
436
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
437
|
-
self._print_decompilation_result(dec)
|
|
438
|
-
|
|
439
|
-
# the decompilation output should somewhat make sense
|
|
440
|
-
assert 'getenv("CHARSETALIASDIR");' in dec.codegen.text
|
|
441
|
-
assert "fscanf(" in dec.codegen.text
|
|
442
|
-
assert '"%50s %50s"' in dec.codegen.text
|
|
443
|
-
|
|
444
|
-
# make sure all "break;" is followed by a curly brace
|
|
445
|
-
dec_no_spaces = dec.codegen.text.replace("\n", "").replace(" ", "")
|
|
446
|
-
replaced = dec_no_spaces.replace("break;}", "")
|
|
447
|
-
assert "break" not in replaced
|
|
448
|
-
|
|
449
|
-
@for_all_structuring_algos
|
|
450
|
-
def test_decompiling_true_1804_x86_64(self, decompiler_options=None):
|
|
451
|
-
# true in Ubuntu 18.04, with -O2, has special optimizations that
|
|
452
|
-
# may mess up the way we structure loops and conditionals
|
|
453
|
-
|
|
454
|
-
bin_path = os.path.join(test_location, "x86_64", "true_ubuntu1804")
|
|
455
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
456
|
-
|
|
457
|
-
cfg = p.analyses.CFG(normalize=True, data_references=True)
|
|
458
|
-
|
|
459
|
-
f = cfg.functions["usage"]
|
|
460
|
-
dec = p.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options)
|
|
461
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
462
|
-
self._print_decompilation_result(dec)
|
|
463
|
-
|
|
464
|
-
@for_all_structuring_algos
|
|
465
|
-
def test_decompiling_true_mips64(self, decompiler_options=None):
|
|
466
|
-
bin_path = os.path.join(test_location, "mips64", "true")
|
|
467
|
-
p = angr.Project(bin_path, auto_load_libs=False, load_debug_info=False)
|
|
468
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
469
|
-
|
|
470
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
471
|
-
"MIPS64", "linux"
|
|
472
|
-
)
|
|
473
|
-
|
|
474
|
-
f = cfg.functions["main"]
|
|
475
|
-
dec = p.analyses[Decompiler].prep()(
|
|
476
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
477
|
-
)
|
|
478
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
479
|
-
self._print_decompilation_result(dec)
|
|
480
|
-
# make sure strings exist
|
|
481
|
-
assert '"coreutils"' in dec.codegen.text
|
|
482
|
-
assert '"/usr/local/share/locale"' in dec.codegen.text
|
|
483
|
-
assert '"--help"' in dec.codegen.text
|
|
484
|
-
assert '"Jim Meyering"' in dec.codegen.text
|
|
485
|
-
# make sure function calls exist
|
|
486
|
-
assert "set_program_name(" in dec.codegen.text
|
|
487
|
-
assert "setlocale(" in dec.codegen.text
|
|
488
|
-
assert "usage(0);" in dec.codegen.text
|
|
489
|
-
|
|
490
|
-
@for_all_structuring_algos
|
|
491
|
-
def test_decompiling_1after909_verify_password(self, decompiler_options=None):
|
|
492
|
-
bin_path = os.path.join(test_location, "x86_64", "1after909")
|
|
493
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
494
|
-
|
|
495
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
496
|
-
|
|
497
|
-
# verify_password
|
|
498
|
-
f = cfg.functions["verify_password"]
|
|
499
|
-
# recover calling convention
|
|
500
|
-
p.analyses[VariableRecoveryFast].prep()(f)
|
|
501
|
-
cca = p.analyses[CallingConventionAnalysis].prep()(f)
|
|
502
|
-
f.calling_convention = cca.cc
|
|
503
|
-
f.prototype = cca.prototype
|
|
504
|
-
dec = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
505
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
506
|
-
self._print_decompilation_result(dec)
|
|
507
|
-
|
|
508
|
-
code = dec.codegen.text
|
|
509
|
-
assert "stack_base" not in code, "Some stack variables are not recognized"
|
|
510
|
-
|
|
511
|
-
m = re.search(r"strncmp\(a1, \S+, 64\)", code)
|
|
512
|
-
assert m is not None
|
|
513
|
-
strncmp_expr = m.group(0)
|
|
514
|
-
strncmp_stmt = strncmp_expr + ";"
|
|
515
|
-
assert strncmp_stmt not in code, "Call expressions folding failed for strncmp()"
|
|
516
|
-
|
|
517
|
-
lines = code.split("\n")
|
|
518
|
-
for line in lines:
|
|
519
|
-
if '"%02x"' in line:
|
|
520
|
-
assert "sprintf(" in line
|
|
521
|
-
assert (
|
|
522
|
-
"v0" in line and "v1" in line and "v2" in line or "v2" in line and "v3" in line and "v4" in line
|
|
523
|
-
), "Failed to find v0, v1, and v2 in the same line. Is propagator over-propagating?"
|
|
524
|
-
|
|
525
|
-
assert "= sprintf" not in code, "Failed to remove the unused return value of sprintf()"
|
|
526
|
-
|
|
527
|
-
@for_all_structuring_algos
|
|
528
|
-
def test_decompiling_1after909_doit(self, decompiler_options=None):
|
|
529
|
-
# the doit() function has an abnormal loop at 0x1d47 - 0x1da1 - 0x1d73
|
|
530
|
-
|
|
531
|
-
bin_path = os.path.join(test_location, "x86_64", "1after909")
|
|
532
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
533
|
-
|
|
534
|
-
cfg = p.analyses[CFGFast].prep()(normalize=True, data_references=True)
|
|
535
|
-
|
|
536
|
-
# doit
|
|
537
|
-
f = cfg.functions["doit"]
|
|
538
|
-
optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
539
|
-
p.arch, p.simos.name
|
|
540
|
-
)
|
|
541
|
-
if angr.analyses.decompiler.optimization_passes.ReturnDuplicator not in optimization_passes:
|
|
542
|
-
optimization_passes += [
|
|
543
|
-
angr.analyses.decompiler.optimization_passes.ReturnDuplicator,
|
|
544
|
-
]
|
|
545
|
-
dec = p.analyses[Decompiler].prep()(
|
|
546
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=optimization_passes
|
|
547
|
-
)
|
|
548
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(f)
|
|
549
|
-
self._print_decompilation_result(dec)
|
|
550
|
-
|
|
551
|
-
code = dec.codegen.text
|
|
552
|
-
# with ReturnDuplicator applied, there should be no goto!
|
|
553
|
-
assert "goto" not in code.lower(), "Found goto statements. ReturnDuplicator might have failed."
|
|
554
|
-
# with global variables discovered, there should not be any loads of constant addresses.
|
|
555
|
-
assert "fflush(stdout);" in code.lower()
|
|
556
|
-
|
|
557
|
-
assert (
|
|
558
|
-
code.count("access(") == 2
|
|
559
|
-
), "The decompilation should contain 2 calls to access(), but instead %d calls are present." % code.count(
|
|
560
|
-
"access("
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
m = re.search(r"if \([\S]*access\(&[\S]+, [\S]+\) == -1\)", code)
|
|
564
|
-
assert m is not None, "The if branch at 0x401c91 is not found. Structurer is incorrectly removing conditionals."
|
|
565
|
-
|
|
566
|
-
# Arguments to the convert call should be fully folded into the call statement itself
|
|
567
|
-
code_lines = [line.strip(" ") for line in code.split("\n")]
|
|
568
|
-
for i, line in enumerate(code_lines):
|
|
569
|
-
if "convert(" in line:
|
|
570
|
-
# the previous line must be a curly brace
|
|
571
|
-
assert i > 0
|
|
572
|
-
assert (
|
|
573
|
-
code_lines[i - 1] == "{"
|
|
574
|
-
), "Some arguments to convert() are probably not folded into this call statement."
|
|
575
|
-
break
|
|
576
|
-
else:
|
|
577
|
-
assert False, "Call to convert() is not found in decompilation output."
|
|
578
|
-
|
|
579
|
-
# propagator should not replace stack variables
|
|
580
|
-
assert "free(v" in code
|
|
581
|
-
assert "free(NULL" not in code and "free(0" not in code
|
|
582
|
-
|
|
583
|
-
# return values are either 0xffffffff or -1
|
|
584
|
-
assert "return 4294967295;" in code or "return -1;" in code
|
|
585
|
-
|
|
586
|
-
# the while loop containing puts("Empty title"); must have both continue and break
|
|
587
|
-
for i, line in enumerate(code_lines):
|
|
588
|
-
if line == 'puts("Empty title");':
|
|
589
|
-
assert "break;" in code_lines[i - 9 : i + 9]
|
|
590
|
-
break
|
|
591
|
-
else:
|
|
592
|
-
assert False, "Did not find statement 'puts(\"Empty title\");'"
|
|
593
|
-
|
|
594
|
-
@slow_test
|
|
595
|
-
@for_all_structuring_algos
|
|
596
|
-
def test_decompiling_libsoap(self, decompiler_options=None):
|
|
597
|
-
bin_path = os.path.join(test_location, "armel", "libsoap.so")
|
|
598
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
599
|
-
|
|
600
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
601
|
-
|
|
602
|
-
func = cfg.functions[0x41D000]
|
|
603
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
604
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(func)
|
|
605
|
-
self._print_decompilation_result(dec)
|
|
606
|
-
|
|
607
|
-
@for_all_structuring_algos
|
|
608
|
-
def test_decompiling_no_arguments_in_variable_list(self, decompiler_options=None):
|
|
609
|
-
# function arguments should never appear in the variable list
|
|
610
|
-
bin_path = os.path.join(test_location, "x86_64", "test_arrays")
|
|
611
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
612
|
-
|
|
613
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
614
|
-
|
|
615
|
-
func = cfg.functions["main"]
|
|
616
|
-
|
|
617
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
618
|
-
assert dec.codegen is not None, "Failed to decompile function %s." % repr(func)
|
|
619
|
-
self._print_decompilation_result(dec)
|
|
620
|
-
self._print_decompilation_result(dec)
|
|
621
|
-
code = dec.codegen.text
|
|
622
|
-
decls = code.split("\n\n")[0]
|
|
623
|
-
|
|
624
|
-
argc_name = " a0" # update this variable once the decompiler picks up
|
|
625
|
-
# argument names from the common definition of main()
|
|
626
|
-
assert argc_name in decls
|
|
627
|
-
assert code.count(decls) == 1 # it should only appear once
|
|
628
|
-
|
|
629
|
-
def test_decompiling_strings_c_representation(self):
|
|
630
|
-
input_expected = [("""Foo"bar""", '"Foo\\"bar"'), ("""Foo'bar""", '"Foo\'bar"')]
|
|
631
|
-
|
|
632
|
-
for _input, expected in input_expected:
|
|
633
|
-
result = angr.analyses.decompiler.structured_codegen.c.CConstant.str_to_c_str(_input)
|
|
634
|
-
assert result == expected
|
|
635
|
-
|
|
636
|
-
@for_all_structuring_algos
|
|
637
|
-
def test_decompiling_strings_local_strlen(self, decompiler_options=None):
|
|
638
|
-
bin_path = os.path.join(test_location, "x86_64", "types", "strings")
|
|
639
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
640
|
-
|
|
641
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
642
|
-
func = cfg.functions["local_strlen"]
|
|
643
|
-
|
|
644
|
-
_ = p.analyses[VariableRecoveryFast].prep()(func)
|
|
645
|
-
cca = p.analyses[CallingConventionAnalysis].prep()(func, cfg=cfg.model)
|
|
646
|
-
func.calling_convention = cca.cc
|
|
647
|
-
func.prototype = cca.prototype
|
|
648
|
-
|
|
649
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
650
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
651
|
-
self._print_decompilation_result(dec)
|
|
652
|
-
|
|
653
|
-
code = dec.codegen.text
|
|
654
|
-
# Make sure argument a0 is correctly typed to char*
|
|
655
|
-
lines = code.split("\n")
|
|
656
|
-
assert "local_strlen(char *a0)" in lines[0], "Argument a0 seems to be incorrectly typed: %s" % lines[0]
|
|
657
|
-
|
|
658
|
-
@for_all_structuring_algos
|
|
659
|
-
def test_decompiling_strings_local_strcat(self, decompiler_options=None):
|
|
660
|
-
bin_path = os.path.join(test_location, "x86_64", "types", "strings")
|
|
661
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
662
|
-
|
|
663
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
664
|
-
func = cfg.functions["local_strcat"]
|
|
665
|
-
|
|
666
|
-
_ = p.analyses[VariableRecoveryFast].prep()(func)
|
|
667
|
-
cca = p.analyses[CallingConventionAnalysis].prep()(func, cfg=cfg.model)
|
|
668
|
-
func.calling_convention = cca.cc
|
|
669
|
-
func.prototype = cca.prototype
|
|
670
|
-
|
|
671
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
672
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
673
|
-
self._print_decompilation_result(dec)
|
|
674
|
-
|
|
675
|
-
code = dec.codegen.text
|
|
676
|
-
# Make sure argument a0 is correctly typed to char*
|
|
677
|
-
lines = code.split("\n")
|
|
678
|
-
assert "local_strcat(char *a0, char *a1)" in lines[0], (
|
|
679
|
-
"Argument a0 and a1 seem to be incorrectly typed: %s" % lines[0]
|
|
680
|
-
)
|
|
681
|
-
|
|
682
|
-
@for_all_structuring_algos
|
|
683
|
-
def test_decompiling_strings_local_strcat_with_local_strlen(self, decompiler_options=None):
|
|
684
|
-
bin_path = os.path.join(test_location, "x86_64", "types", "strings")
|
|
685
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
686
|
-
|
|
687
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
688
|
-
func_strlen = cfg.functions["local_strlen"]
|
|
689
|
-
_ = p.analyses[VariableRecoveryFast].prep()(func_strlen)
|
|
690
|
-
cca = p.analyses[CallingConventionAnalysis].prep()(func_strlen, cfg=cfg.model)
|
|
691
|
-
func_strlen.calling_convention = cca.cc
|
|
692
|
-
func_strlen.prototype = cca.prototype
|
|
693
|
-
p.analyses[Decompiler].prep()(func_strlen, cfg=cfg.model, options=decompiler_options)
|
|
694
|
-
|
|
695
|
-
func = cfg.functions["local_strcat"]
|
|
696
|
-
|
|
697
|
-
_ = p.analyses[VariableRecoveryFast].prep()(func)
|
|
698
|
-
cca = p.analyses[CallingConventionAnalysis].prep()(func, cfg=cfg.model)
|
|
699
|
-
func.calling_convention = cca.cc
|
|
700
|
-
func.prototype = cca.prototype
|
|
701
|
-
|
|
702
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
703
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
704
|
-
self._print_decompilation_result(dec)
|
|
705
|
-
|
|
706
|
-
code = dec.codegen.text
|
|
707
|
-
# Make sure argument a0 is correctly typed to char*
|
|
708
|
-
lines = code.split("\n")
|
|
709
|
-
assert "local_strcat(char *a0, char *a1)" in lines[0], (
|
|
710
|
-
"Argument a0 and a1 seem to be incorrectly typed: %s" % lines[0]
|
|
711
|
-
)
|
|
712
|
-
|
|
713
|
-
@for_all_structuring_algos
|
|
714
|
-
def test_decompilation_call_expr_folding(self, decompiler_options=None):
|
|
715
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "call_expr_folding")
|
|
716
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
717
|
-
|
|
718
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
719
|
-
|
|
720
|
-
func_0 = cfg.functions["strlen_should_fold"]
|
|
721
|
-
opt = [o for o in angr.analyses.decompiler.decompilation_options.options if o.param == "remove_dead_memdefs"][0]
|
|
722
|
-
opt_selection = [(opt, True)]
|
|
723
|
-
options = opt_selection if not decompiler_options else opt_selection + decompiler_options
|
|
724
|
-
dec = p.analyses[Decompiler].prep()(func_0, cfg=cfg.model, options=options)
|
|
725
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func_0
|
|
726
|
-
self._print_decompilation_result(dec)
|
|
727
|
-
|
|
728
|
-
code = dec.codegen.text
|
|
729
|
-
m = re.search(r"v(\d+) = (\(.*\))?strlen\(&v(\d+)\);", code) # e.g., s_428 = (int)strlen(&s_418);
|
|
730
|
-
assert m is not None, (
|
|
731
|
-
"The result of strlen() should be directly assigned to a stack "
|
|
732
|
-
"variable because of call-expression folding."
|
|
733
|
-
)
|
|
734
|
-
assert m.group(1) != m.group(2)
|
|
735
|
-
|
|
736
|
-
func_1 = cfg.functions["strlen_should_not_fold"]
|
|
737
|
-
dec = p.analyses[Decompiler].prep()(func_1, cfg=cfg.model, options=decompiler_options)
|
|
738
|
-
self._print_decompilation_result(dec)
|
|
739
|
-
code = dec.codegen.text
|
|
740
|
-
assert code.count("strlen(") == 1
|
|
741
|
-
|
|
742
|
-
func_2 = cfg.functions["strlen_should_not_fold_into_loop"]
|
|
743
|
-
dec = p.analyses[Decompiler].prep()(func_2, cfg=cfg.model, options=decompiler_options)
|
|
744
|
-
self._print_decompilation_result(dec)
|
|
745
|
-
code = dec.codegen.text
|
|
746
|
-
assert code.count("strlen(") == 1
|
|
747
|
-
|
|
748
|
-
@for_all_structuring_algos
|
|
749
|
-
def test_decompilation_call_expr_folding_mips64_true(self, decompiler_options=None):
|
|
750
|
-
# This test is to ensure call expression folding correctly replaces call expressions in return statements
|
|
751
|
-
bin_path = os.path.join(test_location, "mips64", "true")
|
|
752
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
753
|
-
|
|
754
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
755
|
-
|
|
756
|
-
func_0 = cfg.functions["version_etc"]
|
|
757
|
-
dec = p.analyses[Decompiler].prep()(func_0, cfg=cfg.model, options=decompiler_options)
|
|
758
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func_0
|
|
759
|
-
l.debug("Decompiled function %s\n%s", repr(func_0), dec.codegen.text)
|
|
760
|
-
|
|
761
|
-
code = dec.codegen.text
|
|
762
|
-
assert "version_etc_va(" in code
|
|
763
|
-
|
|
764
|
-
@for_all_structuring_algos
|
|
765
|
-
def test_decompilation_call_expr_folding_x8664_calc(self, decompiler_options=None):
|
|
766
|
-
# This test is to ensure call expression folding do not re-use out-dated definitions when folding expressions
|
|
767
|
-
bin_path = os.path.join(test_location, "x86_64", "calc")
|
|
768
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
769
|
-
|
|
770
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
771
|
-
# unfortunately we cannot correctly figure out the calling convention of "root" by just analyzing the call
|
|
772
|
-
# site... yet
|
|
773
|
-
p.analyses[CompleteCallingConventionsAnalysis].prep()(cfg=cfg.model, recover_variables=True)
|
|
774
|
-
|
|
775
|
-
func_0 = cfg.functions["main"]
|
|
776
|
-
dec = p.analyses[Decompiler].prep()(func_0, cfg=cfg.model, options=decompiler_options)
|
|
777
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func_0
|
|
778
|
-
self._print_decompilation_result(dec)
|
|
779
|
-
code = dec.codegen.text
|
|
780
|
-
|
|
781
|
-
assert "root(" in code
|
|
782
|
-
assert "strlen(" in code # incorrect call expression folding would
|
|
783
|
-
# fold root() into printf() and remove strlen()
|
|
784
|
-
assert "printf(" in code
|
|
785
|
-
|
|
786
|
-
lines = code.split("\n")
|
|
787
|
-
# make sure root() and strlen() appear within the same line
|
|
788
|
-
for line in lines:
|
|
789
|
-
if "root(" in line:
|
|
790
|
-
assert "strlen(" in line
|
|
791
|
-
assert line.count("strlen") == 1
|
|
792
|
-
|
|
793
|
-
@structuring_algo("phoenix")
|
|
794
|
-
def test_decompilation_call_expr_folding_into_if_conditions(self, decompiler_options=None):
|
|
795
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "stat.o")
|
|
796
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
797
|
-
|
|
798
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
799
|
-
|
|
800
|
-
f = proj.kb.functions["find_bind_mount"]
|
|
801
|
-
|
|
802
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
803
|
-
self._print_decompilation_result(d)
|
|
804
|
-
|
|
805
|
-
m = re.search(
|
|
806
|
-
r"if \([^\n]+ == 47 "
|
|
807
|
-
r"&& !strcmp\([^\n]+\) "
|
|
808
|
-
r"&& !stat\([^\n]+\) "
|
|
809
|
-
r"&& [^\n]+ == [^\n]+ "
|
|
810
|
-
r"&& [^\n]+ == [^\n]+\)",
|
|
811
|
-
d.codegen.text,
|
|
812
|
-
)
|
|
813
|
-
assert m is not None
|
|
814
|
-
|
|
815
|
-
@structuring_algo("phoenix")
|
|
816
|
-
def test_decompilation_stat_human_fstype(self, decompiler_options=None):
|
|
817
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "stat.o")
|
|
818
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
819
|
-
|
|
820
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
821
|
-
|
|
822
|
-
f = proj.kb.functions[0x401A70]
|
|
823
|
-
|
|
824
|
-
# enable Lowered Switch Simplifier
|
|
825
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
826
|
-
"AMD64", "linux"
|
|
827
|
-
)
|
|
828
|
-
all_optimization_passes.append(angr.analyses.decompiler.optimization_passes.LoweredSwitchSimplifier)
|
|
829
|
-
d = proj.analyses[Decompiler].prep()(
|
|
830
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
831
|
-
)
|
|
832
|
-
self._print_decompilation_result(d)
|
|
833
|
-
|
|
834
|
-
# we structure the giant if-else tree into a switch-case
|
|
835
|
-
assert "switch (" in d.codegen.text
|
|
836
|
-
assert "if (" not in d.codegen.text
|
|
837
|
-
|
|
838
|
-
@structuring_algo("phoenix")
|
|
839
|
-
def test_decompilation_stat_human_fstype_no_eager_returns(self, decompiler_options=None):
|
|
840
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "stat.o")
|
|
841
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
842
|
-
|
|
843
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
844
|
-
|
|
845
|
-
f = proj.kb.functions[0x401A70]
|
|
846
|
-
|
|
847
|
-
# disable eager returns simplifier
|
|
848
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
849
|
-
"AMD64", "linux"
|
|
850
|
-
)
|
|
851
|
-
all_optimization_passes = [
|
|
852
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
853
|
-
]
|
|
854
|
-
all_optimization_passes.append(angr.analyses.decompiler.optimization_passes.LoweredSwitchSimplifier)
|
|
855
|
-
d = proj.analyses[Decompiler].prep()(
|
|
856
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
857
|
-
)
|
|
858
|
-
self._print_decompilation_result(d)
|
|
859
|
-
|
|
860
|
-
# we structure the giant if-else tree into a switch-case
|
|
861
|
-
assert "switch (" in d.codegen.text
|
|
862
|
-
assert "break;" in d.codegen.text
|
|
863
|
-
assert "if (" not in d.codegen.text
|
|
864
|
-
|
|
865
|
-
@structuring_algo("phoenix")
|
|
866
|
-
def test_decompilation_stat_human_fstype_eager_returns_before_lowered_switch_simplifier(
|
|
867
|
-
self, decompiler_options=None
|
|
868
|
-
):
|
|
869
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "stat.o")
|
|
870
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
871
|
-
|
|
872
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
873
|
-
|
|
874
|
-
f = proj.kb.functions[0x401A70]
|
|
875
|
-
|
|
876
|
-
# enable Lowered Switch Simplifier
|
|
877
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
878
|
-
"AMD64", "linux"
|
|
879
|
-
)[::]
|
|
880
|
-
all_optimization_passes.append(angr.analyses.decompiler.optimization_passes.LoweredSwitchSimplifier)
|
|
881
|
-
d = proj.analyses[Decompiler].prep()(
|
|
882
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
883
|
-
)
|
|
884
|
-
self._print_decompilation_result(d)
|
|
885
|
-
|
|
886
|
-
# we structure the giant if-else tree into a switch-case
|
|
887
|
-
assert "switch (" in d.codegen.text
|
|
888
|
-
assert "break;" not in d.codegen.text # eager return has duplicated the switch-case successor. no break exists
|
|
889
|
-
assert "if (" not in d.codegen.text
|
|
890
|
-
|
|
891
|
-
@for_all_structuring_algos
|
|
892
|
-
def test_decompilation_excessive_condition_removal(self, decompiler_options=None):
|
|
893
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "bf")
|
|
894
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
895
|
-
|
|
896
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
897
|
-
|
|
898
|
-
func = cfg.functions[0x100003890]
|
|
899
|
-
|
|
900
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
901
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
902
|
-
self._print_decompilation_result(dec)
|
|
903
|
-
code = dec.codegen.text
|
|
904
|
-
|
|
905
|
-
code = code.replace(" ", "").replace("\n", "")
|
|
906
|
-
# s_1a += 1 should not be wrapped inside any if-statements. it is always reachable.
|
|
907
|
-
assert "}v4+=1;}" in code or "}v4+=0x1;}" in code
|
|
908
|
-
|
|
909
|
-
@for_all_structuring_algos
|
|
910
|
-
def test_decompilation_excessive_goto_removal(self, decompiler_options=None):
|
|
911
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "bf")
|
|
912
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
913
|
-
|
|
914
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
915
|
-
|
|
916
|
-
func = cfg.functions[0x100003890]
|
|
917
|
-
|
|
918
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
919
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
920
|
-
self._print_decompilation_result(dec)
|
|
921
|
-
|
|
922
|
-
code = dec.codegen.text
|
|
923
|
-
|
|
924
|
-
assert "goto" not in code
|
|
925
|
-
|
|
926
|
-
@for_all_structuring_algos
|
|
927
|
-
def test_decompilation_switch_case_structuring_with_removed_nodes(self, decompiler_options=None):
|
|
928
|
-
# Some jump table entries are fully folded into their successors. Structurer should be able to handle this case.
|
|
929
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "union")
|
|
930
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
931
|
-
|
|
932
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
933
|
-
|
|
934
|
-
func = cfg.functions["build_date"]
|
|
935
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
936
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
937
|
-
self._print_decompilation_result(dec)
|
|
938
|
-
code = dec.codegen.text
|
|
939
|
-
|
|
940
|
-
n = code.count("switch")
|
|
941
|
-
assert n == 2, f"Expect two switch-case constructs, only found {n} instead."
|
|
942
|
-
|
|
943
|
-
@for_all_structuring_algos
|
|
944
|
-
def test_decompilation_x86_64_stack_arguments(self, decompiler_options=None):
|
|
945
|
-
# Arguments passed on the stack should not go missing
|
|
946
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "union")
|
|
947
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
948
|
-
|
|
949
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
950
|
-
|
|
951
|
-
func = cfg.functions["build_date"]
|
|
952
|
-
|
|
953
|
-
# no dead memdef removal
|
|
954
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
955
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
956
|
-
self._print_decompilation_result(dec)
|
|
957
|
-
code = dec.codegen.text
|
|
958
|
-
|
|
959
|
-
lines = code.split("\n")
|
|
960
|
-
for line in lines:
|
|
961
|
-
if "snprintf" in line:
|
|
962
|
-
# The line should look like this:
|
|
963
|
-
# v0 = (int)snprintf(v32[8], (v43 + 0x1) * 0x2 + 0x1a, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT\r\n", &v34,
|
|
964
|
-
# ((long long)v35), &v33, ((long long)v36 + 1900), ((long long)v35), ((long long)v35),
|
|
965
|
-
# ((long long)v35));
|
|
966
|
-
assert line.count(",") == 10, "There is a missing stack argument."
|
|
967
|
-
break
|
|
968
|
-
else:
|
|
969
|
-
assert False, "The line with snprintf() is not found."
|
|
970
|
-
|
|
971
|
-
# with dead memdef removal
|
|
972
|
-
opt = [o for o in angr.analyses.decompiler.decompilation_options.options if o.param == "remove_dead_memdefs"][0]
|
|
973
|
-
# kill the cache since variables to statements won't match any more - variables are re-discovered with the new
|
|
974
|
-
# option.
|
|
975
|
-
p.kb.structured_code.cached.clear()
|
|
976
|
-
options = [(opt, True)] if not decompiler_options else [(opt, True)] + decompiler_options
|
|
977
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=options)
|
|
978
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
979
|
-
self._print_decompilation_result(dec)
|
|
980
|
-
code = dec.codegen.text
|
|
981
|
-
|
|
982
|
-
lines = code.split("\n")
|
|
983
|
-
for line in lines:
|
|
984
|
-
if "snprintf" in line:
|
|
985
|
-
# The line should look like this:
|
|
986
|
-
# v0 = (int)snprintf(v32[8], (v43 + 0x1) * 0x2 + 0x1a, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT\r\n", &v34,
|
|
987
|
-
# ((long long)v35), &v33, ((long long)v36 + 1900), ((long long)v35), ((long long)v35),
|
|
988
|
-
# ((long long)v35));
|
|
989
|
-
assert line.count(",") == 10, "There is a missing stack argument."
|
|
990
|
-
break
|
|
991
|
-
else:
|
|
992
|
-
assert False, "The line with snprintf() is not found."
|
|
993
|
-
|
|
994
|
-
@for_all_structuring_algos
|
|
995
|
-
def test_decompiling_amp_challenge03_arm(self, decompiler_options=None):
|
|
996
|
-
bin_path = os.path.join(test_location, "armhf", "decompiler", "challenge_03")
|
|
997
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
998
|
-
|
|
999
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1000
|
-
func = cfg.functions["main"]
|
|
1001
|
-
|
|
1002
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
1003
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1004
|
-
self._print_decompilation_result(dec)
|
|
1005
|
-
code = dec.codegen.text
|
|
1006
|
-
|
|
1007
|
-
# make sure there are no empty code blocks
|
|
1008
|
-
code = code.replace(" ", "").replace("\n", "")
|
|
1009
|
-
assert "{}" not in code, (
|
|
1010
|
-
"Found empty code blocks in decompilation output. This may indicate some "
|
|
1011
|
-
"assignments are incorrectly removed."
|
|
1012
|
-
)
|
|
1013
|
-
assert '"o"' in code and '"x"' in code, "CFG failed to recognize single-byte strings."
|
|
1014
|
-
|
|
1015
|
-
@for_all_structuring_algos
|
|
1016
|
-
def test_decompiling_amp_challenge03_arm_expr_swapping(self, decompiler_options=None):
|
|
1017
|
-
bin_path = os.path.join(test_location, "armhf", "decompiler", "challenge_03")
|
|
1018
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1019
|
-
|
|
1020
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1021
|
-
func = cfg.functions["main"]
|
|
1022
|
-
|
|
1023
|
-
binop_operators = {OpDescriptor(0x400A1D, 0, 0x400A27, "CmpGT"): "CmpLE"}
|
|
1024
|
-
dec = p.analyses[Decompiler].prep()(
|
|
1025
|
-
func, cfg=cfg.model, options=decompiler_options, binop_operators=binop_operators
|
|
1026
|
-
)
|
|
1027
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1028
|
-
self._print_decompilation_result(dec)
|
|
1029
|
-
code = dec.codegen.text
|
|
1030
|
-
|
|
1031
|
-
# make sure there are no empty code blocks
|
|
1032
|
-
lines = [line.strip(" ") for line in code.split("\n")]
|
|
1033
|
-
# v25 = select(v27, &stack_base-200, NULL, NULL, &v19);
|
|
1034
|
-
select_var = None
|
|
1035
|
-
select_line = None
|
|
1036
|
-
for idx, line in enumerate(lines):
|
|
1037
|
-
m = re.search(r"(v\d+) = select\(v", line)
|
|
1038
|
-
if m is not None:
|
|
1039
|
-
select_line = idx
|
|
1040
|
-
select_var = m.group(1)
|
|
1041
|
-
break
|
|
1042
|
-
|
|
1043
|
-
assert select_var, "Failed to find the variable that stores the result from select()"
|
|
1044
|
-
# if (0 <= v25)
|
|
1045
|
-
next_line = lines[select_line + 1]
|
|
1046
|
-
assert next_line.startswith(f"if (0 <= {select_var})")
|
|
1047
|
-
|
|
1048
|
-
@for_all_structuring_algos
|
|
1049
|
-
def test_decompiling_fauxware_mipsel(self, decompiler_options=None):
|
|
1050
|
-
bin_path = os.path.join(test_location, "mipsel", "fauxware")
|
|
1051
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1052
|
-
|
|
1053
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1054
|
-
func = cfg.functions["main"]
|
|
1055
|
-
|
|
1056
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
1057
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1058
|
-
self._print_decompilation_result(dec)
|
|
1059
|
-
code = dec.codegen.text
|
|
1060
|
-
|
|
1061
|
-
# The function calls must be correctly decompiled
|
|
1062
|
-
assert "puts(" in code
|
|
1063
|
-
assert "read(" in code
|
|
1064
|
-
assert "authenticate(" in code
|
|
1065
|
-
# The string references must be correctly recovered
|
|
1066
|
-
assert '"Username: "' in code
|
|
1067
|
-
assert '"Password: "' in code
|
|
1068
|
-
|
|
1069
|
-
@for_all_structuring_algos
|
|
1070
|
-
def test_stack_canary_removal_x8664_extra_exits(self, decompiler_options=None):
|
|
1071
|
-
# Test stack canary removal on functions with extra exit
|
|
1072
|
-
# nodes (e.g., assert(false);) without stack canary checks
|
|
1073
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "babyheap_level1_teaching1")
|
|
1074
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1075
|
-
|
|
1076
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1077
|
-
func = cfg.functions["main"]
|
|
1078
|
-
|
|
1079
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
1080
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1081
|
-
self._print_decompilation_result(dec)
|
|
1082
|
-
code = dec.codegen.text
|
|
1083
|
-
|
|
1084
|
-
# We should not find "__stack_chk_fail" in the code
|
|
1085
|
-
assert "__stack_chk_fail" not in code
|
|
1086
|
-
|
|
1087
|
-
@for_all_structuring_algos
|
|
1088
|
-
def test_ifelseif_x8664(self, decompiler_options=None):
|
|
1089
|
-
# nested if-else should be transformed to cascading if-elseif constructs
|
|
1090
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "babyheap_level1_teaching1")
|
|
1091
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1092
|
-
|
|
1093
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1094
|
-
func = cfg.functions["main"]
|
|
1095
|
-
|
|
1096
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
1097
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1098
|
-
self._print_decompilation_result(dec)
|
|
1099
|
-
code = dec.codegen.text
|
|
1100
|
-
|
|
1101
|
-
# it should make somewhat sense
|
|
1102
|
-
assert 'printf("[*] flag_buffer = malloc(%d)\\n",' in code
|
|
1103
|
-
|
|
1104
|
-
if decompiler_options and decompiler_options[-1][-1] == "dream":
|
|
1105
|
-
assert code.count("else if") == 3
|
|
1106
|
-
|
|
1107
|
-
@for_all_structuring_algos
|
|
1108
|
-
def test_decompiling_missing_function_call(self, decompiler_options=None):
|
|
1109
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "adams")
|
|
1110
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1111
|
-
|
|
1112
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1113
|
-
func = cfg.functions["main"]
|
|
1114
|
-
|
|
1115
|
-
dec = p.analyses[Decompiler].prep()(
|
|
1116
|
-
func,
|
|
1117
|
-
cfg=cfg.model,
|
|
1118
|
-
options=decompiler_options,
|
|
1119
|
-
)
|
|
1120
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1121
|
-
self._print_decompilation_result(dec)
|
|
1122
|
-
code = dec.codegen.text
|
|
1123
|
-
|
|
1124
|
-
# the call to fileno() should not go missing
|
|
1125
|
-
assert code.count("fileno") == 1
|
|
1126
|
-
|
|
1127
|
-
code_without_spaces = code.replace(" ", "").replace("\n", "")
|
|
1128
|
-
# make sure all break statements are followed by either "case " or "}"
|
|
1129
|
-
replaced = code_without_spaces.replace("break;case", "")
|
|
1130
|
-
replaced = replaced.replace("break;default:", "")
|
|
1131
|
-
replaced = replaced.replace("break;", "")
|
|
1132
|
-
assert "break" not in replaced
|
|
1133
|
-
|
|
1134
|
-
# ensure if-else removal does not incorrectly remove else nodes
|
|
1135
|
-
assert "emaillist=strdup(" in code_without_spaces
|
|
1136
|
-
|
|
1137
|
-
@for_all_structuring_algos
|
|
1138
|
-
def test_decompiling_morton_my_message_callback(self, decompiler_options=None):
|
|
1139
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "morton")
|
|
1140
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1141
|
-
|
|
1142
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1143
|
-
|
|
1144
|
-
func = cfg.functions["my_message_callback"]
|
|
1145
|
-
|
|
1146
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
1147
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1148
|
-
self._print_decompilation_result(dec)
|
|
1149
|
-
code = dec.codegen.text
|
|
1150
|
-
|
|
1151
|
-
# we should not propagate generate_random() calls into function arguments without removing the original call
|
|
1152
|
-
# statement.
|
|
1153
|
-
assert code.count("generate_random(") == 3
|
|
1154
|
-
# we should be able to correctly figure out all arguments for mosquitto_publish() by analyzing call sites
|
|
1155
|
-
assert code.count("mosquitto_publish()") == 0
|
|
1156
|
-
assert code.count("mosquitto_publish(") == 6
|
|
1157
|
-
|
|
1158
|
-
@for_all_structuring_algos
|
|
1159
|
-
def test_decompiling_morton_lib_handle__suback(self, decompiler_options=None):
|
|
1160
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "morton.libmosquitto.so.1")
|
|
1161
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1162
|
-
|
|
1163
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1164
|
-
|
|
1165
|
-
func = cfg.functions.function(name="handle__suback", plt=False)
|
|
1166
|
-
|
|
1167
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
1168
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1169
|
-
self._print_decompilation_result(dec)
|
|
1170
|
-
code = dec.codegen.text
|
|
1171
|
-
|
|
1172
|
-
assert "__stack_chk_fail" not in code # stack canary checks should be removed by default
|
|
1173
|
-
|
|
1174
|
-
@slow_test
|
|
1175
|
-
@for_all_structuring_algos
|
|
1176
|
-
def test_decompiling_newburry_main(self, decompiler_options=None):
|
|
1177
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "newbury")
|
|
1178
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1179
|
-
|
|
1180
|
-
cfg = p.analyses[CFGFast].prep(show_progressbar=not WORKER)(data_references=True, normalize=True)
|
|
1181
|
-
|
|
1182
|
-
func = cfg.functions["main"]
|
|
1183
|
-
|
|
1184
|
-
dec = p.analyses[Decompiler].prep(show_progressbar=not WORKER)(func, cfg=cfg.model, options=decompiler_options)
|
|
1185
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1186
|
-
self._print_decompilation_result(dec)
|
|
1187
|
-
code = dec.codegen.text
|
|
1188
|
-
|
|
1189
|
-
# return statements should not be wrapped into a for statement
|
|
1190
|
-
assert re.search(r"for[^\n]*return[^\n]*;", code) is None
|
|
1191
|
-
|
|
1192
|
-
@for_all_structuring_algos
|
|
1193
|
-
def test_single_instruction_loop(self, decompiler_options=None):
|
|
1194
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "level_12_teaching")
|
|
1195
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1196
|
-
|
|
1197
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
1198
|
-
|
|
1199
|
-
func = cfg.functions["main"]
|
|
1200
|
-
|
|
1201
|
-
dec = p.analyses[Decompiler].prep()(func, cfg=cfg.model, options=decompiler_options)
|
|
1202
|
-
assert dec.codegen is not None, "Failed to decompile function %r." % func
|
|
1203
|
-
self._print_decompilation_result(dec)
|
|
1204
|
-
code = dec.codegen.text
|
|
1205
|
-
|
|
1206
|
-
code_without_spaces = code.replace(" ", "").replace("\n", "")
|
|
1207
|
-
assert "while(true" not in code_without_spaces
|
|
1208
|
-
assert "for(" in code_without_spaces
|
|
1209
|
-
m = re.search(r"if\([^=]+==0\)", code_without_spaces)
|
|
1210
|
-
assert m is None
|
|
1211
|
-
|
|
1212
|
-
@for_all_structuring_algos
|
|
1213
|
-
def test_simple_strcpy(self, decompiler_options=None):
|
|
1214
|
-
"""
|
|
1215
|
-
Original C: while (( *dst++ = *src++ ));
|
|
1216
|
-
Ensures incremented src and dst are not accidentally used in copy statement.
|
|
1217
|
-
"""
|
|
1218
|
-
bin_path = os.path.join(test_location, "x86_64", "test_simple_strcpy")
|
|
1219
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1220
|
-
|
|
1221
|
-
cfg = p.analyses.CFGFast(normalize=True)
|
|
1222
|
-
|
|
1223
|
-
f = p.kb.functions["simple_strcpy"]
|
|
1224
|
-
d = p.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options)
|
|
1225
|
-
assert d.codegen is not None, "Failed to decompile function %r." % f
|
|
1226
|
-
self._print_decompilation_result(d)
|
|
1227
|
-
dw = d.codegen.cfunc.statements.statements[1]
|
|
1228
|
-
assert isinstance(dw, angr.analyses.decompiler.structured_codegen.c.CDoWhileLoop)
|
|
1229
|
-
stmts = dw.body.statements
|
|
1230
|
-
assert len(stmts) == 5
|
|
1231
|
-
assert stmts[1].lhs.unified_variable == stmts[0].rhs.unified_variable
|
|
1232
|
-
assert stmts[3].lhs.unified_variable == stmts[2].rhs.unified_variable
|
|
1233
|
-
assert stmts[4].lhs.operand.variable == stmts[2].lhs.variable
|
|
1234
|
-
assert stmts[4].rhs.operand.variable == stmts[0].lhs.variable
|
|
1235
|
-
assert dw.condition.lhs.operand.variable == stmts[2].lhs.variable
|
|
1236
|
-
|
|
1237
|
-
@for_all_structuring_algos
|
|
1238
|
-
def test_decompiling_nl_i386_pie(self, decompiler_options=None):
|
|
1239
|
-
bin_path = os.path.join(test_location, "i386", "nl")
|
|
1240
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1241
|
-
|
|
1242
|
-
cfg = p.analyses.CFGFast(normalize=True)
|
|
1243
|
-
|
|
1244
|
-
f = p.kb.functions["usage"]
|
|
1245
|
-
d = p.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options)
|
|
1246
|
-
assert d.codegen is not None, "Failed to decompile function %r." % f
|
|
1247
|
-
self._print_decompilation_result(d)
|
|
1248
|
-
|
|
1249
|
-
assert '"Usage: %s [OPTION]... [FILE]...\\n"' in d.codegen.text
|
|
1250
|
-
assert (
|
|
1251
|
-
'"Write each FILE to standard output, with line numbers added.\\nWith no FILE, or when FILE is -,'
|
|
1252
|
-
' read standard input.\\n\\n"' in d.codegen.text
|
|
1253
|
-
)
|
|
1254
|
-
assert "\"For complete documentation, run: info coreutils '%s invocation'\\n\"" in d.codegen.text
|
|
1255
|
-
|
|
1256
|
-
@unittest.skip("Disabled until https://github.com/angr/angr/issues/4406 fixed")
|
|
1257
|
-
@for_all_structuring_algos
|
|
1258
|
-
def test_decompiling_x8664_cvs(self, decompiler_options=None):
|
|
1259
|
-
# TODO: this is broken, but not shown in CI b/c slow, and tracked by https://github.com/angr/angr/issues/4406
|
|
1260
|
-
bin_path = os.path.join(test_location, "x86_64", "cvs")
|
|
1261
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1262
|
-
|
|
1263
|
-
cfg = p.analyses.CFGFast(normalize=True, show_progressbar=not WORKER)
|
|
1264
|
-
|
|
1265
|
-
f = p.kb.functions["main"]
|
|
1266
|
-
d = p.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options, show_progressbar=not WORKER)
|
|
1267
|
-
assert d.codegen is not None, "Failed to decompile function %r." % f
|
|
1268
|
-
self._print_decompilation_result(d)
|
|
1269
|
-
|
|
1270
|
-
# at the very least, it should decompile within a reasonable amount of time...
|
|
1271
|
-
# the switch-case must be recovered
|
|
1272
|
-
assert "switch (" in d.codegen.text
|
|
1273
|
-
|
|
1274
|
-
@for_all_structuring_algos
|
|
1275
|
-
def test_decompiling_short_circuit_O0_func_1(self, decompiler_options=None):
|
|
1276
|
-
bin_path = os.path.join(test_location, "x86_64", "short_circuit_O0")
|
|
1277
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1278
|
-
|
|
1279
|
-
cfg = p.analyses.CFGFast(normalize=True)
|
|
1280
|
-
|
|
1281
|
-
# disable return duplicator
|
|
1282
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
1283
|
-
"AMD64", "linux"
|
|
1284
|
-
)
|
|
1285
|
-
all_optimization_passes = [
|
|
1286
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
1287
|
-
]
|
|
1288
|
-
|
|
1289
|
-
f = p.kb.functions["func_1"]
|
|
1290
|
-
d = p.analyses[Decompiler].prep()(
|
|
1291
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
1292
|
-
)
|
|
1293
|
-
assert d.codegen is not None, "Failed to decompile function %r." % f
|
|
1294
|
-
self._print_decompilation_result(d)
|
|
1295
|
-
|
|
1296
|
-
assert "goto" not in d.codegen.text
|
|
1297
|
-
|
|
1298
|
-
@for_all_structuring_algos
|
|
1299
|
-
def test_decompiling_short_circuit_O0_func_2(self, decompiler_options=None):
|
|
1300
|
-
bin_path = os.path.join(test_location, "x86_64", "short_circuit_O0")
|
|
1301
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1302
|
-
|
|
1303
|
-
cfg = p.analyses.CFGFast(normalize=True)
|
|
1304
|
-
|
|
1305
|
-
# disable eager returns simplifier
|
|
1306
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
1307
|
-
"AMD64", "linux"
|
|
1308
|
-
)
|
|
1309
|
-
all_optimization_passes = [
|
|
1310
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
1311
|
-
]
|
|
1312
|
-
|
|
1313
|
-
f = p.kb.functions["func_2"]
|
|
1314
|
-
d = p.analyses[Decompiler].prep()(
|
|
1315
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
1316
|
-
)
|
|
1317
|
-
assert d.codegen is not None, "Failed to decompile function %r." % f
|
|
1318
|
-
self._print_decompilation_result(d)
|
|
1319
|
-
|
|
1320
|
-
assert "goto" not in d.codegen.text
|
|
1321
|
-
|
|
1322
|
-
@for_all_structuring_algos
|
|
1323
|
-
def test_decompiling_x8664_mv_O2(self, decompiler_options=None):
|
|
1324
|
-
bin_path = os.path.join(test_location, "x86_64", "mv_-O2")
|
|
1325
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1326
|
-
|
|
1327
|
-
cfg = p.analyses.CFGFast(normalize=True, show_progressbar=not WORKER)
|
|
1328
|
-
|
|
1329
|
-
f = p.kb.functions["main"]
|
|
1330
|
-
d = p.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options, show_progressbar=not WORKER)
|
|
1331
|
-
self._print_decompilation_result(d)
|
|
1332
|
-
|
|
1333
|
-
assert "(False)" not in d.codegen.text
|
|
1334
|
-
assert "None" not in d.codegen.text
|
|
1335
|
-
|
|
1336
|
-
@for_all_structuring_algos
|
|
1337
|
-
def test_extern_decl(self, decompiler_options=None):
|
|
1338
|
-
bin_path = os.path.join(test_location, "x86_64", "test_gdb_plugin")
|
|
1339
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
1340
|
-
|
|
1341
|
-
cfg = p.analyses.CFGFast(normalize=True)
|
|
1342
|
-
|
|
1343
|
-
f = p.kb.functions["set_globals"]
|
|
1344
|
-
d = p.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options)
|
|
1345
|
-
l.debug("Decompiled function %s\n%s", repr(f), d.codegen.text)
|
|
1346
|
-
|
|
1347
|
-
assert "extern unsigned int a;" in d.codegen.text
|
|
1348
|
-
assert "extern unsigned int b;" in d.codegen.text
|
|
1349
|
-
assert "extern unsigned int c;" in d.codegen.text
|
|
1350
|
-
|
|
1351
|
-
@for_all_structuring_algos
|
|
1352
|
-
def test_decompiling_amp_challenge_07(self, decompiler_options=None):
|
|
1353
|
-
bin_path = os.path.join(test_location, "armhf", "amp_challenge_07.gcc.dyn.unstripped")
|
|
1354
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1355
|
-
|
|
1356
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1357
|
-
|
|
1358
|
-
f = proj.kb.functions[0x401865]
|
|
1359
|
-
proj.analyses.VariableRecoveryFast(f)
|
|
1360
|
-
cca = proj.analyses.CallingConvention(f)
|
|
1361
|
-
f.prototype = cca.prototype
|
|
1362
|
-
f.calling_convention = cca.cc
|
|
1363
|
-
|
|
1364
|
-
d = proj.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options)
|
|
1365
|
-
self._print_decompilation_result(d)
|
|
1366
|
-
|
|
1367
|
-
# make sure the types of extern variables are correct
|
|
1368
|
-
assert "extern char num_connections;" in d.codegen.text
|
|
1369
|
-
assert "extern char num_packets;" in d.codegen.text
|
|
1370
|
-
assert "extern char src;" in d.codegen.text
|
|
1371
|
-
|
|
1372
|
-
# make sure there are no unidentified stack variables
|
|
1373
|
-
assert "stack_base" not in d.codegen.text
|
|
1374
|
-
|
|
1375
|
-
lines = [line.strip(" ") for line in d.codegen.text.split("\n")]
|
|
1376
|
-
|
|
1377
|
-
# make sure the line with printf("Recieved packet %d for connection with %d\n"...) does not have
|
|
1378
|
-
# "v23->field_5 + 1". otherwise it's an incorrect variable folding result
|
|
1379
|
-
line_0s = [line for line in lines if "printf(" in line and "Recieved packet %d for connection with %d" in line]
|
|
1380
|
-
assert len(line_0s) == 1
|
|
1381
|
-
line_0 = line_0s[0].replace(" ", "")
|
|
1382
|
-
assert "+1" not in line_0
|
|
1383
|
-
|
|
1384
|
-
# make sure v % 7 is present
|
|
1385
|
-
line_mod_7 = [line for line in lines if re.search(r"[^v]*v\d+[)]* % 7", line)]
|
|
1386
|
-
assert len(line_mod_7) == 1
|
|
1387
|
-
|
|
1388
|
-
# make sure all "connection_infos" are followed by a square bracket
|
|
1389
|
-
# we don't allow bizarre expressions like (&connection_infos)[1234]...
|
|
1390
|
-
assert "connection_infos" in d.codegen.text
|
|
1391
|
-
for line in lines:
|
|
1392
|
-
for m in re.finditer(r"connection_infos", line):
|
|
1393
|
-
assert line[m.end()] == "["
|
|
1394
|
-
|
|
1395
|
-
@for_all_structuring_algos
|
|
1396
|
-
def test_decompiling_fmt_put_space(self, decompiler_options=None):
|
|
1397
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "fmt")
|
|
1398
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1399
|
-
|
|
1400
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1401
|
-
|
|
1402
|
-
f = proj.kb.functions["put_space"]
|
|
1403
|
-
assert f.info.get("bp_as_gpr", False) is True
|
|
1404
|
-
|
|
1405
|
-
proj.analyses.VariableRecoveryFast(f)
|
|
1406
|
-
cca = proj.analyses.CallingConvention(f)
|
|
1407
|
-
f.prototype = cca.prototype
|
|
1408
|
-
f.calling_convention = cca.cc
|
|
1409
|
-
|
|
1410
|
-
d = proj.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options)
|
|
1411
|
-
self._print_decompilation_result(d)
|
|
1412
|
-
|
|
1413
|
-
# bitshifts should be properly simplified into signed divisions
|
|
1414
|
-
assert "/ 8" in d.codegen.text
|
|
1415
|
-
assert "* 8" in d.codegen.text
|
|
1416
|
-
assert ">>" not in d.codegen.text
|
|
1417
|
-
|
|
1418
|
-
@for_all_structuring_algos
|
|
1419
|
-
def test_decompiling_fmt_get_space(self, decompiler_options=None):
|
|
1420
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "fmt")
|
|
1421
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1422
|
-
|
|
1423
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1424
|
-
|
|
1425
|
-
f = proj.kb.functions[0x4020F0]
|
|
1426
|
-
proj.analyses.VariableRecoveryFast(f)
|
|
1427
|
-
cca = proj.analyses.CallingConvention(f)
|
|
1428
|
-
f.prototype = cca.prototype
|
|
1429
|
-
f.calling_convention = cca.cc
|
|
1430
|
-
|
|
1431
|
-
d = proj.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options)
|
|
1432
|
-
self._print_decompilation_result(d)
|
|
1433
|
-
|
|
1434
|
-
assert "break" in d.codegen.text
|
|
1435
|
-
|
|
1436
|
-
@for_all_structuring_algos
|
|
1437
|
-
def test_decompiling_fmt_main(self, decompiler_options=None):
|
|
1438
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "fmt")
|
|
1439
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1440
|
-
|
|
1441
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1442
|
-
|
|
1443
|
-
xdectoumax = proj.kb.functions[0x406010]
|
|
1444
|
-
proj.analyses.VariableRecoveryFast(xdectoumax)
|
|
1445
|
-
cca = proj.analyses.CallingConvention(xdectoumax)
|
|
1446
|
-
xdectoumax.prototype = cca.prototype
|
|
1447
|
-
xdectoumax.calling_convention = cca.cc
|
|
1448
|
-
assert isinstance(xdectoumax.prototype.returnty, SimTypeInt)
|
|
1449
|
-
|
|
1450
|
-
f = proj.kb.functions[0x401900]
|
|
1451
|
-
proj.analyses.VariableRecoveryFast(f)
|
|
1452
|
-
cca = proj.analyses.CallingConvention(f)
|
|
1453
|
-
f.prototype = cca.prototype
|
|
1454
|
-
f.calling_convention = cca.cc
|
|
1455
|
-
|
|
1456
|
-
d = proj.analyses.Decompiler(f, cfg=cfg.model, options=decompiler_options)
|
|
1457
|
-
self._print_decompilation_result(d)
|
|
1458
|
-
|
|
1459
|
-
# function arguments must be a0 and a1. they cannot be renamed
|
|
1460
|
-
assert re.search(r"int main\([\s\S]+ a0, [\s\S]+a1[\S]*\)", d.codegen.text) is not None
|
|
1461
|
-
|
|
1462
|
-
assert "max_width = (int)xdectoumax(" in d.codegen.text or "max_width = xdectoumax(" in d.codegen.text
|
|
1463
|
-
assert "goal_width = xdectoumax(" in d.codegen.text
|
|
1464
|
-
assert (
|
|
1465
|
-
"max_width = goal_width + 10;" in d.codegen.text
|
|
1466
|
-
or "max_width = ((int)(goal_width + 10));" in d.codegen.text
|
|
1467
|
-
)
|
|
1468
|
-
|
|
1469
|
-
# by default, largest_successor_tree_outside_loop in RegionIdentifier is set to True, which means the
|
|
1470
|
-
# getopt_long() == -1 case should be entirely left outside the loop. by ensuring the call to error(0x1) is
|
|
1471
|
-
# within the last few lines of decompilation output, we ensure the -1 case is indeed outside the loop.
|
|
1472
|
-
last_six_lines = "\n".join(line.strip(" ") for line in d.codegen.text.split("\n")[-7:])
|
|
1473
|
-
assert 'error(1, *(__errno_location()), "%s");' in last_six_lines
|
|
1474
|
-
|
|
1475
|
-
@for_all_structuring_algos
|
|
1476
|
-
def test_decompiling_fmt0_main(self, decompiler_options=None):
|
|
1477
|
-
bin_path = os.path.join(test_location, "x86_64", "fmt_0")
|
|
1478
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1479
|
-
|
|
1480
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1481
|
-
|
|
1482
|
-
f = proj.kb.functions["main"]
|
|
1483
|
-
proj.analyses.VariableRecoveryFast(f)
|
|
1484
|
-
cca = proj.analyses.CallingConvention(f)
|
|
1485
|
-
f.prototype = cca.prototype
|
|
1486
|
-
f.calling_convention = cca.cc
|
|
1487
|
-
|
|
1488
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1489
|
-
self._print_decompilation_result(d)
|
|
1490
|
-
|
|
1491
|
-
# ensure the default case node is not duplicated
|
|
1492
|
-
cases = set(re.findall(r"case \d+:", d.codegen.text))
|
|
1493
|
-
assert cases.issuperset(
|
|
1494
|
-
{"case 99:", "case 103:", "case 112:", "case 115:", "case 116:", "case 117:", "case 119:"}
|
|
1495
|
-
)
|
|
1496
|
-
|
|
1497
|
-
@for_all_structuring_algos
|
|
1498
|
-
def test_expr_collapsing(self, decompiler_options=None):
|
|
1499
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "deep_expr")
|
|
1500
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1501
|
-
|
|
1502
|
-
proj.analyses.CFGFast(normalize=True)
|
|
1503
|
-
d = proj.analyses.Decompiler(proj.kb.functions["main"], options=decompiler_options)
|
|
1504
|
-
assert "..." in d.codegen.text, "codegen should have a too-deep expression replaced with '...'"
|
|
1505
|
-
collapsed = d.codegen.map_pos_to_node.get_node(d.codegen.text.find("..."))
|
|
1506
|
-
assert collapsed is not None, "collapsed node should appear in map"
|
|
1507
|
-
assert collapsed.collapsed, "collapsed node should be marked as collapsed"
|
|
1508
|
-
collapsed.collapsed = False
|
|
1509
|
-
old_len = len(d.codegen.text)
|
|
1510
|
-
d.codegen.regenerate_text()
|
|
1511
|
-
new_len = len(d.codegen.text)
|
|
1512
|
-
assert new_len > old_len, "un-collapsing node should expand decompilation output"
|
|
1513
|
-
|
|
1514
|
-
@for_all_structuring_algos
|
|
1515
|
-
def test_decompiling_dirname_x2nrealloc(self, decompiler_options=None):
|
|
1516
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "dirname")
|
|
1517
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1518
|
-
|
|
1519
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1520
|
-
|
|
1521
|
-
f = proj.kb.functions["x2nrealloc"]
|
|
1522
|
-
|
|
1523
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1524
|
-
self._print_decompilation_result(d)
|
|
1525
|
-
|
|
1526
|
-
assert "__CFADD__" in d.codegen.text
|
|
1527
|
-
|
|
1528
|
-
@for_all_structuring_algos
|
|
1529
|
-
def test_decompiling_division3(self, decompiler_options=None):
|
|
1530
|
-
bin_path = os.path.join(test_location, "i386", "decompiler", "division3")
|
|
1531
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1532
|
-
|
|
1533
|
-
proj.analyses.CFGFast(normalize=True)
|
|
1534
|
-
|
|
1535
|
-
# disable eager returns simplifier
|
|
1536
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
1537
|
-
"AMD64", "linux"
|
|
1538
|
-
)
|
|
1539
|
-
all_optimization_passes = [
|
|
1540
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
1541
|
-
]
|
|
1542
|
-
d = proj.analyses.Decompiler(
|
|
1543
|
-
proj.kb.functions["division3"], optimization_passes=all_optimization_passes, options=decompiler_options
|
|
1544
|
-
)
|
|
1545
|
-
self._print_decompilation_result(d)
|
|
1546
|
-
|
|
1547
|
-
# get the returned expression from the return statement
|
|
1548
|
-
# e.g., retexpr will be "v2" if the return statement is " return v2;"
|
|
1549
|
-
lines = d.codegen.text.split("\n")
|
|
1550
|
-
retexpr = [line for line in lines if "return " in line][0].strip(" ;")[7:]
|
|
1551
|
-
|
|
1552
|
-
# find the statement "v2 = v0 / 3"
|
|
1553
|
-
div3 = [line for line in lines if re.match(retexpr + r" = v\d+ / 3;", line.strip(" ")) is not None]
|
|
1554
|
-
assert len(div3) == 1, f"Cannot find statement {retexpr} = v0 / 3."
|
|
1555
|
-
# find the statement "v2 = v0 * 7"
|
|
1556
|
-
mul7 = [line for line in lines if re.match(retexpr + r" = v\d+ \* 7;", line.strip(" ")) is not None]
|
|
1557
|
-
assert len(mul7) == 1, f"Cannot find statement {retexpr} = v0 * 7."
|
|
1558
|
-
|
|
1559
|
-
# @for_all_structuring_algos
|
|
1560
|
-
@structuring_algo("dream")
|
|
1561
|
-
def test_decompiling_dirname_quotearg_n_options(self, decompiler_options=None):
|
|
1562
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "dirname")
|
|
1563
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1564
|
-
|
|
1565
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1566
|
-
|
|
1567
|
-
f = proj.kb.functions["quotearg_n_options"]
|
|
1568
|
-
|
|
1569
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1570
|
-
self._print_decompilation_result(d)
|
|
1571
|
-
|
|
1572
|
-
@for_all_structuring_algos
|
|
1573
|
-
def test_decompiling_simple_ctfbin_modulo(self, decompiler_options=None):
|
|
1574
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "simple_ctfbin_modulo")
|
|
1575
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1576
|
-
|
|
1577
|
-
proj.analyses.CFGFast(normalize=True)
|
|
1578
|
-
|
|
1579
|
-
d = proj.analyses.Decompiler(proj.kb.functions["encrypt"], options=decompiler_options)
|
|
1580
|
-
self._print_decompilation_result(d)
|
|
1581
|
-
|
|
1582
|
-
assert "% 61" in d.codegen.text, "Modulo simplification failed."
|
|
1583
|
-
|
|
1584
|
-
@for_all_structuring_algos
|
|
1585
|
-
def test_struct_access(self, decompiler_options=None):
|
|
1586
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "struct_access")
|
|
1587
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1588
|
-
|
|
1589
|
-
proj.analyses.CFGFast(normalize=True)
|
|
1590
|
-
|
|
1591
|
-
typedefs = angr.sim_type.parse_file(
|
|
1592
|
-
"""
|
|
1593
|
-
struct A {
|
|
1594
|
-
int a1;
|
|
1595
|
-
int a2;
|
|
1596
|
-
int a3;
|
|
1597
|
-
};
|
|
1598
|
-
|
|
1599
|
-
struct B {
|
|
1600
|
-
struct A b1;
|
|
1601
|
-
struct A b2;
|
|
1602
|
-
};
|
|
1603
|
-
|
|
1604
|
-
struct C {
|
|
1605
|
-
int c1;
|
|
1606
|
-
struct B c2[10];
|
|
1607
|
-
int c3[10];
|
|
1608
|
-
struct C *c4;
|
|
1609
|
-
};
|
|
1610
|
-
"""
|
|
1611
|
-
)
|
|
1612
|
-
|
|
1613
|
-
d = proj.analyses.Decompiler(proj.kb.functions["main"], options=decompiler_options)
|
|
1614
|
-
vmi: VariableManagerInternal = d.cache.clinic.variable_kb.variables["main"]
|
|
1615
|
-
vmi.set_variable_type(
|
|
1616
|
-
next(iter(vmi.find_variables_by_stack_offset(-0x148))),
|
|
1617
|
-
SimTypePointer(typedefs[1]["struct C"]),
|
|
1618
|
-
all_unified=True,
|
|
1619
|
-
mark_manual=True,
|
|
1620
|
-
)
|
|
1621
|
-
unified = vmi.unified_variable(next(iter(vmi.find_variables_by_stack_offset(-0x148))))
|
|
1622
|
-
unified.name = "c_ptr"
|
|
1623
|
-
unified.renamed = True
|
|
1624
|
-
|
|
1625
|
-
vmi.set_variable_type(
|
|
1626
|
-
next(iter(vmi.find_variables_by_stack_offset(-0x140))),
|
|
1627
|
-
SimTypePointer(typedefs[1]["struct B"]),
|
|
1628
|
-
all_unified=True,
|
|
1629
|
-
mark_manual=True,
|
|
1630
|
-
)
|
|
1631
|
-
unified = vmi.unified_variable(next(iter(vmi.find_variables_by_stack_offset(-0x140))))
|
|
1632
|
-
unified.name = "b_ptr"
|
|
1633
|
-
unified.renamed = True
|
|
1634
|
-
|
|
1635
|
-
# NOTE TO WHOEVER SEES THIS
|
|
1636
|
-
# this is an INCOMPLETE way to set the type of an argument
|
|
1637
|
-
# you also need to change the function prototype
|
|
1638
|
-
vmi.set_variable_type(
|
|
1639
|
-
next(iter(vmi.find_variables_by_register("rdi"))), SimTypeInt(), all_unified=True, mark_manual=True
|
|
1640
|
-
)
|
|
1641
|
-
unified = vmi.unified_variable(next(iter(vmi.find_variables_by_register("rdi"))))
|
|
1642
|
-
unified.name = "argc"
|
|
1643
|
-
unified.renamed = True
|
|
1644
|
-
|
|
1645
|
-
d = proj.analyses.Decompiler(
|
|
1646
|
-
proj.kb.functions["main"], variable_kb=d.cache.clinic.variable_kb, options=decompiler_options
|
|
1647
|
-
)
|
|
1648
|
-
self._print_decompilation_result(d)
|
|
1649
|
-
|
|
1650
|
-
# TODO c_val
|
|
1651
|
-
assert "b_ptr = &c_ptr->c2[argc];" in d.codegen.text
|
|
1652
|
-
assert "c_ptr->c3[argc] = argc;" in d.codegen.text
|
|
1653
|
-
assert "c_ptr->c2[argc].b2.a2 = argc;" in d.codegen.text
|
|
1654
|
-
assert "b_ptr += 1;" in d.codegen.text
|
|
1655
|
-
assert "return c_ptr->c4->c2[argc].b2.a2;" in d.codegen.text
|
|
1656
|
-
|
|
1657
|
-
@slow_test
|
|
1658
|
-
@for_all_structuring_algos
|
|
1659
|
-
def test_call_return_variable_folding(self, decompiler_options=None):
|
|
1660
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "ls_gcc_O0")
|
|
1661
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1662
|
-
|
|
1663
|
-
cfg = proj.analyses.CFGFast(normalize=True)
|
|
1664
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
1665
|
-
|
|
1666
|
-
dec = proj.analyses.Decompiler(proj.kb.functions["print_long_format"], options=decompiler_options)
|
|
1667
|
-
self._print_decompilation_result(dec)
|
|
1668
|
-
|
|
1669
|
-
assert "if (timespec_cmp(" in dec.codegen.text or "if ((int)timespec_cmp(" in dec.codegen.text
|
|
1670
|
-
assert "&& localtime_rz(localtz, " in dec.codegen.text
|
|
1671
|
-
|
|
1672
|
-
@structuring_algo("phoenix")
|
|
1673
|
-
def test_cascading_boolean_and(self, decompiler_options=None):
|
|
1674
|
-
# test binary contributed by zion
|
|
1675
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "test_cascading_boolean_and")
|
|
1676
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1677
|
-
|
|
1678
|
-
cfg = proj.analyses.CFGFast(normalize=True)
|
|
1679
|
-
|
|
1680
|
-
# disable eager returns simplifier
|
|
1681
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
1682
|
-
"AMD64", "linux"
|
|
1683
|
-
)
|
|
1684
|
-
all_optimization_passes = [
|
|
1685
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
1686
|
-
]
|
|
1687
|
-
|
|
1688
|
-
dec = proj.analyses.Decompiler(
|
|
1689
|
-
proj.kb.functions["foo"], cfg=cfg, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
1690
|
-
)
|
|
1691
|
-
self._print_decompilation_result(dec)
|
|
1692
|
-
assert dec.codegen.text.count("goto") == 1 # should have only one goto
|
|
1693
|
-
|
|
1694
|
-
@for_all_structuring_algos
|
|
1695
|
-
def test_decompiling_tee_O2_x2nrealloc(self, decompiler_options=None):
|
|
1696
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "tee_O2")
|
|
1697
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1698
|
-
|
|
1699
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1700
|
-
|
|
1701
|
-
f = proj.kb.functions["x2nrealloc"]
|
|
1702
|
-
|
|
1703
|
-
# disable eager returns simplifier
|
|
1704
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
1705
|
-
"AMD64", "linux"
|
|
1706
|
-
)
|
|
1707
|
-
all_optimization_passes = [
|
|
1708
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
1709
|
-
]
|
|
1710
|
-
|
|
1711
|
-
d = proj.analyses[Decompiler].prep()(
|
|
1712
|
-
f,
|
|
1713
|
-
cfg=cfg.model,
|
|
1714
|
-
options=decompiler_options,
|
|
1715
|
-
optimization_passes=all_optimization_passes,
|
|
1716
|
-
)
|
|
1717
|
-
self._print_decompilation_result(d)
|
|
1718
|
-
|
|
1719
|
-
# ensure xalloc_die() is within its own block
|
|
1720
|
-
lines = [line.strip("\n ") for line in d.codegen.text.split("\n")]
|
|
1721
|
-
for i, line in enumerate(lines):
|
|
1722
|
-
if line.startswith("xalloc_die();"):
|
|
1723
|
-
assert lines[i - 1].strip().startswith("if")
|
|
1724
|
-
assert lines[i + 1].strip() == "}"
|
|
1725
|
-
break
|
|
1726
|
-
else:
|
|
1727
|
-
assert False, "xalloc_die() is not found"
|
|
1728
|
-
|
|
1729
|
-
@for_all_structuring_algos
|
|
1730
|
-
def test_decompiling_mv0_main(self, decompiler_options=None):
|
|
1731
|
-
# one of the jump tables has an entry that goes back to the loop head
|
|
1732
|
-
bin_path = os.path.join(test_location, "x86_64", "mv_0")
|
|
1733
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1734
|
-
|
|
1735
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1736
|
-
|
|
1737
|
-
f = proj.kb.functions["main"]
|
|
1738
|
-
|
|
1739
|
-
# disable eager returns simplifier
|
|
1740
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
1741
|
-
"AMD64", "linux"
|
|
1742
|
-
)
|
|
1743
|
-
all_optimization_passes = [
|
|
1744
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
1745
|
-
]
|
|
1746
|
-
|
|
1747
|
-
d = proj.analyses[Decompiler].prep()(
|
|
1748
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
1749
|
-
)
|
|
1750
|
-
self._print_decompilation_result(d)
|
|
1751
|
-
|
|
1752
|
-
@for_all_structuring_algos
|
|
1753
|
-
def test_decompiling_dirname_last_component_missing_loop(self, decompiler_options=None):
|
|
1754
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "dirname")
|
|
1755
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1756
|
-
|
|
1757
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1758
|
-
|
|
1759
|
-
f = proj.kb.functions["last_component"]
|
|
1760
|
-
|
|
1761
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1762
|
-
self._print_decompilation_result(d)
|
|
1763
|
-
|
|
1764
|
-
assert d.codegen.text.count("for (") == 2 # two loops
|
|
1765
|
-
|
|
1766
|
-
@for_all_structuring_algos
|
|
1767
|
-
def test_decompiling_tee_O2_tail_jumps(self, decompiler_options=None):
|
|
1768
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "tee_O2")
|
|
1769
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1770
|
-
|
|
1771
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1772
|
-
|
|
1773
|
-
# argmatch_die
|
|
1774
|
-
f = proj.kb.functions["__argmatch_die"]
|
|
1775
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1776
|
-
self._print_decompilation_result(d)
|
|
1777
|
-
assert "usage(" in d.codegen.text
|
|
1778
|
-
|
|
1779
|
-
# setlocale_null_androidfix
|
|
1780
|
-
f = proj.kb.functions["setlocale_null_androidfix"]
|
|
1781
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1782
|
-
self._print_decompilation_result(d)
|
|
1783
|
-
assert "setlocale(" in d.codegen.text
|
|
1784
|
-
assert "NULL);" in d.codegen.text, "The arguments for setlocale() are missing"
|
|
1785
|
-
|
|
1786
|
-
@for_all_structuring_algos
|
|
1787
|
-
def test_decompiling_du_di_set_alloc(self, decompiler_options=None):
|
|
1788
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "du")
|
|
1789
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1790
|
-
|
|
1791
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1792
|
-
|
|
1793
|
-
f = proj.kb.functions["di_set_alloc"]
|
|
1794
|
-
|
|
1795
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1796
|
-
self._print_decompilation_result(d)
|
|
1797
|
-
|
|
1798
|
-
# addresses in function pointers should be correctly resolved into function pointers
|
|
1799
|
-
assert "di_ent_hash, di_ent_compare, di_ent_free" in d.codegen.text
|
|
1800
|
-
|
|
1801
|
-
@for_all_structuring_algos
|
|
1802
|
-
def test_decompiling_du_humblock_missing_conditions(self, decompiler_options=None):
|
|
1803
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "du")
|
|
1804
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1805
|
-
|
|
1806
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1807
|
-
|
|
1808
|
-
f = proj.kb.functions["humblock"]
|
|
1809
|
-
|
|
1810
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1811
|
-
self._print_decompilation_result(d)
|
|
1812
|
-
|
|
1813
|
-
assert d.codegen.text.count("if (!v0)") == 3 or d.codegen.text.count("if (v0)") == 3
|
|
1814
|
-
assert d.codegen.text.count("break;") > 0
|
|
1815
|
-
|
|
1816
|
-
@structuring_algo("phoenix")
|
|
1817
|
-
def test_decompiling_setb(self, decompiler_options=None):
|
|
1818
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "basenc")
|
|
1819
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1820
|
-
|
|
1821
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1822
|
-
|
|
1823
|
-
f = proj.kb.functions["c_isupper"]
|
|
1824
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1825
|
-
self._print_decompilation_result(d)
|
|
1826
|
-
|
|
1827
|
-
assert f.prototype.returnty is not None and f.prototype.returnty.size == 8
|
|
1828
|
-
assert "a0 - 65 < 26;" in d.codegen.text
|
|
1829
|
-
|
|
1830
|
-
@for_all_structuring_algos
|
|
1831
|
-
def test_decompiling_tac_base_len(self, decompiler_options=None):
|
|
1832
|
-
# source: https://github.com/coreutils/gnulib/blob/08ba9aaebff69a02cbb794c6213314fd09dd5ec5/lib/basename-lgpl.c#L52
|
|
1833
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "tac")
|
|
1834
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1835
|
-
|
|
1836
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1837
|
-
|
|
1838
|
-
f = proj.kb.functions["base_len"]
|
|
1839
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1840
|
-
self._print_decompilation_result(d)
|
|
1841
|
-
|
|
1842
|
-
spaceless_text = d.codegen.text.replace(" ", "").replace("\n", "")
|
|
1843
|
-
assert "==47" in spaceless_text or "!=47" in spaceless_text
|
|
1844
|
-
|
|
1845
|
-
@for_all_structuring_algos
|
|
1846
|
-
def test_decompiling_dd_argmatch_to_argument_noeagerreturns(self, decompiler_options=None):
|
|
1847
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "dd")
|
|
1848
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1849
|
-
|
|
1850
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1851
|
-
|
|
1852
|
-
# disable eager returns simplifier
|
|
1853
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
1854
|
-
"AMD64",
|
|
1855
|
-
"linux",
|
|
1856
|
-
)
|
|
1857
|
-
all_optimization_passes = [
|
|
1858
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
1859
|
-
]
|
|
1860
|
-
|
|
1861
|
-
f = proj.kb.functions["argmatch_to_argument"]
|
|
1862
|
-
|
|
1863
|
-
d = proj.analyses[Decompiler].prep()(
|
|
1864
|
-
f,
|
|
1865
|
-
cfg=cfg.model,
|
|
1866
|
-
options=set_decompiler_option(decompiler_options, [("cstyle_ifs", False)]),
|
|
1867
|
-
optimization_passes=all_optimization_passes,
|
|
1868
|
-
)
|
|
1869
|
-
self._print_decompilation_result(d)
|
|
1870
|
-
|
|
1871
|
-
# break should always be followed by a curly brace, not another statement
|
|
1872
|
-
t = d.codegen.text.replace(" ", "").replace("\n", "")
|
|
1873
|
-
if "break;" in t:
|
|
1874
|
-
assert "break;}" in t
|
|
1875
|
-
t = t.replace("break;}", "")
|
|
1876
|
-
assert "break;" not in t
|
|
1877
|
-
|
|
1878
|
-
# continue should always be followed by a curly brace, not another statement
|
|
1879
|
-
if "continue;" in t:
|
|
1880
|
-
assert "continue;}" in t
|
|
1881
|
-
t = t.replace("continue;}", "")
|
|
1882
|
-
assert "continue;" not in t
|
|
1883
|
-
|
|
1884
|
-
@for_all_structuring_algos
|
|
1885
|
-
def test_decompiling_dd_argmatch_to_argument_eagerreturns(self, decompiler_options=None):
|
|
1886
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "dd")
|
|
1887
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1888
|
-
|
|
1889
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1890
|
-
|
|
1891
|
-
f = proj.kb.functions["argmatch_to_argument"]
|
|
1892
|
-
|
|
1893
|
-
d = proj.analyses[Decompiler].prep()(
|
|
1894
|
-
f, cfg=cfg.model, options=set_decompiler_option(decompiler_options, [("cstyle_ifs", False)])
|
|
1895
|
-
)
|
|
1896
|
-
self._print_decompilation_result(d)
|
|
1897
|
-
|
|
1898
|
-
# return should always be followed by a curly brace, not another statement
|
|
1899
|
-
t = d.codegen.text.replace(" ", "").replace("\n", "")
|
|
1900
|
-
return_stmt_ctr = 0
|
|
1901
|
-
for m in re.finditer(r"return[^;]+;", t):
|
|
1902
|
-
return_stmt_ctr += 1
|
|
1903
|
-
assert t[m.start() + len(m.group(0))] == "}"
|
|
1904
|
-
|
|
1905
|
-
if return_stmt_ctr == 0:
|
|
1906
|
-
assert False, "Cannot find any return statements."
|
|
1907
|
-
|
|
1908
|
-
# continue should always be followed by a curly brace, not another statement
|
|
1909
|
-
if "continue;}" in t:
|
|
1910
|
-
t = t.replace("continue;}", "")
|
|
1911
|
-
assert "continue;" not in t
|
|
1912
|
-
|
|
1913
|
-
@for_all_structuring_algos
|
|
1914
|
-
def test_decompiling_remove_write_protected_non_symlink(self, decompiler_options=None):
|
|
1915
|
-
# labels test
|
|
1916
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "remove.o")
|
|
1917
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1918
|
-
|
|
1919
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1920
|
-
|
|
1921
|
-
f = proj.kb.functions["write_protected_non_symlink"]
|
|
1922
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
1923
|
-
|
|
1924
|
-
# disable eager returns simplifier
|
|
1925
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
1926
|
-
"AMD64", "linux"
|
|
1927
|
-
)
|
|
1928
|
-
all_optimization_passes = [
|
|
1929
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
1930
|
-
]
|
|
1931
|
-
|
|
1932
|
-
d = proj.analyses[Decompiler].prep()(
|
|
1933
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
1934
|
-
)
|
|
1935
|
-
self._print_decompilation_result(d)
|
|
1936
|
-
|
|
1937
|
-
assert "faccessat(" in d.codegen.text
|
|
1938
|
-
if decompiler_options:
|
|
1939
|
-
if decompiler_options[-1][-1] == "phoenix":
|
|
1940
|
-
# make sure there is one label
|
|
1941
|
-
all_labels = set()
|
|
1942
|
-
all_gotos = set()
|
|
1943
|
-
for m in re.finditer(r"LABEL_[^:;]+:", d.codegen.text):
|
|
1944
|
-
all_labels.add(m.group(0)[:-1])
|
|
1945
|
-
for m in re.finditer(r"goto ([^;]+);", d.codegen.text):
|
|
1946
|
-
all_gotos.add(m.group(1))
|
|
1947
|
-
assert len(all_labels) == 2
|
|
1948
|
-
assert len(all_gotos) == 2
|
|
1949
|
-
assert all_labels == all_gotos
|
|
1950
|
-
else:
|
|
1951
|
-
# dream
|
|
1952
|
-
assert "LABEL_" not in d.codegen.text
|
|
1953
|
-
assert "goto" not in d.codegen.text
|
|
1954
|
-
|
|
1955
|
-
# ensure all return values are still there
|
|
1956
|
-
assert "1;" in d.codegen.text
|
|
1957
|
-
assert "0;" in d.codegen.text
|
|
1958
|
-
assert "-1;" in d.codegen.text or "4294967295" in d.codegen.text
|
|
1959
|
-
|
|
1960
|
-
@structuring_algo("phoenix")
|
|
1961
|
-
def test_decompiling_split_lines_split(self, decompiler_options=None):
|
|
1962
|
-
# Region identifier's fine-tuned loop refinement logic ensures there is only one goto statement in the
|
|
1963
|
-
# decompilation output.
|
|
1964
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "split.o")
|
|
1965
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1966
|
-
|
|
1967
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1968
|
-
|
|
1969
|
-
f = proj.kb.functions["lines_split"]
|
|
1970
|
-
|
|
1971
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1972
|
-
self._print_decompilation_result(d)
|
|
1973
|
-
|
|
1974
|
-
assert d.codegen.text.count("goto ") == 1
|
|
1975
|
-
|
|
1976
|
-
@structuring_algo("phoenix")
|
|
1977
|
-
def test_decompiling_ptx_fix_output_parameters(self, decompiler_options=None):
|
|
1978
|
-
# the carefully tuned edge sorting logic in Phoenix's last_resort_refinement ensures that there are one or two
|
|
1979
|
-
# goto statements in this function.
|
|
1980
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "ptx.o")
|
|
1981
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
1982
|
-
|
|
1983
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
1984
|
-
|
|
1985
|
-
f = proj.kb.functions["fix_output_parameters"]
|
|
1986
|
-
|
|
1987
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
1988
|
-
self._print_decompilation_result(d)
|
|
1989
|
-
|
|
1990
|
-
assert len(list(re.findall(r"LABEL_[^;:]+:", d.codegen.text))) in {1, 2}
|
|
1991
|
-
|
|
1992
|
-
@structuring_algo("phoenix")
|
|
1993
|
-
def test_decompiling_dd_advance_input_after_read_error(self, decompiler_options=None):
|
|
1994
|
-
# incorrect _unify_local_variables logic was creating incorrectly simplified logic:
|
|
1995
|
-
#
|
|
1996
|
-
# *(v2) = input_seek_errno;
|
|
1997
|
-
# v2 = __errno_location();
|
|
1998
|
-
#
|
|
1999
|
-
# it should be
|
|
2000
|
-
#
|
|
2001
|
-
# v2 = __errno_location();
|
|
2002
|
-
# *(v2) = input_seek_errno;
|
|
2003
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "dd.o")
|
|
2004
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2005
|
-
|
|
2006
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2007
|
-
|
|
2008
|
-
f = proj.kb.functions["advance_input_after_read_error"]
|
|
2009
|
-
|
|
2010
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2011
|
-
self._print_decompilation_result(d)
|
|
2012
|
-
|
|
2013
|
-
condensed = d.codegen.text.replace(" ", "").replace("\n", "")
|
|
2014
|
-
assert re.search(r"v\d=__errno_location\(\);\*\(v\d\)=input_seek_errno;", condensed)
|
|
2015
|
-
|
|
2016
|
-
@structuring_algo("phoenix")
|
|
2017
|
-
def test_decompiling_dd_iwrite(self, decompiler_options=None):
|
|
2018
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "dd.o")
|
|
2019
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2020
|
-
|
|
2021
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2022
|
-
|
|
2023
|
-
f = proj.kb.functions[0x401820]
|
|
2024
|
-
|
|
2025
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2026
|
-
self._print_decompilation_result(d)
|
|
2027
|
-
|
|
2028
|
-
assert "amd64g_calculate_condition" not in d.codegen.text # we should rewrite the ccall to expr == 0
|
|
2029
|
-
assert "a1 == a1" not in d.codegen.text
|
|
2030
|
-
|
|
2031
|
-
@structuring_algo("phoenix")
|
|
2032
|
-
def test_decompiling_uname_main(self, decompiler_options=None):
|
|
2033
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "uname.o")
|
|
2034
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2035
|
-
|
|
2036
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2037
|
-
|
|
2038
|
-
f = proj.kb.functions["main"]
|
|
2039
|
-
|
|
2040
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2041
|
-
self._print_decompilation_result(d)
|
|
2042
|
-
|
|
2043
|
-
# the ternary expression should not be propagated. however, we fail to narrow the ebx expression at 0x400c4f,
|
|
2044
|
-
# so we over-propagate the ternary expression once
|
|
2045
|
-
assert d.codegen.text.count("?") in (1, 2)
|
|
2046
|
-
|
|
2047
|
-
@for_all_structuring_algos
|
|
2048
|
-
def test_decompiling_prototype_recovery_two_blocks(self, decompiler_options=None):
|
|
2049
|
-
# we must analyze both 0x40021d and 0x400225 to determine the prototype of xstrtol
|
|
2050
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "stty.o")
|
|
2051
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2052
|
-
|
|
2053
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2054
|
-
|
|
2055
|
-
f = proj.kb.functions["screen_columns"]
|
|
2056
|
-
|
|
2057
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2058
|
-
|
|
2059
|
-
assert proj.kb.functions["xstrtol"].prototype is not None
|
|
2060
|
-
assert proj.kb.functions["xstrtol"].prototype.args is not None
|
|
2061
|
-
assert len(proj.kb.functions["xstrtol"].prototype.args) == 5
|
|
2062
|
-
assert re.search(r"xstrtol\([^\n,]+, [^\n,]+, [^\n,]+, [^\n,]+, [^\n,]+\)", d.codegen.text) is not None
|
|
2063
|
-
|
|
2064
|
-
@structuring_algo("phoenix")
|
|
2065
|
-
def test_decompiling_rewrite_negated_cascading_logical_conjunction_expressions(self, decompiler_options=None):
|
|
2066
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "stty.o")
|
|
2067
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2068
|
-
|
|
2069
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2070
|
-
|
|
2071
|
-
f = proj.kb.functions[0x4013E0]
|
|
2072
|
-
|
|
2073
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2074
|
-
"AMD64", "linux"
|
|
2075
|
-
)
|
|
2076
|
-
all_optimization_passes = [
|
|
2077
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2078
|
-
]
|
|
2079
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2080
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2081
|
-
)
|
|
2082
|
-
self._print_decompilation_result(d)
|
|
2083
|
-
|
|
2084
|
-
# expected: if (*(v4) || *((char *)*((long long *)a1)) != (char)a3 || a0 == *((long long *)a1) || (v5 & -0x100))
|
|
2085
|
-
assert d.codegen.text.count("||") == 3
|
|
2086
|
-
assert d.codegen.text.count("&&") == 0
|
|
2087
|
-
|
|
2088
|
-
@for_all_structuring_algos
|
|
2089
|
-
def test_decompiling_base32_basenc_do_decode(self, decompiler_options=None):
|
|
2090
|
-
# if region identifier works correctly, there should be no gotos
|
|
2091
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "base32-basenc.o")
|
|
2092
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2093
|
-
|
|
2094
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2095
|
-
|
|
2096
|
-
f = proj.kb.functions["do_decode"]
|
|
2097
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2098
|
-
|
|
2099
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2100
|
-
self._print_decompilation_result(d)
|
|
2101
|
-
|
|
2102
|
-
assert "finish_and_exit(" in d.codegen.text
|
|
2103
|
-
assert "goto" not in d.codegen.text
|
|
2104
|
-
|
|
2105
|
-
@structuring_algo("phoenix")
|
|
2106
|
-
def test_decompiling_sort_specify_nmerge(self, decompiler_options=None):
|
|
2107
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "sort.o")
|
|
2108
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2109
|
-
|
|
2110
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2111
|
-
f = proj.kb.functions["specify_nmerge"]
|
|
2112
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2113
|
-
self._print_decompilation_result(d)
|
|
2114
|
-
|
|
2115
|
-
assert "goto" not in d.codegen.text
|
|
2116
|
-
|
|
2117
|
-
@structuring_algo("phoenix")
|
|
2118
|
-
def test_decompiling_ls_print_many_per_line(self, decompiler_options=None):
|
|
2119
|
-
# complex variable types involved. a struct with only one field was causing _access() in
|
|
2120
|
-
# CStructuredCodeGenerator to end up in an infinite loop.
|
|
2121
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "ls.o")
|
|
2122
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2123
|
-
|
|
2124
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2125
|
-
f = proj.kb.functions["print_many_per_line"]
|
|
2126
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2127
|
-
self._print_decompilation_result(d)
|
|
2128
|
-
|
|
2129
|
-
# it should make somewhat sense
|
|
2130
|
-
assert "calculate_columns(" in d.codegen.text
|
|
2131
|
-
assert "putchar_unlocked(eolbyte)" in d.codegen.text
|
|
2132
|
-
|
|
2133
|
-
@structuring_algo("phoenix")
|
|
2134
|
-
def test_decompiling_who_scan_entries(self, decompiler_options=None):
|
|
2135
|
-
# order of edge virtualization matters. the default edge virtualization order (post-ordering) will lead to two
|
|
2136
|
-
# gotos. virtualizing 0x401361 -> 0x4012b5 will lead to only one goto (because it's the edge that the
|
|
2137
|
-
# compiler's optimizations created).
|
|
2138
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "who.o")
|
|
2139
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2140
|
-
|
|
2141
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2142
|
-
f = proj.kb.functions["scan_entries"]
|
|
2143
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2144
|
-
self._print_decompilation_result(d)
|
|
2145
|
-
|
|
2146
|
-
# it should make somewhat sense
|
|
2147
|
-
assert d.codegen.text.count("goto ") == 2
|
|
2148
|
-
|
|
2149
|
-
# a bug in propagator was leading to the removal of the comparison at 0x4012b8
|
|
2150
|
-
lines = d.codegen.text.split("\n")
|
|
2151
|
-
label_4012b8_index = lines.index("LABEL_4012b8:")
|
|
2152
|
-
assert label_4012b8_index != -1
|
|
2153
|
-
assert lines[label_4012b8_index + 1].endswith("== 2)")
|
|
2154
|
-
|
|
2155
|
-
@structuring_algo("phoenix")
|
|
2156
|
-
def test_decompiling_tr_build_spec_list(self, decompiler_options=None):
|
|
2157
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "tr.o")
|
|
2158
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2159
|
-
|
|
2160
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2161
|
-
|
|
2162
|
-
f = proj.kb.functions["build_spec_list"]
|
|
2163
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2164
|
-
|
|
2165
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2166
|
-
"AMD64", "linux"
|
|
2167
|
-
)
|
|
2168
|
-
# lowered-switch simplifier cannot be enabled. otherwise we will have an extra goto that goes into the fake
|
|
2169
|
-
# switch-case.
|
|
2170
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2171
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2172
|
-
)
|
|
2173
|
-
self._print_decompilation_result(d)
|
|
2174
|
-
|
|
2175
|
-
assert d.codegen.text.count("goto ") == 3
|
|
2176
|
-
# `LABEL_400d08` is the label `try_bracketed_repeat` found in the source, which is jumped to twice
|
|
2177
|
-
assert d.codegen.text.count("goto LABEL_400d08;") == 2
|
|
2178
|
-
# this goto may go away in the future if the loops are structured correctly
|
|
2179
|
-
assert d.codegen.text.count("goto LABEL_400d2a;") == 1
|
|
2180
|
-
|
|
2181
|
-
@structuring_algo("phoenix")
|
|
2182
|
-
def test_decompiling_sha384sum_digest_bsd_split_3(self, decompiler_options=None):
|
|
2183
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "sha384sum-digest.o")
|
|
2184
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2185
|
-
|
|
2186
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2187
|
-
|
|
2188
|
-
f = proj.kb.functions["bsd_split_3"]
|
|
2189
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2190
|
-
|
|
2191
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2192
|
-
"AMD64", "linux"
|
|
2193
|
-
)
|
|
2194
|
-
all_optimization_passes = [
|
|
2195
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2196
|
-
]
|
|
2197
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2198
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2199
|
-
)
|
|
2200
|
-
self._print_decompilation_result(d)
|
|
2201
|
-
|
|
2202
|
-
# there should only be two goto statements
|
|
2203
|
-
assert d.codegen.text.count("goto ") == 2
|
|
2204
|
-
|
|
2205
|
-
@for_all_structuring_algos
|
|
2206
|
-
def test_eliminating_stack_canary_reused_stack_chk_fail_call(self, decompiler_options=None):
|
|
2207
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "cksum-digest.o")
|
|
2208
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2209
|
-
|
|
2210
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2211
|
-
f = proj.kb.functions["split_3"]
|
|
2212
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2213
|
-
self._print_decompilation_result(d)
|
|
2214
|
-
|
|
2215
|
-
assert "return " in d.codegen.text
|
|
2216
|
-
assert "stack_chk_fail" not in d.codegen.text
|
|
2217
|
-
|
|
2218
|
-
@structuring_algo("phoenix")
|
|
2219
|
-
def test_decompiling_tr_card_of_complement(self, decompiler_options=None):
|
|
2220
|
-
# this function has a single-block loop (rep stosq). make sure we handle properly without introducing gotos.
|
|
2221
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "tr.o")
|
|
2222
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2223
|
-
|
|
2224
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2225
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2226
|
-
"AMD64", "linux"
|
|
2227
|
-
)
|
|
2228
|
-
f = proj.kb.functions["card_of_complement"]
|
|
2229
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2230
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2231
|
-
)
|
|
2232
|
-
self._print_decompilation_result(d)
|
|
2233
|
-
assert "goto " not in d.codegen.text
|
|
2234
|
-
|
|
2235
|
-
@structuring_algo("phoenix")
|
|
2236
|
-
def test_decompiling_printenv_main(self, decompiler_options=None):
|
|
2237
|
-
# when a subgraph inside a loop cannot be structured, instead of entering last-resort refinement, we should
|
|
2238
|
-
# return the subgraph and let structuring resume with the knowledge of the loop.
|
|
2239
|
-
# otherwise, in this function, we will see a goto while in reality we do not need any gotos.
|
|
2240
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "printenv.o")
|
|
2241
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2242
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2243
|
-
f = proj.kb.functions["main"]
|
|
2244
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2245
|
-
self._print_decompilation_result(d)
|
|
2246
|
-
assert "goto " not in d.codegen.text
|
|
2247
|
-
|
|
2248
|
-
@for_all_structuring_algos
|
|
2249
|
-
def test_decompiling_functions_with_unknown_simprocedures(self, decompiler_options=None):
|
|
2250
|
-
# angr does not have function signatures for cgc_allocate (and other cgc_*) functions, which means we will never
|
|
2251
|
-
# be able to infer the function prototype for these functions. We must not incorrectly assume these functions
|
|
2252
|
-
# do not take any arguments.
|
|
2253
|
-
bin_path = os.path.join(test_location, "i386", "cgc_HIGHCOO.elf")
|
|
2254
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2255
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2256
|
-
proj.analyses[CompleteCallingConventionsAnalysis].prep()(recover_variables=True)
|
|
2257
|
-
f = proj.kb.functions["cgc_recv_haiku"]
|
|
2258
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2259
|
-
self._print_decompilation_result(d)
|
|
2260
|
-
|
|
2261
|
-
cgc_allocate_call = re.search(r"cgc_allocate\(([^()]+)\)", d.codegen.text)
|
|
2262
|
-
assert cgc_allocate_call is not None, "Expect a call to cgc_allocate(), found None"
|
|
2263
|
-
comma_count = cgc_allocate_call.group(1).count(",")
|
|
2264
|
-
assert comma_count == 1, f"Expect cgc_allocate() to have two arguments, found {comma_count + 1}"
|
|
2265
|
-
|
|
2266
|
-
@structuring_algo("phoenix")
|
|
2267
|
-
def test_reverting_switch_lowering_cksum_digest_print_filename(self, decompiler_options=None):
|
|
2268
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "cksum-digest.o")
|
|
2269
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2270
|
-
|
|
2271
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2272
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2273
|
-
"AMD64", "linux"
|
|
2274
|
-
)
|
|
2275
|
-
all_optimization_passes += [angr.analyses.decompiler.optimization_passes.LoweredSwitchSimplifier]
|
|
2276
|
-
|
|
2277
|
-
f = proj.kb.functions["print_filename"]
|
|
2278
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2279
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2280
|
-
)
|
|
2281
|
-
self._print_decompilation_result(d)
|
|
2282
|
-
|
|
2283
|
-
assert "switch" in d.codegen.text
|
|
2284
|
-
assert "case 10:" in d.codegen.text
|
|
2285
|
-
assert "case 13:" in d.codegen.text
|
|
2286
|
-
assert "case 92:" in d.codegen.text
|
|
2287
|
-
assert "default:" in d.codegen.text
|
|
2288
|
-
assert "goto" not in d.codegen.text
|
|
2289
|
-
|
|
2290
|
-
@structuring_algo("phoenix")
|
|
2291
|
-
def test_reverting_switch_lowering_cksum_digest_main(self, decompiler_options=None):
|
|
2292
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "cksum-digest.o")
|
|
2293
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2294
|
-
|
|
2295
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2296
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2297
|
-
"AMD64", "linux"
|
|
2298
|
-
)
|
|
2299
|
-
all_optimization_passes += [angr.analyses.decompiler.optimization_passes.LoweredSwitchSimplifier]
|
|
2300
|
-
|
|
2301
|
-
f = proj.kb.functions["main"]
|
|
2302
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2303
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2304
|
-
)
|
|
2305
|
-
self._print_decompilation_result(d)
|
|
2306
|
-
|
|
2307
|
-
assert "case 4294967165:" in d.codegen.text
|
|
2308
|
-
assert "case 4294967166:" in d.codegen.text
|
|
2309
|
-
|
|
2310
|
-
@structuring_algo("phoenix")
|
|
2311
|
-
def test_reverting_switch_lowering_filename_unescape(self, decompiler_options=None):
|
|
2312
|
-
# nested switch-cases
|
|
2313
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "b2sum-digest.o")
|
|
2314
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2315
|
-
|
|
2316
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2317
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2318
|
-
"AMD64", "linux"
|
|
2319
|
-
)
|
|
2320
|
-
all_optimization_passes += [angr.analyses.decompiler.optimization_passes.LoweredSwitchSimplifier]
|
|
2321
|
-
|
|
2322
|
-
f = proj.kb.functions["filename_unescape"]
|
|
2323
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2324
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2325
|
-
)
|
|
2326
|
-
self._print_decompilation_result(d)
|
|
2327
|
-
|
|
2328
|
-
assert d.codegen.text.count("switch ") == 2
|
|
2329
|
-
assert d.codegen.text.count("case 92:") == 2
|
|
2330
|
-
assert d.codegen.text.count("case 0:") == 1
|
|
2331
|
-
# TODO: structuring failed when removing this goto with ReturnDuplicator.
|
|
2332
|
-
# Fix in: https://github.com/angr/angr/issues/4252
|
|
2333
|
-
# assert "goto" not in d.codegen.text
|
|
2334
|
-
# TODO: the following check requires angr decompiler to implement assignment de-duplication
|
|
2335
|
-
# assert d.codegen.text.count("case 110:") == 1
|
|
2336
|
-
# TODO: the following check requires angr decompiler correctly support rewriting gotos inside nested loops and
|
|
2337
|
-
# switch-cases into break nodes.
|
|
2338
|
-
# assert d.codegen.text.count("break;") == 5
|
|
2339
|
-
|
|
2340
|
-
@structuring_algo("phoenix")
|
|
2341
|
-
def test_reverting_switch_clustering_and_lowering_cat_main(self, decompiler_options=None):
|
|
2342
|
-
# nested switch-cases
|
|
2343
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "cat.o")
|
|
2344
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2345
|
-
|
|
2346
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2347
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2348
|
-
"AMD64", "linux"
|
|
2349
|
-
)
|
|
2350
|
-
all_optimization_passes += [angr.analyses.decompiler.optimization_passes.LoweredSwitchSimplifier]
|
|
2351
|
-
|
|
2352
|
-
f = proj.kb.functions["main"]
|
|
2353
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2354
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2355
|
-
)
|
|
2356
|
-
self._print_decompilation_result(d)
|
|
2357
|
-
|
|
2358
|
-
assert d.codegen.text.count("switch (") == 1
|
|
2359
|
-
assert (
|
|
2360
|
-
"> 118" not in d.codegen.text and ">= 119" not in d.codegen.text
|
|
2361
|
-
) # > 118 (>= 119) goes to the default case
|
|
2362
|
-
|
|
2363
|
-
@structuring_algo("phoenix")
|
|
2364
|
-
def test_reverting_switch_clustering_and_lowering_cat_main_no_endpoint_dup(self, decompiler_options=None):
|
|
2365
|
-
# nested switch-cases
|
|
2366
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "cat.o")
|
|
2367
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2368
|
-
|
|
2369
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2370
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2371
|
-
"AMD64", "linux"
|
|
2372
|
-
)
|
|
2373
|
-
# turn off eager returns simplifier
|
|
2374
|
-
all_optimization_passes = [
|
|
2375
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2376
|
-
]
|
|
2377
|
-
all_optimization_passes += [angr.analyses.decompiler.optimization_passes.LoweredSwitchSimplifier]
|
|
2378
|
-
|
|
2379
|
-
f = proj.kb.functions["main"]
|
|
2380
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2381
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2382
|
-
)
|
|
2383
|
-
self._print_decompilation_result(d)
|
|
2384
|
-
|
|
2385
|
-
assert d.codegen.text.count("switch (") == 1
|
|
2386
|
-
assert (
|
|
2387
|
-
"> 118" not in d.codegen.text and ">= 119" not in d.codegen.text
|
|
2388
|
-
) # > 118 (>= 119) goes to the default case
|
|
2389
|
-
assert "case 65:" in d.codegen.text
|
|
2390
|
-
assert "case 69:" in d.codegen.text
|
|
2391
|
-
assert "case 84:" in d.codegen.text
|
|
2392
|
-
assert "case 98:" in d.codegen.text
|
|
2393
|
-
assert "case 101:" in d.codegen.text
|
|
2394
|
-
assert "case 110:" in d.codegen.text
|
|
2395
|
-
assert "case 115:" in d.codegen.text
|
|
2396
|
-
assert "case 116:" in d.codegen.text
|
|
2397
|
-
assert "case 117:" in d.codegen.text
|
|
2398
|
-
assert "case 118:" in d.codegen.text
|
|
2399
|
-
|
|
2400
|
-
@structuring_algo("phoenix")
|
|
2401
|
-
def test_comma_separated_statement_expression_whoami(self, decompiler_options=None):
|
|
2402
|
-
# nested switch-cases
|
|
2403
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "whoami.o")
|
|
2404
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2405
|
-
|
|
2406
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2407
|
-
f = proj.kb.functions["main"]
|
|
2408
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2409
|
-
self._print_decompilation_result(d)
|
|
2410
|
-
|
|
2411
|
-
assert "goto" not in d.codegen.text
|
|
2412
|
-
assert (
|
|
2413
|
-
re.search(r"if \(\(unsigned int\)v\d+ != -1 \|\| \(v\d+ = 0, !\*\(v\d+\)\)\)", d.codegen.text) is not None
|
|
2414
|
-
or re.search(r"if \(v\d+ != -1 \|\| \(v\d+ = 0, !\*\(v\d+\)\)\)", d.codegen.text) is not None
|
|
2415
|
-
)
|
|
2416
|
-
|
|
2417
|
-
@for_all_structuring_algos
|
|
2418
|
-
def test_complex_stack_offset_calculation(self, decompiler_options=None):
|
|
2419
|
-
# nested switch-cases
|
|
2420
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "babyheap_level1.1")
|
|
2421
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2422
|
-
|
|
2423
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2424
|
-
|
|
2425
|
-
f = proj.kb.functions["main"]
|
|
2426
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2427
|
-
f,
|
|
2428
|
-
cfg=cfg.model,
|
|
2429
|
-
options=decompiler_options,
|
|
2430
|
-
)
|
|
2431
|
-
self._print_decompilation_result(d)
|
|
2432
|
-
|
|
2433
|
-
# The highest level symptom here is that two variable used are
|
|
2434
|
-
# confused and this shows up in the addition types.
|
|
2435
|
-
assert "Other Possible Types" not in d.codegen.text
|
|
2436
|
-
|
|
2437
|
-
# check that the variable used in free is different from the one used in atoi
|
|
2438
|
-
m = re.search(r"free\([^v]*([^)]+)", d.codegen.text)
|
|
2439
|
-
assert m
|
|
2440
|
-
|
|
2441
|
-
var_name = m.group(1)
|
|
2442
|
-
assert not re.search(f"atoi.*{var_name}", d.codegen.text)
|
|
2443
|
-
|
|
2444
|
-
@for_all_structuring_algos
|
|
2445
|
-
def test_switch_case_shared_case_nodes_b2sum_digest(self, decompiler_options=None):
|
|
2446
|
-
# node 0x4028c8 is shared by two switch-case constructs. we should not crash even when eager returns simplifier
|
|
2447
|
-
# is disabled.
|
|
2448
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "b2sum-digest_shared_switch_nodes.o")
|
|
2449
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2450
|
-
|
|
2451
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2452
|
-
"AMD64", "linux"
|
|
2453
|
-
)
|
|
2454
|
-
all_optimization_passes = [
|
|
2455
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2456
|
-
]
|
|
2457
|
-
|
|
2458
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2459
|
-
f = proj.kb.functions["main"]
|
|
2460
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2461
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2462
|
-
)
|
|
2463
|
-
self._print_decompilation_result(d)
|
|
2464
|
-
|
|
2465
|
-
assert d.codegen.text.count("switch") == 1
|
|
2466
|
-
|
|
2467
|
-
@for_all_structuring_algos
|
|
2468
|
-
def test_no_switch_case_touch_touch(self, decompiler_options=None):
|
|
2469
|
-
# node 0x40015b is an if-node that is merged into a switch case node with other if-node's that
|
|
2470
|
-
# have it as a successor, resulting in a switch that point's to its old heads; in this case,
|
|
2471
|
-
# the switch should not exist at all AND not crash
|
|
2472
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "touch_touch_no_switch.o")
|
|
2473
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2474
|
-
|
|
2475
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2476
|
-
"AMD64", "linux"
|
|
2477
|
-
)
|
|
2478
|
-
all_optimization_passes = [
|
|
2479
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2480
|
-
]
|
|
2481
|
-
|
|
2482
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2483
|
-
f = proj.kb.functions["touch"]
|
|
2484
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2485
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2486
|
-
)
|
|
2487
|
-
self._print_decompilation_result(d)
|
|
2488
|
-
|
|
2489
|
-
assert d.codegen.text.count("switch") == 0
|
|
2490
|
-
|
|
2491
|
-
@slow_test
|
|
2492
|
-
@structuring_algo("phoenix")
|
|
2493
|
-
def test_eager_returns_simplifier_no_duplication_of_default_case(self, decompiler_options=None):
|
|
2494
|
-
bin_path = os.path.join(test_location, "x86_64", "ls_ubuntu_2004")
|
|
2495
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2496
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2497
|
-
f = proj.kb.functions["main"]
|
|
2498
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2499
|
-
self._print_decompilation_result(d)
|
|
2500
|
-
|
|
2501
|
-
assert "default:" in d.codegen.text
|
|
2502
|
-
assert "case 49:" in d.codegen.text
|
|
2503
|
-
assert "case 50:" not in d.codegen.text
|
|
2504
|
-
assert "case 51:" not in d.codegen.text
|
|
2505
|
-
assert "case 52:" not in d.codegen.text
|
|
2506
|
-
|
|
2507
|
-
@for_all_structuring_algos
|
|
2508
|
-
def test_df_add_uint_with_neg_flag_ite_expressions(self, decompiler_options=None):
|
|
2509
|
-
# properly handling cmovz and cmovnz in amd64 binaries
|
|
2510
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "df.o")
|
|
2511
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2512
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2513
|
-
f = proj.kb.functions[0x400EA0]
|
|
2514
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2515
|
-
self._print_decompilation_result(d)
|
|
2516
|
-
|
|
2517
|
-
# ITE expressions should not exist. we convert them to if-then-else properly.
|
|
2518
|
-
assert "?" not in d.codegen.text
|
|
2519
|
-
# ensure there are no empty scopes
|
|
2520
|
-
assert "{}" not in d.codegen.text.replace(" ", "").replace("\n", "")
|
|
2521
|
-
|
|
2522
|
-
@for_all_structuring_algos
|
|
2523
|
-
def test_od_else_simplification(self, decompiler_options=None):
|
|
2524
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "od_gccO2.o")
|
|
2525
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2526
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2527
|
-
f = proj.kb.functions["skip"]
|
|
2528
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2529
|
-
f, cfg=cfg.model, options=set_decompiler_option(decompiler_options, [("cstyle_ifs", False)])
|
|
2530
|
-
)
|
|
2531
|
-
self._print_decompilation_result(d)
|
|
2532
|
-
|
|
2533
|
-
text = d.codegen.text
|
|
2534
|
-
# find an if-stmt that has the following properties:
|
|
2535
|
-
# 1. Condition: (!a0)
|
|
2536
|
-
# 2. Has a scope ending in a return
|
|
2537
|
-
# 3. Has no else scope after the return
|
|
2538
|
-
good_if_pattern = r"if \(!a0\)\s*\{[^}]*return 1;\s*\}(?!\s*else)"
|
|
2539
|
-
good_if = re.search(good_if_pattern, text)
|
|
2540
|
-
assert good_if is not None
|
|
2541
|
-
|
|
2542
|
-
first_if_location = text.find("if")
|
|
2543
|
-
assert first_if_location != -1
|
|
2544
|
-
|
|
2545
|
-
# the first if in the program should have no else, and that first else should be a simple return
|
|
2546
|
-
assert first_if_location == good_if.start()
|
|
2547
|
-
|
|
2548
|
-
@structuring_algo("phoenix")
|
|
2549
|
-
def test_sensitive_eager_returns(self, decompiler_options=None):
|
|
2550
|
-
"""
|
|
2551
|
-
Tests the feature to stop eager returns from triggering on return sites that have
|
|
2552
|
-
too many calls. In the `foo` function, this should cause no return duplication.
|
|
2553
|
-
See test_sensitive_eager_returns.c for more details.
|
|
2554
|
-
"""
|
|
2555
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "test_sensitive_eager_returns")
|
|
2556
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2557
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2558
|
-
|
|
2559
|
-
# eager returns should trigger here
|
|
2560
|
-
f1 = proj.kb.functions["bar"]
|
|
2561
|
-
d = proj.analyses[Decompiler](f1, cfg=cfg.model, options=decompiler_options)
|
|
2562
|
-
self._print_decompilation_result(d)
|
|
2563
|
-
assert d.codegen.text.count("goto ") == 0
|
|
2564
|
-
|
|
2565
|
-
# eager returns should not trigger here
|
|
2566
|
-
f2 = proj.kb.functions["foo"]
|
|
2567
|
-
d = proj.analyses[Decompiler](f2, cfg=cfg.model, options=decompiler_options)
|
|
2568
|
-
self._print_decompilation_result(d)
|
|
2569
|
-
assert d.codegen.text.count("goto ") == 1
|
|
2570
|
-
|
|
2571
|
-
@for_all_structuring_algos
|
|
2572
|
-
def test_proper_argument_simplification(self, decompiler_options=None):
|
|
2573
|
-
bin_path = os.path.join(test_location, "x86_64", "true_a")
|
|
2574
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2575
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True, show_progressbar=not WORKER)
|
|
2576
|
-
|
|
2577
|
-
f = proj.kb.functions[0x404410]
|
|
2578
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2579
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2580
|
-
|
|
2581
|
-
target_addrs = {0x4045D8, 0x404575}
|
|
2582
|
-
target_nodes = [node for node in d.codegen.ail_graph.nodes if node.addr in target_addrs]
|
|
2583
|
-
|
|
2584
|
-
for target_node in target_nodes:
|
|
2585
|
-
# these are the two calls, their last arg should actually be r14
|
|
2586
|
-
assert str(target_node.statements[-1].args[2]).startswith("r14")
|
|
2587
|
-
|
|
2588
|
-
@for_all_structuring_algos
|
|
2589
|
-
def test_else_if_scope_printing(self, decompiler_options=None):
|
|
2590
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "fmt")
|
|
2591
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2592
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2593
|
-
|
|
2594
|
-
f = proj.kb.functions[0x401900]
|
|
2595
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2596
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2597
|
-
|
|
2598
|
-
self._print_decompilation_result(d)
|
|
2599
|
-
text = d.codegen.text
|
|
2600
|
-
# all scopes in the program should never be followed by code or tabs
|
|
2601
|
-
for i in re.finditer("{", text):
|
|
2602
|
-
idx = i.start()
|
|
2603
|
-
assert text[idx + 1] == "\n"
|
|
2604
|
-
|
|
2605
|
-
@for_all_structuring_algos
|
|
2606
|
-
def test_fauxware_read_packet_call_folding_into_store_stmt(self, decompiler_options=None):
|
|
2607
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "fauxware_read_packet")
|
|
2608
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2609
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2610
|
-
|
|
2611
|
-
f = proj.kb.functions["main"]
|
|
2612
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2613
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2614
|
-
|
|
2615
|
-
self._print_decompilation_result(d)
|
|
2616
|
-
text = d.codegen.text
|
|
2617
|
-
assert re.search(r"\[read_packet\([^)]*\)\] = 0;", text) is not None
|
|
2618
|
-
|
|
2619
|
-
@structuring_algo("phoenix")
|
|
2620
|
-
def test_ifelsesimplifier_insert_node_into_while_body(self, decompiler_options=None):
|
|
2621
|
-
# https://github.com/angr/angr/issues/4082
|
|
2622
|
-
|
|
2623
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "angr_4082_cache")
|
|
2624
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2625
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2626
|
-
|
|
2627
|
-
f = proj.kb.functions[0x4030D0]
|
|
2628
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2629
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2630
|
-
|
|
2631
|
-
self._print_decompilation_result(d)
|
|
2632
|
-
text = d.codegen.text
|
|
2633
|
-
text = text.replace(" ", "").replace("\n", "")
|
|
2634
|
-
# Incorrect:
|
|
2635
|
-
# while (true)
|
|
2636
|
-
# {
|
|
2637
|
-
# if (v9 >= v10)
|
|
2638
|
-
# return v9;
|
|
2639
|
-
# }
|
|
2640
|
-
# Expected:
|
|
2641
|
-
# while (true)
|
|
2642
|
-
# {
|
|
2643
|
-
# if (v9 >= v10)
|
|
2644
|
-
# return v9;
|
|
2645
|
-
# v8 = 0;
|
|
2646
|
-
# if (read(0x29, &v8, 0x4) != 4)
|
|
2647
|
-
# {
|
|
2648
|
-
# printf("failed to get number\n");
|
|
2649
|
-
# exit(0x1); /* do not return */
|
|
2650
|
-
# }
|
|
2651
|
-
#
|
|
2652
|
-
# we should not see a right curly brace after return v9;
|
|
2653
|
-
assert (
|
|
2654
|
-
re.search(r"while\(true\){if\(v\d+>=v\d+\)returnv\d+;v\d+=0;", text) is not None
|
|
2655
|
-
or re.search(r"for\(v\d+=0;v\d+<v\d+;v\d+\+=1\){v\d+=0", text) is not None
|
|
2656
|
-
)
|
|
2657
|
-
|
|
2658
|
-
@for_all_structuring_algos
|
|
2659
|
-
def test_automatic_ternary_creation_1(self, decompiler_options=None):
|
|
2660
|
-
"""
|
|
2661
|
-
Tests that the decompiler can automatically create ternary expressions from regions that look like:
|
|
2662
|
-
if (c) {x = a} else {x = b}
|
|
2663
|
-
|
|
2664
|
-
In this sample, the very first if-else structure in the code should be transformed to a ternary expression.
|
|
2665
|
-
"""
|
|
2666
|
-
# https://github.com/angr/angr/issues/4050
|
|
2667
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "coreutils_test.o")
|
|
2668
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2669
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2670
|
-
|
|
2671
|
-
f = proj.kb.functions["find_int"]
|
|
2672
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2673
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2674
|
-
|
|
2675
|
-
self._print_decompilation_result(d)
|
|
2676
|
-
text = d.codegen.text
|
|
2677
|
-
# there should be a ternary assignment in the code: x = (c ? a : b);
|
|
2678
|
-
assert re.search(r".+ = \(.+\?.+:.+\);", text) is not None
|
|
2679
|
-
|
|
2680
|
-
@for_all_structuring_algos
|
|
2681
|
-
def test_automatic_ternary_creation_2(self, decompiler_options=None):
|
|
2682
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "head.o")
|
|
2683
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2684
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2685
|
-
|
|
2686
|
-
f = proj.kb.functions["head"]
|
|
2687
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2688
|
-
# disable eager returns simplifier
|
|
2689
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2690
|
-
"AMD64", "linux"
|
|
2691
|
-
)
|
|
2692
|
-
all_optimization_passes = [
|
|
2693
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2694
|
-
]
|
|
2695
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2696
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2697
|
-
)
|
|
2698
|
-
|
|
2699
|
-
self._print_decompilation_result(d)
|
|
2700
|
-
text = d.codegen.text
|
|
2701
|
-
# there should be at least 1 ternary in the code: (c ? a : b);
|
|
2702
|
-
assert re.search(r"\(.+\?.+:.+\);", text) is not None
|
|
2703
|
-
|
|
2704
|
-
@for_all_structuring_algos
|
|
2705
|
-
def test_ternary_propagation_1(self, decompiler_options=None):
|
|
2706
|
-
"""
|
|
2707
|
-
Tests that single-use ternary expression assignments are propagated:
|
|
2708
|
-
x = (c ? a : b);
|
|
2709
|
-
puts(x)
|
|
2710
|
-
|
|
2711
|
-
=>
|
|
2712
|
-
|
|
2713
|
-
puts(c ? a : b);
|
|
2714
|
-
"""
|
|
2715
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "stty.o")
|
|
2716
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2717
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2718
|
-
|
|
2719
|
-
f = proj.kb.functions["display_speed"]
|
|
2720
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2721
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2722
|
-
|
|
2723
|
-
self._print_decompilation_result(d)
|
|
2724
|
-
text = d.codegen.text
|
|
2725
|
-
# all ternary assignments should be destroyed
|
|
2726
|
-
assert re.search(r".+ = \(.+\?.+:.+\);", text) is None
|
|
2727
|
-
|
|
2728
|
-
# normal ternary expressions should exist in both calls
|
|
2729
|
-
ternary_exprs = re.findall(r"\(.+\?.+:.+\);", text)
|
|
2730
|
-
assert len(ternary_exprs) == 2
|
|
2731
|
-
|
|
2732
|
-
@for_all_structuring_algos
|
|
2733
|
-
def test_ternary_propagation_2(self, decompiler_options=None):
|
|
2734
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "du.o")
|
|
2735
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2736
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2737
|
-
|
|
2738
|
-
f = proj.kb.functions["print_only_size"]
|
|
2739
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
2740
|
-
|
|
2741
|
-
# disable eager returns simplifier
|
|
2742
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2743
|
-
"AMD64", "linux"
|
|
2744
|
-
)
|
|
2745
|
-
all_optimization_passes = [
|
|
2746
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2747
|
-
]
|
|
2748
|
-
d = proj.analyses[Decompiler].prep()(
|
|
2749
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2750
|
-
)
|
|
2751
|
-
|
|
2752
|
-
self._print_decompilation_result(d)
|
|
2753
|
-
text = d.codegen.text
|
|
2754
|
-
# all ternary assignments should be destroyed
|
|
2755
|
-
assert re.search(r".+ = \(.+\?.+:.+\);", text) is None
|
|
2756
|
-
|
|
2757
|
-
# normal ternary expressions should exist in both calls
|
|
2758
|
-
ternary_exprs = re.findall(r"\(.+\?.+:.+\)", text)
|
|
2759
|
-
assert len(ternary_exprs) == 1
|
|
2760
|
-
|
|
2761
|
-
@for_all_structuring_algos
|
|
2762
|
-
def test_return_deduplication(self, decompiler_options=None):
|
|
2763
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "tsort.o")
|
|
2764
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2765
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2766
|
-
|
|
2767
|
-
f = proj.kb.functions["record_relation"]
|
|
2768
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
2769
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2770
|
-
|
|
2771
|
-
self._print_decompilation_result(d)
|
|
2772
|
-
text = d.codegen.text
|
|
2773
|
-
|
|
2774
|
-
assert text.count("return") == 1
|
|
2775
|
-
|
|
2776
|
-
@for_all_structuring_algos
|
|
2777
|
-
def test_bool_flipping_type2(self, decompiler_options=None):
|
|
2778
|
-
"""
|
|
2779
|
-
Assures Type2 Boolean Flips near the last statement of a function are not triggerd.
|
|
2780
|
-
This testcase can also fail if `test_return_deduplication` fails.
|
|
2781
|
-
"""
|
|
2782
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "tsort.o")
|
|
2783
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2784
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2785
|
-
|
|
2786
|
-
f = proj.kb.functions["record_relation"]
|
|
2787
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
2788
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2789
|
-
|
|
2790
|
-
self._print_decompilation_result(d)
|
|
2791
|
-
text = d.codegen.text
|
|
2792
|
-
|
|
2793
|
-
text = text.replace(" ", "").replace("\n", "")
|
|
2794
|
-
# Incorrect:
|
|
2795
|
-
# (unsigned int)v5[0] = strcmp(a0[0], *(a1));
|
|
2796
|
-
# if (!(unsigned int)v5)
|
|
2797
|
-
# return v5;
|
|
2798
|
-
# v6 = v1[6];
|
|
2799
|
-
# v5[0] = a1;
|
|
2800
|
-
# v5[1] = v6;
|
|
2801
|
-
# v1[6] = v5;
|
|
2802
|
-
#
|
|
2803
|
-
# Expected:
|
|
2804
|
-
# (unsigned int)v5[0] = strcmp(a0[0], *(a1));
|
|
2805
|
-
# if ((unsigned int)v5)
|
|
2806
|
-
# {
|
|
2807
|
-
# v6 = v1[6];
|
|
2808
|
-
# v5[0] = a1;
|
|
2809
|
-
# v5[1] = v6;
|
|
2810
|
-
# v1[6] = v5;
|
|
2811
|
-
# }
|
|
2812
|
-
# return v5;
|
|
2813
|
-
assert re.search(r"if\(.+?\)\{.+?\}return", text) is not None
|
|
2814
|
-
|
|
2815
|
-
@for_all_structuring_algos
|
|
2816
|
-
def test_ret_dedupe_fakeret_1(self, decompiler_options=None):
|
|
2817
|
-
"""
|
|
2818
|
-
Tests that returns created during structuring (such as returns in Tail Call optimizations)
|
|
2819
|
-
are deduplicated after they have been created.
|
|
2820
|
-
"""
|
|
2821
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "ptx.o")
|
|
2822
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2823
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2824
|
-
|
|
2825
|
-
f = proj.kb.functions["sort_found_occurs"]
|
|
2826
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
2827
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2828
|
-
|
|
2829
|
-
self._print_decompilation_result(d)
|
|
2830
|
-
text = d.codegen.text
|
|
2831
|
-
|
|
2832
|
-
text = text.replace(" ", "").replace("\n", "")
|
|
2833
|
-
# Incorrect:
|
|
2834
|
-
# v1 = number_of_occurs;
|
|
2835
|
-
# if (!number_of_occurs)
|
|
2836
|
-
# return;
|
|
2837
|
-
# v2 = occurs_table;
|
|
2838
|
-
# v3 = &compare_occurs;
|
|
2839
|
-
# v4 = 48;
|
|
2840
|
-
# qsort();
|
|
2841
|
-
# Expected:
|
|
2842
|
-
# v1 = number_of_occurs;
|
|
2843
|
-
# if (number_of_occurs) {
|
|
2844
|
-
# v2 = occurs_table;
|
|
2845
|
-
# v3 = &compare_occurs;
|
|
2846
|
-
# v4 = 48;
|
|
2847
|
-
# qsort();
|
|
2848
|
-
# }
|
|
2849
|
-
# return;
|
|
2850
|
-
assert re.search(r"if\(.+?\)\{.+?\}return", text) is not None
|
|
2851
|
-
|
|
2852
|
-
@for_all_structuring_algos
|
|
2853
|
-
def test_ret_dedupe_fakeret_2(self, decompiler_options=None):
|
|
2854
|
-
"""
|
|
2855
|
-
Tests that returns created during structuring (such as returns in Tail Call optimizations)
|
|
2856
|
-
are deduplicated after they have been created.
|
|
2857
|
-
"""
|
|
2858
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "mkdir.o")
|
|
2859
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2860
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
2861
|
-
|
|
2862
|
-
f = proj.kb.functions["announce_mkdir"]
|
|
2863
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
2864
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
2865
|
-
|
|
2866
|
-
self._print_decompilation_result(d)
|
|
2867
|
-
text = d.codegen.text
|
|
2868
|
-
|
|
2869
|
-
text = text.replace(" ", "").replace("\n", "")
|
|
2870
|
-
# Incorrect:
|
|
2871
|
-
# if (a1->field_20) {
|
|
2872
|
-
# v0 = v2;
|
|
2873
|
-
# v4 = a1->field_20;
|
|
2874
|
-
# v5 = stdout;
|
|
2875
|
-
# v6 = quotearg_style(0x4, a0);
|
|
2876
|
-
# v7 = v0;
|
|
2877
|
-
# prog_fprintf();
|
|
2878
|
-
# }
|
|
2879
|
-
# while (true) {
|
|
2880
|
-
# return;
|
|
2881
|
-
# }
|
|
2882
|
-
# Expected:
|
|
2883
|
-
# if (a1->field_20) {
|
|
2884
|
-
# v0 = v2;
|
|
2885
|
-
# v4 = a1->field_20;
|
|
2886
|
-
# v5 = stdout;
|
|
2887
|
-
# v6 = quotearg_style(0x4, a0);
|
|
2888
|
-
# v7 = v0;
|
|
2889
|
-
# prog_fprintf();
|
|
2890
|
-
# }
|
|
2891
|
-
# return;
|
|
2892
|
-
assert re.search(r"if\(.+?\)\{.+?\}return", text) is not None
|
|
2893
|
-
|
|
2894
|
-
@structuring_algo("phoenix")
|
|
2895
|
-
def test_numfmt_process_field(self, decompiler_options=None):
|
|
2896
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "numfmt.o")
|
|
2897
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
2898
|
-
cfg = proj.analyses.CFGFast(normalize=True)
|
|
2899
|
-
|
|
2900
|
-
f = proj.kb.functions["process_field"]
|
|
2901
|
-
proj.analyses.CompleteCallingConventions(recover_variables=True)
|
|
2902
|
-
|
|
2903
|
-
# disable eager returns simplifier
|
|
2904
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2905
|
-
"AMD64", "linux"
|
|
2906
|
-
)
|
|
2907
|
-
all_optimization_passes = [
|
|
2908
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2909
|
-
]
|
|
2910
|
-
|
|
2911
|
-
d = proj.analyses[Decompiler](
|
|
2912
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2913
|
-
)
|
|
2914
|
-
|
|
2915
|
-
self._print_decompilation_result(d)
|
|
2916
|
-
|
|
2917
|
-
# the two function arguments that are passed through stack into prepare_padded_number must have been eliminated
|
|
2918
|
-
# at this point, leaving block 401f40 empty.
|
|
2919
|
-
the_block = [nn for nn in d.clinic.graph if nn.addr == 0x401F40][0]
|
|
2920
|
-
assert len(the_block.statements) == 1 # it has an unused label
|
|
2921
|
-
|
|
2922
|
-
@for_all_structuring_algos
|
|
2923
|
-
def test_argument_cvars_in_map_pos_to_node(self, decompiler_options=None):
|
|
2924
|
-
bin_path = os.path.join(test_location, "x86_64", "fauxware")
|
|
2925
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
2926
|
-
|
|
2927
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
2928
|
-
f = cfg.functions["authenticate"]
|
|
2929
|
-
|
|
2930
|
-
codegen = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options).codegen
|
|
2931
|
-
|
|
2932
|
-
assert len(codegen.cfunc.arg_list) == 2
|
|
2933
|
-
elements = {n.obj for _, n in codegen.map_pos_to_node.items()}
|
|
2934
|
-
for cvar in codegen.cfunc.arg_list:
|
|
2935
|
-
assert cvar in elements
|
|
2936
|
-
|
|
2937
|
-
@for_all_structuring_algos
|
|
2938
|
-
def test_prototype_args_preserved(self, decompiler_options=None):
|
|
2939
|
-
bin_path = os.path.join(test_location, "x86_64", "fauxware")
|
|
2940
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
2941
|
-
|
|
2942
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
2943
|
-
f = cfg.functions["authenticate"]
|
|
2944
|
-
|
|
2945
|
-
cproto = "int authenticate(char *username, char *password)"
|
|
2946
|
-
_, proto, _ = convert_cproto_to_py(cproto + ";")
|
|
2947
|
-
f.prototype = proto.with_arch(p.arch)
|
|
2948
|
-
f.is_prototype_guessed = False
|
|
2949
|
-
|
|
2950
|
-
d = p.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
2951
|
-
assert cproto in d.codegen.text
|
|
2952
|
-
|
|
2953
|
-
@structuring_algo("phoenix")
|
|
2954
|
-
def test_multistatementexpression_od_read_char(self, decompiler_options=None):
|
|
2955
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "od.o")
|
|
2956
|
-
p = angr.Project(bin_path, auto_load_libs=False)
|
|
2957
|
-
|
|
2958
|
-
cfg = p.analyses[CFGFast].prep()(data_references=True, normalize=True)
|
|
2959
|
-
p.analyses.CompleteCallingConventions(recover_variables=True)
|
|
2960
|
-
f = cfg.functions["read_char"]
|
|
2961
|
-
|
|
2962
|
-
# disable eager returns simplifier
|
|
2963
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
2964
|
-
"AMD64", "linux"
|
|
2965
|
-
)
|
|
2966
|
-
all_optimization_passes = [
|
|
2967
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
2968
|
-
]
|
|
2969
|
-
|
|
2970
|
-
# always use multi-statement expressions
|
|
2971
|
-
decompiler_options.append((PARAM_TO_OPTION["use_multistmtexprs"], MultiStmtExprMode.ALWAYS))
|
|
2972
|
-
dec = p.analyses[Decompiler].prep()(
|
|
2973
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2974
|
-
)
|
|
2975
|
-
assert (
|
|
2976
|
-
re.search(
|
|
2977
|
-
r"v\d+ = in_stream, v\d+ = [^\n]+check_and_close\([^\n]+open_next_file\([^\n]+, in_stream\)",
|
|
2978
|
-
dec.codegen.text,
|
|
2979
|
-
)
|
|
2980
|
-
is not None
|
|
2981
|
-
)
|
|
2982
|
-
self._print_decompilation_result(dec)
|
|
2983
|
-
|
|
2984
|
-
# never use multi-statement expressions
|
|
2985
|
-
decompiler_options.append((PARAM_TO_OPTION["use_multistmtexprs"], MultiStmtExprMode.NEVER))
|
|
2986
|
-
dec = p.analyses[Decompiler].prep()(
|
|
2987
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
2988
|
-
)
|
|
2989
|
-
self._print_decompilation_result(dec)
|
|
2990
|
-
assert (
|
|
2991
|
-
re.search(
|
|
2992
|
-
r"v\d+ = in_stream;\n\s+v\d+ = [^\n]+check_and_close\([^\n]+open_next_file\([^\n;]+;", dec.codegen.text
|
|
2993
|
-
)
|
|
2994
|
-
is not None
|
|
2995
|
-
)
|
|
2996
|
-
saved = dec.codegen.text
|
|
2997
|
-
|
|
2998
|
-
# less than one call statement/expression
|
|
2999
|
-
decompiler_options.append((PARAM_TO_OPTION["use_multistmtexprs"], MultiStmtExprMode.MAX_ONE_CALL))
|
|
3000
|
-
dec = p.analyses[Decompiler].prep()(
|
|
3001
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
3002
|
-
)
|
|
3003
|
-
self._print_decompilation_result(dec)
|
|
3004
|
-
assert dec.codegen.text == saved
|
|
3005
|
-
|
|
3006
|
-
@slow_test
|
|
3007
|
-
@for_all_structuring_algos
|
|
3008
|
-
def test_function_pointer_identification(self, decompiler_options=None):
|
|
3009
|
-
bin_path = os.path.join(test_location, "x86_64", "rust_hello_world")
|
|
3010
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3011
|
-
cfg = proj.analyses.CFGFast(resolve_indirect_jumps=True, normalize=True)
|
|
3012
|
-
|
|
3013
|
-
f = proj.kb.functions["main"]
|
|
3014
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
3015
|
-
|
|
3016
|
-
self._print_decompilation_result(d)
|
|
3017
|
-
text = d.codegen.text
|
|
3018
|
-
assert "extern" not in text
|
|
3019
|
-
assert "std::rt::lang_start::h9b2e0b6aeda0bae0(rust_hello_world::main::h932c4676a11c63c3" in text
|
|
3020
|
-
|
|
3021
|
-
@structuring_algo("phoenix")
|
|
3022
|
-
def test_decompiling_remove_rm_fts(self, decompiler_options=None):
|
|
3023
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "remove.o")
|
|
3024
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3025
|
-
|
|
3026
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3027
|
-
|
|
3028
|
-
f = proj.kb.functions["rm_fts"]
|
|
3029
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
3030
|
-
|
|
3031
|
-
# disable eager returns simplifier
|
|
3032
|
-
all_optimization_passes = angr.analyses.decompiler.optimization_passes.get_default_optimization_passes(
|
|
3033
|
-
"AMD64", "linux"
|
|
3034
|
-
)
|
|
3035
|
-
all_optimization_passes = [
|
|
3036
|
-
p for p in all_optimization_passes if p is not angr.analyses.decompiler.optimization_passes.ReturnDuplicator
|
|
3037
|
-
]
|
|
3038
|
-
|
|
3039
|
-
d = proj.analyses[Decompiler].prep()(
|
|
3040
|
-
f, cfg=cfg.model, options=decompiler_options, optimization_passes=all_optimization_passes
|
|
3041
|
-
)
|
|
3042
|
-
self._print_decompilation_result(d)
|
|
3043
|
-
|
|
3044
|
-
lines = d.codegen.text.split("\n")
|
|
3045
|
-
func_starting_line = [idx for idx, line in enumerate(lines) if "rm_fts" in line][0]
|
|
3046
|
-
lines = lines[func_starting_line:]
|
|
3047
|
-
end_of_variable_list_line = [idx for idx, line in enumerate(lines) if not line.strip(" ")][0]
|
|
3048
|
-
lines = lines[end_of_variable_list_line + 1 :]
|
|
3049
|
-
# the second line of the code should be an if statement. all other variables should have been eliminated by
|
|
3050
|
-
# proper propagation
|
|
3051
|
-
assert lines[1].strip(" ").startswith("if (")
|
|
3052
|
-
|
|
3053
|
-
@structuring_algo("phoenix")
|
|
3054
|
-
def test_decompiling_incorrect_duplication_chcon_main(self, decompiler_options=None):
|
|
3055
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "chcon.o")
|
|
3056
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3057
|
-
|
|
3058
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3059
|
-
|
|
3060
|
-
f = proj.kb.functions["main"]
|
|
3061
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
3062
|
-
|
|
3063
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
3064
|
-
self._print_decompilation_result(d)
|
|
3065
|
-
|
|
3066
|
-
# incorrect region replacement was causing the while loop be duplicated, so we would end up with four while
|
|
3067
|
-
# loops. In the original source, there is only a single while loop.
|
|
3068
|
-
assert d.codegen.text.count("while (") == 1
|
|
3069
|
-
|
|
3070
|
-
@structuring_algo("phoenix")
|
|
3071
|
-
def test_decompiling_function_with_long_cascading_data_flows(self, decompiler_options=None):
|
|
3072
|
-
bin_path = os.path.join(test_location, "x86_64", "netfilter_b64.sys")
|
|
3073
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3074
|
-
|
|
3075
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3076
|
-
|
|
3077
|
-
f = proj.kb.functions[0x140002918]
|
|
3078
|
-
|
|
3079
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
3080
|
-
self._print_decompilation_result(d)
|
|
3081
|
-
|
|
3082
|
-
# each line as at most one __ROL__ or __ROR__
|
|
3083
|
-
lines = d.codegen.text.split("\n")
|
|
3084
|
-
rol_count = 0
|
|
3085
|
-
ror_count = 0
|
|
3086
|
-
for line in lines:
|
|
3087
|
-
rol_count += line.count("__ROL__")
|
|
3088
|
-
ror_count += line.count("__ROR__")
|
|
3089
|
-
count = line.count("__ROL__") + line.count("__ROR__")
|
|
3090
|
-
assert count <= 1
|
|
3091
|
-
|
|
3092
|
-
assert "tmp" not in line
|
|
3093
|
-
assert "..." not in line
|
|
3094
|
-
assert rol_count == 44
|
|
3095
|
-
assert ror_count == 20
|
|
3096
|
-
|
|
3097
|
-
@structuring_algo("phoenix")
|
|
3098
|
-
def test_decompiling_function_with_inline_unicode_strings(self, decompiler_options=None):
|
|
3099
|
-
bin_path = os.path.join(
|
|
3100
|
-
test_location, "x86_64", "windows", "aaba7db353eb9400e3471eaaa1cf0105f6d1fab0ce63f1a2665c8ba0e8963a05.bin"
|
|
3101
|
-
)
|
|
3102
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3103
|
-
|
|
3104
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3105
|
-
|
|
3106
|
-
f = proj.kb.functions[0x1A590]
|
|
3107
|
-
|
|
3108
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
3109
|
-
self._print_decompilation_result(d)
|
|
3110
|
-
|
|
3111
|
-
assert 'L"\\\\Registry\\\\Machine\\\\SYSTEM\\\\CurrentControlSet\\\\Control\\\\WinApi"' in d.codegen.text
|
|
3112
|
-
assert 'L"WinDeviceAddress"' in d.codegen.text
|
|
3113
|
-
|
|
3114
|
-
@structuring_algo("phoenix")
|
|
3115
|
-
def test_ifelseflatten_iplink_bridge(self, decompiler_options=None):
|
|
3116
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "iplink_bridge.o")
|
|
3117
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3118
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3119
|
-
|
|
3120
|
-
f = proj.kb.functions["bridge_print_opt"]
|
|
3121
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
3122
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
3123
|
-
|
|
3124
|
-
self._print_decompilation_result(d)
|
|
3125
|
-
text = d.codegen.text
|
|
3126
|
-
good_if_return_pattern = r"if \(\!a2\)\s+return .*;"
|
|
3127
|
-
good_if_return = re.search(good_if_return_pattern, text)
|
|
3128
|
-
assert good_if_return is not None
|
|
3129
|
-
|
|
3130
|
-
first_if_location = text.find("if")
|
|
3131
|
-
assert first_if_location != -1
|
|
3132
|
-
|
|
3133
|
-
# TODO: this is broken right now on the 1 goto for a bad else. It may not be relevant for this testcase.
|
|
3134
|
-
# there should be no else and no gotos!
|
|
3135
|
-
# assert "goto" not in text
|
|
3136
|
-
# assert "else" not in text
|
|
3137
|
-
|
|
3138
|
-
# the first if in the program should have no else, and that first else should be a simple return
|
|
3139
|
-
assert first_if_location == good_if_return.start()
|
|
3140
|
-
assert not text[first_if_location + len(good_if_return.group(0)) :].startswith(" else")
|
|
3141
|
-
|
|
3142
|
-
@structuring_algo("phoenix")
|
|
3143
|
-
def test_ifelseflatten_gzip(self, decompiler_options=None):
|
|
3144
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "gzip.o")
|
|
3145
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3146
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3147
|
-
|
|
3148
|
-
f = proj.kb.functions["treat_file"]
|
|
3149
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
3150
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
3151
|
-
|
|
3152
|
-
self._print_decompilation_result(d)
|
|
3153
|
-
text = d.codegen.text.replace("\n", " ")
|
|
3154
|
-
first_if_location = text.find("if (")
|
|
3155
|
-
# the very first if-stmt in this function should be a single scope with a return.
|
|
3156
|
-
# there should be no else scope as well.
|
|
3157
|
-
correct_ifs = list(re.finditer(r'if \(!strcmp\(a0, "-"\)\) {5}\{.*? return; {5}}', text))
|
|
3158
|
-
assert len(correct_ifs) >= 1
|
|
3159
|
-
|
|
3160
|
-
first_correct_if = correct_ifs[0]
|
|
3161
|
-
assert first_correct_if.start() == first_if_location
|
|
3162
|
-
|
|
3163
|
-
@structuring_algo("phoenix")
|
|
3164
|
-
def test_ifelseflatten_iprule(self, decompiler_options=None):
|
|
3165
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "iprule.o")
|
|
3166
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3167
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3168
|
-
|
|
3169
|
-
f = proj.kb.functions["flush_rule"]
|
|
3170
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
3171
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
3172
|
-
|
|
3173
|
-
self._print_decompilation_result(d)
|
|
3174
|
-
|
|
3175
|
-
# XXX: this a hack that should be fixed in some other place
|
|
3176
|
-
text = d.codegen.text.replace("4294967295", "-1")
|
|
3177
|
-
text = text.replace("\n", " ")
|
|
3178
|
-
|
|
3179
|
-
first_if_location = text.find("if (")
|
|
3180
|
-
# the very first if-stmt in this function should be a single scope with a return.
|
|
3181
|
-
# there should be no else scope as well.
|
|
3182
|
-
# TODO: fix the dead-variable elimination pass so that it does remove the extra assign here
|
|
3183
|
-
correct_ifs = list(re.finditer(r"if \(.*?\) {5}\{.*? return -1; {5}}", text))
|
|
3184
|
-
assert len(correct_ifs) >= 1
|
|
3185
|
-
|
|
3186
|
-
first_correct_if = correct_ifs[0]
|
|
3187
|
-
assert first_correct_if.start() == first_if_location
|
|
3188
|
-
|
|
3189
|
-
@structuring_algo("phoenix")
|
|
3190
|
-
def test_ifelseflatten_clientloop(self, decompiler_options=None):
|
|
3191
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "clientloop.o")
|
|
3192
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3193
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3194
|
-
|
|
3195
|
-
f = proj.kb.functions["client_request_tun_fwd"]
|
|
3196
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
3197
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
3198
|
-
|
|
3199
|
-
self._print_decompilation_result(d)
|
|
3200
|
-
text = d.codegen.text
|
|
3201
|
-
|
|
3202
|
-
# find all ifs
|
|
3203
|
-
all_if_stmts = list(re.finditer("if \\(.*?\\)", text))
|
|
3204
|
-
assert all_if_stmts is not None
|
|
3205
|
-
assert len(all_if_stmts) >= 2
|
|
3206
|
-
|
|
3207
|
-
# first if-stmt should be a single scope with a return.
|
|
3208
|
-
first_good_if = re.search("if \\(.*?\\)\n {8}return 0;", text)
|
|
3209
|
-
assert first_good_if is not None
|
|
3210
|
-
assert first_good_if.start() == all_if_stmts[0].start()
|
|
3211
|
-
|
|
3212
|
-
# the if-stmt immediately after the first one should be a true check on -1
|
|
3213
|
-
second_good_if = re.search("if \\(.*? == -1\\)", text)
|
|
3214
|
-
assert second_good_if is not None
|
|
3215
|
-
assert second_good_if.start() == all_if_stmts[1].start()
|
|
3216
|
-
|
|
3217
|
-
@structuring_algo("phoenix")
|
|
3218
|
-
def test_ifelseflatten_certtool_common(self, decompiler_options=None):
|
|
3219
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "certtool-common.o")
|
|
3220
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3221
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3222
|
-
|
|
3223
|
-
f = proj.kb.functions["cipher_to_flags"]
|
|
3224
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
3225
|
-
d = proj.analyses[Decompiler](f, cfg=cfg.model, options=decompiler_options)
|
|
3226
|
-
|
|
3227
|
-
self._print_decompilation_result(d)
|
|
3228
|
-
text = d.codegen.text
|
|
3229
|
-
|
|
3230
|
-
# If any incorrect if-else flipping occurs, then there will be an if-stmt inside an if-stmt.
|
|
3231
|
-
# In the correct output, there should only ever be 2 scopes (the function, and a single if-scope) of
|
|
3232
|
-
# deepness in the full function. To verify this, we check that no scope of 3 deepness exists.
|
|
3233
|
-
|
|
3234
|
-
scope_prefix = " "
|
|
3235
|
-
bad_scope_prefix = scope_prefix * 3
|
|
3236
|
-
|
|
3237
|
-
assert scope_prefix in text
|
|
3238
|
-
assert bad_scope_prefix not in text
|
|
3239
|
-
|
|
3240
|
-
# TODO: fix me, this is a real bug
|
|
3241
|
-
# To double-check the structure, we will also verify that all if-conditions are of form `if(!<condition>)`,
|
|
3242
|
-
# since that is the correct form for this program.
|
|
3243
|
-
# bad_matches = re.findall(r'\bif\s*\(\s*[^!].*\)', text)
|
|
3244
|
-
# assert len(bad_matches) == 0
|
|
3245
|
-
|
|
3246
|
-
def test_test_binop_ret_dup(self, decompiler_options=None):
|
|
3247
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "test.o")
|
|
3248
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3249
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3250
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
3251
|
-
f = proj.kb.functions["binop"]
|
|
3252
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
3253
|
-
text = d.codegen.text
|
|
3254
|
-
|
|
3255
|
-
assert "{\n}" not in text
|
|
3256
|
-
assert "goto" not in text
|
|
3257
|
-
|
|
3258
|
-
def test_tail_tail_bytes_ret_dup(self, decompiler_options=None):
|
|
3259
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "tail.o")
|
|
3260
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3261
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3262
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
3263
|
-
f = proj.kb.functions["tail_bytes"]
|
|
3264
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
3265
|
-
text = d.codegen.text
|
|
3266
|
-
|
|
3267
|
-
assert "{\n}" not in text
|
|
3268
|
-
# TODO: should be 0, but we got the wrong address from the GotoManager
|
|
3269
|
-
# and our virtualization choice is not optimal
|
|
3270
|
-
assert text.count("goto") <= 2
|
|
3271
|
-
|
|
3272
|
-
def test_dd_iread_ret_dup_region(self, decompiler_options=None):
|
|
3273
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "dd.o")
|
|
3274
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3275
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3276
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
3277
|
-
f = proj.kb.functions["iread"]
|
|
3278
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
3279
|
-
text = d.codegen.text
|
|
3280
|
-
|
|
3281
|
-
assert "{\n}" not in text
|
|
3282
|
-
assert "goto" not in text
|
|
3283
|
-
# there are 4 or less in the source
|
|
3284
|
-
assert text.count("return") <= 4
|
|
3285
|
-
|
|
3286
|
-
def test_stty_recover_mode_ret_dup_region(self, decompiler_options=None):
|
|
3287
|
-
bin_path = os.path.join(test_location, "x86_64", "decompiler", "stty.o")
|
|
3288
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3289
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3290
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True)
|
|
3291
|
-
f = proj.kb.functions["recover_mode"]
|
|
3292
|
-
d = proj.analyses[Decompiler].prep()(f, cfg=cfg.model, options=decompiler_options)
|
|
3293
|
-
text = d.codegen.text
|
|
3294
|
-
|
|
3295
|
-
assert "{\n}" not in text
|
|
3296
|
-
assert "goto" not in text
|
|
3297
|
-
# there are 4 or less in the source
|
|
3298
|
-
assert text.count("return") <= 4
|
|
3299
|
-
|
|
3300
|
-
def test_plt_stub_annotation(self):
|
|
3301
|
-
bin_path = os.path.join(test_location, "x86_64", "fauxware")
|
|
3302
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3303
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3304
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
3305
|
-
|
|
3306
|
-
func = proj.kb.functions.function(name="puts", plt=True)
|
|
3307
|
-
d = proj.analyses[Decompiler](func, cfg=cfg.model)
|
|
3308
|
-
assert "PLT stub" in d.codegen.text
|
|
3309
|
-
|
|
3310
|
-
def test_name_disambiguation(self):
|
|
3311
|
-
bin_path = os.path.join(test_location, "x86_64", "fauxware")
|
|
3312
|
-
proj = angr.Project(bin_path, auto_load_libs=False)
|
|
3313
|
-
cfg = proj.analyses.CFGFast(normalize=True, data_references=True)
|
|
3314
|
-
proj.analyses.CompleteCallingConventions(cfg=cfg, recover_variables=True, analyze_callsites=True)
|
|
3315
|
-
|
|
3316
|
-
# Test function has same name as local variable
|
|
3317
|
-
d = proj.analyses[Decompiler]("main", cfg=cfg.model)
|
|
3318
|
-
vars_in_use = list(d.codegen.cfunc.variables_in_use.values())
|
|
3319
|
-
vars_in_use[0].variable.name = "puts"
|
|
3320
|
-
vars_in_use[0].variable.renamed = True
|
|
3321
|
-
d.codegen.regenerate_text()
|
|
3322
|
-
assert "::puts" in d.codegen.text
|
|
3323
|
-
|
|
3324
|
-
# Test function has same name as another function
|
|
3325
|
-
d = proj.analyses[Decompiler]("main", cfg=cfg.model)
|
|
3326
|
-
proj.kb.functions["authenticate"].name = "puts"
|
|
3327
|
-
d.codegen.regenerate_text()
|
|
3328
|
-
assert "::0x400510::puts" in d.codegen.text
|
|
3329
|
-
|
|
3330
|
-
# Test function has same name as calling function (PLT stub)
|
|
3331
|
-
d = proj.analyses[Decompiler](proj.kb.functions.function(name="puts", plt=True), cfg=cfg.model)
|
|
3332
|
-
assert "::libc.so.0::puts" in d.codegen.text
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
if __name__ == "__main__":
|
|
3336
|
-
unittest.main()
|