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
File without changes
|
@@ -0,0 +1,393 @@
|
|
1
|
+
import logging
|
2
|
+
import typing
|
3
|
+
|
4
|
+
import lief
|
5
|
+
|
6
|
+
from ..emulators import Emulator
|
7
|
+
from ..exceptions import ConfigurationError
|
8
|
+
from ..hinting import Hint, get_hinter
|
9
|
+
from .state import Code, Memory
|
10
|
+
|
11
|
+
log = logging.getLogger(__name__)
|
12
|
+
hinter = get_hinter(__name__)
|
13
|
+
|
14
|
+
# Prorgam header types
|
15
|
+
PT_NULL = 0 # Empty/unused program header
|
16
|
+
PT_LOAD = 1 # Describes loadable program segment
|
17
|
+
PT_DYNAMIC = 2 # Points to dynamic linking metadata
|
18
|
+
PT_INTERP = 3 # Points to program interpreter
|
19
|
+
PT_NOTE = 4 # Points to auxiliary information
|
20
|
+
PT_SHLIB = 5 # Reserved value; I think it's unused
|
21
|
+
PT_PHDR = 6 # Points to program header table
|
22
|
+
PT_TLS = 7 # Indicates need for thread-local storage
|
23
|
+
PT_LOOS = 0x60000000 # Start of OS-specific types
|
24
|
+
PT_GNU_EH_FRAME = 0x6474E550 # GNU-specific: Points to exception handler segment
|
25
|
+
PT_GNU_STACK = 0x6474E551 # GNU-specific: Describes stack permissions
|
26
|
+
PT_GNU_RELRO = 0x6474E552 # GNU-specific: Describes read-only after relocation segment
|
27
|
+
PT_GNU_PROPERTY = 0x6474E553 # GNU-specific: Points to GNU property
|
28
|
+
PT_HIOS = 0x6FFFFFFF # End of OS-specific types
|
29
|
+
PT_LOPROC = 0x70000000 # Start of processor-specific types
|
30
|
+
PT_HIPROC = 0x7FFFFFFF # End of processor-specific types
|
31
|
+
|
32
|
+
# Program header flags
|
33
|
+
PF_X = 0x1 # Segment is executable
|
34
|
+
PF_W = 0x2 # Segment is writable
|
35
|
+
PF_R = 0x4 # Segment is readable
|
36
|
+
|
37
|
+
|
38
|
+
class ELFImage(Code):
|
39
|
+
default_base = 0x400000
|
40
|
+
page_size = 0x1000
|
41
|
+
|
42
|
+
def __init__(
|
43
|
+
self,
|
44
|
+
image: bytes,
|
45
|
+
format: typing.Optional[str] = None,
|
46
|
+
arch: typing.Optional[str] = None,
|
47
|
+
mode: typing.Optional[str] = None,
|
48
|
+
base: typing.Optional[int] = None,
|
49
|
+
entry: typing.Optional[int] = None,
|
50
|
+
bounds: typing.Optional[typing.Iterable[range]] = None,
|
51
|
+
):
|
52
|
+
super().__init__(
|
53
|
+
image,
|
54
|
+
base=0,
|
55
|
+
format=format,
|
56
|
+
arch=arch,
|
57
|
+
mode=mode,
|
58
|
+
entry=None,
|
59
|
+
bounds=bounds,
|
60
|
+
)
|
61
|
+
self.user_base: typing.Optional[int] = base
|
62
|
+
self.file_base: int = 0
|
63
|
+
self.user_entry: typing.Optional[int] = entry
|
64
|
+
self.file_entry: int = 0
|
65
|
+
|
66
|
+
self.code_segments: typing.List[Code] = list()
|
67
|
+
self.data_segments: typing.List[Memory] = list()
|
68
|
+
|
69
|
+
self.load_elf()
|
70
|
+
|
71
|
+
def determine_base(self):
|
72
|
+
"""Determine base address to load ELF
|
73
|
+
|
74
|
+
There are two possible sources for a base address.
|
75
|
+
ELF files can specify a base address by setting the address
|
76
|
+
of their first loaded segment to non-zero.
|
77
|
+
Otherwise, a user can request a specific base address.
|
78
|
+
|
79
|
+
If both the user and the file specify an address, it's an error.
|
80
|
+
If only one specifies a base address, use it.
|
81
|
+
If neither specify one, halucinate a value.
|
82
|
+
|
83
|
+
Raises:
|
84
|
+
ConfigurationError: If file_base is non-zero, and user_base is not None
|
85
|
+
"""
|
86
|
+
if self.user_base is None:
|
87
|
+
# No base address requested
|
88
|
+
if self.file_base == 0:
|
89
|
+
# Progam file does not need a specific base address
|
90
|
+
# Use a default
|
91
|
+
# FIXME: Using a fixed value will not be valid if we load multiple files
|
92
|
+
hint = Hint(
|
93
|
+
message=f"No base address requested, and ELF is PIC. Using {hex(self.default_base)}"
|
94
|
+
)
|
95
|
+
hinter.info(hint)
|
96
|
+
self.base = self.default_base
|
97
|
+
else:
|
98
|
+
# Program file needs a specific base address
|
99
|
+
# Use the specified address
|
100
|
+
self.base = self.file_base
|
101
|
+
else:
|
102
|
+
# Base address requested
|
103
|
+
if self.file_base == 0:
|
104
|
+
# Program file does not need a specific base address
|
105
|
+
# Use the requested address
|
106
|
+
self.user_base
|
107
|
+
elif self.file_base == self.user_base:
|
108
|
+
# User and file request the same base address.
|
109
|
+
# We are okay with this.
|
110
|
+
self.user_base
|
111
|
+
else:
|
112
|
+
# Program file needs a specific base address
|
113
|
+
# Not possible to rebase
|
114
|
+
hint = Hint(
|
115
|
+
message=f"Requested base address {hex(self.user_base)}, but program needs {hex(self.file_base)}"
|
116
|
+
)
|
117
|
+
hinter.error(hint)
|
118
|
+
raise ConfigurationError("Contradictory base addresses.")
|
119
|
+
|
120
|
+
def determine_entry(self):
|
121
|
+
"""Determine entrypoint address to use
|
122
|
+
|
123
|
+
This performs two jobs.
|
124
|
+
One is to determine which entrypoint to use;
|
125
|
+
it can be specified by the ELF header, or by the user.
|
126
|
+
|
127
|
+
The second job is to rebase that address
|
128
|
+
according to the base address.
|
129
|
+
An entrypoint from the file is relative to the file's base address.
|
130
|
+
An entrypoint from the user is assumed relative to the user's base address.
|
131
|
+
If the user does not specify a base address, their entrypoint
|
132
|
+
should be relative to the start of the file.
|
133
|
+
|
134
|
+
Raises:
|
135
|
+
ConfigurationError: If neither user nor file specify an entrypoint
|
136
|
+
|
137
|
+
"""
|
138
|
+
if self.user_entry is None:
|
139
|
+
# No entrypoint requested.
|
140
|
+
# Get entry from the file
|
141
|
+
if self.file_entry == 0:
|
142
|
+
# No one specified an entrypoint.
|
143
|
+
# No entrypoint will be set for this file.
|
144
|
+
self.entry = None
|
145
|
+
# file_entry is relative to file_base; rebase relative to base
|
146
|
+
self.entry = self.file_entry - self.file_base + self.base
|
147
|
+
else:
|
148
|
+
# Entrypoint requested.
|
149
|
+
if self.user_base is None:
|
150
|
+
user_base = 0
|
151
|
+
else:
|
152
|
+
user_base = self.user_base
|
153
|
+
# user_entry is relative to user_base, or zero if none requested
|
154
|
+
# rebase relative to base
|
155
|
+
self.entry = self.user_entry - user_base + self.base
|
156
|
+
|
157
|
+
def rebase_user(self, addr: int):
|
158
|
+
"""Helper function for rebasing user-relative addresses"""
|
159
|
+
if self.user_base is None:
|
160
|
+
old_base = 0
|
161
|
+
else:
|
162
|
+
old_base = self.user_base
|
163
|
+
if self.base is None:
|
164
|
+
base = 0
|
165
|
+
else:
|
166
|
+
base = self.base
|
167
|
+
return addr - old_base + base
|
168
|
+
|
169
|
+
def rebase_file(self, addr: int):
|
170
|
+
"""Helper function for rebasing file-relative addresses"""
|
171
|
+
if self.base is None:
|
172
|
+
base = 0
|
173
|
+
else:
|
174
|
+
base = self.base
|
175
|
+
return addr - self.file_base + base
|
176
|
+
|
177
|
+
def page_align(self, x: int, up: bool = True):
|
178
|
+
"""Align an address to a page boundary
|
179
|
+
|
180
|
+
Arguments:
|
181
|
+
x (int): Address to align
|
182
|
+
up (bool): If true, round up. If false, round down
|
183
|
+
|
184
|
+
Return:
|
185
|
+
(int): Aligned version of the address
|
186
|
+
"""
|
187
|
+
if up:
|
188
|
+
x += self.page_size - 1
|
189
|
+
return (x // self.page_size) * self.page_size
|
190
|
+
|
191
|
+
def load_elf(self):
|
192
|
+
"""Load an ELF file into a SmallWorld machine state object.
|
193
|
+
|
194
|
+
This parses the ELF header and the program headers,
|
195
|
+
does minimal validation, and then maps the loadable segments into memory.
|
196
|
+
It also reports RE-relevant features of the binary as hints.
|
197
|
+
|
198
|
+
This function assumes that the program should be loaded
|
199
|
+
using the architecture and mode from the CPU object.
|
200
|
+
|
201
|
+
The user can specify a base address and entrypoint.
|
202
|
+
If not specified, this function defaults to the addresses specified in the ELF.
|
203
|
+
|
204
|
+
If both user and ELF specify a base address,
|
205
|
+
this function will raise an exception.
|
206
|
+
|
207
|
+
If netither user nor ELF specify a base address,
|
208
|
+
a default will be used.
|
209
|
+
|
210
|
+
If both user and ELF specify an entrypoint,
|
211
|
+
the user's entrypoint takes precedence.
|
212
|
+
|
213
|
+
If neither user nor elf specify an entrypoint,
|
214
|
+
no entrypoint will be set for this file.
|
215
|
+
|
216
|
+
Raises:
|
217
|
+
ConfigurationError: If the ELF is invalid, or user-provided data conflicts with ELF.
|
218
|
+
"""
|
219
|
+
# Use lief to check if this is an ELF
|
220
|
+
# NOTE: for some reason, this takes list[int], not bytes
|
221
|
+
if not lief.is_elf(list(self.image)):
|
222
|
+
hint = Hint(message="File is not an elf.")
|
223
|
+
hinter.error(hint)
|
224
|
+
raise ConfigurationError("Input is not an elf")
|
225
|
+
|
226
|
+
# Use lief to parse the ELF
|
227
|
+
# NOTE: for some reason, this takes list[int], not bytes
|
228
|
+
elf = lief.ELF.parse(list(self.image))
|
229
|
+
if elf is None:
|
230
|
+
raise ConfigurationError("Failed parsing input")
|
231
|
+
|
232
|
+
ehdr = elf.header
|
233
|
+
if ehdr is None:
|
234
|
+
raise ConfigurationError("Failed extracting Elf header")
|
235
|
+
|
236
|
+
# TODO: Check machine compatibility?
|
237
|
+
# - ei_class
|
238
|
+
# - ei_data
|
239
|
+
# - ei_osabi (some ABIs)
|
240
|
+
# - e_machine
|
241
|
+
# - e_flags (some ABIs)
|
242
|
+
|
243
|
+
# Figure out if this file is loadable.
|
244
|
+
# Easiest way to tell is if there are program headers
|
245
|
+
if ehdr.program_header_offset == 0:
|
246
|
+
# NULL phoff means no program headers.
|
247
|
+
# This file is not loadable; time to use another ELF loader.
|
248
|
+
hint = Hint(message="No program headers; file is not loadable")
|
249
|
+
hinter.error(hint)
|
250
|
+
raise ConfigurationError("File not loadable")
|
251
|
+
if ehdr.program_header_offset >= len(self.image):
|
252
|
+
hint = Hint(
|
253
|
+
message=f"Invalid program header offset: {hex(ehdr.program_header_offset)}"
|
254
|
+
)
|
255
|
+
hinter.error(hint)
|
256
|
+
raise ConfigurationError("Invalid program header offset")
|
257
|
+
|
258
|
+
# Determine the file base address,
|
259
|
+
# entrypoint, and exit points
|
260
|
+
self.file_base = elf.imagebase
|
261
|
+
self.file_entry = elf.entrypoint
|
262
|
+
self.determine_base()
|
263
|
+
self.determine_entry()
|
264
|
+
self.exits = list(map(lambda x: self.rebase_user(x), self.exits))
|
265
|
+
|
266
|
+
for phdr in elf.segments:
|
267
|
+
log.debug(f"{phdr}")
|
268
|
+
if phdr.type == PT_LOAD:
|
269
|
+
# Loadable segment
|
270
|
+
# Map its data into memory
|
271
|
+
self.map_segment(phdr)
|
272
|
+
elif phdr.type == PT_DYNAMIC:
|
273
|
+
# Dynamic linking metadata.
|
274
|
+
# This ELF needs dynamic linking
|
275
|
+
hint = Hint(message="Program includes dynamic linking metadata")
|
276
|
+
hinter.info(hint)
|
277
|
+
elif phdr.type == PT_INTERP:
|
278
|
+
# Program interpreter
|
279
|
+
# This completely changes how program loading works.
|
280
|
+
# Whether you care is a different matter.
|
281
|
+
interp = self.image[
|
282
|
+
phdr.file_offset : phdr.file_offset + phdr.physical_size
|
283
|
+
]
|
284
|
+
hint = Hint(message=f"Program specifies interpreter {interp!r}")
|
285
|
+
hinter.info(hint)
|
286
|
+
elif phdr.type == PT_NOTE:
|
287
|
+
# Auxiliary information
|
288
|
+
# Possibly useful for comparing machine/OS type.
|
289
|
+
pass
|
290
|
+
elif phdr.type == PT_PHDR:
|
291
|
+
# Program header self-reference
|
292
|
+
# Useful for the dynamic linker, but not for us
|
293
|
+
pass
|
294
|
+
elif phdr.type == PT_TLS:
|
295
|
+
# TLS Segment
|
296
|
+
# Your analysis is about to get nasty :(
|
297
|
+
hint = Hint(message="Program includes thread-local storage")
|
298
|
+
hinter.info(hint)
|
299
|
+
elif phdr.type == PT_GNU_EH_FRAME:
|
300
|
+
# Exception handler frame.
|
301
|
+
# GCC puts one of these in everything. Do we care?
|
302
|
+
pass
|
303
|
+
elif phdr.type == PT_GNU_STACK:
|
304
|
+
# Stack executability
|
305
|
+
# If this is missing, assume executable stack
|
306
|
+
hint = Hint(message="Program specifies stack permissions")
|
307
|
+
hinter.info(hint)
|
308
|
+
elif phdr.type == PT_GNU_RELRO:
|
309
|
+
# Read-only after relocation
|
310
|
+
# Only the dynamic linker should write this data.
|
311
|
+
hint = Hint(message="Program specifies RELRO data")
|
312
|
+
hinter.info(hint)
|
313
|
+
elif phdr.type == PT_GNU_PROPERTY:
|
314
|
+
# GNU property segment
|
315
|
+
# Contains extra metadata which I'm not sure anything uses
|
316
|
+
pass
|
317
|
+
elif phdr.type >= PT_LOOS and phdr.type <= PT_HIOS:
|
318
|
+
# Unknown OS-specific program header
|
319
|
+
# Either this is a weird ISA that extends the generic GNU ABI,
|
320
|
+
# or this isn't a Linux ELF.
|
321
|
+
hint = Hint(f"Unknown OS-specific program header: {phdr.type:08x}")
|
322
|
+
hinter.warn(hint)
|
323
|
+
elif phdr.type >= PT_LOPROC and phdr.type <= PT_HIPROC:
|
324
|
+
# Unknown machine-specific program header
|
325
|
+
# This is probably a non-Intel ISA.
|
326
|
+
# Most of these are harmless, serving to tell the RTLD
|
327
|
+
# where to find machine-specific metadata
|
328
|
+
hint = Hint(f"Unknown machine-specific program header: {phdr.type:08x}")
|
329
|
+
hinter.warn(hint)
|
330
|
+
else:
|
331
|
+
# Unknown program header outside the allowed custom ranges
|
332
|
+
hint = Hint(f"Invalid program header: {phdr.type:08x}")
|
333
|
+
hinter.warn(hint)
|
334
|
+
|
335
|
+
def map_segment(self, phdr):
|
336
|
+
"""Map a segment into a SmallWorld machine state object
|
337
|
+
|
338
|
+
This computes the actual mapping boundaries from a LOAD segment,
|
339
|
+
and then maps it into Smallworld.
|
340
|
+
|
341
|
+
Executable segments are mapped as Code objects.
|
342
|
+
Other segments are mapped as Memory.
|
343
|
+
|
344
|
+
Arguments:
|
345
|
+
phdr: Program header object to load
|
346
|
+
"""
|
347
|
+
|
348
|
+
# Compute segment boundaries
|
349
|
+
seg_start = self.page_align(phdr.file_offset, up=False)
|
350
|
+
seg_end = self.page_align(phdr.file_offset + phdr.physical_size)
|
351
|
+
seg_addr = self.page_align(self.rebase_file(phdr.virtual_address), up=False)
|
352
|
+
seg_size = self.page_align(phdr.virtual_size + (phdr.file_offset - seg_start))
|
353
|
+
|
354
|
+
log.debug(f"{phdr.physical_size:012x}")
|
355
|
+
|
356
|
+
log.debug("Mapping: ")
|
357
|
+
log.debug(f" f: [ {seg_start:012x} -> {seg_end:012x} ]")
|
358
|
+
log.debug(f" m: [ {seg_addr:012x} -> {seg_addr + seg_size:012x} ]")
|
359
|
+
|
360
|
+
# Extract segment data, and zero-fill out to seg_size
|
361
|
+
seg_data = self.image[seg_start:seg_end]
|
362
|
+
if len(seg_data) < seg_size:
|
363
|
+
seg_data += b"\x00" * (seg_size - len(seg_data))
|
364
|
+
elif len(seg_data) != seg_size:
|
365
|
+
# Virtual size should always be greater or equal to file size.
|
366
|
+
# If not, something's wrong.
|
367
|
+
raise ConfigurationError(
|
368
|
+
f"Expected segment of size {seg_size}, but got {len(seg_data)}"
|
369
|
+
)
|
370
|
+
|
371
|
+
if (phdr.flags & PF_X) != 0:
|
372
|
+
# If this is an executable segment, treat it as code
|
373
|
+
code = Code(
|
374
|
+
image=seg_data,
|
375
|
+
format="blob",
|
376
|
+
arch=self.arch,
|
377
|
+
mode=self.mode,
|
378
|
+
base=seg_addr,
|
379
|
+
entry=self.entry,
|
380
|
+
exits=self.exits,
|
381
|
+
)
|
382
|
+
self.code_segments.append(code)
|
383
|
+
else:
|
384
|
+
# Otherwise, treat it as Memory
|
385
|
+
data = Memory(seg_addr, seg_size)
|
386
|
+
data.set(seg_data)
|
387
|
+
self.data_segments.append(data)
|
388
|
+
|
389
|
+
def apply(self, emulator: Emulator, override: bool = True):
|
390
|
+
for code in self.code_segments:
|
391
|
+
code.apply(emulator)
|
392
|
+
for data in self.data_segments:
|
393
|
+
data.apply(emulator)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import typing
|
2
|
+
|
3
|
+
from .state import Register
|
4
|
+
|
5
|
+
|
6
|
+
class X86MMRRegister(Register):
|
7
|
+
"""x86 Memory Management Register
|
8
|
+
|
9
|
+
These things have a special internal format,
|
10
|
+
represented by the 4-tuple (selector, base, limit, flags).
|
11
|
+
|
12
|
+
Unicorn takes these as a 4-tuple.
|
13
|
+
|
14
|
+
angr doesn't take an actual value;
|
15
|
+
it takes a pointer to a host-allocated table. I think.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def set_content(self, content: typing.Optional[typing.Any]):
|
19
|
+
self._content = content
|
20
|
+
|
21
|
+
def __str__(self):
|
22
|
+
s = f"Reg({self.name},{self.size})="
|
23
|
+
x = self.get_content()
|
24
|
+
if x is None:
|
25
|
+
s = s + "=None"
|
26
|
+
elif isinstance(x, tuple):
|
27
|
+
s = s + ", ".join(map(lambda v: hex(v), x))
|
28
|
+
else:
|
29
|
+
s = s + "External ref {hex(x)}"
|
30
|
+
return s
|