angr 9.2.124__py3-none-win_amd64.whl → 9.2.126__py3-none-win_amd64.whl

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

Potentially problematic release.


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

Files changed (53) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/__init__.py +13 -1
  3. angr/analyses/codecave.py +77 -0
  4. angr/analyses/decompiler/ail_simplifier.py +1 -0
  5. angr/analyses/decompiler/callsite_maker.py +9 -1
  6. angr/analyses/decompiler/clinic.py +32 -2
  7. angr/analyses/decompiler/condition_processor.py +104 -66
  8. angr/analyses/decompiler/decompiler.py +7 -0
  9. angr/analyses/decompiler/optimization_passes/__init__.py +18 -1
  10. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +6 -0
  11. angr/analyses/decompiler/optimization_passes/tag_slicer.py +41 -0
  12. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +2 -2
  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/structured_codegen/c.py +18 -2
  17. angr/analyses/deobfuscator/__init__.py +18 -0
  18. angr/analyses/deobfuscator/api_obf_finder.py +313 -0
  19. angr/analyses/deobfuscator/api_obf_peephole_optimizer.py +51 -0
  20. angr/analyses/deobfuscator/irsb_reg_collector.py +85 -0
  21. angr/analyses/deobfuscator/string_obf_finder.py +774 -0
  22. angr/analyses/deobfuscator/string_obf_opt_passes.py +133 -0
  23. angr/analyses/deobfuscator/string_obf_peephole_optimizer.py +47 -0
  24. angr/analyses/patchfinder.py +137 -0
  25. angr/analyses/pathfinder.py +282 -0
  26. angr/analyses/reaching_definitions/function_handler_library/stdio.py +8 -1
  27. angr/analyses/smc.py +159 -0
  28. angr/analyses/unpacker/__init__.py +6 -0
  29. angr/analyses/unpacker/obfuscation_detector.py +103 -0
  30. angr/analyses/unpacker/packing_detector.py +138 -0
  31. angr/angrdb/models.py +1 -2
  32. angr/calling_conventions.py +3 -1
  33. angr/engines/vex/claripy/irop.py +10 -5
  34. angr/engines/vex/heavy/heavy.py +2 -0
  35. angr/exploration_techniques/spiller_db.py +1 -2
  36. angr/knowledge_plugins/__init__.py +2 -0
  37. angr/knowledge_plugins/functions/function.py +4 -0
  38. angr/knowledge_plugins/functions/function_manager.py +18 -9
  39. angr/knowledge_plugins/functions/function_parser.py +1 -1
  40. angr/knowledge_plugins/functions/soot_function.py +1 -0
  41. angr/knowledge_plugins/obfuscations.py +36 -0
  42. angr/lib/angr_native.dll +0 -0
  43. angr/misc/ux.py +2 -2
  44. angr/project.py +17 -1
  45. angr/state_plugins/history.py +6 -4
  46. angr/utils/bits.py +4 -0
  47. angr/utils/tagged_interval_map.py +112 -0
  48. {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/METADATA +6 -6
  49. {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/RECORD +53 -36
  50. {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/WHEEL +1 -1
  51. {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/LICENSE +0 -0
  52. {angr-9.2.124.dist-info → angr-9.2.126.dist-info}/entry_points.txt +0 -0
  53. {angr-9.2.124.dist-info → angr-9.2.126.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.124"
5
+ __version__ = "9.2.126"
6
6
 
7
7
  if bytes is str:
8
8
  raise Exception(
angr/analyses/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- # pylint:disable=wrong-import-position
1
+ # " pylint:disable=wrong-import-position
2
2
  from __future__ import annotations
3
3
 
4
4
  from .analysis import Analysis, AnalysesHub
@@ -49,6 +49,12 @@ from .flirt import FlirtAnalysis
49
49
  from .s_propagator import SPropagatorAnalysis
50
50
  from .s_reaching_definitions import SReachingDefinitionsAnalysis
51
51
  from .s_liveness import SLivenessAnalysis
52
+ from .codecave import CodeCaveAnalysis
53
+ from .patchfinder import PatchFinderAnalysis
54
+ from .pathfinder import Pathfinder
55
+ from .smc import SelfModifyingCodeAnalysis
56
+ from .unpacker import PackingDetector
57
+ from . import deobfuscator
52
58
 
53
59
 
54
60
  __all__ = (
@@ -101,4 +107,10 @@ __all__ = (
101
107
  "SPropagatorAnalysis",
102
108
  "SReachingDefinitionsAnalysis",
103
109
  "SLivenessAnalysis",
110
+ "CodeCaveAnalysis",
111
+ "PatchFinderAnalysis",
112
+ "Pathfinder",
113
+ "SelfModifyingCodeAnalysis",
114
+ "PackingDetector",
115
+ "deobfuscator",
104
116
  )
@@ -0,0 +1,77 @@
1
+ from __future__ import annotations
2
+ import logging
3
+ from enum import Enum, auto
4
+ from typing import TYPE_CHECKING
5
+ from dataclasses import dataclass
6
+
7
+ from angr.analyses import Analysis, AnalysesHub
8
+
9
+
10
+ if TYPE_CHECKING:
11
+ from angr.knowledge_plugins import Function
12
+
13
+
14
+ log = logging.getLogger(__name__)
15
+
16
+
17
+ class CodeCaveClassification(Enum):
18
+ """
19
+ Type of code caves.
20
+ """
21
+
22
+ ALIGNMENT = auto()
23
+ UNREACHABLE = auto()
24
+
25
+
26
+ @dataclass
27
+ class CodeCave:
28
+ """
29
+ Describes a code cave in a binary.
30
+ """
31
+
32
+ func: Function | None
33
+ addr: int
34
+ size: int
35
+ classification: CodeCaveClassification
36
+
37
+
38
+ class CodeCaveAnalysis(Analysis):
39
+ """
40
+ Best-effort static location of potential vacant code caves for possible code injection:
41
+ - Padding functions
42
+ - Unreachable code
43
+ """
44
+
45
+ codecaves: list[CodeCave]
46
+
47
+ def __init__(self):
48
+ self.codecaves = []
49
+
50
+ if len(self.project.kb.functions) == 0 and self.project.kb.cfgs.get_most_accurate() is None:
51
+ log.warning("Please run CFGFast analysis first, to identify functions")
52
+ return
53
+
54
+ # Alignment functions
55
+ for func in self.project.kb.functions.values():
56
+ if func.is_alignment:
57
+ for block in func.blocks:
58
+ self.codecaves.append(CodeCave(func, block.addr, block.size, CodeCaveClassification.ALIGNMENT))
59
+
60
+ # Unreachable code
61
+ for func in self.project.kb.functions.values():
62
+ if func.is_alignment or func.is_plt or func.is_simprocedure or func.addr in self.project.kb.labels:
63
+ continue
64
+
65
+ in_degree = self.project.kb.callgraph.in_degree(func.addr)
66
+ if in_degree == 0 or (
67
+ in_degree == 1
68
+ and self.project.kb.functions[next(self.project.kb.callgraph.predecessors(func.addr))].is_alignment
69
+ ):
70
+ for block in func.blocks:
71
+ self.codecaves.append(CodeCave(func, block.addr, block.size, CodeCaveClassification.UNREACHABLE))
72
+
73
+ # FIXME: find dead blocks with argument propagation
74
+ # FIXME: find dead blocks with external coverage info
75
+
76
+
77
+ AnalysesHub.register_default("CodeCaves", CodeCaveAnalysis)
@@ -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)
@@ -42,6 +42,7 @@ from .optimization_passes import (
42
42
  OptimizationPassStage,
43
43
  RegisterSaveAreaSimplifier,
44
44
  StackCanarySimplifier,
45
+ TagSlicer,
45
46
  DUPLICATING_OPTS,
46
47
  CONDENSING_OPTS,
47
48
  )
@@ -111,6 +112,7 @@ class Clinic(Analysis):
111
112
  inlining_parents: set[int] | None = None,
112
113
  vvar_id_start: int = 0,
113
114
  optimization_scratch: dict[str, Any] | None = None,
115
+ desired_variables: set[str] | None = None,
114
116
  ):
115
117
  if not func.normalized and mode == ClinicMode.DECOMPILE:
116
118
  raise ValueError("Decompilation must work on normalized function graphs.")
@@ -154,6 +156,7 @@ class Clinic(Analysis):
154
156
  self._inline_functions = inline_functions
155
157
  self._inlined_counts = {} if inlined_counts is None else inlined_counts
156
158
  self._inlining_parents = inlining_parents or ()
159
+ self._desired_variables = desired_variables
157
160
 
158
161
  self._register_save_areas_removed: bool = False
159
162
 
@@ -220,6 +223,9 @@ class Clinic(Analysis):
220
223
  ail_graph = self._inline_child_functions(ail_graph)
221
224
 
222
225
  ail_graph = self._decompilation_simplifications(ail_graph)
226
+
227
+ if self._desired_variables:
228
+ ail_graph = self._slice_variables(ail_graph)
223
229
  self.graph = ail_graph
224
230
 
225
231
  def _decompilation_graph_recovery(self):
@@ -279,6 +285,27 @@ class Clinic(Analysis):
279
285
 
280
286
  return ail_graph
281
287
 
288
+ def _slice_variables(self, ail_graph):
289
+ nodes_index = {(n.addr, n.idx): n for n in ail_graph.nodes()}
290
+
291
+ vfm = self.variable_kb.variables.function_managers[self.function.addr]
292
+ for v_name in self._desired_variables:
293
+ v = next(iter(vv for vv in vfm._unified_variables if vv.name == v_name))
294
+ for va in vfm.get_variable_accesses(v):
295
+ nodes_index[(va.location.block_addr, va.location.block_idx)].statements[va.location.stmt_idx].tags[
296
+ "keep_in_slice"
297
+ ] = True
298
+
299
+ a = TagSlicer(
300
+ self.function,
301
+ graph=ail_graph,
302
+ variable_kb=self.variable_kb,
303
+ )
304
+ if a.out_graph:
305
+ # use the new graph
306
+ ail_graph = a.out_graph
307
+ return ail_graph
308
+
282
309
  def _inline_child_functions(self, ail_graph):
283
310
  for blk in ail_graph.nodes():
284
311
  for idx, stmt in enumerate(blk.statements):
@@ -909,13 +936,16 @@ class Clinic(Analysis):
909
936
  Convert a VEX block to an AIL block.
910
937
 
911
938
  :param block_node: A BlockNode instance.
912
- :return: An converted AIL block.
939
+ :return: A converted AIL block.
913
940
  :rtype: ailment.Block
914
941
  """
915
942
 
916
943
  if type(block_node) is not BlockNode:
917
944
  return block_node
918
945
 
946
+ if block_node.size == 0:
947
+ return ailment.Block(block_node.addr, 0, statements=[])
948
+
919
949
  block = self.project.factory.block(block_node.addr, block_node.size, cross_insn_opt=False)
920
950
  if block.vex.jumpkind not in {"Ijk_Call", "Ijk_Boring", "Ijk_Ret"} and not block.vex.jumpkind.startswith(
921
951
  "Ijk_Sys"
@@ -1782,7 +1812,7 @@ class Clinic(Analysis):
1782
1812
  s = self.kb.custom_strings[expr.value]
1783
1813
  expr.tags["reference_values"] = {
1784
1814
  SimTypePointer(SimTypeChar().with_arch(self.project.arch)).with_arch(self.project.arch): s.decode(
1785
- "ascii"
1815
+ "latin-1"
1786
1816
  ),
1787
1817
  }
1788
1818
  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)
@@ -66,9 +66,11 @@ class Decompiler(Analysis):
66
66
  decompile=True,
67
67
  regen_clinic=True,
68
68
  inline_functions=frozenset(),
69
+ desired_variables=frozenset(),
69
70
  update_memory_data: bool = True,
70
71
  generate_code: bool = True,
71
72
  use_cache: bool = True,
73
+ expr_collapse_depth: int = 16,
72
74
  ):
73
75
  if not isinstance(func, Function):
74
76
  func = self.kb.functions[func]
@@ -103,6 +105,7 @@ class Decompiler(Analysis):
103
105
  self._update_memory_data = update_memory_data
104
106
  self._generate_code = generate_code
105
107
  self._inline_functions = inline_functions
108
+ self._desired_variables = desired_variables
106
109
  self._cache_parameters = (
107
110
  {
108
111
  "cfg": self._cfg,
@@ -118,6 +121,7 @@ class Decompiler(Analysis):
118
121
  "ite_exprs": self._ite_exprs,
119
122
  "binop_operators": self._binop_operators,
120
123
  "inline_functions": self._inline_functions,
124
+ "desired_variables": self._desired_variables,
121
125
  }
122
126
  if use_cache
123
127
  else None
@@ -132,6 +136,7 @@ class Decompiler(Analysis):
132
136
  self.ail_graph: networkx.DiGraph | None = None
133
137
  self.vvar_id_start = None
134
138
  self._optimization_scratch: dict[str, Any] = {}
139
+ self.expr_collapse_depth = expr_collapse_depth
135
140
 
136
141
  if decompile:
137
142
  self._decompile()
@@ -226,6 +231,7 @@ class Decompiler(Analysis):
226
231
  cache=cache,
227
232
  progress_callback=progress_callback,
228
233
  inline_functions=self._inline_functions,
234
+ desired_variables=self._desired_variables,
229
235
  optimization_scratch=self._optimization_scratch,
230
236
  **self.options_to_params(self.options_by_class["clinic"]),
231
237
  )
@@ -329,6 +335,7 @@ class Decompiler(Analysis):
329
335
  stmt_comments=old_codegen.stmt_comments if old_codegen is not None else None,
330
336
  const_formats=old_codegen.const_formats if old_codegen is not None else None,
331
337
  externs=clinic.externs,
338
+ binop_depth_cutoff=self.expr_collapse_depth,
332
339
  **self.options_to_params(self.options_by_class["codegen"]),
333
340
  )
334
341
 
@@ -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
 
@@ -26,11 +27,16 @@ from .cross_jump_reverter import CrossJumpReverter
26
27
  from .code_motion import CodeMotionOptimization
27
28
  from .switch_default_case_duplicator import SwitchDefaultCaseDuplicator
28
29
  from .deadblock_remover import DeadblockRemover
30
+ from .tag_slicer import TagSlicer
29
31
  from .inlined_string_transformation_simplifier import InlinedStringTransformationSimplifier
30
32
  from .const_prop_reverter import ConstPropOptReverter
31
33
  from .call_stmt_rewriter import CallStatementRewriter
32
34
  from .duplication_reverter import DuplicationReverter
33
35
 
36
+ if TYPE_CHECKING:
37
+ from angr.analyses.decompiler.presets import DecompilationPreset
38
+
39
+
34
40
  # order matters!
35
41
  ALL_OPTIMIZATION_PASSES = [
36
42
  RegisterSaveAreaSimplifier,
@@ -59,6 +65,7 @@ ALL_OPTIMIZATION_PASSES = [
59
65
  FlipBooleanCmp,
60
66
  InlinedStringTransformationSimplifier,
61
67
  CallStatementRewriter,
68
+ TagSlicer,
62
69
  ]
63
70
 
64
71
  # these passes may duplicate code to remove gotos or improve the structure of the graph
@@ -86,9 +93,18 @@ def get_optimization_passes(arch, platform):
86
93
  return passes
87
94
 
88
95
 
89
- def register_optimization_pass(opt_pass):
96
+ def register_optimization_pass(opt_pass, *, presets: list[str | DecompilationPreset] | None = None):
90
97
  ALL_OPTIMIZATION_PASSES.append(opt_pass)
91
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
+
92
108
 
93
109
  __all__ = (
94
110
  "OptimizationPassStage",
@@ -118,6 +134,7 @@ __all__ = (
118
134
  "ConstPropOptReverter",
119
135
  "CallStatementRewriter",
120
136
  "DuplicationReverter",
137
+ "TagSlicer",
121
138
  "ALL_OPTIMIZATION_PASSES",
122
139
  "DUPLICATING_OPTS",
123
140
  "CONDENSING_OPTS",
@@ -240,6 +240,12 @@ class InlinedStringTransformationAILEngine(SimEngineLightAILMixin):
240
240
  return None
241
241
 
242
242
  def _handle_Neg(self, expr: UnaryOp):
243
+ v = self._expr(expr.operand)
244
+ if isinstance(v, claripy.ast.Bits):
245
+ return -v
246
+ return None
247
+
248
+ def _handle_BitwiseNeg(self, expr: UnaryOp):
243
249
  v = self._expr(expr.operand)
244
250
  if isinstance(v, claripy.ast.Bits):
245
251
  return ~v
@@ -0,0 +1,41 @@
1
+ # pylint:disable=too-many-boolean-expressions
2
+ from __future__ import annotations
3
+ import logging
4
+
5
+ from ailment.statement import ConditionalJump, Jump, Label
6
+ from .optimization_pass import OptimizationPass, OptimizationPassStage
7
+
8
+
9
+ _l = logging.getLogger(name=__name__)
10
+
11
+
12
+ class TagSlicer(OptimizationPass):
13
+ """
14
+ Removes unmarked statements from the graph.
15
+ """
16
+
17
+ ARCHES = None
18
+ PLATFORMS = None
19
+ STAGE = OptimizationPassStage.AFTER_VARIABLE_RECOVERY
20
+ NAME = "Remove unmarked statements from the graph."
21
+ DESCRIPTION = __doc__.strip()
22
+
23
+ def __init__(self, func, **kwargs):
24
+ super().__init__(func, **kwargs)
25
+ self.analyze()
26
+
27
+ def _check(self):
28
+ return True, {}
29
+
30
+ def _analyze(self, cache=None):
31
+ for n in self._graph.nodes():
32
+ for i, s in enumerate(n.statements):
33
+ if isinstance(s, (ConditionalJump, Jump, Label)):
34
+ continue
35
+ if not s.tags.get("keep_in_slice", False):
36
+ n.statements[i] = None
37
+
38
+ for n in self._graph.nodes():
39
+ n.statements = [s for s in n.statements if s is not None]
40
+
41
+ self.out_graph = self._graph