angr 9.2.160__cp310-abi3-manylinux2014_aarch64.whl → 9.2.161__cp310-abi3-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.
- angr/__init__.py +4 -1
- angr/analyses/decompiler/ail_simplifier.py +81 -1
- angr/analyses/decompiler/block_simplifier.py +7 -5
- angr/analyses/decompiler/clinic.py +1 -0
- angr/analyses/decompiler/peephole_optimizations/__init__.py +4 -4
- angr/analyses/decompiler/peephole_optimizations/eager_eval.py +53 -0
- angr/analyses/decompiler/peephole_optimizations/modulo_simplifier.py +89 -0
- angr/analyses/decompiler/peephole_optimizations/{const_mull_a_shift.py → optimized_div_simplifier.py} +139 -25
- angr/analyses/decompiler/peephole_optimizations/remove_redundant_bitmasks.py +18 -9
- angr/analyses/decompiler/structuring/phoenix.py +19 -32
- angr/analyses/s_reaching_definitions/s_rda_model.py +1 -0
- angr/analyses/s_reaching_definitions/s_reaching_definitions.py +5 -2
- angr/analyses/variable_recovery/engine_base.py +9 -1
- angr/analyses/variable_recovery/variable_recovery_base.py +30 -2
- angr/analyses/variable_recovery/variable_recovery_fast.py +11 -2
- angr/emulator.py +143 -0
- angr/engines/concrete.py +66 -0
- angr/engines/icicle.py +66 -30
- angr/exploration_techniques/driller_core.py +2 -2
- angr/project.py +7 -0
- angr/rustylib.abi3.so +0 -0
- angr/sim_type.py +16 -8
- angr/utils/graph.py +20 -1
- angr/utils/ssa/__init__.py +3 -3
- {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/METADATA +5 -5
- {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/RECORD +30 -28
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +0 -57
- {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/WHEEL +0 -0
- {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/entry_points.txt +0 -0
- {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.160.dist-info → angr-9.2.161.dist-info}/top_level.txt +0 -0
|
@@ -11,7 +11,7 @@ import networkx
|
|
|
11
11
|
import claripy
|
|
12
12
|
from angr.ailment.block import Block
|
|
13
13
|
from angr.ailment.statement import Statement, ConditionalJump, Jump, Label, Return
|
|
14
|
-
from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression
|
|
14
|
+
from angr.ailment.expression import Const, UnaryOp, MultiStatementExpression, BinaryOp
|
|
15
15
|
|
|
16
16
|
from angr.utils.graph import GraphUtils
|
|
17
17
|
from angr.utils.ail import is_phi_assignment, is_head_controlled_loop_block
|
|
@@ -2174,20 +2174,17 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2174
2174
|
if r is not None:
|
|
2175
2175
|
left, left_cond, right, left_right_cond, succ = r
|
|
2176
2176
|
# create the condition node
|
|
2177
|
-
|
|
2177
|
+
left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
|
|
2178
|
+
left_cond_expr_neg = UnaryOp(None, "Not", left_cond_expr, ins_addr=start_node.addr)
|
|
2179
|
+
left_right_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_right_cond)
|
|
2178
2180
|
if not self._is_single_statement_block(left):
|
|
2179
2181
|
if not self._should_use_multistmtexprs(left):
|
|
2180
2182
|
return False
|
|
2181
2183
|
# create a MultiStatementExpression for left_right_cond
|
|
2182
2184
|
stmts = self._build_multistatementexpr_statements(left)
|
|
2183
2185
|
assert stmts is not None
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
)
|
|
2187
|
-
memo[left_right_cond._hash] = mstmt_expr
|
|
2188
|
-
cond = self.cond_proc.convert_claripy_bool_ast(
|
|
2189
|
-
claripy.Or(claripy.Not(left_cond), left_right_cond), memo=memo
|
|
2190
|
-
)
|
|
2186
|
+
left_right_cond_expr = MultiStatementExpression(None, stmts, left_right_cond_expr, ins_addr=left.addr)
|
|
2187
|
+
cond = BinaryOp(None, "LogicalOr", [left_cond_expr_neg, left_right_cond_expr], ins_addr=start_node.addr)
|
|
2191
2188
|
cond_jump = ConditionalJump(
|
|
2192
2189
|
None,
|
|
2193
2190
|
cond,
|
|
@@ -2212,18 +2209,16 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2212
2209
|
if r is not None:
|
|
2213
2210
|
left, left_cond, right, right_left_cond, else_node = r
|
|
2214
2211
|
# create the condition node
|
|
2215
|
-
|
|
2212
|
+
left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
|
|
2213
|
+
right_left_cond_expr = self.cond_proc.convert_claripy_bool_ast(right_left_cond)
|
|
2216
2214
|
if not self._is_single_statement_block(right):
|
|
2217
2215
|
if not self._should_use_multistmtexprs(right):
|
|
2218
2216
|
return False
|
|
2219
2217
|
# create a MultiStatementExpression for left_right_cond
|
|
2220
2218
|
stmts = self._build_multistatementexpr_statements(right)
|
|
2221
2219
|
assert stmts is not None
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
)
|
|
2225
|
-
memo[right_left_cond._hash] = mstmt_expr
|
|
2226
|
-
cond = self.cond_proc.convert_claripy_bool_ast(claripy.Or(left_cond, right_left_cond), memo=memo)
|
|
2220
|
+
right_left_cond_expr = MultiStatementExpression(None, stmts, right_left_cond_expr, ins_addr=left.addr)
|
|
2221
|
+
cond = BinaryOp(None, "LogicalOr", [left_cond_expr, right_left_cond_expr], ins_addr=start_node.addr)
|
|
2227
2222
|
cond_jump = ConditionalJump(
|
|
2228
2223
|
None,
|
|
2229
2224
|
cond,
|
|
@@ -2248,20 +2243,17 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2248
2243
|
if r is not None:
|
|
2249
2244
|
left, left_cond, succ, left_succ_cond, right = r
|
|
2250
2245
|
# create the condition node
|
|
2251
|
-
|
|
2246
|
+
left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
|
|
2247
|
+
left_succ_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_succ_cond)
|
|
2252
2248
|
if not self._is_single_statement_block(left):
|
|
2253
2249
|
if not self._should_use_multistmtexprs(left):
|
|
2254
2250
|
return False
|
|
2255
2251
|
# create a MultiStatementExpression for left_right_cond
|
|
2256
2252
|
stmts = self._build_multistatementexpr_statements(left)
|
|
2257
2253
|
assert stmts is not None
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
memo[left_succ_cond._hash] = mstmt_expr
|
|
2262
|
-
cond = self.cond_proc.convert_claripy_bool_ast(
|
|
2263
|
-
claripy.And(left_cond, claripy.Not(left_succ_cond)), memo=memo
|
|
2264
|
-
)
|
|
2254
|
+
left_succ_cond_expr = MultiStatementExpression(None, stmts, left_succ_cond_expr, ins_addr=left.addr)
|
|
2255
|
+
left_succ_cond_expr_neg = UnaryOp(None, "Not", left_succ_cond_expr, ins_addr=start_node.addr)
|
|
2256
|
+
cond = BinaryOp(None, "LogicalAnd", [left_cond_expr, left_succ_cond_expr_neg], ins_addr=start_node.addr)
|
|
2265
2257
|
cond_jump = ConditionalJump(
|
|
2266
2258
|
None,
|
|
2267
2259
|
cond,
|
|
@@ -2285,21 +2277,16 @@ class PhoenixStructurer(StructurerBase):
|
|
|
2285
2277
|
if r is not None:
|
|
2286
2278
|
left, left_cond, right, right_left_cond, else_node = r
|
|
2287
2279
|
# create the condition node
|
|
2288
|
-
|
|
2280
|
+
left_cond_expr = self.cond_proc.convert_claripy_bool_ast(left_cond)
|
|
2281
|
+
left_right_cond_expr = self.cond_proc.convert_claripy_bool_ast(right_left_cond)
|
|
2289
2282
|
if not self._is_single_statement_block(left):
|
|
2290
2283
|
if not self._should_use_multistmtexprs(left):
|
|
2291
2284
|
return False
|
|
2292
2285
|
# create a MultiStatementExpression for left_right_cond
|
|
2293
2286
|
stmts = self._build_multistatementexpr_statements(left)
|
|
2294
2287
|
assert stmts is not None
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
)
|
|
2298
|
-
memo[right_left_cond._hash] = mstmt_expr
|
|
2299
|
-
cond = self.cond_proc.convert_claripy_bool_ast(
|
|
2300
|
-
claripy.And(left_cond, right_left_cond),
|
|
2301
|
-
memo=memo,
|
|
2302
|
-
)
|
|
2288
|
+
left_right_cond_expr = MultiStatementExpression(None, stmts, left_right_cond_expr, ins_addr=left.addr)
|
|
2289
|
+
cond = BinaryOp(None, "LogicalAnd", [left_cond_expr, left_right_cond_expr], ins_addr=start_node.addr)
|
|
2303
2290
|
cond_jump = ConditionalJump(
|
|
2304
2291
|
None,
|
|
2305
2292
|
cond,
|
|
@@ -25,6 +25,7 @@ class SRDAModel:
|
|
|
25
25
|
self.all_tmp_definitions: dict[CodeLocation, dict[atoms.Tmp, int]] = defaultdict(dict)
|
|
26
26
|
self.all_tmp_uses: dict[CodeLocation, dict[atoms.Tmp, set[tuple[Tmp, int]]]] = defaultdict(dict)
|
|
27
27
|
self.phi_vvar_ids: set[int] = set()
|
|
28
|
+
self.phivarid_to_varids_with_unknown: dict[int, set[int | None]] = {}
|
|
28
29
|
self.phivarid_to_varids: dict[int, set[int]] = {}
|
|
29
30
|
self.vvar_uses_by_loc: dict[CodeLocation, list[int]] = {}
|
|
30
31
|
|
|
@@ -63,7 +63,7 @@ class SReachingDefinitionsAnalysis(Analysis):
|
|
|
63
63
|
case _:
|
|
64
64
|
raise NotImplementedError
|
|
65
65
|
|
|
66
|
-
phi_vvars: dict[int, set[int]] = {}
|
|
66
|
+
phi_vvars: dict[int, set[int | None]] = {}
|
|
67
67
|
# find all vvar definitions
|
|
68
68
|
vvar_deflocs = get_vvar_deflocs(blocks.values(), phi_vvars=phi_vvars)
|
|
69
69
|
# find all explicit vvar uses
|
|
@@ -87,7 +87,10 @@ class SReachingDefinitionsAnalysis(Analysis):
|
|
|
87
87
|
self.model.phi_vvar_ids = set(phi_vvars)
|
|
88
88
|
self.model.phivarid_to_varids = {}
|
|
89
89
|
for vvar_id, src_vvars in phi_vvars.items():
|
|
90
|
-
self.model.
|
|
90
|
+
self.model.phivarid_to_varids_with_unknown[vvar_id] = src_vvars
|
|
91
|
+
self.model.phivarid_to_varids[vvar_id] = ( # type: ignore
|
|
92
|
+
{vvar_id for vvar_id in src_vvars if vvar_id is not None} if None in src_vvars else src_vvars
|
|
93
|
+
)
|
|
91
94
|
|
|
92
95
|
if self.mode == "function":
|
|
93
96
|
|
|
@@ -525,6 +525,10 @@ class SimEngineVRBase(
|
|
|
525
525
|
def _store_to_stack(
|
|
526
526
|
self, stack_offset, data: RichR[claripy.ast.BV | claripy.ast.FP], size, offset=0, atom=None, endness=None
|
|
527
527
|
):
|
|
528
|
+
"""
|
|
529
|
+
Store data to a stack location. We limit the size of the data to store to 256 bytes for performance reasons.
|
|
530
|
+
"""
|
|
531
|
+
|
|
528
532
|
if atom is None:
|
|
529
533
|
existing_vars = self.state.variable_manager[self.func_addr].find_variables_by_stmt(
|
|
530
534
|
self.block.addr, self.stmt_idx, "memory"
|
|
@@ -550,7 +554,11 @@ class SimEngineVRBase(
|
|
|
550
554
|
variable, variable_offset = next(iter(existing_vars))
|
|
551
555
|
|
|
552
556
|
if isinstance(stack_offset, int):
|
|
553
|
-
expr =
|
|
557
|
+
expr = data.data
|
|
558
|
+
if isinstance(expr, claripy.ast.Bits) and expr.size() > 1024:
|
|
559
|
+
# we don't write more than 256 bytes to the stack at a time for performance reasons
|
|
560
|
+
expr = expr[expr.size() - 1 : expr.size() - 1024]
|
|
561
|
+
expr = self.state.annotate_with_variables(expr, [(variable_offset, variable)])
|
|
554
562
|
stack_addr = self.state.stack_addr_from_offset(stack_offset)
|
|
555
563
|
self.state.stack_region.store(stack_addr, expr, endness=endness)
|
|
556
564
|
|
|
@@ -5,6 +5,8 @@ from collections.abc import Generator, Iterable
|
|
|
5
5
|
import logging
|
|
6
6
|
from collections import defaultdict
|
|
7
7
|
|
|
8
|
+
import networkx
|
|
9
|
+
|
|
8
10
|
import archinfo
|
|
9
11
|
import claripy
|
|
10
12
|
from claripy.annotation import Annotation
|
|
@@ -86,8 +88,18 @@ class VariableRecoveryBase(Analysis):
|
|
|
86
88
|
The base class for VariableRecovery and VariableRecoveryFast.
|
|
87
89
|
"""
|
|
88
90
|
|
|
89
|
-
def __init__(
|
|
91
|
+
def __init__(
|
|
92
|
+
self,
|
|
93
|
+
func,
|
|
94
|
+
max_iterations,
|
|
95
|
+
store_live_variables: bool,
|
|
96
|
+
vvar_to_vvar: dict[int, int] | None = None,
|
|
97
|
+
func_graph: networkx.DiGraph | None = None,
|
|
98
|
+
entry_node_addr: int | tuple[int, int | None] | None = None,
|
|
99
|
+
):
|
|
90
100
|
self.function = func
|
|
101
|
+
self.func_graph = func_graph
|
|
102
|
+
self.entry_node_addr = entry_node_addr
|
|
91
103
|
self.variable_manager = self.kb.variables
|
|
92
104
|
|
|
93
105
|
self._max_iterations = max_iterations
|
|
@@ -120,7 +132,23 @@ class VariableRecoveryBase(Analysis):
|
|
|
120
132
|
|
|
121
133
|
def initialize_dominance_frontiers(self):
|
|
122
134
|
# Computer the dominance frontier for each node in the graph
|
|
123
|
-
|
|
135
|
+
func_entry = None
|
|
136
|
+
if self.func_graph is not None:
|
|
137
|
+
entry_node_addr = self.entry_node_addr if self.entry_node_addr is not None else self.function.addr
|
|
138
|
+
assert entry_node_addr is not None
|
|
139
|
+
if isinstance(entry_node_addr, int):
|
|
140
|
+
func_entry = next(iter(node for node in self.func_graph if node.addr == entry_node_addr))
|
|
141
|
+
elif isinstance(entry_node_addr, tuple):
|
|
142
|
+
func_entry = next(
|
|
143
|
+
iter(
|
|
144
|
+
node
|
|
145
|
+
for node in self.func_graph
|
|
146
|
+
if node.addr == entry_node_addr[0] and node.idx == entry_node_addr[1]
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
else:
|
|
150
|
+
raise TypeError(f"Unsupported entry node address type: {type(entry_node_addr)}")
|
|
151
|
+
df = self.project.analyses.DominanceFrontier(self.function, func_graph=self.func_graph, entry=func_entry)
|
|
124
152
|
self._dominance_frontiers = defaultdict(set)
|
|
125
153
|
for b0, domfront in df.frontiers.items():
|
|
126
154
|
for d in domfront:
|
|
@@ -237,6 +237,7 @@ class VariableRecoveryFast(ForwardAnalysis, VariableRecoveryBase): # pylint:dis
|
|
|
237
237
|
self,
|
|
238
238
|
func: Function | str | int,
|
|
239
239
|
func_graph: networkx.DiGraph | None = None,
|
|
240
|
+
entry_node_addr: int | tuple[int, int | None] | None = None,
|
|
240
241
|
max_iterations: int = 2,
|
|
241
242
|
low_priority=False,
|
|
242
243
|
track_sp=True,
|
|
@@ -259,10 +260,18 @@ class VariableRecoveryFast(ForwardAnalysis, VariableRecoveryBase): # pylint:dis
|
|
|
259
260
|
function_graph_visitor = visitors.FunctionGraphVisitor(func, graph=func_graph)
|
|
260
261
|
|
|
261
262
|
# Make sure the function is not empty
|
|
262
|
-
if not func.block_addrs_set or func.startpoint is None:
|
|
263
|
+
if (not func.block_addrs_set or func.startpoint is None) and not func_graph:
|
|
263
264
|
raise AngrVariableRecoveryError(f"Function {func!r} is empty.")
|
|
264
265
|
|
|
265
|
-
VariableRecoveryBase.__init__(
|
|
266
|
+
VariableRecoveryBase.__init__(
|
|
267
|
+
self,
|
|
268
|
+
func,
|
|
269
|
+
max_iterations,
|
|
270
|
+
store_live_variables,
|
|
271
|
+
vvar_to_vvar=vvar_to_vvar,
|
|
272
|
+
func_graph=func_graph_with_calls,
|
|
273
|
+
entry_node_addr=entry_node_addr,
|
|
274
|
+
)
|
|
266
275
|
ForwardAnalysis.__init__(
|
|
267
276
|
self, order_jobs=True, allow_merging=True, allow_widening=False, graph_visitor=function_graph_visitor
|
|
268
277
|
)
|
angr/emulator.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
from angr.engines.concrete import ConcreteEngine, HeavyConcreteState
|
|
7
|
+
from angr.errors import AngrError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
log = logging.getLogger(name=__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EmulatorException(AngrError):
|
|
14
|
+
"""Base class for exceptions raised by the Emulator."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EngineException(EmulatorException):
|
|
18
|
+
"""Exception raised when the emulator encounters an unhandlable error in the engine."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class StateDivergedException(EmulatorException):
|
|
22
|
+
"""Exception raised when an engine returns multiple successors."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class EmulatorStopReason(Enum):
|
|
26
|
+
"""
|
|
27
|
+
Enum representing the reason for stopping the emulator.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
INSTRUCTION_LIMIT = "instruction_limit"
|
|
31
|
+
BREAKPOINT = "breakpoint"
|
|
32
|
+
NO_SUCCESSORS = "no_successors"
|
|
33
|
+
MEMORY_ERROR = "memory_error"
|
|
34
|
+
FAILURE = "failure"
|
|
35
|
+
EXIT = "exit"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Emulator:
|
|
39
|
+
"""
|
|
40
|
+
Emulator is a utility that adapts an angr `ConcreteEngine` to a more
|
|
41
|
+
user-friendly interface for concrete execution. It only supports concrete
|
|
42
|
+
execution and requires a ConcreteEngine.
|
|
43
|
+
|
|
44
|
+
Saftey: This class is not thread-safe. It should only be used in a
|
|
45
|
+
single-threaded context. It can be safely shared between multiple threads,
|
|
46
|
+
provided that only one thread is using it at a time.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
_engine: ConcreteEngine
|
|
50
|
+
_state: HeavyConcreteState
|
|
51
|
+
|
|
52
|
+
def __init__(self, engine: ConcreteEngine, init_state: HeavyConcreteState):
|
|
53
|
+
"""
|
|
54
|
+
:param engine: The `ConcreteEngine` to use for emulation.
|
|
55
|
+
:param init_state: The initial state to use for emulation.
|
|
56
|
+
"""
|
|
57
|
+
self._engine = engine
|
|
58
|
+
self._state = init_state
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def state(self) -> HeavyConcreteState:
|
|
62
|
+
"""
|
|
63
|
+
The current state of the emulator.
|
|
64
|
+
"""
|
|
65
|
+
return self._state
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def breakpoints(self) -> set[int]:
|
|
69
|
+
"""
|
|
70
|
+
The set of currently set breakpoints.
|
|
71
|
+
"""
|
|
72
|
+
return self._engine.get_breakpoints()
|
|
73
|
+
|
|
74
|
+
def add_breakpoint(self, addr: int) -> None:
|
|
75
|
+
"""
|
|
76
|
+
Add a breakpoint at the given address.
|
|
77
|
+
|
|
78
|
+
:param addr: The address to set the breakpoint at.
|
|
79
|
+
"""
|
|
80
|
+
self._engine.add_breakpoint(addr)
|
|
81
|
+
|
|
82
|
+
def remove_breakpoint(self, addr: int) -> None:
|
|
83
|
+
"""
|
|
84
|
+
Remove a breakpoint at the given address, if present.
|
|
85
|
+
|
|
86
|
+
:param addr: The address to remove the breakpoint from.
|
|
87
|
+
"""
|
|
88
|
+
self._engine.remove_breakpoint(addr)
|
|
89
|
+
|
|
90
|
+
def run(self, num_inst: int | None = None) -> EmulatorStopReason:
|
|
91
|
+
"""
|
|
92
|
+
Execute the emulator.
|
|
93
|
+
"""
|
|
94
|
+
completed_engine_execs = 0
|
|
95
|
+
num_inst_executed: int = 0
|
|
96
|
+
while self._state.history.jumpkind != "Ijk_Exit":
|
|
97
|
+
# Check if there is a breakpoint at the current address
|
|
98
|
+
if completed_engine_execs > 0 and self._state.addr in self._engine.get_breakpoints():
|
|
99
|
+
return EmulatorStopReason.BREAKPOINT
|
|
100
|
+
|
|
101
|
+
# Check if we've already executed the requested number of instructions
|
|
102
|
+
if num_inst is not None and num_inst_executed >= num_inst:
|
|
103
|
+
return EmulatorStopReason.INSTRUCTION_LIMIT
|
|
104
|
+
|
|
105
|
+
# Calculate remaining instructions for this engine execution
|
|
106
|
+
remaining_inst: int | None = None
|
|
107
|
+
if num_inst is not None:
|
|
108
|
+
remaining_inst = num_inst - num_inst_executed
|
|
109
|
+
|
|
110
|
+
# Run the engine to get successors
|
|
111
|
+
try:
|
|
112
|
+
successors = self._engine.process(self._state, num_inst=remaining_inst)
|
|
113
|
+
except EngineException as e:
|
|
114
|
+
raise EngineException(f"Engine encountered an error: {e}") from e
|
|
115
|
+
|
|
116
|
+
# Handle cases with an unexpected number of successors
|
|
117
|
+
if len(successors.successors) == 0:
|
|
118
|
+
return EmulatorStopReason.NO_SUCCESSORS
|
|
119
|
+
if len(successors.successors) > 1:
|
|
120
|
+
log.warning("Concrete engine returned multiple successors")
|
|
121
|
+
|
|
122
|
+
# Set the state before raising further exceptions
|
|
123
|
+
self._state = successors.successors[0]
|
|
124
|
+
|
|
125
|
+
# Track the number of instructions executed using the state's history
|
|
126
|
+
if self._state.history.recent_instruction_count > 0:
|
|
127
|
+
num_inst_executed += self._state.history.recent_instruction_count
|
|
128
|
+
|
|
129
|
+
if successors.successors[0].history.jumpkind == "Ijk_SigSEGV":
|
|
130
|
+
return EmulatorStopReason.MEMORY_ERROR
|
|
131
|
+
|
|
132
|
+
completed_engine_execs += 1
|
|
133
|
+
|
|
134
|
+
return EmulatorStopReason.EXIT
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
__all__ = (
|
|
138
|
+
"Emulator",
|
|
139
|
+
"EmulatorException",
|
|
140
|
+
"EmulatorStopReason",
|
|
141
|
+
"EngineException",
|
|
142
|
+
"StateDivergedException",
|
|
143
|
+
)
|
angr/engines/concrete.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import typing
|
|
5
|
+
from abc import abstractmethod, ABCMeta
|
|
6
|
+
from typing_extensions import override
|
|
7
|
+
|
|
8
|
+
import claripy
|
|
9
|
+
from angr.engines.successors import SimSuccessors, SuccessorsEngine
|
|
10
|
+
from angr.sim_state import SimState
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
HeavyConcreteState = SimState[int, int]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ConcreteEngine(SuccessorsEngine, metaclass=ABCMeta):
|
|
19
|
+
"""
|
|
20
|
+
ConcreteEngine extends SuccessorsEngine and adds APIs for managing breakpoints.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
@abstractmethod
|
|
24
|
+
def get_breakpoints(self) -> set[int]:
|
|
25
|
+
"""Return the set of currently set breakpoints."""
|
|
26
|
+
|
|
27
|
+
@abstractmethod
|
|
28
|
+
def add_breakpoint(self, addr: int) -> None:
|
|
29
|
+
"""Add a breakpoint at the given address."""
|
|
30
|
+
|
|
31
|
+
@abstractmethod
|
|
32
|
+
def remove_breakpoint(self, addr: int) -> None:
|
|
33
|
+
"""Remove a breakpoint at the given address, if present."""
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def process_concrete(self, state: HeavyConcreteState, num_inst: int | None = None) -> HeavyConcreteState:
|
|
37
|
+
"""
|
|
38
|
+
Process the concrete state and return a HeavyState object.
|
|
39
|
+
|
|
40
|
+
:param state: The concrete state to process.
|
|
41
|
+
:return: A HeavyState object representing the processed state.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@override
|
|
45
|
+
def process_successors(
|
|
46
|
+
self, successors: SimSuccessors, *, num_inst: int | None = None, **kwargs: dict[str, typing.Any]
|
|
47
|
+
):
|
|
48
|
+
if len(kwargs) > 0:
|
|
49
|
+
log.warning("ConcreteEngine.process_successors received unknown kwargs: %s", kwargs)
|
|
50
|
+
|
|
51
|
+
# TODO: Properly error here when the state is not a HeavyConcreteState
|
|
52
|
+
# Alternatively, we could make SimSuccessors generic over the state type too
|
|
53
|
+
concrete_state = typing.cast(HeavyConcreteState, self.state)
|
|
54
|
+
|
|
55
|
+
concrete_successor = self.process_concrete(concrete_state, num_inst=num_inst)
|
|
56
|
+
successors.add_successor(
|
|
57
|
+
concrete_successor,
|
|
58
|
+
concrete_successor.ip,
|
|
59
|
+
claripy.true(),
|
|
60
|
+
concrete_successor.history.jumpkind,
|
|
61
|
+
add_guard=False,
|
|
62
|
+
)
|
|
63
|
+
successors.processed = True
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
__all__ = ["ConcreteEngine", "HeavyConcreteState"]
|
angr/engines/icicle.py
CHANGED
|
@@ -5,17 +5,16 @@ from __future__ import annotations
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
7
|
from dataclasses import dataclass
|
|
8
|
+
from typing_extensions import override
|
|
8
9
|
|
|
9
|
-
import claripy
|
|
10
10
|
import pypcode
|
|
11
11
|
from archinfo import Arch, Endness
|
|
12
12
|
|
|
13
|
+
from angr.engines.concrete import ConcreteEngine, HeavyConcreteState
|
|
13
14
|
from angr.engines.failure import SimEngineFailure
|
|
14
15
|
from angr.engines.hook import HooksMixin
|
|
15
|
-
from angr.engines.successors import SuccessorsEngine
|
|
16
16
|
from angr.engines.syscall import SimEngineSyscall
|
|
17
17
|
from angr.rustylib.icicle import Icicle, VmExit, ExceptionCode
|
|
18
|
-
from angr.sim_state import SimState
|
|
19
18
|
|
|
20
19
|
log = logging.getLogger(__name__)
|
|
21
20
|
|
|
@@ -30,12 +29,13 @@ class IcicleStateTranslationData:
|
|
|
30
29
|
to an angr state.
|
|
31
30
|
"""
|
|
32
31
|
|
|
33
|
-
base_state:
|
|
32
|
+
base_state: HeavyConcreteState
|
|
34
33
|
registers: set[str]
|
|
35
34
|
writable_pages: set[int]
|
|
35
|
+
initial_cpu_icount: int
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class IcicleEngine(
|
|
38
|
+
class IcicleEngine(ConcreteEngine):
|
|
39
39
|
"""
|
|
40
40
|
An angr engine that uses Icicle to execute concrete states. The purpose of
|
|
41
41
|
this implementation is to provide a high-performance concrete execution
|
|
@@ -55,6 +55,16 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
55
55
|
intends to provide a more complete set of features, such as hooks and syscalls.
|
|
56
56
|
"""
|
|
57
57
|
|
|
58
|
+
breakpoints: set[int]
|
|
59
|
+
|
|
60
|
+
def __init__(self, *args, **kwargs):
|
|
61
|
+
"""
|
|
62
|
+
Initialize the IcicleEngine. This sets up the breakpoints set and
|
|
63
|
+
initializes the parent class.
|
|
64
|
+
"""
|
|
65
|
+
super().__init__(*args, **kwargs)
|
|
66
|
+
self.breakpoints = set()
|
|
67
|
+
|
|
58
68
|
@staticmethod
|
|
59
69
|
def __make_icicle_arch(arch: Arch) -> str | None:
|
|
60
70
|
"""
|
|
@@ -81,7 +91,7 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
81
91
|
return IcicleEngine.__is_arm(icicle_arch) and addr & 1 == 1
|
|
82
92
|
|
|
83
93
|
@staticmethod
|
|
84
|
-
def __get_pages(state:
|
|
94
|
+
def __get_pages(state: HeavyConcreteState) -> set[int]:
|
|
85
95
|
"""
|
|
86
96
|
Unfortunately, the memory model doesn't have a way to get all pages.
|
|
87
97
|
Instead, we can get all of the backers from the loader, then all of the
|
|
@@ -104,7 +114,7 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
104
114
|
return pages
|
|
105
115
|
|
|
106
116
|
@staticmethod
|
|
107
|
-
def __convert_angr_state_to_icicle(state:
|
|
117
|
+
def __convert_angr_state_to_icicle(state: HeavyConcreteState) -> tuple[Icicle, IcicleStateTranslationData]:
|
|
108
118
|
icicle_arch = IcicleEngine.__make_icicle_arch(state.arch)
|
|
109
119
|
if icicle_arch is None:
|
|
110
120
|
raise ValueError("Unsupported architecture")
|
|
@@ -161,12 +171,15 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
161
171
|
base_state=state,
|
|
162
172
|
registers=copied_registers,
|
|
163
173
|
writable_pages=writable_pages,
|
|
174
|
+
initial_cpu_icount=emu.cpu_icount,
|
|
164
175
|
)
|
|
165
176
|
|
|
166
177
|
return (emu, translation_data)
|
|
167
178
|
|
|
168
179
|
@staticmethod
|
|
169
|
-
def __convert_icicle_state_to_angr(
|
|
180
|
+
def __convert_icicle_state_to_angr(
|
|
181
|
+
emu: Icicle, translation_data: IcicleStateTranslationData, status: VmExit
|
|
182
|
+
) -> HeavyConcreteState:
|
|
170
183
|
state = translation_data.base_state.copy()
|
|
171
184
|
|
|
172
185
|
# 1. Copy the register values
|
|
@@ -181,20 +194,8 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
181
194
|
addr = page_num * state.memory.page_size
|
|
182
195
|
state.memory.store(addr, emu.mem_read(addr, state.memory.page_size))
|
|
183
196
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def process_successors(self, successors, *, num_inst=0, **kwargs):
|
|
187
|
-
if len(kwargs) > 0:
|
|
188
|
-
log.warning("IcicleEngine.process_successors received unknown kwargs: %s", kwargs)
|
|
189
|
-
|
|
190
|
-
emu, translation_data = self.__convert_angr_state_to_icicle(self.state)
|
|
191
|
-
|
|
192
|
-
if num_inst > 0:
|
|
193
|
-
emu.icount_limit = num_inst
|
|
194
|
-
|
|
195
|
-
status = emu.run() # pylint: ignore=assignment-from-no-return (pylint bug)
|
|
197
|
+
# 3. Set history.jumpkind
|
|
196
198
|
exc = emu.exception_code
|
|
197
|
-
|
|
198
199
|
if status == VmExit.UnhandledException:
|
|
199
200
|
if exc in (
|
|
200
201
|
ExceptionCode.ReadUnmapped,
|
|
@@ -203,22 +204,57 @@ class IcicleEngine(SuccessorsEngine):
|
|
|
203
204
|
ExceptionCode.WritePerm,
|
|
204
205
|
ExceptionCode.ExecViolation,
|
|
205
206
|
):
|
|
206
|
-
jumpkind = "Ijk_SigSEGV"
|
|
207
|
+
state.history.jumpkind = "Ijk_SigSEGV"
|
|
207
208
|
elif exc == ExceptionCode.Syscall:
|
|
208
|
-
jumpkind = "Ijk_Syscall"
|
|
209
|
+
state.history.jumpkind = "Ijk_Syscall"
|
|
209
210
|
elif exc == ExceptionCode.Halt:
|
|
210
|
-
jumpkind = "Ijk_Exit"
|
|
211
|
+
state.history.jumpkind = "Ijk_Exit"
|
|
211
212
|
elif exc == ExceptionCode.InvalidInstruction:
|
|
212
|
-
jumpkind = "Ijk_NoDecode"
|
|
213
|
+
state.history.jumpkind = "Ijk_NoDecode"
|
|
213
214
|
else:
|
|
214
|
-
jumpkind = "Ijk_EmFail"
|
|
215
|
+
state.history.jumpkind = "Ijk_EmFail"
|
|
215
216
|
else:
|
|
216
|
-
jumpkind = "Ijk_Boring"
|
|
217
|
+
state.history.jumpkind = "Ijk_Boring"
|
|
218
|
+
|
|
219
|
+
# 4. Set history.recent_instruction_count
|
|
220
|
+
state.history.recent_instruction_count = emu.cpu_icount - translation_data.initial_cpu_icount
|
|
221
|
+
|
|
222
|
+
return state
|
|
223
|
+
|
|
224
|
+
@override
|
|
225
|
+
def get_breakpoints(self) -> set[int]:
|
|
226
|
+
"""Return the set of currently set breakpoints."""
|
|
227
|
+
return self.breakpoints
|
|
228
|
+
|
|
229
|
+
@override
|
|
230
|
+
def add_breakpoint(self, addr: int) -> None:
|
|
231
|
+
"""Add a breakpoint at the given address."""
|
|
232
|
+
self.breakpoints.add(addr)
|
|
233
|
+
|
|
234
|
+
@override
|
|
235
|
+
def remove_breakpoint(self, addr: int) -> None:
|
|
236
|
+
"""Remove a breakpoint at the given address, if present."""
|
|
237
|
+
self.breakpoints.discard(addr)
|
|
238
|
+
|
|
239
|
+
@override
|
|
240
|
+
def process_concrete(self, state: HeavyConcreteState, num_inst: int | None = None) -> HeavyConcreteState:
|
|
241
|
+
emu, translation_data = self.__convert_angr_state_to_icicle(state)
|
|
242
|
+
|
|
243
|
+
# Set breakpoints, skip the current PC. This assumes that if running
|
|
244
|
+
# with a breakpoint at the current PC, then the user has already done
|
|
245
|
+
# the necessary handling and is resuming execution.
|
|
246
|
+
for addr in self.breakpoints:
|
|
247
|
+
if emu.pc != addr:
|
|
248
|
+
emu.add_breakpoint(addr)
|
|
249
|
+
|
|
250
|
+
# Set the instruction count limit
|
|
251
|
+
if num_inst is not None and num_inst > 0:
|
|
252
|
+
emu.icount_limit = num_inst
|
|
217
253
|
|
|
218
|
-
|
|
219
|
-
|
|
254
|
+
# Run it
|
|
255
|
+
status = emu.run()
|
|
220
256
|
|
|
221
|
-
|
|
257
|
+
return IcicleEngine.__convert_icicle_state_to_angr(emu, translation_data, status)
|
|
222
258
|
|
|
223
259
|
|
|
224
260
|
class UberIcicleEngine(SimEngineFailure, SimEngineSyscall, HooksMixin, IcicleEngine):
|
|
@@ -94,7 +94,7 @@ class DrillerCore(ExplorationTechnique):
|
|
|
94
94
|
@staticmethod
|
|
95
95
|
def _has_false(state):
|
|
96
96
|
# Check if the state is unsat even if we remove preconstraints.
|
|
97
|
-
if state.scratch.guard
|
|
97
|
+
if claripy.is_false(state.scratch.guard):
|
|
98
98
|
return True
|
|
99
99
|
|
|
100
|
-
return any(
|
|
100
|
+
return any(claripy.is_false(c) for c in state.solver.constraints)
|
angr/project.py
CHANGED
|
@@ -297,11 +297,18 @@ class Project:
|
|
|
297
297
|
|
|
298
298
|
# Step 1: get the set of libraries we are allowed to use to resolve unresolved symbols
|
|
299
299
|
missing_libs = []
|
|
300
|
+
missing_wincore_dlls = False
|
|
300
301
|
for lib_name in self.loader.missing_dependencies:
|
|
301
302
|
try:
|
|
302
303
|
missing_libs.extend(SIM_LIBRARIES[lib_name])
|
|
303
304
|
except KeyError:
|
|
304
305
|
l.info("There are no simprocedures for missing library %s :(", lib_name)
|
|
306
|
+
if lib_name.startswith("api-ms-win-"):
|
|
307
|
+
missing_wincore_dlls = True
|
|
308
|
+
if missing_wincore_dlls and "kernel32.dll" not in self.loader.missing_dependencies:
|
|
309
|
+
# some of the missing api-ms-win-*.dll libraries are actually provided by kernel32.dll
|
|
310
|
+
missing_libs.extend(SIM_LIBRARIES["kernel32.dll"])
|
|
311
|
+
|
|
305
312
|
# additionally provide libraries we _have_ loaded as a fallback fallback
|
|
306
313
|
# this helps in the case that e.g. CLE picked up a linux arm libc to satisfy an android arm binary
|
|
307
314
|
for lib in self.loader.all_objects:
|
angr/rustylib.abi3.so
CHANGED
|
Binary file
|