angr 9.2.123__py3-none-manylinux2014_aarch64.whl → 9.2.125__py3-none-manylinux2014_aarch64.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 (103) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/__init__.py +9 -1
  3. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +11 -8
  4. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_got.py +2 -2
  5. angr/analyses/codecave.py +77 -0
  6. angr/analyses/decompiler/ail_simplifier.py +16 -19
  7. angr/analyses/decompiler/callsite_maker.py +8 -7
  8. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +24 -2
  9. angr/analyses/decompiler/clinic.py +58 -2
  10. angr/analyses/decompiler/condition_processor.py +10 -3
  11. angr/analyses/decompiler/decompilation_cache.py +2 -0
  12. angr/analyses/decompiler/decompiler.py +54 -8
  13. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +10 -2
  14. angr/analyses/decompiler/dephication/rewriting_engine.py +64 -1
  15. angr/analyses/decompiler/expression_narrower.py +5 -1
  16. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  17. angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
  18. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +13 -0
  19. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +23 -4
  20. angr/analyses/decompiler/optimization_passes/optimization_pass.py +3 -1
  21. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +8 -5
  22. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +10 -5
  23. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +18 -7
  24. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +6 -0
  25. angr/analyses/decompiler/optimization_passes/tag_slicer.py +41 -0
  26. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +2 -0
  27. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +2 -0
  28. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +2 -2
  29. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +8 -2
  30. angr/analyses/decompiler/region_identifier.py +36 -0
  31. angr/analyses/decompiler/region_simplifiers/loop.py +2 -8
  32. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +9 -3
  33. angr/analyses/decompiler/ssailification/rewriting.py +5 -2
  34. angr/analyses/decompiler/ssailification/rewriting_engine.py +151 -25
  35. angr/analyses/decompiler/ssailification/rewriting_state.py +1 -0
  36. angr/analyses/decompiler/ssailification/ssailification.py +17 -9
  37. angr/analyses/decompiler/ssailification/traversal.py +3 -1
  38. angr/analyses/decompiler/ssailification/traversal_engine.py +35 -8
  39. angr/analyses/decompiler/ssailification/traversal_state.py +1 -0
  40. angr/analyses/decompiler/structured_codegen/c.py +42 -4
  41. angr/analyses/decompiler/structuring/phoenix.py +3 -0
  42. angr/analyses/patchfinder.py +137 -0
  43. angr/analyses/pathfinder.py +282 -0
  44. angr/analyses/propagator/engine_ail.py +10 -3
  45. angr/analyses/reaching_definitions/engine_ail.py +10 -15
  46. angr/analyses/s_propagator.py +16 -9
  47. angr/analyses/s_reaching_definitions/s_rda_view.py +127 -63
  48. angr/analyses/smc.py +159 -0
  49. angr/analyses/variable_recovery/engine_ail.py +14 -0
  50. angr/analyses/variable_recovery/engine_base.py +11 -0
  51. angr/angrdb/models.py +1 -2
  52. angr/engines/light/engine.py +12 -0
  53. angr/engines/vex/heavy/heavy.py +2 -0
  54. angr/exploration_techniques/spiller_db.py +1 -2
  55. angr/knowledge_plugins/__init__.py +2 -0
  56. angr/knowledge_plugins/decompilation.py +45 -0
  57. angr/knowledge_plugins/functions/function.py +4 -0
  58. angr/knowledge_plugins/functions/function_manager.py +18 -9
  59. angr/knowledge_plugins/functions/function_parser.py +1 -1
  60. angr/knowledge_plugins/functions/soot_function.py +1 -0
  61. angr/knowledge_plugins/key_definitions/atoms.py +8 -0
  62. angr/misc/ux.py +2 -2
  63. angr/procedures/definitions/parse_win32json.py +2 -1
  64. angr/project.py +17 -1
  65. angr/state_plugins/history.py +6 -4
  66. angr/storage/memory_mixins/actions_mixin.py +7 -7
  67. angr/storage/memory_mixins/address_concretization_mixin.py +5 -5
  68. angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
  69. angr/storage/memory_mixins/clouseau_mixin.py +3 -3
  70. angr/storage/memory_mixins/conditional_store_mixin.py +3 -3
  71. angr/storage/memory_mixins/default_filler_mixin.py +3 -3
  72. angr/storage/memory_mixins/memory_mixin.py +45 -34
  73. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +15 -14
  74. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +27 -16
  75. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +18 -9
  76. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +5 -5
  77. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +89 -55
  78. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +16 -25
  79. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +11 -9
  80. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +23 -7
  81. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +1 -1
  82. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +9 -7
  83. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +9 -9
  84. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +1 -0
  85. angr/storage/memory_mixins/simple_interface_mixin.py +2 -2
  86. angr/storage/memory_mixins/simplification_mixin.py +2 -2
  87. angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
  88. angr/storage/memory_mixins/slotted_memory.py +3 -3
  89. angr/storage/memory_mixins/smart_find_mixin.py +1 -0
  90. angr/storage/memory_mixins/underconstrained_mixin.py +5 -5
  91. angr/storage/memory_mixins/unwrapper_mixin.py +4 -4
  92. angr/storage/memory_object.py +4 -3
  93. angr/utils/bits.py +4 -0
  94. angr/utils/constants.py +1 -1
  95. angr/utils/graph.py +15 -0
  96. angr/utils/tagged_interval_map.py +112 -0
  97. angr/vaults.py +2 -2
  98. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/METADATA +6 -6
  99. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/RECORD +103 -96
  100. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/WHEEL +1 -1
  101. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/LICENSE +0 -0
  102. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/entry_points.txt +0 -0
  103. {angr-9.2.123.dist-info → angr-9.2.125.dist-info}/top_level.txt +0 -0
@@ -66,14 +66,16 @@ class Decompiler(Analysis):
66
66
  decompile=True,
67
67
  regen_clinic=True,
68
68
  inline_functions=frozenset(),
69
+ desired_variables=frozenset(),
69
70
  update_memory_data: bool = True,
70
71
  generate_code: bool = True,
72
+ use_cache: bool = True,
71
73
  ):
72
74
  if not isinstance(func, Function):
73
75
  func = self.kb.functions[func]
74
76
  self.func: Function = func
75
77
  self._cfg = cfg.model if isinstance(cfg, CFGFast) else cfg
76
- self._options = options
78
+ self._options = options or []
77
79
 
78
80
  if preset is None and optimization_passes:
79
81
  self._optimization_passes = optimization_passes
@@ -102,6 +104,27 @@ class Decompiler(Analysis):
102
104
  self._update_memory_data = update_memory_data
103
105
  self._generate_code = generate_code
104
106
  self._inline_functions = inline_functions
107
+ self._desired_variables = desired_variables
108
+ self._cache_parameters = (
109
+ {
110
+ "cfg": self._cfg,
111
+ "variable_kb": self._variable_kb,
112
+ "options": {(o, v) for o, v in self._options if o.category != "Display" and v != o.default_value},
113
+ "optimization_passes": self._optimization_passes,
114
+ "sp_tracker_track_memory": self._sp_tracker_track_memory,
115
+ "peephole_optimizations": self._peephole_optimizations,
116
+ "vars_must_struct": self._vars_must_struct,
117
+ "flavor": self._flavor,
118
+ "expr_comments": self._expr_comments,
119
+ "stmt_comments": self._stmt_comments,
120
+ "ite_exprs": self._ite_exprs,
121
+ "binop_operators": self._binop_operators,
122
+ "inline_functions": self._inline_functions,
123
+ "desired_variables": self._desired_variables,
124
+ }
125
+ if use_cache
126
+ else None
127
+ )
105
128
 
106
129
  self.clinic = None # mostly for debugging purposes
107
130
  self.codegen: CStructuredCodeGenerator | None = None
@@ -111,27 +134,43 @@ class Decompiler(Analysis):
111
134
  self.unoptimized_ail_graph: networkx.DiGraph | None = None
112
135
  self.ail_graph: networkx.DiGraph | None = None
113
136
  self.vvar_id_start = None
137
+ self._optimization_scratch: dict[str, Any] = {}
114
138
 
115
139
  if decompile:
116
140
  self._decompile()
117
141
 
142
+ def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
143
+ a, b = self._cache_parameters, cache.parameters
144
+ id_checks = {"cfg", "variable_kb"}
145
+ return all(a[k] is b[k] if k in id_checks else a[k] == b[k] for k in self._cache_parameters)
146
+
118
147
  @timethis
119
148
  def _decompile(self):
120
149
  if self.func.is_simprocedure:
121
150
  return
122
151
 
123
- # Load from cache
124
- try:
125
- cache = self.kb.structured_code[(self.func.addr, self._flavor)]
152
+ cache = None
153
+
154
+ if self._cache_parameters is not None:
155
+ try:
156
+ cache = self.kb.decompilations[self.func.addr]
157
+ if not self._can_use_decompilation_cache(cache):
158
+ cache = None
159
+ except KeyError:
160
+ pass
161
+
162
+ if cache:
126
163
  old_codegen = cache.codegen
127
164
  old_clinic = cache.clinic
128
165
  ite_exprs = cache.ite_exprs if self._ite_exprs is None else self._ite_exprs
129
166
  binop_operators = cache.binop_operators if self._binop_operators is None else self._binop_operators
130
- except KeyError:
131
- ite_exprs = self._ite_exprs
132
- binop_operators = self._binop_operators
167
+ l.debug("Decompilation cache hit")
168
+ else:
133
169
  old_codegen = None
134
170
  old_clinic = None
171
+ ite_exprs = self._ite_exprs
172
+ binop_operators = self._binop_operators
173
+ l.debug("Decompilation cache miss")
135
174
 
136
175
  self.options_by_class = defaultdict(list)
137
176
 
@@ -167,6 +206,7 @@ class Decompiler(Analysis):
167
206
  fold_callexprs_into_conditions = True
168
207
 
169
208
  cache = DecompilationCache(self.func.addr)
209
+ cache.parameters = self._cache_parameters
170
210
  cache.ite_exprs = ite_exprs
171
211
  cache.binop_operators = binop_operators
172
212
 
@@ -189,6 +229,8 @@ class Decompiler(Analysis):
189
229
  cache=cache,
190
230
  progress_callback=progress_callback,
191
231
  inline_functions=self._inline_functions,
232
+ desired_variables=self._desired_variables,
233
+ optimization_scratch=self._optimization_scratch,
192
234
  **self.options_to_params(self.options_by_class["clinic"]),
193
235
  )
194
236
  else:
@@ -302,6 +344,8 @@ class Decompiler(Analysis):
302
344
  self.cache.codegen = codegen
303
345
  self.cache.clinic = self.clinic
304
346
 
347
+ self.kb.decompilations[self.func.addr] = self.cache
348
+
305
349
  def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
306
350
  return self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
307
351
  self.func,
@@ -355,6 +399,7 @@ class Decompiler(Analysis):
355
399
  variable_kb=self._variable_kb,
356
400
  reaching_definitions=reaching_definitions,
357
401
  entry_node_addr=self.clinic.entry_node_addr,
402
+ scratch=self._optimization_scratch,
358
403
  **kwargs,
359
404
  )
360
405
 
@@ -414,6 +459,7 @@ class Decompiler(Analysis):
414
459
  reaching_definitions=reaching_definitions,
415
460
  vvar_id_start=self.vvar_id_start,
416
461
  entry_node_addr=self.clinic.entry_node_addr,
462
+ scratch=self._optimization_scratch,
417
463
  **kwargs,
418
464
  )
419
465
 
@@ -442,7 +488,7 @@ class Decompiler(Analysis):
442
488
  continue
443
489
 
444
490
  pass_ = timethis(pass_)
445
- a = pass_(self.func, seq=seq_node, **kwargs)
491
+ a = pass_(self.func, seq=seq_node, scratch=self._optimization_scratch, **kwargs)
446
492
  if a.out_seq:
447
493
  seq_node = a.out_seq
448
494
 
@@ -3,7 +3,7 @@ import logging
3
3
  from collections import defaultdict
4
4
 
5
5
  from ailment.block import Block
6
- from ailment.expression import Phi, VirtualVariable
6
+ from ailment.expression import Phi, VirtualVariable, VirtualVariableCategory
7
7
  from ailment.statement import Assignment, Jump, ConditionalJump, Label
8
8
 
9
9
  from angr.analyses import Analysis
@@ -213,8 +213,16 @@ class GraphDephicationVVarMapping(Analysis): # pylint:disable=abstract-method
213
213
 
214
214
  the_block = self._blocks[(src_block_addr, src_block_idx)]
215
215
  ins_addr = the_block.addr + the_block.original_size - 1
216
+ if vvar.category == VirtualVariableCategory.PARAMETER:
217
+ # it's a parameter, so we copy the variable into a dummy register vvar
218
+ # a parameter vvar should never be assigned to
219
+ new_category = VirtualVariableCategory.REGISTER
220
+ new_oident = 4096
221
+ else:
222
+ new_category = vvar.category
223
+ new_oident = vvar.oident
216
224
  new_vvar = VirtualVariable(
217
- None, new_vvar_id, vvar.bits, vvar.category, oident=vvar.oident, ins_addr=ins_addr
225
+ None, new_vvar_id, vvar.bits, new_category, oident=new_oident, ins_addr=ins_addr
218
226
  )
219
227
  assignment = Assignment(None, new_vvar, vvar, ins_addr=ins_addr)
220
228
 
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import logging
4
4
 
5
5
  from ailment.block import Block
6
- from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump
6
+ from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement
7
7
  from ailment.expression import (
8
8
  Register,
9
9
  VirtualVariable,
@@ -14,6 +14,8 @@ from ailment.expression import (
14
14
  Convert,
15
15
  StackBaseOffset,
16
16
  ITE,
17
+ VEXCCallExpression,
18
+ DirtyExpression,
17
19
  )
18
20
 
19
21
  from angr.engines.light import SimEngineLight, SimEngineLightAILMixin
@@ -139,10 +141,19 @@ class SimEngineDephiRewriting(
139
141
  args=stmt.args,
140
142
  ret_expr=stmt.ret_expr if new_ret_expr is None else new_ret_expr,
141
143
  fp_ret_expr=stmt.fp_ret_expr if new_fp_ret_expr is None else new_fp_ret_expr,
144
+ bits=stmt.bits,
142
145
  **stmt.tags,
143
146
  )
144
147
  return None
145
148
 
149
+ _handle_CallExpr = _handle_Call
150
+
151
+ def _handle_DirtyStatement(self, stmt: DirtyStatement) -> DirtyStatement | None:
152
+ dirty = self._expr(stmt.dirty)
153
+ if dirty is None or dirty is stmt.dirty:
154
+ return None
155
+ return DirtyStatement(stmt.idx, dirty, **stmt.tags)
156
+
146
157
  def _handle_Register(self, expr: Register) -> None:
147
158
  return None
148
159
 
@@ -243,5 +254,57 @@ class SimEngineDephiRewriting(
243
254
  )
244
255
  return None
245
256
 
257
+ def _handle_VEXCCallExpression(self, expr: VEXCCallExpression) -> VEXCCallExpression | None:
258
+ new_operands = []
259
+ updated = False
260
+ for o in expr.operands:
261
+ new_o = self._expr(o)
262
+ if new_o is not None:
263
+ updated = True
264
+ new_operands.append(new_o)
265
+ else:
266
+ new_operands.append(o)
267
+
268
+ if updated:
269
+ return VEXCCallExpression(
270
+ expr.idx,
271
+ expr.callee,
272
+ new_operands,
273
+ bits=expr.bits,
274
+ **expr.tags,
275
+ )
276
+ return None
277
+
278
+ def _handle_DirtyExpression(self, expr: DirtyExpression) -> DirtyExpression | None:
279
+ new_operands = []
280
+ updated = False
281
+ for o in expr.operands:
282
+ new_o = self._expr(o)
283
+ if new_o is not None:
284
+ updated = True
285
+ new_operands.append(new_o)
286
+ else:
287
+ new_operands.append(o)
288
+
289
+ new_guard = None
290
+ if expr.guard is not None:
291
+ new_guard = self._expr(expr.guard)
292
+ if new_guard is not None:
293
+ updated = True
294
+
295
+ if updated:
296
+ return DirtyExpression(
297
+ expr.idx,
298
+ expr.callee,
299
+ new_operands,
300
+ guard=new_guard,
301
+ mfx=expr.mfx,
302
+ maddr=expr.maddr,
303
+ msize=expr.msize,
304
+ bits=expr.bits,
305
+ **expr.tags,
306
+ )
307
+ return None
308
+
246
309
  def _handle_StackBaseOffset(self, expr: StackBaseOffset) -> None:
247
310
  return None
@@ -105,7 +105,11 @@ class NarrowingInfoExtractor(AILBlockWalkerBase):
105
105
  def _handle_DirtyExpression(
106
106
  self, expr_idx: int, expr: DirtyExpression, stmt_idx: int, stmt: Statement, block: Block | None
107
107
  ):
108
- return self._handle_expr(0, expr.dirty_expr, stmt_idx, stmt, block)
108
+ r = False
109
+ if expr.operands:
110
+ for i, op in enumerate(expr.operands):
111
+ r |= self._handle_expr(i, op, stmt_idx, stmt, block)
112
+ return r
109
113
 
110
114
  def _handle_VEXCCallExpression(
111
115
  self, expr_idx: int, expr: VEXCCallExpression, stmt_idx: int, stmt: Statement, block: Block | None
@@ -26,6 +26,7 @@ from .cross_jump_reverter import CrossJumpReverter
26
26
  from .code_motion import CodeMotionOptimization
27
27
  from .switch_default_case_duplicator import SwitchDefaultCaseDuplicator
28
28
  from .deadblock_remover import DeadblockRemover
29
+ from .tag_slicer import TagSlicer
29
30
  from .inlined_string_transformation_simplifier import InlinedStringTransformationSimplifier
30
31
  from .const_prop_reverter import ConstPropOptReverter
31
32
  from .call_stmt_rewriter import CallStatementRewriter
@@ -59,6 +60,7 @@ ALL_OPTIMIZATION_PASSES = [
59
60
  FlipBooleanCmp,
60
61
  InlinedStringTransformationSimplifier,
61
62
  CallStatementRewriter,
63
+ TagSlicer,
62
64
  ]
63
65
 
64
66
  # these passes may duplicate code to remove gotos or improve the structure of the graph
@@ -118,6 +120,7 @@ __all__ = (
118
120
  "ConstPropOptReverter",
119
121
  "CallStatementRewriter",
120
122
  "DuplicationReverter",
123
+ "TagSlicer",
121
124
  "ALL_OPTIMIZATION_PASSES",
122
125
  "DUPLICATING_OPTS",
123
126
  "CONDENSING_OPTS",
@@ -18,7 +18,10 @@ class DivSimplifierAILEngine(SimplifierAILEngine):
18
18
  An AIL pass for the div simplifier
19
19
  """
20
20
 
21
- def _check_divisor(self, a, b, ndigits=6): # pylint: disable=no-self-use
21
+ @staticmethod
22
+ def _check_divisor(a, b, ndigits=6):
23
+ if b == 0:
24
+ return None
22
25
  divisor_1 = 1 + (a // b)
23
26
  divisor_2 = int(round(a / float(b), ndigits))
24
27
  return divisor_1 if divisor_1 == divisor_2 else None
@@ -13,6 +13,7 @@ from ailment.expression import (
13
13
  BinaryOp,
14
14
  VirtualVariable,
15
15
  Phi,
16
+ UnaryOp,
16
17
  VirtualVariableCategory,
17
18
  )
18
19
  from ailment.statement import ConditionalJump, Jump, Assignment
@@ -238,6 +239,18 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
238
239
  return self.state.vvar_load(vvar)
239
240
  return None
240
241
 
242
+ def _handle_Neg(self, expr: UnaryOp):
243
+ v = self._expr(expr.operand)
244
+ if isinstance(v, claripy.ast.Bits):
245
+ return -v
246
+ return None
247
+
248
+ def _handle_BitwiseNeg(self, expr: UnaryOp):
249
+ v = self._expr(expr.operand)
250
+ if isinstance(v, claripy.ast.Bits):
251
+ return ~v
252
+ return None
253
+
241
254
  def _handle_Convert(self, expr: Convert):
242
255
  v = self._expr(expr.operand)
243
256
  if isinstance(v, claripy.ast.Bits):
@@ -202,6 +202,7 @@ class ITERegionConverter(OptimizationPass):
202
202
  true_stmt_src = true_stmt.src if isinstance(true_stmt, Assignment) else true_stmt
203
203
  true_stmt_dst = true_stmt.dst if isinstance(true_stmt, Assignment) else true_stmt.ret_expr
204
204
  false_stmt_src = false_stmt.src if isinstance(false_stmt, Assignment) else false_stmt
205
+ false_stmt_dst = false_stmt.dst if isinstance(false_stmt, Assignment) else false_stmt.ret_expr
205
206
 
206
207
  addr_obj = true_stmt_src if "ins_addr" in true_stmt_src.tags else true_stmt
207
208
  ternary_expr = ITE(
@@ -209,9 +210,7 @@ class ITERegionConverter(OptimizationPass):
209
210
  conditional_jump.condition,
210
211
  false_stmt_src,
211
212
  true_stmt_src,
212
- ins_addr=addr_obj.ins_addr,
213
- vex_block_addr=addr_obj.vex_block_addr,
214
- vex_stmt_idx=addr_obj.vex_stmt_idx,
213
+ **addr_obj.tags,
215
214
  )
216
215
  dst = VirtualVariable(
217
216
  true_stmt_dst.idx,
@@ -261,12 +260,32 @@ class ITERegionConverter(OptimizationPass):
261
260
  if not is_phi_assignment(stmt):
262
261
  stmts.append(stmt)
263
262
  continue
263
+
264
+ # is this the statement that we are looking for?
265
+ found_true_src_vvar, found_false_src_vvar = False, False
266
+ for src, vvar in stmt.src.src_and_vvars:
267
+ if vvar is not None:
268
+ if vvar.varid == true_stmt_dst.varid:
269
+ found_true_src_vvar = True
270
+ elif vvar.varid == false_stmt_dst.varid:
271
+ found_false_src_vvar = True
272
+ # we should only update the vvars of this phi statement if we found both true and false source vvars
273
+ update_vars = found_true_src_vvar and found_false_src_vvar
274
+
264
275
  new_src_and_vvars = []
276
+ original_vvars = []
265
277
  for src, vvar in stmt.src.src_and_vvars:
266
278
  if src not in region_tail_pred_srcs:
267
279
  new_src_and_vvars.append((src, vvar))
280
+ else:
281
+ original_vvars.append(vvar)
268
282
  new_vvar = new_assignment.dst.copy()
269
- new_src_and_vvars.append(((region_head.addr, region_head.idx), new_vvar))
283
+ if update_vars:
284
+ new_src_and_vvars.append(((region_head.addr, region_head.idx), new_vvar))
285
+ else:
286
+ new_src_and_vvars.append(
287
+ ((region_head.addr, region_head.idx), original_vvars[0] if original_vvars else None)
288
+ )
270
289
 
271
290
  new_phi = Phi(
272
291
  stmt.src.idx,
@@ -1,7 +1,7 @@
1
1
  # pylint:disable=unused-argument
2
2
  from __future__ import annotations
3
3
  import logging
4
- from typing import TYPE_CHECKING
4
+ from typing import Any, TYPE_CHECKING
5
5
  from collections.abc import Generator
6
6
  from enum import Enum
7
7
 
@@ -117,6 +117,7 @@ class OptimizationPass(BaseOptimizationPass):
117
117
  reaching_definitions=None,
118
118
  vvar_id_start=None,
119
119
  entry_node_addr=None,
120
+ scratch: dict[str, Any] | None = None,
120
121
  **kwargs,
121
122
  ):
122
123
  super().__init__(func)
@@ -127,6 +128,7 @@ class OptimizationPass(BaseOptimizationPass):
127
128
  self._variable_kb = variable_kb
128
129
  self._ri = region_identifier
129
130
  self._rd = reaching_definitions
131
+ self._scratch = scratch if scratch is not None else {}
130
132
  self._new_block_addrs = set()
131
133
  self.vvar_id_start = vvar_id_start
132
134
  self.entry_node_addr: tuple[int, int | None] = (
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
  from typing import Any
3
- from itertools import count
4
3
  import copy
5
4
  import logging
6
5
 
@@ -78,23 +77,27 @@ class ReturnDuplicatorBase:
78
77
  def __init__(
79
78
  self,
80
79
  func,
81
- node_idx_start: int = 0,
82
80
  max_calls_in_regions: int = 2,
83
81
  minimize_copies_for_regions: bool = True,
84
82
  ri: RegionIdentifier | None = None,
85
83
  vvar_id_start: int | None = None,
86
- **kwargs,
84
+ scratch: dict[str, Any] | None = None,
87
85
  ):
88
- self.node_idx = count(start=node_idx_start)
89
86
  self._max_calls_in_region = max_calls_in_regions
90
87
  self._minimize_copies_for_regions = minimize_copies_for_regions
91
88
  self._supergraph = None
92
89
 
93
90
  # this should also be set by the optimization passes initer
91
+ self.scratch = scratch if scratch is not None else {}
94
92
  self._func = func
95
93
  self._ri: RegionIdentifier | None = ri
96
94
  self.vvar_id_start = vvar_id_start
97
95
 
96
+ def next_node_idx(self) -> int:
97
+ node_idx = self.scratch.get("returndup_node_idx", 0) + 1
98
+ self.scratch["returndup_node_idx"] = node_idx
99
+ return node_idx
100
+
98
101
  #
99
102
  # must implement these methods
100
103
  #
@@ -208,7 +211,7 @@ class ReturnDuplicatorBase:
208
211
  else:
209
212
  node_copy = copy.deepcopy(node)
210
213
  node_copy = self._use_fresh_virtual_variables(node_copy, vvar_mapping)
211
- node_copy.idx = next(self.node_idx)
214
+ node_copy.idx = self.next_node_idx()
212
215
  self._fix_copied_node_labels(node_copy)
213
216
  copies[node] = node_copy
214
217
 
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
  import logging
3
+ from typing import Any
3
4
 
4
5
  import networkx
5
6
 
@@ -26,22 +27,26 @@ class ReturnDuplicatorHigh(OptimizationPass, ReturnDuplicatorBase):
26
27
  def __init__(
27
28
  self,
28
29
  func,
29
- # internal parameters that should be used by Clinic
30
- node_idx_start: int = 0,
31
30
  # settings
32
31
  max_calls_in_regions: int = 2,
33
32
  minimize_copies_for_regions: bool = True,
33
+ region_identifier=None,
34
+ vvar_id_start: int | None = None,
35
+ scratch: dict[str, Any] | None = None,
34
36
  **kwargs,
35
37
  ):
38
+ OptimizationPass.__init__(
39
+ self, func, vvar_id_start=vvar_id_start, scratch=scratch, region_identifier=region_identifier, **kwargs
40
+ )
36
41
  ReturnDuplicatorBase.__init__(
37
42
  self,
38
43
  func,
39
- node_idx_start=node_idx_start,
40
44
  max_calls_in_regions=max_calls_in_regions,
41
45
  minimize_copies_for_regions=minimize_copies_for_regions,
42
- **kwargs,
46
+ ri=region_identifier,
47
+ vvar_id_start=vvar_id_start,
48
+ scratch=scratch,
43
49
  )
44
- OptimizationPass.__init__(self, func, **kwargs)
45
50
  # since we run before the RegionIdentification pass in the decompiler, we need to collect it early here
46
51
  self._ri = self._recover_regions(self._graph)
47
52
 
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
  import logging
3
3
  import inspect
4
+ from typing import Any
4
5
 
5
6
  import networkx
6
7
 
@@ -46,25 +47,35 @@ class ReturnDuplicatorLow(StructuringOptimizationPass, ReturnDuplicatorBase):
46
47
  def __init__(
47
48
  self,
48
49
  func,
49
- # internal parameters that should be used by Clinic
50
- node_idx_start: int = 0,
51
50
  # settings
52
51
  max_opt_iters: int = 4,
53
52
  max_calls_in_regions: int = 2,
54
53
  prevent_new_gotos: bool = True,
55
54
  minimize_copies_for_regions: bool = True,
55
+ region_identifier=None,
56
+ vvar_id_start: int | None = None,
57
+ scratch: dict[str, Any] | None = None,
56
58
  **kwargs,
57
59
  ):
60
+ StructuringOptimizationPass.__init__(
61
+ self,
62
+ func,
63
+ max_opt_iters=max_opt_iters,
64
+ prevent_new_gotos=prevent_new_gotos,
65
+ require_gotos=True,
66
+ vvar_id_start=vvar_id_start,
67
+ scratch=scratch,
68
+ region_identifier=region_identifier,
69
+ **kwargs,
70
+ )
58
71
  ReturnDuplicatorBase.__init__(
59
72
  self,
60
73
  func,
61
- node_idx_start=node_idx_start,
62
74
  max_calls_in_regions=max_calls_in_regions,
63
75
  minimize_copies_for_regions=minimize_copies_for_regions,
64
- **kwargs,
65
- )
66
- StructuringOptimizationPass.__init__(
67
- self, func, max_opt_iters=max_opt_iters, prevent_new_gotos=prevent_new_gotos, require_gotos=True, **kwargs
76
+ ri=region_identifier,
77
+ vvar_id_start=vvar_id_start,
78
+ scratch=scratch,
68
79
  )
69
80
  self.analyze()
70
81
 
@@ -75,8 +75,12 @@ class SwitchDefaultCaseDuplicator(OptimizationPass):
75
75
  default_case_node_addrs = cache["default_case_node_addrs"]
76
76
 
77
77
  out_graph = None
78
+ duplicated_default_addrs: set[int] = set()
78
79
 
79
80
  for switch_head_addr, jump_node_addr, default_addr in default_case_node_addrs:
81
+ if default_addr in duplicated_default_addrs:
82
+ continue
83
+
80
84
  default_case_node = self._func.get_node(default_addr)
81
85
  unexpected_pred_addrs = {
82
86
  pred.addr
@@ -92,6 +96,8 @@ class SwitchDefaultCaseDuplicator(OptimizationPass):
92
96
  for jump_node in jump_nodes:
93
97
  jump_node_descedents |= networkx.descendants(self._graph, jump_node)
94
98
 
99
+ duplicated_default_addrs.add(default_addr)
100
+
95
101
  # duplicate default_case_node for each unexpected predecessor
96
102
  for unexpected_pred_addr in unexpected_pred_addrs:
97
103
  for unexpected_pred in self._get_blocks(unexpected_pred_addr):
@@ -0,0 +1,41 @@
1
+ # pylint:disable=too-many-boolean-expressions
2
+ from __future__ import annotations
3
+ import logging
4
+
5
+ from ailment.statement import ConditionalJump, Jump, Label
6
+ from .optimization_pass import OptimizationPass, OptimizationPassStage
7
+
8
+
9
+ _l = logging.getLogger(name=__name__)
10
+
11
+
12
+ class TagSlicer(OptimizationPass):
13
+ """
14
+ Removes unmarked statements from the graph.
15
+ """
16
+
17
+ ARCHES = None
18
+ PLATFORMS = None
19
+ STAGE = OptimizationPassStage.AFTER_VARIABLE_RECOVERY
20
+ NAME = "Remove unmarked statements from the graph."
21
+ DESCRIPTION = __doc__.strip()
22
+
23
+ def __init__(self, func, **kwargs):
24
+ super().__init__(func, **kwargs)
25
+ self.analyze()
26
+
27
+ def _check(self):
28
+ return True, {}
29
+
30
+ def _analyze(self, cache=None):
31
+ for n in self._graph.nodes():
32
+ for i, s in enumerate(n.statements):
33
+ if isinstance(s, (ConditionalJump, Jump, Label)):
34
+ continue
35
+ if not s.tags.get("keep_in_slice", False):
36
+ n.statements[i] = None
37
+
38
+ for n in self._graph.nodes():
39
+ n.statements = [s for s in n.statements if s is not None]
40
+
41
+ self.out_graph = self._graph
@@ -100,6 +100,8 @@ class WinStackCanarySimplifier(OptimizationPass):
100
100
  for pred_addr in pred_addr_to_endpoint_addrs:
101
101
  # the predecessor should call _security_check_cookie
102
102
  endpoint_preds = list(self._get_blocks(pred_addr))
103
+ if not endpoint_preds:
104
+ continue
103
105
  if self._find_stmt_calling_security_check_cookie(endpoint_preds[0]) is None:
104
106
  _l.debug("The predecessor does not call _security_check_cookie().")
105
107
  continue
@@ -182,6 +182,8 @@ class ConstMullAShift(PeepholeOptimizationExprBase):
182
182
 
183
183
  @staticmethod
184
184
  def _check_divisor(a, b, ndigits=6):
185
+ if b == 0:
186
+ return None
185
187
  divisor_1 = 1 + (a // b)
186
188
  divisor_2 = int(round(a / float(b), ndigits))
187
189
  return divisor_1 if divisor_1 == divisor_2 else None
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
  from ailment.expression import Load, Const
3
- from cle.backends import Blob
3
+ from cle.backends import Blob, Hex
4
4
 
5
5
  from .base import PeepholeOptimizationExprBase
6
6
 
@@ -32,7 +32,7 @@ class ConstantDereferences(PeepholeOptimizationExprBase):
32
32
 
33
33
  # is it loading from a blob?
34
34
  obj = self.project.loader.find_object_containing(expr.addr.value)
35
- if obj is not None and isinstance(obj, Blob):
35
+ if obj is not None and isinstance(obj, (Blob, Hex)):
36
36
  # do we know the value that it's reading?
37
37
  try:
38
38
  val = self.project.loader.memory.unpack_word(expr.addr.value, size=self.project.arch.bytes)