angr 9.2.122__py3-none-macosx_11_0_arm64.whl → 9.2.124__py3-none-macosx_11_0_arm64.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 (96) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention.py +6 -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/decompiler/ail_simplifier.py +38 -342
  6. angr/analyses/decompiler/callsite_maker.py +8 -7
  7. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +24 -2
  8. angr/analyses/decompiler/clinic.py +30 -3
  9. angr/analyses/decompiler/condition_processor.py +10 -3
  10. angr/analyses/decompiler/decompilation_cache.py +2 -0
  11. angr/analyses/decompiler/decompiler.py +50 -8
  12. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +10 -2
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +65 -2
  14. angr/analyses/decompiler/expression_narrower.py +206 -6
  15. angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
  16. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +7 -0
  17. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +34 -11
  18. angr/analyses/decompiler/optimization_passes/lowered_switch_simplifier.py +10 -1
  19. angr/analyses/decompiler/optimization_passes/optimization_pass.py +3 -1
  20. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +8 -5
  21. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +10 -5
  22. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +18 -7
  23. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +6 -0
  24. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +2 -0
  25. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +75 -42
  26. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +8 -2
  27. angr/analyses/decompiler/region_identifier.py +36 -0
  28. angr/analyses/decompiler/region_simplifiers/expr_folding.py +4 -0
  29. angr/analyses/decompiler/region_simplifiers/loop.py +2 -8
  30. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +9 -3
  31. angr/analyses/decompiler/sequence_walker.py +20 -4
  32. angr/analyses/decompiler/ssailification/rewriting.py +5 -2
  33. angr/analyses/decompiler/ssailification/rewriting_engine.py +151 -25
  34. angr/analyses/decompiler/ssailification/rewriting_state.py +1 -0
  35. angr/analyses/decompiler/ssailification/ssailification.py +17 -9
  36. angr/analyses/decompiler/ssailification/traversal.py +3 -1
  37. angr/analyses/decompiler/ssailification/traversal_engine.py +35 -8
  38. angr/analyses/decompiler/ssailification/traversal_state.py +1 -0
  39. angr/analyses/decompiler/structured_codegen/c.py +42 -4
  40. angr/analyses/decompiler/structuring/phoenix.py +3 -0
  41. angr/analyses/propagator/engine_ail.py +10 -3
  42. angr/analyses/reaching_definitions/engine_ail.py +10 -15
  43. angr/analyses/s_propagator.py +26 -15
  44. angr/analyses/s_reaching_definitions/s_rda_view.py +127 -63
  45. angr/analyses/variable_recovery/engine_ail.py +14 -0
  46. angr/analyses/variable_recovery/engine_base.py +11 -0
  47. angr/calling_conventions.py +2 -2
  48. angr/engines/light/engine.py +24 -2
  49. angr/engines/soot/expressions/instanceOf.py +4 -1
  50. angr/engines/successors.py +1 -1
  51. angr/engines/vex/heavy/concretizers.py +47 -47
  52. angr/engines/vex/heavy/dirty.py +4 -4
  53. angr/knowledge_plugins/__init__.py +2 -0
  54. angr/knowledge_plugins/decompilation.py +45 -0
  55. angr/knowledge_plugins/key_definitions/atoms.py +8 -0
  56. angr/lib/angr_native.dylib +0 -0
  57. angr/procedures/definitions/parse_win32json.py +2 -1
  58. angr/procedures/java_lang/getsimplename.py +4 -1
  59. angr/procedures/linux_kernel/iovec.py +5 -2
  60. angr/sim_type.py +3 -1
  61. angr/storage/memory_mixins/actions_mixin.py +7 -7
  62. angr/storage/memory_mixins/address_concretization_mixin.py +5 -5
  63. angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
  64. angr/storage/memory_mixins/clouseau_mixin.py +3 -3
  65. angr/storage/memory_mixins/conditional_store_mixin.py +3 -3
  66. angr/storage/memory_mixins/default_filler_mixin.py +3 -3
  67. angr/storage/memory_mixins/memory_mixin.py +45 -34
  68. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +15 -14
  69. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +27 -16
  70. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +18 -9
  71. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +5 -5
  72. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +89 -55
  73. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +16 -25
  74. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +11 -9
  75. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +23 -7
  76. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +1 -1
  77. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +9 -7
  78. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +9 -9
  79. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +1 -0
  80. angr/storage/memory_mixins/simple_interface_mixin.py +2 -2
  81. angr/storage/memory_mixins/simplification_mixin.py +2 -2
  82. angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
  83. angr/storage/memory_mixins/slotted_memory.py +3 -3
  84. angr/storage/memory_mixins/smart_find_mixin.py +1 -0
  85. angr/storage/memory_mixins/underconstrained_mixin.py +5 -5
  86. angr/storage/memory_mixins/unwrapper_mixin.py +4 -4
  87. angr/storage/memory_object.py +4 -3
  88. angr/utils/constants.py +1 -1
  89. angr/utils/graph.py +15 -0
  90. angr/vaults.py +2 -2
  91. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/METADATA +7 -6
  92. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/RECORD +96 -95
  93. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/WHEEL +1 -1
  94. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/LICENSE +0 -0
  95. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/entry_points.txt +0 -0
  96. {angr-9.2.122.dist-info → angr-9.2.124.dist-info}/top_level.txt +0 -0
@@ -110,6 +110,7 @@ class Clinic(Analysis):
110
110
  inlined_counts: dict[int, int] | None = None,
111
111
  inlining_parents: set[int] | None = None,
112
112
  vvar_id_start: int = 0,
113
+ optimization_scratch: dict[str, Any] | None = None,
113
114
  ):
114
115
  if not func.normalized and mode == ClinicMode.DECOMPILE:
115
116
  raise ValueError("Decompilation must work on normalized function graphs.")
@@ -124,6 +125,7 @@ class Clinic(Analysis):
124
125
  self.variable_kb = variable_kb
125
126
  self.externs: set[SimMemoryVariable] = set()
126
127
  self.data_refs: dict[int, int] = {} # data address to instruction address
128
+ self.optimization_scratch = optimization_scratch if optimization_scratch is not None else {}
127
129
 
128
130
  self._func_graph: networkx.DiGraph | None = None
129
131
  self._ail_manager = None
@@ -749,8 +751,9 @@ class Clinic(Analysis):
749
751
  continue
750
752
 
751
753
  call_sites = []
752
- for pred in self.function.transition_graph.predecessors(node):
753
- call_sites.append(pred)
754
+ for pred, _, data in self.function.transition_graph.in_edges(node, data=True):
755
+ if data.get("type", None) != "return":
756
+ call_sites.append(pred)
754
757
  # case 1: calling conventions and prototypes are available at every single call site
755
758
  if call_sites and all(self.kb.callsite_prototypes.has_prototype(callsite.addr) for callsite in call_sites):
756
759
  continue
@@ -918,10 +921,16 @@ class Clinic(Analysis):
918
921
  "Ijk_Sys"
919
922
  ):
920
923
  # we don't support lifting this block. use a dummy block instead
924
+ dirty_expr = ailment.Expr.DirtyExpression(
925
+ self._ail_manager.next_atom,
926
+ f"Unsupported jumpkind {block.vex.jumpkind} at address {block_node.addr}",
927
+ [],
928
+ bits=0,
929
+ )
921
930
  statements = [
922
931
  ailment.Stmt.DirtyStatement(
923
932
  self._ail_manager.next_atom(),
924
- f"Unsupported jumpkind {block.vex.jumpkind} at address {block_node.addr}",
933
+ dirty_expr,
925
934
  ins_addr=block_node.addr,
926
935
  )
927
936
  ]
@@ -1218,6 +1227,7 @@ class Clinic(Analysis):
1218
1227
  variable_kb=variable_kb,
1219
1228
  vvar_id_start=self.vvar_id_start,
1220
1229
  entry_node_addr=self.entry_node_addr,
1230
+ scratch=self.optimization_scratch,
1221
1231
  **kwargs,
1222
1232
  )
1223
1233
  if a.out_graph:
@@ -1255,6 +1265,7 @@ class Clinic(Analysis):
1255
1265
  ailment.Expr.VirtualVariableCategory.PARAMETER,
1256
1266
  oident=arg.reg,
1257
1267
  ins_addr=self.function.addr,
1268
+ vex_block_addr=self.function.addr,
1258
1269
  )
1259
1270
  self.vvar_id_start += 1
1260
1271
  arg_vvars[arg_vvar.varid] = arg_vvar, arg
@@ -1268,6 +1279,7 @@ class Clinic(Analysis):
1268
1279
  False,
1269
1280
  arg_vvar,
1270
1281
  ins_addr=self.function.addr,
1282
+ vex_block_addr=self.function.addr,
1271
1283
  )
1272
1284
 
1273
1285
  fullreg_dst = ailment.Expr.Register(
@@ -1276,12 +1288,14 @@ class Clinic(Analysis):
1276
1288
  basereg_offset,
1277
1289
  basereg_size * self.project.arch.byte_width,
1278
1290
  ins_addr=self.function.addr,
1291
+ vex_block_addr=self.function.addr,
1279
1292
  )
1280
1293
  stmt = ailment.Stmt.Assignment(
1281
1294
  self._ail_manager.next_atom(),
1282
1295
  fullreg_dst,
1283
1296
  arg_vvar,
1284
1297
  ins_addr=self.function.addr,
1298
+ vex_block_addr=self.function.addr,
1285
1299
  )
1286
1300
  new_stmts.append(stmt)
1287
1301
 
@@ -1312,6 +1326,7 @@ class Clinic(Analysis):
1312
1326
  ail_graph,
1313
1327
  entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
1314
1328
  ail_manager=self._ail_manager,
1329
+ ssa_tmps=True,
1315
1330
  ssa_stackvars=True,
1316
1331
  vvar_id_start=self.vvar_id_start,
1317
1332
  )
@@ -1791,6 +1806,18 @@ class Clinic(Analysis):
1791
1806
  elif isinstance(expr, ailment.Stmt.Call):
1792
1807
  self._link_variables_on_call(variable_manager, global_variables, block, stmt_idx, expr, is_expr=True)
1793
1808
 
1809
+ elif isinstance(expr, ailment.Expr.VEXCCallExpression):
1810
+ for operand in expr.operands:
1811
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, operand)
1812
+
1813
+ elif isinstance(expr, ailment.Expr.DirtyExpression):
1814
+ for operand in expr.operands:
1815
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, operand)
1816
+ if expr.maddr:
1817
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.maddr)
1818
+ if expr.guard:
1819
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.guard)
1820
+
1794
1821
  def _function_graph_to_ail_graph(self, func_graph, blocks_by_addr_and_size=None):
1795
1822
  if blocks_by_addr_and_size is None:
1796
1823
  blocks_by_addr_and_size = self._blocks_by_addr_and_size
@@ -160,6 +160,8 @@ _ail2claripy_op_mapping = {
160
160
  "GetMSBs": lambda expr, _, m: _dummy_bvs(expr, m),
161
161
  "InterleaveLOV": lambda expr, _, m: _dummy_bvs(expr, m),
162
162
  "InterleaveHIV": lambda expr, _, m: _dummy_bvs(expr, m),
163
+ # catch-all
164
+ "_DUMMY_": lambda expr, _, m: _dummy_bvs(expr, m),
163
165
  }
164
166
 
165
167
  #
@@ -771,7 +773,7 @@ class ConditionProcessor:
771
773
 
772
774
  if isinstance(
773
775
  condition,
774
- (ailment.Expr.DirtyExpression, ailment.Expr.BasePointerOffset, ailment.Expr.ITE),
776
+ (ailment.Expr.VEXCCallExpression, ailment.Expr.BasePointerOffset, ailment.Expr.ITE),
775
777
  ):
776
778
  return _dummy_bvs(condition, self._condition_mapping)
777
779
  if isinstance(condition, ailment.Stmt.Call):
@@ -828,9 +830,14 @@ class ConditionProcessor:
828
830
  # fall back to op
829
831
  lambda_expr = _ail2claripy_op_mapping.get(condition.op, None)
830
832
  if lambda_expr is None:
831
- raise NotImplementedError(
832
- f"Unsupported AIL expression operation {condition.op} or {condition.verbose_op}. Consider implementing."
833
+ # fall back to the catch-all option
834
+ l.debug(
835
+ "Unsupported AIL expression operation %s (or verbose: %s). Fall back to the default catch-all dummy "
836
+ "option. Consider implementing.",
837
+ condition.op,
838
+ condition.verbose_op,
833
839
  )
840
+ lambda_expr = _ail2claripy_op_mapping["_DUMMY_"]
834
841
  r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping)
835
842
 
836
843
  if isinstance(r, claripy.ast.Bool) and nobool:
@@ -14,6 +14,7 @@ class DecompilationCache:
14
14
  """
15
15
 
16
16
  __slots__ = (
17
+ "parameters",
17
18
  "addr",
18
19
  "type_constraints",
19
20
  "func_typevar",
@@ -25,6 +26,7 @@ class DecompilationCache:
25
26
  )
26
27
 
27
28
  def __init__(self, addr):
29
+ self.parameters: dict[str, Any] = {}
28
30
  self.addr = addr
29
31
  self.type_constraints: set | None = None
30
32
  self.func_typevar = None
@@ -68,12 +68,13 @@ class Decompiler(Analysis):
68
68
  inline_functions=frozenset(),
69
69
  update_memory_data: bool = True,
70
70
  generate_code: bool = True,
71
+ use_cache: bool = True,
71
72
  ):
72
73
  if not isinstance(func, Function):
73
74
  func = self.kb.functions[func]
74
75
  self.func: Function = func
75
76
  self._cfg = cfg.model if isinstance(cfg, CFGFast) else cfg
76
- self._options = options
77
+ self._options = options or []
77
78
 
78
79
  if preset is None and optimization_passes:
79
80
  self._optimization_passes = optimization_passes
@@ -102,6 +103,25 @@ class Decompiler(Analysis):
102
103
  self._update_memory_data = update_memory_data
103
104
  self._generate_code = generate_code
104
105
  self._inline_functions = inline_functions
106
+ self._cache_parameters = (
107
+ {
108
+ "cfg": self._cfg,
109
+ "variable_kb": self._variable_kb,
110
+ "options": {(o, v) for o, v in self._options if o.category != "Display" and v != o.default_value},
111
+ "optimization_passes": self._optimization_passes,
112
+ "sp_tracker_track_memory": self._sp_tracker_track_memory,
113
+ "peephole_optimizations": self._peephole_optimizations,
114
+ "vars_must_struct": self._vars_must_struct,
115
+ "flavor": self._flavor,
116
+ "expr_comments": self._expr_comments,
117
+ "stmt_comments": self._stmt_comments,
118
+ "ite_exprs": self._ite_exprs,
119
+ "binop_operators": self._binop_operators,
120
+ "inline_functions": self._inline_functions,
121
+ }
122
+ if use_cache
123
+ else None
124
+ )
105
125
 
106
126
  self.clinic = None # mostly for debugging purposes
107
127
  self.codegen: CStructuredCodeGenerator | None = None
@@ -111,27 +131,43 @@ class Decompiler(Analysis):
111
131
  self.unoptimized_ail_graph: networkx.DiGraph | None = None
112
132
  self.ail_graph: networkx.DiGraph | None = None
113
133
  self.vvar_id_start = None
134
+ self._optimization_scratch: dict[str, Any] = {}
114
135
 
115
136
  if decompile:
116
137
  self._decompile()
117
138
 
139
+ def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
140
+ a, b = self._cache_parameters, cache.parameters
141
+ id_checks = {"cfg", "variable_kb"}
142
+ return all(a[k] is b[k] if k in id_checks else a[k] == b[k] for k in self._cache_parameters)
143
+
118
144
  @timethis
119
145
  def _decompile(self):
120
146
  if self.func.is_simprocedure:
121
147
  return
122
148
 
123
- # Load from cache
124
- try:
125
- cache = self.kb.structured_code[(self.func.addr, self._flavor)]
149
+ cache = None
150
+
151
+ if self._cache_parameters is not None:
152
+ try:
153
+ cache = self.kb.decompilations[self.func.addr]
154
+ if not self._can_use_decompilation_cache(cache):
155
+ cache = None
156
+ except KeyError:
157
+ pass
158
+
159
+ if cache:
126
160
  old_codegen = cache.codegen
127
161
  old_clinic = cache.clinic
128
162
  ite_exprs = cache.ite_exprs if self._ite_exprs is None else self._ite_exprs
129
163
  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
164
+ l.debug("Decompilation cache hit")
165
+ else:
133
166
  old_codegen = None
134
167
  old_clinic = None
168
+ ite_exprs = self._ite_exprs
169
+ binop_operators = self._binop_operators
170
+ l.debug("Decompilation cache miss")
135
171
 
136
172
  self.options_by_class = defaultdict(list)
137
173
 
@@ -167,6 +203,7 @@ class Decompiler(Analysis):
167
203
  fold_callexprs_into_conditions = True
168
204
 
169
205
  cache = DecompilationCache(self.func.addr)
206
+ cache.parameters = self._cache_parameters
170
207
  cache.ite_exprs = ite_exprs
171
208
  cache.binop_operators = binop_operators
172
209
 
@@ -189,6 +226,7 @@ class Decompiler(Analysis):
189
226
  cache=cache,
190
227
  progress_callback=progress_callback,
191
228
  inline_functions=self._inline_functions,
229
+ optimization_scratch=self._optimization_scratch,
192
230
  **self.options_to_params(self.options_by_class["clinic"]),
193
231
  )
194
232
  else:
@@ -302,6 +340,8 @@ class Decompiler(Analysis):
302
340
  self.cache.codegen = codegen
303
341
  self.cache.clinic = self.clinic
304
342
 
343
+ self.kb.decompilations[self.func.addr] = self.cache
344
+
305
345
  def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
306
346
  return self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
307
347
  self.func,
@@ -355,6 +395,7 @@ class Decompiler(Analysis):
355
395
  variable_kb=self._variable_kb,
356
396
  reaching_definitions=reaching_definitions,
357
397
  entry_node_addr=self.clinic.entry_node_addr,
398
+ scratch=self._optimization_scratch,
358
399
  **kwargs,
359
400
  )
360
401
 
@@ -414,6 +455,7 @@ class Decompiler(Analysis):
414
455
  reaching_definitions=reaching_definitions,
415
456
  vvar_id_start=self.vvar_id_start,
416
457
  entry_node_addr=self.clinic.entry_node_addr,
458
+ scratch=self._optimization_scratch,
417
459
  **kwargs,
418
460
  )
419
461
 
@@ -442,7 +484,7 @@ class Decompiler(Analysis):
442
484
  continue
443
485
 
444
486
  pass_ = timethis(pass_)
445
- a = pass_(self.func, seq=seq_node, **kwargs)
487
+ a = pass_(self.func, seq=seq_node, scratch=self._optimization_scratch, **kwargs)
446
488
  if a.out_seq:
447
489
  seq_node = a.out_seq
448
490
 
@@ -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
@@ -126,7 +128,7 @@ class SimEngineDephiRewriting(
126
128
  return None
127
129
 
128
130
  def _handle_Call(self, stmt: Call) -> Call | None:
129
- new_target = self._expr(stmt.target) if stmt.target is not None else None
131
+ new_target = self._expr(stmt.target) if stmt.target is not None and not isinstance(stmt.target, str) else None
130
132
  new_ret_expr = self._expr(stmt.ret_expr) if stmt.ret_expr is not None else None
131
133
  new_fp_ret_expr = self._expr(stmt.fp_ret_expr) if stmt.fp_ret_expr is not None else None
132
134
 
@@ -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
@@ -1,23 +1,52 @@
1
1
  from __future__ import annotations
2
2
  from typing import Any, TYPE_CHECKING
3
- from ailment import AILBlockWalkerBase
3
+ from collections import defaultdict
4
+ import logging
5
+
6
+ from ailment import AILBlockWalkerBase, AILBlockWalker
7
+ from ailment.statement import Assignment, Call
8
+ from ailment.expression import VirtualVariable, Convert, BinaryOp, Phi
9
+
10
+ from angr.knowledge_plugins.key_definitions import atoms
11
+ from angr.code_location import CodeLocation
4
12
 
5
13
  if TYPE_CHECKING:
6
14
  from ailment.expression import (
7
15
  Expression,
8
- BinaryOp,
9
16
  Load,
10
17
  UnaryOp,
11
- Convert,
12
18
  ITE,
13
19
  DirtyExpression,
14
20
  VEXCCallExpression,
15
21
  )
16
- from ailment.statement import Call, Statement
22
+ from ailment.statement import Statement
17
23
  from ailment.block import Block
18
24
 
19
25
 
20
- class ExpressionNarrowingWalker(AILBlockWalkerBase):
26
+ _l = logging.getLogger(__name__)
27
+
28
+
29
+ class ExprNarrowingInfo:
30
+ """
31
+ Stores the analysis result of _narrowing_needed().
32
+ """
33
+
34
+ __slots__ = ("narrowable", "to_size", "use_exprs", "phi_vars")
35
+
36
+ def __init__(
37
+ self,
38
+ narrowable: bool,
39
+ to_size: int | None = None,
40
+ use_exprs: list[tuple[atoms.VirtualVariable, CodeLocation, tuple[str, tuple[Expression, ...]]]] | None = None,
41
+ phi_vars: set[atoms.VirtualVariable] | None = None,
42
+ ):
43
+ self.narrowable = narrowable
44
+ self.to_size = to_size
45
+ self.use_exprs = use_exprs
46
+ self.phi_vars = phi_vars
47
+
48
+
49
+ class NarrowingInfoExtractor(AILBlockWalkerBase):
21
50
  """
22
51
  Walks a statement or an expression and extracts the operations that are applied on the given expression.
23
52
 
@@ -76,7 +105,11 @@ class ExpressionNarrowingWalker(AILBlockWalkerBase):
76
105
  def _handle_DirtyExpression(
77
106
  self, expr_idx: int, expr: DirtyExpression, stmt_idx: int, stmt: Statement, block: Block | None
78
107
  ):
79
- 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
80
113
 
81
114
  def _handle_VEXCCallExpression(
82
115
  self, expr_idx: int, expr: VEXCCallExpression, stmt_idx: int, stmt: Statement, block: Block | None
@@ -85,3 +118,170 @@ class ExpressionNarrowingWalker(AILBlockWalkerBase):
85
118
  for idx, operand in enumerate(expr.operands):
86
119
  r |= self._handle_expr(idx, operand, stmt_idx, stmt, block)
87
120
  return r
121
+
122
+
123
+ class ExpressionNarrower(AILBlockWalker):
124
+ """
125
+ Narrows an expression regardless of whether the expression is a definition or a use.
126
+ """
127
+
128
+ def __init__(
129
+ self, project, rd, narrowables, addr2blocks: dict[tuple[int, int | None], Block], new_blocks: dict[Block, Block]
130
+ ):
131
+ super().__init__(update_block=False)
132
+
133
+ self.project = project
134
+ self._rd = rd
135
+ self._addr2blocks = addr2blocks
136
+ self._new_blocks = new_blocks
137
+
138
+ self.new_vvar_sizes: dict[int, int] = {}
139
+ self.replacement_core_vvars: dict[int, list[VirtualVariable]] = defaultdict(list)
140
+ self.narrowed_any = False
141
+
142
+ for def_, narrow_info in narrowables:
143
+ self.new_vvar_sizes[def_.atom.varid] = narrow_info.to_size
144
+
145
+ def walk(self, block: Block):
146
+ self.narrowed_any = False
147
+ return super().walk(block)
148
+
149
+ def _handle_Assignment(self, stmt_idx: int, stmt: Assignment, block: Block | None) -> Assignment | None:
150
+
151
+ if isinstance(stmt.src, Phi):
152
+ changed = False
153
+
154
+ src_and_vvars = []
155
+ for src, vvar in stmt.src.src_and_vvars:
156
+ if vvar is None:
157
+ src_and_vvars.append((src, None))
158
+ continue
159
+ if vvar.varid in self.new_vvar_sizes and self.new_vvar_sizes[vvar.varid] != vvar.size:
160
+ self.narrowed_any = True
161
+ changed = True
162
+ new_var = VirtualVariable(
163
+ vvar.idx,
164
+ vvar.varid,
165
+ self.new_vvar_sizes[vvar.varid] * self.project.arch.byte_width,
166
+ category=vvar.category,
167
+ oident=vvar.oident,
168
+ **vvar.tags,
169
+ )
170
+
171
+ self.replacement_core_vvars[new_var.varid].append(new_var)
172
+ else:
173
+ new_var = None
174
+
175
+ src_and_vvars.append((src, new_var))
176
+
177
+ new_src = Phi(stmt.src.idx, stmt.src.bits, src_and_vvars, **stmt.src.tags)
178
+
179
+ else:
180
+ new_src = self._handle_expr(1, stmt.src, stmt_idx, stmt, block)
181
+ if new_src is None:
182
+ changed = False
183
+ new_src = stmt.src
184
+ else:
185
+ changed = True
186
+
187
+ if isinstance(stmt.dst, VirtualVariable) and stmt.dst.varid in self.new_vvar_sizes:
188
+ changed = True
189
+ new_dst = VirtualVariable(
190
+ stmt.dst.idx,
191
+ stmt.dst.varid,
192
+ self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width,
193
+ category=stmt.dst.category,
194
+ oident=stmt.dst.oident,
195
+ **stmt.dst.tags,
196
+ )
197
+
198
+ self.replacement_core_vvars[new_dst.varid].append(new_dst)
199
+
200
+ if isinstance(new_src, Phi):
201
+ new_src.bits = self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width
202
+ else:
203
+ new_src = Convert(
204
+ None,
205
+ stmt.src.bits,
206
+ self.new_vvar_sizes[stmt.dst.varid] * self.project.arch.byte_width,
207
+ False,
208
+ new_src,
209
+ **new_src.tags,
210
+ )
211
+ else:
212
+ new_dst = self._handle_expr(0, stmt.dst, stmt_idx, stmt, block)
213
+ if new_dst is not None:
214
+ changed = True
215
+ else:
216
+ new_dst = stmt.dst
217
+
218
+ if changed:
219
+ self.narrowed_any = True
220
+ return Assignment(stmt.idx, new_dst, new_src, **stmt.tags)
221
+
222
+ return None
223
+
224
+ def _handle_VirtualVariable(
225
+ self, expr_idx: int, expr: VirtualVariable, stmt_idx: int, stmt: Statement, block: Block | None
226
+ ) -> Convert | None:
227
+ if expr.varid in self.new_vvar_sizes and self.new_vvar_sizes[expr.varid] != expr.size:
228
+ self.narrowed_any = True
229
+ new_expr = VirtualVariable(
230
+ expr.idx,
231
+ expr.varid,
232
+ self.new_vvar_sizes[expr.varid] * self.project.arch.byte_width,
233
+ category=expr.category,
234
+ oident=expr.oident,
235
+ **expr.tags,
236
+ )
237
+
238
+ self.replacement_core_vvars[expr.varid].append(new_expr)
239
+
240
+ return Convert(
241
+ None,
242
+ new_expr.bits,
243
+ expr.bits,
244
+ False,
245
+ new_expr,
246
+ **new_expr.tags,
247
+ )
248
+ return None
249
+
250
+ def _handle_Call(self, stmt_idx: int, stmt: Call, block: Block | None) -> Call | None:
251
+ new_stmt = super()._handle_Call(stmt_idx, stmt, block)
252
+ if new_stmt is None:
253
+ changed = False
254
+ new_stmt = stmt
255
+ else:
256
+ changed = True
257
+
258
+ if (
259
+ stmt.ret_expr is not None
260
+ and isinstance(stmt.ret_expr, VirtualVariable)
261
+ and stmt.ret_expr.was_reg
262
+ and stmt.ret_expr.varid in self.new_vvar_sizes
263
+ and stmt.ret_expr.size != self.new_vvar_sizes[stmt.ret_expr.varid]
264
+ ):
265
+ changed = True
266
+
267
+ # update reg name
268
+ tags = dict(stmt.ret_expr.tags)
269
+ tags["reg_name"] = self.project.arch.translate_register_name(
270
+ stmt.ret_expr.reg_offset, size=self.new_vvar_sizes[stmt.ret_expr.varid]
271
+ )
272
+ new_ret_expr = VirtualVariable(
273
+ stmt.ret_expr.idx,
274
+ stmt.ret_expr.varid,
275
+ self.new_vvar_sizes[stmt.ret_expr.varid] * self.project.arch.byte_width,
276
+ category=stmt.ret_expr.category,
277
+ oident=stmt.ret_expr.oident,
278
+ **tags,
279
+ )
280
+ self.replacement_core_vvars[new_ret_expr.varid].append(new_ret_expr)
281
+ new_stmt.ret_expr = new_ret_expr
282
+
283
+ if changed:
284
+ self.narrowed_any = True
285
+ return new_stmt
286
+
287
+ return None
@@ -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