A .gitignore => .gitignore +1 -0
@@ 0,0 1,1 @@
+__pycache__
M __init__.py => __init__.py +6 -4
@@ 1,3 1,4 @@
+import binaryninja
from binaryninja.architecture import Architecture
from binaryninja.callingconvention import CallingConvention
from binaryninja.enums import BranchType
@@ 175,7 176,8 @@ class CC(CallingConvention):
int_return_reg = "R4"
high_int_return_reg = "R5"
-Poke.register()
-arch = Architecture['poke']
-arch.register_calling_convention(CC(arch, "poke"))
-arch.standalone_platform.default_calling_convention = arch.calling_conventions['poke']
+if binaryninja.core_license_count():
+ Poke.register()
+ arch = Architecture['poke']
+ arch.register_calling_convention(CC(arch, "poke"))
+ arch.standalone_platform.default_calling_convention = arch.calling_conventions['poke']
A debugger.py => debugger.py +96 -0
@@ 0,0 1,96 @@
+from . import disassembly
+
+import sys
+
+MOD = (2**64)
+
+class Instr(disassembly.Instr):
+ def emulate(self, vm):
+ args = []
+ for t, v in self.args:
+ if t in "ai":
+ args.append(v)
+ elif t in "rX":
+ args.append(vm.registers[v])
+ elif t == "Y":
+ args[-1] += v
+ else:
+ assert False
+
+ if self.args and self.args[0][0] == "r":
+ dest = self.args[0][1]
+
+ if self.op.startswith("add"):
+ vm.registers[dest] = (args[1] + args[2]) % MOD
+ elif self.op == "sub":
+ vm.registers[dest] = (args[1] - args[2]) % MOD
+ elif self.op == "read":
+ if not vm.input_buffer:
+ vm.input_buffer = input().rstrip("\n").encode() + b"\n"
+ vm.registers[dest] = vm.input_buffer[0]
+ vm.input_buffer = vm.input_buffer[1:]
+ elif self.op == "write":
+ sys.stdout.buffer.write(bytes([args[0] % 256]))
+ sys.stdout.buffer.flush()
+ elif self.op == "movi":
+ vm.registers[dest] = args[1]
+ elif self.op == "xor":
+ vm.registers[dest] = args[1] ^ args[2]
+ elif self.op.startswith("load"):
+ if self.op.endswith("b"):
+ sz = 1
+ f = lambda x: x[0]
+ else:
+ sz = 8
+ f = disassembly.unpack8
+ vm.registers[dest] = f(vm.memory[args[1]:args[1]+sz])
+ elif self.op.startswith("store"):
+ if self.op.endswith("b"):
+ m = 256
+ sz = 1
+ f = lambda x: bytes([x])
+ else:
+ m = MOD
+ sz = 8
+ f = disassembly.pack8
+ vm.memory[args[0]:args[0] + sz] = f(args[1] % m)
+ elif self.op in ["jmp", "jr"]:
+ vm.registers[-1] = args[0]
+ elif self.op == "jeq":
+ if args[0] == args[1]:
+ vm.registers[-1] = args[2]
+ elif self.op == "jb":
+ if args[0] < args[1]:
+ vm.registers[-1] = args[2]
+ elif self.op == "exit":
+ vm.halted = True
+ vm.status_code = args[0]
+ elif self.op == "nop":
+ pass
+ else:
+ assert False, f"operation {self.op} unknown or composite"
+
+class VM:
+ def __init__(self, data):
+ self.registers = [0 for _ in range(12)]
+ self.memory = bytearray(0x10000)
+ self.memory[:len(data)] = data
+ self.halted = False
+ self.status_code = 0
+ self.input_buffer = ""
+
+ def step(self):
+ pc = self.registers[-1]
+ instr = Instr(self.memory[pc:pc + 0x1c], pc)
+ self.registers[-1] += 0x1c
+ instr.emulate(self)
+
+def run(vm):
+ while not vm.halted:
+ vm.step()
+ print(f"Exited with code {vm.status_code}")
+
+if __name__ == "__main__":
+ import sys
+ vm = VM(open(sys.argv[1], "rb").read())
+ run(vm)
A debugger.sh => debugger.sh +2 -0
@@ 0,0 1,2 @@
+#!/usr/bin/env bash
+PYTHONPATH=$(dirname $0)/.. python -m poke.debugger "$@"