angr 9.2.138__py3-none-manylinux2014_x86_64.whl → 9.2.140__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 (100) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +48 -21
  3. angr/analyses/calling_convention/fact_collector.py +59 -12
  4. angr/analyses/calling_convention/utils.py +2 -2
  5. angr/analyses/cfg/cfg_base.py +13 -0
  6. angr/analyses/cfg/cfg_fast.py +23 -4
  7. angr/analyses/decompiler/ail_simplifier.py +79 -53
  8. angr/analyses/decompiler/block_simplifier.py +0 -2
  9. angr/analyses/decompiler/callsite_maker.py +80 -14
  10. angr/analyses/decompiler/clinic.py +99 -80
  11. angr/analyses/decompiler/condition_processor.py +2 -2
  12. angr/analyses/decompiler/decompiler.py +19 -7
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +16 -7
  14. angr/analyses/decompiler/expression_narrower.py +1 -1
  15. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  16. angr/analyses/decompiler/optimization_passes/condition_constprop.py +149 -0
  17. angr/analyses/decompiler/optimization_passes/const_prop_reverter.py +8 -7
  18. angr/analyses/decompiler/optimization_passes/deadblock_remover.py +12 -3
  19. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +1 -1
  20. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +21 -13
  21. angr/analyses/decompiler/optimization_passes/optimization_pass.py +21 -12
  22. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +17 -9
  23. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +7 -10
  24. angr/analyses/decompiler/peephole_optimizations/eager_eval.py +12 -1
  25. angr/analyses/decompiler/peephole_optimizations/remove_redundant_conversions.py +61 -25
  26. angr/analyses/decompiler/peephole_optimizations/remove_redundant_shifts.py +50 -1
  27. angr/analyses/decompiler/presets/fast.py +2 -0
  28. angr/analyses/decompiler/presets/full.py +2 -0
  29. angr/analyses/decompiler/region_simplifiers/expr_folding.py +259 -108
  30. angr/analyses/decompiler/region_simplifiers/region_simplifier.py +28 -9
  31. angr/analyses/decompiler/ssailification/rewriting_engine.py +20 -2
  32. angr/analyses/decompiler/ssailification/traversal_engine.py +4 -3
  33. angr/analyses/decompiler/structured_codegen/c.py +10 -3
  34. angr/analyses/decompiler/structuring/dream.py +28 -19
  35. angr/analyses/decompiler/structuring/phoenix.py +253 -89
  36. angr/analyses/decompiler/structuring/recursive_structurer.py +1 -0
  37. angr/analyses/decompiler/structuring/structurer_base.py +121 -46
  38. angr/analyses/decompiler/structuring/structurer_nodes.py +6 -1
  39. angr/analyses/decompiler/utils.py +60 -1
  40. angr/analyses/deobfuscator/api_obf_finder.py +13 -5
  41. angr/analyses/deobfuscator/api_obf_type2_finder.py +166 -0
  42. angr/analyses/deobfuscator/string_obf_finder.py +105 -18
  43. angr/analyses/forward_analysis/forward_analysis.py +1 -1
  44. angr/analyses/propagator/top_checker_mixin.py +6 -6
  45. angr/analyses/reaching_definitions/__init__.py +2 -1
  46. angr/analyses/reaching_definitions/dep_graph.py +1 -12
  47. angr/analyses/reaching_definitions/engine_vex.py +36 -31
  48. angr/analyses/reaching_definitions/function_handler.py +15 -2
  49. angr/analyses/reaching_definitions/rd_state.py +1 -37
  50. angr/analyses/reaching_definitions/reaching_definitions.py +13 -24
  51. angr/analyses/s_propagator.py +129 -87
  52. angr/analyses/s_reaching_definitions/s_rda_model.py +7 -1
  53. angr/analyses/s_reaching_definitions/s_rda_view.py +2 -2
  54. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +3 -1
  55. angr/analyses/stack_pointer_tracker.py +36 -22
  56. angr/analyses/typehoon/simple_solver.py +45 -7
  57. angr/analyses/typehoon/typeconsts.py +18 -5
  58. angr/analyses/variable_recovery/engine_ail.py +1 -1
  59. angr/analyses/variable_recovery/engine_base.py +62 -67
  60. angr/analyses/variable_recovery/engine_vex.py +1 -1
  61. angr/analyses/variable_recovery/irsb_scanner.py +2 -2
  62. angr/block.py +69 -107
  63. angr/callable.py +14 -7
  64. angr/calling_conventions.py +81 -10
  65. angr/distributed/__init__.py +1 -1
  66. angr/engines/__init__.py +7 -8
  67. angr/engines/engine.py +3 -138
  68. angr/engines/failure.py +2 -2
  69. angr/engines/hook.py +2 -2
  70. angr/engines/light/engine.py +5 -10
  71. angr/engines/pcode/emulate.py +2 -2
  72. angr/engines/pcode/engine.py +2 -14
  73. angr/engines/pcode/lifter.py +2 -2
  74. angr/engines/procedure.py +2 -2
  75. angr/engines/soot/engine.py +2 -2
  76. angr/engines/soot/statements/switch.py +1 -1
  77. angr/engines/successors.py +123 -17
  78. angr/engines/syscall.py +2 -2
  79. angr/engines/unicorn.py +3 -3
  80. angr/engines/vex/heavy/heavy.py +3 -15
  81. angr/engines/vex/lifter.py +2 -2
  82. angr/engines/vex/light/light.py +2 -2
  83. angr/factory.py +4 -19
  84. angr/knowledge_plugins/cfg/cfg_model.py +3 -2
  85. angr/knowledge_plugins/key_definitions/atoms.py +8 -4
  86. angr/knowledge_plugins/key_definitions/live_definitions.py +41 -103
  87. angr/knowledge_plugins/labels.py +2 -2
  88. angr/knowledge_plugins/obfuscations.py +1 -0
  89. angr/knowledge_plugins/xrefs/xref_manager.py +4 -0
  90. angr/sim_type.py +19 -17
  91. angr/state_plugins/plugin.py +19 -4
  92. angr/storage/memory_mixins/memory_mixin.py +1 -1
  93. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +10 -5
  94. angr/utils/ssa/__init__.py +119 -4
  95. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/METADATA +6 -6
  96. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/RECORD +100 -98
  97. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/LICENSE +0 -0
  98. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/WHEEL +0 -0
  99. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/entry_points.txt +0 -0
  100. {angr-9.2.138.dist-info → angr-9.2.140.dist-info}/top_level.txt +0 -0
@@ -27,6 +27,7 @@ from ailment.expression import (
27
27
  from angr.analyses.s_propagator import SPropagatorAnalysis
28
28
  from angr.analyses.s_reaching_definitions import SRDAModel
29
29
  from angr.utils.ail import is_phi_assignment, HasExprWalker
30
+ from angr.utils.ssa import has_call_in_between_stmts, has_store_stmt_in_between_stmts, has_load_expr_in_between_stmts
30
31
  from angr.code_location import CodeLocation, ExternalCodeLocation
31
32
  from angr.sim_variable import SimStackVariable, SimMemoryVariable, SimVariable
32
33
  from angr.knowledge_plugins.propagations.states import Equivalence
@@ -102,7 +103,7 @@ class AILSimplifier(Analysis):
102
103
  self.func = func
103
104
  self.func_graph = func_graph if func_graph is not None else func.graph
104
105
  self._reaching_definitions: SRDAModel | None = None
105
- self._propagator = None
106
+ self._propagator: SPropagatorAnalysis | None = None
106
107
 
107
108
  self._remove_dead_memdefs = remove_dead_memdefs
108
109
  self._stack_arg_offsets = stack_arg_offsets
@@ -117,6 +118,7 @@ class AILSimplifier(Analysis):
117
118
  self._removed_vvar_ids = removed_vvar_ids if removed_vvar_ids is not None else set()
118
119
  self._arg_vvars = arg_vvars
119
120
  self._avoid_vvar_ids = avoid_vvar_ids
121
+ self._propagator_dead_vvar_ids: set[int] = set()
120
122
 
121
123
  self._calls_to_remove: set[CodeLocation] = set()
122
124
  self._assignments_to_remove: set[CodeLocation] = set()
@@ -231,6 +233,7 @@ class AILSimplifier(Analysis):
231
233
  only_consts=self._only_consts,
232
234
  )
233
235
  self._propagator = prop
236
+ self._propagator_dead_vvar_ids = prop.dead_vvar_ids
234
237
  return prop
235
238
 
236
239
  def _compute_equivalence(self) -> set[Equivalence]:
@@ -247,6 +250,9 @@ class AILSimplifier(Analysis):
247
250
  if isinstance(stmt.ret_expr, (VirtualVariable, Load)):
248
251
  codeloc = CodeLocation(block.addr, stmt_idx, block_idx=block.idx, ins_addr=stmt.ins_addr)
249
252
  equivalence.add(Equivalence(codeloc, stmt.ret_expr, stmt))
253
+ elif isinstance(stmt.fp_ret_expr, (VirtualVariable, Load)):
254
+ codeloc = CodeLocation(block.addr, stmt_idx, block_idx=block.idx, ins_addr=stmt.ins_addr)
255
+ equivalence.add(Equivalence(codeloc, stmt.fp_ret_expr, stmt))
250
256
  elif (
251
257
  isinstance(stmt, Store)
252
258
  and isinstance(stmt.size, int)
@@ -287,7 +293,7 @@ class AILSimplifier(Analysis):
287
293
 
288
294
  narrowed = False
289
295
 
290
- addr_and_idx_to_block: dict[tuple[int, int], Block] = {}
296
+ addr_and_idx_to_block: dict[tuple[int, int | None], Block] = {}
291
297
  for block in self.func_graph.nodes():
292
298
  addr_and_idx_to_block[(block.addr, block.idx)] = block
293
299
 
@@ -419,6 +425,7 @@ class AILSimplifier(Analysis):
419
425
  return ExprNarrowingInfo(False)
420
426
 
421
427
  block = self.blocks.get(old_block, old_block)
428
+ assert loc.stmt_idx is not None
422
429
  if loc.stmt_idx >= len(block.statements):
423
430
  # missing a statement for whatever reason
424
431
  return ExprNarrowingInfo(False)
@@ -546,14 +553,23 @@ class AILSimplifier(Analysis):
546
553
  return None, None
547
554
  return expr.size, ("expr", (expr,))
548
555
 
549
- first_op = walker.operations[0]
556
+ ops = walker.operations
557
+ first_op = ops[0]
558
+ if isinstance(first_op, BinaryOp) and first_op.op in {"Add", "Sub"}:
559
+ # expr + x
560
+ ops = ops[1:]
561
+ if not ops:
562
+ if expr is None:
563
+ return None, None
564
+ return expr.size, ("expr", (expr,))
565
+ first_op = ops[0]
550
566
  if isinstance(first_op, Convert) and first_op.to_bits >= self.project.arch.byte_width:
551
567
  # we need at least one byte!
552
568
  return first_op.to_bits // self.project.arch.byte_width, ("convert", (first_op,))
553
569
  if isinstance(first_op, BinaryOp):
554
570
  second_op = None
555
- if len(walker.operations) >= 2:
556
- second_op = walker.operations[1]
571
+ if len(ops) >= 2:
572
+ second_op = ops[1]
557
573
  if (
558
574
  first_op.op == "And"
559
575
  and isinstance(first_op.operands[1], Const)
@@ -618,9 +634,9 @@ class AILSimplifier(Analysis):
618
634
  block = blocks_by_addr_and_idx[(block_addr, block_idx)]
619
635
 
620
636
  # only replace loads if there are stack arguments in this block
621
- replace_loads = insn_addrs_using_stack_args is not None and {
622
- stmt.ins_addr for stmt in block.statements
623
- }.intersection(insn_addrs_using_stack_args)
637
+ replace_loads: bool = insn_addrs_using_stack_args is not None and bool(
638
+ {stmt.ins_addr for stmt in block.statements}.intersection(insn_addrs_using_stack_args)
639
+ )
624
640
 
625
641
  # remove virtual variables in the avoid list
626
642
  if self._avoid_vvar_ids:
@@ -657,7 +673,7 @@ class AILSimplifier(Analysis):
657
673
  if not equivalence:
658
674
  return simplified
659
675
 
660
- addr_and_idx_to_block: dict[tuple[int, int], Block] = {}
676
+ addr_and_idx_to_block: dict[tuple[int, int | None], Block] = {}
661
677
  for block in self.func_graph.nodes():
662
678
  addr_and_idx_to_block[(block.addr, block.idx)] = block
663
679
 
@@ -938,6 +954,8 @@ class AILSimplifier(Analysis):
938
954
  for use_loc in all_use_locs:
939
955
  if use_loc == eq.codeloc:
940
956
  continue
957
+ assert use_loc.block_addr is not None
958
+ assert use_loc.stmt_idx is not None
941
959
  block = addr_and_idx_to_block[(use_loc.block_addr, use_loc.block_idx)]
942
960
  stmt = block.statements[use_loc.stmt_idx]
943
961
  if isinstance(stmt, Assignment) or (isinstance(replace_with, Load) and isinstance(stmt, Store)):
@@ -949,11 +967,15 @@ class AILSimplifier(Analysis):
949
967
 
950
968
  remove_initial_assignment = False # expression folding will take care of it
951
969
 
970
+ assert replace_with is not None
971
+
952
972
  if any(not isinstance(use_and_expr[1], VirtualVariable) for _, use_and_expr in all_uses_with_def):
953
973
  # if any of the uses are phi assignments, we skip
954
974
  used_in_phi_assignment = False
955
975
  for _, use_and_expr in all_uses_with_def:
956
976
  u = use_and_expr[0]
977
+ assert u.block_addr is not None
978
+ assert u.stmt_idx is not None
957
979
  block = addr_and_idx_to_block[(u.block_addr, u.block_idx)]
958
980
  stmt = block.statements[u.stmt_idx]
959
981
  if is_phi_assignment(stmt):
@@ -1116,13 +1138,14 @@ class AILSimplifier(Analysis):
1116
1138
  In such cases, the one-use expression folder in RegionSimplifier will perform this transformation.
1117
1139
  """
1118
1140
 
1141
+ # pylint:disable=unreachable
1119
1142
  simplified = False
1120
1143
 
1121
1144
  equivalence = self._compute_equivalence()
1122
1145
  if not equivalence:
1123
1146
  return simplified
1124
1147
 
1125
- addr_and_idx_to_block: dict[tuple[int, int], Block] = {}
1148
+ addr_and_idx_to_block: dict[tuple[int, int | None], Block] = {}
1126
1149
  for block in self.func_graph.nodes():
1127
1150
  addr_and_idx_to_block[(block.addr, block.idx)] = block
1128
1151
 
@@ -1160,6 +1183,8 @@ class AILSimplifier(Analysis):
1160
1183
  ),
1161
1184
  eq.codeloc,
1162
1185
  )
1186
+ assert the_def.codeloc.block_addr is not None
1187
+ assert the_def.codeloc.stmt_idx is not None
1163
1188
 
1164
1189
  all_uses: set[tuple[CodeLocation, Any]] = set(rd.get_vvar_uses_with_expr(the_def.atom))
1165
1190
 
@@ -1168,6 +1193,8 @@ class AILSimplifier(Analysis):
1168
1193
  u, used_expr = next(iter(all_uses))
1169
1194
  if used_expr is None:
1170
1195
  continue
1196
+ assert u.block_addr is not None
1197
+ assert u.stmt_idx is not None
1171
1198
 
1172
1199
  if u in def_locations_to_remove:
1173
1200
  # this use site has been altered by previous folding attempts. the corresponding statement will be
@@ -1188,41 +1215,28 @@ class AILSimplifier(Analysis):
1188
1215
  if u.block_addr not in {b.addr for b in super_node_blocks}:
1189
1216
  continue
1190
1217
 
1191
- # check if the register has been overwritten by statements in between the def site and the use site
1192
- # usesite_atom_defs = set(rd.get_defs(the_def.atom, u, OP_BEFORE))
1193
- # if len(usesite_atom_defs) != 1:
1194
- # continue
1195
- # usesite_atom_def = next(iter(usesite_atom_defs))
1196
- # if usesite_atom_def != the_def:
1197
- # continue
1198
-
1199
- # check if any atoms that the call relies on has been overwritten by statements in between the def site
1200
- # and the use site.
1201
- # TODO: Prove non-interference
1202
- # defsite_all_expr_uses = set(rd.all_uses.get_uses_by_location(the_def.codeloc))
1203
- # defsite_used_atoms = set()
1204
- # for dd in defsite_all_expr_uses:
1205
- # defsite_used_atoms.add(dd.atom)
1206
- # usesite_expr_def_outdated = False
1207
- # for defsite_expr_atom in defsite_used_atoms:
1208
- # usesite_expr_uses = set(rd.get_defs(defsite_expr_atom, u, OP_BEFORE))
1209
- # if not usesite_expr_uses:
1210
- # # the atom is not defined at the use site - it's fine
1211
- # continue
1212
- # defsite_expr_uses = set(rd.get_defs(defsite_expr_atom, the_def.codeloc, OP_BEFORE))
1213
- # if usesite_expr_uses != defsite_expr_uses:
1214
- # # special case: ok if this atom is assigned to at the def site and has not been overwritten
1215
- # if len(usesite_expr_uses) == 1:
1216
- # usesite_expr_use = next(iter(usesite_expr_uses))
1217
- # if usesite_expr_use.atom == defsite_expr_atom and (
1218
- # usesite_expr_use.codeloc == the_def.codeloc
1219
- # or usesite_expr_use.codeloc.block_addr == call_addr
1220
- # ):
1221
- # continue
1222
- # usesite_expr_def_outdated = True
1223
- # break
1224
- # if usesite_expr_def_outdated:
1225
- # continue
1218
+ # ensure there are no other calls between the def site and the use site.
1219
+ # this is because we do not want to alter the order of calls.
1220
+ u_inclusive = CodeLocation(u.block_addr, u.stmt_idx + 1, block_idx=u.block_idx)
1221
+ # note that the target statement being a store is fine
1222
+ if (
1223
+ has_call_in_between_stmts(
1224
+ self.func_graph,
1225
+ addr_and_idx_to_block,
1226
+ the_def.codeloc,
1227
+ u_inclusive,
1228
+ skip_if_contains_vvar=the_def.atom.varid,
1229
+ )
1230
+ or has_store_stmt_in_between_stmts(self.func_graph, addr_and_idx_to_block, the_def.codeloc, u)
1231
+ or has_load_expr_in_between_stmts(
1232
+ self.func_graph,
1233
+ addr_and_idx_to_block,
1234
+ the_def.codeloc,
1235
+ u_inclusive,
1236
+ skip_if_contains_vvar=the_def.atom.varid,
1237
+ )
1238
+ ):
1239
+ continue
1226
1240
 
1227
1241
  # check if there are any calls in between the def site and the use site
1228
1242
  if self._count_calls_in_supernodeblocks(super_node_blocks, the_def.codeloc, u) > 0:
@@ -1308,8 +1322,8 @@ class AILSimplifier(Analysis):
1308
1322
  # keeping tracking of statements to remove and statements (as well as dead vvars) to keep allows us to handle
1309
1323
  # cases where a statement defines more than one atoms, e.g., a call statement that defines both the return
1310
1324
  # value and the floating-point return value.
1311
- stmts_to_remove_per_block: dict[tuple[int, int], set[int]] = defaultdict(set)
1312
- stmts_to_keep_per_block: dict[tuple[int, int], set[int]] = defaultdict(set)
1325
+ stmts_to_remove_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
1326
+ stmts_to_keep_per_block: dict[tuple[int, int | None], set[int]] = defaultdict(set)
1313
1327
  dead_vvar_ids: set[int] = set()
1314
1328
 
1315
1329
  # Find all statements that should be removed
@@ -1326,13 +1340,17 @@ class AILSimplifier(Analysis):
1326
1340
  if isinstance(def_.atom, atoms.MemoryLocation) and isinstance(def_.atom.addr, int):
1327
1341
  continue
1328
1342
  if isinstance(def_.atom, atoms.VirtualVariable):
1329
- if def_.atom.was_stack:
1343
+ if def_.atom.varid in self._propagator_dead_vvar_ids:
1344
+ # we are definitely removing this variable if it has no uses
1345
+ uses = rd.get_vvar_uses(def_.atom)
1346
+ elif def_.atom.was_stack:
1330
1347
  if not self._remove_dead_memdefs:
1331
1348
  if rd.is_phi_vvar_id(def_.atom.varid):
1332
1349
  # we always remove unused phi variables
1333
1350
  pass
1334
1351
  elif stackarg_offsets is not None:
1335
1352
  # we always remove definitions for stack arguments
1353
+ assert def_.atom.stack_offset is not None
1336
1354
  if (def_.atom.stack_offset & mask) not in stackarg_offsets:
1337
1355
  continue
1338
1356
  else:
@@ -1353,10 +1371,15 @@ class AILSimplifier(Analysis):
1353
1371
  dead_vvar_ids.add(def_.atom.varid)
1354
1372
 
1355
1373
  if not isinstance(def_.codeloc, ExternalCodeLocation):
1374
+ assert def_.codeloc.block_addr is not None
1375
+ assert def_.codeloc.stmt_idx is not None
1356
1376
  stmts_to_remove_per_block[(def_.codeloc.block_addr, def_.codeloc.block_idx)].add(
1357
1377
  def_.codeloc.stmt_idx
1358
1378
  )
1359
1379
  else:
1380
+ if not isinstance(def_.codeloc, ExternalCodeLocation):
1381
+ assert def_.codeloc.block_addr is not None
1382
+ assert def_.codeloc.stmt_idx is not None
1360
1383
  stmts_to_keep_per_block[(def_.codeloc.block_addr, def_.codeloc.block_idx)].add(def_.codeloc.stmt_idx)
1361
1384
 
1362
1385
  # find all phi variables that rely on variables that no longer exist
@@ -1369,6 +1392,7 @@ class AILSimplifier(Analysis):
1369
1392
  vvarid in removed_vvar_ids for vvarid in phi_use_varids
1370
1393
  ):
1371
1394
  loc = rd.all_vvar_definitions[rd.varid_to_vvar[phi_varid]]
1395
+ assert loc.block_addr is not None and loc.stmt_idx is not None
1372
1396
  stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)].add(loc.stmt_idx)
1373
1397
  new_removed_vvar_ids.add(phi_varid)
1374
1398
  all_removed_var_ids.add(phi_varid)
@@ -1380,11 +1404,13 @@ class AILSimplifier(Analysis):
1380
1404
  redundant_phi_and_dirty_varids = self._find_cyclic_dependent_phis_and_dirty_vvars(rd)
1381
1405
  for varid in redundant_phi_and_dirty_varids:
1382
1406
  loc = rd.all_vvar_definitions[rd.varid_to_vvar[varid]]
1407
+ assert loc.block_addr is not None and loc.stmt_idx is not None
1383
1408
  stmts_to_remove_per_block[(loc.block_addr, loc.block_idx)].add(loc.stmt_idx)
1384
1409
  stmts_to_keep_per_block[(loc.block_addr, loc.block_idx)].discard(loc.stmt_idx)
1385
1410
 
1386
1411
  for codeloc in self._calls_to_remove | self._assignments_to_remove:
1387
1412
  # this call can be removed. make sure it exists in stmts_to_remove_per_block
1413
+ assert codeloc.block_addr is not None and codeloc.stmt_idx is not None
1388
1414
  stmts_to_remove_per_block[codeloc.block_addr, codeloc.block_idx].add(codeloc.stmt_idx)
1389
1415
 
1390
1416
  simplified = False
@@ -1540,7 +1566,7 @@ class AILSimplifier(Analysis):
1540
1566
  if bail:
1541
1567
  continue
1542
1568
 
1543
- if all(varid in phi_and_dirty_vvar_ids for varid in scc):
1569
+ if all(varid in phi_and_dirty_vvar_ids or rd.varid_to_vvar[varid].was_reg for varid in scc):
1544
1570
  cyclic_dependent_phi_varids |= set(scc)
1545
1571
 
1546
1572
  return cyclic_dependent_phi_varids
@@ -1554,7 +1580,7 @@ class AILSimplifier(Analysis):
1554
1580
  if rewriter_cls is None:
1555
1581
  return False
1556
1582
 
1557
- walker = None
1583
+ walker = AILBlockWalker()
1558
1584
 
1559
1585
  class _any_update:
1560
1586
  """
@@ -1563,7 +1589,9 @@ class AILSimplifier(Analysis):
1563
1589
 
1564
1590
  v = False
1565
1591
 
1566
- def _handle_expr(expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement, block) -> Expression | None:
1592
+ def _handle_expr(
1593
+ expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement | None, block: Block | None
1594
+ ) -> Expression | None:
1567
1595
  if isinstance(expr, VEXCCallExpression):
1568
1596
  rewriter = rewriter_cls(expr, self.project.arch)
1569
1597
  if rewriter.result is not None:
@@ -1574,8 +1602,6 @@ class AILSimplifier(Analysis):
1574
1602
  return AILBlockWalker._handle_expr(walker, expr_idx, expr, stmt_idx, stmt, block)
1575
1603
 
1576
1604
  blocks_by_addr_and_idx = {(node.addr, node.idx): node for node in self.func_graph.nodes()}
1577
-
1578
- walker = AILBlockWalker()
1579
1605
  walker._handle_expr = _handle_expr
1580
1606
 
1581
1607
  updated = False
@@ -58,7 +58,6 @@ class BlockSimplifier(Analysis):
58
58
  self,
59
59
  block: Block | None,
60
60
  func_addr: int | None = None,
61
- remove_dead_memdefs=False,
62
61
  stack_pointer_tracker=None,
63
62
  peephole_optimizations: None | (
64
63
  Iterable[type[PeepholeOptimizationStmtBase] | type[PeepholeOptimizationExprBase]]
@@ -74,7 +73,6 @@ class BlockSimplifier(Analysis):
74
73
  self.block = block
75
74
  self.func_addr = func_addr
76
75
 
77
- self._remove_dead_memdefs = remove_dead_memdefs
78
76
  self._stack_pointer_tracker = stack_pointer_tracker
79
77
 
80
78
  if peephole_optimizations is None:
@@ -5,9 +5,19 @@ import logging
5
5
 
6
6
  import archinfo
7
7
  from ailment import Stmt, Expr, Const
8
+ from ailment.manager import Manager
8
9
 
9
10
  from angr.procedures.stubs.format_parser import FormatParser, FormatSpecifier
10
- from angr.sim_type import SimTypeBottom, SimTypePointer, SimTypeChar, SimTypeInt, dereference_simtype
11
+ from angr.sim_type import (
12
+ SimTypeBottom,
13
+ SimTypePointer,
14
+ SimTypeChar,
15
+ SimTypeInt,
16
+ SimTypeFloat,
17
+ dereference_simtype,
18
+ SimTypeFunction,
19
+ SimTypeLongLong,
20
+ )
11
21
  from angr.calling_conventions import SimRegArg, SimStackArg, SimCC, SimStructArg, SimComboArg
12
22
  from angr.knowledge_plugins.key_definitions.constants import OP_BEFORE
13
23
  from angr.analyses import Analysis, register_analysis
@@ -27,7 +37,7 @@ class CallSiteMaker(Analysis):
27
37
  Add calling convention, declaration, and args to a call site.
28
38
  """
29
39
 
30
- def __init__(self, block, reaching_definitions=None, stack_pointer_tracker=None, ail_manager=None):
40
+ def __init__(self, block, reaching_definitions=None, stack_pointer_tracker=None, ail_manager: Manager = None):
31
41
  self.block = block
32
42
 
33
43
  self._reaching_definitions = reaching_definitions
@@ -60,7 +70,7 @@ class CallSiteMaker(Analysis):
60
70
  return
61
71
 
62
72
  cc = None
63
- prototype = None
73
+ prototype: SimTypeFunction | None = None
64
74
  func = None
65
75
  stack_arg_locs: list[SimStackArg] = []
66
76
  stackarg_sp_diff = 0
@@ -106,7 +116,9 @@ class CallSiteMaker(Analysis):
106
116
  for typelib_name in prototype_lib.type_collection_names:
107
117
  type_collections.append(SIM_TYPE_COLLECTIONS[typelib_name])
108
118
  if type_collections:
109
- prototype = dereference_simtype(prototype, type_collections).with_arch(self.project.arch)
119
+ prototype = dereference_simtype(prototype, type_collections).with_arch( # type: ignore
120
+ self.project.arch
121
+ )
110
122
 
111
123
  args = []
112
124
  arg_vvars = []
@@ -120,15 +132,18 @@ class CallSiteMaker(Analysis):
120
132
  arg_locs = cc.arg_locs(prototype)
121
133
  if prototype.variadic:
122
134
  # determine the number of variadic arguments
135
+ assert func is not None
123
136
  variadic_args = self._determine_variadic_arguments(func, cc, call_stmt)
124
137
  if variadic_args:
125
138
  callsite_ty = copy.copy(prototype)
126
- callsite_ty.args = list(callsite_ty.args)
139
+ callsite_args = list(callsite_ty.args)
140
+ base_type = SimTypeInt if self.project.arch.bits == 32 else SimTypeLongLong
127
141
  for _ in range(variadic_args):
128
- callsite_ty.args.append(SimTypeInt().with_arch(self.project.arch))
142
+ callsite_args.append(base_type().with_arch(self.project.arch))
143
+ callsite_ty.args = tuple(callsite_args)
129
144
  arg_locs = cc.arg_locs(callsite_ty)
130
145
 
131
- if arg_locs is not None:
146
+ if arg_locs is not None and cc is not None:
132
147
  expanded_arg_locs = []
133
148
  for arg_loc in arg_locs:
134
149
  if isinstance(arg_loc, SimComboArg):
@@ -155,6 +170,38 @@ class CallSiteMaker(Analysis):
155
170
  oident=vvar_def.oident,
156
171
  **vvar_def.tags,
157
172
  )
173
+ vvar_def_reg_offset = None
174
+ if vvar_def.was_reg:
175
+ vvar_def_reg_offset = vvar_def.reg_offset
176
+ elif (
177
+ vvar_def.was_parameter
178
+ and vvar_def.parameter_category == Expr.VirtualVariableCategory.REGISTER
179
+ ):
180
+ vvar_def_reg_offset = vvar_def.parameter_reg_offset
181
+
182
+ if vvar_def_reg_offset is not None and offset > vvar_def_reg_offset:
183
+ # we need to shift the value
184
+ vvar_use = Expr.BinaryOp(
185
+ self._ail_manager.next_atom(),
186
+ "Shr",
187
+ [
188
+ vvar_use,
189
+ Expr.Const(
190
+ self._ail_manager.next_atom(), None, (offset - vvar_def_reg_offset) * 8, 8
191
+ ),
192
+ ],
193
+ **vvar_use.tags,
194
+ )
195
+ if vvar_def.size > arg_loc.size:
196
+ # we need to narrow the value
197
+ vvar_use = Expr.Convert(
198
+ self._ail_manager.next_atom(),
199
+ vvar_use.bits,
200
+ arg_loc.size * self.project.arch.byte_width,
201
+ False,
202
+ vvar_use,
203
+ **vvar_use.tags,
204
+ )
158
205
  args.append(vvar_use)
159
206
  else:
160
207
  reg = Expr.Register(
@@ -219,6 +266,7 @@ class CallSiteMaker(Analysis):
219
266
  # calculate stack offsets for arguments that are put on the stack. these offsets will be consumed by
220
267
  # simplification steps in the future, which may decide to remove statements that store arguments on the stack.
221
268
  if stack_arg_locs:
269
+ assert self._stack_pointer_tracker is not None
222
270
  sp_offset = self._stack_pointer_tracker.offset_before(call_stmt.ins_addr, self.project.arch.sp_offset)
223
271
  if sp_offset is None:
224
272
  l.warning(
@@ -233,8 +281,22 @@ class CallSiteMaker(Analysis):
233
281
  }
234
282
 
235
283
  ret_expr = call_stmt.ret_expr
236
- # if ret_expr is None, it means in previous steps (such as during AIL simplification) we have deemed the return
237
- # value of this call statement as useless and is removed.
284
+ fp_ret_expr = call_stmt.fp_ret_expr
285
+ # if ret_expr and fp_ret_expr are None, it means in previous steps (such as during AIL simplification) we have
286
+ # deemed the return value of this call statement as useless and is removed.
287
+
288
+ if (
289
+ ret_expr is not None
290
+ and fp_ret_expr is not None
291
+ and prototype is not None
292
+ and prototype.returnty is not None
293
+ ):
294
+ # we need to determine the return type of this call (ret_expr vs fp_ret_expr)
295
+ is_float = isinstance(prototype.returnty, SimTypeFloat)
296
+ if is_float:
297
+ ret_expr = None
298
+ else:
299
+ fp_ret_expr = None
238
300
 
239
301
  if (
240
302
  ret_expr is not None
@@ -243,9 +305,9 @@ class CallSiteMaker(Analysis):
243
305
  and not isinstance(prototype.returnty, SimTypeBottom)
244
306
  and not isinstance(ret_expr, Expr.VirtualVariable)
245
307
  ):
246
- # try to narrow the return expression if needed
308
+ # try to narrow the non-float return expression if needed
247
309
  ret_type_bits = prototype.returnty.with_arch(self.project.arch).size
248
- if ret_expr.bits > ret_type_bits:
310
+ if ret_type_bits is not None and ret_expr.bits > ret_type_bits:
249
311
  ret_expr = ret_expr.copy()
250
312
  ret_expr.bits = ret_type_bits
251
313
  # TODO: Support narrowing virtual variables
@@ -257,6 +319,7 @@ class CallSiteMaker(Analysis):
257
319
  prototype=prototype,
258
320
  args=args,
259
321
  ret_expr=ret_expr,
322
+ fp_ret_expr=fp_ret_expr,
260
323
  arg_vvars=arg_vvars,
261
324
  **call_stmt.tags,
262
325
  )
@@ -291,7 +354,7 @@ class CallSiteMaker(Analysis):
291
354
  l.warning("TODO: Unsupported statement type %s for definitions.", type(stmt))
292
355
  return None
293
356
 
294
- def _resolve_register_argument(self, arg_loc) -> tuple[int | None, Expr.VirtualVariable] | None:
357
+ def _resolve_register_argument(self, arg_loc) -> tuple[Expr.Expression | None, Expr.VirtualVariable] | None:
295
358
  offset = arg_loc.check_offset(self.project.arch)
296
359
 
297
360
  if self._reaching_definitions is not None:
@@ -310,6 +373,8 @@ class CallSiteMaker(Analysis):
310
373
  return None
311
374
 
312
375
  def _resolve_stack_argument(self, call_stmt, arg_loc) -> tuple[Any, Any]: # pylint:disable=unused-argument
376
+ assert self._stack_pointer_tracker is not None
377
+
313
378
  size = arg_loc.size
314
379
  offset = arg_loc.stack_offset
315
380
  if self.project.arch.call_pushes_ret:
@@ -331,6 +396,7 @@ class CallSiteMaker(Analysis):
331
396
  sp_offset, size, self.block.addr, self.block.idx, len(self.block.statements) - 1, OP_BEFORE
332
397
  )
333
398
  if vvar is not None:
399
+ # FIXME: vvar may be larger than that we ask; we may need to chop the correct value of vvar
334
400
  value = view.get_vvar_value(vvar)
335
401
  if value is not None and not isinstance(value, Expr.Phi):
336
402
  return None, value
@@ -391,8 +457,8 @@ class CallSiteMaker(Analysis):
391
457
 
392
458
  return s
393
459
 
394
- def _determine_variadic_arguments(self, func: Function | None, cc: SimCC, call_stmt) -> int | None:
395
- if (func is not None and "printf" in func.name) or "scanf" in func.name:
460
+ def _determine_variadic_arguments(self, func: Function, cc: SimCC, call_stmt) -> int | None:
461
+ if "printf" in func.name or "scanf" in func.name:
396
462
  return self._determine_variadic_arguments_for_format_strings(func, cc, call_stmt)
397
463
  return None
398
464