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,575 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import logging
|
4
|
+
import sys
|
5
|
+
import threading
|
6
|
+
import typing
|
7
|
+
from enum import Enum
|
8
|
+
|
9
|
+
import capstone
|
10
|
+
import claripy
|
11
|
+
import pandare
|
12
|
+
|
13
|
+
from ... import exceptions, platforms, utils
|
14
|
+
from .. import emulator, hookable
|
15
|
+
from .machdefs import PandaMachineDef
|
16
|
+
|
17
|
+
logger = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
|
20
|
+
class PandaEmulator(
|
21
|
+
emulator.Emulator,
|
22
|
+
hookable.QInstructionHookable,
|
23
|
+
hookable.QFunctionHookable,
|
24
|
+
hookable.QMemoryReadHookable,
|
25
|
+
hookable.QMemoryWriteHookable,
|
26
|
+
hookable.QInterruptHookable,
|
27
|
+
):
|
28
|
+
"""An emulator for the Panda emulation engine.
|
29
|
+
|
30
|
+
Arguments:
|
31
|
+
arch: Architecture ID string
|
32
|
+
mode: Mode ID string
|
33
|
+
byteorder: Byteorder
|
34
|
+
|
35
|
+
"""
|
36
|
+
|
37
|
+
description = "This is a smallworld class encapsulating the Panda emulator."
|
38
|
+
name = "smallworld's-panda"
|
39
|
+
version = "0.0"
|
40
|
+
|
41
|
+
PAGE_SIZE = 0x1000
|
42
|
+
|
43
|
+
class ThreadState(Enum):
|
44
|
+
SETUP = 1
|
45
|
+
RUN = 2
|
46
|
+
STEP = 3
|
47
|
+
BLOCK = 4
|
48
|
+
EXIT = 5
|
49
|
+
|
50
|
+
class PandaThread(threading.Thread):
|
51
|
+
# If we want to support both basic block iteration and single step iteration
|
52
|
+
# We cannot get pc from state, cpu eip only holds the basic block pc
|
53
|
+
# so we have to transfer eip, we just need to pass it around
|
54
|
+
# do we want to support this?
|
55
|
+
# we could run in single step mode always? what are the implications of this?
|
56
|
+
# you have to tell me IN ADVANCE when you setup whether you want to step or block
|
57
|
+
# through things...
|
58
|
+
# other things that are a bit annoying...cb_insn_translate without single step
|
59
|
+
# iterates through a "translation" of a whole "block" of instructions before it
|
60
|
+
# starts executing them, whereas single step would not do that; im not even entirely
|
61
|
+
# sure at this point how its determining the bounds of a block but it definitely has
|
62
|
+
# something to do with mapped memory
|
63
|
+
|
64
|
+
# NOTE: if there is ANY error in the thread panda code (typos) it will just die...
|
65
|
+
# be careful in callbacks
|
66
|
+
# If we want to support repeated panda instances we need to make this a subprocess, not thread
|
67
|
+
|
68
|
+
def __init__(self, manager, thread_state):
|
69
|
+
super().__init__(daemon=True)
|
70
|
+
self.manager = manager
|
71
|
+
self.machdef = PandaMachineDef.for_platform(self.manager.platform)
|
72
|
+
self.state = thread_state
|
73
|
+
self.panda = None
|
74
|
+
self.hook_return = None
|
75
|
+
|
76
|
+
# Functions to update state, this prob should be changed
|
77
|
+
def setup_state(self, cpu):
|
78
|
+
self.manager.cpu = cpu
|
79
|
+
|
80
|
+
def update_state(self, cpu, pc):
|
81
|
+
self.manager.cpu = cpu
|
82
|
+
self.manager.pc = pc
|
83
|
+
|
84
|
+
def get_panda_args_from_machdef(self):
|
85
|
+
panda_args = []
|
86
|
+
|
87
|
+
if hasattr(self.machdef, "machine"):
|
88
|
+
panda_args.extend(["-M", self.machdef.machine])
|
89
|
+
else:
|
90
|
+
panda_args.extend(["-M", "configurable"])
|
91
|
+
|
92
|
+
if hasattr(self.machdef, "cpu"): # != "":
|
93
|
+
panda_args.extend(["-cpu", self.machdef.cpu])
|
94
|
+
|
95
|
+
panda_args.extend(["-nographic"])
|
96
|
+
# At some point we can send something in that only supports singlestep?
|
97
|
+
# panda_args.extend(["singlestep"])
|
98
|
+
return panda_args
|
99
|
+
|
100
|
+
def run(self):
|
101
|
+
panda_args = self.get_panda_args_from_machdef()
|
102
|
+
|
103
|
+
self.panda = pandare.Panda(self.machdef.panda_arch, extra_args=panda_args)
|
104
|
+
|
105
|
+
@self.panda.cb_after_machine_init
|
106
|
+
def setup(cpu):
|
107
|
+
print("Panda: setting up state")
|
108
|
+
self.setup_state(cpu)
|
109
|
+
self.signal_and_wait()
|
110
|
+
|
111
|
+
@self.panda.cb_insn_translate
|
112
|
+
def should_run_on_insn(env, pc):
|
113
|
+
return True
|
114
|
+
|
115
|
+
@self.panda.cb_insn_exec
|
116
|
+
def on_insn(cpu, pc):
|
117
|
+
# PowerPC pc move pc to end of instr
|
118
|
+
# so we need to do some stuff to fix that
|
119
|
+
if self.machdef.panda_arch == "ppc":
|
120
|
+
pc = pc - 4 # DONT BLAME ME, BLAME ALEX H AND ME :)
|
121
|
+
self.update_state(cpu, pc)
|
122
|
+
|
123
|
+
if pc in self.manager._exit_points:
|
124
|
+
# stay here until i say die
|
125
|
+
print("\ton_insn: exit")
|
126
|
+
self.state = PandaEmulator.ThreadState.EXIT
|
127
|
+
self.signal_and_wait()
|
128
|
+
elif self.state == PandaEmulator.ThreadState.RUN:
|
129
|
+
# keep going until the end
|
130
|
+
print("\ton_insn: run")
|
131
|
+
elif self.state == PandaEmulator.ThreadState.STEP:
|
132
|
+
# stop and wait for me
|
133
|
+
print("\ton_insn: step")
|
134
|
+
self.signal_and_wait()
|
135
|
+
elif self.state == PandaEmulator.ThreadState.BLOCK:
|
136
|
+
# keep going until the end
|
137
|
+
print("\ton_insn: block")
|
138
|
+
|
139
|
+
print(f"Panda: on_insn: {hex(pc)}, {self.state}")
|
140
|
+
# Check if our pc is in bounds; if not stop
|
141
|
+
if (
|
142
|
+
not self.manager._bounds.is_empty()
|
143
|
+
and not self.manager._bounds.contains_value(pc)
|
144
|
+
):
|
145
|
+
print(f"Panda: {pc} out of bounds")
|
146
|
+
self.state = PandaEmulator.ThreadState.EXIT
|
147
|
+
self.signal_and_wait()
|
148
|
+
|
149
|
+
# Always call hooked code first
|
150
|
+
if self.manager.all_instructions_hook:
|
151
|
+
self.manager.all_instructions_hook(self.manager)
|
152
|
+
|
153
|
+
if cb := self.manager.is_instruction_hooked(pc):
|
154
|
+
cb(self.manager)
|
155
|
+
|
156
|
+
if cb := self.manager.is_function_hooked(pc):
|
157
|
+
cb(self.manager)
|
158
|
+
# Mimic a platform-specific "return" instruction.
|
159
|
+
if (
|
160
|
+
self.manager.platform.architecture
|
161
|
+
== platforms.Architecture.X86_32
|
162
|
+
):
|
163
|
+
# i386: pop a 4-byte value off the stack
|
164
|
+
sp = self.manager.read_register("esp")
|
165
|
+
ret = int.from_bytes(
|
166
|
+
self.manager.read_memory(sp, 4),
|
167
|
+
self.manager.platform.byteorder.value,
|
168
|
+
)
|
169
|
+
self.manager.write_register("esp", sp + 4)
|
170
|
+
elif (
|
171
|
+
self.manager.platform.architecture
|
172
|
+
== platforms.Architecture.X86_64
|
173
|
+
):
|
174
|
+
# amd64: pop an 8-byte value off the stack
|
175
|
+
sp = self.manager.read_register("rsp")
|
176
|
+
ret = int.from_bytes(
|
177
|
+
self.manager.read_memory(sp, 8),
|
178
|
+
self.manager.platform.byteorder.value,
|
179
|
+
)
|
180
|
+
self.manager.write_register("rsp", sp + 8)
|
181
|
+
elif (
|
182
|
+
self.manager.platform.architecture
|
183
|
+
== platforms.Architecture.AARCH64
|
184
|
+
or self.manager.platform.architecture
|
185
|
+
== platforms.Architecture.ARM_V5T
|
186
|
+
or self.manager.platform.architecture
|
187
|
+
== platforms.Architecture.ARM_V6M
|
188
|
+
or self.manager.platform.architecture
|
189
|
+
== platforms.Architecture.ARM_V6M_THUMB
|
190
|
+
or self.manager.platform.architecture
|
191
|
+
== platforms.Architecture.ARM_V7A
|
192
|
+
or self.manager.platform.architecture
|
193
|
+
== platforms.Architecture.ARM_V7M
|
194
|
+
or self.manager.platform.architecture
|
195
|
+
== platforms.Architecture.ARM_V7R
|
196
|
+
or self.manager.platform.architecture
|
197
|
+
== platforms.Architecture.POWERPC32
|
198
|
+
or self.manager.platform.architecture
|
199
|
+
== platforms.Architecture.POWERPC64
|
200
|
+
):
|
201
|
+
# aarch64, arm32, powerpc and powerpc64: branch to register 'lr'
|
202
|
+
ret = self.manager.read_register("lr")
|
203
|
+
elif (
|
204
|
+
self.manager.platform.architecture
|
205
|
+
== platforms.Architecture.MIPS32
|
206
|
+
or self.manager.platform.architecture
|
207
|
+
== platforms.Architecture.MIPS64
|
208
|
+
):
|
209
|
+
# mips32 and mips64: branch to register 'ra'
|
210
|
+
ret = self.manager.read_register("ra")
|
211
|
+
else:
|
212
|
+
raise exceptions.ConfigurationError(
|
213
|
+
"Don't know how to return for {self.manager.platform.architecture}"
|
214
|
+
)
|
215
|
+
|
216
|
+
self.manager.write_register("pc", ret)
|
217
|
+
|
218
|
+
# Now, if we for some reason have a different pc
|
219
|
+
# then the one that is set for us, break out of this
|
220
|
+
# This would be from changing eip in a hook
|
221
|
+
# print(f"Panda: {pc}, {self.manager.pc}")
|
222
|
+
# print(self.manager.read_register('pc'))
|
223
|
+
# if self.manager.read_register("pc") != pc:
|
224
|
+
if self.manager.pc != pc:
|
225
|
+
self.panda.libpanda.cpu_loop_exit_noexc(cpu)
|
226
|
+
|
227
|
+
if not self.manager.current_instruction():
|
228
|
+
# report error if function hooking is enabled?
|
229
|
+
pass
|
230
|
+
print(f"\t{self.manager.current_instruction()}")
|
231
|
+
self.hook_return = pc + self.manager.current_instruction().size
|
232
|
+
|
233
|
+
return True
|
234
|
+
|
235
|
+
# Used for stepping over blocks
|
236
|
+
@self.panda.cb_start_block_exec(enabled=True)
|
237
|
+
def on_block(cpu, tb):
|
238
|
+
self.update_state(cpu, tb.pc)
|
239
|
+
if (
|
240
|
+
self.state == PandaEmulator.ThreadState.BLOCK
|
241
|
+
or self.state == PandaEmulator.ThreadState.SETUP
|
242
|
+
):
|
243
|
+
print(f"Panda: on_block: {tb}, {self.state}")
|
244
|
+
# We need to pause on the next block and wait
|
245
|
+
self.signal_and_wait()
|
246
|
+
|
247
|
+
# Used for hooking mem reads
|
248
|
+
@self.panda.cb_virt_mem_before_read(enabled=True)
|
249
|
+
def on_read(cpu, pc, addr, size):
|
250
|
+
print(f"\ton_read: {addr}")
|
251
|
+
orig_data = self.panda.virtual_memory_read(self.manager.cpu, addr, size)
|
252
|
+
if self.manager.all_reads_hook:
|
253
|
+
val = self.manager.all_reads_hook(
|
254
|
+
self.manager, addr, size, orig_data
|
255
|
+
)
|
256
|
+
if val:
|
257
|
+
self.manager.write_memory(addr, val)
|
258
|
+
orig_data = val
|
259
|
+
if cb := self.manager.is_memory_read_hooked(addr):
|
260
|
+
val = cb(self.manager, addr, size, orig_data)
|
261
|
+
if val:
|
262
|
+
self.manager.write_memory(addr, val)
|
263
|
+
|
264
|
+
# Used for hooking mem writes
|
265
|
+
@self.panda.cb_virt_mem_before_write(enabled=True)
|
266
|
+
def on_write(cpu, pc, addr, size, buf):
|
267
|
+
print(f"\ton_write: {hex(addr)}")
|
268
|
+
byte_val = bytes([buf[i] for i in range(size)])
|
269
|
+
|
270
|
+
if self.manager.all_writes_hook:
|
271
|
+
self.manager.all_writes_hook(self.manager, addr, size, byte_val)
|
272
|
+
|
273
|
+
if cb := self.manager.is_memory_write_hooked(addr):
|
274
|
+
cb(self.manager, addr, size, byte_val)
|
275
|
+
|
276
|
+
@self.panda.cb_before_handle_interrupt(enabled=True)
|
277
|
+
def on_interrupt(cpu, intno):
|
278
|
+
print(f"\ton_interrupt: {intno}")
|
279
|
+
# First if all interrupts are hooked, run that function
|
280
|
+
if self.manager.all_interrupts_hook:
|
281
|
+
self.manager.all_interrupts_hook(self.manager)
|
282
|
+
# Then run interrupt specific function
|
283
|
+
if cb := self.manager.is_interrupt_hooked(intno):
|
284
|
+
cb(self.manager)
|
285
|
+
|
286
|
+
@self.panda.cb_before_handle_exception(enabled=True)
|
287
|
+
def on_exception(cpu, exception_index):
|
288
|
+
print(
|
289
|
+
f"Panda for help: you are hitting an exception at {exception_index}."
|
290
|
+
)
|
291
|
+
|
292
|
+
self.panda.run()
|
293
|
+
|
294
|
+
# This is a blocking call for this thread
|
295
|
+
def signal_and_wait(self):
|
296
|
+
print("Signaling main to run")
|
297
|
+
with self.manager.condition:
|
298
|
+
# Signal that we are done and to run main
|
299
|
+
|
300
|
+
self.manager.run_main = True
|
301
|
+
self.manager.condition.notify()
|
302
|
+
|
303
|
+
# Wait until main tells us to run panda
|
304
|
+
while not self.manager.run_panda:
|
305
|
+
self.manager.condition.wait()
|
306
|
+
|
307
|
+
# Clear the event for the next iteration
|
308
|
+
self.manager.run_panda = False
|
309
|
+
|
310
|
+
def __init__(self, platform: platforms.Platform):
|
311
|
+
super().__init__(platform=platform)
|
312
|
+
|
313
|
+
self.PAGE_SIZE = 0x1000
|
314
|
+
self.platform = platform
|
315
|
+
|
316
|
+
# Emulator variables
|
317
|
+
self.mapped_pages = utils.RangeCollection()
|
318
|
+
|
319
|
+
# Thread/Main sync variables
|
320
|
+
self.condition = threading.Condition()
|
321
|
+
self.run_panda = False
|
322
|
+
self.run_main = False
|
323
|
+
|
324
|
+
# Thread communication variables
|
325
|
+
self.cpu = None
|
326
|
+
self.pc: int = 0
|
327
|
+
|
328
|
+
self.panda_thread = self.PandaThread(self, self.ThreadState.SETUP)
|
329
|
+
self.panda_thread.start()
|
330
|
+
|
331
|
+
self.disassembler = capstone.Cs(
|
332
|
+
self.panda_thread.machdef.cs_arch, self.panda_thread.machdef.cs_mode
|
333
|
+
)
|
334
|
+
self.disassembler.detail = True
|
335
|
+
|
336
|
+
# Wait until panda is up and ready
|
337
|
+
with self.condition:
|
338
|
+
while not self.run_main:
|
339
|
+
self.condition.wait()
|
340
|
+
# Clear the event for the next iteration
|
341
|
+
self.run_main = False
|
342
|
+
|
343
|
+
def read_register_content(self, name: str) -> int:
|
344
|
+
# If we are reading a "pc" reg, refer to actual pc reg
|
345
|
+
if name == "pc" or name == self.panda_thread.machdef.panda_reg("pc"):
|
346
|
+
return self.panda_thread.panda.arch.get_pc(self.cpu)
|
347
|
+
|
348
|
+
if not self.panda_thread.machdef.check_panda_reg(name):
|
349
|
+
raise exceptions.UnsupportedRegisterError(
|
350
|
+
f"Panda doesn't support register {name} for {self.platform}"
|
351
|
+
)
|
352
|
+
name = self.panda_thread.machdef.panda_reg(name)
|
353
|
+
|
354
|
+
try:
|
355
|
+
return self.panda_thread.panda.arch.get_reg(self.cpu, name)
|
356
|
+
except:
|
357
|
+
raise exceptions.AnalysisError(f"Failed reading {name} (id: {name})")
|
358
|
+
|
359
|
+
def write_register_content(
|
360
|
+
self, name: str, content: typing.Union[None, int, claripy.ast.bv.BV]
|
361
|
+
) -> None:
|
362
|
+
if content is None:
|
363
|
+
logger.debug(f"ignoring register write to {name} - no value")
|
364
|
+
return
|
365
|
+
|
366
|
+
if isinstance(content, claripy.ast.bv.BV):
|
367
|
+
raise exceptions.SymbolicValueError(
|
368
|
+
"This emulator cannot handle bitvector expressions"
|
369
|
+
)
|
370
|
+
|
371
|
+
if name == "pc" or name == self.panda_thread.machdef.panda_reg("pc"):
|
372
|
+
# This is my internal pc
|
373
|
+
self.pc = content
|
374
|
+
self.panda_thread.panda.arch.set_pc(self.cpu, content)
|
375
|
+
return
|
376
|
+
|
377
|
+
if not self.panda_thread.machdef.check_panda_reg(name):
|
378
|
+
raise exceptions.UnsupportedRegisterError(
|
379
|
+
f"Panda doesn't support register {name} for {self.platform}"
|
380
|
+
)
|
381
|
+
|
382
|
+
name = self.panda_thread.machdef.panda_reg(name)
|
383
|
+
try:
|
384
|
+
self.panda_thread.panda.arch.set_reg(self.cpu, name, content)
|
385
|
+
except:
|
386
|
+
raise exceptions.AnalysisError(f"Failed writing {name} (id: {name})")
|
387
|
+
|
388
|
+
logger.debug(f"set register {name}={content}")
|
389
|
+
|
390
|
+
def read_memory_content(self, address: int, size: int) -> bytes:
|
391
|
+
if size > sys.maxsize:
|
392
|
+
raise ValueError(f"{size} is too large (max: {sys.maxsize})")
|
393
|
+
|
394
|
+
return self.panda_thread.panda.virtual_memory_read(self.cpu, address, size)
|
395
|
+
|
396
|
+
def map_memory(self, address: int, size: int) -> None:
|
397
|
+
def page_down(address):
|
398
|
+
return address // self.PAGE_SIZE
|
399
|
+
|
400
|
+
def page_up(address):
|
401
|
+
return (address + self.PAGE_SIZE - 1) // self.PAGE_SIZE
|
402
|
+
|
403
|
+
logger.info(
|
404
|
+
f"map_memory:asking for mapping at {hex(address)}, size {hex(size)}"
|
405
|
+
)
|
406
|
+
# Translate an addressi + size to a page range
|
407
|
+
if page_down(address) == page_down(address + size):
|
408
|
+
region = (page_down(address), page_up(address + size) + 1)
|
409
|
+
else:
|
410
|
+
region = (page_down(address), page_up(address + size))
|
411
|
+
|
412
|
+
logger.info(f"map_memory: Page range: {region}")
|
413
|
+
|
414
|
+
# Get the missing pages first. Those are the ones we want to map
|
415
|
+
missing_range = self.mapped_pages.get_missing_ranges(region)
|
416
|
+
|
417
|
+
# Map in those pages and change the memory mapping
|
418
|
+
# Whatever you do map just map a page size or above
|
419
|
+
logger.info(f"Mapping memory {missing_range} page(s).")
|
420
|
+
for start_page, end_page in missing_range:
|
421
|
+
page_size = end_page - start_page
|
422
|
+
logger.info(
|
423
|
+
f"Mapping at {hex(start_page * self.PAGE_SIZE)} in panda of size {hex(page_size * self.PAGE_SIZE)}"
|
424
|
+
)
|
425
|
+
self.panda_thread.panda.map_memory(
|
426
|
+
f"{start_page * self.PAGE_SIZE}",
|
427
|
+
page_size * self.PAGE_SIZE,
|
428
|
+
start_page * self.PAGE_SIZE,
|
429
|
+
)
|
430
|
+
# Make sure we add our new region to our mapped_pages
|
431
|
+
self.mapped_pages.add_range(region)
|
432
|
+
|
433
|
+
def get_memory_map(self) -> typing.List[typing.Tuple[int, int]]:
|
434
|
+
return list(self.mapped_pages.ranges)
|
435
|
+
|
436
|
+
def write_memory_content(
|
437
|
+
self, address: int, content: typing.Union[bytes, claripy.ast.bv.BV]
|
438
|
+
) -> None:
|
439
|
+
# Should we type check, if content isnt bytes mad?
|
440
|
+
if content is None:
|
441
|
+
raise ValueError(f"{self.__class__.__name__} requires concrete state")
|
442
|
+
|
443
|
+
if isinstance(content, claripy.ast.bv.BV):
|
444
|
+
raise exceptions.SymbolicValueError(
|
445
|
+
"This emulator cannot handle bitvector expressions"
|
446
|
+
)
|
447
|
+
|
448
|
+
if len(content) > sys.maxsize:
|
449
|
+
raise ValueError(f"{len(content)} is too large (max: {sys.maxsize})")
|
450
|
+
|
451
|
+
if not len(content):
|
452
|
+
raise ValueError("memory write cannot be empty")
|
453
|
+
|
454
|
+
# FIXME: MIPS64's physical memory space already has contents.
|
455
|
+
# The upper 2**32 bytes of a MIPS64 device is reserved for MMIO.
|
456
|
+
# attempting to store stuff there will almost certainly not do what you want.
|
457
|
+
# This would go away if we figured out how to emulate virtual memory.
|
458
|
+
if self.platform.architecture == platforms.Architecture.MIPS64:
|
459
|
+
if address >= 2**32 or (address + len(content)) >= 2**32:
|
460
|
+
logger.error(
|
461
|
+
f"Attempting to write to {hex(address)} - {hex(address + len(content))} on MIPS64"
|
462
|
+
)
|
463
|
+
logger.error("This strays into reserved MMIO memory; please don't.")
|
464
|
+
raise exceptions.EmulationError("Write to MIPS64 MMIO space")
|
465
|
+
|
466
|
+
self.panda_thread.panda.physical_memory_write(address, content)
|
467
|
+
|
468
|
+
logger.debug(f"wrote {len(content)} bytes to 0x{address:x}")
|
469
|
+
|
470
|
+
def disassemble(
|
471
|
+
self, code: bytes, base: int, count: typing.Optional[int] = None
|
472
|
+
) -> typing.Tuple[typing.List[capstone.CsInsn], str]:
|
473
|
+
# TODO: annotate that offsets are relative.
|
474
|
+
#
|
475
|
+
# We don't know what the base address is at disassembly time - so we
|
476
|
+
# just set it to 0. This means relative address arguments aren't
|
477
|
+
# correctly calculated - we should annotate that relative arguments are
|
478
|
+
# relative e.g., with a "+" or something.
|
479
|
+
|
480
|
+
instructions = self.disassembler.disasm(code, base)
|
481
|
+
|
482
|
+
disassembly = []
|
483
|
+
insns = []
|
484
|
+
for i, instruction in enumerate(instructions):
|
485
|
+
if count is not None and i >= count:
|
486
|
+
break
|
487
|
+
insns.append(instruction)
|
488
|
+
disassembly.append(f"{instruction.mnemonic} {instruction.op_str}")
|
489
|
+
|
490
|
+
return (insns, "\n".join(disassembly))
|
491
|
+
|
492
|
+
def current_instruction(self) -> capstone.CsInsn:
|
493
|
+
pc = self.pc
|
494
|
+
code = self.read_memory(pc, 15)
|
495
|
+
if code is None:
|
496
|
+
raise AssertionError("invalid state")
|
497
|
+
for i in self.disassembler.disasm(code, pc):
|
498
|
+
return i
|
499
|
+
|
500
|
+
def check(self) -> None:
|
501
|
+
if len(self._exit_points) == 0:
|
502
|
+
raise exceptions.ConfigurationError(
|
503
|
+
"at least one exit point must be set, emulation cannot start"
|
504
|
+
)
|
505
|
+
if self.panda_thread.state == self.ThreadState.EXIT:
|
506
|
+
logger.debug("stopping emulation at exit point")
|
507
|
+
raise exceptions.EmulationBounds
|
508
|
+
|
509
|
+
def run(self) -> None:
|
510
|
+
self.check()
|
511
|
+
logger.info(f"starting emulation at {hex(self.pc)}")
|
512
|
+
self.panda_thread.state = self.ThreadState.RUN
|
513
|
+
self.signal_and_wait()
|
514
|
+
logger.info("emulation complete")
|
515
|
+
|
516
|
+
def signal_and_wait(self) -> None:
|
517
|
+
print("Main signaling panda to run")
|
518
|
+
with self.condition:
|
519
|
+
# Signal that we are done and to run panda
|
520
|
+
self.run_panda = True
|
521
|
+
self.condition.notify()
|
522
|
+
|
523
|
+
# Wait until main tells us to run panda
|
524
|
+
while not self.run_main:
|
525
|
+
self.condition.wait()
|
526
|
+
|
527
|
+
# Clear the event for the next iteration
|
528
|
+
self.run_main = False
|
529
|
+
|
530
|
+
def step_block(self) -> None:
|
531
|
+
self.check()
|
532
|
+
if self.panda_thread.state == self.ThreadState.SETUP:
|
533
|
+
# Move past setup
|
534
|
+
self.signal_and_wait()
|
535
|
+
|
536
|
+
pc = self.pc
|
537
|
+
code = self.read_memory(pc, 15) # longest possible instruction
|
538
|
+
if code is None:
|
539
|
+
assert False, "impossible state"
|
540
|
+
(instr, disas) = self.disassemble(code, pc, 1)
|
541
|
+
|
542
|
+
logger.info(f"block step at 0x{pc:x}: {disas}")
|
543
|
+
|
544
|
+
self.panda_thread.state = self.ThreadState.BLOCK
|
545
|
+
self.signal_and_wait()
|
546
|
+
|
547
|
+
def step_instruction(self) -> None:
|
548
|
+
self.check()
|
549
|
+
|
550
|
+
if (
|
551
|
+
self.panda_thread.state == self.ThreadState.SETUP
|
552
|
+
or self.panda_thread.state == self.ThreadState.BLOCK
|
553
|
+
):
|
554
|
+
# Move past setup or block
|
555
|
+
self.panda_thread.state = self.ThreadState.STEP
|
556
|
+
self.signal_and_wait()
|
557
|
+
|
558
|
+
self.panda_thread.state = self.ThreadState.STEP
|
559
|
+
|
560
|
+
pc = self.pc
|
561
|
+
print(f"Step: reading register {pc}")
|
562
|
+
|
563
|
+
code = self.read_memory(pc, 15) # longest possible instruction
|
564
|
+
if code is None:
|
565
|
+
assert False, "impossible state"
|
566
|
+
(instr, disas) = self.disassemble(code, pc, 1)
|
567
|
+
|
568
|
+
logger.info(f"single step at 0x{pc:x}: {disas}")
|
569
|
+
|
570
|
+
# We can run now and wait at next instr;
|
571
|
+
self.signal_and_wait()
|
572
|
+
return
|
573
|
+
|
574
|
+
def __repr__(self) -> str:
|
575
|
+
return f"PandaEmulator(platform={self.platform})"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from .unicorn import (
|
2
|
+
UnicornEmulationExecutionError,
|
3
|
+
UnicornEmulationMemoryReadError,
|
4
|
+
UnicornEmulationMemoryWriteError,
|
5
|
+
UnicornEmulator,
|
6
|
+
)
|
7
|
+
|
8
|
+
__all__ = [
|
9
|
+
"UnicornEmulator",
|
10
|
+
"UnicornEmulationMemoryReadError",
|
11
|
+
"UnicornEmulationMemoryWriteError",
|
12
|
+
"UnicornEmulationExecutionError",
|
13
|
+
]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from .aarch64 import AArch64MachineDef
|
2
|
+
from .amd64 import AMD64MachineDef
|
3
|
+
from .arm import (
|
4
|
+
ARMv5TMachineDef,
|
5
|
+
ARMv6MMachineDef,
|
6
|
+
ARMv6MThumbMachineDef,
|
7
|
+
ARMv7AMachineDef,
|
8
|
+
ARMv7MMachineDef,
|
9
|
+
ARMv7RMachineDef,
|
10
|
+
)
|
11
|
+
from .i386 import i386MachineDef
|
12
|
+
from .machdef import UnicornMachineDef
|
13
|
+
from .mips import MIPSBEMachineDef, MIPSELMachineDef
|
14
|
+
|
15
|
+
__all__ = [
|
16
|
+
"AArch64MachineDef",
|
17
|
+
"AMD64MachineDef",
|
18
|
+
"ARMv5TMachineDef",
|
19
|
+
"ARMv6MMachineDef",
|
20
|
+
"ARMv6MThumbMachineDef",
|
21
|
+
"ARMv7AMachineDef",
|
22
|
+
"ARMv7MMachineDef",
|
23
|
+
"ARMv7RMachineDef",
|
24
|
+
"i386MachineDef",
|
25
|
+
"MIPSBEMachineDef",
|
26
|
+
"MIPSELMachineDef",
|
27
|
+
"UnicornMachineDef",
|
28
|
+
]
|