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,63 @@
|
|
1
|
+
import angr
|
2
|
+
|
3
|
+
from ....emulators.angr.exploration import (
|
4
|
+
BoundedExplorationMixin,
|
5
|
+
TerminationExplorationMixin,
|
6
|
+
)
|
7
|
+
from ....emulators.angr.memory import TrackerMemoryMixin
|
8
|
+
from .divergence import DivergenceExplorationMixin, DivergenceMemoryMixin
|
9
|
+
from .model import ModelMemoryMixin
|
10
|
+
from .typedefs import TypeDefPlugin
|
11
|
+
|
12
|
+
|
13
|
+
class NWBTMemoryPlugin( # type: ignore[misc]
|
14
|
+
DivergenceMemoryMixin,
|
15
|
+
TrackerMemoryMixin,
|
16
|
+
ModelMemoryMixin,
|
17
|
+
angr.storage.DefaultMemory,
|
18
|
+
):
|
19
|
+
pass
|
20
|
+
|
21
|
+
|
22
|
+
class NWBTExplorationTechnique(
|
23
|
+
TerminationExplorationMixin,
|
24
|
+
BoundedExplorationMixin,
|
25
|
+
DivergenceExplorationMixin,
|
26
|
+
angr.exploration_techniques.suggestions.Suggestions,
|
27
|
+
):
|
28
|
+
pass
|
29
|
+
|
30
|
+
|
31
|
+
def configure_nwbt_plugins(emu):
|
32
|
+
"""Configure NWBT analysis plugins.
|
33
|
+
|
34
|
+
This creates a new plugin preset that overrides
|
35
|
+
angr's default symbolic memory plugin.
|
36
|
+
This preset is passed to the entry state constructor,
|
37
|
+
and can't be changed afterward.
|
38
|
+
Thus, this needs to get called in a preinit callback.
|
39
|
+
"""
|
40
|
+
preset = angr.SimState._presets["default"].copy()
|
41
|
+
preset.add_default_plugin("sym_memory", NWBTMemoryPlugin)
|
42
|
+
preset.add_default_plugin("typedefs", TypeDefPlugin)
|
43
|
+
emu._plugin_preset = preset
|
44
|
+
|
45
|
+
|
46
|
+
def configure_nwbt_strategy(emu):
|
47
|
+
"""Configure NWBT analysis strategies
|
48
|
+
|
49
|
+
This overrides the default angr exploration strategy.
|
50
|
+
This needs to access the instantiated exploration manager,
|
51
|
+
so it needs to get called in an init callback.
|
52
|
+
"""
|
53
|
+
emu.mgr.use_technique(NWBTExplorationTechnique())
|
54
|
+
# angr bug: states don't inherit plugin presets.
|
55
|
+
#
|
56
|
+
# Normally, they copy all plugins from their parents.
|
57
|
+
# If you define a custom plugin and don't touch it,
|
58
|
+
# it never gets initialized, and won't get inherited.
|
59
|
+
#
|
60
|
+
# If you try to touch that plugin on a successor,
|
61
|
+
# it can't be initialized, since it doesn't have
|
62
|
+
# your custom preset.
|
63
|
+
emu.state.get_plugin("typedefs")
|
@@ -0,0 +1,170 @@
|
|
1
|
+
import ctypes
|
2
|
+
import logging
|
3
|
+
|
4
|
+
import angr
|
5
|
+
import claripy
|
6
|
+
|
7
|
+
log = logging.getLogger(__name__)
|
8
|
+
|
9
|
+
|
10
|
+
class TypeDefPlugin(angr.SimStatePlugin):
|
11
|
+
"""Angr state plugin for tracking typedefs
|
12
|
+
|
13
|
+
This captures two concepts for a state:
|
14
|
+
|
15
|
+
- Type bindings for addresses, registers and symbols
|
16
|
+
- Instance addresses for symbols
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __init__(self):
|
20
|
+
super().__init__()
|
21
|
+
self._structdefs = dict()
|
22
|
+
|
23
|
+
self._symbols = dict()
|
24
|
+
self._sym_values = dict()
|
25
|
+
self._registers = dict()
|
26
|
+
self._addrs = dict()
|
27
|
+
self._allocations = dict()
|
28
|
+
|
29
|
+
@angr.SimStatePlugin.memo
|
30
|
+
def copy(self, memo):
|
31
|
+
dup = TypeDefPlugin()
|
32
|
+
dup._structdefs.update(self._structdefs)
|
33
|
+
dup._symbols.update(self._symbols)
|
34
|
+
dup._sym_values.update(self._sym_values)
|
35
|
+
dup._registers.update(self._registers)
|
36
|
+
|
37
|
+
# heckin' multi-layer copy...
|
38
|
+
for typedef, addrs in self._allocations.items():
|
39
|
+
dup._allocations[typedef] = set(addrs)
|
40
|
+
|
41
|
+
return dup
|
42
|
+
|
43
|
+
def ctype_to_bv(self, typedef, prefix="sw"):
|
44
|
+
if hasattr(typedef, "_fields_"):
|
45
|
+
off = 0
|
46
|
+
fielddefs = list()
|
47
|
+
for name, fielddef in typedef._fields_:
|
48
|
+
field_off = getattr(typedef, name).offset
|
49
|
+
if field_off > off:
|
50
|
+
# handle padding
|
51
|
+
fielddefs.append(claripy.BVS("padding", (field_off - off) * 8))
|
52
|
+
off = field_off
|
53
|
+
fielddefs.append(self.ctype_to_bv(fielddef, f"{prefix}_{name}"))
|
54
|
+
off += ctypes.sizeof(fielddef)
|
55
|
+
|
56
|
+
return claripy.Concat(*fielddefs)
|
57
|
+
else:
|
58
|
+
res = claripy.BVS(prefix, ctypes.sizeof(typedef) * 8)
|
59
|
+
self.bind_symbol(res, typedef)
|
60
|
+
res = claripy.Reverse(res)
|
61
|
+
return res
|
62
|
+
|
63
|
+
def add_struct(self, structdef):
|
64
|
+
"""Add a struct type to this context for easy lookup"""
|
65
|
+
if structdef._name in self._structdefs:
|
66
|
+
raise ValueError(f"Already have a struct def for {structdef._name}")
|
67
|
+
self._structdefs[structdef._name] = structdef
|
68
|
+
|
69
|
+
def get_struct(self, name):
|
70
|
+
"""Look up a struct type by its name"""
|
71
|
+
return self._structdefs[name]
|
72
|
+
|
73
|
+
def bind_symbol(self, sym, typedef):
|
74
|
+
"""Bind a symbol to a typedef"""
|
75
|
+
if sym in self._symbols:
|
76
|
+
raise ValueError(f"Already have a binding for {sym}")
|
77
|
+
self._symbols[sym.args[0]] = typedef
|
78
|
+
|
79
|
+
def get_symbol_binding(self, sym):
|
80
|
+
"""Get the type binding for a symbol"""
|
81
|
+
if sym in self._symbols:
|
82
|
+
return self._symbols[sym]
|
83
|
+
return None
|
84
|
+
|
85
|
+
def set_symbol(self, sym, val):
|
86
|
+
"""Set a concrete value for a symbol
|
87
|
+
|
88
|
+
Symbols are static-single-assignment,
|
89
|
+
so if they are collapsed to a concrete value,
|
90
|
+
that assignment will stick.
|
91
|
+
The assignment is normally bound up in the solver,
|
92
|
+
making it annoying and expensive to recover.
|
93
|
+
This feature lets us quickly recover our stuff
|
94
|
+
"""
|
95
|
+
log.info(f"Setting {sym} to {val}")
|
96
|
+
if sym in self._sym_values:
|
97
|
+
raise ValueError(f"Already have value {self._sym_values[sym]} for {sym}")
|
98
|
+
self._sym_values[sym] = val
|
99
|
+
|
100
|
+
def get_symbol(self, sym):
|
101
|
+
"""Get a concrete value for a symbol"""
|
102
|
+
if sym in self._sym_values:
|
103
|
+
return self._sym_values[sym]
|
104
|
+
return None
|
105
|
+
|
106
|
+
def bind_register(self, name, typedef):
|
107
|
+
"""Bind a register to a typedef
|
108
|
+
|
109
|
+
Registers themselves don't have specific types.
|
110
|
+
However, this only gets used in uninitialized value analysis;
|
111
|
+
it only covers the type a register has before it's
|
112
|
+
written by the program.
|
113
|
+
"""
|
114
|
+
if name in self._registers:
|
115
|
+
raise ValueError(f"Already have a binding for {name}")
|
116
|
+
self._registers[name] = typedef
|
117
|
+
|
118
|
+
def get_register_binding(self, name):
|
119
|
+
"""Get the type binding for a register
|
120
|
+
|
121
|
+
Registers themselves don't have specific types.
|
122
|
+
However, this only gets used in uninitialized value analysis;
|
123
|
+
it only covers the type a register has before it's
|
124
|
+
written by the program.
|
125
|
+
"""
|
126
|
+
if name in self._registers:
|
127
|
+
return self._registers[name]
|
128
|
+
return None
|
129
|
+
|
130
|
+
def bind_address(self, addr, typedef):
|
131
|
+
if addr in self._addrs:
|
132
|
+
raise ValueError(f"Already have a binding for {addr}")
|
133
|
+
self._addrs[addr] = typedef
|
134
|
+
|
135
|
+
def get_address_binding(self, addr):
|
136
|
+
if addr in self._addrs:
|
137
|
+
return self._addrs[addr]
|
138
|
+
return None
|
139
|
+
|
140
|
+
def allocate(self, typedef):
|
141
|
+
addr = self.state.heap.allocate(ctypes.sizeof(typedef))
|
142
|
+
|
143
|
+
rep = self.ctype_to_bv(typedef)
|
144
|
+
|
145
|
+
self._allocations.setdefault(typedef, set()).add(addr)
|
146
|
+
log.debug(f"Storing {rep} at {addr:x}")
|
147
|
+
self.state.memory.store(addr, rep)
|
148
|
+
return addr
|
149
|
+
|
150
|
+
def get_allocs_for_type(self, typedef):
|
151
|
+
if typedef in self._allocations:
|
152
|
+
return list(self._allocations[typedef])
|
153
|
+
return list()
|
154
|
+
|
155
|
+
def pp(self, log, all_allocs=True):
|
156
|
+
log("Register Bindings")
|
157
|
+
for reg_name, reg_def in self._registers.items():
|
158
|
+
log(f"\t{reg_name} -> {reg_def}")
|
159
|
+
log("Symbol Bindings")
|
160
|
+
for sym_name, sym_def in self._symbols.items():
|
161
|
+
if sym_name in self._sym_values:
|
162
|
+
log(f"\t{sym_name} -> {sym_def} ({self._sym_values[sym_name]:x})")
|
163
|
+
else:
|
164
|
+
log(f"\t{sym_name} -> {sym_def} (No Value)")
|
165
|
+
if all_allocs:
|
166
|
+
log("Allocations:")
|
167
|
+
for typedef, addrs in self._allocations.items():
|
168
|
+
log(f"- {typedef}")
|
169
|
+
for addr in addrs:
|
170
|
+
log(f"\t- {addr:x}")
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import angr
|
2
|
+
|
3
|
+
|
4
|
+
def print_state(log, state, tag):
|
5
|
+
"""Pretty-print data contained in an execution state
|
6
|
+
|
7
|
+
This prints useful registers, memory, and state constraints.
|
8
|
+
"""
|
9
|
+
log(f"{state}: {tag}")
|
10
|
+
if not state.regs.rip.symbolic:
|
11
|
+
try:
|
12
|
+
block = state.block()
|
13
|
+
block.pp()
|
14
|
+
except angr.errors.SimEngineError:
|
15
|
+
log("(Invalid IP!)")
|
16
|
+
else:
|
17
|
+
log("(Exit state)")
|
18
|
+
log("Registers")
|
19
|
+
state.registers.pp(log)
|
20
|
+
log("Memory")
|
21
|
+
state.memory.pp(log)
|
22
|
+
log("Constraints:")
|
23
|
+
for expr in state.solver.constraints:
|
24
|
+
log(f"\t{expr}")
|
25
|
+
state.typedefs.pp(log)
|
@@ -0,0 +1,315 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
import claripy
|
4
|
+
|
5
|
+
log = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
|
8
|
+
class ClaripyVisitor:
|
9
|
+
"""Abstract class for a claripy AST visitor"""
|
10
|
+
|
11
|
+
def visit_int(self, v, **kwargs):
|
12
|
+
raise NotImplementedError(f"{type(self)} missing Integer")
|
13
|
+
|
14
|
+
def visit_bvv(self, v, **kwargs):
|
15
|
+
raise NotImplementedError(f"{type(self)} missing BVV")
|
16
|
+
|
17
|
+
def visit_bvs(self, v, **kwargs):
|
18
|
+
raise NotImplementedError(f"{type(self)} missing BVS")
|
19
|
+
|
20
|
+
def visit_add(self, v, **kwargs):
|
21
|
+
raise NotImplementedError(f"{type(self)} missing Add")
|
22
|
+
|
23
|
+
def visit_mul(self, v, **kwargs):
|
24
|
+
raise NotImplementedError(f"{type(self)} missing Mul")
|
25
|
+
|
26
|
+
def visit_bwor(self, v, **kwargs):
|
27
|
+
raise NotImplementedError(f"{type(self)} missing BWOr")
|
28
|
+
|
29
|
+
def visit_concat(self, v, **kwargs):
|
30
|
+
raise NotImplementedError(f"{type(self)} missing Concat")
|
31
|
+
|
32
|
+
def visit_extract(self, v, **kwargs):
|
33
|
+
raise NotImplementedError(f"{type(self)} missing Extract")
|
34
|
+
|
35
|
+
def visit_if(self, v, **kwargs):
|
36
|
+
raise NotImplementedError(f"{type(self)} missing If")
|
37
|
+
|
38
|
+
def visit_reverse(self, v, **kwargs):
|
39
|
+
raise NotImplementedError(f"{type(self)} missing Reverse")
|
40
|
+
|
41
|
+
def visit_lshift(self, v, **kwargs):
|
42
|
+
raise NotImplementedError(f"{type(self)} missing LShift")
|
43
|
+
|
44
|
+
def visit_rshift(self, v, **kwargs):
|
45
|
+
raise NotImplementedError(f"{type(self)} missing RShift")
|
46
|
+
|
47
|
+
def visit_zeroext(self, v, **kwargs):
|
48
|
+
raise NotImplementedError(f"{type(self)} missing ZeroExt")
|
49
|
+
|
50
|
+
def visit(self, v, **kwargs):
|
51
|
+
"""Perform the visitor pattern on a claripy expression."""
|
52
|
+
# TODO: This is very far from complete.
|
53
|
+
# claripy supports a dizzying array of bitvector operations.
|
54
|
+
# It's easier to populate them as I find them.
|
55
|
+
if isinstance(v, int):
|
56
|
+
# v is a python integer.
|
57
|
+
# I occasionally see an int instead of a BVV.
|
58
|
+
# I don't exactly know why.
|
59
|
+
return self.visit_int(v, **kwargs)
|
60
|
+
elif v.op == "BVV":
|
61
|
+
# v is a concrete value
|
62
|
+
return self.visit_bvv(v, **kwargs)
|
63
|
+
elif v.op == "BVS":
|
64
|
+
# v is a symbol
|
65
|
+
return self.visit_bvs(v, **kwargs)
|
66
|
+
elif v.op == "__add__":
|
67
|
+
# v = sum(*X), for two or more expressions x in X.
|
68
|
+
return self.visit_add(v, **kwargs)
|
69
|
+
elif v.op == "__mul__":
|
70
|
+
# v = prod(*X), for two or more expressions x in X.
|
71
|
+
return self.visit_mul(v, **kwargs)
|
72
|
+
elif v.op == "__or__":
|
73
|
+
# v = BWOr(*X) for two or more expressions x in X.
|
74
|
+
return self.visit_bwor(v, **kwargs)
|
75
|
+
elif v.op == "Concat":
|
76
|
+
# v = Concat(*X), for two or more expressions x in X.
|
77
|
+
return self.visit_concat(v, **kwargs)
|
78
|
+
elif v.op == "If":
|
79
|
+
# v is ITE(x, y, z)
|
80
|
+
return self.visit_if(v, **kwargs)
|
81
|
+
elif v.op == "Reverse":
|
82
|
+
# v is Reverse(x)
|
83
|
+
return self.visit_reverse(v, **kwargs)
|
84
|
+
elif v.op == "Extract":
|
85
|
+
# v is x[a:b], for expression x and ints a and b.
|
86
|
+
return self.visit_extract(v, **kwargs)
|
87
|
+
elif v.op == "__lshift__":
|
88
|
+
# v is x << y, for expressions x and y
|
89
|
+
return self.visit_lshift(v, **kwargs)
|
90
|
+
elif v.op == "LShR":
|
91
|
+
# v is x >>> y, for expressions x and y
|
92
|
+
return self.visit_rshift(v, **kwargs)
|
93
|
+
elif v.op == "ZeroExt":
|
94
|
+
# v is ZeroExt(v, a) for expression x and int a
|
95
|
+
return self.visit_zeroext(v, **kwargs)
|
96
|
+
else:
|
97
|
+
# v is something I haven't thought of yet.
|
98
|
+
raise NotImplementedError(f"Unknown op {v.op}")
|
99
|
+
|
100
|
+
|
101
|
+
class ConditionalVisitor(ClaripyVisitor):
|
102
|
+
"""Visitor that splits a conditional expression into its possible evaluations."""
|
103
|
+
|
104
|
+
def visit_int(self, v):
|
105
|
+
# Integers are leaf ASTs, and never conditional.
|
106
|
+
return [(v, claripy.BoolV(True))]
|
107
|
+
|
108
|
+
def visit_bvv(self, v):
|
109
|
+
# BVVs are leaf ASTs, and never conditional.
|
110
|
+
return [(v, claripy.BoolV(True))]
|
111
|
+
|
112
|
+
def visit_bvs(self, v):
|
113
|
+
# BVSs are leaf ASTs, and never conditional.
|
114
|
+
return [(v, claripy.BoolV(True))]
|
115
|
+
|
116
|
+
def visit_add(self, v):
|
117
|
+
# Addition can produce all combinations of evaluations
|
118
|
+
# of the argument expressions.
|
119
|
+
out = self.visit(v.args[0])
|
120
|
+
for arg in v.args[1:]:
|
121
|
+
old_out = out
|
122
|
+
out = list()
|
123
|
+
for res2, guard2 in self.visit(arg):
|
124
|
+
for res1, guard1 in old_out:
|
125
|
+
res = res1 + res2
|
126
|
+
guard = claripy.And(guard1, guard2)
|
127
|
+
out.append((res, guard))
|
128
|
+
return out
|
129
|
+
|
130
|
+
def visit_mul(self, v):
|
131
|
+
# Multiplication can produce all combinations of evaluations
|
132
|
+
# of the argument expressions.
|
133
|
+
out = self.visit(v.args[0])
|
134
|
+
for arg in v.args[1:]:
|
135
|
+
old_out = out
|
136
|
+
out = list()
|
137
|
+
for res2, guard2 in self.visit(arg):
|
138
|
+
for res1, guard1 in old_out:
|
139
|
+
res = res1 * res2
|
140
|
+
guard = claripy.And(guard1, guard2)
|
141
|
+
out.append((res, guard))
|
142
|
+
return out
|
143
|
+
|
144
|
+
def visit_concat(self, v):
|
145
|
+
# Concatenation can produce all combinations of evaluations
|
146
|
+
# of the argument expressions.
|
147
|
+
out = self.visit(v.args[0])
|
148
|
+
for arg in v.args[1:]:
|
149
|
+
old_out = out
|
150
|
+
out = list()
|
151
|
+
for res2, guard2 in self.visit(arg):
|
152
|
+
for res1, guard1 in old_out:
|
153
|
+
res = res1.concat(res2)
|
154
|
+
guard = claripy.And(guard1, guard2)
|
155
|
+
out.append((res, guard))
|
156
|
+
return out
|
157
|
+
|
158
|
+
def visit_extract(self, v):
|
159
|
+
# Extraction produces one expression per evaluation
|
160
|
+
# of the main argument. The other two are always ints.
|
161
|
+
a = v.args[0]
|
162
|
+
b = v.args[1]
|
163
|
+
res = self.visit(v.args[2])
|
164
|
+
res = list(map(lambda x: (x[0][a:b], x[1]), res))
|
165
|
+
return res
|
166
|
+
|
167
|
+
def visit_if(self, v):
|
168
|
+
# ITE produces the union of the results of
|
169
|
+
# the "then" and "else" expressions.
|
170
|
+
# The condition itself is ignored.
|
171
|
+
out = list()
|
172
|
+
out.extend(
|
173
|
+
map(lambda x: (x[0], claripy.And(x[1], v.args[0])), self.visit(v.args[1]))
|
174
|
+
)
|
175
|
+
out.extend(
|
176
|
+
map(
|
177
|
+
lambda x: (x[0], claripy.And(x[1], claripy.Not(v.args[0]))),
|
178
|
+
self.visit(v.args[2]),
|
179
|
+
)
|
180
|
+
)
|
181
|
+
return out
|
182
|
+
|
183
|
+
def visit_reverse(self, v):
|
184
|
+
# Reversal produces one expression per evaluation of the argument.
|
185
|
+
return list(map(lambda x: (claripy.Reverse(x[0]), x[1]), self.visit(v.args[0])))
|
186
|
+
|
187
|
+
|
188
|
+
class EvalVisitor(ClaripyVisitor):
|
189
|
+
"""Visitor that concretizes a symbolic expression.
|
190
|
+
|
191
|
+
Uses a mapping from variables to concrete values.
|
192
|
+
This implementation requires a complete mapping;
|
193
|
+
it will raise an exception if it encounters
|
194
|
+
a symbol for which it does not have a value.
|
195
|
+
"""
|
196
|
+
|
197
|
+
def visit_int(self, v, bindings=None):
|
198
|
+
return v
|
199
|
+
|
200
|
+
def visit_bvv(self, v, bindings=None):
|
201
|
+
return v
|
202
|
+
|
203
|
+
def visit_bvs(self, v, bindings=None):
|
204
|
+
if v.args[0] not in bindings:
|
205
|
+
raise KeyError("Missing binding for {v}")
|
206
|
+
return bindings[v.args[0]]
|
207
|
+
|
208
|
+
def visit_add(self, v, bindings=None):
|
209
|
+
# Eval of sum is sum of evals
|
210
|
+
out = self.visit(v.args[0], bindings=bindings)
|
211
|
+
for x in v.args[1:]:
|
212
|
+
out += self.visit(x, bindings=bindings)
|
213
|
+
return out
|
214
|
+
|
215
|
+
def visit_concat(self, v, bindings=None):
|
216
|
+
# Eval of concat is concat of evals
|
217
|
+
return claripy.Concat(map(lambda x: self.visit(x, bindings=bindings), v.args))
|
218
|
+
|
219
|
+
def visit_extract(self, v, bindings=None):
|
220
|
+
# Extract only hase one BV argument; the range limits are ints.
|
221
|
+
a = v.args[0]
|
222
|
+
b = v.args[1]
|
223
|
+
return self.visit(v.args[2], bindings=bindings)[a:b]
|
224
|
+
|
225
|
+
def visit_if(self, v, bindings=None):
|
226
|
+
# Concretize all three args of the ITE expression.
|
227
|
+
i = self.visit(v.args[0], bindings=bindings)
|
228
|
+
t = self.visit(v.args[1], bindings=bindings)
|
229
|
+
e = self.visit(v.args[2], bindings=bindings)
|
230
|
+
return claripy.If(i, t, e)
|
231
|
+
|
232
|
+
def visit_reverse(self, v, bindings=None):
|
233
|
+
# Reverse is a simple unary; reverse the result from the arg.
|
234
|
+
res = claripy.Reverse(self.visit(v.args[0], bindings=bindings))
|
235
|
+
return res
|
236
|
+
|
237
|
+
|
238
|
+
class PPrintVisitor(ClaripyVisitor):
|
239
|
+
def visit_int(self, v, **kwargs):
|
240
|
+
raise NotImplementedError("Why?")
|
241
|
+
|
242
|
+
def visit_bvv(self, v, **kwargs):
|
243
|
+
indent = kwargs.get("indent", 0)
|
244
|
+
out = kwargs.get("out", print)
|
245
|
+
out(" " * indent + str(v))
|
246
|
+
|
247
|
+
def visit_bvs(self, v, **kwargs):
|
248
|
+
indent = kwargs.get("indent", 0)
|
249
|
+
out = kwargs.get("out", print)
|
250
|
+
out(" " * indent + str(v))
|
251
|
+
|
252
|
+
def visit_add(self, v, **kwargs):
|
253
|
+
indent = kwargs.get("indent", 0)
|
254
|
+
out = kwargs.get("out", print)
|
255
|
+
out(" " * indent + "Add:")
|
256
|
+
for arg in v.args:
|
257
|
+
self.visit(arg, indent=indent + 1, out=out)
|
258
|
+
|
259
|
+
def visit_bwor(self, v, **kwargs):
|
260
|
+
indent = kwargs.get("indent", 0)
|
261
|
+
out = kwargs.get("out", print)
|
262
|
+
out(" " * indent + "BWOr:")
|
263
|
+
for arg in v.args:
|
264
|
+
self.visit(arg, indent=indent + 1, out=out)
|
265
|
+
|
266
|
+
def visit_concat(self, v, **kwargs):
|
267
|
+
indent = kwargs.get("indent", 0)
|
268
|
+
out = kwargs.get("out", print)
|
269
|
+
out(" " * indent + "Concat:")
|
270
|
+
for arg in v.args:
|
271
|
+
self.visit(arg, indent=indent + 1, out=out)
|
272
|
+
|
273
|
+
def visit_extract(self, v, **kwargs):
|
274
|
+
indent = kwargs.get("indent", 0)
|
275
|
+
out = kwargs.get("out", print)
|
276
|
+
out(" " * indent + f"Extract [{v.args[0]}:{v.args[1]}]:")
|
277
|
+
self.visit(v.args[2], indent=indent + 1, out=out)
|
278
|
+
|
279
|
+
def visit_if(self, v, **kwargs):
|
280
|
+
indent = kwargs.get("indent", 0)
|
281
|
+
out = kwargs.get("out", print)
|
282
|
+
out(" " * indent + "If:")
|
283
|
+
self.visit(v.args[0], indent=indent + 1, out=out)
|
284
|
+
out(" " * indent + "Then:")
|
285
|
+
self.visit(v.args[1], indent=indent + 1, out=out)
|
286
|
+
out(" " * indent + "Else:")
|
287
|
+
self.visit(v.args[2], indent=indent + 1, out=out)
|
288
|
+
|
289
|
+
def visit_reverse(self, v, **kwargs):
|
290
|
+
indent = kwargs.get("indent", 0)
|
291
|
+
out = kwargs.get("out", print)
|
292
|
+
out(" " * indent + "Reverse:")
|
293
|
+
self.visit(v.args[0], indent=indent + 1, out=out)
|
294
|
+
|
295
|
+
def visit_lshift(self, v, **kwargs):
|
296
|
+
indent = kwargs.get("indent", 0)
|
297
|
+
out = kwargs.get("out", print)
|
298
|
+
out(" " * indent + "LShift:")
|
299
|
+
self.visit(v.args[0], indent=indent + 1, out=out)
|
300
|
+
out(" " * indent + "By:")
|
301
|
+
self.visit(v.args[1], indent=indent + 1, out=out)
|
302
|
+
|
303
|
+
def visit_rshift(self, v, **kwargs):
|
304
|
+
indent = kwargs.get("indent", 0)
|
305
|
+
out = kwargs.get("out", print)
|
306
|
+
out(" " * indent + "RShift (Logical):")
|
307
|
+
self.visit(v.args[0], indent=indent + 1, out=out)
|
308
|
+
out(" " * indent + "By:")
|
309
|
+
self.visit(v.args[1], indent=indent + 1, out=out)
|
310
|
+
|
311
|
+
def visit_zeroext(self, v, **kwargs):
|
312
|
+
indent = kwargs.get("indent", 0)
|
313
|
+
out = kwargs.get("out", print)
|
314
|
+
out(" " * indent + f"ZeroExt[{v.args[0]}]:")
|
315
|
+
self.visit(v.args[1], indent=indent + 1, out=out)
|
@@ -0,0 +1,106 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from ... import emulators, hinting, state
|
4
|
+
from .. import analysis
|
5
|
+
from .angr.nwbt import configure_nwbt_plugins, configure_nwbt_strategy
|
6
|
+
from .angr.utils import print_state
|
7
|
+
|
8
|
+
log = logging.getLogger(__name__)
|
9
|
+
hinter = hinting.get_hinter(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
class AngrNWBTAnalysis(analysis.Analysis):
|
13
|
+
name = "angr-nwbt"
|
14
|
+
description = "Angr-based Not-Written-By-Trace detection and correction"
|
15
|
+
version = "0.0.1"
|
16
|
+
|
17
|
+
def __init__(self, *args, initfunc=None, max_steps=500, **kwargs):
|
18
|
+
self.steps_left = max_steps
|
19
|
+
self.initfunc = initfunc
|
20
|
+
|
21
|
+
def analysis_preinit(self, emu):
|
22
|
+
log.info("Registering plugins")
|
23
|
+
configure_nwbt_plugins(emu)
|
24
|
+
|
25
|
+
def analysis_init(self, emu):
|
26
|
+
configure_nwbt_strategy(emu)
|
27
|
+
if self.initfunc is not None:
|
28
|
+
self.initfunc(self, emu.entry)
|
29
|
+
|
30
|
+
def run(self, machine: state.Machine):
|
31
|
+
cpu = machine.get_cpu()
|
32
|
+
emu = emulators.AngrEmulator(
|
33
|
+
cpu.platform, preinit=self.analysis_preinit, init=self.analysis_init
|
34
|
+
)
|
35
|
+
machine.apply(emu)
|
36
|
+
|
37
|
+
# Extract typedef info from the CPU state,
|
38
|
+
# and bind it to the machine state
|
39
|
+
for item in cpu:
|
40
|
+
if isinstance(item, state.Register):
|
41
|
+
if item.get_type() is not None:
|
42
|
+
log.debug(f"Applying type for {item}")
|
43
|
+
emu.state.typedefs.bind_register(item.name, item.get_type())
|
44
|
+
for item in machine:
|
45
|
+
if isinstance(item, state.memory.code.Executable):
|
46
|
+
# TODO: Code is also memory, so it should have types...?
|
47
|
+
pass
|
48
|
+
elif isinstance(item, state.memory.Memory):
|
49
|
+
for offset, value in item.items():
|
50
|
+
if value.get_type() is not None:
|
51
|
+
addr = item.address + offset
|
52
|
+
log.debug(f"Applying type for {hex(addr)}")
|
53
|
+
emu.state.typedefs.bind_address(addr, value.get_type())
|
54
|
+
else:
|
55
|
+
if not isinstance(item, state.cpus.CPU) and item.type is not None:
|
56
|
+
raise NotImplementedError(
|
57
|
+
f"Applying typedef {item.get_type()} of type {type(item)} not implemented"
|
58
|
+
)
|
59
|
+
|
60
|
+
while (self.steps_left is None or self.steps_left > 0) and not self.step(emu):
|
61
|
+
if self.steps_left is not None:
|
62
|
+
self.steps_left -= 1
|
63
|
+
|
64
|
+
def _report_status(self, emu):
|
65
|
+
for st in emu.mgr.active:
|
66
|
+
dis = "\r".join(map(str, st.block().disassembly.insns))
|
67
|
+
log.debug(f"Active state: {dis}")
|
68
|
+
for st in emu.mgr.unconstrained:
|
69
|
+
hint = hinting.OutputHint(
|
70
|
+
message="State left the program",
|
71
|
+
registers=st.registers.create_hint(),
|
72
|
+
memory=st.memory.create_hint(),
|
73
|
+
)
|
74
|
+
hinter.info(hint)
|
75
|
+
for st in emu.mgr.deadended:
|
76
|
+
hint = hinting.OutputHint(
|
77
|
+
message="State exited due to breakpoint",
|
78
|
+
registers=st.registers.create_hint(),
|
79
|
+
memory=st.memory.create_hint(),
|
80
|
+
)
|
81
|
+
hinter.info(hint)
|
82
|
+
for st in emu.mgr.unsat:
|
83
|
+
hint = hinting.OutputHint(
|
84
|
+
message="State cannot continue; constraints unsat",
|
85
|
+
registers=st.registers.create_hint(),
|
86
|
+
memory=st.memory.create_hint(),
|
87
|
+
)
|
88
|
+
hinter.info(hint)
|
89
|
+
for err in emu.mgr.errored:
|
90
|
+
print_state(log.info, err.state, "error")
|
91
|
+
log.error(
|
92
|
+
"\tError:",
|
93
|
+
exc_info=(type(err.error), err.error, err.error.__traceback__),
|
94
|
+
)
|
95
|
+
log.info(f"Summary: {emu.mgr}")
|
96
|
+
|
97
|
+
def step(self, emu):
|
98
|
+
# Report our current status
|
99
|
+
self._report_status(emu)
|
100
|
+
# In our case, return states are unconstrained.
|
101
|
+
emu.mgr.move(from_stash="deadended", to_stash="done")
|
102
|
+
emu.mgr.move(from_stash="unconstrained", to_stash="done")
|
103
|
+
# Drop unsat states once we've logged them.
|
104
|
+
emu.mgr.drop(stash="unsat")
|
105
|
+
self._report_status(emu)
|
106
|
+
return emu.step()
|