angr 9.2.136__py3-none-win_amd64.whl → 9.2.137__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of angr might be problematic. Click here for more details.

Files changed (60) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +2 -1
  3. angr/analyses/calling_convention/fact_collector.py +10 -2
  4. angr/analyses/cfg/cfg_base.py +3 -33
  5. angr/analyses/cfg/cfg_emulated.py +0 -103
  6. angr/analyses/cfg/cfg_fast.py +28 -12
  7. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +15 -0
  8. angr/analyses/class_identifier.py +1 -2
  9. angr/analyses/complete_calling_conventions.py +3 -0
  10. angr/analyses/decompiler/ail_simplifier.py +12 -1
  11. angr/analyses/decompiler/block_simplifier.py +2 -2
  12. angr/analyses/decompiler/ccall_rewriters/__init__.py +2 -0
  13. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +1 -1
  14. angr/analyses/decompiler/ccall_rewriters/x86_ccalls.py +69 -0
  15. angr/analyses/decompiler/clinic.py +77 -65
  16. angr/analyses/decompiler/condition_processor.py +2 -0
  17. angr/analyses/decompiler/decompiler.py +1 -0
  18. angr/analyses/decompiler/dephication/dephication_base.py +2 -0
  19. angr/analyses/decompiler/dephication/rewriting_engine.py +8 -6
  20. angr/analyses/decompiler/dephication/seqnode_dephication.py +10 -1
  21. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +1 -2
  22. angr/analyses/decompiler/sequence_walker.py +6 -2
  23. angr/analyses/decompiler/ssailification/rewriting.py +11 -1
  24. angr/analyses/decompiler/ssailification/rewriting_engine.py +56 -19
  25. angr/analyses/decompiler/ssailification/ssailification.py +13 -3
  26. angr/analyses/decompiler/ssailification/traversal.py +28 -2
  27. angr/analyses/decompiler/ssailification/traversal_state.py +6 -1
  28. angr/analyses/decompiler/structured_codegen/c.py +44 -21
  29. angr/analyses/decompiler/structuring/phoenix.py +117 -14
  30. angr/analyses/decompiler/utils.py +113 -8
  31. angr/analyses/reaching_definitions/function_handler.py +1 -1
  32. angr/analyses/s_liveness.py +5 -1
  33. angr/analyses/s_propagator.py +25 -4
  34. angr/analyses/s_reaching_definitions/s_rda_model.py +2 -1
  35. angr/analyses/s_reaching_definitions/s_rda_view.py +20 -1
  36. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +11 -1
  37. angr/analyses/stack_pointer_tracker.py +26 -16
  38. angr/analyses/variable_recovery/engine_ail.py +19 -7
  39. angr/analyses/variable_recovery/engine_base.py +16 -14
  40. angr/analyses/variable_recovery/engine_vex.py +2 -2
  41. angr/analyses/variable_recovery/variable_recovery_fast.py +22 -1
  42. angr/block.py +59 -20
  43. angr/engines/pcode/emulate.py +1 -1
  44. angr/engines/pcode/lifter.py +31 -18
  45. angr/engines/soot/expressions/__init__.py +2 -4
  46. angr/engines/soot/statements/__init__.py +1 -2
  47. angr/engines/soot/values/__init__.py +1 -2
  48. angr/engines/successors.py +11 -6
  49. angr/engines/vex/lifter.py +9 -6
  50. angr/flirt/build_sig.py +8 -15
  51. angr/knowledge_plugins/functions/function.py +0 -6
  52. angr/knowledge_plugins/functions/soot_function.py +5 -8
  53. angr/knowledge_plugins/variables/variable_manager.py +16 -10
  54. angr/lib/angr_native.dll +0 -0
  55. {angr-9.2.136.dist-info → angr-9.2.137.dist-info}/METADATA +7 -7
  56. {angr-9.2.136.dist-info → angr-9.2.137.dist-info}/RECORD +60 -59
  57. {angr-9.2.136.dist-info → angr-9.2.137.dist-info}/WHEEL +1 -1
  58. {angr-9.2.136.dist-info → angr-9.2.137.dist-info}/LICENSE +0 -0
  59. {angr-9.2.136.dist-info → angr-9.2.137.dist-info}/entry_points.txt +0 -0
  60. {angr-9.2.136.dist-info → angr-9.2.137.dist-info}/top_level.txt +0 -0
@@ -49,7 +49,6 @@ from .optimization_passes import (
49
49
  DUPLICATING_OPTS,
50
50
  CONDENSING_OPTS,
51
51
  )
52
- from .utils import first_nonlabel_statement_id
53
52
 
54
53
  if TYPE_CHECKING:
55
54
  from angr.knowledge_plugins.cfg import CFGModel
@@ -461,12 +460,12 @@ class Clinic(Analysis):
461
460
  # Make function arguments
462
461
  self._update_progress(33.0, text="Making argument list")
463
462
  arg_list = self._make_argument_list()
464
- arg_vvars = {}
465
- ail_graph = self._create_argument_accessing_statements(arg_list, ail_graph, arg_vvars)
463
+ arg_vvars = self._create_function_argument_vvars(arg_list)
464
+ func_args = {arg_vvar for arg_vvar, _ in arg_vvars.values()}
466
465
 
467
466
  # Transform the graph into partial SSA form
468
467
  self._update_progress(35.0, text="Transforming to partial-SSA form")
469
- ail_graph = self._transform_to_ssa_level0(ail_graph)
468
+ ail_graph = self._transform_to_ssa_level0(ail_graph, func_args)
470
469
 
471
470
  # full-function constant-only propagation
472
471
  self._update_progress(36.0, text="Constant propagation")
@@ -521,7 +520,7 @@ class Clinic(Analysis):
521
520
  )
522
521
 
523
522
  # rewrite (qualified) stack variables into SSA form
524
- ail_graph = self._transform_to_ssa_level1(ail_graph)
523
+ ail_graph = self._transform_to_ssa_level1(ail_graph, func_args)
525
524
 
526
525
  # clear _blocks_by_addr_and_size so no one can use it again
527
526
  # TODO: Totally remove this dict
@@ -529,10 +528,12 @@ class Clinic(Analysis):
529
528
 
530
529
  # Rust-specific; only call this on Rust binaries when we can identify language and compiler
531
530
  ail_graph = self._rewrite_rust_probestack_call(ail_graph)
531
+ # Windows-specific
532
+ ail_graph = self._rewrite_windows_stkchk_call(ail_graph)
532
533
 
533
534
  # Make call-sites
534
535
  self._update_progress(50.0, text="Making callsites")
535
- _, stackarg_offsets, removed_vvar_ids = self._make_callsites(ail_graph, stack_pointer_tracker=spt)
536
+ _, stackarg_offsets, removed_vvar_ids = self._make_callsites(ail_graph, func_args, stack_pointer_tracker=spt)
536
537
 
537
538
  # Run simplification passes
538
539
  self._update_progress(53.0, text="Running simplifications 2")
@@ -1306,84 +1307,57 @@ class Clinic(Analysis):
1306
1307
  return ail_graph
1307
1308
 
1308
1309
  @timethis
1309
- def _create_argument_accessing_statements(
1310
- self,
1311
- arg_list: list[SimVariable],
1312
- ail_graph: networkx.DiGraph,
1313
- arg_vvars: dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]],
1314
- ) -> networkx.DiGraph:
1315
- entrypoint = next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr))
1316
- new_stmts = []
1310
+ def _create_function_argument_vvars(self, arg_list) -> dict[int, tuple[ailment.Expr.VirtualVariable, SimVariable]]:
1311
+ arg_vvars = {}
1317
1312
  for arg in arg_list:
1318
- if not isinstance(arg, SimRegisterVariable):
1319
- continue
1320
-
1321
- # get the full register if needed
1322
- basereg_offset, basereg_size = self.project.arch.get_base_register(arg.reg, size=arg.size)
1323
-
1324
- arg_vvar = ailment.Expr.VirtualVariable(
1325
- self._ail_manager.next_atom(),
1326
- self.vvar_id_start,
1327
- arg.bits,
1328
- ailment.Expr.VirtualVariableCategory.PARAMETER,
1329
- oident=arg.reg,
1330
- ins_addr=self.function.addr,
1331
- vex_block_addr=self.function.addr,
1332
- )
1333
- self.vvar_id_start += 1
1334
- arg_vvars[arg_vvar.varid] = arg_vvar, arg
1335
-
1336
- if basereg_size != arg.size:
1337
- # extend the value to the full register
1338
- arg_vvar = ailment.Expr.Convert(
1313
+ if isinstance(arg, SimRegisterVariable):
1314
+ # get the full register if needed
1315
+ arg_vvar = ailment.Expr.VirtualVariable(
1339
1316
  self._ail_manager.next_atom(),
1340
- arg.size * self.project.arch.byte_width,
1341
- basereg_size * self.project.arch.byte_width,
1342
- False,
1343
- arg_vvar,
1317
+ self.vvar_id_start,
1318
+ arg.bits,
1319
+ ailment.Expr.VirtualVariableCategory.PARAMETER,
1320
+ oident=(ailment.Expr.VirtualVariableCategory.REGISTER, arg.reg),
1344
1321
  ins_addr=self.function.addr,
1345
1322
  vex_block_addr=self.function.addr,
1346
1323
  )
1324
+ self.vvar_id_start += 1
1325
+ arg_vvars[arg_vvar.varid] = arg_vvar, arg
1326
+ elif isinstance(arg, SimStackVariable):
1327
+ arg_vvar = ailment.Expr.VirtualVariable(
1328
+ self._ail_manager.next_atom(),
1329
+ self.vvar_id_start,
1330
+ arg.bits,
1331
+ ailment.Expr.VirtualVariableCategory.PARAMETER,
1332
+ oident=(ailment.Expr.VirtualVariableCategory.STACK, arg.offset),
1333
+ ins_addr=self.function.addr,
1334
+ vex_block_addr=self.function.addr,
1335
+ )
1336
+ self.vvar_id_start += 1
1337
+ arg_vvars[arg_vvar.varid] = arg_vvar, arg
1347
1338
 
1348
- fullreg_dst = ailment.Expr.Register(
1349
- self._ail_manager.next_atom(),
1350
- None,
1351
- basereg_offset,
1352
- basereg_size * self.project.arch.byte_width,
1353
- ins_addr=self.function.addr,
1354
- vex_block_addr=self.function.addr,
1355
- )
1356
- stmt = ailment.Stmt.Assignment(
1357
- self._ail_manager.next_atom(),
1358
- fullreg_dst,
1359
- arg_vvar,
1360
- ins_addr=self.function.addr,
1361
- vex_block_addr=self.function.addr,
1362
- )
1363
- new_stmts.append(stmt)
1364
-
1365
- non_label_stmt_idx = first_nonlabel_statement_id(entrypoint)
1366
- # update the ail block in-place
1367
- entrypoint.statements = (
1368
- entrypoint.statements[:non_label_stmt_idx] + new_stmts + entrypoint.statements[non_label_stmt_idx:]
1369
- )
1370
- return ail_graph
1339
+ return arg_vvars
1371
1340
 
1372
1341
  @timethis
1373
- def _transform_to_ssa_level0(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
1342
+ def _transform_to_ssa_level0(
1343
+ self, ail_graph: networkx.DiGraph, func_args: set[ailment.Expr.VirtualVariable]
1344
+ ) -> networkx.DiGraph:
1374
1345
  ssailification = self.project.analyses[Ssailification].prep(fail_fast=self._fail_fast)(
1375
1346
  self.function,
1376
1347
  ail_graph,
1377
1348
  entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
1378
1349
  ail_manager=self._ail_manager,
1379
1350
  ssa_stackvars=False,
1351
+ func_args=func_args,
1380
1352
  vvar_id_start=self.vvar_id_start,
1381
1353
  )
1382
1354
  self.vvar_id_start = ssailification.max_vvar_id + 1
1383
1355
  return ssailification.out_graph
1384
1356
 
1385
1357
  @timethis
1386
- def _transform_to_ssa_level1(self, ail_graph: networkx.DiGraph) -> networkx.DiGraph:
1358
+ def _transform_to_ssa_level1(
1359
+ self, ail_graph: networkx.DiGraph, func_args: set[ailment.Expr.VirtualVariable]
1360
+ ) -> networkx.DiGraph:
1387
1361
  ssailification = self.project.analyses.Ssailification(
1388
1362
  self.function,
1389
1363
  ail_graph,
@@ -1392,6 +1366,7 @@ class Clinic(Analysis):
1392
1366
  ail_manager=self._ail_manager,
1393
1367
  ssa_tmps=True,
1394
1368
  ssa_stackvars=True,
1369
+ func_args=func_args,
1395
1370
  vvar_id_start=self.vvar_id_start,
1396
1371
  )
1397
1372
  self.vvar_id_start = ssailification.max_vvar_id + 1
@@ -1449,7 +1424,7 @@ class Clinic(Analysis):
1449
1424
  return []
1450
1425
 
1451
1426
  @timethis
1452
- def _make_callsites(self, ail_graph, stack_pointer_tracker=None):
1427
+ def _make_callsites(self, ail_graph, func_args: set[ailment.Expr.VirtualVariable], stack_pointer_tracker=None):
1453
1428
  """
1454
1429
  Simplify all function call statements.
1455
1430
  """
@@ -1458,6 +1433,7 @@ class Clinic(Analysis):
1458
1433
  rd = self.project.analyses.SReachingDefinitions(
1459
1434
  subject=self.function,
1460
1435
  func_graph=ail_graph,
1436
+ func_args=func_args,
1461
1437
  fail_fast=self._fail_fast,
1462
1438
  # use_callee_saved_regs_at_return=not self._register_save_areas_removed, FIXME
1463
1439
  )
@@ -1714,6 +1690,9 @@ class Clinic(Analysis):
1714
1690
  elif stmt_type is ailment.Stmt.ConditionalJump:
1715
1691
  self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.condition)
1716
1692
 
1693
+ elif stmt_type is ailment.Stmt.Jump and not isinstance(stmt.target, ailment.Expr.Const):
1694
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.target)
1695
+
1717
1696
  elif stmt_type is ailment.Stmt.Call:
1718
1697
  self._link_variables_on_call(variable_manager, global_variables, block, stmt_idx, stmt, is_expr=False)
1719
1698
 
@@ -2767,6 +2746,39 @@ class Clinic(Analysis):
2767
2746
  break
2768
2747
  return ail_graph
2769
2748
 
2749
+ def _rewrite_windows_stkchk_call(self, ail_graph) -> networkx.DiGraph:
2750
+ if not (self.project.simos is not None and self.project.simos.name == "Win32"):
2751
+ return ail_graph
2752
+
2753
+ for node in ail_graph:
2754
+ if not node.statements or ail_graph.out_degree[node] != 1:
2755
+ continue
2756
+ last_stmt = node.statements[-1]
2757
+ if isinstance(last_stmt, ailment.Stmt.Call) and isinstance(last_stmt.target, ailment.Expr.Const):
2758
+ func = (
2759
+ self.project.kb.functions.get_by_addr(last_stmt.target.value)
2760
+ if self.project.kb.functions.contains_addr(last_stmt.target.value)
2761
+ else None
2762
+ )
2763
+ if func is not None and func.name == "__chkstk":
2764
+ # get rid of this call
2765
+ node.statements = node.statements[:-1]
2766
+ if self.project.arch.call_pushes_ret and node.statements:
2767
+ last_stmt = node.statements[-1]
2768
+ succ = next(iter(ail_graph.successors(node)))
2769
+ if (
2770
+ isinstance(last_stmt, ailment.Stmt.Store)
2771
+ and isinstance(last_stmt.addr, ailment.Expr.StackBaseOffset)
2772
+ and isinstance(last_stmt.addr.offset, int)
2773
+ and last_stmt.addr.offset < 0
2774
+ and isinstance(last_stmt.data, ailment.Expr.Const)
2775
+ and last_stmt.data.value == succ.addr
2776
+ ):
2777
+ # remove the statement that pushes the return address
2778
+ node.statements = node.statements[:-1]
2779
+ break
2780
+ return ail_graph
2781
+
2770
2782
  def _rewrite_alloca(self, ail_graph):
2771
2783
  # pylint:disable=too-many-boolean-expressions
2772
2784
  alloca_node = None
@@ -721,6 +721,8 @@ class ConditionProcessor:
721
721
  :return: None
722
722
  """
723
723
 
724
+ if not isinstance(cond, claripy.ast.Base):
725
+ return cond
724
726
  if memo is None:
725
727
  memo = {}
726
728
  if cond._hash in memo:
@@ -368,6 +368,7 @@ class Decompiler(Analysis):
368
368
  self.cache.clinic = self.clinic
369
369
 
370
370
  self.kb.decompilations[(self.func.addr, self._flavor)] = self.cache
371
+ self._finish_progress()
371
372
 
372
373
  def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
373
374
  return self.project.analyses[RegionIdentifier].prep(kb=self.kb, fail_fast=self._fail_fast)(
@@ -77,6 +77,8 @@ class DephicationBase(Analysis):
77
77
  mapped_phivarid = phivarid_to_phivarid.get(phi_varid, phi_varid)
78
78
  for varid in varids:
79
79
  vvar_to_vvar[varid] = mapped_phivarid
80
+ if phi_varid != mapped_phivarid:
81
+ vvar_to_vvar[phi_varid] = mapped_phivarid
80
82
 
81
83
  return vvar_to_vvar
82
84
 
@@ -88,12 +88,14 @@ class SimEngineDephiRewriting(SimEngineNostmtAIL[None, Expression | None, Statem
88
88
  )
89
89
 
90
90
  if new_dst is not None or new_src is not None:
91
- return Assignment(
92
- stmt.idx,
93
- stmt.dst if new_dst is None else new_dst,
94
- stmt.src if new_src is None else new_src,
95
- **stmt.tags,
96
- )
91
+ # ensure we do not generate vvar_A = vvar_A
92
+ dst = stmt.dst if new_dst is None else new_dst
93
+ src = stmt.src if new_src is None else new_src
94
+ if isinstance(dst, VirtualVariable) and isinstance(src, VirtualVariable) and dst.varid == src.varid:
95
+ # skip it
96
+ return ()
97
+
98
+ return Assignment(stmt.idx, dst, src, **stmt.tags)
97
99
  return None
98
100
 
99
101
  def _handle_stmt_Store(self, stmt):
@@ -5,7 +5,7 @@ from typing import Any
5
5
 
6
6
  from ailment.block import Block
7
7
  from ailment.statement import Assignment
8
- from ailment.expression import VirtualVariable, Phi
8
+ from ailment.expression import VirtualVariable, Phi, BinaryOp, UnaryOp
9
9
 
10
10
  import angr
11
11
  from angr.utils.ail import is_phi_assignment
@@ -60,6 +60,9 @@ class SeqNodeRewriter(SequenceWalker):
60
60
  Block: self._handle_Block,
61
61
  # statement handlers
62
62
  Assignment: self._handle_Assignment,
63
+ # expression handlers
64
+ BinaryOp: self._handle_BinaryOp,
65
+ UnaryOp: self._handle_UnaryOp,
63
66
  }
64
67
  )
65
68
 
@@ -74,6 +77,12 @@ class SeqNodeRewriter(SequenceWalker):
74
77
  def _handle_Assignment(self, stmt: Assignment, **kwargs) -> Assignment: # pylint:disable=unused-argument
75
78
  return self.engine._handle_stmt_Assignment(stmt)
76
79
 
80
+ def _handle_BinaryOp(self, expr, **kwargs): # pylint:disable=unused-argument
81
+ return self.engine._handle_expr_BinaryOp(expr)
82
+
83
+ def _handle_UnaryOp(self, expr, **kwargs): # pylint:disable=unused-argument
84
+ return self.engine._handle_expr_UnaryOp(expr)
85
+
77
86
  def _handle_Block(self, block: Block, **kwargs) -> Block | None: # pylint:disable=unused-argument
78
87
  self.engine.out_block = None
79
88
  self.engine.process(None, block=block)
@@ -571,8 +571,7 @@ class ReturnDuplicatorBase:
571
571
  new_name = stmt.name if stmt.name else f"Label_{stmt.ins_addr:x}"
572
572
  if stmt.block_idx is not None:
573
573
  suffix = f"__{stmt.block_idx}"
574
- if new_name.endswith(suffix):
575
- new_name = new_name[: -len(suffix)]
574
+ new_name = new_name.removesuffix(suffix)
576
575
  else:
577
576
  new_name = stmt.name
578
577
  new_name += f"__{block.idx}"
@@ -204,13 +204,17 @@ class SequenceWalker:
204
204
 
205
205
  new_false_node = self._handle(node.false_node, parent=node, index=1) if node.false_node is not None else None
206
206
 
207
- if new_true_node is None and new_false_node is None:
207
+ new_condition = (
208
+ self._handle(node.condition, parent=node, label="condition") if node.condition is not None else None
209
+ )
210
+
211
+ if new_true_node is None and new_false_node is None and new_condition is None:
208
212
  return None
209
213
 
210
214
  return ConditionNode(
211
215
  node.addr,
212
216
  node.reaching_condition,
213
- node.condition,
217
+ node.condition if new_condition is None else new_condition,
214
218
  node.true_node if new_true_node is None else new_true_node,
215
219
  false_node=node.false_node if new_false_node is None else new_false_node,
216
220
  )
@@ -40,6 +40,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
40
40
  stackvar_locs: dict[int, int],
41
41
  rewrite_tmps: bool,
42
42
  ail_manager,
43
+ func_args: set[VirtualVariable],
43
44
  vvar_id_start: int = 0,
44
45
  ):
45
46
  self.project = project
@@ -55,6 +56,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
55
56
  self._stackvar_locs = stackvar_locs
56
57
  self._rewrite_tmps = rewrite_tmps
57
58
  self._ail_manager = ail_manager
59
+ self._func_args = func_args
58
60
  self._engine_ail = SimEngineSSARewriting(
59
61
  self.project,
60
62
  sp_tracker=sp_tracker,
@@ -205,12 +207,20 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
205
207
  self.insert_phi_statements(node, phi_stmts)
206
208
 
207
209
  def _initial_abstract_state(self, node) -> RewritingState:
208
- return RewritingState(
210
+ state = RewritingState(
209
211
  CodeLocation(node.addr, stmt_idx=0, ins_addr=node.addr, block_idx=node.idx),
210
212
  self.project.arch,
211
213
  self._function,
212
214
  node,
213
215
  )
216
+ # update state with function arguments
217
+ for func_arg in self._func_args:
218
+ if func_arg.oident[0] == VirtualVariableCategory.REGISTER:
219
+ reg_offset, reg_size = func_arg.oident[1], func_arg.size
220
+ state.registers[reg_offset][reg_size] = func_arg
221
+ elif func_arg.oident[0] == VirtualVariableCategory.STACK:
222
+ state.stackvars[func_arg.oident[1]][func_arg.size] = func_arg
223
+ return state
214
224
 
215
225
  def _run_on_node(self, node, state: RewritingState):
216
226
  """
@@ -462,7 +462,7 @@ class SimEngineSSARewriting(
462
462
  base_mask = Const(self.ail_manager.next_atom(), None, base_mask, existing_vvar.bits)
463
463
  new_base_expr = BinaryOp(
464
464
  self.ail_manager.next_atom(),
465
- "Or",
465
+ "And",
466
466
  [existing_vvar, base_mask],
467
467
  False,
468
468
  bits=existing_vvar.bits,
@@ -618,6 +618,15 @@ class SimEngineSSARewriting(
618
618
  ):
619
619
  vvar = self.state.registers[reg_expr.reg_offset][reg_expr.size]
620
620
  assert vvar is not None
621
+ if vvar.category == VirtualVariableCategory.PARAMETER:
622
+ return VirtualVariable(
623
+ reg_expr.idx,
624
+ vvar.varid,
625
+ vvar.bits,
626
+ VirtualVariableCategory.PARAMETER,
627
+ oident=vvar.oident,
628
+ **vvar.tags,
629
+ )
621
630
  return VirtualVariable(
622
631
  reg_expr.idx,
623
632
  vvar.varid,
@@ -640,6 +649,20 @@ class SimEngineSSARewriting(
640
649
  vvar,
641
650
  **reg_expr.tags,
642
651
  )
652
+ elif reg_expr.size > existing_size:
653
+ # part of the variable exists... maybe it's a parameter?
654
+ vvar = self.state.registers[reg_expr.reg_offset][existing_size]
655
+ if vvar.category == VirtualVariableCategory.PARAMETER:
656
+ # just zero-extend it
657
+ return Convert(
658
+ self.ail_manager.next_atom(),
659
+ existing_size * self.project.arch.byte_width,
660
+ reg_expr.size * self.project.arch.byte_width,
661
+ False,
662
+ vvar,
663
+ **vvar.tags,
664
+ )
665
+ break
643
666
  else:
644
667
  break
645
668
 
@@ -651,24 +674,29 @@ class SimEngineSSARewriting(
651
674
  ins_addr=reg_expr.ins_addr,
652
675
  )
653
676
  # extract
654
- shift_amount = Const(
655
- self.ail_manager.next_atom(),
656
- None,
657
- (reg_expr.reg_offset - vvar.oident) * self.arch.byte_width,
658
- 8,
659
- **reg_expr.tags,
660
- )
661
- shifted = BinaryOp(
662
- self.ail_manager.next_atom(),
663
- "Shr",
664
- [
665
- vvar,
666
- shift_amount,
667
- ],
668
- False,
669
- bits=vvar.bits,
670
- **reg_expr.tags,
671
- )
677
+ if reg_expr.reg_offset == vvar.oident:
678
+ shifted = vvar
679
+ else:
680
+ shift_amount = Const(
681
+ self.ail_manager.next_atom(),
682
+ None,
683
+ (reg_expr.reg_offset - vvar.oident) * self.arch.byte_width,
684
+ 8,
685
+ **reg_expr.tags,
686
+ )
687
+ shifted = BinaryOp(
688
+ self.ail_manager.next_atom(),
689
+ "Shr",
690
+ [
691
+ vvar,
692
+ shift_amount,
693
+ ],
694
+ False,
695
+ bits=vvar.bits,
696
+ **reg_expr.tags,
697
+ )
698
+ if shifted.bits == reg_expr.bits:
699
+ return shifted
672
700
  return Convert(
673
701
  self.ail_manager.next_atom(),
674
702
  shifted.bits,
@@ -700,6 +728,15 @@ class SimEngineSSARewriting(
700
728
  # TODO: Support truncation
701
729
  # TODO: Maybe also support concatenation
702
730
  vvar = self.state.stackvars[expr.addr.offset][expr.size]
731
+ if vvar.category == VirtualVariableCategory.PARAMETER:
732
+ return VirtualVariable(
733
+ expr.idx,
734
+ vvar.varid,
735
+ vvar.bits,
736
+ VirtualVariableCategory.PARAMETER,
737
+ oident=vvar.oident,
738
+ **vvar.tags,
739
+ )
703
740
  return VirtualVariable(
704
741
  expr.idx,
705
742
  vvar.varid,
@@ -5,7 +5,7 @@ from collections import defaultdict
5
5
  from itertools import count
6
6
  from bisect import bisect_left
7
7
 
8
- from ailment.expression import Expression, Register, StackBaseOffset, Tmp
8
+ from ailment.expression import Expression, Register, StackBaseOffset, Tmp, VirtualVariable, VirtualVariableCategory
9
9
  from ailment.statement import Statement, Store
10
10
 
11
11
  from angr.knowledge_plugins.functions import Function
@@ -34,6 +34,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
34
34
  ail_manager=None,
35
35
  ssa_stackvars: bool = False,
36
36
  ssa_tmps: bool = False,
37
+ func_args: set[VirtualVariable] | None = None,
37
38
  vvar_id_start: int = 0,
38
39
  ):
39
40
  """
@@ -53,6 +54,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
53
54
  self._ail_manager = ail_manager
54
55
  self._ssa_stackvars = ssa_stackvars
55
56
  self._ssa_tmps = ssa_tmps
57
+ self._func_args = func_args if func_args is not None else set()
56
58
  self._entry = (
57
59
  entry
58
60
  if entry is not None
@@ -71,6 +73,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
71
73
  bp_as_gpr,
72
74
  ssa_stackvars,
73
75
  ssa_tmps,
76
+ self._func_args,
74
77
  )
75
78
 
76
79
  # calculate virtual variables and phi nodes
@@ -91,6 +94,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
91
94
  self._stackvar_locs,
92
95
  self._ssa_tmps,
93
96
  self._ail_manager,
97
+ self._func_args,
94
98
  vvar_id_start=vvar_id_start,
95
99
  )
96
100
  self.out_graph = rewriter.out_graph
@@ -122,6 +126,11 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
122
126
  # for stack variables, we collect all definitions and identify stack variable locations using heuristics
123
127
 
124
128
  stackvar_locs = self._synthesize_stackvar_locs([def_ for def_, _ in def_to_loc if isinstance(def_, Store)])
129
+ # handle function arguments
130
+ if self._func_args:
131
+ for func_arg in self._func_args:
132
+ if func_arg.oident[0] == VirtualVariableCategory.STACK:
133
+ stackvar_locs[func_arg.oident[1]] = func_arg.size
125
134
  sorted_stackvar_offs = sorted(stackvar_locs)
126
135
  else:
127
136
  stackvar_locs = {}
@@ -137,9 +146,10 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
137
146
  udef_to_defs[("reg", base_off, base_reg_bits)].add(def_)
138
147
  udef_to_blockkeys[("reg", base_off, base_reg_bits)].add((loc.block_addr, loc.block_idx))
139
148
  # add a definition for the partial register
140
- if base_off != def_.reg_offset:
149
+ if base_off != def_.reg_offset or base_size != def_.size:
141
150
  reg_bits = def_.size * self.project.arch.byte_width
142
- udef_to_defs[("reg", def_.reg_offset, reg_bits)].add((loc.block_addr, loc.block_idx))
151
+ udef_to_defs[("reg", def_.reg_offset, reg_bits)].add(def_)
152
+ udef_to_blockkeys[("reg", def_.reg_offset, reg_bits)].add((loc.block_addr, loc.block_idx))
143
153
  elif isinstance(def_, Store):
144
154
  if isinstance(def_.addr, StackBaseOffset) and isinstance(def_.addr.offset, int):
145
155
  idx_begin = bisect_left(sorted_stackvar_offs, def_.addr.offset)
@@ -17,13 +17,24 @@ class TraversalAnalysis(ForwardAnalysis[TraversalState, ailment.Block, object, t
17
17
  TraversalAnalysis traverses the AIL graph and collects definitions.
18
18
  """
19
19
 
20
- def __init__(self, project, func, ail_graph, sp_tracker, bp_as_gpr: bool, stackvars: bool, tmps: bool):
20
+ def __init__(
21
+ self,
22
+ project,
23
+ func,
24
+ ail_graph,
25
+ sp_tracker,
26
+ bp_as_gpr: bool,
27
+ stackvars: bool,
28
+ tmps: bool,
29
+ func_args: set[ailment.Expr.VirtualVariable],
30
+ ):
21
31
 
22
32
  self.project = project
23
33
  self._stackvars = stackvars
24
34
  self._tmps = tmps
25
35
  self._function = func
26
36
  self._graph_visitor = FunctionGraphVisitor(self._function, ail_graph)
37
+ self._func_args = func_args
27
38
 
28
39
  ForwardAnalysis.__init__(
29
40
  self, order_jobs=True, allow_merging=True, allow_widening=False, graph_visitor=self._graph_visitor
@@ -52,13 +63,28 @@ class TraversalAnalysis(ForwardAnalysis[TraversalState, ailment.Block, object, t
52
63
  pass
53
64
 
54
65
  def _initial_abstract_state(self, node: ailment.Block) -> TraversalState:
55
- return TraversalState(self.project.arch, self._function)
66
+ state = TraversalState(self.project.arch, self._function)
67
+ # update it with function arguments
68
+ if self._func_args:
69
+ for func_arg in self._func_args:
70
+ if func_arg.oident[0] == ailment.Expr.VirtualVariableCategory.REGISTER:
71
+ reg_offset = func_arg.oident[1]
72
+ reg_size = func_arg.size
73
+ state.live_registers.add(reg_offset)
74
+ # get the full register if needed
75
+ basereg_offset, basereg_size = self.project.arch.get_base_register(reg_offset, size=reg_size)
76
+ if basereg_size != reg_size or basereg_offset != reg_offset:
77
+ state.live_registers.add(basereg_offset)
78
+ elif func_arg.oident[0] == ailment.Expr.VirtualVariableCategory.STACK:
79
+ state.live_stackvars.add((func_arg.oident[1], func_arg.size))
80
+ return state
56
81
 
57
82
  def _merge_states(self, node: ailment.Block, *states: TraversalState) -> tuple[TraversalState, bool]:
58
83
  merged_state = TraversalState(
59
84
  self.project.arch,
60
85
  self._function,
61
86
  live_registers=states[0].live_registers.copy(),
87
+ live_stackvars=states[0].live_stackvars.copy(),
62
88
  )
63
89
  merge_occurred = merged_state.merge(*states[1:])
64
90
  return merged_state, not merge_occurred
@@ -37,7 +37,12 @@ class TraversalState:
37
37
  merge_occurred = True
38
38
  all_regs |= o.live_registers
39
39
 
40
- # TODO: merge of live_stackvars
40
+ all_stackvars: set[tuple[int, int]] = self.live_stackvars.copy()
41
+ for o in others:
42
+ if o.live_stackvars.difference(all_stackvars):
43
+ merge_occurred = True
44
+ all_stackvars |= o.live_stackvars
41
45
 
42
46
  self.live_registers = all_regs
47
+ self.live_stackvars = all_stackvars
43
48
  return merge_occurred