angr 9.2.181__cp310-abi3-macosx_10_12_x86_64.whl → 9.2.182__cp310-abi3-macosx_10_12_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 (139) hide show
  1. angr/__init__.py +1 -1
  2. angr/ailment/expression.py +2 -2
  3. angr/analyses/decompiler/ail_simplifier.py +77 -5
  4. angr/analyses/decompiler/callsite_maker.py +6 -1
  5. angr/analyses/decompiler/clinic.py +22 -10
  6. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +4 -10
  7. angr/analyses/decompiler/ssailification/rewriting.py +82 -10
  8. angr/analyses/decompiler/ssailification/rewriting_engine.py +22 -11
  9. angr/analyses/decompiler/ssailification/ssailification.py +7 -5
  10. angr/analyses/s_reaching_definitions/s_rda_view.py +38 -16
  11. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +59 -6
  12. angr/engines/pcode/behavior.py +6 -2
  13. angr/knowledge_plugins/functions/function_manager.py +1 -1
  14. angr/knowledge_plugins/variables/variable_manager.py +10 -19
  15. angr/procedures/definitions/parse_glibc.py +0 -1
  16. angr/procedures/definitions/parse_win32json.py +12 -3
  17. angr/procedures/definitions/wdk/fltmgr.json +11 -11
  18. angr/procedures/definitions/wdk/fwpuclnt.json +32 -32
  19. angr/procedures/definitions/wdk/gdi32.json +1 -1
  20. angr/procedures/definitions/wdk/ntoskrnl.json +61 -61
  21. angr/procedures/definitions/win32/_types_win32.json +1103 -1102
  22. angr/procedures/definitions/win32/activeds.json +2 -2
  23. angr/procedures/definitions/win32/advapi32.json +47 -47
  24. angr/procedures/definitions/win32/apphelp.json +1 -1
  25. angr/procedures/definitions/win32/avifil32.json +10 -10
  26. angr/procedures/definitions/win32/avrt.json +4 -4
  27. angr/procedures/definitions/win32/bluetoothapis.json +3 -3
  28. angr/procedures/definitions/win32/certpoleng.json +1 -1
  29. angr/procedures/definitions/win32/cfgmgr32.json +43 -43
  30. angr/procedures/definitions/win32/clusapi.json +1 -1
  31. angr/procedures/definitions/win32/comctl32.json +3 -3
  32. angr/procedures/definitions/win32/computenetwork.json +14 -14
  33. angr/procedures/definitions/win32/comsvcs.json +3 -3
  34. angr/procedures/definitions/win32/crypt32.json +5 -5
  35. angr/procedures/definitions/win32/d2d1.json +1 -1
  36. angr/procedures/definitions/win32/d3d12.json +6 -6
  37. angr/procedures/definitions/win32/d3dcompiler_47.json +2 -2
  38. angr/procedures/definitions/win32/dbgeng.json +4 -4
  39. angr/procedures/definitions/win32/dbghelp.json +2 -2
  40. angr/procedures/definitions/win32/dcomp.json +4 -4
  41. angr/procedures/definitions/win32/ddraw.json +6 -6
  42. angr/procedures/definitions/win32/diagnosticdataquery.json +1 -1
  43. angr/procedures/definitions/win32/dinput8.json +1 -1
  44. angr/procedures/definitions/win32/directml.json +2 -2
  45. angr/procedures/definitions/win32/dsound.json +10 -10
  46. angr/procedures/definitions/win32/dsparse.json +2 -2
  47. angr/procedures/definitions/win32/dwmapi.json +1 -1
  48. angr/procedures/definitions/win32/dwrite.json +1 -1
  49. angr/procedures/definitions/win32/dxcompiler.json +2 -2
  50. angr/procedures/definitions/win32/dxcore.json +1 -1
  51. angr/procedures/definitions/win32/dxgi.json +4 -4
  52. angr/procedures/definitions/win32/dxva2.json +1 -1
  53. angr/procedures/definitions/win32/eappprxy.json +3 -3
  54. angr/procedures/definitions/win32/evr.json +4 -4
  55. angr/procedures/definitions/win32/fwpuclnt.json +32 -32
  56. angr/procedures/definitions/win32/gdiplus.json +9 -9
  57. angr/procedures/definitions/win32/hid.json +1 -1
  58. angr/procedures/definitions/win32/hlink.json +7 -7
  59. angr/procedures/definitions/win32/ieframe.json +4 -4
  60. angr/procedures/definitions/win32/imgutil.json +1 -1
  61. angr/procedures/definitions/win32/inkobjcore.json +4 -4
  62. angr/procedures/definitions/win32/iphlpapi.json +8 -8
  63. angr/procedures/definitions/win32/kernel32.json +5 -5
  64. angr/procedures/definitions/win32/ksproxy_ax.json +2 -2
  65. angr/procedures/definitions/win32/ktmw32.json +10 -10
  66. angr/procedures/definitions/win32/mapi32.json +2 -2
  67. angr/procedures/definitions/win32/mf.json +5 -5
  68. angr/procedures/definitions/win32/mfplat.json +33 -33
  69. angr/procedures/definitions/win32/mfsensorgroup.json +2 -2
  70. angr/procedures/definitions/win32/mmdevapi.json +1 -1
  71. angr/procedures/definitions/win32/mqrt.json +2 -2
  72. angr/procedures/definitions/win32/mscoree.json +9 -9
  73. angr/procedures/definitions/win32/msdmo.json +5 -5
  74. angr/procedures/definitions/win32/mswsock.json +8 -8
  75. angr/procedures/definitions/win32/ndfapi.json +1 -1
  76. angr/procedures/definitions/win32/netapi32.json +6 -6
  77. angr/procedures/definitions/win32/netsh.json +1 -1
  78. angr/procedures/definitions/win32/ntdll.json +1 -1
  79. angr/procedures/definitions/win32/ntdsapi.json +14 -14
  80. angr/procedures/definitions/win32/ntlanman.json +3 -3
  81. angr/procedures/definitions/win32/ole32.json +78 -78
  82. angr/procedures/definitions/win32/oleacc.json +6 -6
  83. angr/procedures/definitions/win32/oleaut32.json +15 -15
  84. angr/procedures/definitions/win32/oledlg.json +1 -1
  85. angr/procedures/definitions/win32/p2p.json +11 -11
  86. angr/procedures/definitions/win32/p2pgraph.json +7 -7
  87. angr/procedures/definitions/win32/pdh.json +1 -1
  88. angr/procedures/definitions/win32/powrprof.json +47 -47
  89. angr/procedures/definitions/win32/projectedfslib.json +2 -2
  90. angr/procedures/definitions/win32/propsys.json +25 -25
  91. angr/procedures/definitions/win32/query.json +1 -1
  92. angr/procedures/definitions/win32/resutils.json +1 -1
  93. angr/procedures/definitions/win32/rpcns4.json +5 -5
  94. angr/procedures/definitions/win32/rpcrt4.json +33 -33
  95. angr/procedures/definitions/win32/rtm.json +1 -1
  96. angr/procedures/definitions/win32/sensorsutilsv2.json +4 -4
  97. angr/procedures/definitions/win32/setupapi.json +49 -49
  98. angr/procedures/definitions/win32/shell32.json +34 -34
  99. angr/procedures/definitions/win32/shlwapi.json +7 -7
  100. angr/procedures/definitions/win32/slc.json +25 -25
  101. angr/procedures/definitions/win32/slcext.json +2 -2
  102. angr/procedures/definitions/win32/slwga.json +1 -1
  103. angr/procedures/definitions/win32/tapi32.json +4 -4
  104. angr/procedures/definitions/win32/tdh.json +6 -6
  105. angr/procedures/definitions/win32/traffic.json +6 -6
  106. angr/procedures/definitions/win32/txfw32.json +1 -1
  107. angr/procedures/definitions/win32/uiautomationcore.json +1 -1
  108. angr/procedures/definitions/win32/urlmon.json +6 -6
  109. angr/procedures/definitions/win32/user32.json +1 -1
  110. angr/procedures/definitions/win32/userenv.json +4 -4
  111. angr/procedures/definitions/win32/virtdisk.json +4 -4
  112. angr/procedures/definitions/win32/vmdevicehost.json +1 -1
  113. angr/procedures/definitions/win32/wcmapi.json +2 -2
  114. angr/procedures/definitions/win32/webauthn.json +2 -2
  115. angr/procedures/definitions/win32/winbio.json +2 -2
  116. angr/procedures/definitions/win32/windows_ui_xaml.json +2 -2
  117. angr/procedures/definitions/win32/windowscodecs.json +9 -9
  118. angr/procedures/definitions/win32/winhttp.json +1 -1
  119. angr/procedures/definitions/win32/winhvplatform.json +1 -1
  120. angr/procedures/definitions/win32/winscard.json +12 -12
  121. angr/procedures/definitions/win32/winspool_drv.json +4 -4
  122. angr/procedures/definitions/win32/wintrust.json +9 -9
  123. angr/procedures/definitions/win32/wlanapi.json +27 -27
  124. angr/procedures/definitions/win32/wlanui.json +1 -1
  125. angr/procedures/definitions/win32/wldp.json +4 -4
  126. angr/procedures/definitions/win32/ws2_32.json +34 -34
  127. angr/procedures/definitions/win32/xaudio2_8.json +1 -1
  128. angr/procedures/definitions/win32/xmllite.json +2 -2
  129. angr/procedures/definitions/win32/xolehlp.json +4 -4
  130. angr/project.py +4 -1
  131. angr/rustylib.abi3.so +0 -0
  132. angr/unicornlib.dylib +0 -0
  133. angr/utils/ail.py +107 -1
  134. {angr-9.2.181.dist-info → angr-9.2.182.dist-info}/METADATA +5 -5
  135. {angr-9.2.181.dist-info → angr-9.2.182.dist-info}/RECORD +139 -139
  136. {angr-9.2.181.dist-info → angr-9.2.182.dist-info}/WHEEL +0 -0
  137. {angr-9.2.181.dist-info → angr-9.2.182.dist-info}/entry_points.txt +0 -0
  138. {angr-9.2.181.dist-info → angr-9.2.182.dist-info}/licenses/LICENSE +0 -0
  139. {angr-9.2.181.dist-info → angr-9.2.182.dist-info}/top_level.txt +0 -0
angr/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # pylint: disable=wrong-import-position
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "9.2.181"
5
+ __version__ = "9.2.182"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
@@ -345,9 +345,9 @@ class VirtualVariable(Atom):
345
345
  ori_str = ""
346
346
  match self.category:
347
347
  case VirtualVariableCategory.REGISTER:
348
- ori_str = f"{{reg {self.reg_offset}}}"
348
+ ori_str = f"{{r{self.reg_offset}|{self.size}b}}"
349
349
  case VirtualVariableCategory.STACK:
350
- ori_str = f"{{stack {self.oident}}}"
350
+ ori_str = f"{{s{self.oident}|{self.size}b}}"
351
351
  return f"vvar_{self.varid}{ori_str}"
352
352
 
353
353
  __hash__ = TaggedObject.__hash__ # type: ignore
@@ -37,7 +37,7 @@ from angr.ailment.expression import (
37
37
 
38
38
  from angr.analyses.s_propagator import SPropagatorAnalysis
39
39
  from angr.analyses.s_reaching_definitions import SRDAModel
40
- from angr.utils.ail import is_phi_assignment, HasExprWalker
40
+ from angr.utils.ail import is_phi_assignment, HasExprWalker, is_expr_used_as_reg_base_value
41
41
  from angr.utils.ssa import (
42
42
  has_call_in_between_stmts,
43
43
  has_store_stmt_in_between_stmts,
@@ -315,7 +315,7 @@ class AILSimplifier(Analysis):
315
315
  subject=self.func,
316
316
  func_graph=self.func_graph,
317
317
  func_args=func_args,
318
- # use_callee_saved_regs_at_return=self._use_callee_saved_regs_at_return,
318
+ use_callee_saved_regs_at_return=self._use_callee_saved_regs_at_return,
319
319
  # track_tmps=True,
320
320
  ).model
321
321
  self._reaching_definitions = rd
@@ -406,6 +406,10 @@ class AILSimplifier(Analysis):
406
406
 
407
407
  rd = self._compute_reaching_definitions()
408
408
  sorted_defs = sorted(rd.all_definitions, key=lambda d: d.codeloc, reverse=True)
409
+
410
+ # compute effective sizes for each vvar
411
+ effective_sizes = self._compute_effective_sizes(rd, sorted_defs, addr_and_idx_to_block)
412
+
409
413
  narrowing_candidates: dict[int, tuple[Definition, ExprNarrowingInfo]] = {}
410
414
  for def_ in (d_ for d_ in sorted_defs if d_.codeloc.context is None):
411
415
  if isinstance(def_.atom, atoms.VirtualVariable) and (def_.atom.was_reg or def_.atom.was_parameter):
@@ -421,7 +425,7 @@ class AILSimplifier(Analysis):
421
425
  if skip_def:
422
426
  continue
423
427
 
424
- narrow = self._narrowing_needed(def_, rd, addr_and_idx_to_block)
428
+ narrow = self._narrowing_needed(def_, rd, addr_and_idx_to_block, effective_sizes)
425
429
  if narrow.narrowable:
426
430
  # we cannot narrow it immediately because any definition that is used by phi variables must be
427
431
  # narrowed together with all other definitions that can reach the phi variables.
@@ -466,6 +470,47 @@ class AILSimplifier(Analysis):
466
470
 
467
471
  return narrowed
468
472
 
473
+ def _compute_effective_sizes(self, rd, defs, addr_and_idx_to_block) -> dict[int, int]:
474
+
475
+ vvar_effective_sizes: dict[int, int] = {}
476
+
477
+ # determine effective sizes for non-phi vvars
478
+ for def_ in defs:
479
+ # find its def statement
480
+ old_block = addr_and_idx_to_block.get((def_.codeloc.block_addr, def_.codeloc.block_idx), None)
481
+ if old_block is None:
482
+ continue
483
+ block = self.blocks.get(old_block, old_block)
484
+ if def_.codeloc.stmt_idx is None or def_.codeloc.stmt_idx >= len(block.statements):
485
+ continue
486
+ def_stmt = block.statements[def_.codeloc.stmt_idx]
487
+ if (
488
+ isinstance(def_stmt, Assignment)
489
+ and isinstance(def_stmt.src, Convert)
490
+ and not def_stmt.src.is_signed
491
+ and def_stmt.src.from_type == Convert.TYPE_INT
492
+ and def_stmt.src.to_type == Convert.TYPE_INT
493
+ and def_stmt.src.from_bits < def_stmt.src.to_bits
494
+ ):
495
+ effective_size = def_stmt.src.from_bits // self.project.arch.byte_width
496
+ vvar_effective_sizes[def_.atom.varid] = effective_size
497
+
498
+ # update effective sizes for phi vvars
499
+ changed = True
500
+ while changed:
501
+ changed = False
502
+ for phi_vvar in rd.phivarid_to_varids:
503
+ if phi_vvar in vvar_effective_sizes:
504
+ continue
505
+ if rd.phivarid_to_varids[phi_vvar] and all(
506
+ src_vvar in vvar_effective_sizes for src_vvar in rd.phivarid_to_varids[phi_vvar]
507
+ ):
508
+ effective_size = max(vvar_effective_sizes[src_vvar] for src_vvar in rd.phivarid_to_varids[phi_vvar])
509
+ vvar_effective_sizes[phi_vvar] = effective_size
510
+ changed = True
511
+
512
+ return vvar_effective_sizes
513
+
469
514
  @staticmethod
470
515
  def _compute_narrowables_once(
471
516
  rd, narrowing_candidates: dict, vvar_to_narrowing_size: dict[int, int], blacklist_varids: set
@@ -513,7 +558,9 @@ class AILSimplifier(Analysis):
513
558
 
514
559
  return repeat, narrowables
515
560
 
516
- def _narrowing_needed(self, def_, rd: SRDAModel, addr_and_idx_to_block) -> ExprNarrowingInfo:
561
+ def _narrowing_needed(
562
+ self, def_: Definition, rd: SRDAModel, addr_and_idx_to_block, effective_sizes: dict[int, int]
563
+ ) -> ExprNarrowingInfo:
517
564
 
518
565
  def_size = def_.size
519
566
  # find its uses
@@ -524,6 +571,7 @@ class AILSimplifier(Analysis):
524
571
  use_and_exprs, phi_vars = result
525
572
 
526
573
  all_used_sizes = set()
574
+ noncall_used_sizes = set()
527
575
  used_by: list[tuple[atoms.VirtualVariable, CodeLocation, tuple[str, tuple[Expression, ...]]]] = []
528
576
  used_by_loc = defaultdict(list)
529
577
 
@@ -543,10 +591,16 @@ class AILSimplifier(Analysis):
543
591
  # special case: if the statement is a Call statement and expr is None, it means we have not been able to
544
592
  # determine if the expression is really used by the call or not. skip it in this case
545
593
  if isinstance(stmt, Call) and expr is None:
594
+ all_used_sizes.add(atom.size)
546
595
  continue
547
596
  # special case: if the statement is a phi statement, we ignore it
548
597
  if is_phi_assignment(stmt):
549
598
  continue
599
+ # special case: if the statement is an assignment to a destination vvar A, and the source is the bitwise-or
600
+ # of two expressions where one of them is the high bits of expr, and expr is a phi var that relies on
601
+ # vvar A, then we skip it.
602
+ if is_expr_used_as_reg_base_value(stmt, expr, rd):
603
+ continue
550
604
 
551
605
  expr_size, used_by_exprs = self._extract_expression_effective_size(stmt, expr)
552
606
  if expr_size is None:
@@ -554,9 +608,27 @@ class AILSimplifier(Analysis):
554
608
  return ExprNarrowingInfo(False)
555
609
 
556
610
  all_used_sizes.add(expr_size)
611
+ if not isinstance(stmt, Call):
612
+ noncall_used_sizes.add(expr_size)
557
613
  used_by_loc[loc].append((atom, used_by_exprs))
558
614
 
615
+ target_size = None
559
616
  if len(all_used_sizes) == 1 and next(iter(all_used_sizes)) < def_size:
617
+ target_size = next(iter(all_used_sizes))
618
+ else:
619
+ effective_size = effective_sizes.get(def_.atom.varid, None)
620
+ if (
621
+ effective_size is not None
622
+ and any(used_size <= effective_size for used_size in all_used_sizes)
623
+ and all(used_size <= effective_size for used_size in noncall_used_sizes)
624
+ ):
625
+ # special case: sometimes we have an explicit Convert that narrows the value, all other uses are either
626
+ # in the effective size or narrower, but we pass the full register to a function call as an argument
627
+ # because we do not know the real type of the argument, or there are cases like putchar(int ch) while
628
+ # ch is actually a char. We use effective size in such cases to narrow the variable.
629
+ target_size = effective_size
630
+
631
+ if target_size is not None:
560
632
  for loc, atom_expr_pairs in used_by_loc.items():
561
633
  if len(atom_expr_pairs) == 1:
562
634
  atom, used_by_exprs = atom_expr_pairs[0]
@@ -582,7 +654,7 @@ class AILSimplifier(Analysis):
582
654
  for atom, used_by_exprs in ordered:
583
655
  used_by.append((atom, loc, used_by_exprs))
584
656
 
585
- return ExprNarrowingInfo(True, to_size=next(iter(all_used_sizes)), use_exprs=used_by, phi_vars=phi_vars)
657
+ return ExprNarrowingInfo(True, to_size=target_size, use_exprs=used_by, phi_vars=phi_vars)
586
658
 
587
659
  return ExprNarrowingInfo(False)
588
660
 
@@ -364,7 +364,12 @@ class CallSiteMaker(Analysis):
364
364
  # Find its definition
365
365
  view = SRDAView(self._reaching_definitions.model)
366
366
  vvar = view.get_reg_vvar_by_stmt(
367
- offset, self.block.addr, self.block.idx, len(self.block.statements) - 1, OP_BEFORE
367
+ offset,
368
+ arg_loc.size,
369
+ self.block.addr,
370
+ self.block.idx,
371
+ len(self.block.statements) - 1,
372
+ OP_BEFORE,
368
373
  )
369
374
 
370
375
  if vvar is not None:
@@ -1740,7 +1740,7 @@ class Clinic(Analysis):
1740
1740
  func_graph=ail_graph,
1741
1741
  func_args=func_args,
1742
1742
  fail_fast=self._fail_fast,
1743
- # use_callee_saved_regs_at_return=not self._register_save_areas_removed, FIXME
1743
+ use_callee_saved_regs_at_return=not self._register_save_areas_removed,
1744
1744
  )
1745
1745
 
1746
1746
  class TempClass: # pylint:disable=missing-class-docstring
@@ -2008,9 +2008,7 @@ class Clinic(Analysis):
2008
2008
 
2009
2009
  # link struct member info
2010
2010
  if isinstance(stmt.variable, SimStackVariable):
2011
- off = stmt.variable.offset
2012
- if off in variable_manager.stack_offset_to_struct_member_info:
2013
- stmt.tags["struct_member_info"] = variable_manager.stack_offset_to_struct_member_info[off]
2011
+ self._map_stackvar_to_struct_member(variable_manager, stmt, stmt.variable.offset)
2014
2012
 
2015
2013
  elif stmt_type is ailment.Stmt.Assignment or stmt_type is ailment.Stmt.WeakAssignment:
2016
2014
  self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, stmt.dst)
@@ -2094,9 +2092,7 @@ class Clinic(Analysis):
2094
2092
  expr.variable_offset = offset
2095
2093
 
2096
2094
  if isinstance(expr, ailment.Expr.VirtualVariable) and expr.was_stack:
2097
- off = expr.stack_offset
2098
- if off in variable_manager.stack_offset_to_struct_member_info:
2099
- expr.tags["struct_member_info"] = variable_manager.stack_offset_to_struct_member_info[off]
2095
+ self._map_stackvar_to_struct_member(variable_manager, expr, expr.stack_offset)
2100
2096
 
2101
2097
  elif type(expr) is ailment.Expr.Load:
2102
2098
  variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
@@ -2133,9 +2129,7 @@ class Clinic(Analysis):
2133
2129
  expr.variable_offset = offset
2134
2130
 
2135
2131
  if isinstance(var, SimStackVariable):
2136
- off = var.offset
2137
- if off in variable_manager.stack_offset_to_struct_member_info:
2138
- expr.tags["struct_member_info"] = variable_manager.stack_offset_to_struct_member_info[off]
2132
+ self._map_stackvar_to_struct_member(variable_manager, expr, var.offset)
2139
2133
 
2140
2134
  elif type(expr) is ailment.Expr.BinaryOp:
2141
2135
  variables = variable_manager.find_variables_by_atom(block.addr, stmt_idx, expr, block_idx=block.idx)
@@ -2227,6 +2221,24 @@ class Clinic(Analysis):
2227
2221
  if vvar is not None:
2228
2222
  self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, vvar)
2229
2223
 
2224
+ def _map_stackvar_to_struct_member(
2225
+ self,
2226
+ variable_manager,
2227
+ expr_or_stmt: ailment.expression.Expression | ailment.statement.Statement,
2228
+ the_stack_offset: int,
2229
+ ) -> bool:
2230
+ any_struct_found = False
2231
+ off = the_stack_offset
2232
+ for stack_off in variable_manager.stack_offset_to_struct.irange(maximum=off, reverse=True):
2233
+ the_var, vartype = variable_manager.stack_offset_to_struct[stack_off]
2234
+ if stack_off <= off < stack_off + vartype.size // self.project.arch.byte_width:
2235
+ expr_or_stmt.tags["struct_member_info"] = off - stack_off, the_var, vartype
2236
+ any_struct_found = True
2237
+ break
2238
+ if stack_off + vartype.size // self.project.arch.byte_width <= off:
2239
+ break
2240
+ return any_struct_found
2241
+
2230
2242
  def _function_graph_to_ail_graph(self, func_graph, blocks_by_addr_and_size=None):
2231
2243
  if blocks_by_addr_and_size is None:
2232
2244
  blocks_by_addr_and_size = self._blocks_by_addr_and_size
@@ -4,14 +4,14 @@ from collections import defaultdict
4
4
 
5
5
  from angr.ailment import AILBlockWalker
6
6
  from angr.ailment.block import Block
7
- from angr.ailment.expression import Phi, VirtualVariable, VirtualVariableCategory
7
+ from angr.ailment.expression import Phi, VirtualVariable
8
8
  from angr.ailment.statement import Assignment, Jump, ConditionalJump, Label
9
9
 
10
10
  from angr.analyses import Analysis
11
11
  from angr.analyses.s_reaching_definitions import SRDAModel
12
12
  from angr.knowledge_plugins.functions import Function
13
13
  from angr.analyses import register_analysis
14
- from angr.utils.ssa import is_phi_assignment, DEPHI_VVAR_REG_OFFSET
14
+ from angr.utils.ssa import is_phi_assignment
15
15
 
16
16
  l = logging.getLogger(name=__name__)
17
17
 
@@ -232,14 +232,8 @@ class GraphDephicationVVarMapping(Analysis): # pylint:disable=abstract-method
232
232
  # we have not yet appended a statement to this block
233
233
  the_block = self._blocks[src]
234
234
  ins_addr = the_block.addr + the_block.original_size - 1
235
- if vvar.category == VirtualVariableCategory.PARAMETER:
236
- # it's a parameter, so we copy the variable into a dummy register vvar
237
- # a parameter vvar should never be assigned to
238
- new_category = VirtualVariableCategory.REGISTER
239
- new_oident = DEPHI_VVAR_REG_OFFSET
240
- else:
241
- new_category = vvar.category
242
- new_oident = vvar.oident
235
+ new_category = phi_stmt.dst.category
236
+ new_oident = phi_stmt.dst.oident
243
237
  new_vvar = VirtualVariable(
244
238
  None, new_vvar_id, vvar.bits, new_category, oident=new_oident, ins_addr=ins_addr
245
239
  )
@@ -9,12 +9,13 @@ import networkx
9
9
  import angr.ailment as ailment
10
10
  from angr.ailment import Block
11
11
  from angr.ailment.expression import Phi, VirtualVariable, VirtualVariableCategory
12
- from angr.ailment.statement import Assignment, Label
12
+ from angr.ailment.statement import Assignment, Label, Statement
13
13
 
14
14
  from angr.code_location import CodeLocation
15
15
  from angr.analyses import ForwardAnalysis
16
16
  from angr.analyses.forward_analysis import FunctionGraphVisitor
17
- from angr.utils.ail import is_head_controlled_loop_block
17
+ from angr.utils.ail import is_head_controlled_loop_block, extract_partial_expr
18
+ from angr.utils.ssa import get_reg_offset_base_and_size, is_phi_assignment
18
19
  from .rewriting_engine import SimEngineSSARewriting, DefExprType, AT
19
20
  from .rewriting_state import RewritingState
20
21
 
@@ -192,6 +193,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, ailment.Block, object, o
192
193
  else self.out_states[(node_.addr, node_.idx)]
193
194
  )
194
195
  if reg_offset in out_state.registers and reg_size in out_state.registers[reg_offset]:
196
+ # we found a perfect hit
195
197
  existing_var = out_state.registers[reg_offset][reg_size]
196
198
  if existing_var is None:
197
199
  # the vvar is not set. it should never be referenced
@@ -199,6 +201,25 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, ailment.Block, object, o
199
201
  vvar = existing_var.copy()
200
202
  vvar.idx = self._ail_manager.next_atom()
201
203
  return True, vvar
204
+
205
+ # try to see if any register writes overlap with the requested one
206
+ # note that we only support full overlaps for now...
207
+ for off in out_state.registers:
208
+ if reg_offset + reg_size <= off or (
209
+ out_state.registers[off] and reg_offset > off + max(out_state.registers[off])
210
+ ):
211
+ continue
212
+ for sz in sorted(out_state.registers[off], reverse=True):
213
+ if reg_offset >= off and reg_offset + reg_size <= off + sz:
214
+ existing_var = out_state.registers[off][sz]
215
+ if existing_var is None:
216
+ # the vvar is not set.
217
+ return True, None
218
+
219
+ # return the base vvar
220
+ base_vvar = existing_var.copy()
221
+ base_vvar.idx = self._ail_manager.next_atom()
222
+ return True, base_vvar
202
223
  return False, None
203
224
 
204
225
  def _stack_predicate(self, node_: Block, *, stack_offset: int, stackvar_size: int) -> tuple[bool, Any]:
@@ -319,19 +340,26 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, ailment.Block, object, o
319
340
  for stmt in node.statements
320
341
  ):
321
342
  # there we go
322
- new_stmts = []
343
+ phi_stmts = []
344
+ phi_extraction_stmts = []
345
+ other_stmts = []
323
346
  for stmt in node.statements:
324
- if not (
347
+ if (
325
348
  isinstance(stmt, Assignment)
326
349
  and isinstance(stmt.dst, VirtualVariable)
327
350
  and isinstance(stmt.src, Phi)
328
- and len(stmt.src.src_and_vvars) == 0 # avoid re-assignment
351
+ and len(stmt.src.src_and_vvars) > 0
329
352
  ):
330
- new_stmts.append(stmt)
353
+ # avoid re-assignment
354
+ phi_stmts.append(stmt)
355
+ continue
356
+ if not is_phi_assignment(stmt):
357
+ other_stmts.append(stmt)
331
358
  continue
332
359
 
333
360
  src_and_vvars = []
334
361
  if stmt.dst.was_reg:
362
+ perfect_matches = []
335
363
  for pred in self.graph.predecessors(original_node):
336
364
  vvar = self._follow_one_path_backward(
337
365
  self.graph,
@@ -339,6 +367,36 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, ailment.Block, object, o
339
367
  partial(self._reg_predicate, reg_offset=stmt.dst.reg_offset, reg_size=stmt.dst.size),
340
368
  )
341
369
  src_and_vvars.append(((pred.addr, pred.idx), vvar))
370
+ perfect_matches.append(
371
+ (vvar.reg_offset == stmt.dst.reg_offset and vvar.size == stmt.dst.size)
372
+ if vvar is not None
373
+ else True
374
+ )
375
+ if all(perfect_matches):
376
+ phi_var = Phi(stmt.src.idx, stmt.src.bits, src_and_vvars=src_and_vvars)
377
+ phi_stmt = Assignment(stmt.idx, stmt.dst, phi_var, **stmt.tags)
378
+ phi_stmts.append(phi_stmt)
379
+ else:
380
+ # different sizes of vvars found; we need to resort to the base register and extract the
381
+ # requested register out of the base register
382
+ # here we rely on the fact that the larger register vvar must be created in this block when
383
+ # the smaller register has been created
384
+ base_reg_offset, max_reg_size = get_reg_offset_base_and_size(
385
+ stmt.dst.reg_offset, self.project.arch, size=stmt.dst.size
386
+ )
387
+ # find the phi assignment statement of the larger base register
388
+ base_vvar = self._find_phi_vvar_for_reg(base_reg_offset, max_reg_size, node.statements)
389
+ assert base_vvar is not None
390
+ partial_base_vvar = extract_partial_expr(
391
+ base_vvar,
392
+ stmt.dst.reg_offset - base_reg_offset,
393
+ stmt.dst.size,
394
+ self._ail_manager,
395
+ byte_width=self.project.arch.byte_width,
396
+ )
397
+ new_stmt = Assignment(stmt.idx, stmt.dst, partial_base_vvar, **base_vvar.tags)
398
+ phi_extraction_stmts.append(new_stmt)
399
+
342
400
  elif stmt.dst.was_stack:
343
401
  for pred in self.graph.predecessors(original_node):
344
402
  vvar = self._follow_one_path_backward(
@@ -351,13 +409,14 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, ailment.Block, object, o
351
409
  ),
352
410
  )
353
411
  src_and_vvars.append(((pred.addr, pred.idx), vvar))
412
+
413
+ phi_var = Phi(stmt.src.idx, stmt.src.bits, src_and_vvars=src_and_vvars)
414
+ phi_stmt = Assignment(stmt.idx, stmt.dst, phi_var, **stmt.tags)
415
+ phi_stmts.append(phi_stmt)
354
416
  else:
355
417
  raise NotImplementedError
356
418
 
357
- phi_var = Phi(stmt.src.idx, stmt.src.bits, src_and_vvars=src_and_vvars)
358
- new_stmt = Assignment(stmt.idx, stmt.dst, phi_var, **stmt.tags)
359
- new_stmts.append(new_stmt)
360
- node = node.copy(statements=new_stmts)
419
+ node = node.copy(statements=phi_stmts + phi_extraction_stmts + other_stmts)
361
420
  self.out_blocks[node_key] = node
362
421
 
363
422
  @staticmethod
@@ -377,3 +436,16 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, ailment.Block, object, o
377
436
  break
378
437
  the_node = more_preds[0]
379
438
  return return_value
439
+
440
+ @staticmethod
441
+ def _find_phi_vvar_for_reg(reg_offset: int, reg_size: int, stmts: list[Statement]) -> VirtualVariable | None:
442
+ for stmt in stmts:
443
+ if (
444
+ is_phi_assignment(stmt)
445
+ and isinstance(stmt.dst, VirtualVariable)
446
+ and stmt.dst.was_reg
447
+ and stmt.dst.reg_offset == reg_offset
448
+ and stmt.dst.size == reg_size
449
+ ):
450
+ return stmt.dst
451
+ return None
@@ -153,7 +153,7 @@ class SimEngineSSARewriting(
153
153
  else:
154
154
  new_dst = self._replace_def_expr(self.block.addr, self.block.idx, self.stmt_idx, stmt.dst)
155
155
 
156
- stmt_base_reg = None
156
+ additional_stmts = []
157
157
  if new_dst is not None:
158
158
  if isinstance(stmt.dst, Register):
159
159
  # remove everything else that is an alias
@@ -163,10 +163,11 @@ class SimEngineSSARewriting(
163
163
 
164
164
  self.state.registers[stmt.dst.reg_offset][stmt.dst.size] = new_dst
165
165
 
166
- # generate an assignment that updates the base register if needed
167
166
  base_offset, base_size = get_reg_offset_base_and_size(
168
167
  stmt.dst.reg_offset, self.arch, size=stmt.dst.size
169
168
  )
169
+
170
+ # generate an assignment that updates the base register if needed
170
171
  if base_offset != stmt.dst.reg_offset or base_size != stmt.dst.size:
171
172
  base_reg_expr = Register(
172
173
  self.ail_manager.next_atom(),
@@ -180,15 +181,25 @@ class SimEngineSSARewriting(
180
181
  self.block.addr, self.block.idx, self.stmt_idx, base_reg_expr
181
182
  )
182
183
  assert base_reg_vvar is not None
184
+ base_reg_value = self._partial_update_expr(
185
+ existing_base_reg_vvar,
186
+ base_offset,
187
+ base_size,
188
+ new_dst,
189
+ stmt.dst.reg_offset,
190
+ stmt.dst.size,
191
+ )
183
192
  stmt_base_reg = Assignment(
184
193
  self.ail_manager.next_atom(),
185
194
  base_reg_vvar,
186
- self._partial_update_expr(
187
- existing_base_reg_vvar, base_offset, base_size, new_dst, stmt.dst.reg_offset, stmt.dst.size
188
- ),
195
+ base_reg_value,
189
196
  **stmt.tags,
190
197
  )
198
+ additional_stmts.append(stmt_base_reg)
191
199
  self.state.registers[base_offset][base_size] = base_reg_vvar
200
+ else:
201
+ base_reg_vvar = new_dst
202
+
192
203
  elif isinstance(stmt.dst, Tmp):
193
204
  pass
194
205
  else:
@@ -201,8 +212,8 @@ class SimEngineSSARewriting(
201
212
  stmt.src if new_src is None else new_src,
202
213
  **stmt.tags,
203
214
  )
204
- if stmt_base_reg is not None:
205
- return new_stmt, stmt_base_reg
215
+ if additional_stmts:
216
+ return new_stmt, *additional_stmts
206
217
  return new_stmt
207
218
  return None
208
219
 
@@ -612,12 +623,12 @@ class SimEngineSSARewriting(
612
623
  existing_vvar: Expression,
613
624
  base_offset: int,
614
625
  base_size: int,
615
- new_vvar: VirtualVariable,
626
+ new_value: Expression,
616
627
  offset: int,
617
628
  size: int,
618
629
  ) -> VirtualVariable | Expression:
619
630
  if offset == base_offset and base_size == size:
620
- return new_vvar
631
+ return new_value
621
632
  if base_offset > offset:
622
633
  raise ValueError(f"Base offset {base_offset} is greater than expression offset {offset}")
623
634
 
@@ -639,8 +650,8 @@ class SimEngineSSARewriting(
639
650
  size * self.arch.byte_width,
640
651
  base_size * self.arch.byte_width,
641
652
  False,
642
- new_vvar,
643
- **new_vvar.tags,
653
+ new_value,
654
+ **new_value.tags,
644
655
  )
645
656
  if base_offset < offset:
646
657
  shift_amount = Const(
@@ -85,9 +85,9 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
85
85
  )
86
86
 
87
87
  # calculate virtual variables and phi nodes
88
- self._udef_to_phiid: dict[tuple, set[int]] = None
89
- self._phiid_to_loc: dict[int, tuple[int, int | None]] = None
90
- self._stackvar_locs: dict[int, set[int]] = None
88
+ self._udef_to_phiid: dict[tuple, set[int]] = {}
89
+ self._phiid_to_loc: dict[int, tuple[int, int | None]] = {}
90
+ self._stackvar_locs: dict[int, set[int]] = {}
91
91
  self._calculate_virtual_variables(ail_graph, traversal.def_to_loc, traversal.loc_to_defs)
92
92
 
93
93
  # insert phi variables and rewrite uses
@@ -140,14 +140,16 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
140
140
  # handle function arguments
141
141
  if self._func_args:
142
142
  for func_arg in self._func_args:
143
- if func_arg.oident[0] == VirtualVariableCategory.STACK:
144
- stackvar_locs[func_arg.oident[1]] = {func_arg.size}
143
+ if func_arg.parameter_category == VirtualVariableCategory.STACK:
144
+ assert isinstance(func_arg.parameter_stack_offset, int)
145
+ stackvar_locs[func_arg.parameter_stack_offset] = {func_arg.size}
145
146
  sorted_stackvar_offs = sorted(stackvar_locs)
146
147
  else:
147
148
  stackvar_locs = {}
148
149
  sorted_stackvar_offs = []
149
150
 
150
151
  # compute phi node locations for each unified definition
152
+ # compute udef_to_blockkeys
151
153
  udef_to_defs = defaultdict(set)
152
154
  udef_to_blockkeys = defaultdict(set)
153
155
  for def_, loc in def_to_loc: