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

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

Potentially problematic release.


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

Files changed (84) 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/lib/angr_native.dylib +0 -0
  48. angr/procedures/definitions/parse_win32json.py +2 -1
  49. angr/storage/memory_mixins/actions_mixin.py +7 -7
  50. angr/storage/memory_mixins/address_concretization_mixin.py +5 -5
  51. angr/storage/memory_mixins/bvv_conversion_mixin.py +1 -1
  52. angr/storage/memory_mixins/clouseau_mixin.py +3 -3
  53. angr/storage/memory_mixins/conditional_store_mixin.py +3 -3
  54. angr/storage/memory_mixins/default_filler_mixin.py +3 -3
  55. angr/storage/memory_mixins/memory_mixin.py +45 -34
  56. angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +15 -14
  57. angr/storage/memory_mixins/paged_memory/paged_memory_mixin.py +27 -16
  58. angr/storage/memory_mixins/paged_memory/pages/cooperation.py +18 -9
  59. angr/storage/memory_mixins/paged_memory/pages/ispo_mixin.py +5 -5
  60. angr/storage/memory_mixins/paged_memory/pages/multi_values.py +89 -55
  61. angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +16 -25
  62. angr/storage/memory_mixins/paged_memory/pages/permissions_mixin.py +11 -9
  63. angr/storage/memory_mixins/paged_memory/pages/ultra_page.py +23 -7
  64. angr/storage/memory_mixins/paged_memory/privileged_mixin.py +1 -1
  65. angr/storage/memory_mixins/regioned_memory/region_meta_mixin.py +9 -7
  66. angr/storage/memory_mixins/regioned_memory/regioned_memory_mixin.py +9 -9
  67. angr/storage/memory_mixins/regioned_memory/static_find_mixin.py +1 -0
  68. angr/storage/memory_mixins/simple_interface_mixin.py +2 -2
  69. angr/storage/memory_mixins/simplification_mixin.py +2 -2
  70. angr/storage/memory_mixins/size_resolution_mixin.py +1 -1
  71. angr/storage/memory_mixins/slotted_memory.py +3 -3
  72. angr/storage/memory_mixins/smart_find_mixin.py +1 -0
  73. angr/storage/memory_mixins/underconstrained_mixin.py +5 -5
  74. angr/storage/memory_mixins/unwrapper_mixin.py +4 -4
  75. angr/storage/memory_object.py +4 -3
  76. angr/utils/constants.py +1 -1
  77. angr/utils/graph.py +15 -0
  78. angr/vaults.py +2 -2
  79. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/METADATA +6 -6
  80. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/RECORD +84 -83
  81. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/WHEEL +1 -1
  82. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/LICENSE +0 -0
  83. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/entry_points.txt +0 -0
  84. {angr-9.2.123.dist-info → angr-9.2.124.dist-info}/top_level.txt +0 -0
angr/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # pylint: disable=wrong-import-position
3
3
  from __future__ import annotations
4
4
 
5
- __version__ = "9.2.123"
5
+ __version__ = "9.2.124"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
@@ -153,6 +153,11 @@ class MipsElfFastResolver(IndirectJumpResolver):
153
153
 
154
154
  def _resolve_case_1(self, addr: int, block: pyvex.IRSB, func_addr: int, gp_value: int, cfg) -> int | None:
155
155
  # lift the block again with the correct setting
156
+
157
+ inital_regs = [(self.project.arch.registers["t9"][0], self.project.arch.registers["t9"][1], func_addr)]
158
+ if gp_value is not None:
159
+ inital_regs.append((self.project.arch.registers["gp"][0], self.project.arch.registers["gp"][1], gp_value))
160
+
156
161
  first_irsb = self.project.factory.block(
157
162
  addr,
158
163
  size=block.size,
@@ -160,10 +165,7 @@ class MipsElfFastResolver(IndirectJumpResolver):
160
165
  const_prop=True,
161
166
  cross_insn_opt=False,
162
167
  load_from_ro_regions=True,
163
- initial_regs=[
164
- (self.project.arch.registers["t9"][0], self.project.arch.registers["t9"][1], func_addr),
165
- (self.project.arch.registers["gp"][0], self.project.arch.registers["gp"][1], gp_value),
166
- ],
168
+ initial_regs=inital_regs,
167
169
  ).vex_nostmt
168
170
 
169
171
  if not isinstance(first_irsb.next, pyvex.IRExpr.RdTmp):
@@ -193,6 +195,10 @@ class MipsElfFastResolver(IndirectJumpResolver):
193
195
  # the register (t9) is set in this block - we can resolve the jump target using only the current block
194
196
  return Case2Result.RESUME, None
195
197
 
198
+ inital_regs = [(self.project.arch.registers["t9"][0], self.project.arch.registers["t9"][1], func_addr)]
199
+ if gp_value is not None:
200
+ inital_regs.append((self.project.arch.registers["gp"][0], self.project.arch.registers["gp"][1], gp_value))
201
+
196
202
  # lift the first block again with the correct setting
197
203
  first_irsb = self.project.factory.block(
198
204
  first_block_addr,
@@ -200,10 +206,7 @@ class MipsElfFastResolver(IndirectJumpResolver):
200
206
  collect_data_refs=False,
201
207
  const_prop=True,
202
208
  load_from_ro_regions=True,
203
- initial_regs=[
204
- (self.project.arch.registers["t9"][0], self.project.arch.registers["t9"][1], func_addr),
205
- (self.project.arch.registers["gp"][0], self.project.arch.registers["gp"][1], gp_value),
206
- ],
209
+ initial_regs=inital_regs,
207
210
  ).vex_nostmt
208
211
 
209
212
  last_reg_setting_tmp = self._get_last_reg_setting_tmp(first_irsb, jump_target_reg)
@@ -51,11 +51,11 @@ class MipsElfGotResolver(IndirectJumpResolver):
51
51
  return False, []
52
52
  dynsym_addr = self._find_and_cache_section_addr(obj, ".dynsym")
53
53
  if dynsym_addr is None:
54
- return None
54
+ return False, []
55
55
 
56
56
  dynstr_addr = self._find_and_cache_section_addr(obj, ".dynstr")
57
57
  if dynstr_addr is None:
58
- return None
58
+ return False, []
59
59
 
60
60
  if block.size != 16:
61
61
  return False, []
@@ -1316,18 +1316,7 @@ class AILSimplifier(Analysis):
1316
1316
  continue
1317
1317
  uses = rd.get_vvar_uses(def_.atom)
1318
1318
 
1319
- elif def_.atom.was_reg:
1320
- uses = rd.get_vvar_uses(def_.atom)
1321
- if (
1322
- def_.atom.reg_offset in self.project.arch.artificial_registers_offsets
1323
- and len(uses) == 1
1324
- and next(iter(uses)) == def_.codeloc
1325
- ):
1326
- # TODO: Verify if we still need this hack after moving to SSA
1327
- # cc_ndep = amd64g_calculate_condition(..., cc_ndep)
1328
- uses = set()
1329
-
1330
- elif def_.atom.was_parameter:
1319
+ elif def_.atom.was_reg or def_.atom.was_parameter:
1331
1320
  uses = rd.get_vvar_uses(def_.atom)
1332
1321
 
1333
1322
  else:
@@ -1425,9 +1414,17 @@ class AILSimplifier(Analysis):
1425
1414
  simplified = True
1426
1415
  continue
1427
1416
  if isinstance(stmt, Assignment) and isinstance(stmt.dst, VirtualVariable):
1428
- # no one is using the returned virtual variable. replace this assignment statement with
1429
- # a call statement
1430
- stmt = stmt.src
1417
+ # no one is using the returned virtual variable.
1418
+ # now the things are a bit tricky here
1419
+ if isinstance(stmt.src, Call):
1420
+ # replace this assignment statement with a call statement
1421
+ stmt = stmt.src
1422
+ elif isinstance(stmt.src, Convert) and isinstance(stmt.src.operand, Call):
1423
+ # the convert is useless now
1424
+ stmt = stmt.src.operand
1425
+ else:
1426
+ # we can't change this stmt at all because it has an expression with Calls inside
1427
+ pass
1431
1428
  else:
1432
1429
  # no calls. remove it
1433
1430
  simplified = True
@@ -1460,7 +1457,7 @@ class AILSimplifier(Analysis):
1460
1457
  def _find_cyclic_dependent_phis_and_dirty_vvars(self, rd: SRDAModel) -> set[int]:
1461
1458
  blocks_dict = {(bb.addr, bb.idx): bb for bb in self.func_graph}
1462
1459
 
1463
- # find dirty vvars
1460
+ # find dirty vvars and vexccall vvars
1464
1461
  dirty_vvar_ids = set()
1465
1462
  for bb in self.func_graph:
1466
1463
  for stmt in bb.statements:
@@ -1468,7 +1465,7 @@ class AILSimplifier(Analysis):
1468
1465
  isinstance(stmt, Assignment)
1469
1466
  and isinstance(stmt.dst, VirtualVariable)
1470
1467
  and stmt.dst.was_reg
1471
- and isinstance(stmt.src, DirtyExpression)
1468
+ and isinstance(stmt.src, (DirtyExpression, VEXCCallExpression))
1472
1469
  ):
1473
1470
  dirty_vvar_ids.add(stmt.dst.varid)
1474
1471
 
@@ -1544,8 +1541,8 @@ class AILSimplifier(Analysis):
1544
1541
  v = False
1545
1542
 
1546
1543
  def _handle_expr(expr_idx: int, expr: Expression, stmt_idx: int, stmt: Statement, block) -> Expression | None:
1547
- if isinstance(expr, DirtyExpression) and isinstance(expr.dirty_expr, VEXCCallExpression):
1548
- rewriter = rewriter_cls(expr.dirty_expr, self.project.arch)
1544
+ if isinstance(expr, VEXCCallExpression):
1545
+ rewriter = rewriter_cls(expr, self.project.arch)
1549
1546
  if rewriter.result is not None:
1550
1547
  _any_update.v = True
1551
1548
  return rewriter.result
@@ -143,7 +143,7 @@ class CallSiteMaker(Analysis):
143
143
  if isinstance(arg_loc, SimRegArg):
144
144
  size = arg_loc.size
145
145
  offset = arg_loc.check_offset(cc.arch)
146
- value_and_def = self._resolve_register_argument(call_stmt, arg_loc)
146
+ value_and_def = self._resolve_register_argument(arg_loc)
147
147
  if value_and_def is not None:
148
148
  vvar_def = value_and_def[1]
149
149
  arg_vvars.append(vvar_def)
@@ -283,14 +283,15 @@ class CallSiteMaker(Analysis):
283
283
  l.warning("TODO: Unsupported statement type %s for definitions.", type(stmt))
284
284
  return None
285
285
 
286
- def _resolve_register_argument(self, call_stmt, arg_loc) -> tuple[int | None, Expr.VirtualVariable] | None:
286
+ def _resolve_register_argument(self, arg_loc) -> tuple[int | None, Expr.VirtualVariable] | None:
287
287
  offset = arg_loc.check_offset(self.project.arch)
288
288
 
289
289
  if self._reaching_definitions is not None:
290
290
  # Find its definition
291
- ins_addr = call_stmt.tags["ins_addr"]
292
291
  view = SRDAView(self._reaching_definitions.model)
293
- vvar = view.get_reg_vvar_by_insn(offset, ins_addr, OP_BEFORE, block_idx=self.block.idx)
292
+ vvar = view.get_reg_vvar_by_stmt(
293
+ offset, self.block.addr, self.block.idx, len(self.block.statements) - 1, OP_BEFORE
294
+ )
294
295
 
295
296
  if vvar is not None:
296
297
  vvar_value = view.get_vvar_value(vvar)
@@ -318,8 +319,8 @@ class CallSiteMaker(Analysis):
318
319
  if self._reaching_definitions is not None:
319
320
  # find its definition
320
321
  view = SRDAView(self._reaching_definitions.model)
321
- vvar = view.get_stack_vvar_by_insn(
322
- sp_offset, size, call_stmt.ins_addr, OP_BEFORE, block_idx=self.block.idx
322
+ vvar = view.get_stack_vvar_by_stmt(
323
+ sp_offset, size, self.block.addr, self.block.idx, len(self.block.statements) - 1, OP_BEFORE
323
324
  )
324
325
  if vvar is not None:
325
326
  value = view.get_vvar_value(vvar)
@@ -416,7 +417,7 @@ class CallSiteMaker(Analysis):
416
417
 
417
418
  value = None
418
419
  if isinstance(arg_loc, SimRegArg):
419
- value_and_def = self._resolve_register_argument(call_stmt, arg_loc)
420
+ value_and_def = self._resolve_register_argument(arg_loc)
420
421
  if value_and_def is not None:
421
422
  value = value_and_def[0]
422
423
 
@@ -20,7 +20,7 @@ class AMD64CCallRewriter(CCallRewriterBase):
20
20
  __slots__ = ()
21
21
 
22
22
  def _rewrite(self, ccall: Expr.VEXCCallExpression) -> Expr.Expression | None:
23
- if ccall.cee_name == "amd64g_calculate_condition":
23
+ if ccall.callee == "amd64g_calculate_condition":
24
24
  cond = ccall.operands[0]
25
25
  op = ccall.operands[1]
26
26
  dep_1 = ccall.operands[2]
@@ -288,6 +288,28 @@ class AMD64CCallRewriter(CCallRewriterBase):
288
288
 
289
289
  r = Expr.BinaryOp(ccall.idx, "CmpLT", (dep_1, dep_2), True, **ccall.tags)
290
290
  return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
291
+
292
+ if op_v in {
293
+ AMD64_OpTypes["G_CC_OP_LOGICB"],
294
+ AMD64_OpTypes["G_CC_OP_LOGICW"],
295
+ AMD64_OpTypes["G_CC_OP_LOGICL"],
296
+ AMD64_OpTypes["G_CC_OP_LOGICQ"],
297
+ }:
298
+ # dep_1 is the result, dep_2 is always zero
299
+ # dep_1 <s 0
300
+
301
+ dep_1 = self._fix_size(
302
+ dep_1,
303
+ op_v,
304
+ AMD64_OpTypes["G_CC_OP_LOGICB"],
305
+ AMD64_OpTypes["G_CC_OP_LOGICW"],
306
+ AMD64_OpTypes["G_CC_OP_LOGICL"],
307
+ ccall.tags,
308
+ )
309
+ zero = Expr.Const(None, None, 0, dep_1.bits)
310
+ r = Expr.BinaryOp(ccall.idx, "CmpLT", (dep_1, zero), True, **ccall.tags)
311
+ return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
312
+
291
313
  elif cond_v == AMD64_CondTypes["CondNBE"]:
292
314
  if op_v in {
293
315
  AMD64_OpTypes["G_CC_OP_SUBB"],
@@ -390,7 +412,7 @@ class AMD64CCallRewriter(CCallRewriterBase):
390
412
  )
391
413
  return Expr.Convert(None, r.bits, ccall.bits, False, r, **ccall.tags)
392
414
 
393
- elif ccall.cee_name == "amd64g_calculate_rflags_c":
415
+ elif ccall.callee == "amd64g_calculate_rflags_c":
394
416
  # calculate the carry flag
395
417
  op = ccall.operands[0]
396
418
  dep_1 = ccall.operands[1]
@@ -110,6 +110,7 @@ class Clinic(Analysis):
110
110
  inlined_counts: dict[int, int] | None = None,
111
111
  inlining_parents: set[int] | None = None,
112
112
  vvar_id_start: int = 0,
113
+ optimization_scratch: dict[str, Any] | None = None,
113
114
  ):
114
115
  if not func.normalized and mode == ClinicMode.DECOMPILE:
115
116
  raise ValueError("Decompilation must work on normalized function graphs.")
@@ -124,6 +125,7 @@ class Clinic(Analysis):
124
125
  self.variable_kb = variable_kb
125
126
  self.externs: set[SimMemoryVariable] = set()
126
127
  self.data_refs: dict[int, int] = {} # data address to instruction address
128
+ self.optimization_scratch = optimization_scratch if optimization_scratch is not None else {}
127
129
 
128
130
  self._func_graph: networkx.DiGraph | None = None
129
131
  self._ail_manager = None
@@ -919,10 +921,16 @@ class Clinic(Analysis):
919
921
  "Ijk_Sys"
920
922
  ):
921
923
  # we don't support lifting this block. use a dummy block instead
924
+ dirty_expr = ailment.Expr.DirtyExpression(
925
+ self._ail_manager.next_atom,
926
+ f"Unsupported jumpkind {block.vex.jumpkind} at address {block_node.addr}",
927
+ [],
928
+ bits=0,
929
+ )
922
930
  statements = [
923
931
  ailment.Stmt.DirtyStatement(
924
932
  self._ail_manager.next_atom(),
925
- f"Unsupported jumpkind {block.vex.jumpkind} at address {block_node.addr}",
933
+ dirty_expr,
926
934
  ins_addr=block_node.addr,
927
935
  )
928
936
  ]
@@ -1219,6 +1227,7 @@ class Clinic(Analysis):
1219
1227
  variable_kb=variable_kb,
1220
1228
  vvar_id_start=self.vvar_id_start,
1221
1229
  entry_node_addr=self.entry_node_addr,
1230
+ scratch=self.optimization_scratch,
1222
1231
  **kwargs,
1223
1232
  )
1224
1233
  if a.out_graph:
@@ -1256,6 +1265,7 @@ class Clinic(Analysis):
1256
1265
  ailment.Expr.VirtualVariableCategory.PARAMETER,
1257
1266
  oident=arg.reg,
1258
1267
  ins_addr=self.function.addr,
1268
+ vex_block_addr=self.function.addr,
1259
1269
  )
1260
1270
  self.vvar_id_start += 1
1261
1271
  arg_vvars[arg_vvar.varid] = arg_vvar, arg
@@ -1269,6 +1279,7 @@ class Clinic(Analysis):
1269
1279
  False,
1270
1280
  arg_vvar,
1271
1281
  ins_addr=self.function.addr,
1282
+ vex_block_addr=self.function.addr,
1272
1283
  )
1273
1284
 
1274
1285
  fullreg_dst = ailment.Expr.Register(
@@ -1277,12 +1288,14 @@ class Clinic(Analysis):
1277
1288
  basereg_offset,
1278
1289
  basereg_size * self.project.arch.byte_width,
1279
1290
  ins_addr=self.function.addr,
1291
+ vex_block_addr=self.function.addr,
1280
1292
  )
1281
1293
  stmt = ailment.Stmt.Assignment(
1282
1294
  self._ail_manager.next_atom(),
1283
1295
  fullreg_dst,
1284
1296
  arg_vvar,
1285
1297
  ins_addr=self.function.addr,
1298
+ vex_block_addr=self.function.addr,
1286
1299
  )
1287
1300
  new_stmts.append(stmt)
1288
1301
 
@@ -1313,6 +1326,7 @@ class Clinic(Analysis):
1313
1326
  ail_graph,
1314
1327
  entry=next(iter(bb for bb in ail_graph if (bb.addr, bb.idx) == self.entry_node_addr)),
1315
1328
  ail_manager=self._ail_manager,
1329
+ ssa_tmps=True,
1316
1330
  ssa_stackvars=True,
1317
1331
  vvar_id_start=self.vvar_id_start,
1318
1332
  )
@@ -1792,6 +1806,18 @@ class Clinic(Analysis):
1792
1806
  elif isinstance(expr, ailment.Stmt.Call):
1793
1807
  self._link_variables_on_call(variable_manager, global_variables, block, stmt_idx, expr, is_expr=True)
1794
1808
 
1809
+ elif isinstance(expr, ailment.Expr.VEXCCallExpression):
1810
+ for operand in expr.operands:
1811
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, operand)
1812
+
1813
+ elif isinstance(expr, ailment.Expr.DirtyExpression):
1814
+ for operand in expr.operands:
1815
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, operand)
1816
+ if expr.maddr:
1817
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.maddr)
1818
+ if expr.guard:
1819
+ self._link_variables_on_expr(variable_manager, global_variables, block, stmt_idx, stmt, expr.guard)
1820
+
1795
1821
  def _function_graph_to_ail_graph(self, func_graph, blocks_by_addr_and_size=None):
1796
1822
  if blocks_by_addr_and_size is None:
1797
1823
  blocks_by_addr_and_size = self._blocks_by_addr_and_size
@@ -160,6 +160,8 @@ _ail2claripy_op_mapping = {
160
160
  "GetMSBs": lambda expr, _, m: _dummy_bvs(expr, m),
161
161
  "InterleaveLOV": lambda expr, _, m: _dummy_bvs(expr, m),
162
162
  "InterleaveHIV": lambda expr, _, m: _dummy_bvs(expr, m),
163
+ # catch-all
164
+ "_DUMMY_": lambda expr, _, m: _dummy_bvs(expr, m),
163
165
  }
164
166
 
165
167
  #
@@ -771,7 +773,7 @@ class ConditionProcessor:
771
773
 
772
774
  if isinstance(
773
775
  condition,
774
- (ailment.Expr.DirtyExpression, ailment.Expr.BasePointerOffset, ailment.Expr.ITE),
776
+ (ailment.Expr.VEXCCallExpression, ailment.Expr.BasePointerOffset, ailment.Expr.ITE),
775
777
  ):
776
778
  return _dummy_bvs(condition, self._condition_mapping)
777
779
  if isinstance(condition, ailment.Stmt.Call):
@@ -828,9 +830,14 @@ class ConditionProcessor:
828
830
  # fall back to op
829
831
  lambda_expr = _ail2claripy_op_mapping.get(condition.op, None)
830
832
  if lambda_expr is None:
831
- raise NotImplementedError(
832
- f"Unsupported AIL expression operation {condition.op} or {condition.verbose_op}. Consider implementing."
833
+ # fall back to the catch-all option
834
+ l.debug(
835
+ "Unsupported AIL expression operation %s (or verbose: %s). Fall back to the default catch-all dummy "
836
+ "option. Consider implementing.",
837
+ condition.op,
838
+ condition.verbose_op,
833
839
  )
840
+ lambda_expr = _ail2claripy_op_mapping["_DUMMY_"]
834
841
  r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping)
835
842
 
836
843
  if isinstance(r, claripy.ast.Bool) and nobool:
@@ -14,6 +14,7 @@ class DecompilationCache:
14
14
  """
15
15
 
16
16
  __slots__ = (
17
+ "parameters",
17
18
  "addr",
18
19
  "type_constraints",
19
20
  "func_typevar",
@@ -25,6 +26,7 @@ class DecompilationCache:
25
26
  )
26
27
 
27
28
  def __init__(self, addr):
29
+ self.parameters: dict[str, Any] = {}
28
30
  self.addr = addr
29
31
  self.type_constraints: set | None = None
30
32
  self.func_typevar = None
@@ -68,12 +68,13 @@ class Decompiler(Analysis):
68
68
  inline_functions=frozenset(),
69
69
  update_memory_data: bool = True,
70
70
  generate_code: bool = True,
71
+ use_cache: bool = True,
71
72
  ):
72
73
  if not isinstance(func, Function):
73
74
  func = self.kb.functions[func]
74
75
  self.func: Function = func
75
76
  self._cfg = cfg.model if isinstance(cfg, CFGFast) else cfg
76
- self._options = options
77
+ self._options = options or []
77
78
 
78
79
  if preset is None and optimization_passes:
79
80
  self._optimization_passes = optimization_passes
@@ -102,6 +103,25 @@ class Decompiler(Analysis):
102
103
  self._update_memory_data = update_memory_data
103
104
  self._generate_code = generate_code
104
105
  self._inline_functions = inline_functions
106
+ self._cache_parameters = (
107
+ {
108
+ "cfg": self._cfg,
109
+ "variable_kb": self._variable_kb,
110
+ "options": {(o, v) for o, v in self._options if o.category != "Display" and v != o.default_value},
111
+ "optimization_passes": self._optimization_passes,
112
+ "sp_tracker_track_memory": self._sp_tracker_track_memory,
113
+ "peephole_optimizations": self._peephole_optimizations,
114
+ "vars_must_struct": self._vars_must_struct,
115
+ "flavor": self._flavor,
116
+ "expr_comments": self._expr_comments,
117
+ "stmt_comments": self._stmt_comments,
118
+ "ite_exprs": self._ite_exprs,
119
+ "binop_operators": self._binop_operators,
120
+ "inline_functions": self._inline_functions,
121
+ }
122
+ if use_cache
123
+ else None
124
+ )
105
125
 
106
126
  self.clinic = None # mostly for debugging purposes
107
127
  self.codegen: CStructuredCodeGenerator | None = None
@@ -111,27 +131,43 @@ class Decompiler(Analysis):
111
131
  self.unoptimized_ail_graph: networkx.DiGraph | None = None
112
132
  self.ail_graph: networkx.DiGraph | None = None
113
133
  self.vvar_id_start = None
134
+ self._optimization_scratch: dict[str, Any] = {}
114
135
 
115
136
  if decompile:
116
137
  self._decompile()
117
138
 
139
+ def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
140
+ a, b = self._cache_parameters, cache.parameters
141
+ id_checks = {"cfg", "variable_kb"}
142
+ return all(a[k] is b[k] if k in id_checks else a[k] == b[k] for k in self._cache_parameters)
143
+
118
144
  @timethis
119
145
  def _decompile(self):
120
146
  if self.func.is_simprocedure:
121
147
  return
122
148
 
123
- # Load from cache
124
- try:
125
- cache = self.kb.structured_code[(self.func.addr, self._flavor)]
149
+ cache = None
150
+
151
+ if self._cache_parameters is not None:
152
+ try:
153
+ cache = self.kb.decompilations[self.func.addr]
154
+ if not self._can_use_decompilation_cache(cache):
155
+ cache = None
156
+ except KeyError:
157
+ pass
158
+
159
+ if cache:
126
160
  old_codegen = cache.codegen
127
161
  old_clinic = cache.clinic
128
162
  ite_exprs = cache.ite_exprs if self._ite_exprs is None else self._ite_exprs
129
163
  binop_operators = cache.binop_operators if self._binop_operators is None else self._binop_operators
130
- except KeyError:
131
- ite_exprs = self._ite_exprs
132
- binop_operators = self._binop_operators
164
+ l.debug("Decompilation cache hit")
165
+ else:
133
166
  old_codegen = None
134
167
  old_clinic = None
168
+ ite_exprs = self._ite_exprs
169
+ binop_operators = self._binop_operators
170
+ l.debug("Decompilation cache miss")
135
171
 
136
172
  self.options_by_class = defaultdict(list)
137
173
 
@@ -167,6 +203,7 @@ class Decompiler(Analysis):
167
203
  fold_callexprs_into_conditions = True
168
204
 
169
205
  cache = DecompilationCache(self.func.addr)
206
+ cache.parameters = self._cache_parameters
170
207
  cache.ite_exprs = ite_exprs
171
208
  cache.binop_operators = binop_operators
172
209
 
@@ -189,6 +226,7 @@ class Decompiler(Analysis):
189
226
  cache=cache,
190
227
  progress_callback=progress_callback,
191
228
  inline_functions=self._inline_functions,
229
+ optimization_scratch=self._optimization_scratch,
192
230
  **self.options_to_params(self.options_by_class["clinic"]),
193
231
  )
194
232
  else:
@@ -302,6 +340,8 @@ class Decompiler(Analysis):
302
340
  self.cache.codegen = codegen
303
341
  self.cache.clinic = self.clinic
304
342
 
343
+ self.kb.decompilations[self.func.addr] = self.cache
344
+
305
345
  def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
306
346
  return self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
307
347
  self.func,
@@ -355,6 +395,7 @@ class Decompiler(Analysis):
355
395
  variable_kb=self._variable_kb,
356
396
  reaching_definitions=reaching_definitions,
357
397
  entry_node_addr=self.clinic.entry_node_addr,
398
+ scratch=self._optimization_scratch,
358
399
  **kwargs,
359
400
  )
360
401
 
@@ -414,6 +455,7 @@ class Decompiler(Analysis):
414
455
  reaching_definitions=reaching_definitions,
415
456
  vvar_id_start=self.vvar_id_start,
416
457
  entry_node_addr=self.clinic.entry_node_addr,
458
+ scratch=self._optimization_scratch,
417
459
  **kwargs,
418
460
  )
419
461
 
@@ -442,7 +484,7 @@ class Decompiler(Analysis):
442
484
  continue
443
485
 
444
486
  pass_ = timethis(pass_)
445
- a = pass_(self.func, seq=seq_node, **kwargs)
487
+ a = pass_(self.func, seq=seq_node, scratch=self._optimization_scratch, **kwargs)
446
488
  if a.out_seq:
447
489
  seq_node = a.out_seq
448
490
 
@@ -3,7 +3,7 @@ import logging
3
3
  from collections import defaultdict
4
4
 
5
5
  from ailment.block import Block
6
- from ailment.expression import Phi, VirtualVariable
6
+ from ailment.expression import Phi, VirtualVariable, VirtualVariableCategory
7
7
  from ailment.statement import Assignment, Jump, ConditionalJump, Label
8
8
 
9
9
  from angr.analyses import Analysis
@@ -213,8 +213,16 @@ class GraphDephicationVVarMapping(Analysis): # pylint:disable=abstract-method
213
213
 
214
214
  the_block = self._blocks[(src_block_addr, src_block_idx)]
215
215
  ins_addr = the_block.addr + the_block.original_size - 1
216
+ if vvar.category == VirtualVariableCategory.PARAMETER:
217
+ # it's a parameter, so we copy the variable into a dummy register vvar
218
+ # a parameter vvar should never be assigned to
219
+ new_category = VirtualVariableCategory.REGISTER
220
+ new_oident = 4096
221
+ else:
222
+ new_category = vvar.category
223
+ new_oident = vvar.oident
216
224
  new_vvar = VirtualVariable(
217
- None, new_vvar_id, vvar.bits, vvar.category, oident=vvar.oident, ins_addr=ins_addr
225
+ None, new_vvar_id, vvar.bits, new_category, oident=new_oident, ins_addr=ins_addr
218
226
  )
219
227
  assignment = Assignment(None, new_vvar, vvar, ins_addr=ins_addr)
220
228
 
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import logging
4
4
 
5
5
  from ailment.block import Block
6
- from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump
6
+ from ailment.statement import Statement, Assignment, Store, Call, Return, ConditionalJump, DirtyStatement
7
7
  from ailment.expression import (
8
8
  Register,
9
9
  VirtualVariable,
@@ -14,6 +14,8 @@ from ailment.expression import (
14
14
  Convert,
15
15
  StackBaseOffset,
16
16
  ITE,
17
+ VEXCCallExpression,
18
+ DirtyExpression,
17
19
  )
18
20
 
19
21
  from angr.engines.light import SimEngineLight, SimEngineLightAILMixin
@@ -139,10 +141,19 @@ class SimEngineDephiRewriting(
139
141
  args=stmt.args,
140
142
  ret_expr=stmt.ret_expr if new_ret_expr is None else new_ret_expr,
141
143
  fp_ret_expr=stmt.fp_ret_expr if new_fp_ret_expr is None else new_fp_ret_expr,
144
+ bits=stmt.bits,
142
145
  **stmt.tags,
143
146
  )
144
147
  return None
145
148
 
149
+ _handle_CallExpr = _handle_Call
150
+
151
+ def _handle_DirtyStatement(self, stmt: DirtyStatement) -> DirtyStatement | None:
152
+ dirty = self._expr(stmt.dirty)
153
+ if dirty is None or dirty is stmt.dirty:
154
+ return None
155
+ return DirtyStatement(stmt.idx, dirty, **stmt.tags)
156
+
146
157
  def _handle_Register(self, expr: Register) -> None:
147
158
  return None
148
159
 
@@ -243,5 +254,57 @@ class SimEngineDephiRewriting(
243
254
  )
244
255
  return None
245
256
 
257
+ def _handle_VEXCCallExpression(self, expr: VEXCCallExpression) -> VEXCCallExpression | None:
258
+ new_operands = []
259
+ updated = False
260
+ for o in expr.operands:
261
+ new_o = self._expr(o)
262
+ if new_o is not None:
263
+ updated = True
264
+ new_operands.append(new_o)
265
+ else:
266
+ new_operands.append(o)
267
+
268
+ if updated:
269
+ return VEXCCallExpression(
270
+ expr.idx,
271
+ expr.callee,
272
+ new_operands,
273
+ bits=expr.bits,
274
+ **expr.tags,
275
+ )
276
+ return None
277
+
278
+ def _handle_DirtyExpression(self, expr: DirtyExpression) -> DirtyExpression | None:
279
+ new_operands = []
280
+ updated = False
281
+ for o in expr.operands:
282
+ new_o = self._expr(o)
283
+ if new_o is not None:
284
+ updated = True
285
+ new_operands.append(new_o)
286
+ else:
287
+ new_operands.append(o)
288
+
289
+ new_guard = None
290
+ if expr.guard is not None:
291
+ new_guard = self._expr(expr.guard)
292
+ if new_guard is not None:
293
+ updated = True
294
+
295
+ if updated:
296
+ return DirtyExpression(
297
+ expr.idx,
298
+ expr.callee,
299
+ new_operands,
300
+ guard=new_guard,
301
+ mfx=expr.mfx,
302
+ maddr=expr.maddr,
303
+ msize=expr.msize,
304
+ bits=expr.bits,
305
+ **expr.tags,
306
+ )
307
+ return None
308
+
246
309
  def _handle_StackBaseOffset(self, expr: StackBaseOffset) -> None:
247
310
  return None
@@ -105,7 +105,11 @@ class NarrowingInfoExtractor(AILBlockWalkerBase):
105
105
  def _handle_DirtyExpression(
106
106
  self, expr_idx: int, expr: DirtyExpression, stmt_idx: int, stmt: Statement, block: Block | None
107
107
  ):
108
- return self._handle_expr(0, expr.dirty_expr, stmt_idx, stmt, block)
108
+ r = False
109
+ if expr.operands:
110
+ for i, op in enumerate(expr.operands):
111
+ r |= self._handle_expr(i, op, stmt_idx, stmt, block)
112
+ return r
109
113
 
110
114
  def _handle_VEXCCallExpression(
111
115
  self, expr_idx: int, expr: VEXCCallExpression, stmt_idx: int, stmt: Statement, block: Block | None
@@ -18,7 +18,10 @@ class DivSimplifierAILEngine(SimplifierAILEngine):
18
18
  An AIL pass for the div simplifier
19
19
  """
20
20
 
21
- def _check_divisor(self, a, b, ndigits=6): # pylint: disable=no-self-use
21
+ @staticmethod
22
+ def _check_divisor(a, b, ndigits=6):
23
+ if b == 0:
24
+ return None
22
25
  divisor_1 = 1 + (a // b)
23
26
  divisor_2 = int(round(a / float(b), ndigits))
24
27
  return divisor_1 if divisor_1 == divisor_2 else None