smallworld-re 1.0.0__py3-none-any.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.
- smallworld/__init__.py +35 -0
- smallworld/analyses/__init__.py +14 -0
- smallworld/analyses/analysis.py +88 -0
- smallworld/analyses/code_coverage.py +31 -0
- smallworld/analyses/colorizer.py +682 -0
- smallworld/analyses/colorizer_summary.py +100 -0
- smallworld/analyses/field_detection/__init__.py +14 -0
- smallworld/analyses/field_detection/field_analysis.py +536 -0
- smallworld/analyses/field_detection/guards.py +26 -0
- smallworld/analyses/field_detection/hints.py +133 -0
- smallworld/analyses/field_detection/malloc.py +211 -0
- smallworld/analyses/forced_exec/__init__.py +3 -0
- smallworld/analyses/forced_exec/forced_exec.py +87 -0
- smallworld/analyses/underlays/__init__.py +4 -0
- smallworld/analyses/underlays/basic.py +13 -0
- smallworld/analyses/underlays/underlay.py +31 -0
- smallworld/analyses/unstable/__init__.py +4 -0
- smallworld/analyses/unstable/angr/__init__.py +0 -0
- smallworld/analyses/unstable/angr/base.py +12 -0
- smallworld/analyses/unstable/angr/divergence.py +274 -0
- smallworld/analyses/unstable/angr/model.py +383 -0
- smallworld/analyses/unstable/angr/nwbt.py +63 -0
- smallworld/analyses/unstable/angr/typedefs.py +170 -0
- smallworld/analyses/unstable/angr/utils.py +25 -0
- smallworld/analyses/unstable/angr/visitor.py +315 -0
- smallworld/analyses/unstable/angr_nwbt.py +106 -0
- smallworld/analyses/unstable/code_coverage.py +54 -0
- smallworld/analyses/unstable/code_reachable.py +44 -0
- smallworld/analyses/unstable/control_flow_tracer.py +71 -0
- smallworld/analyses/unstable/pointer_finder.py +90 -0
- smallworld/arch/__init__.py +0 -0
- smallworld/arch/aarch64_arch.py +286 -0
- smallworld/arch/amd64_arch.py +86 -0
- smallworld/arch/i386_arch.py +44 -0
- smallworld/emulators/__init__.py +14 -0
- smallworld/emulators/angr/__init__.py +7 -0
- smallworld/emulators/angr/angr.py +1652 -0
- smallworld/emulators/angr/default.py +15 -0
- smallworld/emulators/angr/exceptions.py +7 -0
- smallworld/emulators/angr/exploration/__init__.py +9 -0
- smallworld/emulators/angr/exploration/bounds.py +27 -0
- smallworld/emulators/angr/exploration/default.py +17 -0
- smallworld/emulators/angr/exploration/terminate.py +22 -0
- smallworld/emulators/angr/factory.py +55 -0
- smallworld/emulators/angr/machdefs/__init__.py +35 -0
- smallworld/emulators/angr/machdefs/aarch64.py +292 -0
- smallworld/emulators/angr/machdefs/amd64.py +192 -0
- smallworld/emulators/angr/machdefs/arm.py +387 -0
- smallworld/emulators/angr/machdefs/i386.py +221 -0
- smallworld/emulators/angr/machdefs/machdef.py +138 -0
- smallworld/emulators/angr/machdefs/mips.py +184 -0
- smallworld/emulators/angr/machdefs/mips64.py +189 -0
- smallworld/emulators/angr/machdefs/ppc.py +101 -0
- smallworld/emulators/angr/machdefs/riscv.py +261 -0
- smallworld/emulators/angr/machdefs/xtensa.py +255 -0
- smallworld/emulators/angr/memory/__init__.py +7 -0
- smallworld/emulators/angr/memory/default.py +10 -0
- smallworld/emulators/angr/memory/fixups.py +43 -0
- smallworld/emulators/angr/memory/memtrack.py +105 -0
- smallworld/emulators/angr/scratch.py +43 -0
- smallworld/emulators/angr/simos.py +53 -0
- smallworld/emulators/angr/utils.py +70 -0
- smallworld/emulators/emulator.py +1013 -0
- smallworld/emulators/hookable.py +252 -0
- smallworld/emulators/panda/__init__.py +5 -0
- smallworld/emulators/panda/machdefs/__init__.py +28 -0
- smallworld/emulators/panda/machdefs/aarch64.py +93 -0
- smallworld/emulators/panda/machdefs/amd64.py +71 -0
- smallworld/emulators/panda/machdefs/arm.py +89 -0
- smallworld/emulators/panda/machdefs/i386.py +36 -0
- smallworld/emulators/panda/machdefs/machdef.py +86 -0
- smallworld/emulators/panda/machdefs/mips.py +94 -0
- smallworld/emulators/panda/machdefs/mips64.py +91 -0
- smallworld/emulators/panda/machdefs/ppc.py +79 -0
- smallworld/emulators/panda/panda.py +575 -0
- smallworld/emulators/unicorn/__init__.py +13 -0
- smallworld/emulators/unicorn/machdefs/__init__.py +28 -0
- smallworld/emulators/unicorn/machdefs/aarch64.py +310 -0
- smallworld/emulators/unicorn/machdefs/amd64.py +326 -0
- smallworld/emulators/unicorn/machdefs/arm.py +321 -0
- smallworld/emulators/unicorn/machdefs/i386.py +137 -0
- smallworld/emulators/unicorn/machdefs/machdef.py +117 -0
- smallworld/emulators/unicorn/machdefs/mips.py +202 -0
- smallworld/emulators/unicorn/unicorn.py +684 -0
- smallworld/exceptions/__init__.py +5 -0
- smallworld/exceptions/exceptions.py +85 -0
- smallworld/exceptions/unstable/__init__.py +1 -0
- smallworld/exceptions/unstable/exceptions.py +25 -0
- smallworld/extern/__init__.py +4 -0
- smallworld/extern/ctypes.py +94 -0
- smallworld/extern/unstable/__init__.py +1 -0
- smallworld/extern/unstable/ghidra.py +129 -0
- smallworld/helpers.py +107 -0
- smallworld/hinting/__init__.py +8 -0
- smallworld/hinting/hinting.py +214 -0
- smallworld/hinting/hints.py +427 -0
- smallworld/hinting/unstable/__init__.py +2 -0
- smallworld/hinting/utils.py +19 -0
- smallworld/instructions/__init__.py +18 -0
- smallworld/instructions/aarch64.py +20 -0
- smallworld/instructions/arm.py +18 -0
- smallworld/instructions/bsid.py +67 -0
- smallworld/instructions/instructions.py +258 -0
- smallworld/instructions/mips.py +21 -0
- smallworld/instructions/x86.py +100 -0
- smallworld/logging.py +90 -0
- smallworld/platforms.py +95 -0
- smallworld/py.typed +0 -0
- smallworld/state/__init__.py +6 -0
- smallworld/state/cpus/__init__.py +32 -0
- smallworld/state/cpus/aarch64.py +563 -0
- smallworld/state/cpus/amd64.py +676 -0
- smallworld/state/cpus/arm.py +630 -0
- smallworld/state/cpus/cpu.py +71 -0
- smallworld/state/cpus/i386.py +239 -0
- smallworld/state/cpus/mips.py +374 -0
- smallworld/state/cpus/mips64.py +372 -0
- smallworld/state/cpus/powerpc.py +229 -0
- smallworld/state/cpus/riscv.py +357 -0
- smallworld/state/cpus/xtensa.py +80 -0
- smallworld/state/memory/__init__.py +7 -0
- smallworld/state/memory/code.py +70 -0
- smallworld/state/memory/elf/__init__.py +3 -0
- smallworld/state/memory/elf/elf.py +564 -0
- smallworld/state/memory/elf/rela/__init__.py +32 -0
- smallworld/state/memory/elf/rela/aarch64.py +27 -0
- smallworld/state/memory/elf/rela/amd64.py +32 -0
- smallworld/state/memory/elf/rela/arm.py +51 -0
- smallworld/state/memory/elf/rela/i386.py +32 -0
- smallworld/state/memory/elf/rela/mips.py +45 -0
- smallworld/state/memory/elf/rela/ppc.py +45 -0
- smallworld/state/memory/elf/rela/rela.py +63 -0
- smallworld/state/memory/elf/rela/riscv64.py +27 -0
- smallworld/state/memory/elf/rela/xtensa.py +15 -0
- smallworld/state/memory/elf/structs.py +55 -0
- smallworld/state/memory/heap.py +85 -0
- smallworld/state/memory/memory.py +181 -0
- smallworld/state/memory/stack/__init__.py +31 -0
- smallworld/state/memory/stack/aarch64.py +22 -0
- smallworld/state/memory/stack/amd64.py +42 -0
- smallworld/state/memory/stack/arm.py +66 -0
- smallworld/state/memory/stack/i386.py +22 -0
- smallworld/state/memory/stack/mips.py +34 -0
- smallworld/state/memory/stack/mips64.py +34 -0
- smallworld/state/memory/stack/ppc.py +34 -0
- smallworld/state/memory/stack/riscv.py +22 -0
- smallworld/state/memory/stack/stack.py +127 -0
- smallworld/state/memory/stack/xtensa.py +34 -0
- smallworld/state/models/__init__.py +6 -0
- smallworld/state/models/mmio.py +186 -0
- smallworld/state/models/model.py +163 -0
- smallworld/state/models/posix.py +455 -0
- smallworld/state/models/x86/__init__.py +2 -0
- smallworld/state/models/x86/microsoftcdecl.py +35 -0
- smallworld/state/models/x86/systemv.py +240 -0
- smallworld/state/state.py +962 -0
- smallworld/state/unstable/__init__.py +0 -0
- smallworld/state/unstable/elf.py +393 -0
- smallworld/state/x86_registers.py +30 -0
- smallworld/utils.py +935 -0
- smallworld_re-1.0.0.dist-info/LICENSE.txt +21 -0
- smallworld_re-1.0.0.dist-info/METADATA +189 -0
- smallworld_re-1.0.0.dist-info/RECORD +166 -0
- smallworld_re-1.0.0.dist-info/WHEEL +5 -0
- smallworld_re-1.0.0.dist-info/entry_points.txt +2 -0
- smallworld_re-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,274 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
import angr
|
4
|
+
from angr.storage.memory_mixins.memory_mixin import MemoryMixin
|
5
|
+
|
6
|
+
from .... import hinting
|
7
|
+
from ....emulators.angr import PathTerminationSignal
|
8
|
+
from ....exceptions import AnalysisSignal
|
9
|
+
from .base import BaseMemoryMixin
|
10
|
+
from .utils import print_state
|
11
|
+
from .visitor import ConditionalVisitor
|
12
|
+
|
13
|
+
log = logging.getLogger(__name__)
|
14
|
+
hinter = hinting.get_hinter(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class DivergentAddressSignal(AnalysisSignal):
|
18
|
+
"""Fault for communicating divergent address data between plugins.
|
19
|
+
|
20
|
+
Divergent address concretizations are detected
|
21
|
+
in the memory plugin, but can only be resolved
|
22
|
+
in the exploration strategy.
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(self, state, addr, results):
|
26
|
+
self.state = state
|
27
|
+
self.addr = addr
|
28
|
+
self.results = results
|
29
|
+
|
30
|
+
|
31
|
+
class DivergenceMemoryMixin(BaseMemoryMixin):
|
32
|
+
_visitor = ConditionalVisitor()
|
33
|
+
"""Mixin for handling memory-side address concretization
|
34
|
+
|
35
|
+
This hooks the actual address concretization operation,
|
36
|
+
and looks for attempts to concretize conditional addresses.
|
37
|
+
angr's default concretization is extremely imprecise
|
38
|
+
when applied to conditional expressions,
|
39
|
+
so hooking the operation gives us freedom to handle things differently.
|
40
|
+
|
41
|
+
The current strategy I like is to fork the execution state
|
42
|
+
into separate copies, one per possible evaluation of the conditional.
|
43
|
+
The actual fork operation cannot be done from within this plugin,
|
44
|
+
so it needs to communicate with DivergenceExplorationMixin.
|
45
|
+
"""
|
46
|
+
|
47
|
+
def __init__(self, **kwargs):
|
48
|
+
super().__init__(**kwargs)
|
49
|
+
|
50
|
+
@MemoryMixin.memo
|
51
|
+
def copy(self, memo):
|
52
|
+
o = super().copy(memo)
|
53
|
+
o._setup_tui()
|
54
|
+
return o
|
55
|
+
|
56
|
+
def _concretize_addr(self, supercall, addr, strategies=None, condition=None):
|
57
|
+
"""Helper function for performing conditional concretization.
|
58
|
+
|
59
|
+
This replaces the normal concretization strategy,
|
60
|
+
which is to compute all possible suggestions for addr,
|
61
|
+
and to assert the OR of them.
|
62
|
+
|
63
|
+
This default strategy falls over badly for addresses computed
|
64
|
+
via conditional data flow, since said addresses will be
|
65
|
+
essentially a case-statement of several possible base addresses.
|
66
|
+
The resulting assertion becomes "one of these variables stores one of these addresses."
|
67
|
+
This loses a lot of precision.
|
68
|
+
|
69
|
+
This implementation works as follows:
|
70
|
+
|
71
|
+
- Carve apart the address expression into all possible non-conditional expressions
|
72
|
+
- Concretize each possible expression independently.
|
73
|
+
- If there is more than one possible sub-expression, raise an exception.
|
74
|
+
- The exception acts as a virtual pagefault, which carries data back to the exploration strategy.
|
75
|
+
- The exploration strategy creates one state per sub-expression, constrained so that said expression is picked.
|
76
|
+
|
77
|
+
N.B.: This is in a separate helper because the operation is identical
|
78
|
+
for concretizing reads and writes.
|
79
|
+
"""
|
80
|
+
exprs = dict()
|
81
|
+
guards = dict()
|
82
|
+
res = set()
|
83
|
+
|
84
|
+
block = self.state.block()
|
85
|
+
(insn,) = list(
|
86
|
+
filter(
|
87
|
+
lambda x: x.address == self.state._ip.concrete_value,
|
88
|
+
block.disassembly.insns,
|
89
|
+
)
|
90
|
+
)
|
91
|
+
|
92
|
+
try:
|
93
|
+
# Split the address into non-conditional sub-expressions.
|
94
|
+
for expr, guard in self._visitor.visit(addr):
|
95
|
+
try:
|
96
|
+
# Concretize the sub-expression normally
|
97
|
+
for tmp in supercall(
|
98
|
+
expr, strategies=strategies, condition=condition
|
99
|
+
):
|
100
|
+
# check that the suggestion is satisfiable.
|
101
|
+
# I'd have hoped that angr does this automatically,
|
102
|
+
# but it doesn't seem to.
|
103
|
+
if self.state.satisfiable(extra_constraints=[addr == tmp]):
|
104
|
+
log.debug(
|
105
|
+
f"Concrete result for {expr}, slice of {addr}: {tmp:x} -> {self.state.memory.load(tmp, 8)}"
|
106
|
+
)
|
107
|
+
# TODO: Handle multiple concretizations from non-conditional source.
|
108
|
+
# I've never actually seen this, but you never know.
|
109
|
+
guards[expr] = guard
|
110
|
+
exprs[expr] = tmp
|
111
|
+
res.add(tmp)
|
112
|
+
else:
|
113
|
+
log.debug(
|
114
|
+
f"Ignoring result for {expr}, slice of {addr}: {tmp:x}"
|
115
|
+
)
|
116
|
+
for expr in self.state.solver.constraints:
|
117
|
+
log.debug(f"\t{expr}")
|
118
|
+
except angr.errors.SimMemoryAddressError as e:
|
119
|
+
# Something went wrong that shouldn't go wrong.
|
120
|
+
log.error(
|
121
|
+
f"Could not concretize expression {expr} with following constraints:"
|
122
|
+
)
|
123
|
+
for expr in self.state.solver.constraints:
|
124
|
+
log.error(f"\t{expr}")
|
125
|
+
log.error(f"Cause: {e}")
|
126
|
+
raise e
|
127
|
+
except AnalysisSignal as s:
|
128
|
+
# A lower analysis raised a signal.
|
129
|
+
# Raise it upwards.
|
130
|
+
raise s
|
131
|
+
except Exception as e:
|
132
|
+
# Something went wrong that shouldn't go wrong.
|
133
|
+
log.error(f"Fatal error concretizing {addr}")
|
134
|
+
log.exception(f"Cause: {type(e)} {e}")
|
135
|
+
raise e
|
136
|
+
log.debug("All recommendations:")
|
137
|
+
for expr, val in exprs.items():
|
138
|
+
log.debug(f" {expr} := {hex(val)}")
|
139
|
+
if len(exprs) > 1:
|
140
|
+
# We've got a conditional dereference.
|
141
|
+
hint = hinting.UnderSpecifiedMemoryBranchHint(
|
142
|
+
message="Conditional address dereference",
|
143
|
+
instruction=self.state._ip.concrete_value,
|
144
|
+
address=str(addr),
|
145
|
+
options=[(str(k), str(v)) for (k, v) in guards.items()],
|
146
|
+
)
|
147
|
+
hinter.info(hint)
|
148
|
+
options = {
|
149
|
+
"fork": self.divergence_fork,
|
150
|
+
"choose": self.divergence_choose,
|
151
|
+
"ignore": self.divergence_ignore,
|
152
|
+
"details": self.divergence_details,
|
153
|
+
"stop": self.divergence_stop,
|
154
|
+
"quit": self.divergence_quit,
|
155
|
+
}
|
156
|
+
option = "fork"
|
157
|
+
options[option](this=self, addr=addr, exprs=exprs)
|
158
|
+
|
159
|
+
return list(res)
|
160
|
+
|
161
|
+
def concretize_read_addr(self, addr, strategies=None, condition=None):
|
162
|
+
log.debug(f"Concretizing read addr {addr}")
|
163
|
+
return self._concretize_addr(
|
164
|
+
super().concretize_read_addr,
|
165
|
+
addr,
|
166
|
+
strategies=strategies,
|
167
|
+
condition=condition,
|
168
|
+
)
|
169
|
+
|
170
|
+
def concretize_write_addr(self, addr, strategies=None, condition=None):
|
171
|
+
log.debug(f"Concretizing write addr {addr}")
|
172
|
+
return self._concretize_addr(
|
173
|
+
super().concretize_write_addr,
|
174
|
+
addr,
|
175
|
+
strategies=strategies,
|
176
|
+
condition=condition,
|
177
|
+
)
|
178
|
+
|
179
|
+
# TUI handler methods. Fear the boilerplate.
|
180
|
+
def divergence_fork(self, addr=None, exprs=None, **kwargs):
|
181
|
+
log.warn("Rewinding and forking to avoid conditional dereference")
|
182
|
+
raise DivergentAddressSignal(self.state, addr, exprs)
|
183
|
+
|
184
|
+
def divergence_choose(**kwargs):
|
185
|
+
raise NotImplementedError(
|
186
|
+
"Chosing a specific evaluation is not yet implemented"
|
187
|
+
)
|
188
|
+
|
189
|
+
def divergence_ignore(self, **kwargs):
|
190
|
+
log.warn("Accepting conditional dereference")
|
191
|
+
|
192
|
+
def divergence_details(self, addr=None, exprs=None, **kwargs):
|
193
|
+
log.warn(f"Details of dereference at {self.state.ip}:")
|
194
|
+
print_state(log.warn, self.state, "conditional dereference")
|
195
|
+
log.warn(f"Address: {addr}")
|
196
|
+
log.warn("Possible Evaluations:")
|
197
|
+
for expr, result in exprs.items():
|
198
|
+
log.warn(f"\t{expr}: {result:x}")
|
199
|
+
|
200
|
+
def divergence_stop(self, **kwargs):
|
201
|
+
log.warn("Killing execution path")
|
202
|
+
raise PathTerminationSignal()
|
203
|
+
|
204
|
+
def divergence_quit(self, **kwargs):
|
205
|
+
log.warn("Aborting execution")
|
206
|
+
quit()
|
207
|
+
|
208
|
+
|
209
|
+
class DivergenceExplorationMixin:
|
210
|
+
"""Mixin for handling exploration-side address concretization
|
211
|
+
|
212
|
+
A memory mixin can detect problematic address concretizations,
|
213
|
+
but it can't do anything about them beyond modifying
|
214
|
+
a single state.
|
215
|
+
|
216
|
+
This exploration strategy mixin allows the memory mixin
|
217
|
+
to handle exceptional cases that need to fork a state.
|
218
|
+
"""
|
219
|
+
|
220
|
+
def step_state(self, simgr, state, **kwargs):
|
221
|
+
try:
|
222
|
+
out = super().step_state(simgr, state, **kwargs)
|
223
|
+
log.debug(f"Clean Successors: {state} -> {out}")
|
224
|
+
for stash, states in out.items():
|
225
|
+
if len(states) > 0:
|
226
|
+
if stash is None:
|
227
|
+
stash = "active"
|
228
|
+
for state in states:
|
229
|
+
log.debug(f"\t{state} ({stash}): {state.scratch.guard}")
|
230
|
+
|
231
|
+
except DivergentAddressSignal as e:
|
232
|
+
# Fault: we got a divergent address.
|
233
|
+
#
|
234
|
+
# This means that we tried to dereference a
|
235
|
+
# conditional symbolic expression.
|
236
|
+
# Normal address concretization causes a big loss of precision;
|
237
|
+
# binding a conditional expression doesn't clearly bind any
|
238
|
+
# one symbol.
|
239
|
+
#
|
240
|
+
# One way to handle this is to fork the state,
|
241
|
+
# with one state per possible evaluation of the
|
242
|
+
# conditional expression.
|
243
|
+
#
|
244
|
+
# The possible values and their concretizations
|
245
|
+
# are computed in the memory plugin,
|
246
|
+
# but it's not possible to fork a state in that plugin.
|
247
|
+
# It sends us its results via a DivergentAddressException,
|
248
|
+
# and we fix it up here.
|
249
|
+
|
250
|
+
# Set up the successors dict.
|
251
|
+
out = {None: list(), "unsat": list()}
|
252
|
+
log.debug(f"Divergent successors: {state} ->")
|
253
|
+
|
254
|
+
# Bind all address concretizations
|
255
|
+
backup = e.state.copy()
|
256
|
+
for expr, addr in e.results.items():
|
257
|
+
backup.solver.add(expr == addr)
|
258
|
+
|
259
|
+
# Fork a new state for each possible
|
260
|
+
# binding of the conditional address.
|
261
|
+
for expr, addr in e.results.items():
|
262
|
+
new_state = backup.copy()
|
263
|
+
new_state.solver.add(e.addr == addr)
|
264
|
+
|
265
|
+
# Test the new state for satisfiability,
|
266
|
+
# just in case the concretizer misses something.
|
267
|
+
# I wish I didn't have to do this, since it's slow.
|
268
|
+
if new_state.solver.satisfiable():
|
269
|
+
log.debug(f"\t{new_state} (active): {e.addr == addr}")
|
270
|
+
out[None].append(new_state)
|
271
|
+
else:
|
272
|
+
out["unsat"].append(new_state)
|
273
|
+
log.debug(f"\t{new_state} (unsat): {e.addr == addr}")
|
274
|
+
return out
|
@@ -0,0 +1,383 @@
|
|
1
|
+
import ctypes
|
2
|
+
import logging
|
3
|
+
|
4
|
+
import claripy
|
5
|
+
from angr.storage.memory_mixins.memory_mixin import MemoryMixin
|
6
|
+
|
7
|
+
from .... import hinting
|
8
|
+
from ....emulators.angr import PathTerminationSignal
|
9
|
+
from ....emulators.angr.utils import reg_name_from_offset
|
10
|
+
from ....extern.ctypes import TypedPointer
|
11
|
+
from .base import BaseMemoryMixin
|
12
|
+
from .visitor import EvalVisitor
|
13
|
+
|
14
|
+
log = logging.getLogger(__name__)
|
15
|
+
hinter = hinting.get_hinter(__name__)
|
16
|
+
visitor = EvalVisitor()
|
17
|
+
|
18
|
+
|
19
|
+
class ModelMemoryMixin(BaseMemoryMixin):
|
20
|
+
"""
|
21
|
+
Mixin for detecting model violations in memory ops.
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __init__(self, **kwargs):
|
25
|
+
super().__init__(**kwargs)
|
26
|
+
self._setup_tui()
|
27
|
+
|
28
|
+
@MemoryMixin.memo
|
29
|
+
def copy(self, memo):
|
30
|
+
o = super().copy(memo)
|
31
|
+
o._setup_tui()
|
32
|
+
return o
|
33
|
+
|
34
|
+
def _handle_typed_value(self, typedef, default, pretty):
|
35
|
+
"""
|
36
|
+
Process user decision for a typed variable without a value
|
37
|
+
|
38
|
+
Pointer types allow the following options:
|
39
|
+
|
40
|
+
- Allocate a new region on the heap, and point to it.
|
41
|
+
- Ase a reference to a new heap region
|
42
|
+
|
43
|
+
All types allow the following options:
|
44
|
+
|
45
|
+
- Use a placeholder symbol to track this value
|
46
|
+
- Use a user-provided integer as a concrete value (not recommended for multi-KB structs)
|
47
|
+
- Use zero/NULL as a concrete value
|
48
|
+
- Use whatever default angr suggests
|
49
|
+
- Print details of the current situation
|
50
|
+
- Abort this execution path
|
51
|
+
- Abort all execution
|
52
|
+
"""
|
53
|
+
|
54
|
+
options = {
|
55
|
+
"placeholder": self.typed_value_placeholder,
|
56
|
+
"const": self.typed_value_const,
|
57
|
+
"null": self.typed_value_null,
|
58
|
+
"ignore": self.typed_value_ignore,
|
59
|
+
"stop": self.typed_value_stop,
|
60
|
+
"quit": self.typed_value_quit,
|
61
|
+
}
|
62
|
+
|
63
|
+
if issubclass(typedef, TypedPointer):
|
64
|
+
option = "alloc"
|
65
|
+
options.update(
|
66
|
+
{
|
67
|
+
"alloc": self.typed_value_alloc,
|
68
|
+
"reuse": self.typed_value_reuse,
|
69
|
+
}
|
70
|
+
)
|
71
|
+
else:
|
72
|
+
option = "placeholder"
|
73
|
+
return options[option](typedef=typedef, default=default, pretty=pretty)
|
74
|
+
|
75
|
+
def _handle_typed_symbol(self, typedef, symbol, supercall, strategies, condition):
|
76
|
+
# Symbols need special handling;
|
77
|
+
options = {
|
78
|
+
"default": self.typed_symbol_default,
|
79
|
+
"const": self.typed_value_const,
|
80
|
+
"null": self.typed_value_null,
|
81
|
+
"stop": self.typed_value_stop,
|
82
|
+
"quit": self.typed_value_quit,
|
83
|
+
}
|
84
|
+
|
85
|
+
if issubclass(typedef, TypedPointer):
|
86
|
+
option = "alloc"
|
87
|
+
options.update(
|
88
|
+
{
|
89
|
+
"alloc": self.typed_value_alloc,
|
90
|
+
"reuse": self.typed_value_reuse,
|
91
|
+
}
|
92
|
+
)
|
93
|
+
else:
|
94
|
+
option = "default"
|
95
|
+
return options[option](
|
96
|
+
typedef=typedef,
|
97
|
+
symbol=symbol,
|
98
|
+
supercall=supercall,
|
99
|
+
strategies=strategies,
|
100
|
+
condition=condition,
|
101
|
+
)
|
102
|
+
|
103
|
+
def _handle_untyped_register(self, reg_name, default):
|
104
|
+
options = {
|
105
|
+
"ignore": self.untyped_value_ignore,
|
106
|
+
"bind": self.untyped_addr_bind,
|
107
|
+
"stop": self.untyped_value_stop,
|
108
|
+
"quit": self.untyped_value_quit,
|
109
|
+
}
|
110
|
+
option = "ignore"
|
111
|
+
return options[option](reg_name=reg_name, default=default)
|
112
|
+
|
113
|
+
def _handle_untyped_address(self, addr, default):
|
114
|
+
options = {
|
115
|
+
"ignore": self.untyped_value_ignore,
|
116
|
+
"bind": self.untyped_addr_bind,
|
117
|
+
"stop": self.untyped_value_stop,
|
118
|
+
"quit": self.untyped_value_quit,
|
119
|
+
}
|
120
|
+
option = "ignore"
|
121
|
+
return options[option](addr=addr, default=default)
|
122
|
+
|
123
|
+
def _handle_untyped_symbol(self, sym_name, default):
|
124
|
+
options = {
|
125
|
+
"ignore": self.untyped_value_ignore,
|
126
|
+
"bind": self.untyped_sym_bind,
|
127
|
+
"stop": self.untyped_value_stop,
|
128
|
+
"quit": self.untyped_value_quit,
|
129
|
+
}
|
130
|
+
option = "ignore"
|
131
|
+
return options[option](sym_name=sym_name, default=default)
|
132
|
+
|
133
|
+
def _default_value(self, addr, size, **kwargs):
|
134
|
+
environ = self.state.typedefs
|
135
|
+
res = super()._default_value(addr, size, **kwargs)
|
136
|
+
|
137
|
+
block = self.state.block()
|
138
|
+
(insn,) = list(
|
139
|
+
filter(
|
140
|
+
lambda x: x.address == self.state._ip.concrete_value,
|
141
|
+
block.disassembly.insns,
|
142
|
+
)
|
143
|
+
)
|
144
|
+
if self.id == "reg":
|
145
|
+
# Thanks to Pcode, this becomes much more complicated.
|
146
|
+
# angr can't tell the difference between a read from a sub-register,
|
147
|
+
# and read from a register truncated as part of the opcode's sleigh definition.
|
148
|
+
# This makes it halucinate registers that don't actually exist.
|
149
|
+
|
150
|
+
reg_name = reg_name_from_offset(self.state.arch, addr, size)
|
151
|
+
reg_def = environ.get_register_binding(reg_name)
|
152
|
+
if reg_def is not None:
|
153
|
+
res = self._handle_typed_value(reg_def, res, reg_name)
|
154
|
+
hint = hinting.TypedUnderSpecifiedRegisterHint(
|
155
|
+
message="Register has type, but no value",
|
156
|
+
typedef=str(reg_def),
|
157
|
+
register=reg_name,
|
158
|
+
instruction=self.state._ip.concrete_value,
|
159
|
+
value=str(res),
|
160
|
+
)
|
161
|
+
hinter.info(hint)
|
162
|
+
else:
|
163
|
+
res = self._handle_untyped_register(reg_name, res)
|
164
|
+
hint = hinting.UntypedUnderSpecifiedRegisterHint(
|
165
|
+
message="Register has no type or value",
|
166
|
+
register=reg_name,
|
167
|
+
instruction=self.state._ip.concrete_value,
|
168
|
+
value=str(res),
|
169
|
+
)
|
170
|
+
hinter.info(hint)
|
171
|
+
if isinstance(res, int):
|
172
|
+
res = self.state.solver.BVV(res, size * 8)
|
173
|
+
else:
|
174
|
+
addr_def = environ.get_address_binding(addr)
|
175
|
+
if addr_def is not None:
|
176
|
+
res = self._handle_typed_value(addr_def, res, addr)
|
177
|
+
hint = hinting.TypedUnderSpecifiedMemoryHint(
|
178
|
+
message="Memory has type, but no value",
|
179
|
+
typedef=str(addr_def),
|
180
|
+
address=addr,
|
181
|
+
size=size,
|
182
|
+
instruction=self.state._ip.concrete_value,
|
183
|
+
value=str(res),
|
184
|
+
)
|
185
|
+
hinter.info(hint)
|
186
|
+
else:
|
187
|
+
hint = hinting.UntypedUnderSpecifiedMemoryHint(
|
188
|
+
message="Memory has no type or value",
|
189
|
+
address=addr,
|
190
|
+
size=size,
|
191
|
+
instruction=self.state._ip.concrete_value,
|
192
|
+
value=str(res),
|
193
|
+
)
|
194
|
+
hinter.info(hint)
|
195
|
+
self._handle_untyped_address(addr, res)
|
196
|
+
|
197
|
+
return res
|
198
|
+
|
199
|
+
def _concretize_bindings(self, supercall, addr, strategies, condition):
|
200
|
+
"""
|
201
|
+
Concretization strategy for interrogating model
|
202
|
+
|
203
|
+
This is the same code for both reads and writes.
|
204
|
+
"""
|
205
|
+
environ = self.state.typedefs
|
206
|
+
block = self.state.block()
|
207
|
+
(insn,) = list(
|
208
|
+
filter(
|
209
|
+
lambda x: x.address == self.state._ip.concrete_value,
|
210
|
+
block.disassembly.insns,
|
211
|
+
)
|
212
|
+
)
|
213
|
+
|
214
|
+
log.debug(f"\tAddr: {addr}")
|
215
|
+
bindings = dict()
|
216
|
+
complete = True
|
217
|
+
for v in filter(lambda x: x.op == "BVS", addr.leaf_asts()):
|
218
|
+
value = environ.get_symbol(v.args[0])
|
219
|
+
if value is None:
|
220
|
+
binding = environ.get_symbol_binding(v.args[0])
|
221
|
+
if binding is None:
|
222
|
+
log.warn(
|
223
|
+
f"Symbol {v} (part of address expression {addr}) has no type"
|
224
|
+
)
|
225
|
+
value = self._handle_untyped_symbol(v, None)
|
226
|
+
if value is None:
|
227
|
+
complete = False
|
228
|
+
continue
|
229
|
+
hint = hinting.TypedUnderSpecifiedAddressHint(
|
230
|
+
message="Symbol has no type",
|
231
|
+
symbol=v.args[0],
|
232
|
+
addr=str(addr),
|
233
|
+
instruction=self.state._ip.concrete_value,
|
234
|
+
value=str(value),
|
235
|
+
)
|
236
|
+
else:
|
237
|
+
log.warn(
|
238
|
+
f"Symbol {v} (part of address expression {addr}) has type {binding}, but no value"
|
239
|
+
)
|
240
|
+
while True:
|
241
|
+
value = self._handle_typed_symbol(
|
242
|
+
binding, v, supercall, strategies, condition
|
243
|
+
)
|
244
|
+
if value is not None and not self.state.solver.satisfiable(
|
245
|
+
extra_constraints=[v == value]
|
246
|
+
):
|
247
|
+
log.warn(f"Selection {value:x} is not valid")
|
248
|
+
self._untyped_value_stop()
|
249
|
+
break
|
250
|
+
hint = hinting.TypedUnderSpecifiedAddressHint(
|
251
|
+
message="Symbol has type, but no value",
|
252
|
+
typedef=str(binding),
|
253
|
+
symbol=v.args[0],
|
254
|
+
addr=str(addr),
|
255
|
+
instruction=self.state._ip.concrete_value,
|
256
|
+
value=str(value),
|
257
|
+
)
|
258
|
+
hinter.info(hint)
|
259
|
+
environ.set_symbol(v.args[0], value)
|
260
|
+
if isinstance(value, int):
|
261
|
+
pretty_value = hex(value)
|
262
|
+
else:
|
263
|
+
log.error(f"Bad value for {v.args[0]}: {value} ({type(value)})")
|
264
|
+
raise NotImplementedError()
|
265
|
+
quit(1)
|
266
|
+
log.debug(f"\tNew Value for {v.args[0]}: {pretty_value}")
|
267
|
+
else:
|
268
|
+
log.debug(f"\tExisting Value for {v.args[0]}: {value:x}")
|
269
|
+
bindings[v.args[0]] = claripy.BVV(value, 64)
|
270
|
+
if complete:
|
271
|
+
# We have bindings for all symbols.
|
272
|
+
# These must be _concrete_ bindings.
|
273
|
+
value = visitor.visit(addr, bindings=bindings)
|
274
|
+
if not isinstance(value, int):
|
275
|
+
value = value.concrete_value
|
276
|
+
res = [value]
|
277
|
+
else:
|
278
|
+
log.warn(
|
279
|
+
f"Address expression {addr} not completely concretized; falling back"
|
280
|
+
)
|
281
|
+
res = supercall(addr, strategies=strategies, condition=condition)
|
282
|
+
log.debug("\tResults:")
|
283
|
+
for v in res:
|
284
|
+
if isinstance(v, int):
|
285
|
+
log.debug(f"\t\t{v:x}")
|
286
|
+
else:
|
287
|
+
log.debug(f"\t\t{v}")
|
288
|
+
return res
|
289
|
+
|
290
|
+
def concretize_read_addr(self, addr, strategies=None, condition=None):
|
291
|
+
log.debug("Concretizing on read:")
|
292
|
+
return self._concretize_bindings(
|
293
|
+
super().concretize_read_addr, addr, strategies, condition
|
294
|
+
)
|
295
|
+
|
296
|
+
def concretize_write_addr(self, addr, strategies=None, condition=None):
|
297
|
+
log.debug("Concretizing on write:")
|
298
|
+
return self._concretize_bindings(
|
299
|
+
super().concretize_write_addr, addr, strategies, condition
|
300
|
+
)
|
301
|
+
|
302
|
+
# TUI handler functions. Fear the boilerplate.
|
303
|
+
|
304
|
+
def typed_value_alloc(self, typedef=None, **kwargs):
|
305
|
+
# User wants to allocate a new instance
|
306
|
+
environ = self.state.typedefs
|
307
|
+
res = environ.allocate(typedef.type)
|
308
|
+
log.warn(f"Allocated {ctypes.sizeof(typedef.type)} bytes at {res:x}")
|
309
|
+
return res
|
310
|
+
|
311
|
+
def typed_value_reuse(self, typedef=None, **kwargs):
|
312
|
+
raise NotImplementedError("Reuse requires human input.")
|
313
|
+
|
314
|
+
def typed_value_placeholder(self, typedef=None, **kwargs):
|
315
|
+
# User wants to assign a placeholder symbol, not a real value.
|
316
|
+
if hasattr(typedef, "__fields__"):
|
317
|
+
log.info(f"{typedef} is struct-like")
|
318
|
+
raise NotImplementedError(f"Not handling {typedef}")
|
319
|
+
else:
|
320
|
+
res = claripy.BVS(f"{typedef.__name__}", ctypes.sizeof(typedef) * 8)
|
321
|
+
self.state.typedefs.bind_symbol(res, typedef)
|
322
|
+
|
323
|
+
log.warn(f"Assigned placeholder {res}")
|
324
|
+
return res
|
325
|
+
|
326
|
+
def typed_value_const(self, **kwargs):
|
327
|
+
# User wants to provide their own value
|
328
|
+
res = input("( hex ) > ")
|
329
|
+
res = int(res, 16)
|
330
|
+
log.warn(f"Using user-provided value {res:x}")
|
331
|
+
return res
|
332
|
+
|
333
|
+
def typed_value_null(self, **kwargs):
|
334
|
+
log.warn("Using NULL")
|
335
|
+
return 0
|
336
|
+
|
337
|
+
def typed_value_ignore(self, default=None, **kwargs):
|
338
|
+
if default is None:
|
339
|
+
log.warn("Will use default when determined.")
|
340
|
+
elif isinstance(default, int):
|
341
|
+
log.warn(f"Using default {default:x}")
|
342
|
+
else:
|
343
|
+
log.warn(f"Using default {default}")
|
344
|
+
return default
|
345
|
+
|
346
|
+
def typed_symbol_default(self, symbol, supercall, strategies, condition, **kwargs):
|
347
|
+
res = supercall(symbol, strategies=strategies, condition=condition)[0]
|
348
|
+
log.warn(f"Concretizing {symbol} to {res:x}")
|
349
|
+
return res
|
350
|
+
|
351
|
+
def typed_value_stop(self, **kwargs):
|
352
|
+
log.warn("Stopping execution path")
|
353
|
+
raise PathTerminationSignal()
|
354
|
+
|
355
|
+
def typed_value_quit(self, **kwargs):
|
356
|
+
log.warn("Aborting execution")
|
357
|
+
quit()
|
358
|
+
|
359
|
+
def untyped_value_ignore(self, default=None, **kwargs):
|
360
|
+
if default is None:
|
361
|
+
log.warn("Will use default when generated")
|
362
|
+
elif isinstance(default, int):
|
363
|
+
log.warn(f"Using default value {default:x}")
|
364
|
+
else:
|
365
|
+
log.warn(f"Using default value {default}")
|
366
|
+
return default
|
367
|
+
|
368
|
+
def untyped_reg_bind(**kwargs):
|
369
|
+
raise NotImplementedError("Type binding not implemented for registers")
|
370
|
+
|
371
|
+
def untyped_addr_bind(**kwargs):
|
372
|
+
raise NotImplementedError("Type binding not implemented for addresses")
|
373
|
+
|
374
|
+
def untyped_sym_bind(**kwargs):
|
375
|
+
raise NotImplementedError("Type binding not implemented for symbols")
|
376
|
+
|
377
|
+
def untyped_value_stop(self, **kwargs):
|
378
|
+
self.warn("Stopping self execution path")
|
379
|
+
raise PathTerminationSignal()
|
380
|
+
|
381
|
+
def untyped_value_quit(self, **kwargs):
|
382
|
+
log.warn("Aborting execution")
|
383
|
+
quit()
|