angr 9.2.124__py3-none-manylinux2014_aarch64.whl → 9.2.125__py3-none-manylinux2014_aarch64.whl

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

Potentially problematic release.


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

Files changed (30) hide show
  1. angr/__init__.py +1 -1
  2. angr/analyses/__init__.py +9 -1
  3. angr/analyses/codecave.py +77 -0
  4. angr/analyses/decompiler/clinic.py +31 -1
  5. angr/analyses/decompiler/decompiler.py +4 -0
  6. angr/analyses/decompiler/optimization_passes/__init__.py +3 -0
  7. angr/analyses/decompiler/optimization_passes/inlined_string_transformation_simplifier.py +6 -0
  8. angr/analyses/decompiler/optimization_passes/tag_slicer.py +41 -0
  9. angr/analyses/decompiler/peephole_optimizations/constant_derefs.py +2 -2
  10. angr/analyses/patchfinder.py +137 -0
  11. angr/analyses/pathfinder.py +282 -0
  12. angr/analyses/smc.py +159 -0
  13. angr/angrdb/models.py +1 -2
  14. angr/engines/vex/heavy/heavy.py +2 -0
  15. angr/exploration_techniques/spiller_db.py +1 -2
  16. angr/knowledge_plugins/functions/function.py +4 -0
  17. angr/knowledge_plugins/functions/function_manager.py +18 -9
  18. angr/knowledge_plugins/functions/function_parser.py +1 -1
  19. angr/knowledge_plugins/functions/soot_function.py +1 -0
  20. angr/misc/ux.py +2 -2
  21. angr/project.py +17 -1
  22. angr/state_plugins/history.py +6 -4
  23. angr/utils/bits.py +4 -0
  24. angr/utils/tagged_interval_map.py +112 -0
  25. {angr-9.2.124.dist-info → angr-9.2.125.dist-info}/METADATA +6 -6
  26. {angr-9.2.124.dist-info → angr-9.2.125.dist-info}/RECORD +30 -24
  27. {angr-9.2.124.dist-info → angr-9.2.125.dist-info}/LICENSE +0 -0
  28. {angr-9.2.124.dist-info → angr-9.2.125.dist-info}/WHEEL +0 -0
  29. {angr-9.2.124.dist-info → angr-9.2.125.dist-info}/entry_points.txt +0 -0
  30. {angr-9.2.124.dist-info → angr-9.2.125.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,282 @@
1
+ # pylint:disable=missing-class-docstring
2
+ from __future__ import annotations
3
+ from enum import Enum, auto
4
+ from dataclasses import dataclass
5
+ from weakref import ref
6
+ from collections import defaultdict
7
+
8
+ from networkx import DiGraph
9
+ from networkx.algorithms.shortest_paths import single_target_shortest_path_length
10
+
11
+ from angr.sim_state import SimState
12
+ from angr.engines.successors import SimSuccessors
13
+ from angr.knowledge_plugins.cfg import CFGModel, CFGNode
14
+ from .analysis import Analysis, AnalysesHub
15
+
16
+
17
+ class Unreachable(Exception):
18
+ pass
19
+
20
+
21
+ @dataclass(eq=False)
22
+ class SimStateMarker:
23
+ addr: int
24
+ parent: SimStateMarker | None = None
25
+ banned: bool = False
26
+ misses: int = 0
27
+
28
+ def __repr__(self):
29
+ inner_repr = "None" if self.parent is None else "..."
30
+ return f"SimStateMarker(addr={self.addr:#x}, parent={inner_repr}, banned={self.banned}, misses={self.misses})"
31
+
32
+
33
+ class SuccessorsKind(Enum):
34
+ SAT = auto()
35
+ UNSAT = auto()
36
+ MISSING = auto()
37
+
38
+
39
+ @dataclass
40
+ class TestPathReport:
41
+ path_markers: dict[int, SimStateMarker]
42
+ termination: SuccessorsKind
43
+
44
+
45
+ def nilref():
46
+ return None
47
+
48
+
49
+ class Pathfinder(Analysis):
50
+ def __init__(self, start_state: SimState, goal_addr: int, cfg: CFGModel, cache_size=10000):
51
+ self.start_state = start_state
52
+ self.goal_addr = goal_addr
53
+ self.goal_state: SimState | None = None
54
+ self.cfg = cfg
55
+ self.cache_size = cache_size
56
+
57
+ # HACK HACK HACK HACK TODO FIXME FISH PLEASE GET RID OF THIS
58
+ extra_edges = []
59
+ for node in self.cfg.graph.nodes:
60
+ if node.is_syscall:
61
+ for pred in self.cfg.graph.pred[node]:
62
+ for succ, data in self.cfg.graph.succ[pred].items():
63
+ if data["jumpkind"] == "Ijk_FakeRet":
64
+ extra_edges.append((node, succ))
65
+ for node, succ in extra_edges:
66
+ self.cfg.graph.add_edge(node, succ, jumpkind="Ijk_Ret")
67
+
68
+ goal_node = self.cfg.get_any_node(goal_addr)
69
+ if goal_node is None:
70
+ raise ValueError(f"Node {goal_addr:#x} is not in graph")
71
+
72
+ self.start_marker = SimStateMarker(start_state.addr)
73
+ self.transition_cache: DiGraph[SimStateMarker] = DiGraph()
74
+ self.transition_cache.add_node(self.start_marker, state=ref(start_state))
75
+ self.base_heuristic: dict[int, int] = {
76
+ node.addr: dist for node, dist in single_target_shortest_path_length(cfg.graph, goal_node)
77
+ }
78
+ self.state_cache = {}
79
+ self.unsat_markers = set()
80
+ self.extra_weight = defaultdict(int)
81
+
82
+ self._search_frontier_marker = self.start_marker
83
+ self._search_path: list[tuple[int, str]] = [(self.start_marker.addr, "Ijk_Boring")]
84
+ self._search_stack = []
85
+ self._search_backtrack_to = {self.start_marker}
86
+ self._search_address_backtrack_points = {self.start_marker.addr: self.start_marker}
87
+
88
+ def cache_state(self, state: SimState):
89
+ self.state_cache[state] = self.state_cache.pop(state, None)
90
+ if len(self.state_cache) > self.cache_size:
91
+ self.state_cache.pop(next(iter(self.state_cache)))
92
+
93
+ def marker_to_state(self, marker: SimStateMarker) -> SimState | None:
94
+ return self.transition_cache.nodes[marker]["state"]()
95
+
96
+ def analyze(self) -> bool:
97
+ while True:
98
+ search_path = self.find_best_hypothesis_path()
99
+ result = self.test_path(search_path)
100
+ if result.termination == SuccessorsKind.SAT:
101
+ self.goal_state = self.marker_to_state(result.path_markers[len(search_path) - 1])
102
+ return True
103
+ marker = result.path_markers[max(result.path_markers)]
104
+ marker.banned = True
105
+ self._search_backtrack_to.add(marker)
106
+ if result.termination == SuccessorsKind.UNSAT:
107
+ self.unsat_markers.add(marker)
108
+
109
+ def _search_backtrack(self):
110
+ if self._search_address_backtrack_points[self._search_frontier_marker.addr] is self._search_frontier_marker:
111
+ self._search_address_backtrack_points.pop(self._search_frontier_marker.addr)
112
+
113
+ self._search_frontier_marker = self._search_frontier_marker.parent
114
+ if self._search_frontier_marker is None:
115
+ raise Unreachable
116
+
117
+ addr, jumpkind = self._search_path.pop()
118
+ if jumpkind == "Ijk_Ret":
119
+ self._search_stack.append(addr)
120
+ elif jumpkind == "Ijk_Call" or jumpkind.startswith("Ijk_Sys"):
121
+ self._search_stack.pop()
122
+
123
+ def find_best_hypothesis_path(self) -> tuple[int, ...]:
124
+ assert self._search_backtrack_to, "Uhh every iteration should set at least one backtrack point"
125
+ if self.start_marker in self._search_backtrack_to:
126
+ self._search_frontier_marker = self.start_marker
127
+ self._search_path: list[tuple[int, str]] = [(self.start_marker.addr, "Ijk_Boring")]
128
+ self._search_stack = []
129
+ self._search_backtrack_to = set()
130
+ else:
131
+ while self._search_backtrack_to:
132
+ self._search_backtrack_to.discard(self._search_frontier_marker)
133
+ try:
134
+ self._search_backtrack()
135
+ except Unreachable as e:
136
+ raise RuntimeError("oops") from e
137
+
138
+ while self._search_path[-1][0] != self.goal_addr:
139
+ banned = {
140
+ marker.addr for marker in self.transition_cache.succ[self._search_frontier_marker] if marker.banned
141
+ }
142
+ current_node = self.cfg.get_any_node(self._search_path[-1][0])
143
+ options = [
144
+ (node, data["jumpkind"], self.base_heuristic[node.addr] + self.extra_weight[node.addr])
145
+ for node, data in self.cfg.graph.succ[current_node].items()
146
+ if data["jumpkind"] != "Ijk_FakeRet"
147
+ and node.addr not in banned
148
+ and node.addr in self.base_heuristic
149
+ and (data["jumpkind"] != "Ijk_Ret" or node.addr == self._search_stack[-1])
150
+ ]
151
+ if not options:
152
+ # backtrack
153
+ self._search_frontier_marker.banned = True
154
+ self._search_backtrack()
155
+ continue
156
+
157
+ best_node, best_jumpkind, best_weight = min(
158
+ options,
159
+ default=(None, None),
160
+ key=lambda xyz: xyz[2],
161
+ )
162
+
163
+ assert isinstance(best_jumpkind, str)
164
+ assert isinstance(best_node, CFGNode)
165
+ self.extra_weight[best_node.addr] += 1
166
+ self._search_path.append((best_node.addr, best_jumpkind))
167
+
168
+ if best_jumpkind == "Ijk_Call" or best_jumpkind.startswith("Ijk_Sys"):
169
+ self._search_stack.append(
170
+ next(
171
+ iter(
172
+ node.addr
173
+ for node, data in self.cfg.graph.succ[current_node].items()
174
+ if data["jumpkind"] == "Ijk_FakeRet"
175
+ ),
176
+ None,
177
+ )
178
+ )
179
+ elif best_jumpkind == "Ijk_Ret":
180
+ self._search_stack.pop()
181
+
182
+ frontier_marker_nullable = next(
183
+ (
184
+ marker
185
+ for marker in self.transition_cache.succ[self._search_frontier_marker]
186
+ if marker.addr == best_node.addr
187
+ ),
188
+ None,
189
+ )
190
+ if frontier_marker_nullable is None:
191
+ new_marker = SimStateMarker(best_node.addr, self._search_frontier_marker)
192
+ self.transition_cache.add_node(new_marker, state=nilref)
193
+ self.transition_cache.add_edge(self._search_frontier_marker, new_marker)
194
+ self._search_frontier_marker = new_marker
195
+ else:
196
+ self._search_frontier_marker = frontier_marker_nullable
197
+
198
+ if self._search_frontier_marker.addr not in self._search_address_backtrack_points:
199
+ self._search_address_backtrack_points[self._search_frontier_marker.addr] = self._search_frontier_marker
200
+
201
+ # TODO does this go above the above stanza?
202
+ if sum(weight == best_weight for _, _, weight in options) != 1:
203
+ self._search_backtrack_to.add(self._search_address_backtrack_points[self._search_frontier_marker.addr])
204
+
205
+ return tuple(addr for addr, _ in self._search_path)
206
+
207
+ def diagnose_unsat(self, state: SimState):
208
+ pass
209
+
210
+ def test_path(self, bbl_addr_trace: tuple[int, ...]) -> TestPathReport:
211
+ assert bbl_addr_trace[0] == self.start_marker.addr, "Paths must begin with the start state"
212
+
213
+ known_markers = [self.start_marker]
214
+ for addr in bbl_addr_trace[1:]:
215
+ for succ in self.transition_cache.succ[known_markers[-1]]:
216
+ if succ.addr == addr:
217
+ break
218
+ else:
219
+ break
220
+ known_markers.append(succ)
221
+
222
+ marker = None
223
+ for ri, marker_ in enumerate(reversed(known_markers)):
224
+ i = len(known_markers) - 1 - ri
225
+ state: SimState = self.transition_cache.nodes[marker_]["state"]()
226
+ marker = marker_
227
+ if state is not None:
228
+ break
229
+ else:
230
+ assert False, "The first item in known_markers should always have a resolvable weakref"
231
+
232
+ while i != len(bbl_addr_trace) - 1:
233
+ assert state.addr == bbl_addr_trace[i]
234
+
235
+ marker.misses += 1
236
+ successors = state.step(strict_block_end=True)
237
+ succ, kind = find_successor(successors, bbl_addr_trace[i + 1])
238
+
239
+ # cache state
240
+ if i + 1 < len(known_markers):
241
+ succ_marker = known_markers[i + 1]
242
+ else:
243
+ succ_marker = SimStateMarker(bbl_addr_trace[i + 1], parent=marker)
244
+ self.transition_cache.add_node(succ_marker)
245
+ self.transition_cache.add_edge(marker, succ_marker)
246
+ self.transition_cache.nodes[succ_marker]["state"] = ref(succ) if succ is not None else nilref
247
+ if succ is not None:
248
+ self.cache_state(succ)
249
+
250
+ if kind == SuccessorsKind.SAT:
251
+ assert succ is not None
252
+ state = succ
253
+ marker = succ_marker
254
+ i += 1
255
+ continue
256
+ if kind == SuccessorsKind.UNSAT:
257
+ assert succ is not None
258
+ return TestPathReport(
259
+ path_markers={i: marker, i + 1: succ_marker},
260
+ termination=SuccessorsKind.UNSAT,
261
+ )
262
+ return TestPathReport(path_markers={i: marker, i + 1: succ_marker}, termination=SuccessorsKind.MISSING)
263
+
264
+ return TestPathReport(path_markers={i: marker}, termination=SuccessorsKind.SAT)
265
+
266
+
267
+ def find_successor(successors: SimSuccessors, target_addr: int) -> tuple[SimState | None, SuccessorsKind]:
268
+ for succ in successors.flat_successors:
269
+ if succ.addr == target_addr:
270
+ return succ, SuccessorsKind.SAT
271
+ for succ in successors.unsat_successors:
272
+ if succ.addr == target_addr:
273
+ return succ, SuccessorsKind.UNSAT
274
+ for succ in successors.unconstrained_successors:
275
+ succ2 = succ.copy()
276
+ succ2.add_constraints(succ2._ip == target_addr)
277
+ if succ2.satisfiable():
278
+ return succ2, SuccessorsKind.SAT
279
+ return None, SuccessorsKind.MISSING
280
+
281
+
282
+ AnalysesHub.register_default("Pathfinder", Pathfinder)
angr/analyses/smc.py ADDED
@@ -0,0 +1,159 @@
1
+ from __future__ import annotations
2
+ import logging
3
+ import random
4
+
5
+ from enum import auto, IntFlag
6
+ from collections.abc import Generator
7
+
8
+ import angr
9
+ from angr.analyses import Analysis, AnalysesHub
10
+ from angr.knowledge_plugins import Function
11
+ from angr.sim_state import SimState
12
+
13
+ from angr.utils.tagged_interval_map import TaggedIntervalMap
14
+
15
+
16
+ log = logging.getLogger(__name__)
17
+ log.setLevel(logging.INFO)
18
+
19
+
20
+ class TraceActions(IntFlag):
21
+ """
22
+ Describe memory access actions.
23
+ """
24
+
25
+ WRITE = auto()
26
+ EXECUTE = auto()
27
+
28
+
29
+ class TraceClassifier:
30
+ """
31
+ Classify traces.
32
+ """
33
+
34
+ def __init__(self, state: SimState | None = None):
35
+ self.map = TaggedIntervalMap()
36
+ if state:
37
+ self.instrument(state)
38
+
39
+ def act_mem_write(self, state) -> None:
40
+ """
41
+ SimInspect callback for memory writes.
42
+ """
43
+ addr = state.solver.eval(state.inspect.mem_write_address)
44
+ length = state.inspect.mem_write_length
45
+ if not isinstance(length, int):
46
+ length = state.solver.eval(length)
47
+ self.map.add(addr, length, TraceActions.WRITE)
48
+
49
+ def act_instruction(self, state) -> None:
50
+ """
51
+ SimInspect callback for instruction execution.
52
+ """
53
+ addr = state.inspect.instruction
54
+ if addr is None:
55
+ log.warning("Symbolic addr")
56
+ return
57
+
58
+ # FIXME: Ensure block size is correct
59
+ self.map.add(addr, state.block().size, TraceActions.EXECUTE)
60
+
61
+ def instrument(self, state) -> None:
62
+ """
63
+ Instrument `state` for tracing.
64
+ """
65
+ state.inspect.b("mem_write", when=angr.BP_BEFORE, action=self.act_mem_write)
66
+ state.inspect.b("instruction", when=angr.BP_BEFORE, action=self.act_instruction)
67
+
68
+ def get_smc_address_and_lengths(self) -> Generator[tuple[int, int]]:
69
+ """
70
+ Evaluate the trace to find which areas of memory were both written to and executed.
71
+ """
72
+ smc_flags = TraceActions.WRITE | TraceActions.EXECUTE
73
+ for addr, size, flags in self.map.irange():
74
+ if (flags & smc_flags) == smc_flags:
75
+ yield (addr, size)
76
+
77
+ def determine_smc(self) -> bool:
78
+ """
79
+ Evaluate the trace to find areas of memory that were both written to and executed.
80
+ """
81
+ return any(self.get_smc_address_and_lengths())
82
+
83
+ def pp(self):
84
+ for a, b, c in self.map.irange():
85
+ print(f"{a:8x} {b} {c}")
86
+
87
+
88
+ class SelfModifyingCodeAnalysis(Analysis):
89
+ """
90
+ Determine if some piece of code is self-modifying.
91
+
92
+ This determination is made by simply executing. If an address is executed
93
+ that is also written to, the code is determined to be self-modifying. The
94
+ determination is stored in the `result` property. The `regions` property
95
+ contains a list of (addr, length) regions that were both written to and
96
+ executed.
97
+ """
98
+
99
+ result: bool
100
+ regions: list[tuple[int, int]]
101
+
102
+ def __init__(self, subject: None | int | str | Function, max_bytes: int = 0, state: SimState | None = None):
103
+ """
104
+ :param subject: Subject of analysis
105
+ :param max_bytes: Maximum number of bytes from subject address. 0 for no limit (default).
106
+ :param state: State to begin executing from from.
107
+ """
108
+ assert self.project.selfmodifying_code
109
+
110
+ if subject is None:
111
+ subject = self.project.entry
112
+ if isinstance(subject, str):
113
+ try:
114
+ addr = self.project.kb.labels.lookup(subject)
115
+ except KeyError:
116
+ addr = self.project.kb.functions[subject].addr
117
+ elif isinstance(subject, Function):
118
+ addr = subject.addr
119
+ elif isinstance(subject, int):
120
+ addr = subject
121
+ else:
122
+ raise ValueError("Not a supported subject")
123
+
124
+ if state is None:
125
+ init_state = self.project.factory.call_state(addr)
126
+ else:
127
+ init_state = state.copy()
128
+ init_state.regs.pc = addr
129
+
130
+ init_state.options -= angr.sim_options.simplification
131
+
132
+ self._trace_classifier = TraceClassifier(init_state)
133
+ simgr = self.project.factory.simgr(init_state)
134
+
135
+ kwargs = {}
136
+ if max_bytes:
137
+ kwargs["filter_func"] = lambda s: (
138
+ "active" if s.solver.eval(addr <= s.regs.pc) and s.solver.eval(s.regs.pc < addr + max_bytes) else "oob"
139
+ )
140
+
141
+ # FIXME: Early out on SMC detect
142
+ # FIXME: Configurable step threshold
143
+ # FIXME: Loop analysis
144
+
145
+ for n in range(100):
146
+ self._update_progress(n)
147
+ simgr.step(n=3)
148
+ random.shuffle(simgr.active)
149
+ simgr.split(from_stash="active", to_stash=simgr.DROP, limit=10)
150
+
151
+ # Classify any out of bound entrypoints
152
+ for state_ in simgr.stashes["oob"]:
153
+ self._trace_classifier.act_instruction(state_)
154
+
155
+ self.regions = list(self._trace_classifier.get_smc_address_and_lengths())
156
+ self.result = len(self.regions) > 0
157
+
158
+
159
+ AnalysesHub.register_default("SMC", SelfModifyingCodeAnalysis)
angr/angrdb/models.py CHANGED
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
  from sqlalchemy import Column, Integer, String, Boolean, BLOB, ForeignKey
3
- from sqlalchemy.orm import relationship
4
- from sqlalchemy.ext.declarative import declarative_base
3
+ from sqlalchemy.orm import declarative_base, relationship
5
4
 
6
5
  Base = declarative_base()
7
6
 
@@ -90,6 +90,7 @@ class HeavyVEXMixin(SuccessorsMixin, ClaripyDataMixin, SimStateStorageMixin, VEX
90
90
  num_inst=None,
91
91
  extra_stop_points=None,
92
92
  opt_level=None,
93
+ strict_block_end=None,
93
94
  **kwargs,
94
95
  ):
95
96
  if not pyvex.lifting.lifters[self.state.arch.name] or type(successors.addr) is not int:
@@ -144,6 +145,7 @@ class HeavyVEXMixin(SuccessorsMixin, ClaripyDataMixin, SimStateStorageMixin, VEX
144
145
  num_inst=num_inst,
145
146
  extra_stop_points=extra_stop_points,
146
147
  opt_level=opt_level,
148
+ strict_block_end=strict_block_end,
147
149
  )
148
150
 
149
151
  if (
@@ -5,8 +5,7 @@ import datetime
5
5
  try:
6
6
  import sqlalchemy
7
7
  from sqlalchemy import Column, Integer, String, Boolean, DateTime, create_engine
8
- from sqlalchemy.orm import sessionmaker
9
- from sqlalchemy.ext.declarative import declarative_base
8
+ from sqlalchemy.orm import declarative_base, sessionmaker
10
9
  from sqlalchemy.exc import OperationalError
11
10
 
12
11
  Base = declarative_base()
@@ -56,6 +56,7 @@ class Function(Serializable):
56
56
  "addr",
57
57
  "is_simprocedure",
58
58
  "_name",
59
+ "previous_names",
59
60
  "is_default_name",
60
61
  "from_signature",
61
62
  "binary_name",
@@ -224,6 +225,7 @@ class Function(Serializable):
224
225
  else:
225
226
  self.is_default_name = False
226
227
  self._name = name
228
+ self.previous_names = []
227
229
  self.from_signature = None
228
230
 
229
231
  # Determine the name the binary where this function is.
@@ -274,6 +276,7 @@ class Function(Serializable):
274
276
 
275
277
  @name.setter
276
278
  def name(self, v):
279
+ self.previous_names.append(self._name)
277
280
  self._name = v
278
281
  self._function_manager._kb.labels[self.addr] = v
279
282
 
@@ -1667,6 +1670,7 @@ class Function(Serializable):
1667
1670
  func._endpoints = self._endpoints.copy()
1668
1671
  func._call_sites = self._call_sites.copy()
1669
1672
  func._project = self._project
1673
+ func.previous_names = list(self.previous_names)
1670
1674
  func.is_plt = self.is_plt
1671
1675
  func.is_simprocedure = self.is_simprocedure
1672
1676
  func.binary_name = self.binary_name
@@ -313,7 +313,7 @@ class FunctionManager(KnowledgeBasePlugin, collections.abc.Mapping):
313
313
  if isinstance(k, self.function_address_types):
314
314
  f = self.function(addr=k)
315
315
  elif type(k) is str:
316
- f = self.function(name=k)
316
+ f = self.function(name=k) or self.function(name=k, check_previous_names=True)
317
317
  else:
318
318
  raise ValueError(f"FunctionManager.__getitem__ does not support keys of type {type(k)}")
319
319
 
@@ -350,9 +350,9 @@ class FunctionManager(KnowledgeBasePlugin, collections.abc.Mapping):
350
350
  def get_by_addr(self, addr) -> Function:
351
351
  return self._function_map.get(addr)
352
352
 
353
- def get_by_name(self, name: str) -> Generator[Function]:
353
+ def get_by_name(self, name: str, check_previous_names: bool = False) -> Generator[Function]:
354
354
  for f in self._function_map.values():
355
- if f.name == name:
355
+ if f.name == name or (check_previous_names and name in f.previous_names):
356
356
  yield f
357
357
 
358
358
  def _function_added(self, func: Function):
@@ -411,7 +411,7 @@ class FunctionManager(KnowledgeBasePlugin, collections.abc.Mapping):
411
411
  except KeyError:
412
412
  return None
413
413
 
414
- def query(self, query: str) -> Function | None:
414
+ def query(self, query: str, check_previous_names: bool = False) -> Function | None:
415
415
  """
416
416
  Query for a function using selectors to disambiguate. Supported variations:
417
417
 
@@ -430,19 +430,21 @@ class FunctionManager(KnowledgeBasePlugin, collections.abc.Mapping):
430
430
  addr = int(matches.group(2), 0)
431
431
  try:
432
432
  func = self._function_map.get(addr)
433
- if func.name == name:
433
+ if func.name == name or (check_previous_names and name in func.previous_names):
434
434
  return func
435
435
  except KeyError:
436
436
  pass
437
437
 
438
438
  obj_name = selector or self._kb._project.loader.main_object.binary_basename
439
- for func in self.get_by_name(name):
439
+ for func in self.get_by_name(name, check_previous_names=check_previous_names):
440
440
  if func.binary_name == obj_name:
441
441
  return func
442
442
 
443
443
  return None
444
444
 
445
- def function(self, addr=None, name=None, create=False, syscall=False, plt=None) -> Function | None:
445
+ def function(
446
+ self, addr=None, name=None, check_previous_names=False, create=False, syscall=False, plt=None
447
+ ) -> Function | None:
446
448
  """
447
449
  Get a function object from the function manager.
448
450
 
@@ -457,6 +459,13 @@ class FunctionManager(KnowledgeBasePlugin, collections.abc.Mapping):
457
459
  :return: The Function instance, or None if the function is not found and create is False.
458
460
  :rtype: Function or None
459
461
  """
462
+ if name is not None and name.startswith("sub_"):
463
+ try:
464
+ addr = int(name.split("_")[-1], 16)
465
+ name = None
466
+ except ValueError:
467
+ pass
468
+
460
469
  if addr is not None:
461
470
  try:
462
471
  f = self._function_map.get(addr)
@@ -472,11 +481,11 @@ class FunctionManager(KnowledgeBasePlugin, collections.abc.Mapping):
472
481
  f.is_syscall = True
473
482
  return f
474
483
  elif name is not None:
475
- func = self.query(name)
484
+ func = self.query(name, check_previous_names=check_previous_names)
476
485
  if func is not None:
477
486
  return func
478
487
 
479
- for func in self.get_by_name(name):
488
+ for func in self.get_by_name(name, check_previous_names=check_previous_names):
480
489
  if plt is None or func.is_plt == plt:
481
490
  return func
482
491
 
@@ -33,7 +33,7 @@ class FunctionParser:
33
33
  obj.is_syscall = function.is_syscall
34
34
  obj.is_simprocedure = function.is_simprocedure
35
35
  obj.returning = function.returning
36
- obj.alignment = function.alignment
36
+ obj.alignment = function.is_alignment
37
37
  obj.binary_name = function.binary_name or ""
38
38
  obj.normalized = function.normalized
39
39
 
@@ -34,6 +34,7 @@ class SootFunction(Function):
34
34
  # block nodes (basic block nodes) at whose ends the function terminates
35
35
  # in theory, if everything works fine, endpoints == ret_sites | jumpout_sites | callout_sites
36
36
  self._endpoints = defaultdict(set)
37
+ self.previous_names = []
37
38
 
38
39
  self._call_sites = {}
39
40
  self.addr = addr
angr/misc/ux.py CHANGED
@@ -20,9 +20,9 @@ def deprecated(replacement=None):
20
20
  def inner(*args, **kwargs):
21
21
  if func not in already_complained:
22
22
  if replacement is None:
23
- warnings.warn(f"Don't use {func.__name__}", DeprecationWarning, stacklevel=1)
23
+ warnings.warn(f"Don't use {func.__name__}", DeprecationWarning, stacklevel=2)
24
24
  else:
25
- warnings.warn(f"Use {replacement} instead of {func.__name__}", DeprecationWarning, stacklevel=1)
25
+ warnings.warn(f"Use {replacement} instead of {func.__name__}", DeprecationWarning, stacklevel=2)
26
26
  already_complained.add(func)
27
27
  return func(*args, **kwargs)
28
28
 
angr/project.py CHANGED
@@ -236,7 +236,7 @@ class Project:
236
236
  self._initialize_analyses_hub()
237
237
 
238
238
  # Step 5.3: ...etc
239
- self.kb = KnowledgeBase(self, name="global")
239
+ self._knowledge_bases = {"default": KnowledgeBase(self, name="global")}
240
240
 
241
241
  self.is_java_project = isinstance(self.arch, ArchSoot)
242
242
  self.is_java_jni_project = isinstance(self.arch, ArchSoot) and getattr(
@@ -257,6 +257,22 @@ class Project:
257
257
  # Step 7: Run OS-specific configuration
258
258
  self.simos.configure_project()
259
259
 
260
+ @property
261
+ def kb(self):
262
+ return self._knowledge_bases["default"]
263
+
264
+ @kb.setter
265
+ def kb(self, kb):
266
+ self._knowledge_bases["default"] = kb
267
+
268
+ def get_kb(self, name):
269
+ try:
270
+ return self._knowledge_bases[name]
271
+ except KeyError:
272
+ kb = KnowledgeBase(self, name)
273
+ self._knowledge_bases[name] = kb
274
+ return kb
275
+
260
276
  @property
261
277
  def analyses(self) -> AnalysesHubWithDefault:
262
278
  result = self._analyses