~robin_jadoul/binja_poke_vm

10b64d18ed60773a61f0a702910acaa3652f1ea7 — Robin Jadoul 3 years ago 03c9d3d master
Python based vm
4 files changed, 105 insertions(+), 4 deletions(-)

A .gitignore
M __init__.py
A debugger.py
A debugger.sh
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 "$@"