angr 9.2.131__py3-none-win_amd64.whl → 9.2.132__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 (112) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/analysis.py +6 -2
  3. angr/analyses/cfg/cfg_emulated.py +5 -5
  4. angr/analyses/cfg/cfg_fast.py +2 -2
  5. angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +139 -94
  6. angr/analyses/cfg/indirect_jump_resolvers/x86_elf_pic_plt.py +1 -1
  7. angr/analyses/ddg.py +14 -11
  8. angr/analyses/decompiler/ail_simplifier.py +3 -2
  9. angr/analyses/decompiler/block_simplifier.py +10 -21
  10. angr/analyses/decompiler/clinic.py +108 -34
  11. angr/analyses/decompiler/condition_processor.py +12 -10
  12. angr/analyses/decompiler/dephication/graph_rewriting.py +1 -1
  13. angr/analyses/decompiler/dephication/rewriting_engine.py +169 -45
  14. angr/analyses/decompiler/dephication/seqnode_dephication.py +5 -4
  15. angr/analyses/decompiler/optimization_passes/const_derefs.py +1 -0
  16. angr/analyses/decompiler/optimization_passes/div_simplifier.py +41 -16
  17. angr/analyses/decompiler/optimization_passes/engine_base.py +261 -83
  18. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +173 -35
  19. angr/analyses/decompiler/optimization_passes/mod_simplifier.py +5 -2
  20. angr/analyses/decompiler/optimization_passes/optimization_pass.py +39 -19
  21. angr/analyses/decompiler/peephole_optimizations/remove_noop_conversions.py +2 -0
  22. angr/analyses/decompiler/ssailification/rewriting.py +1 -2
  23. angr/analyses/decompiler/ssailification/rewriting_engine.py +138 -55
  24. angr/analyses/decompiler/ssailification/ssailification.py +2 -1
  25. angr/analyses/decompiler/ssailification/traversal.py +4 -6
  26. angr/analyses/decompiler/ssailification/traversal_engine.py +125 -42
  27. angr/analyses/decompiler/structured_codegen/c.py +5 -3
  28. angr/analyses/decompiler/structuring/phoenix.py +26 -9
  29. angr/analyses/decompiler/structuring/structurer_nodes.py +9 -0
  30. angr/analyses/deobfuscator/irsb_reg_collector.py +29 -60
  31. angr/analyses/deobfuscator/string_obf_finder.py +2 -2
  32. angr/analyses/init_finder.py +47 -22
  33. angr/analyses/propagator/engine_base.py +21 -14
  34. angr/analyses/propagator/engine_vex.py +149 -179
  35. angr/analyses/propagator/propagator.py +10 -28
  36. angr/analyses/propagator/top_checker_mixin.py +211 -5
  37. angr/analyses/propagator/vex_vars.py +1 -1
  38. angr/analyses/reaching_definitions/dep_graph.py +1 -1
  39. angr/analyses/reaching_definitions/engine_ail.py +304 -329
  40. angr/analyses/reaching_definitions/engine_vex.py +243 -229
  41. angr/analyses/reaching_definitions/function_handler.py +3 -3
  42. angr/analyses/reaching_definitions/rd_state.py +37 -32
  43. angr/analyses/s_propagator.py +18 -3
  44. angr/analyses/s_reaching_definitions/s_reaching_definitions.py +9 -5
  45. angr/analyses/typehoon/simple_solver.py +7 -5
  46. angr/analyses/typehoon/translator.py +8 -0
  47. angr/analyses/typehoon/typeconsts.py +10 -2
  48. angr/analyses/typehoon/typevars.py +9 -7
  49. angr/analyses/variable_recovery/engine_ail.py +299 -259
  50. angr/analyses/variable_recovery/engine_base.py +135 -117
  51. angr/analyses/variable_recovery/engine_vex.py +175 -185
  52. angr/analyses/variable_recovery/irsb_scanner.py +49 -38
  53. angr/analyses/variable_recovery/variable_recovery.py +28 -5
  54. angr/analyses/variable_recovery/variable_recovery_base.py +32 -33
  55. angr/analyses/variable_recovery/variable_recovery_fast.py +2 -2
  56. angr/analyses/xrefs.py +46 -19
  57. angr/annocfg.py +19 -14
  58. angr/block.py +4 -9
  59. angr/calling_conventions.py +1 -1
  60. angr/engines/engine.py +30 -14
  61. angr/engines/light/__init__.py +11 -3
  62. angr/engines/light/engine.py +1003 -1185
  63. angr/engines/pcode/cc.py +2 -0
  64. angr/engines/successors.py +13 -9
  65. angr/engines/vex/claripy/datalayer.py +1 -1
  66. angr/engines/vex/claripy/irop.py +1 -1
  67. angr/engines/vex/light/slicing.py +2 -2
  68. angr/exploration_techniques/__init__.py +1 -124
  69. angr/exploration_techniques/base.py +126 -0
  70. angr/exploration_techniques/bucketizer.py +1 -1
  71. angr/exploration_techniques/dfs.py +3 -1
  72. angr/exploration_techniques/director.py +2 -3
  73. angr/exploration_techniques/driller_core.py +1 -1
  74. angr/exploration_techniques/explorer.py +4 -2
  75. angr/exploration_techniques/lengthlimiter.py +2 -1
  76. angr/exploration_techniques/local_loop_seer.py +2 -1
  77. angr/exploration_techniques/loop_seer.py +5 -5
  78. angr/exploration_techniques/manual_mergepoint.py +2 -1
  79. angr/exploration_techniques/memory_watcher.py +3 -1
  80. angr/exploration_techniques/oppologist.py +4 -5
  81. angr/exploration_techniques/slicecutor.py +4 -2
  82. angr/exploration_techniques/spiller.py +1 -1
  83. angr/exploration_techniques/stochastic.py +2 -1
  84. angr/exploration_techniques/stub_stasher.py +2 -1
  85. angr/exploration_techniques/suggestions.py +3 -1
  86. angr/exploration_techniques/symbion.py +3 -1
  87. angr/exploration_techniques/tech_builder.py +2 -1
  88. angr/exploration_techniques/threading.py +4 -7
  89. angr/exploration_techniques/timeout.py +4 -2
  90. angr/exploration_techniques/tracer.py +4 -3
  91. angr/exploration_techniques/unique.py +3 -2
  92. angr/exploration_techniques/veritesting.py +1 -1
  93. angr/knowledge_plugins/key_definitions/atoms.py +2 -2
  94. angr/knowledge_plugins/key_definitions/live_definitions.py +16 -13
  95. angr/knowledge_plugins/propagations/states.py +13 -8
  96. angr/knowledge_plugins/variables/variable_manager.py +23 -9
  97. angr/lib/angr_native.dll +0 -0
  98. angr/sim_manager.py +1 -3
  99. angr/sim_state.py +39 -41
  100. angr/sim_type.py +5 -0
  101. angr/sim_variable.py +29 -28
  102. angr/utils/bits.py +12 -0
  103. angr/utils/orderedset.py +4 -1
  104. angr/utils/ssa/__init__.py +21 -3
  105. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/METADATA +6 -6
  106. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/RECORD +110 -111
  107. angr/analyses/propagator/engine_ail.py +0 -1562
  108. angr/storage/memory_mixins/__init__.pyi +0 -48
  109. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/LICENSE +0 -0
  110. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/WHEEL +0 -0
  111. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/entry_points.txt +0 -0
  112. {angr-9.2.131.dist-info → angr-9.2.132.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,14 @@
1
- # pylint:disable=arguments-renamed,too-many-boolean-expressions,no-self-use
1
+ # pylint:disable=arguments-renamed,too-many-boolean-expressions,no-self-use,unused-argument
2
2
  from __future__ import annotations
3
3
  from typing import Any
4
+ from collections.abc import Callable
4
5
  from collections import defaultdict
5
6
 
6
7
  from archinfo import Endness
7
8
  from ailment.expression import (
8
9
  Const,
9
10
  Register,
10
- Load,
11
+ Expression,
11
12
  StackBaseOffset,
12
13
  Convert,
13
14
  BinaryOp,
@@ -19,7 +20,8 @@ from ailment.expression import (
19
20
  from ailment.statement import ConditionalJump, Jump, Assignment
20
21
  import claripy
21
22
 
22
- from angr.engines.light import SimEngineLightAILMixin
23
+ from angr.utils.bits import zeroextend_on_demand
24
+ from angr.engines.light import SimEngineNostmtAIL
23
25
  from angr.storage.memory_mixins import (
24
26
  SimpleInterfaceMixin,
25
27
  DefaultFillerMixin,
@@ -31,6 +33,19 @@ from angr.errors import SimMemoryMissingError
31
33
  from .optimization_pass import OptimizationPass, OptimizationPassStage
32
34
 
33
35
 
36
+ def _make_binop(compute: Callable[[claripy.ast.BV, claripy.ast.BV], claripy.ast.BV]):
37
+ def inner(self, expr: BinaryOp) -> claripy.ast.BV | None:
38
+ a, b = self._expr(expr.operands[0]), self._expr(expr.operands[1])
39
+ if a is None or b is None:
40
+ return None
41
+ try:
42
+ return compute(a, b)
43
+ except (ZeroDivisionError, claripy.ClaripyZeroDivisionError):
44
+ return None
45
+
46
+ return inner
47
+
48
+
34
49
  class FasterMemory(
35
50
  SimpleInterfaceMixin,
36
51
  DefaultFillerMixin,
@@ -61,12 +76,12 @@ class InlinedStringTransformationState:
61
76
  def _get_weakref(self):
62
77
  return self
63
78
 
64
- def reg_store(self, reg: Register, value: claripy.ast.Bits) -> None:
79
+ def reg_store(self, reg: Register, value: claripy.ast.BV) -> None:
65
80
  self.registers.store(
66
81
  reg.reg_offset, value, size=value.size() // self.arch.byte_width, endness=str(self.arch.register_endness)
67
82
  )
68
83
 
69
- def reg_load(self, reg: Register) -> claripy.ast.Bits | None:
84
+ def reg_load(self, reg: Register) -> claripy.ast.BV | None:
70
85
  try:
71
86
  return self.registers.load(
72
87
  reg.reg_offset, size=reg.size, endness=self.arch.register_endness, fill_missing=False
@@ -77,7 +92,7 @@ class InlinedStringTransformationState:
77
92
  def mem_store(self, addr: int, value: claripy.ast.Bits, endness: str) -> None:
78
93
  self.memory.store(addr, value, size=value.size() // self.arch.byte_width, endness=endness)
79
94
 
80
- def mem_load(self, addr: int, size: int, endness) -> claripy.ast.Bits | None:
95
+ def mem_load(self, addr: int, size: int, endness) -> claripy.ast.BV | None:
81
96
  try:
82
97
  return self.memory.load(addr, size=size, endness=str(endness), fill_missing=False)
83
98
  except SimMemoryMissingError:
@@ -86,21 +101,21 @@ class InlinedStringTransformationState:
86
101
  def vvar_store(self, vvar: VirtualVariable, value: claripy.ast.Bits | None) -> None:
87
102
  self.virtual_variables[vvar.varid] = value
88
103
 
89
- def vvar_load(self, vvar: VirtualVariable) -> claripy.ast.Bits | None:
104
+ def vvar_load(self, vvar: VirtualVariable) -> claripy.ast.BV | None:
90
105
  if vvar.varid in self.virtual_variables:
91
106
  return self.virtual_variables[vvar.varid]
92
107
  return None
93
108
 
94
109
 
95
- class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
110
+ class InlinedStringTransformationAILEngine(
111
+ SimEngineNostmtAIL[InlinedStringTransformationState, claripy.ast.BV | None, None, None],
112
+ ):
96
113
  """
97
114
  A simple AIL execution engine
98
115
  """
99
116
 
100
117
  def __init__(self, project, nodes: dict[int, Any], start: int, end: int, step_limit: int):
101
- super().__init__()
102
-
103
- self.arch = project.arch
118
+ super().__init__(project)
104
119
  self.nodes: dict[int, Any] = nodes
105
120
  self.start: int = start
106
121
  self.end: int = end
@@ -121,7 +136,7 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
121
136
  # jumped to a node that we do not know about
122
137
  break
123
138
  block = self.nodes[self.pc]
124
- self._process(state, None, block=block)
139
+ self._process(state, block=block, whitelist=None)
125
140
  if self.pc is None:
126
141
  # not sure where to jump...
127
142
  break
@@ -131,8 +146,18 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
131
146
  break
132
147
  i += 1
133
148
 
134
- def _process_address(self, addr: Const | StackBaseOffset) -> tuple[int, str] | None:
149
+ def _top(self, bits):
150
+ assert False, "Should not be reachable"
151
+
152
+ def _is_top(self, expr):
153
+ assert False, "Should not be reachable"
154
+
155
+ def _process_block_end(self, block, stmt_data, whitelist):
156
+ pass
157
+
158
+ def _process_address(self, addr: Expression) -> tuple[int, str] | None:
135
159
  if isinstance(addr, Const):
160
+ assert isinstance(addr.value, int)
136
161
  return addr.value, "mem"
137
162
  if isinstance(addr, StackBaseOffset):
138
163
  return (addr.offset + self.STACK_BASE) & self.MASK, "stack"
@@ -145,7 +170,7 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
145
170
  return (v0 + v1.concrete_value) & self.MASK, "stack"
146
171
  return None
147
172
 
148
- def _handle_Assignment(self, stmt):
173
+ def _handle_stmt_Assignment(self, stmt):
149
174
  if isinstance(stmt.dst, VirtualVariable):
150
175
  if stmt.dst.was_reg:
151
176
  val = self._expr(stmt.src)
@@ -163,7 +188,7 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
163
188
  byte_off = val.size() // self.arch.byte_width - i - 1
164
189
  self.stack_accesses[addr + i].append(("store", self._codeloc(), val.get_byte(byte_off)))
165
190
 
166
- def _handle_Store(self, stmt):
191
+ def _handle_stmt_Store(self, stmt):
167
192
  addr_and_type = self._process_address(stmt.addr)
168
193
  if addr_and_type is not None:
169
194
  addr, addr_type = addr_and_type
@@ -178,14 +203,14 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
178
203
  byte_off = val.size() // self.arch.byte_width - i - 1
179
204
  self.stack_accesses[addr + i].append(("store", self._codeloc(), val.get_byte(byte_off)))
180
205
 
181
- def _handle_Jump(self, stmt):
206
+ def _handle_stmt_Jump(self, stmt):
182
207
  self.last_pc = self.pc
183
208
  if isinstance(stmt.target, Const):
184
209
  self.pc = stmt.target.value
185
210
  else:
186
211
  self.pc = None
187
212
 
188
- def _handle_ConditionalJump(self, stmt):
213
+ def _handle_stmt_ConditionalJump(self, stmt):
189
214
  self.last_pc = self.pc
190
215
  self.pc = None
191
216
  if isinstance(stmt.true_target, Const) and isinstance(stmt.false_target, Const):
@@ -196,10 +221,12 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
196
221
  elif isinstance(cond, claripy.ast.Bits) and cond.concrete_value == 0:
197
222
  self.pc = stmt.false_target.value
198
223
 
199
- def _handle_Const(self, expr):
200
- return claripy.BVV(expr.value, expr.bits)
224
+ def _handle_expr_Const(self, expr):
225
+ if isinstance(expr.value, int):
226
+ return claripy.BVV(expr.value, expr.bits)
227
+ return None
201
228
 
202
- def _handle_Load(self, expr: Load):
229
+ def _handle_expr_Load(self, expr):
203
230
  addr_and_type = self._process_address(expr.addr)
204
231
  if addr_and_type is not None:
205
232
  addr, addr_type = addr_and_type
@@ -214,14 +241,14 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
214
241
  return v
215
242
  return None
216
243
 
217
- def _handle_Register(self, expr: Register):
244
+ def _handle_expr_Register(self, expr: Register):
218
245
  return self.state.reg_load(expr)
219
246
 
220
- def _handle_VirtualVariable(self, expr: VirtualVariable):
247
+ def _handle_expr_VirtualVariable(self, expr: VirtualVariable):
221
248
  if expr.was_stack:
222
249
  addr = (expr.stack_offset + self.STACK_BASE) & self.MASK
223
250
  v = self.state.mem_load(addr, expr.size, self.arch.memory_endness)
224
- if isinstance(v, claripy.ast.Bits):
251
+ if v is not None:
225
252
  # log it
226
253
  for i in range(expr.size):
227
254
  byte_off = i
@@ -233,25 +260,43 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
233
260
  return self.state.vvar_load(expr)
234
261
  return None
235
262
 
236
- def _handle_Phi(self, expr: Phi):
263
+ def _handle_expr_Phi(self, expr: Phi):
237
264
  for src, vvar in expr.src_and_vvars:
238
265
  if src[0] == self.last_pc and vvar is not None:
239
266
  return self.state.vvar_load(vvar)
240
267
  return None
241
268
 
242
- def _handle_Neg(self, expr: UnaryOp):
269
+ def _handle_unop_Neg(self, expr: UnaryOp):
243
270
  v = self._expr(expr.operand)
244
271
  if isinstance(v, claripy.ast.Bits):
245
272
  return -v
246
273
  return None
247
274
 
248
- def _handle_BitwiseNeg(self, expr: UnaryOp):
275
+ def _handle_unop_Not(self, expr: UnaryOp):
249
276
  v = self._expr(expr.operand)
250
277
  if isinstance(v, claripy.ast.Bits):
251
278
  return ~v
252
279
  return None
253
280
 
254
- def _handle_Convert(self, expr: Convert):
281
+ def _handle_unop_BitwiseNeg(self, expr: UnaryOp):
282
+ v = self._expr(expr.operand)
283
+ if isinstance(v, claripy.ast.Bits):
284
+ return ~v
285
+ return None
286
+
287
+ def _handle_unop_Default(self, expr: UnaryOp):
288
+ return None
289
+
290
+ _handle_unop_Clz = _handle_unop_Default
291
+ _handle_unop_Ctz = _handle_unop_Default
292
+ _handle_unop_Dereference = _handle_unop_Default
293
+ _handle_unop_Reference = _handle_unop_Default
294
+ _handle_unop_GetMSBs = _handle_unop_Default
295
+ _handle_unop_unpack = _handle_unop_Default
296
+ _handle_unop_Sqrt = _handle_unop_Default
297
+ _handle_unop_RSqrtEst = _handle_unop_Default
298
+
299
+ def _handle_expr_Convert(self, expr: Convert):
255
300
  v = self._expr(expr.operand)
256
301
  if isinstance(v, claripy.ast.Bits):
257
302
  if expr.to_bits > expr.from_bits:
@@ -263,48 +308,139 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
263
308
  return v
264
309
  return None
265
310
 
266
- def _handle_CmpEQ(self, expr):
311
+ def _handle_binop_CmpEQ(self, expr):
267
312
  op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
268
313
  if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
269
314
  return claripy.BVV(1, 1) if op0.concrete_value == op1.concrete_value else claripy.BVV(0, 1)
270
315
  return None
271
316
 
272
- def _handle_CmpNE(self, expr):
317
+ def _handle_binop_CmpNE(self, expr):
273
318
  op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
274
319
  if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
275
320
  return claripy.BVV(1, 1) if op0.concrete_value != op1.concrete_value else claripy.BVV(0, 1)
276
321
  return None
277
322
 
278
- def _handle_CmpLT(self, expr):
323
+ def _handle_binop_CmpLT(self, expr):
279
324
  op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
280
325
  if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
281
326
  return claripy.BVV(1, 1) if op0.concrete_value < op1.concrete_value else claripy.BVV(0, 1)
282
327
  return None
283
328
 
284
- def _handle_CmpLE(self, expr):
329
+ def _handle_binop_CmpLE(self, expr):
285
330
  op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
286
331
  if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
287
332
  return claripy.BVV(1, 1) if op0.concrete_value <= op1.concrete_value else claripy.BVV(0, 1)
288
333
  return None
289
334
 
290
- def _handle_CmpGT(self, expr):
335
+ def _handle_binop_CmpGT(self, expr):
291
336
  op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
292
337
  if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
293
338
  return claripy.BVV(1, 1) if op0.concrete_value > op1.concrete_value else claripy.BVV(0, 1)
294
339
  return None
295
340
 
296
- def _handle_CmpGE(self, expr):
341
+ def _handle_binop_CmpGE(self, expr):
297
342
  op0, op1 = self._expr(expr.operands[0]), self._expr(expr.operands[1])
298
343
  if isinstance(op0, claripy.ast.Bits) and isinstance(op1, claripy.ast.Bits) and op0.concrete and op1.concrete:
299
344
  return claripy.BVV(1, 1) if op0.concrete_value >= op1.concrete_value else claripy.BVV(0, 1)
300
345
  return None
301
346
 
302
- def _handle_Call(self, stmt):
347
+ def _handle_stmt_Call(self, stmt):
303
348
  pass
304
349
 
305
- def _ail_handle_CallExpr(self, expr):
350
+ def _handle_expr_Call(self, expr):
306
351
  pass
307
352
 
353
+ def _handle_expr_BasePointerOffset(self, expr):
354
+ return None
355
+
356
+ def _handle_expr_DirtyExpression(self, expr):
357
+ return None
358
+
359
+ def _handle_expr_ITE(self, expr):
360
+ return None
361
+
362
+ def _handle_expr_MultiStatementExpression(self, expr):
363
+ return None
364
+
365
+ def _handle_expr_Reinterpret(self, expr):
366
+ return None
367
+
368
+ def _handle_expr_StackBaseOffset(self, expr):
369
+ return None
370
+
371
+ def _handle_expr_Tmp(self, expr):
372
+ try:
373
+ return self.tmps[expr.tmp_idx]
374
+ except KeyError:
375
+ return None
376
+
377
+ def _handle_expr_VEXCCallExpression(self, expr):
378
+ return None
379
+
380
+ def _handle_binop_Default(self, expr):
381
+ self._expr(expr.operands[0])
382
+ self._expr(expr.operands[1])
383
+
384
+ _handle_binop_Add = _make_binop(lambda a, b: a + b)
385
+ _handle_binop_And = _make_binop(lambda a, b: a & b)
386
+ _handle_binop_Concat = _make_binop(lambda a, b: a.concat(b))
387
+ _handle_binop_Div = _make_binop(lambda a, b: a // b)
388
+ _handle_binop_LogicalAnd = _make_binop(lambda a, b: a & b)
389
+ _handle_binop_LogicalOr = _make_binop(lambda a, b: a | b)
390
+ _handle_binop_Mod = _make_binop(lambda a, b: a % b)
391
+ _handle_binop_Mul = _make_binop(lambda a, b: a * b)
392
+ _handle_binop_Or = _make_binop(lambda a, b: a | b)
393
+ _handle_binop_Rol = _make_binop(lambda a, b: claripy.RotateLeft(a, zeroextend_on_demand(a, b)))
394
+ _handle_binop_Ror = _make_binop(lambda a, b: claripy.RotateRight(a, zeroextend_on_demand(a, b)))
395
+ _handle_binop_Sar = _make_binop(lambda a, b: a >> zeroextend_on_demand(a, b))
396
+ _handle_binop_Shl = _make_binop(lambda a, b: a << zeroextend_on_demand(a, b))
397
+ _handle_binop_Shr = _make_binop(lambda a, b: a.LShR(zeroextend_on_demand(a, b)))
398
+ _handle_binop_Sub = _make_binop(lambda a, b: a - b)
399
+ _handle_binop_Xor = _make_binop(lambda a, b: a ^ b)
400
+
401
+ def _handle_binop_Mull(self, expr):
402
+ a, b = self._expr(expr.operands[0]), self._expr(expr.operands[1])
403
+ if a is None or b is None:
404
+ return None
405
+ xt = a.size()
406
+ if expr.signed:
407
+ return a.sign_extend(xt) * b.sign_extend(xt)
408
+ return a.zero_extend(xt) * b.zero_extend(xt)
409
+
410
+ _handle_binop_AddF = _handle_binop_Default
411
+ _handle_binop_AddV = _handle_binop_Default
412
+ _handle_binop_Carry = _handle_binop_Default
413
+ _handle_binop_CmpF = _handle_binop_Default
414
+ _handle_binop_DivF = _handle_binop_Default
415
+ _handle_binop_DivV = _handle_binop_Default
416
+ _handle_binop_InterleaveLOV = _handle_binop_Default
417
+ _handle_binop_InterleaveHIV = _handle_binop_Default
418
+ _handle_binop_CasCmpEQ = _handle_binop_Default
419
+ _handle_binop_CasCmpNE = _handle_binop_Default
420
+ _handle_binop_ExpCmpNE = _handle_binop_Default
421
+ _handle_binop_SarNV = _handle_binop_Default
422
+ _handle_binop_ShrNV = _handle_binop_Default
423
+ _handle_binop_ShlNV = _handle_binop_Default
424
+ _handle_binop_CmpEQV = _handle_binop_Default
425
+ _handle_binop_CmpNEV = _handle_binop_Default
426
+ _handle_binop_CmpGEV = _handle_binop_Default
427
+ _handle_binop_CmpGTV = _handle_binop_Default
428
+ _handle_binop_CmpLEV = _handle_binop_Default
429
+ _handle_binop_CmpLTV = _handle_binop_Default
430
+ _handle_binop_MulF = _handle_binop_Default
431
+ _handle_binop_MulV = _handle_binop_Default
432
+ _handle_binop_MulHiV = _handle_binop_Default
433
+ _handle_binop_SBorrow = _handle_binop_Default
434
+ _handle_binop_SCarry = _handle_binop_Default
435
+ _handle_binop_SubF = _handle_binop_Default
436
+ _handle_binop_SubV = _handle_binop_Default
437
+ _handle_binop_MinV = _handle_binop_Default
438
+ _handle_binop_MaxV = _handle_binop_Default
439
+ _handle_binop_QAddV = _handle_binop_Default
440
+ _handle_binop_QNarrowBinV = _handle_binop_Default
441
+ _handle_binop_PermV = _handle_binop_Default
442
+ _handle_binop_Set = _handle_binop_Default
443
+
308
444
 
309
445
  class InlineStringTransformationDescriptor:
310
446
  """
@@ -387,6 +523,7 @@ class InlinedStringTransformationSimplifier(OptimizationPass):
387
523
 
388
524
  # remote the loop node
389
525
  # since the loop node has exactly one external predecessor and one external successor, we can get rid of it
526
+ assert self.out_graph is not None
390
527
  pred = next(iter(nn for nn in self.out_graph.predecessors(desc.loop_body) if nn is not desc.loop_body))
391
528
  succ = next(iter(nn for nn in self.out_graph.successors(desc.loop_body) if nn is not desc.loop_body))
392
529
 
@@ -404,6 +541,7 @@ class InlinedStringTransformationSimplifier(OptimizationPass):
404
541
  def _find_string_transformation_loops(self):
405
542
  # find self loops
406
543
  self_loops = []
544
+ assert self._graph is not None
407
545
  for node in self._graph.nodes:
408
546
  preds = list(self._graph.predecessors(node))
409
547
  succs = list(self._graph.successors(node))
@@ -12,7 +12,7 @@ _l.addFilter(UniqueLogFilter())
12
12
 
13
13
 
14
14
  class ModSimplifierAILEngine(SimplifierAILEngine):
15
- def _ail_handle_Sub(self, expr):
15
+ def _handle_binop_Sub(self, expr):
16
16
  operand_0 = self._expr(expr.operands[0])
17
17
  operand_1 = self._expr(expr.operands[1])
18
18
 
@@ -40,6 +40,8 @@ class ModSimplifierAILEngine(SimplifierAILEngine):
40
40
  x_1 = operand_0
41
41
  c_0 = operand_1.operands[1]
42
42
  c_1 = operand_1.operands[0].operand.operands[1]
43
+ else:
44
+ assert False, "Unreachable"
43
45
 
44
46
  if x_0 is not None and x_1 is not None and x_0.likes(x_1) and c_0.value == c_1.value:
45
47
  return Expr.BinaryOp(expr.idx, "Mod", [x_0, c_0], expr.signed, **expr.tags)
@@ -70,7 +72,7 @@ class ModSimplifier(OptimizationPass):
70
72
  super().__init__(func, **kwargs)
71
73
 
72
74
  self.state = SimplifierAILState(self.project.arch)
73
- self.engine = ModSimplifierAILEngine()
75
+ self.engine = ModSimplifierAILEngine(self.project)
74
76
 
75
77
  self.analyze()
76
78
 
@@ -78,6 +80,7 @@ class ModSimplifier(OptimizationPass):
78
80
  return True, None
79
81
 
80
82
  def _analyze(self, cache=None):
83
+ assert self._graph is not None
81
84
  for block in list(self._graph.nodes()):
82
85
  new_block = block
83
86
  old_block = None
@@ -5,15 +5,17 @@ from typing import Any, TYPE_CHECKING
5
5
  from collections.abc import Generator
6
6
  from enum import Enum
7
7
 
8
- import networkx # pylint:disable=unused-import
8
+ import networkx
9
+
9
10
  import ailment
10
11
 
11
12
  from angr.analyses.decompiler import RegionIdentifier
12
13
  from angr.analyses.decompiler.condition_processor import ConditionProcessor
13
- from angr.analyses.decompiler.goto_manager import GotoManager
14
+ from angr.analyses.decompiler.goto_manager import Goto, GotoManager
14
15
  from angr.analyses.decompiler.structuring import RecursiveStructurer, SAILRStructurer
15
16
  from angr.analyses.decompiler.utils import add_labels
16
17
  from angr.analyses.decompiler.counters import ControlFlowStructureCounter
18
+ from angr.project import Project
17
19
 
18
20
  if TYPE_CHECKING:
19
21
  from angr.knowledge_plugins.functions import Function
@@ -60,8 +62,10 @@ class BaseOptimizationPass:
60
62
 
61
63
  ARCHES = [] # strings of supported architectures
62
64
  PLATFORMS = [] # strings of supported platforms. Can be one of the following: "win32", "linux"
63
- STAGE: int = None # Specifies when this optimization pass should be executed
64
- STRUCTURING: str | None = None # specifies if this optimization pass is specific to a certain structuring algorithm
65
+ STAGE: OptimizationPassStage # Specifies when this optimization pass should be executed
66
+ STRUCTURING: list[str] | None = (
67
+ None # specifies if this optimization pass is specific to a certain structuring algorithm
68
+ )
65
69
  NAME = "N/A"
66
70
  DESCRIPTION = "N/A"
67
71
 
@@ -69,7 +73,8 @@ class BaseOptimizationPass:
69
73
  self._func: Function = func
70
74
 
71
75
  @property
72
- def project(self):
76
+ def project(self) -> Project:
77
+ assert self._func.project is not None
73
78
  return self._func.project
74
79
 
75
80
  @property
@@ -115,7 +120,7 @@ class OptimizationPass(BaseOptimizationPass):
115
120
  variable_kb=None,
116
121
  region_identifier=None,
117
122
  reaching_definitions=None,
118
- vvar_id_start=None,
123
+ vvar_id_start: int = 0,
119
124
  entry_node_addr=None,
120
125
  scratch: dict[str, Any] | None = None,
121
126
  force_loop_single_exit: bool = True,
@@ -124,8 +129,8 @@ class OptimizationPass(BaseOptimizationPass):
124
129
  ):
125
130
  super().__init__(func)
126
131
  # self._blocks is just a cache
127
- self._blocks_by_addr: dict[int, set[ailment.Block]] = blocks_by_addr
128
- self._blocks_by_addr_and_idx: dict[tuple[int, int | None], ailment.Block] = blocks_by_addr_and_idx
132
+ self._blocks_by_addr: dict[int, set[ailment.Block]] = blocks_by_addr or {}
133
+ self._blocks_by_addr_and_idx: dict[tuple[int, int | None], ailment.Block] = blocks_by_addr_and_idx or {}
129
134
  self._graph: networkx.DiGraph | None = graph
130
135
  self._variable_kb = variable_kb
131
136
  self._ri = region_identifier
@@ -164,10 +169,23 @@ class OptimizationPass(BaseOptimizationPass):
164
169
  self._new_block_addrs.add(new_addr)
165
170
  return new_addr
166
171
 
167
- def _get_block(self, addr, idx=None) -> ailment.Block | None:
172
+ def _get_block(self, addr, **kwargs) -> ailment.Block | None:
173
+ """
174
+ Get exactly one block by its address and optionally, also considering its block ID. An exception,
175
+ MultipleBlocksException, will be raised if there are more than one block satisfying the specified criteria.
176
+
177
+ :param addr: The address of the block.
178
+ :param kwargs: Optionally, you can specify "idx" to consider the block ID. If "idx" is not specified, this
179
+ method will return the only block at the specified address, None if there is no block at
180
+ that address, or raise an exception if there are more than one block at that address.
181
+ :return: The requested block or None if no block matching the specified criteria exists.
182
+ """
183
+
168
184
  if not self._blocks_by_addr:
169
185
  return None
170
- if idx is None:
186
+ idx_specified = "idx" in kwargs
187
+ idx = kwargs.get("idx")
188
+ if not idx_specified:
171
189
  blocks = self._blocks_by_addr.get(addr, None)
172
190
  else:
173
191
  blocks = [self._blocks_by_addr_and_idx.get((addr, idx), None)]
@@ -175,8 +193,12 @@ class OptimizationPass(BaseOptimizationPass):
175
193
  return None
176
194
  if len(blocks) == 1:
177
195
  return next(iter(blocks))
196
+ if idx_specified:
197
+ raise MultipleBlocksException(
198
+ f"There are {len(blocks)} blocks at address {addr:#x}.{idx} but only one is requested."
199
+ )
178
200
  raise MultipleBlocksException(
179
- "There are %d blocks at address %#x.%s but only one is requested." % (len(blocks), addr, idx)
201
+ f"There are {len(blocks)} blocks at address {addr:#x} (block ID ignored) but only one is requested."
180
202
  )
181
203
 
182
204
  def _get_blocks(self, addr, idx=None) -> Generator[ailment.Block]:
@@ -195,6 +217,7 @@ class OptimizationPass(BaseOptimizationPass):
195
217
  def _update_block(self, old_block, new_block):
196
218
  if self.out_graph is None:
197
219
  self.out_graph = self._graph # we do not make copy here for performance reasons. we can change it if needed
220
+ assert self.out_graph is not None
198
221
 
199
222
  if old_block not in self.out_graph:
200
223
  return
@@ -220,6 +243,7 @@ class OptimizationPass(BaseOptimizationPass):
220
243
  def _remove_block(self, block):
221
244
  if self.out_graph is None:
222
245
  self.out_graph = self._graph
246
+ assert self.out_graph is not None
223
247
 
224
248
  if block in self.out_graph:
225
249
  self.out_graph.remove_node(block)
@@ -270,10 +294,6 @@ class SequenceOptimizationPass(BaseOptimizationPass):
270
294
  The base class for any sequence node optimization pass.
271
295
  """
272
296
 
273
- ARCHES = [] # strings of supported architectures
274
- PLATFORMS = [] # strings of supported platforms. Can be one of the following: "win32", "linux"
275
- STAGE: int = None # Specifies when this optimization pass should be executed
276
-
277
297
  def __init__(self, func, seq=None, **kwargs):
278
298
  super().__init__(func)
279
299
  self.seq = seq
@@ -298,6 +318,10 @@ class StructuringOptimizationPass(OptimizationPass):
298
318
  STRUCTURING = [SAILRStructurer.NAME]
299
319
  STAGE = OptimizationPassStage.DURING_REGION_IDENTIFICATION
300
320
 
321
+ _initial_gotos: set[Goto]
322
+ _goto_manager: GotoManager
323
+ _prev_graph: networkx.DiGraph
324
+
301
325
  def __init__(
302
326
  self,
303
327
  func,
@@ -321,10 +345,6 @@ class StructuringOptimizationPass(OptimizationPass):
321
345
  self._must_improve_rel_quality = must_improve_rel_quality
322
346
  self._readd_labels = readd_labels
323
347
 
324
- self._initial_gotos = None
325
- self._goto_manager: GotoManager | None = None
326
- self._prev_graph: networkx.DiGraph | None = None
327
-
328
348
  # relative quality metrics (excludes gotos)
329
349
  self._initial_structure_counter = None
330
350
  self._current_structure_counter = None
@@ -22,6 +22,7 @@ class RemoveNoopConversions(PeepholeOptimizationExprBase):
22
22
  and expr.to_bits < expr.from_bits
23
23
  and expr.from_bits == inner.to_bits
24
24
  and expr.is_signed == inner.is_signed
25
+ and expr.from_type == expr.to_type == inner.from_type == inner.to_type == Convert.TYPE_INT
25
26
  ):
26
27
  # extension then truncation (e.g., 1->64->1) can be removed, but truncation then extension cannot be
27
28
  # removed (e.g., the high 32 bits must be removed during 64->32->64)
@@ -31,6 +32,7 @@ class RemoveNoopConversions(PeepholeOptimizationExprBase):
31
32
  and expr.from_bits == inner.to_bits
32
33
  and inner.to_bits <= inner.from_bits
33
34
  and expr.is_signed == inner.is_signed
35
+ and expr.from_type == expr.to_type == inner.from_type == inner.to_type == Convert.TYPE_INT
34
36
  ):
35
37
  # merging two truncations into one
36
38
  return Convert(
@@ -56,8 +56,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
56
56
  self._rewrite_tmps = rewrite_tmps
57
57
  self._ail_manager = ail_manager
58
58
  self._engine_ail = SimEngineSSARewriting(
59
- self.project.arch,
60
- project=self.project,
59
+ self.project,
61
60
  sp_tracker=sp_tracker,
62
61
  bp_as_gpr=bp_as_gpr,
63
62
  udef_to_phiid=self._udef_to_phiid,