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,51 @@
|
|
1
|
+
from ..... import platforms
|
2
|
+
from .....exceptions import ConfigurationError
|
3
|
+
from ..structs import ElfRela
|
4
|
+
from .rela import ElfRelocator
|
5
|
+
|
6
|
+
R_ARM_GLOB_DAT = 21 # Create GOT entry
|
7
|
+
R_ARM_JUMP_SLOT = 22 # Create PLT entry
|
8
|
+
R_ARM_RELATIVE = 23 # Adjust by program base
|
9
|
+
R_ARM_NUM = 256 # This and higher aren't valid
|
10
|
+
|
11
|
+
|
12
|
+
class ArmElfRelocator(ElfRelocator):
|
13
|
+
byteorder = platforms.Byteorder.LITTLE
|
14
|
+
|
15
|
+
def _compute_value(self, rela: ElfRela):
|
16
|
+
if (
|
17
|
+
rela.type == R_ARM_GLOB_DAT
|
18
|
+
or rela.type == R_ARM_JUMP_SLOT
|
19
|
+
or rela.type == R_ARM_RELATIVE
|
20
|
+
):
|
21
|
+
# Different semantics, all behave the same
|
22
|
+
val = rela.symbol.value + rela.symbol.baseaddr + rela.addend
|
23
|
+
return val.to_bytes(4, "little")
|
24
|
+
elif rela.type >= 0 and rela.type < R_ARM_NUM:
|
25
|
+
raise ConfigurationError(
|
26
|
+
"Valid, but unsupported relocation for {rela.symbol.name}: {rela.type}"
|
27
|
+
)
|
28
|
+
else:
|
29
|
+
raise ConfigurationError(
|
30
|
+
"Invalid relocation type for {rela.symbol.name}: {rela.type}"
|
31
|
+
)
|
32
|
+
|
33
|
+
|
34
|
+
class Armv5TElfRelocator(ArmElfRelocator):
|
35
|
+
arch = platforms.Architecture.ARM_V5T
|
36
|
+
|
37
|
+
|
38
|
+
class Armv6MElfRelocator(ArmElfRelocator):
|
39
|
+
arch = platforms.Architecture.ARM_V6M
|
40
|
+
|
41
|
+
|
42
|
+
class Armv7MElfRelocator(ArmElfRelocator):
|
43
|
+
arch = platforms.Architecture.ARM_V7M
|
44
|
+
|
45
|
+
|
46
|
+
class Armv7RElfRelocator(ArmElfRelocator):
|
47
|
+
arch = platforms.Architecture.ARM_V7R
|
48
|
+
|
49
|
+
|
50
|
+
class Armv7AElfRelocator(ArmElfRelocator):
|
51
|
+
arch = platforms.Architecture.ARM_V7A
|
@@ -0,0 +1,32 @@
|
|
1
|
+
from ..... import platforms
|
2
|
+
from .....exceptions import ConfigurationError
|
3
|
+
from ..structs import ElfRela
|
4
|
+
from .rela import ElfRelocator
|
5
|
+
|
6
|
+
R_386_GLOB_DAT = 6 # Create GOT entry
|
7
|
+
R_386_JUMP_SLOT = 7 # Create PLT entry
|
8
|
+
R_386_RELATIVE = 8 # Adjust by program base
|
9
|
+
R_386_NUM = 44 # This and higher aren't valid
|
10
|
+
|
11
|
+
|
12
|
+
class I386ElfRelocator(ElfRelocator):
|
13
|
+
arch = platforms.Architecture.X86_32
|
14
|
+
byteorder = platforms.Byteorder.LITTLE
|
15
|
+
|
16
|
+
def _compute_value(self, rela: ElfRela):
|
17
|
+
if (
|
18
|
+
rela.type == R_386_GLOB_DAT
|
19
|
+
or rela.type == R_386_JUMP_SLOT
|
20
|
+
or rela.type == R_386_RELATIVE
|
21
|
+
):
|
22
|
+
# Different semantics, all behave the same
|
23
|
+
val = rela.symbol.value + rela.symbol.baseaddr + rela.addend
|
24
|
+
return val.to_bytes(4, "little")
|
25
|
+
elif rela.type >= 0 and rela.type < R_386_NUM:
|
26
|
+
raise ConfigurationError(
|
27
|
+
"Valid, but unsupported relocation for {rela.symbol.name}: {rela.type}"
|
28
|
+
)
|
29
|
+
else:
|
30
|
+
raise ConfigurationError(
|
31
|
+
"Invalid relocation type for {rela.symbol.name}: {rela.type}"
|
32
|
+
)
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from ..... import platforms
|
2
|
+
from .....exceptions import ConfigurationError
|
3
|
+
from ..structs import ElfRela
|
4
|
+
from .rela import ElfRelocator
|
5
|
+
|
6
|
+
R_MIPS_32 = 2 # 32-bit direct
|
7
|
+
R_MIPS_64 = 18 # 64-bit direct
|
8
|
+
|
9
|
+
|
10
|
+
class MIPSElfRelocator(ElfRelocator):
|
11
|
+
arch = platforms.Architecture.MIPS32
|
12
|
+
byteorder = platforms.Byteorder.BIG
|
13
|
+
|
14
|
+
def _compute_value(self, rela: ElfRela):
|
15
|
+
# Because mypy doesn't believe I know what I'm doing...
|
16
|
+
|
17
|
+
if rela.type == R_MIPS_32:
|
18
|
+
# 32-bit direct
|
19
|
+
val = rela.symbol.value + rela.symbol.baseaddr + rela.addend
|
20
|
+
return val.to_bytes(
|
21
|
+
4, "big" if self.byteorder == platforms.Byteorder.BIG else "little"
|
22
|
+
)
|
23
|
+
elif rela.type == R_MIPS_64:
|
24
|
+
# 64-bit direct
|
25
|
+
val = rela.symbol.value + rela.symbol.baseaddr + rela.addend
|
26
|
+
return val.to_bytes(
|
27
|
+
8, "big" if self.byteorder == platforms.Byteorder.BIG else "little"
|
28
|
+
)
|
29
|
+
else:
|
30
|
+
raise ConfigurationError(
|
31
|
+
"Invalid relocation type for {rela.symbol.name}: {rela.type}"
|
32
|
+
)
|
33
|
+
|
34
|
+
|
35
|
+
class MIPSELElfRelocator(MIPSElfRelocator):
|
36
|
+
byteorder = platforms.Byteorder.LITTLE
|
37
|
+
|
38
|
+
|
39
|
+
class MIPS64ElfRelocator(MIPSElfRelocator):
|
40
|
+
arch = platforms.Architecture.MIPS64
|
41
|
+
|
42
|
+
|
43
|
+
class MIPS64ELElfRelocator(MIPSElfRelocator):
|
44
|
+
arch = platforms.Architecture.MIPS64
|
45
|
+
byteorder = platforms.Byteorder.LITTLE
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from ..... import platforms
|
2
|
+
from .....exceptions import ConfigurationError
|
3
|
+
from ..structs import ElfRela
|
4
|
+
from .rela import ElfRelocator
|
5
|
+
|
6
|
+
R_PPC_GLOB_DAT = 20 # Create GOT entry
|
7
|
+
R_PPC_JUMP_SLOT = 21 # Create PLT entry
|
8
|
+
R_PPC_RELATIVE = 22 # Adjust by program base
|
9
|
+
|
10
|
+
|
11
|
+
class PowerPCElfRelocator(ElfRelocator):
|
12
|
+
arch = platforms.Architecture.POWERPC32
|
13
|
+
byteorder = platforms.Byteorder.BIG
|
14
|
+
addrsz = 4
|
15
|
+
|
16
|
+
def _compute_value(self, rela: ElfRela):
|
17
|
+
if (
|
18
|
+
rela.type == R_PPC_GLOB_DAT
|
19
|
+
or rela.type == R_PPC_JUMP_SLOT
|
20
|
+
or rela.type == R_PPC_RELATIVE
|
21
|
+
):
|
22
|
+
# Different semantics, all behave the same
|
23
|
+
val = rela.symbol.value + rela.symbol.baseaddr + rela.addend
|
24
|
+
return val.to_bytes(self.addrsz, "big")
|
25
|
+
else:
|
26
|
+
raise ConfigurationError(
|
27
|
+
"Invalid relocation type for {rela.symbol.name}: {rela.type}"
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
class PowerPC64ElfRelocator(PowerPCElfRelocator):
|
32
|
+
arch = platforms.Architecture.POWERPC64
|
33
|
+
addrsz = 8
|
34
|
+
# TODO: This is only the beginning
|
35
|
+
#
|
36
|
+
# The PowerPC64 JUMP_SLOT relocation is much more complicated,
|
37
|
+
# possibly the most complicated I've ever seen.
|
38
|
+
# It actually fills in three 64-bit values:
|
39
|
+
#
|
40
|
+
# 1. The actual function address
|
41
|
+
# 2. The TOC base address; serves a similar purpose to the Global Pointer in MIPS.
|
42
|
+
# 3. An "environment" pointer, not used by C.
|
43
|
+
#
|
44
|
+
# We're currently correctly filling in 1.
|
45
|
+
# Entry 2. is the address of the GOT section of the containing binary + 0x8000.
|
@@ -0,0 +1,63 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import abc
|
4
|
+
|
5
|
+
from ..... import platforms, utils
|
6
|
+
from .....exceptions import ConfigurationError
|
7
|
+
from .. import elf
|
8
|
+
from ..structs import ElfRela
|
9
|
+
|
10
|
+
|
11
|
+
class ElfRelocator:
|
12
|
+
"""Platform-specific ELF relocator
|
13
|
+
|
14
|
+
Each platform defines its own set of relocation types,
|
15
|
+
defining how symbol values can update an image at link-time.
|
16
|
+
|
17
|
+
|
18
|
+
"""
|
19
|
+
|
20
|
+
@property
|
21
|
+
@abc.abstractmethod
|
22
|
+
def arch(self) -> platforms.Architecture:
|
23
|
+
"""The architecture ID"""
|
24
|
+
raise NotImplementedError("This is an abstract method")
|
25
|
+
|
26
|
+
@property
|
27
|
+
@abc.abstractmethod
|
28
|
+
def byteorder(self) -> platforms.Byteorder:
|
29
|
+
"""The byte order"""
|
30
|
+
raise NotImplementedError("This is an abstract method.")
|
31
|
+
|
32
|
+
@abc.abstractmethod
|
33
|
+
def _compute_value(self, rela: ElfRela) -> bytes:
|
34
|
+
raise NotImplementedError("This is an abstract method.")
|
35
|
+
|
36
|
+
def relocate(self, elf: elf.ElfExecutable, rela: ElfRela) -> None:
|
37
|
+
val = self._compute_value(rela)
|
38
|
+
for off, seg in elf.items():
|
39
|
+
start = off + elf.address
|
40
|
+
stop = start + seg.get_size()
|
41
|
+
|
42
|
+
if rela.offset >= start and rela.offset < stop:
|
43
|
+
start = rela.offset - start
|
44
|
+
end = start + len(val)
|
45
|
+
|
46
|
+
contents = seg.get_content()
|
47
|
+
contents = contents[0:start] + val + contents[end:]
|
48
|
+
seg.set_content(contents)
|
49
|
+
return
|
50
|
+
raise ConfigurationError(
|
51
|
+
f"No segment in ELF loaded at {hex(elf.address)} covers address {hex(rela.offset)}"
|
52
|
+
)
|
53
|
+
|
54
|
+
@classmethod
|
55
|
+
def for_platform(cls, platform: platforms.Platform):
|
56
|
+
try:
|
57
|
+
return utils.find_subclass(
|
58
|
+
cls,
|
59
|
+
lambda x: x.arch == platform.architecture
|
60
|
+
and x.byteorder == platform.byteorder,
|
61
|
+
)
|
62
|
+
except:
|
63
|
+
raise ValueError(f"No relocator for {platform}")
|
@@ -0,0 +1,27 @@
|
|
1
|
+
from ..... import platforms
|
2
|
+
from .....exceptions import ConfigurationError
|
3
|
+
from ..structs import ElfRela
|
4
|
+
from .rela import ElfRelocator
|
5
|
+
|
6
|
+
R_RISCV_64 = 2 #
|
7
|
+
R_RISCV_JUMP_SLOT = 5 # Create PLT entry
|
8
|
+
R_RISCV_RELATIVE = 3 # Adjust by program base
|
9
|
+
|
10
|
+
|
11
|
+
class RISCV64ElfRelocator(ElfRelocator):
|
12
|
+
arch = platforms.Architecture.RISCV64
|
13
|
+
byteorder = platforms.Byteorder.LITTLE
|
14
|
+
|
15
|
+
def _compute_value(self, rela: ElfRela):
|
16
|
+
if (
|
17
|
+
rela.type == R_RISCV_64
|
18
|
+
or rela.type == R_RISCV_JUMP_SLOT
|
19
|
+
or rela.type == R_RISCV_RELATIVE
|
20
|
+
):
|
21
|
+
# Different semantics, all behave the same
|
22
|
+
val = rela.symbol.value + rela.symbol.baseaddr + rela.addend
|
23
|
+
return val.to_bytes(8, "little")
|
24
|
+
else:
|
25
|
+
raise ConfigurationError(
|
26
|
+
"Unknown relocation type for {rela.symbol.name}: {rela.type}"
|
27
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from ..... import platforms
|
2
|
+
from .....exceptions import ConfigurationError
|
3
|
+
from ..structs import ElfRela
|
4
|
+
from .rela import ElfRelocator
|
5
|
+
|
6
|
+
|
7
|
+
class XtensaElfRelocator(ElfRelocator):
|
8
|
+
arch = platforms.Architecture.XTENSA
|
9
|
+
byteorder = platforms.Byteorder.LITTLE
|
10
|
+
|
11
|
+
def _compute_value(self, rela: ElfRela):
|
12
|
+
# Xtensa doesn't have a dynamic linker.
|
13
|
+
raise ConfigurationError(
|
14
|
+
"Unknown relocation type for {rela.symbol.name}: {rela.type}"
|
15
|
+
)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import typing
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
|
4
|
+
|
5
|
+
@dataclass(frozen=False)
|
6
|
+
class ElfSymbol:
|
7
|
+
"""ELF symbol struct
|
8
|
+
|
9
|
+
Lief parses this data, but I can't keep the objects around.
|
10
|
+
|
11
|
+
This adds one field to the typical ELF symbol, baseaddr.
|
12
|
+
ELF symbol values are often relative,
|
13
|
+
but relative to what is an annoying question:
|
14
|
+
|
15
|
+
- In non-PIC binaries, the value is relative to zero.
|
16
|
+
- In linked PIC binaries, the value is relative to the load address
|
17
|
+
- In relocatables (.o and .ko), the value is relative to the section's address
|
18
|
+
|
19
|
+
I'll figure out the base address; to get the absolute address,
|
20
|
+
just add value to baseaddr.
|
21
|
+
|
22
|
+
This also adds a data structure helper field, relas,
|
23
|
+
so I can collect the relas linked to a symbol
|
24
|
+
without a separate dict.
|
25
|
+
"""
|
26
|
+
|
27
|
+
name: str # Symbol name
|
28
|
+
type: int # Symbol type
|
29
|
+
bind: int # Symbol binding
|
30
|
+
visibility: int # Symbol visibility
|
31
|
+
shndx: int # Symbol section index, or reserved flags
|
32
|
+
value: int # Symbol value
|
33
|
+
size: int # Symbol size
|
34
|
+
baseaddr: int # Base address for relative symbol values
|
35
|
+
relas: typing.List["ElfRela"] = field(
|
36
|
+
default_factory=list
|
37
|
+
) # List of associated relas
|
38
|
+
|
39
|
+
|
40
|
+
@dataclass(frozen=False)
|
41
|
+
class ElfRela:
|
42
|
+
"""ELF relocation struct
|
43
|
+
|
44
|
+
Lief parses this data, but I can't keep the objects around.
|
45
|
+
|
46
|
+
NOTE: offset is the absolute address this rela is modifying.
|
47
|
+
Normally, it's optionally relative to the load address,
|
48
|
+
or to the linked section's address in a .o or .ko.
|
49
|
+
I don't want to deal with that.
|
50
|
+
"""
|
51
|
+
|
52
|
+
offset: int # Address of the relocation
|
53
|
+
type: int # Relocation type; platform-dependent
|
54
|
+
symbol: ElfSymbol # Relevant symbol in use
|
55
|
+
addend: int # Extra argument to computation
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import abc
|
2
|
+
import typing
|
3
|
+
|
4
|
+
from .. import state
|
5
|
+
from . import memory
|
6
|
+
|
7
|
+
|
8
|
+
class Heap(memory.Memory):
|
9
|
+
"""A heap-like memory region, with convenient operations like allocate and free."""
|
10
|
+
|
11
|
+
@abc.abstractmethod
|
12
|
+
def allocate(self, value: state.Value) -> int:
|
13
|
+
"""Allocate space for and write a value to the heap.
|
14
|
+
|
15
|
+
Arguments:
|
16
|
+
value: The value to go on the heap.
|
17
|
+
Returns:
|
18
|
+
The address at which the value was allocated.
|
19
|
+
"""
|
20
|
+
pass
|
21
|
+
|
22
|
+
@abc.abstractmethod
|
23
|
+
def free(self, address: int) -> None:
|
24
|
+
"""Free space on the heap for previously allocated data.
|
25
|
+
|
26
|
+
Arguements:
|
27
|
+
address: The start address of the previously allocated data.
|
28
|
+
"""
|
29
|
+
pass
|
30
|
+
|
31
|
+
def allocate_integer(self, integer: int, size: int, label: str) -> int:
|
32
|
+
"""Allocate space for and write an integer to the heap.
|
33
|
+
|
34
|
+
Arguments:
|
35
|
+
integer: The integer to go on the heap.
|
36
|
+
size: The number of bytes to allocate to the integer.
|
37
|
+
label: The label to give to the allocated region containing the integer.
|
38
|
+
Returns:
|
39
|
+
The address at which the integer was allocated.
|
40
|
+
"""
|
41
|
+
value = state.IntegerValue(integer, size, label)
|
42
|
+
return self.allocate(value)
|
43
|
+
|
44
|
+
def allocate_bytes(
|
45
|
+
self, content: typing.Union[bytes, bytearray], label: str
|
46
|
+
) -> int:
|
47
|
+
"""Allocate space for and write bytes to the heap.
|
48
|
+
|
49
|
+
Arguments:
|
50
|
+
content: The bytes to go on the heap.
|
51
|
+
label: The label to give to the bytes on the heap.
|
52
|
+
Returns:
|
53
|
+
The address at which the integer was allocated.
|
54
|
+
"""
|
55
|
+
value = state.BytesValue(content, label)
|
56
|
+
return self.allocate(value)
|
57
|
+
|
58
|
+
def allocate_ctype(self, content, label: str) -> int:
|
59
|
+
"""Allocate space for and write structured bytes to the heap.
|
60
|
+
|
61
|
+
Arguements:
|
62
|
+
content: The ctypes-structured data to go on the heap.
|
63
|
+
label: The label to give to the data bytes on the heap.
|
64
|
+
|
65
|
+
Returns:
|
66
|
+
The address at which the structured data was allocated.
|
67
|
+
"""
|
68
|
+
value = state.Value.from_ctypes(content, label)
|
69
|
+
return self.allocate(value)
|
70
|
+
|
71
|
+
|
72
|
+
class BumpAllocator(Heap):
|
73
|
+
"""A simple bump allocator heap."""
|
74
|
+
|
75
|
+
def allocate(self, value: state.Value) -> int:
|
76
|
+
self._is_safe(value)
|
77
|
+
offset = self.get_used()
|
78
|
+
self[offset] = value
|
79
|
+
return self.address + offset
|
80
|
+
|
81
|
+
def free(self, address: int) -> None:
|
82
|
+
raise NotImplementedError("freeing with a BumpAllocator is not yet implemented")
|
83
|
+
|
84
|
+
|
85
|
+
__all__ = ["Heap", "BumpAllocator"]
|
@@ -0,0 +1,181 @@
|
|
1
|
+
import os
|
2
|
+
import typing
|
3
|
+
|
4
|
+
import claripy
|
5
|
+
|
6
|
+
from ... import emulators, exceptions, platforms
|
7
|
+
from .. import state
|
8
|
+
|
9
|
+
|
10
|
+
class Memory(state.Stateful, dict):
|
11
|
+
"""A memory region.
|
12
|
+
|
13
|
+
A memory maps integer offsets (implicitly from the base ``address``)
|
14
|
+
to ``Value`` objects.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, address: int, size: int, *args, **kwargs) -> None:
|
18
|
+
super().__init__(*args, **kwargs)
|
19
|
+
|
20
|
+
self.address: int = address
|
21
|
+
"""The start address of this memory region."""
|
22
|
+
|
23
|
+
self.size: int = size
|
24
|
+
"""The size address of this memory region."""
|
25
|
+
|
26
|
+
def to_bytes(self, byteorder: platforms.Byteorder) -> bytes:
|
27
|
+
"""Convert this memory region into a byte string.
|
28
|
+
|
29
|
+
Missing/undefined space will be filled with zeros.
|
30
|
+
|
31
|
+
Arguments:
|
32
|
+
byteorder: Byteorder for conversion to raw bytes.
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Bytes for this object with the given byteorder.
|
36
|
+
"""
|
37
|
+
|
38
|
+
result = b"\x00" * self.size
|
39
|
+
for offset, value in self.items():
|
40
|
+
# data = value.get_content()
|
41
|
+
result = (
|
42
|
+
result[:offset]
|
43
|
+
+ value.to_bytes(byteorder=byteorder)
|
44
|
+
+ result[offset + value.get_size() :]
|
45
|
+
)
|
46
|
+
|
47
|
+
return result
|
48
|
+
|
49
|
+
def get_capacity(self) -> int:
|
50
|
+
"""Gets the total number of bytes this memory region can store.
|
51
|
+
Returns:
|
52
|
+
The total number of bytes this memory region can store.
|
53
|
+
"""
|
54
|
+
return self.size
|
55
|
+
|
56
|
+
def get_used(self) -> int:
|
57
|
+
"""Gets the number of bytes written to this memory region.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
The number of bytes written to this memory region.
|
61
|
+
"""
|
62
|
+
return sum([v.get_size() for v in self.values()])
|
63
|
+
|
64
|
+
def get_size(self) -> int:
|
65
|
+
raise NotImplementedError("You probably want get_capacity()")
|
66
|
+
|
67
|
+
def _is_safe(self, value: state.Value):
|
68
|
+
if (self.get_used() + value.get_size()) > self.get_capacity():
|
69
|
+
raise ValueError("Stack is full")
|
70
|
+
|
71
|
+
def _write_content(
|
72
|
+
self, emulator: emulators.Emulator, address: int, value: state.Value
|
73
|
+
):
|
74
|
+
# Internal stub; makes it easy for Code to switch to write_code()
|
75
|
+
content = value.get_content()
|
76
|
+
if isinstance(content, claripy.ast.bv.BV):
|
77
|
+
try:
|
78
|
+
emulator.write_memory_content(address, content)
|
79
|
+
return
|
80
|
+
except exceptions.SymbolicValueError:
|
81
|
+
pass
|
82
|
+
emulator.write_memory_content(
|
83
|
+
address, value.to_bytes(emulator.platform.byteorder)
|
84
|
+
)
|
85
|
+
|
86
|
+
def apply(self, emulator: emulators.Emulator) -> None:
|
87
|
+
emulator.map_memory(self.address, self.get_capacity())
|
88
|
+
for offset, value in self.items():
|
89
|
+
if value.get_content() is not None:
|
90
|
+
self._write_content(emulator, self.address + offset, value)
|
91
|
+
if value.get_type() is not None:
|
92
|
+
emulator.write_memory_type(
|
93
|
+
self.address + offset, value.get_size(), value.get_type()
|
94
|
+
)
|
95
|
+
if value.get_label() is not None:
|
96
|
+
emulator.write_memory_label(
|
97
|
+
self.address + offset, value.get_size(), value.get_label()
|
98
|
+
)
|
99
|
+
|
100
|
+
def extract(self, emulator: emulators.Emulator) -> None:
|
101
|
+
value: state.Value
|
102
|
+
try:
|
103
|
+
bytes = emulator.read_memory(self.address, self.get_capacity())
|
104
|
+
value = state.BytesValue(bytes, f"Extracted memory from {self.address}")
|
105
|
+
except exceptions.SymbolicValueError:
|
106
|
+
expr = emulator.read_memory_symbolic(self.address, self.get_capacity())
|
107
|
+
value = state.SymbolicValue(
|
108
|
+
self.get_capacity(), expr, None, f"Extracted memory from {self.address}"
|
109
|
+
)
|
110
|
+
except Exception as e:
|
111
|
+
raise exceptions.EmulationError(
|
112
|
+
f"Failed reading {hex(self.address)}"
|
113
|
+
) from e
|
114
|
+
self.clear()
|
115
|
+
self[0] = value
|
116
|
+
|
117
|
+
def __hash__(self):
|
118
|
+
return super(dict, self).__hash__()
|
119
|
+
|
120
|
+
|
121
|
+
class RawMemory(Memory):
|
122
|
+
@classmethod
|
123
|
+
def from_bytes(cls, bytes: bytes, address: int):
|
124
|
+
"""Load from a byte array.
|
125
|
+
|
126
|
+
Arguments:
|
127
|
+
bytes: The bytes of the memory.
|
128
|
+
address: The address where this memory should be loaded
|
129
|
+
into an emulator's memory.
|
130
|
+
|
131
|
+
Returns:
|
132
|
+
A RawMemory contstructed from the given bytes.
|
133
|
+
"""
|
134
|
+
|
135
|
+
memory = cls(address=address, size=len(bytes))
|
136
|
+
memory[0] = state.BytesValue(bytes, None)
|
137
|
+
|
138
|
+
return memory
|
139
|
+
|
140
|
+
@classmethod
|
141
|
+
def from_file(
|
142
|
+
cls, file: typing.BinaryIO, address: int, label: typing.Optional[str] = None
|
143
|
+
):
|
144
|
+
"""Load from an open file-like object.
|
145
|
+
|
146
|
+
Arguments:
|
147
|
+
file: The open file-like object from which to load data.
|
148
|
+
address: The address where this memory should be loaded
|
149
|
+
into an emulator's memory.
|
150
|
+
|
151
|
+
Returns:
|
152
|
+
A RawMemory from the given file-like object.
|
153
|
+
"""
|
154
|
+
|
155
|
+
data, size = file.read(), file.tell()
|
156
|
+
|
157
|
+
memory = cls(address=address, size=size)
|
158
|
+
memory[0] = state.BytesValue(data, label)
|
159
|
+
|
160
|
+
return memory
|
161
|
+
|
162
|
+
@classmethod
|
163
|
+
def from_filepath(cls, path: str, address: int):
|
164
|
+
"""Load from a file.
|
165
|
+
|
166
|
+
Arguments:
|
167
|
+
path: The path to the file.
|
168
|
+
address: The address where this memory should be loaded
|
169
|
+
into an emulator's memory.
|
170
|
+
|
171
|
+
Returns:
|
172
|
+
A RawMemory parsed from the given file path.
|
173
|
+
"""
|
174
|
+
|
175
|
+
path = os.path.abspath(os.path.expanduser(path))
|
176
|
+
|
177
|
+
with open(path, "rb") as f:
|
178
|
+
return cls.from_file(file=f, address=address)
|
179
|
+
|
180
|
+
|
181
|
+
__all__ = ["Memory", "RawMemory"]
|
@@ -0,0 +1,31 @@
|
|
1
|
+
from .aarch64 import AArch64Stack
|
2
|
+
from .amd64 import AMD64Stack
|
3
|
+
from .arm import ARMv5tStack, ARMv6mStack, ARMv7aStack, ARMv7mStack, ARMv7rStack
|
4
|
+
from .i386 import X86Stack
|
5
|
+
from .mips import MIPSBEStack, MIPSELStack
|
6
|
+
from .mips64 import MIPS64BEStack, MIPS64ELStack
|
7
|
+
from .ppc import PowerPC32Stack, PowerPC64Stack
|
8
|
+
from .riscv import RISCV64Stack
|
9
|
+
from .stack import * # noqa: F401, F403
|
10
|
+
from .stack import __all__ as __stack__
|
11
|
+
from .xtensa import XTensaBEStack, XTensaELStack
|
12
|
+
|
13
|
+
__all__ = __stack__ + [
|
14
|
+
"AArch64Stack",
|
15
|
+
"AMD64Stack",
|
16
|
+
"ARMv5tStack",
|
17
|
+
"ARMv6mStack",
|
18
|
+
"ARMv7mStack",
|
19
|
+
"ARMv7rStack",
|
20
|
+
"ARMv7aStack",
|
21
|
+
"X86Stack",
|
22
|
+
"MIPSBEStack",
|
23
|
+
"MIPSELStack",
|
24
|
+
"MIPS64BEStack",
|
25
|
+
"MIPS64ELStack",
|
26
|
+
"PowerPC32Stack",
|
27
|
+
"PowerPC64Stack",
|
28
|
+
"RISCV64Stack",
|
29
|
+
"XTensaBEStack",
|
30
|
+
"XTensaELStack",
|
31
|
+
]
|
@@ -0,0 +1,22 @@
|
|
1
|
+
import typing
|
2
|
+
|
3
|
+
from .... import platforms
|
4
|
+
from . import stack
|
5
|
+
|
6
|
+
|
7
|
+
class AArch64Stack(stack.DescendingStack):
|
8
|
+
"""A stack for an ARM 64-bit CPU"""
|
9
|
+
|
10
|
+
platform = platforms.Platform(
|
11
|
+
platforms.Architecture.AARCH64, platforms.Byteorder.LITTLE
|
12
|
+
)
|
13
|
+
|
14
|
+
def get_pointer(self) -> int:
|
15
|
+
return (self.address + self.size) - self.get_used()
|
16
|
+
|
17
|
+
def get_alignment(self) -> int:
|
18
|
+
return 16
|
19
|
+
|
20
|
+
@classmethod
|
21
|
+
def initialize_stack(cls, argv: typing.List[bytes], *args, **kwargs):
|
22
|
+
raise NotImplementedError("Stack initialization not implemented for AArch64")
|