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,133 @@
|
|
1
|
+
import dataclasses
|
2
|
+
import typing
|
3
|
+
|
4
|
+
from ... import hinting
|
5
|
+
|
6
|
+
|
7
|
+
class ClaripySerializable(hinting.Serializable):
|
8
|
+
"""Serializable wrapper that allows serializing claripy expressions in hints."""
|
9
|
+
|
10
|
+
@classmethod
|
11
|
+
def claripy_to_dict(cls, expr):
|
12
|
+
if (
|
13
|
+
expr is None
|
14
|
+
or isinstance(expr, int)
|
15
|
+
or isinstance(expr, str)
|
16
|
+
or isinstance(expr, bool)
|
17
|
+
):
|
18
|
+
return expr
|
19
|
+
else:
|
20
|
+
return {"op": expr.op, "args": list(map(cls.claripy_to_dict, expr.args))}
|
21
|
+
|
22
|
+
def __init__(self, expr):
|
23
|
+
self.expr = expr
|
24
|
+
|
25
|
+
def to_dict(self):
|
26
|
+
return self.claripy_to_dict(self.expr)
|
27
|
+
|
28
|
+
@classmethod
|
29
|
+
def from_dict(cls, dict):
|
30
|
+
raise NotImplementedError(
|
31
|
+
"Rebuilding clairpy is a lot harder than tearing it apart"
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
@dataclasses.dataclass(frozen=True)
|
36
|
+
class FieldHint(hinting.Hint):
|
37
|
+
address: int = 0
|
38
|
+
size: int = 0
|
39
|
+
|
40
|
+
def pp(self, out):
|
41
|
+
out(self.message)
|
42
|
+
out(f" address: {hex(self.address)}")
|
43
|
+
out(f" size: {self.size}")
|
44
|
+
|
45
|
+
|
46
|
+
@dataclasses.dataclass(frozen=True)
|
47
|
+
class TrackedFieldHint(FieldHint):
|
48
|
+
label: str = ""
|
49
|
+
|
50
|
+
def pp(self, out):
|
51
|
+
super().pp(out)
|
52
|
+
out(f" label: {self.label}")
|
53
|
+
|
54
|
+
|
55
|
+
@dataclasses.dataclass(frozen=True)
|
56
|
+
class FieldEventHint(FieldHint):
|
57
|
+
"""Hint representing a detected, unhandled field"""
|
58
|
+
|
59
|
+
pc: int = 0
|
60
|
+
guards: typing.List[typing.Tuple[int, ClaripySerializable]] = dataclasses.field(
|
61
|
+
default_factory=list
|
62
|
+
)
|
63
|
+
access: str = ""
|
64
|
+
|
65
|
+
def pp(self, out):
|
66
|
+
super().pp(out)
|
67
|
+
out(f" pc: {hex(self.pc)}")
|
68
|
+
out(f" access: {self.access}")
|
69
|
+
out(" guards:")
|
70
|
+
for pc, guard in self.guards:
|
71
|
+
out(
|
72
|
+
f" {hex(pc)}: {guard.expr.shallow_repr()} [{list(filter(lambda x: x.op != 'BVV', guard.expr.leaf_asts()))}]"
|
73
|
+
)
|
74
|
+
|
75
|
+
|
76
|
+
@dataclasses.dataclass(frozen=True)
|
77
|
+
class UnknownFieldHint(FieldEventHint):
|
78
|
+
"""Hint representing an access of an unknown field.
|
79
|
+
|
80
|
+
The expression loaded from memory is not
|
81
|
+
a simple slice of any known label.
|
82
|
+
"""
|
83
|
+
|
84
|
+
message: str = "Accessed unknown field" # Don't need to replace
|
85
|
+
expr: str = ""
|
86
|
+
|
87
|
+
def pp(self, out):
|
88
|
+
super().pp(out)
|
89
|
+
out(f" expr: {self.expr}")
|
90
|
+
|
91
|
+
|
92
|
+
@dataclasses.dataclass(frozen=True)
|
93
|
+
class PartialByteFieldAccessHint(FieldEventHint):
|
94
|
+
"""Hint representing an access to part of a known field."""
|
95
|
+
|
96
|
+
message: str = "Partial access to known field" # Don't need to replace
|
97
|
+
label: str = ""
|
98
|
+
start: int = 0
|
99
|
+
end: int = 0
|
100
|
+
|
101
|
+
def pp(self, out):
|
102
|
+
super().pp(out)
|
103
|
+
out(f" field: {self.label}[{self.start}:{self.end}]")
|
104
|
+
|
105
|
+
|
106
|
+
@dataclasses.dataclass(frozen=True)
|
107
|
+
class PartialByteFieldWriteHint(PartialByteFieldAccessHint):
|
108
|
+
"""Hint representing a write to part of a known field.
|
109
|
+
|
110
|
+
Also includes the data I'm trying to write
|
111
|
+
"""
|
112
|
+
|
113
|
+
message: str = "Partial write to known field" # Don't need to replace
|
114
|
+
access: str = "write" # Don't need to replace
|
115
|
+
expr: str = ""
|
116
|
+
|
117
|
+
def pp(self, out):
|
118
|
+
super().pp(out)
|
119
|
+
out(f" expr: {self.expr}")
|
120
|
+
|
121
|
+
|
122
|
+
@dataclasses.dataclass(frozen=True)
|
123
|
+
class PartialBitFieldAccessHint(FieldEventHint):
|
124
|
+
"""Hint representing an access to a bit field within a known field."""
|
125
|
+
|
126
|
+
message: str = "Bit field access in known field" # Don't need to replace
|
127
|
+
label: str = ""
|
128
|
+
start: int = 0
|
129
|
+
end: int = 0
|
130
|
+
|
131
|
+
def pp(self, out):
|
132
|
+
super().pp(out)
|
133
|
+
out(f" field: {self.label}[{self.start}:{self.end}]")
|
@@ -0,0 +1,211 @@
|
|
1
|
+
import logging
|
2
|
+
import typing
|
3
|
+
|
4
|
+
import claripy
|
5
|
+
|
6
|
+
from ... import emulators, exceptions, platforms, state
|
7
|
+
from ...emulators.angr.exceptions import PathTerminationSignal
|
8
|
+
|
9
|
+
log = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
class MallocModel(state.models.Model):
|
13
|
+
name = "malloc"
|
14
|
+
platform = platforms.Platform(
|
15
|
+
platforms.Architecture.X86_64, platforms.Byteorder.LITTLE
|
16
|
+
)
|
17
|
+
abi = platforms.ABI.SYSTEMV
|
18
|
+
|
19
|
+
_arg1_for_arch = {
|
20
|
+
platforms.Architecture.AARCH64: "x0",
|
21
|
+
platforms.Architecture.ARM_V5T: "r0",
|
22
|
+
platforms.Architecture.ARM_V7A: "r0",
|
23
|
+
platforms.Architecture.MIPS32: "a0",
|
24
|
+
platforms.Architecture.MIPS64: "a0",
|
25
|
+
platforms.Architecture.POWERPC32: "r3",
|
26
|
+
platforms.Architecture.X86_64: "rdi",
|
27
|
+
}
|
28
|
+
|
29
|
+
_ret_for_arch = {
|
30
|
+
platforms.Architecture.AARCH64: "x0",
|
31
|
+
platforms.Architecture.ARM_V5T: "r0",
|
32
|
+
platforms.Architecture.ARM_V7A: "r0",
|
33
|
+
platforms.Architecture.MIPS32: "v0",
|
34
|
+
platforms.Architecture.MIPS64: "v0",
|
35
|
+
platforms.Architecture.POWERPC32: "r3",
|
36
|
+
platforms.Architecture.X86_64: "rax",
|
37
|
+
}
|
38
|
+
|
39
|
+
def __init__(
|
40
|
+
self,
|
41
|
+
address: int,
|
42
|
+
heap: state.memory.heap.Heap,
|
43
|
+
platform: platforms.Platform,
|
44
|
+
read_callback,
|
45
|
+
write_callback,
|
46
|
+
):
|
47
|
+
super().__init__(address)
|
48
|
+
|
49
|
+
self.arg1_reg = self._arg1_for_arch[platform.architecture]
|
50
|
+
self.ret_reg = self._ret_for_arch[platform.architecture]
|
51
|
+
|
52
|
+
if len(heap) > 0:
|
53
|
+
raise exceptions.ConfigurationError("This only works with a blank heap")
|
54
|
+
end = state.IntegerValue(heap.address + heap.get_capacity(), 8, None)
|
55
|
+
ptr = state.IntegerValue(heap.address + 16, 8, None)
|
56
|
+
|
57
|
+
heap.allocate(end)
|
58
|
+
heap.allocate(ptr)
|
59
|
+
|
60
|
+
self.heap_addr = heap.address
|
61
|
+
self.read_callback = read_callback
|
62
|
+
self.write_callback = write_callback
|
63
|
+
|
64
|
+
self.struct_lengths: typing.Dict[str, int] = dict()
|
65
|
+
self.struct_prefixes: typing.Dict[str, str] = dict()
|
66
|
+
self.struct_fields: typing.Dict[
|
67
|
+
str, typing.List[typing.Tuple[int, str]]
|
68
|
+
] = dict()
|
69
|
+
|
70
|
+
def bind_length_to_struct(
|
71
|
+
self, field: str, prefix: str, labels: typing.List[typing.Tuple[int, str]]
|
72
|
+
):
|
73
|
+
self.struct_lengths[field] = 0
|
74
|
+
self.struct_prefixes[field] = prefix
|
75
|
+
for size, label in labels:
|
76
|
+
self.struct_lengths[field] += size
|
77
|
+
self.struct_fields[field] = labels
|
78
|
+
|
79
|
+
def model(self, emulator: emulators.Emulator):
|
80
|
+
if not isinstance(emulator, emulators.AngrEmulator):
|
81
|
+
raise exceptions.ConfigurationError("Model only works with angr")
|
82
|
+
# Read the capacity.
|
83
|
+
# Bypass the smallworld API; I want to know if it's symbolic
|
84
|
+
fda = emulator.get_extension("fda")
|
85
|
+
capacity = emulator.read_register_symbolic(self.arg1_reg)
|
86
|
+
length = 0
|
87
|
+
|
88
|
+
if capacity.symbolic:
|
89
|
+
# Capacity is a symbolic expression.
|
90
|
+
# Check if it's collapsible
|
91
|
+
good = True
|
92
|
+
try:
|
93
|
+
vals = emulator.eval_atmost(capacity, 1)
|
94
|
+
except exceptions.UnsatError:
|
95
|
+
log.error("Capacity is unsat")
|
96
|
+
good = False
|
97
|
+
except exceptions.SymbolicValueError:
|
98
|
+
log.error("Capacity did not collapse to one value")
|
99
|
+
good = False
|
100
|
+
|
101
|
+
if not good or len(vals) > 1:
|
102
|
+
log.error("The following fields are lengths:")
|
103
|
+
for var in capacity.variables:
|
104
|
+
log.error(f" {var}")
|
105
|
+
log.error("Concretize them to continue analysis")
|
106
|
+
raise PathTerminationSignal()
|
107
|
+
|
108
|
+
length = vals[0]
|
109
|
+
else:
|
110
|
+
length = capacity.concrete_value
|
111
|
+
|
112
|
+
heap_end = int.from_bytes(
|
113
|
+
emulator.read_memory_content(self.heap_addr, 8), "little"
|
114
|
+
)
|
115
|
+
heap_ptr = int.from_bytes(
|
116
|
+
emulator.read_memory_content(self.heap_addr + 8, 8), "little"
|
117
|
+
)
|
118
|
+
|
119
|
+
if heap_end - heap_ptr < length:
|
120
|
+
# OOM; Return NULL
|
121
|
+
log.warning(
|
122
|
+
f"malloc: OOM! Need {length}, only have {hex(heap_end)} - {hex(heap_ptr)} = {heap_end - heap_ptr}"
|
123
|
+
)
|
124
|
+
res = 0
|
125
|
+
else:
|
126
|
+
# Return the pointer to the new region.
|
127
|
+
log.warning(f"malloc: Alloc'd {length} bytes at {hex(heap_ptr)}")
|
128
|
+
res = heap_ptr
|
129
|
+
heap_ptr += length
|
130
|
+
emulator.write_memory_content(
|
131
|
+
self.heap_addr + 8, heap_ptr.to_bytes(8, "little")
|
132
|
+
)
|
133
|
+
|
134
|
+
# Check if this is something we want to track.
|
135
|
+
fields = list(map(lambda x: x.split("_")[0], capacity.variables))
|
136
|
+
|
137
|
+
if len(fields) != 1:
|
138
|
+
# Don't track; too complex
|
139
|
+
log.warn(" Length has multiple variables; not solving")
|
140
|
+
elif fields[0] not in self.struct_lengths:
|
141
|
+
# Don't track; not something we asked to track
|
142
|
+
log.warn(f" Length field {fields[0]} not known")
|
143
|
+
else:
|
144
|
+
struct_length = self.struct_lengths[fields[0]]
|
145
|
+
struct_prefix = self.struct_prefixes[fields[0]]
|
146
|
+
struct_labels = self.struct_fields[fields[0]]
|
147
|
+
|
148
|
+
sym = list(filter(lambda x: x.op == "BVS", capacity.leaf_asts()))[0]
|
149
|
+
if not isinstance(sym, claripy.ast.bv.BV):
|
150
|
+
raise TypeError("BVS isn't a bitvector: {sym}")
|
151
|
+
n = emulator.eval_atmost(sym, 1)[0]
|
152
|
+
|
153
|
+
if length // n != struct_length:
|
154
|
+
# Don't track; not an obvious "n * sizeof(struct foo)"
|
155
|
+
log.warn(f" {length} // {n} != {struct_length}")
|
156
|
+
elif length % struct_length != 0:
|
157
|
+
# Don't track; not an obvious "n * sizeof(struct foo)"
|
158
|
+
log.warn(f" {length} not evenly divisible by {n}")
|
159
|
+
else:
|
160
|
+
# Track!
|
161
|
+
log.warn(f" Alloc of {n} items")
|
162
|
+
addr = res
|
163
|
+
for i in range(0, n):
|
164
|
+
struct_fields = list()
|
165
|
+
off = 0
|
166
|
+
for flen, fname in struct_labels:
|
167
|
+
# Build a symbol for this field of this item
|
168
|
+
flabel = struct_prefix + "." + str(i) + "." + fname
|
169
|
+
expr = claripy.BVS(flabel, flen * 8, explicit_name=True)
|
170
|
+
struct_fields.append(expr)
|
171
|
+
|
172
|
+
# Track the new field
|
173
|
+
r = range(addr + off, addr + off + flen)
|
174
|
+
fda.fda_labels.add(flabel)
|
175
|
+
fda.fda_addr_to_label[r] = flabel
|
176
|
+
fda.fda_label_to_addr[flabel] = r
|
177
|
+
fda.fda_bindings[flabel] = expr
|
178
|
+
|
179
|
+
off += flen
|
180
|
+
|
181
|
+
# Insert the whole struct into memory
|
182
|
+
expr = claripy.Concat(*struct_fields)
|
183
|
+
emulator.write_memory(addr, expr)
|
184
|
+
|
185
|
+
addr += struct_length
|
186
|
+
|
187
|
+
# Hook the entire array
|
188
|
+
fda.fda_mem_ranges.add((res, res + length))
|
189
|
+
emulator.hook_memory_read_symbolic(
|
190
|
+
res, res + length, self.read_callback
|
191
|
+
)
|
192
|
+
emulator.hook_memory_write_symbolic(
|
193
|
+
res, res + length, self.write_callback
|
194
|
+
)
|
195
|
+
|
196
|
+
emulator.write_register_content(self.ret_reg, res)
|
197
|
+
|
198
|
+
|
199
|
+
class FreeModel(state.models.Model):
|
200
|
+
name = "free"
|
201
|
+
platform = platforms.Platform(
|
202
|
+
platforms.Architecture.X86_64, platforms.Byteorder.LITTLE
|
203
|
+
)
|
204
|
+
abi = platforms.ABI.SYSTEMV
|
205
|
+
|
206
|
+
def __init__(self, address: int):
|
207
|
+
super().__init__(address)
|
208
|
+
|
209
|
+
def model(self, emulator: emulators.Emulator):
|
210
|
+
# Just free it.
|
211
|
+
return
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import logging
|
2
|
+
import typing
|
3
|
+
|
4
|
+
from ...emulators import AngrEmulator
|
5
|
+
from ...exceptions import EmulationStop
|
6
|
+
from ...platforms import Platform
|
7
|
+
from ...state import Machine
|
8
|
+
from ..underlays import AnalysisUnderlay
|
9
|
+
|
10
|
+
log = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
class ForcedExecutionUnderlay(AnalysisUnderlay):
|
14
|
+
"""Forced execution analysis underlay
|
15
|
+
|
16
|
+
This allows you to emulate arbitrary program slices
|
17
|
+
by forcing the emulator to visit specific instructions,
|
18
|
+
ignoring the normal program control flow.
|
19
|
+
|
20
|
+
This isn't a complete analysis;
|
21
|
+
instead, it's a dummy base class that makes it easy
|
22
|
+
to support forced execution mode by implementing
|
23
|
+
the complex logic as an overlay,
|
24
|
+
and building the actual analysis as a combination
|
25
|
+
of overlay and underlay.
|
26
|
+
See field detection to see what I mean.
|
27
|
+
|
28
|
+
NOTE: This is not compatible with all architectures.
|
29
|
+
The architecture needs to support single-stepping;
|
30
|
+
delay slot architectures such as MIPS can't
|
31
|
+
be single-stepped by angr.
|
32
|
+
|
33
|
+
Arguments:
|
34
|
+
trace: The list of program counter addresses you want to visit
|
35
|
+
|
36
|
+
"""
|
37
|
+
|
38
|
+
def __init__(self, trace: typing.List[typing.Dict[str, int]]):
|
39
|
+
self.trace: typing.List[typing.Dict[str, int]] = trace
|
40
|
+
|
41
|
+
def execute(self):
|
42
|
+
try:
|
43
|
+
for regs in self.trace:
|
44
|
+
for reg, val in regs.items():
|
45
|
+
self.emulator.write_register_content(reg, val)
|
46
|
+
self.emulator.step()
|
47
|
+
except EmulationStop:
|
48
|
+
pass
|
49
|
+
|
50
|
+
|
51
|
+
class ForcedExecution(ForcedExecutionUnderlay):
|
52
|
+
"""Forced execution using angr
|
53
|
+
|
54
|
+
This allows you to emulate arbitrary program slices
|
55
|
+
by forcing the emulator to visit specific instructions,
|
56
|
+
ignoring the normal program control flow.
|
57
|
+
|
58
|
+
NOTE: This is not compatible with all architectures.
|
59
|
+
The architecture needs to support single-stepping;
|
60
|
+
delay slot architectures such as MIPS can't
|
61
|
+
be single-stepped by angr.
|
62
|
+
|
63
|
+
Arguments:
|
64
|
+
platform: The platform you want to emulate
|
65
|
+
trace: The list of program counter addresses you want to visit
|
66
|
+
"""
|
67
|
+
|
68
|
+
name = "forced-execution"
|
69
|
+
description = "Forced execution using angr"
|
70
|
+
version = "0.0.1"
|
71
|
+
|
72
|
+
def __init__(self, platform: Platform, trace: typing.List[typing.Dict[str, int]]):
|
73
|
+
super().__init__(trace)
|
74
|
+
self.platform: Platform = platform
|
75
|
+
self.emulator = AngrEmulator(platform)
|
76
|
+
self.emulator.enable_linear()
|
77
|
+
|
78
|
+
def run(self, machine: Machine):
|
79
|
+
emulator = self.emulator
|
80
|
+
if not isinstance(emulator, AngrEmulator):
|
81
|
+
raise TypeError("Impossible!")
|
82
|
+
machine.apply(emulator)
|
83
|
+
emulator.initialize()
|
84
|
+
self.execute()
|
85
|
+
for s in emulator.mgr.active:
|
86
|
+
log.info(s.solver.constraints)
|
87
|
+
s.registers.pp(log.info)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from ...exceptions import EmulationStop
|
2
|
+
from .underlay import AnalysisUnderlay
|
3
|
+
|
4
|
+
|
5
|
+
class BasicAnalysisUnderlay(AnalysisUnderlay):
|
6
|
+
"""Basic analysis underlay that steps until done"""
|
7
|
+
|
8
|
+
def execute(self):
|
9
|
+
try:
|
10
|
+
while True:
|
11
|
+
self.emulator.step()
|
12
|
+
except EmulationStop:
|
13
|
+
pass
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import abc
|
2
|
+
|
3
|
+
from ...emulators import Emulator
|
4
|
+
from ..analysis import Analysis
|
5
|
+
|
6
|
+
|
7
|
+
class AnalysisUnderlay(Analysis):
|
8
|
+
"""Base class for execution underlays
|
9
|
+
|
10
|
+
Some analyses are orthogonal to
|
11
|
+
exactly how their program gets actuated.
|
12
|
+
In those cases, the author can write the
|
13
|
+
bulk of the analysis in an overlay,
|
14
|
+
and pair that overlay with different underlays.
|
15
|
+
"""
|
16
|
+
|
17
|
+
@property
|
18
|
+
def emulator(self) -> Emulator:
|
19
|
+
"""The emulator to run
|
20
|
+
Underlays need the overlay to define the emulator.
|
21
|
+
"""
|
22
|
+
return self._emulator
|
23
|
+
|
24
|
+
@emulator.setter
|
25
|
+
def emulator(self, emu: Emulator):
|
26
|
+
self._emulator = emu
|
27
|
+
|
28
|
+
@abc.abstractmethod
|
29
|
+
def execute(self) -> None:
|
30
|
+
"""Exercise the emulator"""
|
31
|
+
raise NotImplementedError()
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from angr.storage.memory_mixins.memory_mixin import MemoryMixin
|
2
|
+
|
3
|
+
|
4
|
+
class BaseMemoryMixin(MemoryMixin): # type: ignore[misc]
|
5
|
+
"""Base class for memory mixins.
|
6
|
+
|
7
|
+
I wanted to add the _setup_tui() method,
|
8
|
+
and needed a base class to make multiple inheritance happen cleanly.
|
9
|
+
"""
|
10
|
+
|
11
|
+
def _setup_tui(self):
|
12
|
+
pass
|