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