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.

Files changed (248) hide show
  1. angr/__init__.py +4 -1
  2. angr/analyses/decompiler/clinic.py +16 -0
  3. angr/analyses/decompiler/decompiler.py +3 -0
  4. angr/analyses/decompiler/optimization_passes/__init__.py +5 -0
  5. angr/analyses/decompiler/optimization_passes/cross_jump_reverter.py +108 -0
  6. angr/analyses/decompiler/optimization_passes/optimization_pass.py +17 -4
  7. angr/analyses/decompiler/optimization_passes/return_duplicator.py +4 -32
  8. angr/analyses/decompiler/structured_codegen/c.py +12 -2
  9. angr/analyses/decompiler/utils.py +13 -0
  10. angr/analyses/typehoon/dfa.py +108 -0
  11. angr/analyses/typehoon/lifter.py +34 -2
  12. angr/analyses/typehoon/simple_solver.py +1043 -503
  13. angr/analyses/typehoon/translator.py +13 -4
  14. angr/analyses/typehoon/typeconsts.py +117 -36
  15. angr/analyses/typehoon/typehoon.py +31 -11
  16. angr/analyses/typehoon/typevars.py +88 -21
  17. angr/analyses/typehoon/variance.py +10 -0
  18. angr/analyses/variable_recovery/engine_ail.py +28 -9
  19. angr/analyses/variable_recovery/engine_base.py +50 -43
  20. angr/analyses/variable_recovery/variable_recovery_base.py +16 -3
  21. angr/analyses/variable_recovery/variable_recovery_fast.py +14 -5
  22. angr/exploration_techniques/tracer.py +2 -0
  23. angr/misc/autoimport.py +26 -0
  24. angr/procedures/definitions/__init__.py +32 -3
  25. angr/utils/constants.py +1 -0
  26. angr/utils/graph.py +20 -1
  27. {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/METADATA +7 -6
  28. {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/RECORD +32 -244
  29. angr-9.2.89.dist-info/top_level.txt +1 -0
  30. angr/procedures/definitions/ntdll.py +0 -12
  31. angr-9.2.87.dist-info/top_level.txt +0 -2
  32. tests/__init__.py +0 -0
  33. tests/analyses/__init__.py +0 -0
  34. tests/analyses/cfg/__init__.py +0 -0
  35. tests/analyses/cfg/test_cfg_clflush.py +0 -43
  36. tests/analyses/cfg/test_cfg_get_any_node.py +0 -34
  37. tests/analyses/cfg/test_cfg_manager.py +0 -32
  38. tests/analyses/cfg/test_cfg_model.py +0 -55
  39. tests/analyses/cfg/test_cfg_patching.py +0 -378
  40. tests/analyses/cfg/test_cfg_rust_got_resolution.py +0 -36
  41. tests/analyses/cfg/test_cfg_thumb_firmware.py +0 -50
  42. tests/analyses/cfg/test_cfg_vex_postprocessor.py +0 -27
  43. tests/analyses/cfg/test_cfgemulated.py +0 -634
  44. tests/analyses/cfg/test_cfgfast.py +0 -1123
  45. tests/analyses/cfg/test_cfgfast_soot.py +0 -38
  46. tests/analyses/cfg/test_const_resolver.py +0 -38
  47. tests/analyses/cfg/test_iat_resolver.py +0 -37
  48. tests/analyses/cfg/test_jumptables.py +0 -3008
  49. tests/analyses/cfg/test_noop_blocks.py +0 -54
  50. tests/analyses/cfg_slice_to_sink/__init__.py +0 -0
  51. tests/analyses/cfg_slice_to_sink/test_cfg_slice_to_sink.py +0 -93
  52. tests/analyses/cfg_slice_to_sink/test_graph.py +0 -114
  53. tests/analyses/cfg_slice_to_sink/test_transitions.py +0 -28
  54. tests/analyses/decompiler/__init__.py +0 -0
  55. tests/analyses/decompiler/test_baseptr_save_simplifier.py +0 -80
  56. tests/analyses/decompiler/test_decompiler.py +0 -3336
  57. tests/analyses/decompiler/test_peephole_optimizations.py +0 -48
  58. tests/analyses/decompiler/test_propagator_loops.py +0 -101
  59. tests/analyses/decompiler/test_structurer.py +0 -275
  60. tests/analyses/reaching_definitions/__init__.py +0 -0
  61. tests/analyses/reaching_definitions/test_dep_graph.py +0 -432
  62. tests/analyses/reaching_definitions/test_function_handler.py +0 -131
  63. tests/analyses/reaching_definitions/test_heap_allocator.py +0 -46
  64. tests/analyses/reaching_definitions/test_rd_state.py +0 -78
  65. tests/analyses/reaching_definitions/test_reachingdefinitions.py +0 -463
  66. tests/analyses/reaching_definitions/test_subject.py +0 -76
  67. tests/analyses/test_bindiff.py +0 -52
  68. tests/analyses/test_block_simplifier.py +0 -112
  69. tests/analyses/test_boyscout.py +0 -104
  70. tests/analyses/test_calling_convention_analysis.py +0 -352
  71. tests/analyses/test_callsite_maker.py +0 -60
  72. tests/analyses/test_cdg.py +0 -165
  73. tests/analyses/test_cfb.py +0 -37
  74. tests/analyses/test_class_identifier.py +0 -46
  75. tests/analyses/test_clinic.py +0 -30
  76. tests/analyses/test_codetagging.py +0 -32
  77. tests/analyses/test_constantpropagation.py +0 -88
  78. tests/analyses/test_ddg.py +0 -95
  79. tests/analyses/test_ddg_global_var_dependencies.py +0 -83
  80. tests/analyses/test_ddg_memvar_addresses.py +0 -40
  81. tests/analyses/test_disassembly.py +0 -121
  82. tests/analyses/test_find_objects_static.py +0 -35
  83. tests/analyses/test_flirt.py +0 -49
  84. tests/analyses/test_identifier.py +0 -33
  85. tests/analyses/test_init_finder.py +0 -38
  86. tests/analyses/test_proximitygraph.py +0 -31
  87. tests/analyses/test_reassembler.py +0 -295
  88. tests/analyses/test_regionidentifier.py +0 -27
  89. tests/analyses/test_slicing.py +0 -164
  90. tests/analyses/test_stack_pointer_tracker.py +0 -74
  91. tests/analyses/test_static_hooker.py +0 -28
  92. tests/analyses/test_typehoon.py +0 -55
  93. tests/analyses/test_variablerecovery.py +0 -464
  94. tests/analyses/test_vfg.py +0 -221
  95. tests/analyses/test_vtable.py +0 -31
  96. tests/analyses/test_xrefs.py +0 -77
  97. tests/common.py +0 -128
  98. tests/engines/__init__.py +0 -0
  99. tests/engines/light/__init__.py +0 -0
  100. tests/engines/light/test_data.py +0 -17
  101. tests/engines/pcode/__init__.py +0 -0
  102. tests/engines/pcode/test_emulate.py +0 -607
  103. tests/engines/pcode/test_pcode.py +0 -84
  104. tests/engines/test_actions.py +0 -27
  105. tests/engines/test_hook.py +0 -112
  106. tests/engines/test_java.py +0 -697
  107. tests/engines/test_unicorn.py +0 -518
  108. tests/engines/vex/__init__.py +0 -0
  109. tests/engines/vex/test_lifter.py +0 -124
  110. tests/engines/vex/test_vex.py +0 -574
  111. tests/exploration_techniques/__init__.py +0 -0
  112. tests/exploration_techniques/test_cacher.py +0 -45
  113. tests/exploration_techniques/test_director.py +0 -67
  114. tests/exploration_techniques/test_driller_core.py +0 -48
  115. tests/exploration_techniques/test_loop_seer.py +0 -158
  116. tests/exploration_techniques/test_memory_watcher.py +0 -46
  117. tests/exploration_techniques/test_oppologist.py +0 -65
  118. tests/exploration_techniques/test_spiller.py +0 -82
  119. tests/exploration_techniques/test_stochastic.py +0 -40
  120. tests/exploration_techniques/test_tech_builder.py +0 -61
  121. tests/exploration_techniques/test_tracer.py +0 -856
  122. tests/exploration_techniques/test_unique.py +0 -40
  123. tests/exploration_techniques/test_veritesting.py +0 -120
  124. tests/factory/__init__.py +0 -0
  125. tests/factory/block/__init__.py +0 -0
  126. tests/factory/block/test_block_cache.py +0 -33
  127. tests/factory/block/test_keystone.py +0 -106
  128. tests/factory/test_argc.py +0 -101
  129. tests/factory/test_argc_sym.py +0 -110
  130. tests/factory/test_argv.py +0 -158
  131. tests/factory/test_callable.py +0 -266
  132. tests/factory/test_windows_args.py +0 -36
  133. tests/knowledge_plugins/__init__.py +0 -0
  134. tests/knowledge_plugins/cfg/__init__.py +0 -0
  135. tests/knowledge_plugins/cfg/test_cfg_manager.py +0 -36
  136. tests/knowledge_plugins/functions/__init__.py +0 -0
  137. tests/knowledge_plugins/functions/test_function.py +0 -91
  138. tests/knowledge_plugins/functions/test_function2.py +0 -79
  139. tests/knowledge_plugins/functions/test_function_manager.py +0 -139
  140. tests/knowledge_plugins/functions/test_prototypes.py +0 -53
  141. tests/knowledge_plugins/key_definitions/__init__.py +0 -0
  142. tests/knowledge_plugins/key_definitions/test_atoms.py +0 -24
  143. tests/knowledge_plugins/key_definitions/test_environment.py +0 -126
  144. tests/knowledge_plugins/key_definitions/test_heap_address.py +0 -27
  145. tests/knowledge_plugins/key_definitions/test_live_definitions.py +0 -72
  146. tests/knowledge_plugins/test_dwarf_variables.py +0 -240
  147. tests/knowledge_plugins/test_kb_plugins.py +0 -91
  148. tests/knowledge_plugins/test_kb_plugins_dwarf.py +0 -36
  149. tests/knowledge_plugins/test_patches.py +0 -48
  150. tests/misc/__init__.py +0 -0
  151. tests/misc/test_hookset.py +0 -57
  152. tests/perf/__init__.py +0 -0
  153. tests/perf/perf_cfgemulated.py +0 -19
  154. tests/perf/perf_cfgfast.py +0 -18
  155. tests/perf/perf_concrete_execution.py +0 -41
  156. tests/perf/perf_siminspect_nop.py +0 -36
  157. tests/perf/perf_state_copy.py +0 -33
  158. tests/perf/perf_unicorn_0.py +0 -27
  159. tests/perf/perf_unicorn_1.py +0 -23
  160. tests/procedures/__init__.py +0 -0
  161. tests/procedures/glibc/__init__.py +0 -0
  162. tests/procedures/glibc/test_ctype_locale.py +0 -164
  163. tests/procedures/libc/__init__.py +0 -0
  164. tests/procedures/libc/test_fgets.py +0 -53
  165. tests/procedures/libc/test_scanf.py +0 -205
  166. tests/procedures/libc/test_sprintf.py +0 -44
  167. tests/procedures/libc/test_sscanf.py +0 -63
  168. tests/procedures/libc/test_strcasecmp.py +0 -37
  169. tests/procedures/libc/test_string.py +0 -1102
  170. tests/procedures/libc/test_strtol.py +0 -78
  171. tests/procedures/linux_kernel/__init__.py +0 -0
  172. tests/procedures/linux_kernel/test_lseek.py +0 -174
  173. tests/procedures/posix/__init__.py +0 -0
  174. tests/procedures/posix/test_chroot.py +0 -33
  175. tests/procedures/posix/test_getenv.py +0 -78
  176. tests/procedures/posix/test_pwrite_pread.py +0 -57
  177. tests/procedures/posix/test_sim_time.py +0 -46
  178. tests/procedures/posix/test_unlink.py +0 -46
  179. tests/procedures/test_project_resolve_simproc.py +0 -43
  180. tests/procedures/test_sim_procedure.py +0 -117
  181. tests/procedures/test_stub_procedure_args.py +0 -53
  182. tests/serialization/__init__.py +0 -0
  183. tests/serialization/test_db.py +0 -197
  184. tests/serialization/test_pickle.py +0 -95
  185. tests/serialization/test_serialization.py +0 -132
  186. tests/serialization/test_vault.py +0 -169
  187. tests/sim/__init__.py +0 -3
  188. tests/sim/exec_func/__init__.py +0 -0
  189. tests/sim/exec_func/test_mem_funcs.py +0 -55
  190. tests/sim/exec_func/test_str_funcs.py +0 -93
  191. tests/sim/exec_func/test_syscall_result.py +0 -39
  192. tests/sim/exec_insn/__init__.py +0 -0
  193. tests/sim/exec_insn/test_adc.py +0 -44
  194. tests/sim/exec_insn/test_ops.py +0 -83
  195. tests/sim/exec_insn/test_rcr.py +0 -26
  196. tests/sim/exec_insn/test_rol.py +0 -51
  197. tests/sim/exec_insn/test_signed_div.py +0 -34
  198. tests/sim/exec_insn/test_sqrt.py +0 -56
  199. tests/sim/options/__init__.py +0 -0
  200. tests/sim/options/test_0div.py +0 -54
  201. tests/sim/options/test_symbolic_fd.py +0 -59
  202. tests/sim/options/test_unsupported.py +0 -34
  203. tests/sim/test_accuracy.py +0 -137
  204. tests/sim/test_checkbyte.py +0 -53
  205. tests/sim/test_echo.py +0 -36
  206. tests/sim/test_fauxware.py +0 -202
  207. tests/sim/test_self_modifying_code.py +0 -65
  208. tests/sim/test_simple_api.py +0 -36
  209. tests/sim/test_simulation_manager.py +0 -147
  210. tests/sim/test_stack_alignment.py +0 -65
  211. tests/sim/test_state.py +0 -303
  212. tests/sim/test_state_customization.py +0 -54
  213. tests/sim/test_symbol_hooked_by.py +0 -49
  214. tests/simos/__init__.py +0 -0
  215. tests/simos/windows/__init__.py +0 -0
  216. tests/simos/windows/test_windows_stack_cookie.py +0 -58
  217. tests/state_plugins/__init__.py +0 -0
  218. tests/state_plugins/inspect/__init__.py +0 -0
  219. tests/state_plugins/inspect/test_inspect.py +0 -310
  220. tests/state_plugins/inspect/test_syscall_override.py +0 -90
  221. tests/state_plugins/posix/__init__.py +0 -0
  222. tests/state_plugins/posix/test_file_struct_funcs.py +0 -56
  223. tests/state_plugins/posix/test_files.py +0 -69
  224. tests/state_plugins/posix/test_posix.py +0 -72
  225. tests/state_plugins/solver/__init__.py +0 -0
  226. tests/state_plugins/solver/test_simsolver.py +0 -58
  227. tests/state_plugins/solver/test_symbolic.py +0 -153
  228. tests/state_plugins/solver/test_variable_registration.py +0 -46
  229. tests/state_plugins/test_callstack.py +0 -54
  230. tests/state_plugins/test_gdb_plugin.py +0 -35
  231. tests/state_plugins/test_multi_open_file.py +0 -47
  232. tests/state_plugins/test_symbolization.py +0 -38
  233. tests/storage/__init__.py +0 -0
  234. tests/storage/test_memory.py +0 -960
  235. tests/storage/test_memory_merge.py +0 -114
  236. tests/storage/test_memview.py +0 -205
  237. tests/storage/test_mmap.py +0 -26
  238. tests/storage/test_multivalues.py +0 -44
  239. tests/storage/test_permissions.py +0 -32
  240. tests/storage/test_ptmalloc.py +0 -291
  241. tests/storage/test_relro_perm.py +0 -49
  242. tests/test_calling_conventions.py +0 -86
  243. tests/test_types.py +0 -329
  244. tests/utils/__init__.py +0 -0
  245. tests/utils/test_graph.py +0 -41
  246. {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/LICENSE +0 -0
  247. {angr-9.2.87.dist-info → angr-9.2.89.dist-info}/WHEEL +0 -0
  248. {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()