angr 9.2.139__py3-none-manylinux2014_x86_64.whl → 9.2.141__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 (87) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +136 -53
  3. angr/analyses/calling_convention/fact_collector.py +44 -18
  4. angr/analyses/calling_convention/utils.py +3 -1
  5. angr/analyses/cfg/cfg_base.py +13 -0
  6. angr/analyses/cfg/cfg_fast.py +11 -0
  7. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +9 -8
  8. angr/analyses/decompiler/ail_simplifier.py +115 -72
  9. angr/analyses/decompiler/callsite_maker.py +24 -11
  10. angr/analyses/decompiler/clinic.py +78 -43
  11. angr/analyses/decompiler/decompiler.py +18 -7
  12. angr/analyses/decompiler/expression_narrower.py +1 -1
  13. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  14. angr/analyses/decompiler/optimization_passes/duplication_reverter/duplication_reverter.py +3 -1
  15. angr/analyses/decompiler/optimization_passes/flip_boolean_cmp.py +21 -2
  16. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  17. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +84 -15
  18. angr/analyses/decompiler/optimization_passes/optimization_pass.py +92 -11
  19. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +53 -9
  20. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +44 -7
  21. angr/analyses/decompiler/region_identifier.py +6 -4
  22. angr/analyses/decompiler/region_simplifiers/expr_folding.py +287 -122
  23. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +31 -13
  24. angr/analyses/decompiler/ssailification/rewriting.py +23 -15
  25. angr/analyses/decompiler/ssailification/rewriting_engine.py +105 -24
  26. angr/analyses/decompiler/ssailification/ssailification.py +22 -14
  27. angr/analyses/decompiler/structured_codegen/c.py +73 -137
  28. angr/analyses/decompiler/structuring/dream.py +22 -18
  29. angr/analyses/decompiler/structuring/phoenix.py +158 -41
  30. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  31. angr/analyses/decompiler/structuring/structurer_base.py +37 -10
  32. angr/analyses/decompiler/structuring/structurer_nodes.py +4 -1
  33. angr/analyses/decompiler/utils.py +106 -21
  34. angr/analyses/deobfuscator/api_obf_finder.py +8 -5
  35. angr/analyses/deobfuscator/api_obf_type2_finder.py +18 -10
  36. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  37. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  38. angr/analyses/propagator/top_checker_mixin.py +6 -6
  39. angr/analyses/reaching_definitions/__init__.py +2 -1
  40. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  41. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  42. angr/analyses/reaching_definitions/function_handler.py +15 -2
  43. angr/analyses/reaching_definitions/rd_state.py +1 -37
  44. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  45. angr/analyses/s_propagator.py +6 -41
  46. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  47. angr/analyses/s_reaching_definitions/s_rda_view.py +43 -25
  48. angr/analyses/stack_pointer_tracker.py +36 -22
  49. angr/analyses/typehoon/simple_solver.py +45 -7
  50. angr/analyses/typehoon/typeconsts.py +18 -5
  51. angr/analyses/variable_recovery/engine_ail.py +1 -1
  52. angr/analyses/variable_recovery/engine_base.py +7 -5
  53. angr/analyses/variable_recovery/engine_vex.py +20 -4
  54. angr/block.py +69 -107
  55. angr/callable.py +14 -7
  56. angr/calling_conventions.py +30 -11
  57. angr/distributed/__init__.py +1 -1
  58. angr/engines/__init__.py +7 -8
  59. angr/engines/engine.py +1 -120
  60. angr/engines/failure.py +2 -2
  61. angr/engines/hook.py +2 -2
  62. angr/engines/light/engine.py +2 -2
  63. angr/engines/pcode/engine.py +2 -14
  64. angr/engines/procedure.py +2 -2
  65. angr/engines/soot/engine.py +2 -2
  66. angr/engines/soot/statements/switch.py +1 -1
  67. angr/engines/successors.py +124 -11
  68. angr/engines/syscall.py +2 -2
  69. angr/engines/unicorn.py +3 -3
  70. angr/engines/vex/heavy/heavy.py +3 -15
  71. angr/factory.py +12 -22
  72. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  73. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  74. angr/knowledge_plugins/variables/variable_manager.py +7 -5
  75. angr/sim_type.py +19 -17
  76. angr/simos/simos.py +3 -1
  77. angr/state_plugins/plugin.py +19 -4
  78. angr/storage/memory_mixins/memory_mixin.py +1 -1
  79. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  80. angr/utils/ssa/__init__.py +119 -4
  81. angr/utils/types.py +48 -0
  82. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/METADATA +6 -6
  83. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/RECORD +87 -86
  84. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/LICENSE +0 -0
  85. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/WHEEL +0 -0
  86. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/entry_points.txt +0 -0
  87. {angr-9.2.139.dist-info → angr-9.2.141.dist-info}/top_level.txt +0 -0
@@ -89,6 +89,8 @@ class Clinic(Analysis):
89
89
  A Clinic deals with AILments.
90
90
  """
91
91
 
92
+ _ail_manager: ailment.Manager
93
+
92
94
  def __init__(
93
95
  self,
94
96
  func,
@@ -117,7 +119,6 @@ class Clinic(Analysis):
117
119
  desired_variables: set[str] | None = None,
118
120
  force_loop_single_exit: bool = True,
119
121
  complete_successors: bool = False,
120
- unsound_fix_abnormal_switches: bool = True,
121
122
  ):
122
123
  if not func.normalized and mode == ClinicMode.DECOMPILE:
123
124
  raise ValueError("Decompilation must work on normalized function graphs.")
@@ -135,7 +136,6 @@ class Clinic(Analysis):
135
136
  self.optimization_scratch = optimization_scratch if optimization_scratch is not None else {}
136
137
 
137
138
  self._func_graph: networkx.DiGraph | None = None
138
- self._ail_manager = None
139
139
  self._blocks_by_addr_and_size = {}
140
140
  self.entry_node_addr: tuple[int, int | None] = self.function.addr, None
141
141
 
@@ -149,12 +149,14 @@ class Clinic(Analysis):
149
149
  self._must_struct = must_struct
150
150
  self._reset_variable_names = reset_variable_names
151
151
  self._rewrite_ites_to_diamonds = rewrite_ites_to_diamonds
152
- self._unsound_fix_abnormal_switches = unsound_fix_abnormal_switches
153
152
  self.reaching_definitions: ReachingDefinitionsAnalysis | None = None
154
153
  self._cache = cache
155
154
  self._mode = mode
156
155
  self.vvar_id_start = vvar_id_start
157
156
  self.vvar_to_vvar: dict[int, int] | None = None
157
+ # during SSA conversion, we create secondary stack variables because they overlap and are larger than the
158
+ # actual stack variables. these secondary stack variables can be safely eliminated if not used by anything.
159
+ self.secondary_stackvars: set[int] = set()
158
160
 
159
161
  # inlining help
160
162
  self._sp_shift = sp_shift
@@ -167,6 +169,7 @@ class Clinic(Analysis):
167
169
  self._complete_successors = complete_successors
168
170
 
169
171
  self._register_save_areas_removed: bool = False
172
+ self.edges_to_remove: list[tuple[tuple[int, int | None], tuple[int, int | None]]] = []
170
173
 
171
174
  self._new_block_addrs = set()
172
175
 
@@ -209,6 +212,7 @@ class Clinic(Analysis):
209
212
 
210
213
  :return:
211
214
  """
215
+ assert self.graph is not None
212
216
 
213
217
  s = ""
214
218
 
@@ -297,6 +301,8 @@ class Clinic(Analysis):
297
301
  return ail_graph
298
302
 
299
303
  def _slice_variables(self, ail_graph):
304
+ assert self.variable_kb is not None
305
+
300
306
  nodes_index = {(n.addr, n.idx): n for n in ail_graph.nodes()}
301
307
 
302
308
  vfm = self.variable_kb.variables.function_managers[self.function.addr]
@@ -344,7 +350,7 @@ class Clinic(Analysis):
344
350
  optimization_passes=[StackCanarySimplifier],
345
351
  sp_shift=self._max_stack_depth,
346
352
  vvar_id_start=self.vvar_id_start,
347
- fail_fast=self._fail_fast,
353
+ fail_fast=self._fail_fast, # type: ignore
348
354
  )
349
355
  self.vvar_id_start = callee_clinic.vvar_id_start + 1
350
356
  self._max_stack_depth = callee_clinic._max_stack_depth
@@ -618,6 +624,7 @@ class Clinic(Analysis):
618
624
 
619
625
  # remove empty nodes from the graph
620
626
  ail_graph = self.remove_empty_nodes(ail_graph)
627
+ # note that there are still edges to remove before we can structure this graph!
621
628
 
622
629
  self.arg_list = arg_list
623
630
  self.arg_vvars = arg_vvars
@@ -800,7 +807,7 @@ class Clinic(Analysis):
800
807
 
801
808
  # case 2: the callee is a SimProcedure
802
809
  if target_func.is_simprocedure:
803
- cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast)
810
+ cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast) # type: ignore
804
811
  if cc.cc is not None and cc.prototype is not None:
805
812
  target_func.calling_convention = cc.cc
806
813
  target_func.prototype = cc.prototype
@@ -808,7 +815,7 @@ class Clinic(Analysis):
808
815
 
809
816
  # case 3: the callee is a PLT function
810
817
  if target_func.is_plt:
811
- cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast)
818
+ cc = self.project.analyses.CallingConvention(target_func, fail_fast=self._fail_fast) # type: ignore
812
819
  if cc.cc is not None and cc.prototype is not None:
813
820
  target_func.calling_convention = cc.cc
814
821
  target_func.prototype = cc.prototype
@@ -878,7 +885,7 @@ class Clinic(Analysis):
878
885
  # finally, recover the calling convention of the current function
879
886
  if self.function.prototype is None or self.function.calling_convention is None:
880
887
  self.project.analyses.CompleteCallingConventions(
881
- fail_fast=self._fail_fast,
888
+ fail_fast=self._fail_fast, # type: ignore
882
889
  recover_variables=True,
883
890
  prioritize_func_addrs=[self.function.addr],
884
891
  skip_other_funcs=True,
@@ -1235,6 +1242,7 @@ class Clinic(Analysis):
1235
1242
  rewrite_ccalls=rewrite_ccalls,
1236
1243
  removed_vvar_ids=removed_vvar_ids,
1237
1244
  arg_vvars=arg_vvars,
1245
+ secondary_stackvars=self.secondary_stackvars,
1238
1246
  )
1239
1247
  # cache the simplifier's RDA analysis
1240
1248
  self.reaching_definitions = simp._reaching_definitions
@@ -1360,6 +1368,7 @@ class Clinic(Analysis):
1360
1368
  vvar_id_start=self.vvar_id_start,
1361
1369
  )
1362
1370
  self.vvar_id_start = ssailification.max_vvar_id + 1
1371
+ self.secondary_stackvars = ssailification.secondary_stackvars
1363
1372
  return ssailification.out_graph
1364
1373
 
1365
1374
  @timethis
@@ -1860,6 +1869,11 @@ class Clinic(Analysis):
1860
1869
  if expr.guard:
1861
1870
  self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.guard)
1862
1871
 
1872
+ elif isinstance(expr, ailment.Expr.Phi):
1873
+ for _, vvar in expr.src_and_vvars:
1874
+ if vvar is not None:
1875
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, vvar)
1876
+
1863
1877
  def _function_graph_to_ail_graph(self, func_graph, blocks_by_addr_and_size=None):
1864
1878
  if blocks_by_addr_and_size is None:
1865
1879
  blocks_by_addr_and_size = self._blocks_by_addr_and_size
@@ -2109,49 +2123,60 @@ class Clinic(Analysis):
2109
2123
  # the overlapped instructions and add an unconditional jump so that it jumps to 0x41da9d.
2110
2124
  # this is the most common case created by jump threading optimization in compilers. it's easy to handle.
2111
2125
 
2112
- # Case 2: the intended head and the other heads do not share the same suffix of instructions. in this case,
2113
- # we cannot reliably convert the blocks into a properly structured switch-case construct. we will alter the
2114
- # last instruction of all other heads to jump to the cmp instruction in the intended head, but do not remove
2115
- # any other instructions in these other heads. this is unsound, but is the best we can do in this case.
2126
+ # Case 2 & 3: the intended head and the other heads do not share the same suffix of instructions. in this
2127
+ # case, we have two choices:
2128
+ # Case 2: The intended head has two successors, but at least one unintended head has only one successor.
2129
+ # we cannot reliably convert the blocks into a properly structured switch-case construct. we will
2130
+ # last instruction of all other heads to jump to the cmp instruction in the intended head, but do
2131
+ # not remove any other instructions in these other heads. this is unsound, but is the best we can
2132
+ # do in this case.
2133
+ # Case 3: The intended head has only one successor (which is the indirect jump node). during structuring,
2134
+ # we expect it will be structured as a no-default-node switch-case construct. in this case, we
2135
+ # can simply remove the edges from all other heads to the jump node and only leave the edge from
2136
+ # the intended head to the jump node. we will see goto statements in the output, but this will
2137
+ # lead to correct structuring result.
2116
2138
 
2117
2139
  overlaps = [self._get_overlapping_suffix_instructions(intended_head, head) for head in other_heads]
2118
2140
  if overlaps and (overlap := min(overlaps)) > 0:
2119
2141
  # Case 1
2120
2142
  self._fix_abnormal_switch_case_heads_case1(ail_graph, candidate, intended_head, other_heads, overlap)
2121
- else:
2122
- if self._unsound_fix_abnormal_switches:
2123
- # Case 2
2124
- l.warning("Switch-case at %#x has multiple head nodes but cannot be fixed soundly.", candidate.addr)
2125
- # find the comparison instruction in the intended head
2126
- comparison_stmt = None
2127
- if "cc_op" in self.project.arch.registers:
2128
- comparison_stmt = next(
2129
- iter(
2130
- stmt
2131
- for stmt in intended_head.statements
2132
- if isinstance(stmt, ailment.Stmt.Assignment)
2133
- and isinstance(stmt.dst, ailment.Expr.Register)
2134
- and stmt.dst.reg_offset == self.project.arch.registers["cc_op"][0]
2135
- ),
2136
- None,
2137
- )
2138
- intended_head_block = self.project.factory.block(
2139
- intended_head.addr, size=intended_head.original_size
2143
+ elif ail_graph.out_degree[intended_head] == 2:
2144
+ # Case 2
2145
+ l.warning("Switch-case at %#x has multiple head nodes but cannot be fixed soundly.", candidate.addr)
2146
+ # find the comparison instruction in the intended head
2147
+ comparison_stmt = None
2148
+ if "cc_op" in self.project.arch.registers:
2149
+ comparison_stmt = next(
2150
+ iter(
2151
+ stmt
2152
+ for stmt in intended_head.statements
2153
+ if isinstance(stmt, ailment.Stmt.Assignment)
2154
+ and isinstance(stmt.dst, ailment.Expr.Register)
2155
+ and stmt.dst.reg_offset == self.project.arch.registers["cc_op"][0]
2156
+ ),
2157
+ None,
2140
2158
  )
2141
- if comparison_stmt is not None:
2142
- cmp_rpos = len(
2143
- intended_head_block.instruction_addrs
2144
- ) - intended_head_block.instruction_addrs.index(comparison_stmt.ins_addr)
2145
- else:
2146
- cmp_rpos = min(len(intended_head_block.instruction_addrs), 2)
2147
- self._fix_abnormal_switch_case_heads_case2(
2148
- ail_graph,
2149
- candidate,
2150
- intended_head,
2151
- other_heads,
2152
- intended_head_split_insns=cmp_rpos,
2153
- other_head_split_insns=0,
2159
+ intended_head_block = self.project.factory.block(intended_head.addr, size=intended_head.original_size)
2160
+ if comparison_stmt is not None:
2161
+ cmp_rpos = len(intended_head_block.instruction_addrs) - intended_head_block.instruction_addrs.index(
2162
+ comparison_stmt.ins_addr
2154
2163
  )
2164
+ else:
2165
+ cmp_rpos = min(len(intended_head_block.instruction_addrs), 2)
2166
+ self._fix_abnormal_switch_case_heads_case2(
2167
+ ail_graph,
2168
+ candidate,
2169
+ intended_head,
2170
+ other_heads,
2171
+ intended_head_split_insns=cmp_rpos,
2172
+ other_head_split_insns=0,
2173
+ )
2174
+ else:
2175
+ # Case 3
2176
+ self._fix_abnormal_switch_case_heads_case3(
2177
+ candidate,
2178
+ other_heads,
2179
+ )
2155
2180
 
2156
2181
  def _get_overlapping_suffix_instructions(self, ailblock_0: ailment.Block, ailblock_1: ailment.Block) -> int:
2157
2182
  # we first compare their ending conditional jumps
@@ -2360,6 +2385,16 @@ class Clinic(Analysis):
2360
2385
  # it should be going to the default node. ignore it
2361
2386
  pass
2362
2387
 
2388
+ def _fix_abnormal_switch_case_heads_case3(
2389
+ self, indirect_jump_node: ailment.Block, other_heads: list[ailment.Block]
2390
+ ) -> None:
2391
+ # remove all edges from other_heads to the indirect jump node
2392
+ for other_head in other_heads:
2393
+ # delay the edge removal so that we don't mess up the SSA analysis
2394
+ self.edges_to_remove.append(
2395
+ ((other_head.addr, other_head.idx), (indirect_jump_node.addr, indirect_jump_node.idx))
2396
+ )
2397
+
2363
2398
  @staticmethod
2364
2399
  def _remove_redundant_jump_blocks(ail_graph):
2365
2400
  def first_conditional_jump(block: ailment.Block) -> ailment.Stmt.ConditionalJump | None:
@@ -2,8 +2,8 @@
2
2
  from __future__ import annotations
3
3
  import logging
4
4
  from collections import defaultdict
5
- from typing import Optional, Union, Any, TYPE_CHECKING
6
5
  from collections.abc import Iterable
6
+ from typing import Optional, Union, Any, TYPE_CHECKING
7
7
 
8
8
  import networkx
9
9
  from cle import SymbolType
@@ -15,6 +15,7 @@ from angr.knowledge_base import KnowledgeBase
15
15
  from angr.sim_variable import SimMemoryVariable, SimRegisterVariable, SimStackVariable
16
16
  from angr.utils import timethis
17
17
  from angr.analyses import Analysis, AnalysesHub
18
+ from .structured_codegen.c import CStructuredCodeGenerator
18
19
  from .structuring import RecursiveStructurer, PhoenixStructurer, DEFAULT_STRUCTURER
19
20
  from .region_identifier import RegionIdentifier
20
21
  from .optimization_passes.optimization_pass import OptimizationPassStage
@@ -22,7 +23,7 @@ from .ailgraph_walker import AILGraphWalker
22
23
  from .condition_processor import ConditionProcessor
23
24
  from .decompilation_options import DecompilationOption
24
25
  from .decompilation_cache import DecompilationCache
25
- from .utils import remove_labels
26
+ from .utils import remove_labels, remove_edges_in_ailgraph
26
27
  from .sequence_walker import SequenceWalker
27
28
  from .structuring.structurer_nodes import SequenceNode
28
29
  from .presets import DECOMPILATION_PRESETS, DecompilationPreset
@@ -30,7 +31,6 @@ from .presets import DECOMPILATION_PRESETS, DecompilationPreset
30
31
  if TYPE_CHECKING:
31
32
  from angr.knowledge_plugins.cfg.cfg_model import CFGModel
32
33
  from .peephole_optimizations import PeepholeOptimizationExprBase, PeepholeOptimizationStmtBase
33
- from .structured_codegen.c import CStructuredCodeGenerator
34
34
 
35
35
  l = logging.getLogger(name=__name__)
36
36
 
@@ -157,6 +157,8 @@ class Decompiler(Analysis):
157
157
  self.kb.decompilations[(self.func.addr, self._flavor)].errors.append(error.format())
158
158
 
159
159
  def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
160
+ if self._cache_parameters is None or cache.parameters is None:
161
+ return False
160
162
  a, b = self._cache_parameters, cache.parameters
161
163
  id_checks = {"cfg", "variable_kb"}
162
164
  return all(a[k] is b[k] if k in id_checks else a[k] == b[k] for k in self._cache_parameters)
@@ -201,7 +203,7 @@ class Decompiler(Analysis):
201
203
 
202
204
  variable_kb = self._variable_kb
203
205
  # fall back to old codegen
204
- if variable_kb is None and old_codegen is not None:
206
+ if variable_kb is None and old_codegen is not None and isinstance(old_codegen, CStructuredCodeGenerator):
205
207
  variable_kb = old_codegen._variable_kb
206
208
 
207
209
  if variable_kb is None:
@@ -223,7 +225,8 @@ class Decompiler(Analysis):
223
225
  fold_callexprs_into_conditions = True
224
226
 
225
227
  cache = DecompilationCache(self.func.addr)
226
- cache.parameters = self._cache_parameters
228
+ if self._cache_parameters is not None:
229
+ cache.parameters = self._cache_parameters
227
230
  cache.ite_exprs = ite_exprs
228
231
  cache.binop_operators = binop_operators
229
232
 
@@ -296,8 +299,14 @@ class Decompiler(Analysis):
296
299
  ri,
297
300
  clinic.reaching_definitions,
298
301
  ite_exprs=ite_exprs,
302
+ arg_vvars=set(clinic.arg_vvars),
303
+ edges_to_remove=clinic.edges_to_remove,
299
304
  )
300
305
 
306
+ # finally (no more graph-based simplifications will run in the future), we can remove the edges that should be
307
+ # removed!
308
+ remove_edges_in_ailgraph(clinic.graph, clinic.edges_to_remove)
309
+
301
310
  # Rewrite the graph to remove phi expressions
302
311
  # this is probably optional if we do not pretty-print clinic.graph
303
312
  clinic.graph = self._transform_graph_from_ssa(clinic.graph)
@@ -325,9 +334,9 @@ class Decompiler(Analysis):
325
334
  s = self.project.analyses.RegionSimplifier(
326
335
  self.func,
327
336
  rs.result,
337
+ arg_vvars=set(self.clinic.arg_vvars),
328
338
  kb=self.kb,
329
339
  fail_fast=self._fail_fast,
330
- variable_kb=clinic.variable_kb,
331
340
  **self.options_to_params(self.options_by_class["region_simplifier"]),
332
341
  )
333
342
  seq_node = s.result
@@ -439,7 +448,7 @@ class Decompiler(Analysis):
439
448
  return ail_graph
440
449
 
441
450
  @timethis
442
- def _run_region_simplification_passes(self, ail_graph, ri, reaching_definitions, **kwargs):
451
+ def _run_region_simplification_passes(self, ail_graph, ri, reaching_definitions, arg_vvars: set[int], **kwargs):
443
452
  """
444
453
  Runs optimizations that should be executed after a single region identification. This function will return
445
454
  two items: the new RegionIdentifier object and the new AIL Graph, which should probably be written
@@ -483,6 +492,7 @@ class Decompiler(Analysis):
483
492
  blocks_by_addr_and_idx=addr_and_idx_to_blocks,
484
493
  graph=ail_graph,
485
494
  variable_kb=self._variable_kb,
495
+ arg_vvars=arg_vvars,
486
496
  region_identifier=ri,
487
497
  reaching_definitions=reaching_definitions,
488
498
  vvar_id_start=self.vvar_id_start,
@@ -490,6 +500,7 @@ class Decompiler(Analysis):
490
500
  scratch=self._optimization_scratch,
491
501
  force_loop_single_exit=self._force_loop_single_exit,
492
502
  complete_successors=self._complete_successors,
503
+ peephole_optimizations=self._peephole_optimizations,
493
504
  **kwargs,
494
505
  )
495
506
 
@@ -38,7 +38,7 @@ class ExprNarrowingInfo:
38
38
  narrowable: bool,
39
39
  to_size: int | None = None,
40
40
  use_exprs: list[tuple[atoms.VirtualVariable, CodeLocation, tuple[str, tuple[Expression, ...]]]] | None = None,
41
- phi_vars: set[atoms.VirtualVariable] | None = None,
41
+ phi_vars: set[VirtualVariable] | None = None,
42
42
  ):
43
43
  self.narrowable = narrowable
44
44
  self.to_size = to_size
@@ -8,7 +8,7 @@ import claripy
8
8
  from ailment import Const
9
9
  from ailment.block_walker import AILBlockWalkerBase
10
10
  from ailment.statement import Call, Statement, ConditionalJump, Assignment, Store, Return
11
- from ailment.expression import Convert, Register, Expression
11
+ from ailment.expression import Convert, Register, Expression, Load
12
12
 
13
13
  from .optimization_pass import OptimizationPass, OptimizationPassStage
14
14
  from angr.analyses.decompiler.structuring import SAILRStructurer, DreamStructurer
@@ -207,16 +207,17 @@ class ConstPropOptReverter(OptimizationPass):
207
207
  continue
208
208
 
209
209
  unwrapped_sym_arg = sym_arg.operands[0] if isinstance(sym_arg, Convert) else sym_arg
210
- try:
210
+ if (
211
+ isinstance(unwrapped_sym_arg, Load)
212
+ and isinstance(unwrapped_sym_arg.addr, Const)
213
+ and isinstance(unwrapped_sym_arg.addr.value, int)
214
+ ):
211
215
  # TODO: make this support more than just Loads
212
216
  # target must be a Load of a memory location
213
217
  target_atom = MemoryLocation(unwrapped_sym_arg.addr.value, unwrapped_sym_arg.size, "Iend_LE")
214
218
  const_state = self.rd.get_reaching_definitions_by_node(blks[calls[const_arg]].addr, OP_BEFORE)
215
-
216
- state_load_vals = const_state.get_value_from_atom(target_atom)
217
- except AttributeError:
218
- continue
219
- except KeyError:
219
+ state_load_vals = const_state.get_values(target_atom)
220
+ else:
220
221
  continue
221
222
 
222
223
  if not state_load_vals:
@@ -950,7 +950,9 @@ class DuplicationReverter(StructuringOptimizationPass):
950
950
  #
951
951
 
952
952
  def _share_subregion(self, blocks: list[Block]) -> bool:
953
- return any(all(block.addr in region for block in blocks) for region in self._ri.regions_by_block_addrs)
953
+ return any(
954
+ all((block.addr, block.idx) in region for block in blocks) for region in self._ri.regions_by_block_addrs
955
+ )
954
956
 
955
957
  def _is_valid_candidate(self, b0, b1):
956
958
  # blocks must have statements
@@ -6,7 +6,11 @@ import ailment
6
6
  from ailment.expression import Op
7
7
 
8
8
  from angr.analyses.decompiler.structuring.structurer_nodes import ConditionNode
9
- from angr.analyses.decompiler.utils import structured_node_is_simple_return, sequence_to_statements
9
+ from angr.analyses.decompiler.utils import (
10
+ structured_node_is_simple_return,
11
+ sequence_to_statements,
12
+ structured_node_has_multi_predecessors,
13
+ )
10
14
  from angr.analyses.decompiler.sequence_walker import SequenceWalker
11
15
  from .optimization_pass import SequenceOptimizationPass, OptimizationPassStage
12
16
 
@@ -43,7 +47,22 @@ class FlipBooleanWalker(SequenceWalker):
43
47
  and structured_node_is_simple_return(seq_node.nodes[idx + 1], self._graph)
44
48
  and node not in type1_condition_nodes
45
49
  ):
46
- type2_condition_nodes.append((idx, node, seq_node.nodes[idx + 1]))
50
+ # Type 2: Special Filter:
51
+ # consider code that looks like the following:
52
+ # {if (cond) {LABEL: ... } return;}; goto LABEL;
53
+ #
54
+ # if we were to do the normal flip, this happens:
55
+ # {if (!cond) return; LABEL: ...}; goto LABEL;
56
+ #
57
+ # This is incorrect because we've now created an infinite loop in the event that cond is false,
58
+ # which is not what the original code was. The gist here is that you can't ever flip these cases
59
+ # in the presence of more than one incoming edge to `...` region.
60
+ #
61
+ # To eliminate this illegal case, we simply need to find all the condition nodes of the above structure
62
+ # that have multiple incoming edges to the `...` region.
63
+ illegal_flip = structured_node_has_multi_predecessors(node.true_node, self._graph)
64
+ if not illegal_flip:
65
+ type2_condition_nodes.append((idx, node, seq_node.nodes[idx + 1]))
47
66
 
48
67
  for node in type1_condition_nodes:
49
68
  if isinstance(node.condition, Op) and structured_node_is_simple_return(node.false_node, self._graph):
@@ -287,19 +287,27 @@ class ITERegionConverter(OptimizationPass):
287
287
  ((region_head.addr, region_head.idx), original_vvars[0] if original_vvars else None)
288
288
  )
289
289
 
290
- new_phi = Phi(
291
- stmt.src.idx,
292
- stmt.src.bits,
293
- new_src_and_vvars,
294
- **stmt.src.tags,
295
- )
296
- new_phi_assignment = Assignment(
297
- stmt.idx,
298
- stmt.dst,
299
- new_phi,
300
- **stmt.tags,
301
- )
302
- stmts.append(new_phi_assignment)
290
+ if len(new_src_and_vvars) == 1:
291
+ new_assignment = Assignment(
292
+ stmt.idx,
293
+ stmt.dst,
294
+ new_src_and_vvars[0][1],
295
+ **stmt.tags,
296
+ )
297
+ else:
298
+ new_phi = Phi(
299
+ stmt.src.idx,
300
+ stmt.src.bits,
301
+ new_src_and_vvars,
302
+ **stmt.src.tags,
303
+ )
304
+ new_assignment = Assignment(
305
+ stmt.idx,
306
+ stmt.dst,
307
+ new_phi,
308
+ **stmt.tags,
309
+ )
310
+ stmts.append(new_assignment)
303
311
  new_region_tail = Block(region_tail.addr, region_tail.original_size, statements=stmts, idx=region_tail.idx)
304
312
 
305
313
  #