commit 63175f36d04ebbc58094c478477871c1f001263f Author: Jon Michael Aanes Date: Wed Dec 16 00:05:18 2015 +0100 Initial commit. Holy hell this code is terrible. diff --git a/README.md b/README.md new file mode 100644 index 0000000..da263a5 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ + +# Infernal Interpreter & Devious Stack Painter ################################# + +A very simple interpreter and stack tracer for the AMD x86_64 ABI written in +Python. Does not support all commands as of yet (or anytime). diff --git a/internal.py b/internal.py new file mode 100644 index 0000000..1b71141 --- /dev/null +++ b/internal.py @@ -0,0 +1,265 @@ + +import re +import sys + +REGISTERS=["%rip","%rbp","%rsp","","%rax","%rdi","%r8","%r9"] +OPTIONS = """register_node/.style={rectangle, draw=black!30, + fill=black!5, very thick, minimum size=0.5, minimum width=20mm}, + text_node/.style={}""" + +class Emulator: + + def __init__ (self, source_text): + self.registers = {} + for reg_name in REGISTERS: + self.registers[reg_name] = 0 + self.stack = [0,1,2,3,4,5,6,7] + self.code = [] + self.codep = [] + self.linesExec = [] + self.labels = {} + self.changes = {} + self.status = {} + self.text = "" + self.x_coord = 0 + self.last_sp = 0 + self.max_stack_size = 0 + index = 0 + for line in iter(source_text.splitlines()): + index = self.processSourceLine(line, index) + + def getReg (self, reg_name): + return self.registers[reg_name] + + def setRegs (self, reg_dict): + for reg_name, reg_val in reg_dict.iteritems(): + self.registers[reg_name] = reg_val + + def getVal (self, valText): + if valText[0] == "$": + return int(valText[1:]) + elif valText[0] == "%": + return self.getReg(valText) + else: + raise "NOT A VALUE! UGHGUIHGUE!" + + def compareVal (self, valT1, valT2): + val1 = self.getVal(valT2) + val2 = self.getVal(valT1) + self.status = {"g":val1>val2} + + def writeRegs (self, line_nr=None): + self.text += self.draw((self.x_coord+1)*2.5-0.5, line_nr) + self.text += "\n" + self.x_coord += 1 + + def changedRegisters (self, *args): + for reg, val in args: + self.changes[reg] = val + + def iterate (self): + old_rip = self.registers['%rip'] + self.last_sp = self.registers['%rsp'] + #self.linesExec.append(old_rip) + if self.registers['%rip'] >= len(self.code): + return False + instruct = self.code[self.registers['%rip']] + opcode = instruct[0] + self.changes = {} + #print(opcode, instruct) + if opcode == "push" or opcode == "pushq": + self.registers['%rsp'] = self.registers['%rsp'] + 1 + self.stack[self.registers['%rsp']] = self.getReg(instruct[1]) + self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']),"insert")) + self.max_stack_size = max(self.registers['%rsp'], self.max_stack_size) + elif opcode == "popq": + self.registers[instruct[1]] = self.stack[self.registers['%rsp']] + self.registers['%rsp'] = self.registers['%rsp'] - 1 + self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']+1),"remove")) + elif opcode == "movq" or opcode == "mov": + self.changedRegisters((instruct[2],"change")) + self.registers[instruct[2]] = self.getVal(instruct[1]) + elif opcode == "cmpq": + self.compareVal(instruct[1],instruct[2]) + elif opcode == "addq": + self.changedRegisters((instruct[2],"change")) + self.registers[instruct[2]] = self.getVal(instruct[2]) + self.getVal(instruct[1]) + elif opcode == "subq": + self.changedRegisters((instruct[2],"change")) + self.registers[instruct[2]] = self.getVal(instruct[2]) - self.getVal(instruct[1]) + elif opcode == "jg": + if self.status["g"]: + self.registers['%rip'] = self.labels[instruct[1]] + elif opcode == "jmp": + self.registers['%rip'] = self.labels[instruct[1]] + + elif opcode == "call": + self.registers['%rsp'] = self.registers['%rsp'] + 1 + self.stack[self.registers['%rsp']] = self.registers['%rip']+1 + self.registers['%rip'] = self.labels[instruct[1]] + self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']),"insert")) + self.max_stack_size = max(self.registers['%rsp'], self.max_stack_size) + elif opcode == "leave": + self.registers["%rsp"] = self.registers["%rbp"] + self.registers["%rbp"] = self.stack[self.registers['%rsp']] + self.registers['%rsp'] = self.registers['%rsp'] - 1 + self.changedRegisters(('%rbp',"change")) + self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']+1),"remove")) + elif opcode == "ret": + self.registers['%rip'] = self.stack[self.registers['%rsp']] + self.registers['%rsp'] = self.registers['%rsp'] - 1 + self.changedRegisters(('%rsp',"change"),("m"+str(self.registers['%rsp']+1),"remove")) + + if self.registers['%rip'] == old_rip: + self.registers['%rip'] += 1 + else: + self.changedRegisters(('%rip',"change")) + + self.writeRegs(old_rip) + + def processSourceLine(self, line_text, index): + tokens = re.findall(r'[\w%:$#]+', line_text) + if len(tokens) <= 0: + return index + if tokens[0][-1] == ':': + self.labels[tokens[0][:-1]] = index + tokens = tokens[1:] + for i in range(len(tokens)): + if tokens[i][0] == "#": + tokens = tokens[:i] + break + self.code.append(tokens) + self.codep.append(line_text) + return index + 1 + + REG_STATUS_TO_COLOR = {"insert":"green","change":"yellow","remove":"red"} + + def getRegColor (self, reg_name): + if reg_name in self.changes: + return self.REG_STATUS_TO_COLOR[self.changes[reg_name]] + else: + return "black" + + def getLineSignature (self, line_nr): + return [ "\\"+token if token[0]=="$" or token[0]=="%" else token for token in self.code[line_nr] ] + + def draw (self, x, line_nr=None): + x = str(x) + text = "" + pos = 0 + for reg_name in REGISTERS: + reg_dect = "register_node, fill=" + self.getRegColor(reg_name)+"!10" + if reg_name != "": + text += "\t\\node["+reg_dect+"]("+reg_name[1:]+") at ("+x+", "+str(pos)+"){"+str(self.registers[reg_name])+"};\n" + pos -= 0.5 + + pos -= 1 + base_sp = max(self.last_sp,self.registers['%rsp']) + if self.last_sp - 1 == self.registers['%rsp']: + pos += 0.5 + for index in range(base_sp, -1,-1): + mem_name = "m"+str(index) + reg_dect = "register_node, fill=" + self.getRegColor(mem_name)+"!10" + text += "\t\\node["+reg_dect+"]("+mem_name+") at ("+x+", "+str(pos)+"){"+str(self.stack[index])+"};\n" + pos -= 0.5 + + if line_nr != None: + pos = 2 + signature = self.getLineSignature(line_nr) + try: + for i in range(3): + text += "\t\\node[text_node]() at ("+str(float(x)-1.25)+", "+str(pos)+"){"+signature[i]+"};\n" + pos -= 0.5 + except: + pass + #for line_nr in self.linesExec: + # text += "\t\\node[text_node]() at ("+x+", "+str(pos)+"){"+self.codep[line_nr]+"};\n" + self.linesExec = [] + + return text + + def drawNames (self, x, stack_size=8): + x = str(x) + text = "" + pos = 0 + for reg_name in REGISTERS: + text += "\t\\node[text_node]("+reg_name[1:]+") at ("+x+", "+str(pos)+"){"+reg_name[1:]+"};\n" + pos -= 0.5 + + text += "\t\\node[text_node]() at ("+x+", "+str(pos-0.5)+"){TOS+1};\n" + text += "\t\\node[text_node]() at ("+x+", "+str(pos-1)+"){TOS};\n" + pos -= 1.5 + for index in range(1,stack_size+1): + mem_name = "m"+str(index) + text += "\t\\node[text_node]() at ("+x+", "+str(pos)+"){TOS-"+str(index)+"};\n" + pos -= 0.5 + + return text + +fib_prog = """ +fib: push %rbp # push previous base pointer + movq %rsp,%rbp # setup new base pointer +if: cmpq $1, %rdi # if (a-1) > 0: goto long + jg long +quick: movq %rdi, %rax + jmp return +long: pushq %rdi # Push a onto the stack + subq $1, %rdi + call fib # Call fib(a-1) + pushq %rax # Push fib(a-1) onto the stack + subq $1, %rdi + call fib # Call fib(a-2) + popq %rdi # pop fib(a-1) into rdi + addq %rdi, %rax # rax = fib(a-1)+fib(a-2) + popq %rdi # Garentee that a lies in rdi. +return: leave # Clean up (stack pointers?) + ret # return m (rax) +""" + +fib_iter_prog = """ +fib: push %rbp # push previous base pointer + movq %rsp,%rbp # setup new base pointer + +if: cmpq $1, %rdi # if (a-1) > 0: goto loops + jg loops + +quick: movq %rdi, %rax + jmp return + +loops: movq %rdi, %r8 # i (r8) = a + movq $1, %rdi # minus_one (rdi) = 1 + movq $0, %rax # minus_two (rax) = 0 + +loopb: movq %rdi, %9 # r = minus_one + addq %rax, %rdi # minus_one += minus_two + movq %9, %rax # minus_two = r + subq $1, %r8 # i-- + cmpq $0, %r8 # if a > 0 + jg loopb + +return: leave # Clean up + ret # return m (rax) +""" + +if __name__ == "__main__": + try: + a = int(sys.argv[1]) + except: + a = 2 + new_em = Emulator(fib_iter_prog) + new_em.stack = ["junk...","calling eip"] + for i in range(100): + new_em.stack.append(0) + new_em.setRegs({'%rip':0,'%rbp':'old bp','%rsp':1,'%rdi':a}) + print("Running for fib("+str(a)+")") + new_em.writeRegs() + while True: + if new_em.iterate() == False: + break + new_em.text += new_em.drawNames(0, new_em.max_stack_size) + print("fib("+str(a)+") = "+str(new_em.registers["%rax"])) + with open("tikz.tex","w") as f: + f.write("\\documentclass{standalone}\n\n\usepackage{tikz}\\begin{document}\n") + f.write("\\begin{tikzpicture}["+OPTIONS+"]\n") + f.write(new_em.text) + f.write("\\end{tikzpicture}\n\\end{document}")