angr 9.2.125__py3-none-manylinux2014_x86_64.whl → 9.2.127__py3-none-manylinux2014_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 (50) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/__init__.py +4 -0
  3. angr/analyses/analysis.py +8 -2
  4. angr/analyses/cfg/cfg_fast.py +12 -1
  5. angr/analyses/decompiler/ail_simplifier.py +1 -0
  6. angr/analyses/decompiler/callsite_maker.py +9 -1
  7. angr/analyses/decompiler/clinic.py +2 -1
  8. angr/analyses/decompiler/condition_processor.py +109 -73
  9. angr/analyses/decompiler/decompilation_cache.py +4 -0
  10. angr/analyses/decompiler/decompiler.py +21 -3
  11. angr/analyses/decompiler/dephication/graph_vvar_mapping.py +1 -2
  12. angr/analyses/decompiler/optimization_passes/__init__.py +15 -1
  13. angr/analyses/decompiler/return_maker.py +1 -0
  14. angr/analyses/decompiler/ssailification/rewriting.py +4 -0
  15. angr/analyses/decompiler/ssailification/rewriting_engine.py +10 -3
  16. angr/analyses/decompiler/ssailification/traversal.py +1 -0
  17. angr/analyses/decompiler/ssailification/traversal_engine.py +15 -0
  18. angr/analyses/decompiler/structured_codegen/c.py +18 -5
  19. angr/analyses/decompiler/structured_codegen/dwarf_import.py +4 -1
  20. angr/analyses/deobfuscator/__init__.py +18 -0
  21. angr/analyses/deobfuscator/api_obf_finder.py +313 -0
  22. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +51 -0
  23. angr/analyses/deobfuscator/irsb_reg_collector.py +85 -0
  24. angr/analyses/deobfuscator/string_obf_finder.py +774 -0
  25. angr/analyses/deobfuscator/string_obf_opt_passes.py +133 -0
  26. angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +47 -0
  27. angr/analyses/reaching_definitions/function_handler_library/stdio.py +8 -1
  28. angr/analyses/reaching_definitions/function_handler_library/string.py +2 -2
  29. angr/analyses/s_liveness.py +3 -3
  30. angr/analyses/s_propagator.py +74 -3
  31. angr/analyses/unpacker/__init__.py +6 -0
  32. angr/analyses/unpacker/obfuscation_detector.py +103 -0
  33. angr/analyses/unpacker/packing_detector.py +138 -0
  34. angr/angrdb/models.py +2 -1
  35. angr/angrdb/serializers/kb.py +3 -3
  36. angr/angrdb/serializers/structured_code.py +5 -3
  37. angr/calling_conventions.py +4 -2
  38. angr/engines/vex/claripy/irop.py +10 -5
  39. angr/knowledge_base.py +1 -1
  40. angr/knowledge_plugins/__init__.py +2 -2
  41. angr/knowledge_plugins/obfuscations.py +36 -0
  42. angr/knowledge_plugins/structured_code.py +1 -1
  43. angr/utils/ssa/__init__.py +8 -3
  44. {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/METADATA +6 -6
  45. {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/RECORD +49 -39
  46. {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/WHEEL +1 -1
  47. angr/knowledge_plugins/decompilation.py +0 -45
  48. {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/LICENSE +0 -0
  49. {angr-9.2.125.dist-info → angr-9.2.127.dist-info}/entry_points.txt +0 -0
  50. {angr-9.2.125.dist-info → angr-9.2.127.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.125"
5
+ __version__ = "9.2.127"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
angr/analyses/__init__.py CHANGED
@@ -53,6 +53,8 @@ from .codecave import CodeCaveAnalysis
53
53
  from .patchfinder import PatchFinderAnalysis
54
54
  from .pathfinder import Pathfinder
55
55
  from .smc import SelfModifyingCodeAnalysis
56
+ from .unpacker import PackingDetector
57
+ from . import deobfuscator
56
58
 
57
59
 
58
60
  __all__ = (
@@ -109,4 +111,6 @@ __all__ = (
109
111
  "PatchFinderAnalysis",
110
112
  "Pathfinder",
111
113
  "SelfModifyingCodeAnalysis",
114
+ "PackingDetector",
115
+ "deobfuscator",
112
116
  )
angr/analyses/analysis.py CHANGED
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, TypeVar, Generic, cast
9
9
  from collections.abc import Callable
10
10
  from types import NoneType
11
11
  from itertools import chain
12
+ from traceback import format_exception
12
13
 
13
14
  import logging
14
15
  import time
@@ -75,6 +76,11 @@ class AnalysisLogEntry:
75
76
 
76
77
  self.message = message
77
78
 
79
+ def format(self) -> str:
80
+ if self.exc_traceback is None:
81
+ return self.message
82
+ return "\n".join((*format_exception(self.exc_type, self.exc_value, self.exc_traceback), "", self.message))
83
+
78
84
  def __getstate__(self):
79
85
  return (
80
86
  str(self.__dict__.get("exc_type")),
@@ -281,8 +287,8 @@ class Analysis:
281
287
  kb: KnowledgeBase
282
288
  _fail_fast: bool
283
289
  _name: str
284
- errors = []
285
- named_errors = defaultdict(list)
290
+ errors: list[AnalysisLogEntry] = []
291
+ named_errors: defaultdict[str, list[AnalysisLogEntry]] = defaultdict(list)
286
292
  _progress_callback = None
287
293
  _show_progressbar = False
288
294
  _progressbar = None
@@ -3309,7 +3309,18 @@ class CFGFast(ForwardAnalysis[CFGNode, CFGNode, CFGJob, int], CFGBase): # pylin
3309
3309
  # this is not a no-op block. Determine where nop instructions terminate.
3310
3310
  insns = block.capstone.insns
3311
3311
  if insns:
3312
- nop_length = self._get_nop_length(insns)
3312
+ if (
3313
+ self.project.simos is not None
3314
+ and self.project.simos.name == "Win32"
3315
+ and insns[0].mnemonic == "mov"
3316
+ ):
3317
+ op0, op1 = insns[0].operands
3318
+ if op0.type == 1 and op1.type == 1 and op0.reg == op1.reg:
3319
+ # hot-patch points on Windows DLLs
3320
+ # https://devblogs.microsoft.com/oldnewthing/20110921-00/?p=9583
3321
+ nop_length = None
3322
+ else:
3323
+ nop_length = self._get_nop_length(insns)
3313
3324
 
3314
3325
  if nop_length is None or nop_length <= 0:
3315
3326
  continue
@@ -872,6 +872,7 @@ class AILSimplifier(Analysis):
872
872
  Const(None, None, eq.atom0.addr, self.project.arch.bits),
873
873
  eq.atom0.size,
874
874
  endness=self.project.arch.memory_endness,
875
+ **eq.atom1.tags,
875
876
  )
876
877
  elif isinstance(eq.atom0, VirtualVariable) and eq.atom0.was_reg:
877
878
  if isinstance(eq.atom1, VirtualVariable) and eq.atom1.was_reg:
@@ -157,7 +157,15 @@ class CallSiteMaker(Analysis):
157
157
  )
158
158
  args.append(vvar_use)
159
159
  else:
160
- args.append(Expr.Register(self._atom_idx(), None, offset, size * 8, reg_name=arg_loc.reg_name))
160
+ reg = Expr.Register(
161
+ self._atom_idx(),
162
+ None,
163
+ offset,
164
+ size * 8,
165
+ reg_name=arg_loc.reg_name,
166
+ ins_addr=last_stmt.ins_addr,
167
+ )
168
+ args.append(reg)
161
169
  elif isinstance(arg_loc, SimStackArg):
162
170
  stack_arg_locs.append(arg_loc)
163
171
  _, the_arg = self._resolve_stack_argument(call_stmt, arg_loc)
@@ -1043,6 +1043,7 @@ class Clinic(Analysis):
1043
1043
  reg_name=self.project.arch.translate_register_name(
1044
1044
  ret_reg_offset, size=self.project.arch.bits
1045
1045
  ),
1046
+ **target.tags,
1046
1047
  )
1047
1048
  call_stmt = ailment.Stmt.Call(
1048
1049
  None,
@@ -1812,7 +1813,7 @@ class Clinic(Analysis):
1812
1813
  s = self.kb.custom_strings[expr.value]
1813
1814
  expr.tags["reference_values"] = {
1814
1815
  SimTypePointer(SimTypeChar().with_arch(self.project.arch)).with_arch(self.project.arch): s.decode(
1815
- "ascii"
1816
+ "latin-1"
1816
1817
  ),
1817
1818
  }
1818
1819
  else:
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
  from collections import defaultdict, OrderedDict
3
3
  from typing import Any
4
+ from collections.abc import Callable
4
5
  from collections.abc import Generator
5
6
  import operator
6
7
  import logging
@@ -80,17 +81,17 @@ _INVERSE_OPERATIONS = {
80
81
  #
81
82
 
82
83
 
83
- def _op_with_unified_size(op, conv, operand0, operand1):
84
+ def _op_with_unified_size(op, conv: Callable, operand0, operand1, ins_addr: int):
84
85
  # ensure operand1 is of the same size as operand0
85
86
  if isinstance(operand1, ailment.Expr.Const):
86
87
  # amazing - we do the easy thing here
87
- return op(conv(operand0, nobool=True), operand1.value)
88
+ return op(conv(operand0, nobool=True, ins_addr=ins_addr), operand1.value)
88
89
  if operand1.bits == operand0.bits:
89
- return op(conv(operand0, nobool=True), conv(operand1))
90
+ return op(conv(operand0, nobool=True, ins_addr=ins_addr), conv(operand1, ins_addr=ins_addr))
90
91
  # extension is required
91
92
  assert operand1.bits < operand0.bits
92
93
  operand1 = ailment.Expr.Convert(None, operand1.bits, operand0.bits, False, operand1)
93
- return op(conv(operand0, nobool=True), conv(operand1, nobool=True))
94
+ return op(conv(operand0, nobool=True, ins_addr=ins_addr), conv(operand1, nobool=True, ins_addr=ins_addr))
94
95
 
95
96
 
96
97
  def _dummy_bvs(condition, condition_mapping, name_suffix=""):
@@ -106,62 +107,94 @@ def _dummy_bools(condition, condition_mapping, name_suffix=""):
106
107
 
107
108
 
108
109
  _ail2claripy_op_mapping = {
109
- "LogicalAnd": lambda expr, conv, _: claripy.And(conv(expr.operands[0]), conv(expr.operands[1])),
110
- "LogicalOr": lambda expr, conv, _: claripy.Or(conv(expr.operands[0]), conv(expr.operands[1])),
111
- "CmpEQ": lambda expr, conv, _: conv(expr.operands[0]) == conv(expr.operands[1]),
112
- "CmpNE": lambda expr, conv, _: conv(expr.operands[0]) != conv(expr.operands[1]),
113
- "CmpLE": lambda expr, conv, _: conv(expr.operands[0]) <= conv(expr.operands[1]),
114
- "CmpLEs": lambda expr, conv, _: claripy.SLE(conv(expr.operands[0]), conv(expr.operands[1])),
115
- "CmpLT": lambda expr, conv, _: conv(expr.operands[0]) < conv(expr.operands[1]),
116
- "CmpLTs": lambda expr, conv, _: claripy.SLT(conv(expr.operands[0]), conv(expr.operands[1])),
117
- "CmpGE": lambda expr, conv, _: conv(expr.operands[0]) >= conv(expr.operands[1]),
118
- "CmpGEs": lambda expr, conv, _: claripy.SGE(conv(expr.operands[0]), conv(expr.operands[1])),
119
- "CmpGT": lambda expr, conv, _: conv(expr.operands[0]) > conv(expr.operands[1]),
120
- "CmpGTs": lambda expr, conv, _: claripy.SGT(conv(expr.operands[0]), conv(expr.operands[1])),
121
- "CasCmpEQ": lambda expr, conv, _: conv(expr.operands[0]) == conv(expr.operands[1]),
122
- "CasCmpNE": lambda expr, conv, _: conv(expr.operands[0]) != conv(expr.operands[1]),
123
- "CasCmpLE": lambda expr, conv, _: conv(expr.operands[0]) <= conv(expr.operands[1]),
124
- "CasCmpLEs": lambda expr, conv, _: claripy.SLE(conv(expr.operands[0]), conv(expr.operands[1])),
125
- "CasCmpLT": lambda expr, conv, _: conv(expr.operands[0]) < conv(expr.operands[1]),
126
- "CasCmpLTs": lambda expr, conv, _: claripy.SLT(conv(expr.operands[0]), conv(expr.operands[1])),
127
- "CasCmpGE": lambda expr, conv, _: conv(expr.operands[0]) >= conv(expr.operands[1]),
128
- "CasCmpGEs": lambda expr, conv, _: claripy.SGE(conv(expr.operands[0]), conv(expr.operands[1])),
129
- "CasCmpGT": lambda expr, conv, _: conv(expr.operands[0]) > conv(expr.operands[1]),
130
- "CasCmpGTs": lambda expr, conv, _: claripy.SGT(conv(expr.operands[0]), conv(expr.operands[1])),
131
- "Add": lambda expr, conv, _: conv(expr.operands[0], nobool=True) + conv(expr.operands[1], nobool=True),
132
- "Sub": lambda expr, conv, _: conv(expr.operands[0], nobool=True) - conv(expr.operands[1], nobool=True),
133
- "Mul": lambda expr, conv, _: conv(expr.operands[0], nobool=True) * conv(expr.operands[1], nobool=True),
134
- "Div": lambda expr, conv, _: conv(expr.operands[0], nobool=True) / conv(expr.operands[1], nobool=True),
135
- "Mod": lambda expr, conv, _: conv(expr.operands[0], nobool=True) % conv(expr.operands[1], nobool=True),
136
- "Not": lambda expr, conv, _: claripy.Not(conv(expr.operand)),
137
- "Neg": lambda expr, conv, _: -conv(expr.operand),
138
- "BitwiseNeg": lambda expr, conv, _: ~conv(expr.operand),
139
- "Xor": lambda expr, conv, _: conv(expr.operands[0], nobool=True) ^ conv(expr.operands[1], nobool=True),
140
- "And": lambda expr, conv, _: conv(expr.operands[0], nobool=True) & conv(expr.operands[1], nobool=True),
141
- "Or": lambda expr, conv, _: conv(expr.operands[0], nobool=True) | conv(expr.operands[1], nobool=True),
142
- "Shr": lambda expr, conv, _: _op_with_unified_size(claripy.LShR, conv, expr.operands[0], expr.operands[1]),
143
- "Shl": lambda expr, conv, _: _op_with_unified_size(operator.lshift, conv, expr.operands[0], expr.operands[1]),
144
- "Sar": lambda expr, conv, _: _op_with_unified_size(operator.rshift, conv, expr.operands[0], expr.operands[1]),
145
- "Concat": lambda expr, conv, _: claripy.Concat(*[conv(operand) for operand in expr.operands]),
110
+ "LogicalAnd": lambda expr, conv, _, ia: claripy.And(
111
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
112
+ ),
113
+ "LogicalOr": lambda expr, conv, _, ia: claripy.Or(
114
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
115
+ ),
116
+ "CmpEQ": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) == conv(expr.operands[1], ins_addr=ia),
117
+ "CmpNE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) != conv(expr.operands[1], ins_addr=ia),
118
+ "CmpLE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) <= conv(expr.operands[1], ins_addr=ia),
119
+ "CmpLEs": lambda expr, conv, _, ia: claripy.SLE(
120
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
121
+ ),
122
+ "CmpLT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) < conv(expr.operands[1], ins_addr=ia),
123
+ "CmpLTs": lambda expr, conv, _, ia: claripy.SLT(
124
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
125
+ ),
126
+ "CmpGE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) >= conv(expr.operands[1], ins_addr=ia),
127
+ "CmpGEs": lambda expr, conv, _, ia: claripy.SGE(
128
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
129
+ ),
130
+ "CmpGT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) > conv(expr.operands[1], ins_addr=ia),
131
+ "CmpGTs": lambda expr, conv, _, ia: claripy.SGT(
132
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
133
+ ),
134
+ "CasCmpEQ": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) == conv(expr.operands[1], ins_addr=ia),
135
+ "CasCmpNE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) != conv(expr.operands[1], ins_addr=ia),
136
+ "CasCmpLE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) <= conv(expr.operands[1], ins_addr=ia),
137
+ "CasCmpLEs": lambda expr, conv, _, ia: claripy.SLE(
138
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
139
+ ),
140
+ "CasCmpLT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) < conv(expr.operands[1], ins_addr=ia),
141
+ "CasCmpLTs": lambda expr, conv, _, ia: claripy.SLT(
142
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
143
+ ),
144
+ "CasCmpGE": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) >= conv(expr.operands[1], ins_addr=ia),
145
+ "CasCmpGEs": lambda expr, conv, _, ia: claripy.SGE(
146
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
147
+ ),
148
+ "CasCmpGT": lambda expr, conv, _, ia: conv(expr.operands[0], ins_addr=ia) > conv(expr.operands[1], ins_addr=ia),
149
+ "CasCmpGTs": lambda expr, conv, _, ia: claripy.SGT(
150
+ conv(expr.operands[0], ins_addr=ia), conv(expr.operands[1], ins_addr=ia)
151
+ ),
152
+ "Add": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
153
+ + conv(expr.operands[1], nobool=True, ins_addr=ia),
154
+ "Sub": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
155
+ - conv(expr.operands[1], nobool=True, ins_addr=ia),
156
+ "Mul": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
157
+ * conv(expr.operands[1], nobool=True, ins_addr=ia),
158
+ "Div": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
159
+ / conv(expr.operands[1], nobool=True, ins_addr=ia),
160
+ "Mod": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
161
+ % conv(expr.operands[1], nobool=True, ins_addr=ia),
162
+ "Not": lambda expr, conv, _, ia: claripy.Not(conv(expr.operand, ins_addr=ia)),
163
+ "Neg": lambda expr, conv, _, ia: -conv(expr.operand, ins_addr=ia),
164
+ "BitwiseNeg": lambda expr, conv, _, ia: ~conv(expr.operand, ins_addr=ia),
165
+ "Xor": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
166
+ ^ conv(expr.operands[1], nobool=True, ins_addr=ia),
167
+ "And": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
168
+ & conv(expr.operands[1], nobool=True, ins_addr=ia),
169
+ "Or": lambda expr, conv, _, ia: conv(expr.operands[0], nobool=True, ins_addr=ia)
170
+ | conv(expr.operands[1], nobool=True, ins_addr=ia),
171
+ "Shr": lambda expr, conv, _, ia: _op_with_unified_size(claripy.LShR, conv, expr.operands[0], expr.operands[1], ia),
172
+ "Shl": lambda expr, conv, _, ia: _op_with_unified_size(
173
+ operator.lshift, conv, expr.operands[0], expr.operands[1], ia
174
+ ),
175
+ "Sar": lambda expr, conv, _, ia: _op_with_unified_size(
176
+ operator.rshift, conv, expr.operands[0], expr.operands[1], ia
177
+ ),
178
+ "Concat": lambda expr, conv, _, ia: claripy.Concat(*[conv(operand, ins_addr=ia) for operand in expr.operands]),
146
179
  # There are no corresponding claripy operations for the following operations
147
- "DivMod": lambda expr, _, m: _dummy_bvs(expr, m),
148
- "CmpF": lambda expr, _, m: _dummy_bvs(expr, m),
149
- "Mull": lambda expr, _, m: _dummy_bvs(expr, m),
150
- "Mulls": lambda expr, _, m: _dummy_bvs(expr, m),
151
- "Reinterpret": lambda expr, _, m: _dummy_bvs(expr, m),
152
- "Rol": lambda expr, _, m: _dummy_bvs(expr, m),
153
- "Ror": lambda expr, _, m: _dummy_bvs(expr, m),
154
- "LogicalXor": lambda expr, _, m: _dummy_bvs(expr, m),
155
- "Carry": lambda expr, _, m: _dummy_bvs(expr, m),
156
- "SCarry": lambda expr, _, m: _dummy_bvs(expr, m),
157
- "SBorrow": lambda expr, _, m: _dummy_bvs(expr, m),
158
- "ExpCmpNE": lambda expr, _, m: _dummy_bools(expr, m),
159
- "CmpORD": lambda expr, _, m: _dummy_bvs(expr, m), # in case CmpORDRewriter fails
160
- "GetMSBs": lambda expr, _, m: _dummy_bvs(expr, m),
161
- "InterleaveLOV": lambda expr, _, m: _dummy_bvs(expr, m),
162
- "InterleaveHIV": lambda expr, _, m: _dummy_bvs(expr, m),
180
+ "DivMod": lambda expr, _, m, *args: _dummy_bvs(expr, m),
181
+ "CmpF": lambda expr, _, m, *args: _dummy_bvs(expr, m),
182
+ "Mull": lambda expr, _, m, *args: _dummy_bvs(expr, m),
183
+ "Mulls": lambda expr, _, m, *args: _dummy_bvs(expr, m),
184
+ "Reinterpret": lambda expr, _, m, *args: _dummy_bvs(expr, m),
185
+ "Rol": lambda expr, _, m, *args: _dummy_bvs(expr, m),
186
+ "Ror": lambda expr, _, m, *args: _dummy_bvs(expr, m),
187
+ "LogicalXor": lambda expr, _, m, *args: _dummy_bvs(expr, m),
188
+ "Carry": lambda expr, _, m, *args: _dummy_bvs(expr, m),
189
+ "SCarry": lambda expr, _, m, *args: _dummy_bvs(expr, m),
190
+ "SBorrow": lambda expr, _, m, *args: _dummy_bvs(expr, m),
191
+ "ExpCmpNE": lambda expr, _, m, *args: _dummy_bools(expr, m),
192
+ "CmpORD": lambda expr, _, m, *args: _dummy_bvs(expr, m), # in case CmpORDRewriter fails
193
+ "GetMSBs": lambda expr, _, m, *args: _dummy_bvs(expr, m),
194
+ "InterleaveLOV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
195
+ "InterleaveHIV": lambda expr, _, m, *args: _dummy_bvs(expr, m),
163
196
  # catch-all
164
- "_DUMMY_": lambda expr, _, m: _dummy_bvs(expr, m),
197
+ "_DUMMY_": lambda expr, _, m, *args: _dummy_bvs(expr, m),
165
198
  }
166
199
 
167
200
  #
@@ -610,6 +643,7 @@ class ConditionProcessor:
610
643
  ),
611
644
  False,
612
645
  ),
646
+ ins_addr=dst_block.addr,
613
647
  )
614
648
 
615
649
  if type(src_block) is ConditionalBreakNode:
@@ -638,10 +672,10 @@ class ConditionProcessor:
638
672
  if isinstance(last_stmt.target, ailment.Expr.Const):
639
673
  return claripy.true()
640
674
  # indirect jump
641
- target_ast = self.claripy_ast_from_ail_condition(last_stmt.target)
675
+ target_ast = self.claripy_ast_from_ail_condition(last_stmt.target, ins_addr=last_stmt.ins_addr)
642
676
  return target_ast == dst_block.addr
643
677
  if type(last_stmt) is ailment.Stmt.ConditionalJump:
644
- bool_var = self.claripy_ast_from_ail_condition(last_stmt.condition)
678
+ bool_var = self.claripy_ast_from_ail_condition(last_stmt.condition, ins_addr=last_stmt.ins_addr)
645
679
  if isinstance(last_stmt.true_target, ailment.Expr.Const) and last_stmt.true_target.value == dst_block.addr:
646
680
  return bool_var
647
681
  return claripy.Not(bool_var)
@@ -766,7 +800,7 @@ class ConditionProcessor:
766
800
  f"Condition variable {cond} has an unsupported operator {cond.op}. Consider implementing."
767
801
  )
768
802
 
769
- def claripy_ast_from_ail_condition(self, condition, nobool: bool = False) -> claripy.ast.Bool:
803
+ def claripy_ast_from_ail_condition(self, condition, nobool: bool = False, *, ins_addr: int = 0) -> claripy.ast.Bool:
770
804
  # Unpack a condition all the way to the leaves
771
805
  if isinstance(condition, claripy.ast.Base): # pylint:disable=isinstance-second-argument-not-valid-type
772
806
  return condition
@@ -782,20 +816,24 @@ class ConditionProcessor:
782
816
  # does it have a variable associated?
783
817
  if condition.variable is not None:
784
818
  var = claripy.BVS(
785
- f"ailexpr_{condition!r}-{condition.variable.ident}", condition.bits, explicit_name=True
819
+ f"ailexpr_{condition!r}-{condition.variable.ident}-{ins_addr:x}",
820
+ condition.bits,
821
+ explicit_name=True,
786
822
  )
787
823
  else:
788
- var = claripy.BVS(f"ailexpr_{condition!r}-{condition.idx}", condition.bits, explicit_name=True)
824
+ var = claripy.BVS(
825
+ f"ailexpr_{condition!r}-{condition.idx}-{ins_addr:x}", condition.bits, explicit_name=True
826
+ )
789
827
  self._condition_mapping[var.args[0]] = condition
790
828
  return var
791
829
  if isinstance(condition, ailment.Expr.Convert):
792
830
  # convert is special. if it generates a 1-bit variable, it should be treated as a BoolS
793
831
  if condition.to_bits == 1:
794
- var_ = self.claripy_ast_from_ail_condition(condition.operands[0])
832
+ var_ = self.claripy_ast_from_ail_condition(condition.operands[0], ins_addr=ins_addr)
795
833
  name = "ailcond_Conv(%d->%d, %d)" % (condition.from_bits, condition.to_bits, hash(var_))
796
834
  var = claripy.BoolS(name, explicit_name=True)
797
835
  else:
798
- var_ = self.claripy_ast_from_ail_condition(condition.operands[0])
836
+ var_ = self.claripy_ast_from_ail_condition(condition.operands[0], ins_addr=ins_addr)
799
837
  name = "ailexpr_Conv(%d->%d, %d)" % (condition.from_bits, condition.to_bits, hash(var_))
800
838
  var = claripy.BVS(name, condition.to_bits, explicit_name=True)
801
839
  self._condition_mapping[var.args[0]] = condition
@@ -838,7 +876,7 @@ class ConditionProcessor:
838
876
  condition.verbose_op,
839
877
  )
840
878
  lambda_expr = _ail2claripy_op_mapping["_DUMMY_"]
841
- r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping)
879
+ r = lambda_expr(condition, self.claripy_ast_from_ail_condition, self._condition_mapping, ins_addr)
842
880
 
843
881
  if isinstance(r, claripy.ast.Bool) and nobool:
844
882
  r = claripy.BVS(f"ailexpr_from_bool_{r!r}", 1, explicit_name=True)
@@ -1075,17 +1113,15 @@ class ConditionProcessor:
1075
1113
  r1_with: claripy.ast.Bool,
1076
1114
  ) -> claripy.ast.Bool:
1077
1115
  if ast.op == "And":
1078
- return ast.make_like(
1079
- "And", (ConditionProcessor._replace_term_in_ast(arg, r0, r0_with, r1, r1_with) for arg in ast.args)
1116
+ return claripy.And(
1117
+ *(ConditionProcessor._replace_term_in_ast(arg, r0, r0_with, r1, r1_with) for arg in ast.args)
1080
1118
  )
1081
1119
  if ast.op == "Or":
1082
- return ast.make_like(
1083
- "Or", (ConditionProcessor._replace_term_in_ast(arg, r0, r0_with, r1, r1_with) for arg in ast.args)
1120
+ return claripy.Or(
1121
+ *(ConditionProcessor._replace_term_in_ast(arg, r0, r0_with, r1, r1_with) for arg in ast.args)
1084
1122
  )
1085
1123
  if ast.op == "Not":
1086
- return ast.make_like(
1087
- "Not", (ConditionProcessor._replace_term_in_ast(ast.args[0], r0, r0_with, r1, r1_with),)
1088
- )
1124
+ return claripy.Not(ConditionProcessor._replace_term_in_ast(ast.args[0], r0, r0_with, r1, r1_with))
1089
1125
  if ast is r0:
1090
1126
  return r0_with
1091
1127
  if ast is r1:
@@ -23,6 +23,7 @@ class DecompilationCache:
23
23
  "clinic",
24
24
  "ite_exprs",
25
25
  "binop_operators",
26
+ "errors",
26
27
  )
27
28
 
28
29
  def __init__(self, addr):
@@ -35,7 +36,10 @@ class DecompilationCache:
35
36
  self.clinic: Clinic | None = None
36
37
  self.ite_exprs: set[tuple[int, Any]] | None = None
37
38
  self.binop_operators: dict[OpDescriptor, str] | None = None
39
+ self.errors: list[str] = []
38
40
 
39
41
  @property
40
42
  def local_types(self):
43
+ if self.clinic is None or self.clinic.variable_kb is None:
44
+ return None
41
45
  return self.clinic.variable_kb.variables[self.addr].types
@@ -70,6 +70,7 @@ class Decompiler(Analysis):
70
70
  update_memory_data: bool = True,
71
71
  generate_code: bool = True,
72
72
  use_cache: bool = True,
73
+ expr_collapse_depth: int = 16,
73
74
  ):
74
75
  if not isinstance(func, Function):
75
76
  func = self.kb.functions[func]
@@ -135,9 +136,25 @@ class Decompiler(Analysis):
135
136
  self.ail_graph: networkx.DiGraph | None = None
136
137
  self.vvar_id_start = None
137
138
  self._optimization_scratch: dict[str, Any] = {}
139
+ self.expr_collapse_depth = expr_collapse_depth
138
140
 
139
141
  if decompile:
140
- self._decompile()
142
+ with self._resilience():
143
+ self._decompile()
144
+ if self.errors:
145
+ if (self.func.addr, self._flavor) not in self.kb.decompilations:
146
+ self.kb.decompilations[(self.func.addr, self._flavor)] = DecompilationCache(self.func.addr)
147
+ for error in self.errors:
148
+ self.kb.decompilations[(self.func.addr, self._flavor)].errors.append(error.format())
149
+ with self._resilience():
150
+ l.info("Decompilation failed for %s. Switching to basic preset and trying again.")
151
+ if preset != DECOMPILATION_PRESETS["basic"]:
152
+ self._optimization_passes = DECOMPILATION_PRESETS["basic"].get_optimization_passes(
153
+ self.project.arch, self.project.simos.name
154
+ )
155
+ self._decompile()
156
+ for error in self.errors:
157
+ self.kb.decompilations[(self.func.addr, self._flavor)].errors.append(error.format())
141
158
 
142
159
  def _can_use_decompilation_cache(self, cache: DecompilationCache) -> bool:
143
160
  a, b = self._cache_parameters, cache.parameters
@@ -153,7 +170,7 @@ class Decompiler(Analysis):
153
170
 
154
171
  if self._cache_parameters is not None:
155
172
  try:
156
- cache = self.kb.decompilations[self.func.addr]
173
+ cache = self.kb.decompilations[(self.func.addr, self._flavor)]
157
174
  if not self._can_use_decompilation_cache(cache):
158
175
  cache = None
159
176
  except KeyError:
@@ -333,6 +350,7 @@ class Decompiler(Analysis):
333
350
  stmt_comments=old_codegen.stmt_comments if old_codegen is not None else None,
334
351
  const_formats=old_codegen.const_formats if old_codegen is not None else None,
335
352
  externs=clinic.externs,
353
+ binop_depth_cutoff=self.expr_collapse_depth,
336
354
  **self.options_to_params(self.options_by_class["codegen"]),
337
355
  )
338
356
 
@@ -344,7 +362,7 @@ class Decompiler(Analysis):
344
362
  self.cache.codegen = codegen
345
363
  self.cache.clinic = self.clinic
346
364
 
347
- self.kb.decompilations[self.func.addr] = self.cache
365
+ self.kb.decompilations[(self.func.addr, self._flavor)] = self.cache
348
366
 
349
367
  def _recover_regions(self, graph: networkx.DiGraph, condition_processor, update_graph: bool = True):
350
368
  return self.project.analyses[RegionIdentifier].prep(kb=self.kb)(
@@ -290,8 +290,7 @@ class GraphDephicationVVarMapping(Analysis): # pylint:disable=abstract-method
290
290
  for stmt in dst_block.statements:
291
291
  if isinstance(stmt, Label):
292
292
  continue
293
- r, _ = is_phi_assignment(stmt)
294
- if r:
293
+ if is_phi_assignment(stmt):
295
294
  for src_, vvar in stmt.src.src_and_vvars:
296
295
  if src_ == src and vvar is not None and vvar.varid == vvar_id:
297
296
  return True
@@ -1,5 +1,6 @@
1
1
  # pylint:disable=import-outside-toplevel
2
2
  from __future__ import annotations
3
+ from typing import TYPE_CHECKING
3
4
 
4
5
  from archinfo import Arch
5
6
 
@@ -32,6 +33,10 @@ from .const_prop_reverter import ConstPropOptReverter
32
33
  from .call_stmt_rewriter import CallStatementRewriter
33
34
  from .duplication_reverter import DuplicationReverter
34
35
 
36
+ if TYPE_CHECKING:
37
+ from angr.analyses.decompiler.presets import DecompilationPreset
38
+
39
+
35
40
  # order matters!
36
41
  ALL_OPTIMIZATION_PASSES = [
37
42
  RegisterSaveAreaSimplifier,
@@ -88,9 +93,18 @@ def get_optimization_passes(arch, platform):
88
93
  return passes
89
94
 
90
95
 
91
- def register_optimization_pass(opt_pass):
96
+ def register_optimization_pass(opt_pass, *, presets: list[str | DecompilationPreset] | None = None):
92
97
  ALL_OPTIMIZATION_PASSES.append(opt_pass)
93
98
 
99
+ if presets:
100
+ from angr.analyses.decompiler.presets import DECOMPILATION_PRESETS
101
+
102
+ for preset in presets:
103
+ if isinstance(preset, str):
104
+ preset = DECOMPILATION_PRESETS[preset] # intentionally raise a KeyError if the preset is not found
105
+ if opt_pass not in preset.opt_passes:
106
+ preset.opt_passes.append(opt_pass)
107
+
94
108
 
95
109
  __all__ = (
96
110
  "OptimizationPassStage",
@@ -48,6 +48,7 @@ class ReturnMaker(AILGraphWalker):
48
48
  reg[0],
49
49
  ret_val.size * self.arch.byte_width,
50
50
  reg_name=self.arch.translate_register_name(reg[0], ret_val.size),
51
+ ins_addr=stmt.ins_addr,
51
52
  )
52
53
  )
53
54
  else:
@@ -119,6 +119,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
119
119
  self._ail_manager.next_atom(),
120
120
  reg_bits,
121
121
  src_and_vvars=[], # back patch later
122
+ ins_addr=node.addr,
122
123
  )
123
124
  phi_dst = VirtualVariable(
124
125
  self._ail_manager.next_atom(),
@@ -126,6 +127,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
126
127
  reg_bits,
127
128
  VirtualVariableCategory.REGISTER,
128
129
  oident=reg_offset,
130
+ ins_addr=node.addr,
129
131
  )
130
132
 
131
133
  case "stack":
@@ -135,6 +137,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
135
137
  self._ail_manager.next_atom(),
136
138
  stack_size * self.project.arch.byte_width,
137
139
  src_and_vvars=[], # back patch later
140
+ ins_addr=node.addr,
138
141
  )
139
142
  phi_dst = VirtualVariable(
140
143
  self._ail_manager.next_atom(),
@@ -142,6 +145,7 @@ class RewritingAnalysis(ForwardAnalysis[RewritingState, NodeType, object, object
142
145
  stack_size * self.project.arch.byte_width,
143
146
  VirtualVariableCategory.STACK,
144
147
  oident=stack_offset,
148
+ ins_addr=node.addr,
145
149
  )
146
150
  case _:
147
151
  raise NotImplementedError
@@ -525,7 +525,7 @@ class SimEngineSSARewriting(
525
525
  **expr.tags,
526
526
  )
527
527
 
528
- def _get_full_reg_vvar(self, reg_offset: int, size: int) -> VirtualVariable:
528
+ def _get_full_reg_vvar(self, reg_offset: int, size: int, ins_addr: int | None = None) -> VirtualVariable:
529
529
  base_off, base_size = get_reg_offset_base_and_size(reg_offset, self.arch, size=size)
530
530
  if (
531
531
  base_off not in self.state.registers
@@ -534,13 +534,16 @@ class SimEngineSSARewriting(
534
534
  ):
535
535
  # somehow it's never defined before...
536
536
  _l.debug("Creating a new virtual variable for an undefined register (%d [%d]).", base_off, base_size)
537
+ tags = {}
538
+ if ins_addr is not None:
539
+ tags["ins_addr"] = ins_addr
537
540
  vvar = VirtualVariable(
538
541
  self.ail_manager.next_atom(),
539
542
  self.next_vvar_id(),
540
543
  base_size * self.arch.byte_width,
541
544
  category=VirtualVariableCategory.REGISTER,
542
545
  oident=base_off,
543
- # FIXME: tags
546
+ **tags,
544
547
  )
545
548
  self.state.registers[base_off][base_size] = vvar
546
549
  return vvar
@@ -628,7 +631,11 @@ class SimEngineSSARewriting(
628
631
 
629
632
  # no good size available
630
633
  # get the full register, then extract from there
631
- vvar = self._get_full_reg_vvar(reg_expr.reg_offset, reg_expr.size)
634
+ vvar = self._get_full_reg_vvar(
635
+ reg_expr.reg_offset,
636
+ reg_expr.size,
637
+ ins_addr=reg_expr.ins_addr,
638
+ )
632
639
  # extract
633
640
  shift_amount = Const(
634
641
  self.ail_manager.next_atom(),
@@ -32,6 +32,7 @@ class TraversalAnalysis(ForwardAnalysis[None, NodeType, object, object]):
32
32
  )
33
33
  self._engine_ail = SimEngineSSATraversal(
34
34
  self.project.arch,
35
+ self.project.simos,
35
36
  sp_tracker=sp_tracker,
36
37
  bp_as_gpr=bp_as_gpr,
37
38
  stackvars=self._stackvars,