angr 9.2.159__cp310-abi3-macosx_11_0_arm64.whl → 9.2.161__cp310-abi3-macosx_11_0_arm64.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 +5 -1
- angr/analyses/decompiler/decompiler.py +12 -9
- 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/typehoon/simple_solver.py +231 -29
- angr/analyses/typehoon/typehoon.py +10 -2
- angr/analyses/variable_recovery/engine_ail.py +8 -20
- 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/state_plugins/unicorn_engine.py +4 -4
- angr/{lib/angr_native.dylib → unicornlib.dylib} +0 -0
- angr/utils/graph.py +20 -1
- angr/utils/ssa/__init__.py +3 -3
- {angr-9.2.159.dist-info → angr-9.2.161.dist-info}/METADATA +5 -6
- {angr-9.2.159.dist-info → angr-9.2.161.dist-info}/RECORD +36 -34
- angr/analyses/decompiler/peephole_optimizations/a_sub_a_div_const_mul_const.py +0 -57
- {angr-9.2.159.dist-info → angr-9.2.161.dist-info}/WHEEL +0 -0
- {angr-9.2.159.dist-info → angr-9.2.161.dist-info}/entry_points.txt +0 -0
- {angr-9.2.159.dist-info → angr-9.2.161.dist-info}/licenses/LICENSE +0 -0
- {angr-9.2.159.dist-info → angr-9.2.161.dist-info}/top_level.txt +0 -0
|
@@ -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
|
angr/sim_type.py
CHANGED
|
@@ -609,33 +609,41 @@ class SimTypeWideChar(SimTypeReg):
|
|
|
609
609
|
|
|
610
610
|
_base_name = "char"
|
|
611
611
|
|
|
612
|
-
def __init__(self, signed=True, label=None):
|
|
612
|
+
def __init__(self, signed=True, label=None, endness: Endness = Endness.BE):
|
|
613
613
|
"""
|
|
614
614
|
:param label: the type label.
|
|
615
615
|
"""
|
|
616
616
|
SimTypeReg.__init__(self, 16, label=label)
|
|
617
617
|
self.signed = signed
|
|
618
|
+
self.endness = endness
|
|
618
619
|
|
|
619
620
|
def __repr__(self):
|
|
620
621
|
return "wchar"
|
|
621
622
|
|
|
622
623
|
def store(self, state, addr, value: StoreType):
|
|
623
|
-
self._size = state.arch.byte_width
|
|
624
624
|
try:
|
|
625
625
|
super().store(state, addr, value)
|
|
626
626
|
except TypeError:
|
|
627
627
|
if isinstance(value, bytes) and len(value) == 2:
|
|
628
|
-
|
|
628
|
+
inner = (
|
|
629
|
+
((value[0] << state.arch.byte_width) | value[1])
|
|
630
|
+
if self.endness == Endness.BE
|
|
631
|
+
else ((value[1] << state.arch.byte_width) | value[0])
|
|
632
|
+
)
|
|
633
|
+
value = claripy.BVV(inner, state.arch.byte_width * 2)
|
|
629
634
|
super().store(state, addr, value)
|
|
630
635
|
else:
|
|
631
636
|
raise
|
|
632
637
|
|
|
633
638
|
def extract(self, state, addr, concrete=False) -> Any:
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
out = super().extract(state, addr, concrete)
|
|
639
|
+
out = state.memory.load(addr, 2)
|
|
637
640
|
if concrete:
|
|
638
|
-
|
|
641
|
+
data = state.solver.eval(out, cast_to=bytes)
|
|
642
|
+
fmt_str = "utf-16be" if self.endness == Endness.BE else "utf-16le"
|
|
643
|
+
try:
|
|
644
|
+
return data.decode(fmt_str)
|
|
645
|
+
except UnicodeDecodeError:
|
|
646
|
+
return data
|
|
639
647
|
return out
|
|
640
648
|
|
|
641
649
|
def _init_str(self):
|
|
@@ -645,7 +653,7 @@ class SimTypeWideChar(SimTypeReg):
|
|
|
645
653
|
)
|
|
646
654
|
|
|
647
655
|
def copy(self):
|
|
648
|
-
return self.__class__(signed=self.signed, label=self.label)
|
|
656
|
+
return self.__class__(signed=self.signed, label=self.label, endness=self.endness)
|
|
649
657
|
|
|
650
658
|
|
|
651
659
|
class SimTypeBool(SimTypeReg):
|
|
@@ -394,14 +394,14 @@ class _VexArchInfo(ctypes.Structure):
|
|
|
394
394
|
|
|
395
395
|
def _load_native():
|
|
396
396
|
if sys.platform == "darwin":
|
|
397
|
-
libfile = "
|
|
397
|
+
libfile = "unicornlib.dylib"
|
|
398
398
|
elif sys.platform in {"win32", "cygwin"}:
|
|
399
|
-
libfile = "
|
|
399
|
+
libfile = "unicornlib.dll"
|
|
400
400
|
else:
|
|
401
|
-
libfile = "
|
|
401
|
+
libfile = "unicornlib.so"
|
|
402
402
|
|
|
403
403
|
try:
|
|
404
|
-
angr_path = str(importlib.resources.files("angr") /
|
|
404
|
+
angr_path = str(importlib.resources.files("angr") / libfile)
|
|
405
405
|
h = ctypes.CDLL(angr_path)
|
|
406
406
|
|
|
407
407
|
VexArch = ctypes.c_int
|
|
Binary file
|
angr/utils/graph.py
CHANGED
|
@@ -534,11 +534,30 @@ class Dominators:
|
|
|
534
534
|
|
|
535
535
|
def _pd_eval(self, v):
|
|
536
536
|
assert self._ancestor is not None
|
|
537
|
+
assert self._semi is not None
|
|
537
538
|
assert self._label is not None
|
|
538
539
|
|
|
539
540
|
if self._ancestor[v.index] is None:
|
|
540
541
|
return v
|
|
541
|
-
|
|
542
|
+
|
|
543
|
+
# pd_compress without recursion
|
|
544
|
+
queue = []
|
|
545
|
+
current = v
|
|
546
|
+
ancestor = self._ancestor[current.index]
|
|
547
|
+
greater_ancestor = self._ancestor[ancestor.index]
|
|
548
|
+
while greater_ancestor is not None:
|
|
549
|
+
queue.append(current)
|
|
550
|
+
current, ancestor = ancestor, greater_ancestor
|
|
551
|
+
greater_ancestor = self._ancestor[ancestor.index]
|
|
552
|
+
|
|
553
|
+
for vv in reversed(queue):
|
|
554
|
+
if (
|
|
555
|
+
self._semi[self._label[self._ancestor[vv.index].index].index].index
|
|
556
|
+
< self._semi[self._label[vv.index].index].index
|
|
557
|
+
):
|
|
558
|
+
self._label[vv.index] = self._label[self._ancestor[vv.index].index]
|
|
559
|
+
self._ancestor[vv.index] = self._ancestor[self._ancestor[vv.index].index]
|
|
560
|
+
|
|
542
561
|
return self._label[v.index]
|
|
543
562
|
|
|
544
563
|
def _pd_compress(self, v):
|
angr/utils/ssa/__init__.py
CHANGED
|
@@ -89,7 +89,7 @@ def get_reg_offset_base(reg_offset, arch, size=None, resilient=True):
|
|
|
89
89
|
|
|
90
90
|
|
|
91
91
|
def get_vvar_deflocs(
|
|
92
|
-
blocks, phi_vvars: dict[int, set[int]] | None = None
|
|
92
|
+
blocks, phi_vvars: dict[int, set[int | None]] | None = None
|
|
93
93
|
) -> dict[int, tuple[VirtualVariable, CodeLocation]]:
|
|
94
94
|
vvar_to_loc: dict[int, tuple[VirtualVariable, CodeLocation]] = {}
|
|
95
95
|
for block in blocks:
|
|
@@ -100,7 +100,7 @@ def get_vvar_deflocs(
|
|
|
100
100
|
)
|
|
101
101
|
if phi_vvars is not None and isinstance(stmt.src, Phi):
|
|
102
102
|
phi_vvars[stmt.dst.varid] = {
|
|
103
|
-
vvar_.varid for src, vvar_ in stmt.src.src_and_vvars
|
|
103
|
+
vvar_.varid if vvar_ is not None else None for src, vvar_ in stmt.src.src_and_vvars
|
|
104
104
|
}
|
|
105
105
|
elif isinstance(stmt, Call):
|
|
106
106
|
if isinstance(stmt.ret_expr, VirtualVariable):
|
|
@@ -161,7 +161,7 @@ def get_tmp_uselocs(blocks) -> dict[CodeLocation, dict[atoms.Tmp, set[tuple[Tmp,
|
|
|
161
161
|
return tmp_to_loc
|
|
162
162
|
|
|
163
163
|
|
|
164
|
-
def is_const_assignment(stmt: Statement) -> tuple[bool, Const | None]:
|
|
164
|
+
def is_const_assignment(stmt: Statement) -> tuple[bool, Const | StackBaseOffset | None]:
|
|
165
165
|
if isinstance(stmt, Assignment) and isinstance(stmt.src, (Const, StackBaseOffset)):
|
|
166
166
|
return True, stmt.src
|
|
167
167
|
return False, None
|