Skip to content

Instantly share code, notes, and snippets.

@ymgve
Created November 19, 2017 12:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ymgve/1540c8fb03ce8489d059202d2452900b to your computer and use it in GitHub Desktop.
Save ymgve/1540c8fb03ce8489d059202d2452900b to your computer and use it in GitHub Desktop.
import sys, struct
def s2n(s):
if s.startswith("0x"):
if s.endswith("L"):
s = s[:-1]
n = int(s[2:], 16)
else:
n = int(s)
return n
def s2b(s, nbits=8):
n = s2n(s)
if n < 0 or n >= (1<<nbits):
raise Exception("num too large")
res = bin(n)[2:].rjust(nbits, "0")
return res
functions = []
ram = []
for line in open(sys.argv[1]):
line = line.split(";")[0].strip()
if len(line) == 0:
continue
tokens = line.split()
op = tokens[0].lower()
if op == "function":
function = []
inscount = 0
elif op == "end":
# emit function bits here
functions.append(function)
elif op == "blankram":
ram.extend([0] * s2n(tokens[1]))
elif op == "qword":
ram.append(s2n(tokens[1]))
elif op == "jmpz":
function.append("000" + s2b(tokens[1].replace("label", "")))
elif op == "jmp":
function.append("001" + s2b(tokens[1].replace("label", "")))
elif op == "call":
function.append("010" + s2b(tokens[1]))
elif op == "loadram":
function.append("1000" + s2b(tokens[1], 7))
elif op == "saveram":
function.append("1001" + s2b(tokens[1], 7))
elif op == "dup":
function.append("1010" + s2b(tokens[1], 7))
elif op == "place":
function.append("1011" + s2b(tokens[1], 7))
elif op == "loadi":
function.append("110" + s2b(tokens[1]))
elif op == "shl":
function.append("11100" + s2b(tokens[1], 6))
elif op == "shr":
function.append("11101" + s2b(tokens[1], 6))
elif op == "not":
function.append("111100")
elif op == "and":
function.append("111101")
elif op == "or":
function.append("111110")
elif op == "setif":
function.append("111111")
elif op == "pop":
function.append("110" + s2b("0"))
function.append("111101")
function.append("111110")
elif op.startswith("label"):
function.append("011" + s2b(op.replace("label", "")))
else:
raise Exception("invalid token " + line)
headersize = 16 + len(functions) * 8
header = struct.pack(">Q", len(functions))
data = ""
for function in functions:
codebits = "".join(function)
while len(codebits) % 8 != 0:
codebits += "0"
code = ""
for i in xrange(0, len(codebits), 8):
code += chr(int(codebits[i:i+8], 2))
header += struct.pack(">Q", headersize + len(data))
data += struct.pack(">Q", len(function))
data += code
header += struct.pack(">Q", headersize + len(data))
data += struct.pack(">Q", len(ram)) * 2
for n in ram:
data += struct.pack(">Q", n)
open("output.cmp", "wb").write(header + data)
function 0
call 252
loadi 0
call 8
loadi 1
call 8
loadi 2
call 8
loadi 3
call 8
call 253
end
function 1 ; xor(a, b)
loadi 0
dup 1
loadi 0
dup 1
or
loadi 0
dup 2
loadi 0
dup 2
and
not
and
end
function 2 ; add(a, b)
loadi 0
dup 1
loadi 0
dup 1
label1
call 1
loadi 0
dup 2
loadi 0
dup 2
and
shl 1
loadi 0
dup 0
jmpz label2
loadi 0
place 2
loadi 0
place 0
jmp label1
label2
pop
end
function 3 ; get key via index
loadi 0
dup 0
loadi 3
and
loadi 0
dup 0
shr 1
loadram 4
loadi 0
dup 1
loadi 1
and
jmpz label1
shl 32
label1
shr 32
end
function 4 ; do encrypt round
loadi 0
dup 0
shr 32
loadi 0
dup 1
shl 32
shr 32
loadi 0
dup 0
shl 4
loadi 0
dup 1
shr 5
call 1
loadi 0
place 1
pop
call 2
loadi 0
place 0
loadi 0
loadram 8
call 3
call 2
loadi 0
place 1
pop
call 1
loadi 0
place 1
pop
loadi 0
dup 2
call 2
shl 32
shr 32
loadi 0
place 3
pop
pop
loadi 0
loadram 8
loadi 0
loadram 9
call 2
loadi 0
saveram 8
pop
pop
loadi 0
dup 1
shl 4
loadi 0
dup 2
shr 5
call 1
loadi 0
place 1
pop
loadi 0
dup 2
call 2
loadi 0
place 1
pop
loadi 0
loadram 8
loadi 0
dup 0
shr 11
call 3
loadi 0
place 0
call 2
loadi 0
place 1
pop
call 1
loadi 0
place 1
pop
call 2
shl 32
shr 32
loadi 0
dup 3
shl 32
or
end
function 5
loadi 0
loadi 0
saveram 8
loadi 1
shl 31
label1
loadi 0
dup 1
loadram 0
call 4
loadi 0
dup 3
saveram 0
pop
shr 1
loadi 0
dup 0
jmpz label2
jmp label1
label2
end
function 6
loadi 0
dup 0
not
loadi 1
call 2
loadi 0
dup 4
call 2
end
function 7
loadi 0
dup 0
shr 32
loadi 0
dup 1
shl 32
shr 32
loadi 0
dup 1
shl 4
loadi 0
dup 2
shr 5
call 1
loadi 0
place 1
pop
loadi 0
dup 2
call 2
loadi 0
place 1
pop
loadi 0
loadram 8
loadi 0
dup 0
shr 11
call 3
loadi 0
place 0
call 2
loadi 0
place 1
pop
call 1
loadi 0
dup 3
loadi 0
dup 1
call 6
shl 32
shr 32
loadi 0
place 5
pop
pop
pop
pop
pop
loadi 0
loadram 8
loadi 0
loadram 9
call 6
loadi 0
saveram 8
pop
pop
loadi 0
dup 0
shl 4
loadi 0
dup 1
shr 5
call 1
loadi 0
place 1
pop
call 2
loadi 0
place 0
loadi 0
loadram 8
call 3
call 2
loadi 0
place 1
pop
call 1
loadi 0
dup 4
loadi 0
dup 1
call 6
shl 32
shr 32
shl 32
loadi 0
dup 6
or
end
function 8
loadi 0
loadram 10
loadi 0
saveram 8
loadi 1
shl 31
label1
loadi 0
dup 1
loadram 0
call 7
loadi 0
dup 3
saveram 0
pop
shr 1
loadi 0
dup 0
jmpz label2
jmp label1
label2
end
qword 0x64c729478f8e9f5a
qword 0x4444444433333333
qword 0x1111111122222222
qword 0x1111111122222222
qword 0x123456789ABCDEF0
qword 0x1F2E3D4C5B6A7988
qword 0
qword 0
qword 0 ; sum
qword 0x9e3779b9 ; delta
qword 0x13c6ef3720 ; decryptsum
blankram 16
import struct, sys
class Bits(object):
def __init__(self, s):
self.s = s
self.index = 0
def readbit(self):
bitpos = 7 - (self.index % 8)
bytepos = self.index / 8
bit = (ord(self.s[bytepos]) >> bitpos) & 1
self.index += 1
return bit
def readbits(self, n):
res = 0
for i in xrange(n):
res = 2 * res + self.readbit()
return res
data = open(sys.argv[1], "rb").read()
ncodeblocks = struct.unpack(">Q", data[0:8])[0]
splits = []
for i in xrange(ncodeblocks+1):
offset = struct.unpack(">Q", data[8+i*8:16+i*8])[0]
splits.append(offset)
for i in xrange(ncodeblocks):
print "-------------"
print "function %d" % i
print "-------------"
foo = data[splits[i]:splits[i+1]]
num_instr = struct.unpack(">Q", foo[0:8])[0]
# x = int(foo[8:].encode("hex"), 16)
# print bin(x)
b = Bits(foo[8:])
for j in xrange(num_instr):
op = b.readbits(3)
# x = bin(x)[2:]
# x = x.rjust(11, "0")
# print x
if op == 0:
arg = b.readbits(8)
print "JMPZ label%02x" % arg
elif op == 1:
arg = b.readbits(8)
print "JMP label%02x" % arg
elif op == 2:
arg = b.readbits(8)
print "CALL %d" % arg
elif op == 3:
arg = b.readbits(8)
print "SETLABEL label%02x" % arg
elif op == 4:
mode = b.readbits(1)
offset = b.readbits(7)
if mode == 0:
print "LOAD MEM rel+%02x" % offset
else:
print "SAVE MEM rel+%02x" % offset
elif op == 5:
mode = b.readbits(1)
offset = b.readbits(7)
if mode == 0:
print "DUP %02x" % offset
else:
print "REPLACE %02x" % offset
elif op == 6:
arg = b.readbits(8)
print "LOAD IMM %02x" % arg
elif op == 7:
op2 = b.readbits(3)
if op2 in (0, 1):
arg = (op2 & 1) << 5 | b.readbits(5)
print "SHL %d" % arg
elif op2 in (2, 3):
arg = (op2 & 1) << 5 | b.readbits(5)
print "SHR %d" % arg
elif op2 == 4:
print "NEG"
elif op2 == 5:
print "AND s1, s2"
elif op2 == 6:
print "OR s1, s2"
elif op2 == 7:
print "SETIF s2 >= s1"
else:
print "UNKNOWN OP2", bin(op2)
exit()
else:
print "UNKNOWN", bin(op), b.readbits(8)
break
print
# 111 01000 xxx - shift right
# 111 00000 xxx - shift left
# 111 100 - xor 0xff
# 111 101 - and stack1, stack2
# 111 110 - or stack1, stack2
import struct, sys
class Bits(object):
def __init__(self, s):
self.s = s
self.index = 0
def readbit(self):
bitpos = 7 - (self.index % 8)
bytepos = self.index / 8
bit = (ord(self.s[bytepos]) >> bitpos) & 1
self.index += 1
return bit
def readbits(self, n):
res = 0
for i in xrange(n):
res = 2 * res + self.readbit()
return res
class Function(object):
def __init__(self, codeseq, labels):
self.codeseq = codeseq
self.labels = labels
class VMException(Exception):
pass
class VM(object):
def __init__(self, functions, memory):
self.functions = functions
self.memory = memory
self.funcstack = []
self.stack = []
self.currfunc = 0
self.currip = 0
def printstate(self):
op, op2, arg = self.functions[self.currfunc].codeseq[self.currip]
print "stack",
for val in self.stack:
print hex(val),
print
print self.currfunc, self.currip, "|", op, op2, arg
print
def step(self):
op, op2, arg = self.functions[self.currfunc].codeseq[self.currip]
# print "funcstack", self.funcstack
#self.printstate()
do_inc = True
if op == 0:
if len(self.stack) < 1:
raise VMException("stack empty")
if self.stack.pop() == 0:
if arg not in self.functions[self.currfunc].labels:
raise VMException("unknown label")
self.currip = self.functions[self.currfunc].labels[arg]
do_inc = False
elif op == 1:
if arg not in self.functions[self.currfunc].labels:
raise VMException("unknown label")
self.currip = self.functions[self.currfunc].labels[arg]
do_inc = False
elif op == 2:
if arg == 255:
print "output byte %s" % repr(chr(self.stack[-1] & 0xff))
# sys.stdout.write(chr(self.stack[-1] & 0xff))
elif arg in (252, 253, 254):
raise VMException("this call not impl")
else:
if arg >= len(self.functions):
raise VMException("unknown function called")
self.funcstack.append((self.currfunc, self.currip + 1, len(self.stack)))
self.currfunc = arg
self.currip = 0
do_inc = False
#self.printstate()
elif op == 4:
if op2 == 0:
if len(self.stack) < 1:
raise VMException("stack empty")
index = self.stack.pop() + arg
if index >= len(self.memory) or index < 0:
raise VMException("memory out of bounds")
print "read from mem location %016x %016x" % (index, self.memory[index])
self.stack.append(self.memory[index])
else:
if len(self.stack) < 2:
raise VMException("stack empty")
index = self.stack.pop() + arg
data = self.stack.pop()
if index >= len(self.memory) or index < 0:
raise VMException("memory out of bounds")
print "wrote to mem location %016x %016x" % (index, data)
self.memory[index] = data
elif op == 5:
if op2 == 0:
if len(self.stack) < 1:
raise VMException("stack empty")
index = self.stack.pop() + arg + 1
if len(self.stack) < index:
raise VMException("stack underflow")
self.stack.append(self.stack[-index])
else:
if len(self.stack) < 2:
raise VMException("stack empty")
index = self.stack.pop() + arg + 1
value = self.stack.pop()
if len(self.stack) < index:
raise VMException("stack underflow")
self.stack[-index] = value
elif op == 6:
self.stack.append(arg)
elif op == 7:
if op2 == 0:
if len(self.stack) < 1:
raise VMException("stack empty")
res = (self.stack.pop() << arg) & 0xffffffffffffffff
self.stack.append(res)
elif op2 == 2:
if len(self.stack) < 1:
raise VMException("stack empty")
res = self.stack.pop() >> arg
self.stack.append(res)
elif op2 == 4:
if len(self.stack) < 1:
raise VMException("stack empty")
res = self.stack.pop() ^ 0xffffffffffffffff
self.stack.append(res)
elif op2 == 5:
if len(self.stack) < 2:
raise VMException("stack empty")
val1 = self.stack.pop()
val2 = self.stack.pop()
self.stack.append(val1 & val2)
elif op2 == 6:
if len(self.stack) < 2:
raise VMException("stack empty")
val1 = self.stack.pop()
val2 = self.stack.pop()
self.stack.append(val1 | val2)
elif op2 == 7:
if len(self.stack) < 2:
raise VMException("stack empty")
val1 = self.stack.pop()
val2 = self.stack.pop()
if val2 >= val1:
self.stack.append(1)
else:
self.stack.append(0)
else:
raise VMException("op2 not impl %d" % op2)
else:
raise VMException("op not impl %d" % op)
if do_inc:
self.currip += 1
while self.currip >= len(self.functions[self.currfunc].codeseq):
if len(self.funcstack) == 0:
print "program exited"
return False
if len(self.stack) < 1:
raise VMException("stack empty")
self.currfunc, self.currip, stacksize = self.funcstack.pop()
retval = self.stack.pop()
self.stack = self.stack[0:stacksize]
self.stack.append(retval)
return True
data = open(sys.argv[1], "rb").read()
nfuncs = struct.unpack(">Q", data[0:8])[0]
offsets = []
for i in xrange(nfuncs):
offset = struct.unpack(">Q", data[8+i*8:16+i*8])[0]
offsets.append(offset)
functions = []
for funcno in xrange(nfuncs):
codeseq = []
labels = {}
bytecode = data[offsets[funcno]:]
b = Bits(bytecode[8:])
num_instr = struct.unpack(">Q", bytecode[0:8])[0]
for i in xrange(num_instr):
op = b.readbits(3)
if op in (0, 1, 2, 6):
arg = b.readbits(8)
codeseq.append((op, 0, arg))
elif op == 3:
arg = b.readbits(8)
labels[arg] = len(codeseq)
elif op in (4, 5):
mode = b.readbits(1)
offset = b.readbits(7)
codeseq.append((op, mode, offset))
else:
op2 = b.readbits(3)
if op2 in (0, 1, 2, 3):
arg = (op2 & 1) << 5 | b.readbits(5)
codeseq.append((op, op2 & 6, arg))
elif op2 in (4, 5, 6, 7):
codeseq.append((op, op2, 0))
else:
print "UNKNOWN OP2", bin(op2)
exit()
functions.append(Function(codeseq, labels))
offset = struct.unpack(">Q", data[8+nfuncs*8:16+nfuncs*8])[0]
numqwords, totalqwords = struct.unpack(">QQ", data[offset:offset+16])
memory = [0] * totalqwords
for i in xrange(numqwords):
qword = struct.unpack(">Q", data[offset+16+8*i:offset+24+8*i])[0]
memory[i] = qword
vm = VM(functions, memory)
while True:
cont = vm.step()
if not cont:
break
# for i in xrange(6):
# print "%016x" % vm.memory[i]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment