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,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
+ ]