angr 9.2.123__py3-none-manylinux2014_aarch64.whl → 9.2.124__py3-none-manylinux2014_aarch64.whl

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

Potentially problematic release.


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

Files changed (83) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_fast.py +11 -8
  3. angr/analyses/cfg/indirect_jump_resolvers/mips_elf_got.py +2 -2
  4. angr/analyses/decompiler/ail_simplifier.py +16 -19
  5. angr/analyses/decompiler/callsite_maker.py +8 -7
  6. angr/analyses/decompiler/ccall_rewriters/amd64_ccalls.py +24 -2
  7. angr/analyses/decompiler/clinic.py +27 -1
  8. angr/analyses/decompiler/condition_processor.py +10 -3
  9. angr/analyses/decompiler/decompilation_cache.py +2 -0
  10. angr/analyses/decompiler/decompiler.py +50 -8
  11. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +10 -2
  12. angr/analyses/decompiler/dephication/rewriting_engine.py +64 -1
  13. angr/analyses/decompiler/expression_narrower.py +5 -1
  14. angr/analyses/decompiler/optimization_passes/div_simplifier.py +4 -1
  15. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +7 -0
  16. angr/analyses/decompiler/optimization_passes/ite_region_converter.py +23 -4
  17. angr/analyses/decompiler/optimization_passes/optimization_pass.py +3 -1
  18. angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +8 -5
  19. angr/analyses/decompiler/optimization_passes/return_duplicator_high.py +10 -5
  20. angr/analyses/decompiler/optimization_passes/return_duplicator_low.py +18 -7
  21. angr/analyses/decompiler/optimization_passes/switch_default_case_duplicator.py +6 -0
  22. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +2 -0
  23. angr/analyses/decompiler/peephole_optimizations/const_mull_a_shift.py +2 -0
  24. angr/analyses/decompiler/peephole_optimizations/remove_cascading_conversions.py +8 -2
  25. angr/analyses/decompiler/region_identifier.py +36 -0
  26. angr/analyses/decompiler/region_simplifiers/loop.py +2 -8
  27. angr/analyses/decompiler/region_simplifiers/switch_cluster_simplifier.py +9 -3
  28. angr/analyses/decompiler/ssailification/rewriting.py +5 -2
  29. angr/analyses/decompiler/ssailification/rewriting_engine.py +151 -25
  30. angr/analyses/decompiler/ssailification/rewriting_state.py +1 -0
  31. angr/analyses/decompiler/ssailification/ssailification.py +17 -9
  32. angr/analyses/decompiler/ssailification/traversal.py +3 -1
  33. angr/analyses/decompiler/ssailification/traversal_engine.py +35 -8
  34. angr/analyses/decompiler/ssailification/traversal_state.py +1 -0
  35. angr/analyses/decompiler/structured_codegen/c.py +42 -4
  36. angr/analyses/decompiler/structuring/phoenix.py +3 -0
  37. angr/analyses/propagator/engine_ail.py +10 -3
  38. angr/analyses/reaching_definitions/engine_ail.py +10 -15
  39. angr/analyses/s_propagator.py +16 -9
  40. angr/analyses/s_reaching_definitions/s_rda_view.py +127 -63
  41. angr/analyses/variable_recovery/engine_ail.py +14 -0
  42. angr/analyses/variable_recovery/engine_base.py +11 -0
  43. angr/engines/light/engine.py +12 -0
  44. angr/knowledge_plugins/__init__.py +2 -0
  45. angr/knowledge_plugins/decompilation.py +45 -0
  46. angr/knowledge_plugins/key_definitions/atoms.py +8 -0
  47. angr/procedures/definitions/parse_win32json.py +2 -1
  48. angr/storage/memory_mixins/actions_mixin.py +7 -7
  49. angr/storage/memory_mixins/address_concretization_mixin.py +5 -5
  50. angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
  51. angr/storage/memory_mixins/clouseau_mixin.py +3 -3
  52. angr/storage/memory_mixins/conditional_store_mixin.py +3 -3
  53. angr/storage/memory_mixins/default_filler_mixin.py +3 -3
  54. angr/storage/memory_mixins/memory_mixin.py +45 -34
  55. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +15 -14
  56. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +27 -16
  57. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +18 -9
  58. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +5 -5
  59. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +89 -55
  60. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +16 -25
  61. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +11 -9
  62. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +23 -7
  63. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +1 -1
  64. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +9 -7
  65. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +9 -9
  66. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +1 -0
  67. angr/storage/memory_mixins/simple_interface_mixin.py +2 -2
  68. angr/storage/memory_mixins/simplification_mixin.py +2 -2
  69. angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
  70. angr/storage/memory_mixins/slotted_memory.py +3 -3
  71. angr/storage/memory_mixins/smart_find_mixin.py +1 -0
  72. angr/storage/memory_mixins/underconstrained_mixin.py +5 -5
  73. angr/storage/memory_mixins/unwrapper_mixin.py +4 -4
  74. angr/storage/memory_object.py +4 -3
  75. angr/utils/constants.py +1 -1
  76. angr/utils/graph.py +15 -0
  77. angr/vaults.py +2 -2
  78. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/METADATA +6 -6
  79. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/RECORD +83 -82
  80. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/WHEEL +1 -1
  81. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/LICENSE +0 -0
  82. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/entry_points.txt +0 -0
  83. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,8 @@
1
1
  # pylint:disable=no-self-use,unused-argument
2
2
  from __future__ import annotations
3
- from typing import Any
4
3
  import logging
5
4
 
6
- from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump
5
+ from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement
7
6
  from ailment.expression import (
8
7
  Expression,
9
8
  Register,
@@ -18,6 +17,8 @@ from ailment.expression import (
18
17
  StackBaseOffset,
19
18
  VEXCCallExpression,
20
19
  ITE,
20
+ Tmp,
21
+ DirtyExpression,
21
22
  )
22
23
 
23
24
  from angr.utils.ssa import get_reg_offset_base_and_size
@@ -51,6 +52,7 @@ class SimEngineSSARewriting(
51
52
  ail_manager=None,
52
53
  vvar_id_start: int = 0,
53
54
  bp_as_gpr: bool = False,
55
+ rewrite_tmps: bool = False,
54
56
  ):
55
57
  super().__init__()
56
58
 
@@ -58,10 +60,11 @@ class SimEngineSSARewriting(
58
60
  self.project = project
59
61
  self.sp_tracker = sp_tracker
60
62
  self.bp_as_gpr = bp_as_gpr
61
- self.def_to_vvid: dict[Any, int] = {}
63
+ self.def_to_vvid: dict[tuple[int, int | None, int, Expression | Statement], int] = {}
62
64
  self.stackvar_locs = stackvar_locs
63
65
  self.udef_to_phiid = udef_to_phiid
64
66
  self.phiid_to_loc = phiid_to_loc
67
+ self.rewrite_tmps = rewrite_tmps
65
68
  self.ail_manager = ail_manager
66
69
 
67
70
  self._current_vvar_id = vvar_id_start
@@ -97,9 +100,11 @@ class SimEngineSSARewriting(
97
100
  self.state.registers[stmt.dst.reg_offset][stmt.dst.size] = stmt.dst
98
101
  elif stmt.dst.category == VirtualVariableCategory.STACK:
99
102
  self.state.stackvars[stmt.dst.stack_offset][stmt.dst.size] = stmt.dst
103
+ elif stmt.dst.category == VirtualVariableCategory.TMP:
104
+ self.state.tmps[stmt.dst.tmp_idx] = stmt.dst
100
105
  new_dst = None
101
106
  else:
102
- new_dst = self._replace_def_expr(stmt.dst)
107
+ new_dst = self._replace_def_expr(self.block.addr, self.block.idx, self.stmt_idx, stmt.dst)
103
108
 
104
109
  stmt_base_reg = None
105
110
  if new_dst is not None:
@@ -124,7 +129,9 @@ class SimEngineSSARewriting(
124
129
  **stmt.dst.tags,
125
130
  )
126
131
  existing_base_reg_vvar = self._replace_use_reg(base_reg_expr)
127
- base_reg_vvar = self._replace_def_expr(base_reg_expr)
132
+ base_reg_vvar = self._replace_def_expr(
133
+ self.block.addr, self.block.idx, self.stmt_idx, base_reg_expr
134
+ )
128
135
  stmt_base_reg = Assignment(
129
136
  self.ail_manager.next_atom(),
130
137
  base_reg_vvar,
@@ -134,6 +141,8 @@ class SimEngineSSARewriting(
134
141
  **stmt.tags,
135
142
  )
136
143
  self.state.registers[base_offset][base_size] = base_reg_vvar
144
+ elif isinstance(stmt.dst, Tmp):
145
+ pass
137
146
  else:
138
147
  raise NotImplementedError
139
148
 
@@ -151,7 +160,7 @@ class SimEngineSSARewriting(
151
160
 
152
161
  def _handle_Store(self, stmt: Store) -> Store | Assignment | None:
153
162
  new_data = self._expr(stmt.data)
154
- vvar = self._replace_def_store(stmt)
163
+ vvar = self._replace_def_store(self.block.addr, self.block.idx, self.stmt_idx, stmt)
155
164
  if vvar is not None:
156
165
  return Assignment(stmt.idx, vvar, stmt.data if new_data is None else new_data, **stmt.tags)
157
166
 
@@ -189,9 +198,19 @@ class SimEngineSSARewriting(
189
198
  return None
190
199
 
191
200
  def _handle_Call(self, stmt: Call) -> Call | None:
192
- new_target = self._replace_use_reg(stmt.target) if isinstance(stmt.target, Register) else None
193
- new_ret_expr = self._replace_def_expr(stmt.ret_expr) if stmt.ret_expr is not None else None
194
- new_fp_ret_expr = self._replace_def_expr(stmt.fp_ret_expr) if stmt.fp_ret_expr is not None else None
201
+ changed = False
202
+
203
+ new_target = self._replace_use_expr(stmt.target)
204
+ new_ret_expr = (
205
+ self._replace_def_expr(self.block.addr, self.block.idx, self.stmt_idx, stmt.ret_expr)
206
+ if stmt.ret_expr is not None
207
+ else None
208
+ )
209
+ new_fp_ret_expr = (
210
+ self._replace_def_expr(self.block.addr, self.block.idx, self.stmt_idx, stmt.fp_ret_expr)
211
+ if stmt.fp_ret_expr is not None
212
+ else None
213
+ )
195
214
 
196
215
  cc = stmt.calling_convention if stmt.calling_convention is not None else self.project.factory.cc()
197
216
  if cc is not None:
@@ -211,22 +230,50 @@ class SimEngineSSARewriting(
211
230
  self._clear_aliasing_regs(stmt.fp_ret_expr.reg_offset, stmt.fp_ret_expr.size)
212
231
  self.state.registers[stmt.fp_ret_expr.reg_offset][stmt.fp_ret_expr.size] = new_fp_ret_expr
213
232
 
233
+ new_args = None
234
+ if stmt.args is not None:
235
+ new_args = []
236
+ for arg in stmt.args:
237
+ new_arg = self._expr(arg)
238
+ if new_arg is not None:
239
+ changed = True
240
+ new_args.append(new_arg)
241
+ else:
242
+ new_args.append(arg)
243
+
214
244
  if new_target is not None or new_ret_expr is not None or new_fp_ret_expr is not None:
245
+ changed = True
246
+
247
+ if changed:
215
248
  return Call(
216
249
  stmt.idx,
217
250
  stmt.target if new_target is None else new_target,
218
251
  calling_convention=stmt.calling_convention,
219
252
  prototype=stmt.prototype,
220
- args=stmt.args,
253
+ args=new_args,
221
254
  ret_expr=stmt.ret_expr if new_ret_expr is None else new_ret_expr,
222
255
  fp_ret_expr=stmt.fp_ret_expr if new_fp_ret_expr is None else new_fp_ret_expr,
256
+ bits=stmt.bits,
223
257
  **stmt.tags,
224
258
  )
225
259
  return None
226
260
 
261
+ _handle_CallExpr = _handle_Call
262
+
263
+ def _handle_DirtyStatement(self, stmt: DirtyStatement) -> DirtyStatement | None:
264
+ dirty = self._expr(stmt.dirty)
265
+ if dirty is None or dirty is stmt.dirty:
266
+ return None
267
+ return DirtyStatement(stmt.idx, dirty, **stmt.tags)
268
+
227
269
  def _handle_Register(self, expr: Register) -> VirtualVariable | None:
228
270
  return self._replace_use_reg(expr)
229
271
 
272
+ def _handle_Tmp(self, expr: Tmp) -> VirtualVariable | None:
273
+ return (
274
+ self._replace_use_tmp(self.block.addr, self.block.idx, self.stmt_idx, expr) if self.rewrite_tmps else None
275
+ )
276
+
230
277
  def _handle_Load(self, expr: Load) -> Load | VirtualVariable | None:
231
278
  if isinstance(expr.addr, StackBaseOffset) and isinstance(expr.addr.offset, int):
232
279
  new_expr = self._replace_use_load(expr)
@@ -341,13 +388,42 @@ class SimEngineSSARewriting(
341
388
  new_operands.append(operand)
342
389
 
343
390
  if updated:
344
- return VEXCCallExpression(expr.idx, expr.cee_name, new_operands, bits=expr.bits, **expr.tags)
391
+ return VEXCCallExpression(expr.idx, expr.callee, new_operands, bits=expr.bits, **expr.tags)
345
392
  return None
346
393
 
347
- def _handle_Dummy(self, expr) -> None:
394
+ def _handle_DirtyExpression(self, expr: DirtyExpression) -> DirtyExpression | None:
395
+ updated = False
396
+ new_operands = []
397
+ for operand in expr.operands:
398
+ new_operand = self._expr(operand)
399
+ if new_operand is not None:
400
+ updated = True
401
+ new_operands.append(new_operand)
402
+ else:
403
+ new_operands.append(operand)
404
+
405
+ new_guard = None
406
+ if expr.guard is not None:
407
+ new_guard = self._expr(expr.guard)
408
+ if new_guard is not None:
409
+ updated = True
410
+
411
+ if updated:
412
+ return DirtyExpression(
413
+ expr.idx,
414
+ expr.callee,
415
+ new_operands,
416
+ guard=new_guard,
417
+ mfx=expr.mfx,
418
+ maddr=expr.maddr,
419
+ msize=expr.msize,
420
+ bits=expr.bits,
421
+ **expr.tags,
422
+ )
348
423
  return None
349
424
 
350
- _handle_DirtyExpression = _handle_Dummy
425
+ def _handle_Dummy(self, expr) -> None:
426
+ return None
351
427
 
352
428
  #
353
429
  # Expression replacement
@@ -417,23 +493,29 @@ class SimEngineSSARewriting(
417
493
  **new_base_expr.tags,
418
494
  )
419
495
 
420
- def _replace_def_expr(self, thing: Expression | Statement) -> VirtualVariable | None:
496
+ def _replace_def_expr(
497
+ self, block_addr: int, block_idx: int | None, stmt_idx: int, thing: Expression | Statement
498
+ ) -> VirtualVariable | None:
421
499
  """
422
500
  Return a new virtual variable for the given defined expression.
423
501
  """
424
502
  if isinstance(thing, Register):
425
- return self._replace_def_reg(thing)
503
+ return self._replace_def_reg(block_addr, block_idx, stmt_idx, thing)
426
504
  if isinstance(thing, Store):
427
- return self._replace_def_store(thing)
505
+ return self._replace_def_store(block_addr, block_idx, stmt_idx, thing)
506
+ if isinstance(thing, Tmp) and self.rewrite_tmps:
507
+ return self._replace_def_tmp(block_addr, block_idx, stmt_idx, thing)
428
508
  return None
429
509
 
430
- def _replace_def_reg(self, expr: Register) -> VirtualVariable:
510
+ def _replace_def_reg(
511
+ self, block_addr: int, block_idx: int | None, stmt_idx: int, expr: Register
512
+ ) -> VirtualVariable:
431
513
  """
432
514
  Return a new virtual variable for the given defined register.
433
515
  """
434
516
 
435
517
  # get the virtual variable ID
436
- vvid = self.get_vvid_by_def(expr)
518
+ vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, expr)
437
519
  return VirtualVariable(
438
520
  expr.idx,
439
521
  vvid,
@@ -464,14 +546,16 @@ class SimEngineSSARewriting(
464
546
  return vvar
465
547
  return self.state.registers[base_off][base_size]
466
548
 
467
- def _replace_def_store(self, stmt: Store) -> VirtualVariable | None:
549
+ def _replace_def_store(
550
+ self, block_addr: int, block_idx: int | None, stmt_idx: int, stmt: Store
551
+ ) -> VirtualVariable | None:
468
552
  if (
469
553
  isinstance(stmt.addr, StackBaseOffset)
470
554
  and isinstance(stmt.addr.offset, int)
471
555
  and stmt.addr.offset in self.stackvar_locs
472
556
  and stmt.size == self.stackvar_locs[stmt.addr.offset]
473
557
  ):
474
- vvar_id = self.get_vvid_by_def(stmt)
558
+ vvar_id = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, stmt)
475
559
  vvar = VirtualVariable(
476
560
  self.ail_manager.next_atom(),
477
561
  vvar_id,
@@ -484,6 +568,31 @@ class SimEngineSSARewriting(
484
568
  return vvar
485
569
  return None
486
570
 
571
+ def _replace_def_tmp(self, block_addr: int, block_idx: int | None, stmt_idx: int, expr: Tmp) -> VirtualVariable:
572
+ vvid = self.get_vvid_by_def(block_addr, block_idx, stmt_idx, expr)
573
+ vvar = VirtualVariable(
574
+ expr.idx,
575
+ vvid,
576
+ expr.bits,
577
+ VirtualVariableCategory.TMP,
578
+ oident=expr.tmp_idx,
579
+ **expr.tags,
580
+ )
581
+ self.state.tmps[expr.tmp_idx] = vvar
582
+ return vvar
583
+
584
+ def _replace_use_expr(self, thing: Expression | Statement) -> VirtualVariable | None:
585
+ """
586
+ Return a new virtual variable for the given defined expression.
587
+ """
588
+ if isinstance(thing, Register):
589
+ return self._replace_use_reg(thing)
590
+ if isinstance(thing, Store):
591
+ raise NotImplementedError("Store expressions are not supported in _replace_use_expr.")
592
+ if isinstance(thing, Tmp) and self.rewrite_tmps:
593
+ return self._replace_use_tmp(self.block.addr, self.block.idx, self.stmt_idx, thing)
594
+ return None
595
+
487
596
  def _replace_use_reg(self, reg_expr: Register) -> VirtualVariable | Expression:
488
597
 
489
598
  if reg_expr.reg_offset in self.state.registers:
@@ -556,7 +665,8 @@ class SimEngineSSARewriting(
556
665
  and expr.size == self.stackvar_locs[expr.addr.offset]
557
666
  ):
558
667
  if expr.size not in self.state.stackvars[expr.addr.offset]:
559
- vvar_id = self.get_vvid_by_def(expr)
668
+ # create it on the fly
669
+ vvar_id = self.get_vvid_by_def(self.block.addr, self.block.idx, self.stmt_idx, expr)
560
670
  return VirtualVariable(
561
671
  self.ail_manager.next_atom(),
562
672
  vvar_id,
@@ -579,15 +689,31 @@ class SimEngineSSARewriting(
579
689
  )
580
690
  return None
581
691
 
692
+ def _replace_use_tmp(self, block_addr: int, block_idx: int | None, stmt_idx: int, expr: Tmp) -> VirtualVariable:
693
+ vvar = self.state.tmps.get(expr.tmp_idx)
694
+ if vvar is None:
695
+ return self._replace_def_tmp(block_addr, block_idx, stmt_idx, expr)
696
+ return VirtualVariable(
697
+ expr.idx,
698
+ vvar.varid,
699
+ vvar.bits,
700
+ VirtualVariableCategory.TMP,
701
+ oident=expr.tmp_idx,
702
+ **expr.tags,
703
+ )
704
+
582
705
  #
583
706
  # Utils
584
707
  #
585
708
 
586
- def get_vvid_by_def(self, thing: Expression | Statement) -> int:
587
- if thing in self.def_to_vvid:
588
- return self.def_to_vvid[thing]
709
+ def get_vvid_by_def(
710
+ self, block_addr: int, block_idx: int | None, stmt_idx: int, thing: Expression | Statement
711
+ ) -> int:
712
+ key = block_addr, block_idx, stmt_idx, thing
713
+ if key in self.def_to_vvid:
714
+ return self.def_to_vvid[key]
589
715
  vvid = self.next_vvar_id()
590
- self.def_to_vvid[thing] = vvid
716
+ self.def_to_vvid[key] = vvid
591
717
  return vvid
592
718
 
593
719
  def _clear_aliasing_regs(self, reg_offset: int, size: int, remove_base_reg: bool = True) -> None:
@@ -32,6 +32,7 @@ class RewritingState:
32
32
  self.stackvars: defaultdict[int, dict[int, VirtualVariable]] = (
33
33
  stackvars if stackvars is not None else defaultdict(dict)
34
34
  )
35
+ self.tmps: dict[int, VirtualVariable] = {}
35
36
  self.original_block = original_block
36
37
  self.out_block = None
37
38
 
@@ -5,8 +5,8 @@ from collections import defaultdict
5
5
  from itertools import count
6
6
  from bisect import bisect_left
7
7
 
8
- from ailment.expression import Register, StackBaseOffset
9
- from ailment.statement import Store
8
+ from ailment.expression import Expression, Register, StackBaseOffset, Tmp
9
+ from ailment.statement import Statement, Store
10
10
 
11
11
  from angr.knowledge_plugins.functions import Function
12
12
  from angr.code_location import CodeLocation
@@ -33,6 +33,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
33
33
  func_addr: int | None = None,
34
34
  ail_manager=None,
35
35
  ssa_stackvars: bool = False,
36
+ ssa_tmps: bool = False,
36
37
  vvar_id_start: int = 0,
37
38
  ):
38
39
  """
@@ -51,6 +52,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
51
52
  self._func_addr = func_addr
52
53
  self._ail_manager = ail_manager
53
54
  self._ssa_stackvars = ssa_stackvars
55
+ self._ssa_tmps = ssa_tmps
54
56
  self._entry = (
55
57
  entry
56
58
  if entry is not None
@@ -68,6 +70,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
68
70
  stack_pointer_tracker,
69
71
  bp_as_gpr,
70
72
  ssa_stackvars,
73
+ ssa_tmps,
71
74
  )
72
75
 
73
76
  # calculate virtual variables and phi nodes
@@ -86,13 +89,19 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
86
89
  self._udef_to_phiid,
87
90
  self._phiid_to_loc,
88
91
  self._stackvar_locs,
92
+ self._ssa_tmps,
89
93
  self._ail_manager,
90
94
  vvar_id_start=vvar_id_start,
91
95
  )
92
96
  self.out_graph = rewriter.out_graph
93
97
  self.max_vvar_id = rewriter.max_vvar_id
94
98
 
95
- def _calculate_virtual_variables(self, ail_graph, def_to_loc: dict, loc_to_defs: dict[CodeLocation, Any]):
99
+ def _calculate_virtual_variables(
100
+ self,
101
+ ail_graph,
102
+ def_to_loc: list[tuple[Expression | Statement, CodeLocation]],
103
+ loc_to_defs: dict[CodeLocation, Any],
104
+ ):
96
105
  """
97
106
  Calculate the mapping from defs to virtual variables as well as where to insert phi nodes.
98
107
  """
@@ -112,7 +121,7 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
112
121
  if self._ssa_stackvars:
113
122
  # for stack variables, we collect all definitions and identify stack variable locations using heuristics
114
123
 
115
- stackvar_locs = self._synthesize_stackvar_locs([def_ for def_ in def_to_loc if isinstance(def_, Store)])
124
+ stackvar_locs = self._synthesize_stackvar_locs([def_ for def_, _ in def_to_loc if isinstance(def_, Store)])
116
125
  sorted_stackvar_offs = sorted(stackvar_locs)
117
126
  else:
118
127
  stackvar_locs = {}
@@ -121,10 +130,8 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
121
130
  # computer phi node locations for each unified definition
122
131
  udef_to_defs = defaultdict(set)
123
132
  udef_to_blockkeys = defaultdict(set)
124
- for def_ in def_to_loc:
133
+ for def_, loc in def_to_loc:
125
134
  if isinstance(def_, Register):
126
- loc = def_to_loc[def_]
127
-
128
135
  base_off, base_size = get_reg_offset_base_and_size(def_.reg_offset, self.project.arch, size=def_.size)
129
136
  base_reg_bits = base_size * self.project.arch.byte_width
130
137
  udef_to_defs[("reg", base_off, base_reg_bits)].add(def_)
@@ -135,8 +142,6 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
135
142
  udef_to_defs[("reg", def_.reg_offset, reg_bits)].add((loc.block_addr, loc.block_idx))
136
143
  elif isinstance(def_, Store):
137
144
  if isinstance(def_.addr, StackBaseOffset) and isinstance(def_.addr.offset, int):
138
- loc = def_to_loc[def_]
139
-
140
145
  idx_begin = bisect_left(sorted_stackvar_offs, def_.addr.offset)
141
146
  for i in range(idx_begin, len(sorted_stackvar_offs)):
142
147
  off = sorted_stackvar_offs[i]
@@ -144,6 +149,9 @@ class Ssailification(Analysis): # pylint:disable=abstract-method
144
149
  break
145
150
  udef_to_defs[("stack", off, stackvar_locs[off])].add(def_)
146
151
  udef_to_blockkeys[("stack", off, stackvar_locs[off])].add((loc.block_addr, loc.block_idx))
152
+ elif isinstance(def_, Tmp):
153
+ # Tmps are local to each block and do not need phi nodes
154
+ pass
147
155
  else:
148
156
  raise NotImplementedError
149
157
  # other types are not supported yet
@@ -19,10 +19,11 @@ class TraversalAnalysis(ForwardAnalysis[None, NodeType, object, object]):
19
19
  TraversalAnalysis traverses the AIL graph and collects definitions.
20
20
  """
21
21
 
22
- def __init__(self, project, func, ail_graph, sp_tracker, bp_as_gpr: bool, stackvars: bool):
22
+ def __init__(self, project, func, ail_graph, sp_tracker, bp_as_gpr: bool, stackvars: bool, tmps: bool):
23
23
 
24
24
  self.project = project
25
25
  self._stackvars = stackvars
26
+ self._tmps = tmps
26
27
  self._function = func
27
28
  self._graph_visitor = FunctionGraphVisitor(self._function, ail_graph)
28
29
 
@@ -34,6 +35,7 @@ class TraversalAnalysis(ForwardAnalysis[None, NodeType, object, object]):
34
35
  sp_tracker=sp_tracker,
35
36
  bp_as_gpr=bp_as_gpr,
36
37
  stackvars=self._stackvars,
38
+ tmps=self._tmps,
37
39
  )
38
40
 
39
41
  self._visited_blocks: set[Any] = set()
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
  from collections import OrderedDict
3
3
 
4
4
  from ailment.statement import Assignment, Call, Store, ConditionalJump
5
- from ailment.expression import Register, BinaryOp, StackBaseOffset, ITE, VEXCCallExpression
5
+ from ailment.expression import Register, BinaryOp, StackBaseOffset, ITE, VEXCCallExpression, Tmp, DirtyExpression
6
6
 
7
7
  from angr.engines.light import SimEngineLight, SimEngineLightAILMixin
8
8
  from angr.utils.ssa import get_reg_offset_base
@@ -21,7 +21,14 @@ class SimEngineSSATraversal(
21
21
  state: TraversalState
22
22
 
23
23
  def __init__(
24
- self, arch, sp_tracker=None, bp_as_gpr: bool = False, def_to_loc=None, loc_to_defs=None, stackvars: bool = False
24
+ self,
25
+ arch,
26
+ sp_tracker=None,
27
+ bp_as_gpr: bool = False,
28
+ def_to_loc=None,
29
+ loc_to_defs=None,
30
+ stackvars: bool = False,
31
+ tmps: bool = False,
25
32
  ):
26
33
  super().__init__()
27
34
 
@@ -29,14 +36,15 @@ class SimEngineSSATraversal(
29
36
  self.sp_tracker = sp_tracker
30
37
  self.bp_as_gpr = bp_as_gpr
31
38
  self.stackvars = stackvars
39
+ self.tmps = tmps
32
40
 
33
- self.def_to_loc = def_to_loc if def_to_loc is not None else OrderedDict()
41
+ self.def_to_loc = def_to_loc if def_to_loc is not None else []
34
42
  self.loc_to_defs = loc_to_defs if loc_to_defs is not None else OrderedDict()
35
43
 
36
44
  def _handle_Assignment(self, stmt: Assignment):
37
45
  if isinstance(stmt.dst, Register):
38
46
  codeloc = self._codeloc()
39
- self.def_to_loc[stmt.dst] = codeloc
47
+ self.def_to_loc.append((stmt.dst, codeloc))
40
48
  if codeloc not in self.loc_to_defs:
41
49
  self.loc_to_defs[codeloc] = OrderedSet()
42
50
  self.loc_to_defs[codeloc].add(stmt.dst)
@@ -52,7 +60,7 @@ class SimEngineSSATraversal(
52
60
 
53
61
  if self.stackvars and isinstance(stmt.addr, StackBaseOffset) and isinstance(stmt.addr.offset, int):
54
62
  codeloc = self._codeloc()
55
- self.def_to_loc[stmt] = codeloc
63
+ self.def_to_loc.append((stmt, codeloc))
56
64
  if codeloc not in self.loc_to_defs:
57
65
  self.loc_to_defs[codeloc] = OrderedSet()
58
66
  self.loc_to_defs[codeloc].add(stmt)
@@ -69,7 +77,7 @@ class SimEngineSSATraversal(
69
77
  def _handle_Call(self, stmt: Call):
70
78
  if stmt.ret_expr is not None and isinstance(stmt.ret_expr, Register):
71
79
  codeloc = self._codeloc()
72
- self.def_to_loc[stmt.ret_expr] = codeloc
80
+ self.def_to_loc.append((stmt.ret_expr, codeloc))
73
81
  if codeloc not in self.loc_to_defs:
74
82
  self.loc_to_defs[codeloc] = OrderedSet()
75
83
  self.loc_to_defs[codeloc].add(stmt.ret_expr)
@@ -79,18 +87,30 @@ class SimEngineSSATraversal(
79
87
 
80
88
  super()._ail_handle_Call(stmt)
81
89
 
90
+ _handle_CallExpr = _handle_Call
91
+
82
92
  def _handle_Register(self, expr: Register):
83
93
  base_offset = get_reg_offset_base(expr.reg_offset, self.arch)
84
94
 
85
95
  if base_offset not in self.state.live_registers:
86
96
  codeloc = self._codeloc()
87
- self.def_to_loc[expr] = codeloc
97
+ self.def_to_loc.append((expr, codeloc))
88
98
  if codeloc not in self.loc_to_defs:
89
99
  self.loc_to_defs[codeloc] = OrderedSet()
90
100
  self.loc_to_defs[codeloc].add(expr)
91
101
 
92
102
  self.state.live_registers.add(base_offset)
93
103
 
104
+ def _handle_Tmp(self, expr: Tmp):
105
+ if self.tmps:
106
+ codeloc = self._codeloc()
107
+ self.def_to_loc.append((expr, codeloc))
108
+ if codeloc not in self.loc_to_defs:
109
+ self.loc_to_defs[codeloc] = OrderedSet()
110
+ self.loc_to_defs[codeloc].add(expr)
111
+
112
+ self.state.live_tmps.add(expr.tmp_idx)
113
+
94
114
  def _handle_Cmp(self, expr: BinaryOp):
95
115
  self._expr(expr.operands[0])
96
116
  self._expr(expr.operands[1])
@@ -123,9 +143,16 @@ class SimEngineSSATraversal(
123
143
  for operand in expr.operands:
124
144
  self._expr(operand)
125
145
 
146
+ def _handle_DirtyExpression(self, expr: DirtyExpression):
147
+ for operand in expr.operands:
148
+ self._expr(operand)
149
+ if expr.guard is not None:
150
+ self._expr(expr.guard)
151
+ if expr.maddr is not None:
152
+ self._expr(expr.maddr)
153
+
126
154
  def _handle_Dummy(self, expr):
127
155
  pass
128
156
 
129
157
  _handle_VirtualVariable = _handle_Dummy
130
158
  _handle_Phi = _handle_Dummy
131
- _handle_DirtyExpression = _handle_Dummy
@@ -18,6 +18,7 @@ class TraversalState:
18
18
 
19
19
  self.live_registers: set[int] = set() if live_registers is None else live_registers
20
20
  self.live_stackvars: set[tuple[int, int]] = set() if live_stackvars is None else live_stackvars
21
+ self.live_tmps: set[int] = set() # tmps are internal to a block only and never propagated from another state
21
22
 
22
23
  def copy(self) -> TraversalState:
23
24
  return TraversalState(
@@ -1408,7 +1408,7 @@ class CUnsupportedStatement(CStatement):
1408
1408
  class CDirtyStatement(CExpression):
1409
1409
  __slots__ = ("dirty",)
1410
1410
 
1411
- def __init__(self, dirty, **kwargs):
1411
+ def __init__(self, dirty: CDirtyExpression, **kwargs):
1412
1412
  super().__init__(**kwargs)
1413
1413
  self.dirty = dirty
1414
1414
 
@@ -1420,7 +1420,7 @@ class CDirtyStatement(CExpression):
1420
1420
  indent_str = self.indent_str(indent=indent)
1421
1421
 
1422
1422
  yield indent_str, None
1423
- yield str(self.dirty), None
1423
+ yield from self.dirty.c_repr_chunks()
1424
1424
  yield "\n", None
1425
1425
 
1426
1426
 
@@ -2303,6 +2303,38 @@ class CMultiStatementExpression(CExpression):
2303
2303
  yield ")", paren
2304
2304
 
2305
2305
 
2306
+ class CVEXCCallExpression(CExpression):
2307
+ """
2308
+ ccall_name(arg0, arg1, ...)
2309
+ """
2310
+
2311
+ __slots__ = (
2312
+ "callee",
2313
+ "operands",
2314
+ "tags",
2315
+ )
2316
+
2317
+ def __init__(self, callee: str, operands: list[CExpression], tags=None, **kwargs):
2318
+ super().__init__(**kwargs)
2319
+ self.callee = callee
2320
+ self.operands = operands
2321
+ self.tags = tags
2322
+
2323
+ @property
2324
+ def type(self):
2325
+ return SimTypeInt().with_arch(self.codegen.project.arch)
2326
+
2327
+ def c_repr_chunks(self, indent=0, asexpr=False):
2328
+ paren = CClosingObject("(")
2329
+ yield f"{self.callee}", self
2330
+ yield "(", paren
2331
+ for idx, operand in enumerate(self.operands):
2332
+ if idx != 0:
2333
+ yield ", ", None
2334
+ yield from operand.c_repr_chunks()
2335
+ yield ")", paren
2336
+
2337
+
2306
2338
  class CDirtyExpression(CExpression):
2307
2339
  """
2308
2340
  Ideally all dirty expressions should be handled and converted to proper conversions during conversion from VEX to
@@ -2424,6 +2456,7 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
2424
2456
  Expr.BinaryOp: self._handle_Expr_BinaryOp,
2425
2457
  Expr.Convert: self._handle_Expr_Convert,
2426
2458
  Expr.StackBaseOffset: self._handle_Expr_StackBaseOffset,
2459
+ Expr.VEXCCallExpression: self._handle_Expr_VEXCCallExpression,
2427
2460
  Expr.DirtyExpression: self._handle_Expr_Dirty,
2428
2461
  Expr.ITE: self._handle_Expr_ITE,
2429
2462
  Expr.Reinterpret: self._handle_Reinterpret,
@@ -3318,7 +3351,8 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3318
3351
  return clabel
3319
3352
 
3320
3353
  def _handle_Stmt_Dirty(self, stmt: Stmt.DirtyStatement, **kwargs):
3321
- return CDirtyStatement(stmt, codegen=self)
3354
+ dirty = self._handle(stmt.dirty)
3355
+ return CDirtyStatement(dirty, codegen=self)
3322
3356
 
3323
3357
  #
3324
3358
  # AIL expression handlers
@@ -3519,7 +3553,11 @@ class CStructuredCodeGenerator(BaseStructuredCodeGenerator, Analysis):
3519
3553
 
3520
3554
  return CTypeCast(None, dst_type.with_arch(self.project.arch), child, tags=expr.tags, codegen=self)
3521
3555
 
3522
- def _handle_Expr_Dirty(self, expr, **kwargs):
3556
+ def _handle_Expr_VEXCCallExpression(self, expr: Expr.VEXCCallExpression, **kwargs):
3557
+ operands = [self._handle(arg) for arg in expr.operands]
3558
+ return CVEXCCallExpression(expr.callee, operands, tags=expr.tags, codegen=self)
3559
+
3560
+ def _handle_Expr_Dirty(self, expr: Expr.DirtyExpression, **kwargs):
3523
3561
  return CDirtyExpression(expr, codegen=self)
3524
3562
 
3525
3563
  def _handle_Expr_ITE(self, expr: Expr.ITE, **kwargs):
@@ -566,6 +566,9 @@ class PhoenixStructurer(StructurerBase):
566
566
 
567
567
  if next_node is node:
568
568
  break
569
+ if next_node is head:
570
+ # we don't want a loop with region head not as the first node of the body!
571
+ return False, None
569
572
  if next_node is not node and next_node in seen_nodes:
570
573
  return False, None
571
574