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.
Files changed (166) hide show
  1. smallworld/__init__.py +35 -0
  2. smallworld/analyses/__init__.py +14 -0
  3. smallworld/analyses/analysis.py +88 -0
  4. smallworld/analyses/code_coverage.py +31 -0
  5. smallworld/analyses/colorizer.py +682 -0
  6. smallworld/analyses/colorizer_summary.py +100 -0
  7. smallworld/analyses/field_detection/__init__.py +14 -0
  8. smallworld/analyses/field_detection/field_analysis.py +536 -0
  9. smallworld/analyses/field_detection/guards.py +26 -0
  10. smallworld/analyses/field_detection/hints.py +133 -0
  11. smallworld/analyses/field_detection/malloc.py +211 -0
  12. smallworld/analyses/forced_exec/__init__.py +3 -0
  13. smallworld/analyses/forced_exec/forced_exec.py +87 -0
  14. smallworld/analyses/underlays/__init__.py +4 -0
  15. smallworld/analyses/underlays/basic.py +13 -0
  16. smallworld/analyses/underlays/underlay.py +31 -0
  17. smallworld/analyses/unstable/__init__.py +4 -0
  18. smallworld/analyses/unstable/angr/__init__.py +0 -0
  19. smallworld/analyses/unstable/angr/base.py +12 -0
  20. smallworld/analyses/unstable/angr/divergence.py +274 -0
  21. smallworld/analyses/unstable/angr/model.py +383 -0
  22. smallworld/analyses/unstable/angr/nwbt.py +63 -0
  23. smallworld/analyses/unstable/angr/typedefs.py +170 -0
  24. smallworld/analyses/unstable/angr/utils.py +25 -0
  25. smallworld/analyses/unstable/angr/visitor.py +315 -0
  26. smallworld/analyses/unstable/angr_nwbt.py +106 -0
  27. smallworld/analyses/unstable/code_coverage.py +54 -0
  28. smallworld/analyses/unstable/code_reachable.py +44 -0
  29. smallworld/analyses/unstable/control_flow_tracer.py +71 -0
  30. smallworld/analyses/unstable/pointer_finder.py +90 -0
  31. smallworld/arch/__init__.py +0 -0
  32. smallworld/arch/aarch64_arch.py +286 -0
  33. smallworld/arch/amd64_arch.py +86 -0
  34. smallworld/arch/i386_arch.py +44 -0
  35. smallworld/emulators/__init__.py +14 -0
  36. smallworld/emulators/angr/__init__.py +7 -0
  37. smallworld/emulators/angr/angr.py +1652 -0
  38. smallworld/emulators/angr/default.py +15 -0
  39. smallworld/emulators/angr/exceptions.py +7 -0
  40. smallworld/emulators/angr/exploration/__init__.py +9 -0
  41. smallworld/emulators/angr/exploration/bounds.py +27 -0
  42. smallworld/emulators/angr/exploration/default.py +17 -0
  43. smallworld/emulators/angr/exploration/terminate.py +22 -0
  44. smallworld/emulators/angr/factory.py +55 -0
  45. smallworld/emulators/angr/machdefs/__init__.py +35 -0
  46. smallworld/emulators/angr/machdefs/aarch64.py +292 -0
  47. smallworld/emulators/angr/machdefs/amd64.py +192 -0
  48. smallworld/emulators/angr/machdefs/arm.py +387 -0
  49. smallworld/emulators/angr/machdefs/i386.py +221 -0
  50. smallworld/emulators/angr/machdefs/machdef.py +138 -0
  51. smallworld/emulators/angr/machdefs/mips.py +184 -0
  52. smallworld/emulators/angr/machdefs/mips64.py +189 -0
  53. smallworld/emulators/angr/machdefs/ppc.py +101 -0
  54. smallworld/emulators/angr/machdefs/riscv.py +261 -0
  55. smallworld/emulators/angr/machdefs/xtensa.py +255 -0
  56. smallworld/emulators/angr/memory/__init__.py +7 -0
  57. smallworld/emulators/angr/memory/default.py +10 -0
  58. smallworld/emulators/angr/memory/fixups.py +43 -0
  59. smallworld/emulators/angr/memory/memtrack.py +105 -0
  60. smallworld/emulators/angr/scratch.py +43 -0
  61. smallworld/emulators/angr/simos.py +53 -0
  62. smallworld/emulators/angr/utils.py +70 -0
  63. smallworld/emulators/emulator.py +1013 -0
  64. smallworld/emulators/hookable.py +252 -0
  65. smallworld/emulators/panda/__init__.py +5 -0
  66. smallworld/emulators/panda/machdefs/__init__.py +28 -0
  67. smallworld/emulators/panda/machdefs/aarch64.py +93 -0
  68. smallworld/emulators/panda/machdefs/amd64.py +71 -0
  69. smallworld/emulators/panda/machdefs/arm.py +89 -0
  70. smallworld/emulators/panda/machdefs/i386.py +36 -0
  71. smallworld/emulators/panda/machdefs/machdef.py +86 -0
  72. smallworld/emulators/panda/machdefs/mips.py +94 -0
  73. smallworld/emulators/panda/machdefs/mips64.py +91 -0
  74. smallworld/emulators/panda/machdefs/ppc.py +79 -0
  75. smallworld/emulators/panda/panda.py +575 -0
  76. smallworld/emulators/unicorn/__init__.py +13 -0
  77. smallworld/emulators/unicorn/machdefs/__init__.py +28 -0
  78. smallworld/emulators/unicorn/machdefs/aarch64.py +310 -0
  79. smallworld/emulators/unicorn/machdefs/amd64.py +326 -0
  80. smallworld/emulators/unicorn/machdefs/arm.py +321 -0
  81. smallworld/emulators/unicorn/machdefs/i386.py +137 -0
  82. smallworld/emulators/unicorn/machdefs/machdef.py +117 -0
  83. smallworld/emulators/unicorn/machdefs/mips.py +202 -0
  84. smallworld/emulators/unicorn/unicorn.py +684 -0
  85. smallworld/exceptions/__init__.py +5 -0
  86. smallworld/exceptions/exceptions.py +85 -0
  87. smallworld/exceptions/unstable/__init__.py +1 -0
  88. smallworld/exceptions/unstable/exceptions.py +25 -0
  89. smallworld/extern/__init__.py +4 -0
  90. smallworld/extern/ctypes.py +94 -0
  91. smallworld/extern/unstable/__init__.py +1 -0
  92. smallworld/extern/unstable/ghidra.py +129 -0
  93. smallworld/helpers.py +107 -0
  94. smallworld/hinting/__init__.py +8 -0
  95. smallworld/hinting/hinting.py +214 -0
  96. smallworld/hinting/hints.py +427 -0
  97. smallworld/hinting/unstable/__init__.py +2 -0
  98. smallworld/hinting/utils.py +19 -0
  99. smallworld/instructions/__init__.py +18 -0
  100. smallworld/instructions/aarch64.py +20 -0
  101. smallworld/instructions/arm.py +18 -0
  102. smallworld/instructions/bsid.py +67 -0
  103. smallworld/instructions/instructions.py +258 -0
  104. smallworld/instructions/mips.py +21 -0
  105. smallworld/instructions/x86.py +100 -0
  106. smallworld/logging.py +90 -0
  107. smallworld/platforms.py +95 -0
  108. smallworld/py.typed +0 -0
  109. smallworld/state/__init__.py +6 -0
  110. smallworld/state/cpus/__init__.py +32 -0
  111. smallworld/state/cpus/aarch64.py +563 -0
  112. smallworld/state/cpus/amd64.py +676 -0
  113. smallworld/state/cpus/arm.py +630 -0
  114. smallworld/state/cpus/cpu.py +71 -0
  115. smallworld/state/cpus/i386.py +239 -0
  116. smallworld/state/cpus/mips.py +374 -0
  117. smallworld/state/cpus/mips64.py +372 -0
  118. smallworld/state/cpus/powerpc.py +229 -0
  119. smallworld/state/cpus/riscv.py +357 -0
  120. smallworld/state/cpus/xtensa.py +80 -0
  121. smallworld/state/memory/__init__.py +7 -0
  122. smallworld/state/memory/code.py +70 -0
  123. smallworld/state/memory/elf/__init__.py +3 -0
  124. smallworld/state/memory/elf/elf.py +564 -0
  125. smallworld/state/memory/elf/rela/__init__.py +32 -0
  126. smallworld/state/memory/elf/rela/aarch64.py +27 -0
  127. smallworld/state/memory/elf/rela/amd64.py +32 -0
  128. smallworld/state/memory/elf/rela/arm.py +51 -0
  129. smallworld/state/memory/elf/rela/i386.py +32 -0
  130. smallworld/state/memory/elf/rela/mips.py +45 -0
  131. smallworld/state/memory/elf/rela/ppc.py +45 -0
  132. smallworld/state/memory/elf/rela/rela.py +63 -0
  133. smallworld/state/memory/elf/rela/riscv64.py +27 -0
  134. smallworld/state/memory/elf/rela/xtensa.py +15 -0
  135. smallworld/state/memory/elf/structs.py +55 -0
  136. smallworld/state/memory/heap.py +85 -0
  137. smallworld/state/memory/memory.py +181 -0
  138. smallworld/state/memory/stack/__init__.py +31 -0
  139. smallworld/state/memory/stack/aarch64.py +22 -0
  140. smallworld/state/memory/stack/amd64.py +42 -0
  141. smallworld/state/memory/stack/arm.py +66 -0
  142. smallworld/state/memory/stack/i386.py +22 -0
  143. smallworld/state/memory/stack/mips.py +34 -0
  144. smallworld/state/memory/stack/mips64.py +34 -0
  145. smallworld/state/memory/stack/ppc.py +34 -0
  146. smallworld/state/memory/stack/riscv.py +22 -0
  147. smallworld/state/memory/stack/stack.py +127 -0
  148. smallworld/state/memory/stack/xtensa.py +34 -0
  149. smallworld/state/models/__init__.py +6 -0
  150. smallworld/state/models/mmio.py +186 -0
  151. smallworld/state/models/model.py +163 -0
  152. smallworld/state/models/posix.py +455 -0
  153. smallworld/state/models/x86/__init__.py +2 -0
  154. smallworld/state/models/x86/microsoftcdecl.py +35 -0
  155. smallworld/state/models/x86/systemv.py +240 -0
  156. smallworld/state/state.py +962 -0
  157. smallworld/state/unstable/__init__.py +0 -0
  158. smallworld/state/unstable/elf.py +393 -0
  159. smallworld/state/x86_registers.py +30 -0
  160. smallworld/utils.py +935 -0
  161. smallworld_re-1.0.0.dist-info/LICENSE.txt +21 -0
  162. smallworld_re-1.0.0.dist-info/METADATA +189 -0
  163. smallworld_re-1.0.0.dist-info/RECORD +166 -0
  164. smallworld_re-1.0.0.dist-info/WHEEL +5 -0
  165. smallworld_re-1.0.0.dist-info/entry_points.txt +2 -0
  166. 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")