angr 9.2.148__py3-none-manylinux2014_aarch64.whl → 9.2.149__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 (55) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/calling_convention/calling_convention.py +42 -2
  3. angr/analyses/cfg/cfg_emulated.py +5 -2
  4. angr/analyses/cfg/cfg_fast.py +48 -46
  5. angr/analyses/decompiler/ail_simplifier.py +65 -32
  6. angr/analyses/decompiler/block_simplifier.py +20 -6
  7. angr/analyses/decompiler/clinic.py +80 -13
  8. angr/analyses/decompiler/dephication/rewriting_engine.py +24 -2
  9. angr/analyses/decompiler/optimization_passes/__init__.py +5 -0
  10. angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +15 -13
  11. angr/analyses/decompiler/optimization_passes/determine_load_sizes.py +64 -0
  12. angr/analyses/decompiler/optimization_passes/eager_std_string_concatenation.py +165 -0
  13. angr/analyses/decompiler/optimization_passes/engine_base.py +11 -2
  14. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +17 -2
  15. angr/analyses/decompiler/optimization_passes/optimization_pass.py +10 -6
  16. angr/analyses/decompiler/optimization_passes/win_stack_canary_simplifier.py +99 -30
  17. angr/analyses/decompiler/peephole_optimizations/__init__.py +6 -0
  18. angr/analyses/decompiler/peephole_optimizations/base.py +43 -3
  19. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +1 -1
  20. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy.py +3 -0
  21. angr/analyses/decompiler/peephole_optimizations/inlined_strcpy_consolidation.py +4 -1
  22. angr/analyses/decompiler/peephole_optimizations/remove_cxx_destructor_calls.py +32 -0
  23. angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +69 -2
  24. angr/analyses/decompiler/peephole_optimizations/rewrite_conv_mul.py +40 -0
  25. angr/analyses/decompiler/peephole_optimizations/rewrite_cxx_operator_calls.py +90 -0
  26. angr/analyses/decompiler/presets/fast.py +2 -0
  27. angr/analyses/decompiler/presets/full.py +2 -0
  28. angr/analyses/decompiler/ssailification/rewriting_engine.py +51 -4
  29. angr/analyses/decompiler/ssailification/ssailification.py +23 -3
  30. angr/analyses/decompiler/ssailification/traversal_engine.py +15 -1
  31. angr/analyses/decompiler/structured_codegen/c.py +141 -10
  32. angr/analyses/decompiler/utils.py +6 -1
  33. angr/analyses/s_reaching_definitions/s_rda_view.py +1 -0
  34. angr/analyses/typehoon/lifter.py +20 -0
  35. angr/analyses/typehoon/simple_solver.py +42 -9
  36. angr/analyses/typehoon/translator.py +4 -1
  37. angr/analyses/typehoon/typeconsts.py +17 -6
  38. angr/analyses/typehoon/typehoon.py +21 -5
  39. angr/analyses/variable_recovery/engine_ail.py +44 -5
  40. angr/analyses/variable_recovery/engine_base.py +35 -12
  41. angr/analyses/variable_recovery/variable_recovery_fast.py +33 -2
  42. angr/calling_conventions.py +23 -5
  43. angr/engines/light/engine.py +7 -0
  44. angr/knowledge_plugins/functions/function.py +68 -0
  45. angr/knowledge_plugins/propagations/states.py +5 -2
  46. angr/knowledge_plugins/variables/variable_manager.py +3 -3
  47. angr/procedures/definitions/__init__.py +1 -1
  48. angr/procedures/definitions/types_stl.py +22 -0
  49. angr/sim_type.py +251 -130
  50. {angr-9.2.148.dist-info → angr-9.2.149.dist-info}/METADATA +7 -7
  51. {angr-9.2.148.dist-info → angr-9.2.149.dist-info}/RECORD +55 -49
  52. {angr-9.2.148.dist-info → angr-9.2.149.dist-info}/WHEEL +1 -1
  53. {angr-9.2.148.dist-info → angr-9.2.149.dist-info}/licenses/LICENSE +3 -0
  54. {angr-9.2.148.dist-info → angr-9.2.149.dist-info}/entry_points.txt +0 -0
  55. {angr-9.2.148.dist-info → angr-9.2.149.dist-info}/top_level.txt +0 -0
@@ -161,13 +161,28 @@ class InlinedStringTransformationAILEngine(
161
161
  return addr.value, "mem"
162
162
  if isinstance(addr, StackBaseOffset):
163
163
  return (addr.offset + self.STACK_BASE) & self.MASK, "stack"
164
- if isinstance(addr, BinaryOp) and isinstance(addr.operands[0], StackBaseOffset):
164
+ if (
165
+ isinstance(addr, UnaryOp)
166
+ and addr.op == "Reference"
167
+ and isinstance(addr.operand, VirtualVariable)
168
+ and addr.operand.was_stack
169
+ ):
170
+ return (addr.operand.stack_offset + self.STACK_BASE) & self.MASK, "stack"
171
+ if (
172
+ isinstance(addr, BinaryOp)
173
+ and addr.op in {"Add", "Sub"}
174
+ and isinstance(addr.operands[0], (StackBaseOffset, UnaryOp, Const))
175
+ ):
165
176
  v0_and_type = self._process_address(addr.operands[0])
166
177
  if v0_and_type is not None:
167
178
  v0 = v0_and_type[0]
168
179
  v1 = self._expr(addr.operands[1])
169
180
  if isinstance(v1, claripy.ast.Bits) and v1.concrete:
170
- return (v0 + v1.concrete_value) & self.MASK, "stack"
181
+ if addr.op == "Add":
182
+ return (v0 + v1.concrete_value) & self.MASK, "stack"
183
+ if addr.op == "Sub":
184
+ return (v0 - v1.concrete_value) & self.MASK, "stack"
185
+ raise NotImplementedError("Unreachable")
171
186
  return None
172
187
 
173
188
  def _handle_stmt_Assignment(self, stmt):
@@ -53,12 +53,14 @@ class OptimizationPassStage(Enum):
53
53
  AFTER_AIL_GRAPH_CREATION = 0
54
54
  BEFORE_SSA_LEVEL0_TRANSFORMATION = 1
55
55
  AFTER_SINGLE_BLOCK_SIMPLIFICATION = 2
56
- AFTER_MAKING_CALLSITES = 3
57
- AFTER_GLOBAL_SIMPLIFICATION = 4
58
- AFTER_VARIABLE_RECOVERY = 5
59
- BEFORE_REGION_IDENTIFICATION = 6
60
- DURING_REGION_IDENTIFICATION = 7
61
- AFTER_STRUCTURING = 8
56
+ BEFORE_SSA_LEVEL1_TRANSFORMATION = 3
57
+ AFTER_MAKING_CALLSITES = 4
58
+ AFTER_GLOBAL_SIMPLIFICATION = 5
59
+ BEFORE_VARIABLE_RECOVERY = 6
60
+ AFTER_VARIABLE_RECOVERY = 7
61
+ BEFORE_REGION_IDENTIFICATION = 8
62
+ DURING_REGION_IDENTIFICATION = 9
63
+ AFTER_STRUCTURING = 10
62
64
 
63
65
 
64
66
  class BaseOptimizationPass:
@@ -137,6 +139,7 @@ class OptimizationPass(BaseOptimizationPass):
137
139
  avoid_vvar_ids: set[int] | None = None,
138
140
  arg_vvars: set[int] | None = None,
139
141
  peephole_optimizations=None,
142
+ stack_pointer_tracker=None,
140
143
  **kwargs,
141
144
  ):
142
145
  super().__init__(func)
@@ -158,6 +161,7 @@ class OptimizationPass(BaseOptimizationPass):
158
161
  self._complete_successors = complete_successors
159
162
  self._avoid_vvar_ids = avoid_vvar_ids or set()
160
163
  self._peephole_optimizations = peephole_optimizations
164
+ self._stack_pointer_tracker = stack_pointer_tracker
161
165
 
162
166
  # output
163
167
  self.out_graph: networkx.DiGraph | None = None
@@ -29,7 +29,7 @@ class WinStackCanarySimplifier(OptimizationPass):
29
29
  PLATFORMS = ["windows"]
30
30
  STAGE = OptimizationPassStage.AFTER_SINGLE_BLOCK_SIMPLIFICATION
31
31
  NAME = "Simplify stack canaries in Windows PE files"
32
- DESCRIPTION = __doc__.strip()
32
+ DESCRIPTION = __doc__.strip() # type:ignore
33
33
 
34
34
  def __init__(self, func, **kwargs):
35
35
  super().__init__(func, **kwargs)
@@ -63,19 +63,16 @@ class WinStackCanarySimplifier(OptimizationPass):
63
63
  first_block, canary_init_stmt_ids = init_stmts
64
64
  canary_init_stmt = first_block.statements[canary_init_stmt_ids[-1]]
65
65
  # where is the stack canary stored?
66
- if not isinstance(canary_init_stmt, ailment.Stmt.Store) or not isinstance(
67
- canary_init_stmt.addr, ailment.Expr.StackBaseOffset
68
- ):
66
+ if not isinstance(canary_init_stmt, ailment.Stmt.Store):
67
+ return
68
+ store_offset = self._get_bp_offset(canary_init_stmt.addr, canary_init_stmt.ins_addr)
69
+ if store_offset is None:
69
70
  _l.debug(
70
- "Unsupported canary storing location %s. Expects an ailment.Expr.StackBaseOffset.",
71
+ "Unsupported canary storing location %s. Expects a StackBaseOffset or (bp - Const).",
71
72
  canary_init_stmt.addr,
72
73
  )
73
74
  return
74
75
 
75
- store_offset = canary_init_stmt.addr.offset
76
- if not isinstance(store_offset, int):
77
- _l.debug("Unsupported canary storing offset %s. Expects an int.", store_offset)
78
-
79
76
  # The function should have at least one end point calling _security_check_cookie
80
77
  # note that (at least for now) we rely on FLIRT to identify the _security_check_cookie function inside the
81
78
  # binary.
@@ -154,17 +151,42 @@ class WinStackCanarySimplifier(OptimizationPass):
154
151
  store_offset, canary_init_stmt.size, "canary", StackItemType.STACK_CANARY
155
152
  )
156
153
 
157
- def _find_canary_init_stmt(self):
154
+ def _find_canary_init_stmt(self) -> tuple[ailment.Block, list[int]] | None:
158
155
  first_block = self._get_block(self._func.addr)
159
156
  if first_block is None:
160
157
  return None
161
158
 
159
+ r = self._extract_canary_init_stmt_from_block(first_block)
160
+ if r is not None:
161
+ return first_block, r
162
+
163
+ # if the first block is calling alloca_probe, we may want to take the second block instead
164
+ if (
165
+ first_block.statements
166
+ and first_block.original_size > 0
167
+ and isinstance(first_block.statements[-1], ailment.statement.Call)
168
+ and isinstance(first_block.statements[-1].target, ailment.expression.Const)
169
+ ):
170
+ # check if the target is alloca_probe
171
+ callee_addr = first_block.statements[-1].target.value
172
+ if (
173
+ self.kb.functions.contains_addr(callee_addr)
174
+ and self.kb.functions.get_by_addr(callee_addr).info.get("is_alloca_probe", False) is True
175
+ ):
176
+ second_block = self._get_block(first_block.addr + first_block.original_size)
177
+ if second_block is not None:
178
+ r = self._extract_canary_init_stmt_from_block(second_block)
179
+ if r is not None:
180
+ return second_block, r
181
+ return None
182
+
183
+ def _extract_canary_init_stmt_from_block(self, block: ailment.Block) -> list[int] | None:
162
184
  load_stmt_idx = None
163
185
  load_reg = None
164
186
  xor_stmt_idx = None
165
187
  xored_reg = None
166
188
 
167
- for idx, stmt in enumerate(first_block.statements):
189
+ for idx, stmt in enumerate(block.statements):
168
190
  # if we are lucky and things get folded into one statement:
169
191
  if (
170
192
  isinstance(stmt, ailment.Stmt.Store)
@@ -178,7 +200,7 @@ class WinStackCanarySimplifier(OptimizationPass):
178
200
  # Check addr: must be __security_cookie
179
201
  load_addr = stmt.data.operands[0].addr.value
180
202
  if load_addr == self._security_cookie_addr:
181
- return first_block, [idx]
203
+ return [idx]
182
204
  # or if we are unlucky and the load and the xor are two different statements
183
205
  if (
184
206
  isinstance(stmt, ailment.Stmt.Assignment)
@@ -191,33 +213,48 @@ class WinStackCanarySimplifier(OptimizationPass):
191
213
  if load_addr == self._security_cookie_addr:
192
214
  load_stmt_idx = idx
193
215
  load_reg = stmt.dst.reg_offset
194
- if load_stmt_idx is not None and idx == load_stmt_idx + 1:
216
+ if load_stmt_idx is not None and xor_stmt_idx is None and idx >= load_stmt_idx + 1: # noqa:SIM102
195
217
  if (
196
218
  isinstance(stmt, ailment.Stmt.Assignment)
197
219
  and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
198
220
  and stmt.dst.was_reg
221
+ and not self.project.arch.is_artificial_register(stmt.dst.reg_offset, stmt.dst.size)
199
222
  and isinstance(stmt.src, ailment.Expr.BinaryOp)
200
223
  and stmt.src.op == "Xor"
201
224
  and isinstance(stmt.src.operands[0], ailment.Expr.VirtualVariable)
202
225
  and stmt.src.operands[0].was_reg
203
226
  and stmt.src.operands[0].reg_offset == load_reg
204
- and isinstance(stmt.src.operands[1], ailment.Expr.StackBaseOffset)
227
+ and (
228
+ isinstance(stmt.src.operands[1], ailment.Expr.StackBaseOffset)
229
+ or (
230
+ isinstance(stmt.src.operands[1], ailment.Expr.VirtualVariable)
231
+ and stmt.src.operands[1].was_reg
232
+ and stmt.src.operands[1].reg_offset == self.project.arch.registers["ebp"][0]
233
+ )
234
+ )
205
235
  ):
206
236
  xor_stmt_idx = idx
207
237
  xored_reg = stmt.dst.reg_offset
208
- else:
209
- break
210
- if xor_stmt_idx is not None and idx == xor_stmt_idx + 1:
238
+ if load_stmt_idx is not None and xor_stmt_idx is not None and idx == xor_stmt_idx + 1:
211
239
  if (
212
240
  isinstance(stmt, ailment.Stmt.Store)
213
- and isinstance(stmt.addr, ailment.Expr.StackBaseOffset)
241
+ and (
242
+ isinstance(stmt.addr, ailment.Expr.StackBaseOffset)
243
+ or (
244
+ isinstance(stmt.addr, ailment.Expr.BinaryOp)
245
+ and stmt.addr.op == "Sub"
246
+ and isinstance(stmt.addr.operands[0], ailment.Expr.VirtualVariable)
247
+ and stmt.addr.operands[0].was_reg
248
+ and stmt.addr.operands[0].reg_offset == self.project.arch.registers["ebp"][0]
249
+ and isinstance(stmt.addr.operands[1], ailment.Expr.Const)
250
+ )
251
+ )
214
252
  and isinstance(stmt.data, ailment.Expr.VirtualVariable)
215
253
  and stmt.data.was_reg
216
254
  and stmt.data.reg_offset == xored_reg
217
255
  ):
218
- return first_block, [load_stmt_idx, xor_stmt_idx, idx]
256
+ return [load_stmt_idx, xor_stmt_idx, idx]
219
257
  break
220
-
221
258
  return None
222
259
 
223
260
  def _find_amd64_canary_storing_stmt(self, block, canary_value_stack_offset):
@@ -275,6 +312,7 @@ class WinStackCanarySimplifier(OptimizationPass):
275
312
 
276
313
  def _find_x86_canary_storing_stmt(self, block, canary_value_stack_offset):
277
314
  load_stmt_idx = None
315
+ canary_reg_dst_offset = None
278
316
 
279
317
  for idx, stmt in enumerate(block.statements):
280
318
  # when we are lucky, we have one instruction
@@ -283,7 +321,7 @@ class WinStackCanarySimplifier(OptimizationPass):
283
321
  isinstance(stmt, ailment.Stmt.Assignment)
284
322
  and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
285
323
  and stmt.dst.was_reg
286
- and stmt.dst.reg_offset == self.project.arch.registers["eax"][0]
324
+ and not self.project.arch.is_artificial_register(stmt.dst.reg_offset, stmt.dst.size)
287
325
  )
288
326
  and isinstance(stmt.src, ailment.Expr.BinaryOp)
289
327
  and stmt.src.op == "Xor"
@@ -291,8 +329,7 @@ class WinStackCanarySimplifier(OptimizationPass):
291
329
  op0, op1 = stmt.src.operands
292
330
  if (
293
331
  isinstance(op0, ailment.Expr.Load)
294
- and isinstance(op0.addr, ailment.Expr.StackBaseOffset)
295
- and op0.addr.offset == canary_value_stack_offset
332
+ and self._get_bp_offset(op0.addr, stmt.ins_addr) == canary_value_stack_offset
296
333
  ) and isinstance(op1, ailment.Expr.StackBaseOffset):
297
334
  # found it
298
335
  return idx
@@ -300,12 +337,12 @@ class WinStackCanarySimplifier(OptimizationPass):
300
337
  if (
301
338
  isinstance(stmt, ailment.Stmt.Assignment)
302
339
  and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
303
- and stmt.dst.reg_offset == self.project.arch.registers["eax"][0]
340
+ and not self.project.arch.is_artificial_register(stmt.dst.reg_offset, stmt.dst.size)
304
341
  and isinstance(stmt.src, ailment.Expr.Load)
305
- and isinstance(stmt.src.addr, ailment.Expr.StackBaseOffset)
306
- and stmt.src.addr.offset == canary_value_stack_offset
342
+ and self._get_bp_offset(stmt.src.addr, stmt.ins_addr) == canary_value_stack_offset
307
343
  ):
308
344
  load_stmt_idx = idx
345
+ canary_reg_dst_offset = stmt.dst.reg_offset
309
346
  if (
310
347
  load_stmt_idx is not None
311
348
  and idx >= load_stmt_idx + 1
@@ -313,19 +350,49 @@ class WinStackCanarySimplifier(OptimizationPass):
313
350
  isinstance(stmt, ailment.Stmt.Assignment)
314
351
  and isinstance(stmt.dst, ailment.Expr.VirtualVariable)
315
352
  and stmt.dst.was_reg
353
+ and not self.project.arch.is_artificial_register(stmt.dst.reg_offset, stmt.dst.size)
316
354
  and isinstance(stmt.src, ailment.Expr.BinaryOp)
317
355
  and stmt.src.op == "Xor"
318
356
  )
319
357
  and (
320
358
  isinstance(stmt.src.operands[0], ailment.Expr.VirtualVariable)
321
359
  and stmt.src.operands[0].was_reg
322
- and stmt.src.operands[0].reg_offset == self.project.arch.registers["eax"][0]
323
- and isinstance(stmt.src.operands[1], ailment.Expr.StackBaseOffset)
360
+ and stmt.src.operands[0].reg_offset == canary_reg_dst_offset
361
+ and self._get_bp_offset(stmt.src.operands[1], stmt.ins_addr) is not None
324
362
  )
325
363
  ):
326
364
  return idx
327
365
  return None
328
366
 
367
+ def _get_bp_offset(self, expr: ailment.Expr.Expression, ins_addr: int) -> int | None:
368
+ if isinstance(expr, ailment.Expr.StackBaseOffset):
369
+ return expr.offset
370
+ if (
371
+ isinstance(expr, ailment.Expr.VirtualVariable)
372
+ and expr.was_reg
373
+ and expr.reg_offset == self.project.arch.bp_offset
374
+ ):
375
+ if self._stack_pointer_tracker is None:
376
+ return None
377
+ return self._stack_pointer_tracker.offset_before(ins_addr, self.project.arch.bp_offset)
378
+ if (
379
+ isinstance(expr, ailment.Expr.BinaryOp)
380
+ and expr.op == "Sub"
381
+ and isinstance(expr.operands[0], ailment.Expr.VirtualVariable)
382
+ and expr.operands[0].was_reg
383
+ and expr.operands[0].reg_offset == self.project.arch.bp_offset
384
+ and isinstance(expr.operands[1], ailment.Expr.Const)
385
+ ):
386
+ if self._stack_pointer_tracker is None:
387
+ return None
388
+ base_bp_offset = self._stack_pointer_tracker.offset_before(ins_addr, self.project.arch.bp_offset)
389
+ if base_bp_offset is None:
390
+ return None
391
+ bp_off = base_bp_offset - expr.operands[1].value
392
+ mask = 0xFFFF_FFFF if self.project.arch.bits == 32 else 0xFFFF_FFFF_FFFF_FFFF
393
+ return bp_off & mask
394
+ return None
395
+
329
396
  @staticmethod
330
397
  def _find_return_addr_storing_stmt(block):
331
398
  for idx, stmt in enumerate(block.statements):
@@ -339,11 +406,13 @@ class WinStackCanarySimplifier(OptimizationPass):
339
406
  return None
340
407
 
341
408
  def _find_stmt_calling_security_check_cookie(self, node):
409
+ assert self._security_cookie_addr is not None
342
410
  for idx, stmt in enumerate(node.statements):
343
411
  if isinstance(stmt, ailment.Stmt.Call) and isinstance(stmt.target, ailment.Expr.Const):
344
412
  const_target = stmt.target.value
345
- if const_target in self.kb.functions:
346
- func = self.kb.functions.function(addr=const_target)
413
+ if self.kb.functions.contains_addr(const_target):
414
+ func = self.kb.functions.get_by_addr(const_target)
415
+ assert func is not None
347
416
  if func.name == "_security_check_cookie" or is_function_security_check_cookie(
348
417
  func, self.project, self._security_cookie_addr
349
418
  ):
@@ -48,6 +48,9 @@ from .inlined_wstrcpy import InlinedWstrcpy
48
48
  from .cmpord_rewriter import CmpORDRewriter
49
49
  from .coalesce_adjacent_shrs import CoalesceAdjacentShiftRights
50
50
  from .a_mul_const_sub_a import AMulConstSubA
51
+ from .rewrite_cxx_operator_calls import RewriteCxxOperatorCalls
52
+ from .remove_cxx_destructor_calls import RemoveCxxDestructorCalls
53
+ from .rewrite_conv_mul import RewriteConvMul
51
54
  from .base import PeepholeOptimizationExprBase, PeepholeOptimizationStmtBase, PeepholeOptimizationMultiStmtBase
52
55
 
53
56
 
@@ -100,6 +103,9 @@ ALL_PEEPHOLE_OPTS: list[type[PeepholeOptimizationExprBase]] = [
100
103
  CmpORDRewriter,
101
104
  CoalesceAdjacentShiftRights,
102
105
  ShlToMul,
106
+ RewriteCxxOperatorCalls,
107
+ RemoveCxxDestructorCalls,
108
+ RewriteConvMul,
103
109
  ]
104
110
 
105
111
  MULTI_STMT_OPTS: list[type[PeepholeOptimizationMultiStmtBase]] = [
@@ -4,6 +4,7 @@ from ailment.statement import Statement, Assignment
4
4
  from ailment import Block
5
5
  from angr.project import Project
6
6
  from angr.knowledge_base import KnowledgeBase
7
+ from angr.knowledge_plugins.key_definitions import atoms
7
8
 
8
9
 
9
10
  class PeepholeOptimizationStmtBase:
@@ -14,20 +15,33 @@ class PeepholeOptimizationStmtBase:
14
15
  __slots__ = (
15
16
  "func_addr",
16
17
  "kb",
18
+ "preserve_vvar_ids",
17
19
  "project",
20
+ "type_hints",
18
21
  )
19
22
  project: Project | None
20
23
  kb: KnowledgeBase | None
21
24
  func_addr: int | None
25
+ preserve_vvar_ids: set[int]
26
+ type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]]
22
27
 
23
28
  NAME = "Peephole Optimization - Statement"
24
29
  DESCRIPTION = "Peephole Optimization - Statement"
25
30
  stmt_classes = None
26
31
 
27
- def __init__(self, project: Project | None, kb: KnowledgeBase | None, func_addr: int | None = None):
32
+ def __init__(
33
+ self,
34
+ project: Project | None,
35
+ kb: KnowledgeBase | None,
36
+ func_addr: int | None = None,
37
+ preserve_vvar_ids: set[int] | None = None,
38
+ type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] | None = None,
39
+ ):
28
40
  self.project = project
29
41
  self.kb = kb
30
42
  self.func_addr = func_addr
43
+ self.preserve_vvar_ids = set() if preserve_vvar_ids is None else preserve_vvar_ids
44
+ self.type_hints = [] if type_hints is None else type_hints
31
45
 
32
46
  def optimize(self, stmt, stmt_idx: int | None = None, block=None, **kwargs):
33
47
  raise NotImplementedError("_optimize() is not implemented.")
@@ -41,20 +55,33 @@ class PeepholeOptimizationMultiStmtBase:
41
55
  __slots__ = (
42
56
  "func_addr",
43
57
  "kb",
58
+ "preserve_vvar_ids",
44
59
  "project",
60
+ "type_hints",
45
61
  )
46
62
  project: Project | None
47
63
  kb: KnowledgeBase | None
48
64
  func_addr: int | None
65
+ preserve_vvar_ids: set[int]
66
+ type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]]
49
67
 
50
68
  NAME = "Peephole Optimization - Multi-statement"
51
69
  DESCRIPTION = "Peephole Optimization - Multi-statement"
52
70
  stmt_classes = None
53
71
 
54
- def __init__(self, project: Project | None, kb: KnowledgeBase | None, func_addr: int | None = None):
72
+ def __init__(
73
+ self,
74
+ project: Project | None,
75
+ kb: KnowledgeBase | None,
76
+ func_addr: int | None = None,
77
+ preserve_vvar_ids: set[int] | None = None,
78
+ type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] | None = None,
79
+ ):
55
80
  self.project = project
56
81
  self.kb = kb
57
82
  self.func_addr = func_addr
83
+ self.preserve_vvar_ids = set() if preserve_vvar_ids is None else preserve_vvar_ids
84
+ self.type_hints = [] if type_hints is None else type_hints
58
85
 
59
86
  def optimize(self, stmts: list[Statement], stmt_idx: int | None = None, block=None, **kwargs):
60
87
  raise NotImplementedError("_optimize() is not implemented.")
@@ -68,20 +95,33 @@ class PeepholeOptimizationExprBase:
68
95
  __slots__ = (
69
96
  "func_addr",
70
97
  "kb",
98
+ "preserve_vvar_ids",
71
99
  "project",
100
+ "type_hints",
72
101
  )
73
102
  project: Project | None
74
103
  kb: KnowledgeBase | None
75
104
  func_addr: int | None
105
+ preserve_vvar_ids: set[int]
106
+ type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]]
76
107
 
77
108
  NAME = "Peephole Optimization - Expression"
78
109
  DESCRIPTION = "Peephole Optimization - Expression"
79
110
  expr_classes = None
80
111
 
81
- def __init__(self, project: Project | None, kb: KnowledgeBase | None, func_addr: int | None = None):
112
+ def __init__(
113
+ self,
114
+ project: Project | None,
115
+ kb: KnowledgeBase | None,
116
+ func_addr: int | None = None,
117
+ preserve_vvar_ids: set[int] | None = None,
118
+ type_hints: list[tuple[atoms.VirtualVariable | atoms.MemoryLocation, str]] | None = None,
119
+ ):
82
120
  self.project = project
83
121
  self.kb = kb
84
122
  self.func_addr = func_addr
123
+ self.preserve_vvar_ids = set() if preserve_vvar_ids is None else preserve_vvar_ids
124
+ self.type_hints = [] if type_hints is None else type_hints
85
125
 
86
126
  def optimize(self, expr, **kwargs):
87
127
  raise NotImplementedError("_optimize() is not implemented.")
@@ -16,7 +16,7 @@ class ConstantDereferences(PeepholeOptimizationExprBase):
16
16
  expr_classes = (Load,)
17
17
 
18
18
  def optimize(self, expr: Load, **kwargs):
19
- if isinstance(expr.addr, Const):
19
+ if isinstance(expr.addr, Const) and expr.size in {1, 2, 4, 8, 10, 16, 32, 64, 128, 256}:
20
20
  # is it loading from a read-only section?
21
21
  sec = self.project.loader.find_section_containing(expr.addr.value)
22
22
  if sec is not None and sec.is_readable and (not sec.is_writable or "got" in sec.name):
@@ -7,6 +7,7 @@ from archinfo import Endness
7
7
  from ailment.expression import Const, StackBaseOffset, VirtualVariable
8
8
  from ailment.statement import Call, Assignment
9
9
 
10
+ from angr import SIM_LIBRARIES
10
11
  from angr.utils.endness import ail_const_to_be
11
12
  from .base import PeepholeOptimizationStmtBase
12
13
 
@@ -44,6 +45,7 @@ class InlinedStrcpy(PeepholeOptimizationStmtBase):
44
45
  Const(None, None, str_id, self.project.arch.bits, custom_string=True),
45
46
  Const(None, None, len(s), self.project.arch.bits),
46
47
  ],
48
+ prototype=SIM_LIBRARIES["libc.so"][0].get_prototype("strncpy"),
47
49
  **stmt.tags,
48
50
  )
49
51
 
@@ -85,6 +87,7 @@ class InlinedStrcpy(PeepholeOptimizationStmtBase):
85
87
  Const(None, None, str_id, self.project.arch.bits, custom_string=True),
86
88
  Const(None, None, len(s), self.project.arch.bits),
87
89
  ],
90
+ prototype=SIM_LIBRARIES["libc.so"][0].get_prototype("strncpy"),
88
91
  **stmt.tags,
89
92
  )
90
93
 
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
  from ailment.expression import Expression, BinaryOp, Const, Register, StackBaseOffset
5
5
  from ailment.statement import Call, Store
6
6
 
7
+ from angr import SIM_LIBRARIES
7
8
  from .base import PeepholeOptimizationMultiStmtBase
8
9
  from .inlined_strcpy import InlinedStrcpy
9
10
 
@@ -58,6 +59,7 @@ class InlinedStrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
58
59
  last_stmt.args[0],
59
60
  Const(None, None, new_str_idx, last_stmt.args[0].bits, custom_string=True),
60
61
  ]
62
+ prototype = SIM_LIBRARIES["libc.so"][0].get_prototype("strcpy")
61
63
  else:
62
64
  call_name = "strncpy"
63
65
  new_str_idx = self.kb.custom_strings.allocate(new_str)
@@ -66,8 +68,9 @@ class InlinedStrcpyConsolidation(PeepholeOptimizationMultiStmtBase):
66
68
  Const(None, None, new_str_idx, last_stmt.args[0].bits, custom_string=True),
67
69
  Const(None, None, len(new_str), self.project.arch.bits),
68
70
  ]
71
+ prototype = SIM_LIBRARIES["libc.so"][0].get_prototype("strncpy")
69
72
 
70
- return [Call(stmt.idx, call_name, args=args, **stmt.tags)]
73
+ return [Call(stmt.idx, call_name, args=args, prototype=prototype, **stmt.tags)]
71
74
 
72
75
  return None
73
76
 
@@ -0,0 +1,32 @@
1
+ # pylint:disable=arguments-differ
2
+ from __future__ import annotations
3
+
4
+ from ailment.expression import Const
5
+ from ailment.statement import Call
6
+
7
+ from .base import PeepholeOptimizationStmtBase
8
+
9
+
10
+ class RemoveCxxDestructorCalls(PeepholeOptimizationStmtBase):
11
+ """
12
+ Rewrite C++ operator function calls into operations.
13
+ """
14
+
15
+ __slots__ = ()
16
+
17
+ NAME = "Remove C++ destructor function calls"
18
+ stmt_classes = (Call,)
19
+
20
+ def optimize(self, stmt: Call, **kwargs) -> tuple | None: # type:ignore
21
+ # are we calling a function that we deem as a C++ destructor?
22
+ assert self.project is not None
23
+
24
+ if isinstance(stmt.target, Const):
25
+ func_addr = stmt.target.value
26
+ if not self.project.kb.functions.contains_addr(func_addr):
27
+ return None
28
+ func = self.project.kb.functions[func_addr]
29
+ if "::~" in func.demangled_name and stmt.args is not None:
30
+ # yes it is!
31
+ return ()
32
+ return None
@@ -20,10 +20,19 @@ class RemoveRedundantBitmasks(PeepholeOptimizationExprBase):
20
20
  __slots__ = ()
21
21
 
22
22
  NAME = "Remove redundant bitmasks"
23
- expr_classes = (BinaryOp,)
23
+ expr_classes = (BinaryOp, Convert)
24
24
 
25
- def optimize(self, expr: BinaryOp, **kwargs):
25
+ def optimize(self, expr: BinaryOp | Convert, **kwargs):
26
+
27
+ if isinstance(expr, BinaryOp):
28
+ return self._optimize_BinaryOp(expr)
29
+ if isinstance(expr, Convert):
30
+ return RemoveRedundantBitmasks._optimize_Convert(expr)
31
+ return None
32
+
33
+ def _optimize_BinaryOp(self, expr: BinaryOp):
26
34
  # And(expr, full_N_bitmask) ==> expr
35
+ # And(SHR(expr, N), bitmask)) ==> SHR(expr, N)
27
36
  # And(Conv(1->N, expr), bitmask) ==> Conv(1->N, expr)
28
37
  # And(Conv(1->N, bool_expr), bitmask) ==> Conv(1->N, bool_expr)
29
38
  # And(ITE(?, const_expr, const_expr), bitmask) ==> ITE(?, const_expr, const_expr)
@@ -31,6 +40,17 @@ class RemoveRedundantBitmasks(PeepholeOptimizationExprBase):
31
40
  inner_expr = expr.operands[0]
32
41
  if expr.operands[1].value == _MASKS.get(inner_expr.bits, None):
33
42
  return inner_expr
43
+
44
+ if isinstance(inner_expr, BinaryOp) and inner_expr.op == "Shr":
45
+ mask = expr.operands[1]
46
+ shift_val = inner_expr.operands[1]
47
+ if (
48
+ isinstance(shift_val, Const)
49
+ and shift_val.value in _MASKS
50
+ and mask.value == _MASKS.get(int(64 - shift_val.value), None)
51
+ ):
52
+ return inner_expr
53
+
34
54
  if isinstance(inner_expr, Convert) and self.is_bool_expr(inner_expr.operand):
35
55
  # useless masking
36
56
  return inner_expr
@@ -47,3 +67,50 @@ class RemoveRedundantBitmasks(PeepholeOptimizationExprBase):
47
67
  return ite
48
68
 
49
69
  return None
70
+
71
+ @staticmethod
72
+ def _optimize_Convert(expr: Convert):
73
+ # Conv(64->32, (expr & bitmask) + expr)
74
+ # => Conv(64->32, (expr + expr))
75
+ if (
76
+ expr.op == "Convert"
77
+ and expr.from_bits > expr.to_bits
78
+ and isinstance(expr.operand, BinaryOp)
79
+ and expr.operand.op == "Add"
80
+ ):
81
+ operand_expr = expr.operand
82
+ op0, op1 = operand_expr.operands
83
+ if (
84
+ isinstance(op0, BinaryOp)
85
+ and op0.op == "And"
86
+ and isinstance(op0.operands[1], Const)
87
+ and op0.operands[1].value == _MASKS.get(expr.to_bits, None)
88
+ ):
89
+ new_op0 = op0.operands[0]
90
+ replaced, new_operand_expr = operand_expr.replace(op0, new_op0)
91
+ if replaced:
92
+ expr.operand = new_operand_expr
93
+ return expr
94
+ # Conv(64->32, (expr) - (expr) & 0xffffffff<64>)))
95
+ # => Conv(64->32, (expr - expr))
96
+ elif (
97
+ expr.op == "Convert"
98
+ and expr.from_bits > expr.to_bits
99
+ and isinstance(expr.operand, BinaryOp)
100
+ and expr.operand.op == "Sub"
101
+ ):
102
+ operand_expr = expr.operand
103
+ op0, op1 = operand_expr.operands
104
+ if (
105
+ isinstance(op1, BinaryOp)
106
+ and op1.op == "And"
107
+ and isinstance(op1.operands[1], Const)
108
+ and op1.operands[1].value == _MASKS.get(expr.to_bits, None)
109
+ ):
110
+ new_op1 = op1.operands[0]
111
+ replaced, new_operand_expr = operand_expr.replace(op1, new_op1)
112
+ if replaced:
113
+ expr.operand = new_operand_expr
114
+ return expr
115
+
116
+ return None