angr 9.2.142__py3-none-manylinux2014_x86_64.whl → 9.2.144__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.
- angr/__init__.py +1 -1
- angr/analyses/calling_convention/calling_convention.py +22 -10
- angr/analyses/calling_convention/fact_collector.py +72 -14
- angr/analyses/cfg/cfg_base.py +7 -2
- angr/analyses/cfg/cfg_emulated.py +13 -4
- angr/analyses/cfg/cfg_fast.py +21 -60
- angr/analyses/cfg/indirect_jump_resolvers/__init__.py +2 -0
- angr/analyses/cfg/indirect_jump_resolvers/const_resolver.py +12 -1
- angr/analyses/cfg/indirect_jump_resolvers/constant_value_manager.py +107 -0
- angr/analyses/cfg/indirect_jump_resolvers/default_resolvers.py +2 -1
- angr/analyses/cfg/indirect_jump_resolvers/jumptable.py +6 -102
- angr/analyses/cfg/indirect_jump_resolvers/syscall_resolver.py +92 -0
- angr/analyses/complete_calling_conventions.py +18 -5
- angr/analyses/decompiler/ail_simplifier.py +95 -65
- angr/analyses/decompiler/clinic.py +162 -68
- angr/analyses/decompiler/decompiler.py +4 -4
- angr/analyses/decompiler/optimization_passes/base_ptr_save_simplifier.py +1 -1
- angr/analyses/decompiler/optimization_passes/condition_constprop.py +49 -14
- angr/analyses/decompiler/optimization_passes/ite_region_converter.py +8 -0
- angr/analyses/decompiler/optimization_passes/optimization_pass.py +5 -5
- angr/analyses/decompiler/optimization_passes/return_duplicator_base.py +5 -0
- angr/analyses/decompiler/peephole_optimizations/__init__.py +2 -0
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_shr_const_shr_const.py +37 -0
- angr/analyses/decompiler/peephole_optimizations/simplify_pc_relative_loads.py +15 -1
- angr/analyses/decompiler/sequence_walker.py +8 -0
- angr/analyses/decompiler/ssailification/rewriting_engine.py +2 -0
- angr/analyses/decompiler/ssailification/ssailification.py +10 -2
- angr/analyses/decompiler/ssailification/traversal_engine.py +17 -2
- angr/analyses/decompiler/structured_codegen/c.py +25 -4
- angr/analyses/decompiler/utils.py +13 -0
- angr/analyses/disassembly.py +3 -3
- angr/analyses/fcp/fcp.py +1 -4
- angr/analyses/s_propagator.py +40 -29
- angr/analyses/s_reaching_definitions/s_rda_model.py +45 -36
- angr/analyses/s_reaching_definitions/s_rda_view.py +6 -3
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +41 -42
- angr/analyses/typehoon/dfa.py +13 -3
- angr/analyses/typehoon/typehoon.py +60 -18
- angr/analyses/typehoon/typevars.py +11 -7
- angr/analyses/variable_recovery/engine_ail.py +19 -23
- angr/analyses/variable_recovery/engine_base.py +26 -30
- angr/analyses/variable_recovery/variable_recovery_fast.py +17 -21
- angr/calling_conventions.py +18 -8
- angr/knowledge_plugins/functions/function.py +29 -15
- angr/knowledge_plugins/key_definitions/constants.py +2 -2
- angr/knowledge_plugins/key_definitions/liveness.py +4 -4
- angr/lib/angr_native.so +0 -0
- angr/procedures/definitions/linux_kernel.py +5 -0
- angr/state_plugins/unicorn_engine.py +24 -8
- angr/storage/memory_mixins/paged_memory/page_backer_mixins.py +1 -2
- angr/storage/memory_mixins/paged_memory/pages/mv_list_page.py +2 -2
- angr/utils/doms.py +40 -33
- angr/utils/graph.py +26 -20
- angr/utils/ssa/__init__.py +21 -14
- angr/utils/ssa/vvar_uses_collector.py +2 -2
- {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/METADATA +11 -8
- {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/RECORD +61 -58
- {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/WHEEL +1 -1
- {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/LICENSE +0 -0
- {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/entry_points.txt +0 -0
- {angr-9.2.142.dist-info → angr-9.2.144.dist-info}/top_level.txt +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# pylint:disable=wrong-import-position,wrong-import-order
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
import enum
|
|
4
|
-
from typing import TYPE_CHECKING,
|
|
4
|
+
from typing import TYPE_CHECKING, Literal, cast
|
|
5
5
|
from collections.abc import Sequence
|
|
6
6
|
from collections import defaultdict, OrderedDict
|
|
7
7
|
import logging
|
|
8
8
|
import functools
|
|
9
|
+
import contextlib
|
|
9
10
|
|
|
10
11
|
import pyvex
|
|
11
12
|
import claripy
|
|
@@ -15,7 +16,6 @@ from claripy.annotation import UninitializedAnnotation
|
|
|
15
16
|
from angr import sim_options as o
|
|
16
17
|
from angr import BP, BP_BEFORE, BP_AFTER
|
|
17
18
|
from angr.misc.ux import once
|
|
18
|
-
from angr.code_location import CodeLocation
|
|
19
19
|
from angr.concretization_strategies import SimConcretizationStrategyAny
|
|
20
20
|
from angr.knowledge_plugins.cfg import IndirectJump, IndirectJumpType
|
|
21
21
|
from angr.engines.vex.claripy import ccall
|
|
@@ -26,13 +26,11 @@ from angr.annocfg import AnnotatedCFG
|
|
|
26
26
|
from angr.exploration_techniques.slicecutor import Slicecutor
|
|
27
27
|
from angr.exploration_techniques.local_loop_seer import LocalLoopSeer
|
|
28
28
|
from angr.exploration_techniques.explorer import Explorer
|
|
29
|
-
from angr.project import Project
|
|
30
29
|
from angr.utils.constants import DEFAULT_STATEMENT
|
|
31
|
-
from angr.analyses.propagator.vex_vars import VEXReg
|
|
32
30
|
from angr.analyses.propagator.top_checker_mixin import ClaripyDataVEXEngineMixin
|
|
33
31
|
from angr.engines.vex.claripy.datalayer import value
|
|
34
32
|
from .resolver import IndirectJumpResolver
|
|
35
|
-
from .
|
|
33
|
+
from .constant_value_manager import ConstantValueManager
|
|
36
34
|
|
|
37
35
|
try:
|
|
38
36
|
from angr.engines import pcode
|
|
@@ -40,7 +38,6 @@ except ImportError:
|
|
|
40
38
|
pcode = None
|
|
41
39
|
|
|
42
40
|
if TYPE_CHECKING:
|
|
43
|
-
from angr import SimState
|
|
44
41
|
from angr.knowledge_plugins import Function
|
|
45
42
|
|
|
46
43
|
l = logging.getLogger(name=__name__)
|
|
@@ -133,101 +130,6 @@ class JumpTargetBaseAddr:
|
|
|
133
130
|
return self.base_addr is not None
|
|
134
131
|
|
|
135
132
|
|
|
136
|
-
#
|
|
137
|
-
# Constant register resolving support
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
class ConstantValueManager:
|
|
142
|
-
"""
|
|
143
|
-
Manages the loading of registers who hold constant values.
|
|
144
|
-
"""
|
|
145
|
-
|
|
146
|
-
__slots__ = (
|
|
147
|
-
"func",
|
|
148
|
-
"indirect_jump_addr",
|
|
149
|
-
"kb",
|
|
150
|
-
"mapping",
|
|
151
|
-
"project",
|
|
152
|
-
)
|
|
153
|
-
|
|
154
|
-
def __init__(self, project: Project, kb, func: Function, ij_addr: int):
|
|
155
|
-
self.project = project
|
|
156
|
-
self.kb = kb
|
|
157
|
-
self.func = func
|
|
158
|
-
self.indirect_jump_addr = ij_addr
|
|
159
|
-
|
|
160
|
-
self.mapping: dict[Any, dict[Any, claripy.ast.Base]] | None = None
|
|
161
|
-
|
|
162
|
-
def reg_read_callback(self, state: SimState):
|
|
163
|
-
if self.mapping is None:
|
|
164
|
-
self._build_mapping()
|
|
165
|
-
assert self.mapping is not None
|
|
166
|
-
|
|
167
|
-
codeloc = CodeLocation(state.scratch.bbl_addr, state.scratch.stmt_idx, ins_addr=state.scratch.ins_addr)
|
|
168
|
-
if codeloc in self.mapping:
|
|
169
|
-
reg_read_offset = state.inspect.reg_read_offset
|
|
170
|
-
if isinstance(reg_read_offset, claripy.ast.BV) and reg_read_offset.op == "BVV":
|
|
171
|
-
reg_read_offset = reg_read_offset.args[0]
|
|
172
|
-
variable = VEXReg(reg_read_offset, state.inspect.reg_read_length)
|
|
173
|
-
if variable in self.mapping[codeloc]:
|
|
174
|
-
v = self.mapping[codeloc][variable]
|
|
175
|
-
if isinstance(v, int):
|
|
176
|
-
v = claripy.BVV(v, state.inspect.reg_read_length * state.arch.byte_width)
|
|
177
|
-
state.inspect.reg_read_expr = v
|
|
178
|
-
|
|
179
|
-
def _build_mapping(self):
|
|
180
|
-
# constant propagation
|
|
181
|
-
l.debug("JumpTable: Propagating for %r at %#x.", self.func, self.indirect_jump_addr)
|
|
182
|
-
|
|
183
|
-
# determine blocks to run FCP on
|
|
184
|
-
|
|
185
|
-
# - include at most three levels of superblock successors from the entrypoint
|
|
186
|
-
self.mapping = {}
|
|
187
|
-
startpoint = self.func.startpoint
|
|
188
|
-
if startpoint is None:
|
|
189
|
-
return
|
|
190
|
-
|
|
191
|
-
blocks = set()
|
|
192
|
-
succ_and_levels = [(startpoint, 0)]
|
|
193
|
-
while succ_and_levels:
|
|
194
|
-
new_succs = []
|
|
195
|
-
for node, level in succ_and_levels:
|
|
196
|
-
if node in blocks:
|
|
197
|
-
continue
|
|
198
|
-
blocks.add(node)
|
|
199
|
-
if node.addr == self.indirect_jump_addr:
|
|
200
|
-
# stop at the indirect jump block
|
|
201
|
-
continue
|
|
202
|
-
for _, succ, data in self.func.graph.out_edges(node, data=True):
|
|
203
|
-
new_level = level if data.get("type") == "fake_return" else level + 1
|
|
204
|
-
if new_level <= 3:
|
|
205
|
-
new_succs.append((succ, new_level))
|
|
206
|
-
succ_and_levels = new_succs
|
|
207
|
-
|
|
208
|
-
# - include at most six levels of predecessors from the indirect jump block
|
|
209
|
-
ij_block = self.func.get_node(self.indirect_jump_addr)
|
|
210
|
-
preds = [ij_block]
|
|
211
|
-
for _ in range(6):
|
|
212
|
-
new_preds = []
|
|
213
|
-
for node in preds:
|
|
214
|
-
if node in blocks:
|
|
215
|
-
continue
|
|
216
|
-
blocks.add(node)
|
|
217
|
-
new_preds += list(self.func.graph.predecessors(node))
|
|
218
|
-
preds = new_preds
|
|
219
|
-
if not preds:
|
|
220
|
-
break
|
|
221
|
-
|
|
222
|
-
prop = self.project.analyses.FastConstantPropagation(
|
|
223
|
-
self.func,
|
|
224
|
-
blocks=blocks,
|
|
225
|
-
vex_cross_insn_opt=True,
|
|
226
|
-
load_callback=PropagatorLoadCallback(self.project).propagator_load_callback,
|
|
227
|
-
)
|
|
228
|
-
self.mapping = prop.replacements
|
|
229
|
-
|
|
230
|
-
|
|
231
133
|
#
|
|
232
134
|
# Jump table pre-check
|
|
233
135
|
#
|
|
@@ -1798,7 +1700,9 @@ class JumpTableResolver(IndirectJumpResolver):
|
|
|
1798
1700
|
# swap the two tmps
|
|
1799
1701
|
jump_base_addr.tmp, jump_base_addr.tmp_1 = jump_base_addr.tmp_1, jump_base_addr.tmp
|
|
1800
1702
|
# Load the concrete base address
|
|
1801
|
-
|
|
1703
|
+
with contextlib.suppress(SimError):
|
|
1704
|
+
# silently eat the claripy exception
|
|
1705
|
+
jump_base_addr.base_addr = state.solver.eval(state.scratch.temps[jump_base_addr.tmp_1])
|
|
1802
1706
|
else:
|
|
1803
1707
|
# We do not support the cases where the base address involves more than one addition.
|
|
1804
1708
|
# One such case exists in libc-2.27.so shipped with Ubuntu x86 where esi is used as the address of the
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import contextlib
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
from angr import sim_options as o
|
|
7
|
+
from angr import BP, BP_AFTER
|
|
8
|
+
from angr.errors import (
|
|
9
|
+
AngrUnsupportedSyscallError,
|
|
10
|
+
SimOperationError,
|
|
11
|
+
SimError,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
from .resolver import IndirectJumpResolver
|
|
15
|
+
from .constant_value_manager import ConstantValueManager
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from angr import Block
|
|
19
|
+
from angr.engines import SimSuccessors
|
|
20
|
+
from angr.sim_state import SimState
|
|
21
|
+
from angr.sim_procedure import SimProcedure
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_l = logging.getLogger(name=__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SyscallResolver(IndirectJumpResolver):
|
|
28
|
+
"""
|
|
29
|
+
Resolve syscalls to SimProcedures.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, project):
|
|
33
|
+
super().__init__(project, timeless=True)
|
|
34
|
+
|
|
35
|
+
def filter(self, cfg, addr, func_addr, block, jumpkind):
|
|
36
|
+
return jumpkind.startswith("Ijk_Sys")
|
|
37
|
+
|
|
38
|
+
def resolve( # pylint:disable=unused-argument
|
|
39
|
+
self, cfg, addr: int, func_addr: int, block: Block, jumpkind: str, func_graph_complete: bool = True, **kwargs
|
|
40
|
+
):
|
|
41
|
+
stub = self._resolve_syscall_to_stub(cfg, addr, func_addr, block)
|
|
42
|
+
return (True, [stub.addr]) if stub else (False, [])
|
|
43
|
+
|
|
44
|
+
def _resolve_syscall_to_stub(self, cfg, addr: int, func_addr: int, block: Block) -> SimProcedure | None:
|
|
45
|
+
if not cfg.functions.contains_addr(func_addr):
|
|
46
|
+
return None
|
|
47
|
+
func = cfg.functions.get_by_addr(func_addr)
|
|
48
|
+
|
|
49
|
+
cv_manager = ConstantValueManager(self.project, cfg.kb, func, addr)
|
|
50
|
+
constant_value_reg_read_bp = BP(when=BP_AFTER, enabled=True, action=cv_manager.reg_read_callback)
|
|
51
|
+
|
|
52
|
+
state = self.project.factory.blank_state(
|
|
53
|
+
mode="fastpath",
|
|
54
|
+
addr=block.addr,
|
|
55
|
+
add_options={o.SYMBOL_FILL_UNCONSTRAINED_MEMORY, o.SYMBOL_FILL_UNCONSTRAINED_REGISTERS},
|
|
56
|
+
)
|
|
57
|
+
state.inspect.add_breakpoint("reg_read", constant_value_reg_read_bp)
|
|
58
|
+
|
|
59
|
+
successors = self._simulate_block_with_resilience(state)
|
|
60
|
+
if successors:
|
|
61
|
+
state = self._get_syscall_state_from_successors(successors)
|
|
62
|
+
if state:
|
|
63
|
+
with contextlib.suppress(AngrUnsupportedSyscallError):
|
|
64
|
+
return self.project.simos.syscall(state)
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
def _simulate_block_with_resilience(self, state: SimState) -> SimSuccessors | None:
|
|
68
|
+
"""
|
|
69
|
+
Execute a basic block with "On Error Resume Next". Give up when there is no way moving forward.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
stmt_idx = 0
|
|
73
|
+
successors = None # make PyCharm's linting happy
|
|
74
|
+
|
|
75
|
+
while True:
|
|
76
|
+
try:
|
|
77
|
+
successors = self.project.factory.successors(state, skip_stmts=stmt_idx)
|
|
78
|
+
break
|
|
79
|
+
except SimOperationError:
|
|
80
|
+
stmt_idx += 1
|
|
81
|
+
continue
|
|
82
|
+
except SimError:
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
return successors
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def _get_syscall_state_from_successors(successors: SimSuccessors) -> SimState | None:
|
|
89
|
+
for state in successors.flat_successors:
|
|
90
|
+
if state.history.jumpkind and state.history.jumpkind.startswith("Ijk_Sys"):
|
|
91
|
+
return state
|
|
92
|
+
return None
|
|
@@ -63,7 +63,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
63
63
|
max_function_size: int | None = None,
|
|
64
64
|
workers: int = 0,
|
|
65
65
|
cc_callback: Callable | None = None,
|
|
66
|
-
prioritize_func_addrs:
|
|
66
|
+
prioritize_func_addrs: list[int] | set[int] | None = None,
|
|
67
67
|
skip_other_funcs: bool = False,
|
|
68
68
|
auto_start: bool = True,
|
|
69
69
|
func_graphs: dict[int, networkx.DiGraph] | None = None,
|
|
@@ -130,9 +130,20 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
130
130
|
Infer calling conventions for all functions in the current project.
|
|
131
131
|
"""
|
|
132
132
|
|
|
133
|
-
#
|
|
134
|
-
#
|
|
135
|
-
|
|
133
|
+
# special case: if both _prioritize_func_addrs and _skip_other_funcs are set, we only need to sort part of
|
|
134
|
+
# the call graph; even better, if there is only one function set, we don't need to sort the call graph at all!
|
|
135
|
+
if self._prioritize_func_addrs and self._skip_other_funcs:
|
|
136
|
+
if len(self._prioritize_func_addrs) == 1:
|
|
137
|
+
self._func_addrs = list(self._prioritize_func_addrs)
|
|
138
|
+
self._total_funcs = 1
|
|
139
|
+
return
|
|
140
|
+
directed_callgraph = networkx.DiGraph(self.kb.functions.callgraph)
|
|
141
|
+
directed_callgraph = directed_callgraph.subgraph(self._prioritize_func_addrs)
|
|
142
|
+
else:
|
|
143
|
+
# get an ordering of functions based on the call graph
|
|
144
|
+
# note that the call graph is a multi-digraph. we convert it to a digraph to speed up topological sort
|
|
145
|
+
directed_callgraph = networkx.DiGraph(self.kb.functions.callgraph)
|
|
146
|
+
assert isinstance(directed_callgraph, networkx.DiGraph)
|
|
136
147
|
sorted_funcs = GraphUtils.quasi_topological_sort_nodes(directed_callgraph)
|
|
137
148
|
|
|
138
149
|
total_funcs = 0
|
|
@@ -148,7 +159,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
148
159
|
continue
|
|
149
160
|
|
|
150
161
|
if self._max_function_size is not None:
|
|
151
|
-
func_size = sum(block.size for block in func.blocks)
|
|
162
|
+
func_size = sum(block.size for block in func.blocks if block.size is not None)
|
|
152
163
|
if func_size > self._max_function_size:
|
|
153
164
|
_l.info(
|
|
154
165
|
"Skipping variable recovery for %r since its size (%d) is greater than the cutoff "
|
|
@@ -189,6 +200,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
189
200
|
|
|
190
201
|
def work(self):
|
|
191
202
|
total_funcs = self._total_funcs
|
|
203
|
+
assert total_funcs is not None
|
|
192
204
|
if self._workers == 0:
|
|
193
205
|
self._update_progress(0)
|
|
194
206
|
for idx, func_addr in enumerate(self._func_addrs):
|
|
@@ -211,6 +223,7 @@ class CompleteCallingConventionsAnalysis(Analysis):
|
|
|
211
223
|
self._finish_progress()
|
|
212
224
|
|
|
213
225
|
else:
|
|
226
|
+
assert self._remaining_funcs is not None and self._func_queue is not None
|
|
214
227
|
self._remaining_funcs.value = len(self._func_addrs)
|
|
215
228
|
|
|
216
229
|
# generate a call tree (obviously, it's acyclic)
|