# Writeup:We are given some documentation and files related to a made up language called "seventeen".## seventeen.txt:```= Overview of seventeen =** CONFIDENTIAL: Please do not talk about seventeen with non-Evil Robot Corp. employees **
Seventeen is a very simple programming language. It gets its name from the factthat there are only 17 instructions.
Seventeen is used for internal cryptography applications. The seventeen core runtimehas been proven to be secure (see seventeen.coq). In the future, we plan to expandthis language and build commercial products, so please keep all this confidential.
Here are some of the core concepts:
* Each program starts execution with the first instruction in the file, and keeps  processing instructions sequentially unless an IFZ, IFG, CALL, JMP or EXIT  instruction is encountered.
* There are three storage systems:  - a stack (where data can be pushed)  - a vector (where data can be read or written using an offset)  - variables
  The state of all these storage systems is undefined when a program starts.
* Most operations work with the stack. E.g. "1 2 ADD" will:  - push 1 on the stack  - push 2 on the stack  - Replace these two numbers with their sum
  The resulting stack will contain 3.
* Variables and labels are in the "a-z_" range ('a' to 'z' with '_').Make sure to read the formal specification (see seventeen.spec)= Sample code =* see primes.17
```## .bash_history:```lsecho "root" | /usr/bin/seventeen passmgr.17mysql --user=root --password="8a30347d2a" secure.evil-robot.corpecho "150" | seventeen primes.17 > primes.outexit```Besides the partial documentation and usage examples, we are given two "executables".
One of them is a program that prints primes, which is used to understand how the language works, and the other one is a program that is hard to reverse, and we need its output on the string "sandflea".
By reading the code and the partial documention, we can infer that most of the operations get arguments from the stack, then push the result into the stack.
The following python code runs files written in the "seventeen" language.
After cleaning the file a bit, it evaluates the custom functions and their line number, adding them to a dictionary (for example: {"loop_start:",5}).
Then, it interprets words in each line by the order they appear. The following words are possible:
* A "seventeen" syscall - meaning, one of the built in functions in the "seventeen" language that we need to emulate. We will execute the syscall.
* A number - self explanatory, just push it.
* A custom function - we will push the function name onto the stack. If needed, a syscall will set our instruction pointer to the line accompanying the function.
* A known variable - a variable which already has a value (via the store syscall).
* An unknown variable - a word that doesn't describe any of the above.
Since some syscalls refer to a variables value, and some to its symbol, we push a tuple ('varname', value) onto the stack, and let the syscalls use the relevant value.
## interpreter.py:```import sys,reimport syscalls
debug = 0 			#set to 1 to enable. [P?]=Pushing onto stack var of type "?". [S] executing a seventeen syscall.ip = 0 				#instruction pointer (line number)stack = []functions = {}variables = {}vect = [None] * 4096
def clean(data):	#deleteComments	data = re.sub(r"\/\*(.|\n)*?\*\/", "", data)	#deleteEmptyLines	data = "\n".join([ll.rstrip() for ll in data.splitlines() if ll.strip()])	return data
def parseFunctions(data):	for i,line in enumerate(data.splitlines()):		if re.search(r"[a-z_]*:", line) != None:			functions[re.search(r"[a-z_]*:", line).group(0)[:-1]] = i
def parseFile(f):	data = f.read()	data = clean(data)	parseFunctions(data)	return data
def executeWord(word):	global stack,variables,vect,ip	if word in syscalls.__dict__: #word is a builtin seventeen function		sys.stdout.write(("[S]Syscall: " + word + "\n") * debug)		stack,variables,vect,ip = syscalls.__dict__[word](stack,variables,vect,functions,ip)		return	if word.isdigit(): #word is a number, push it		sys.stdout.write(("[PN]Number: " + word + "\n") * debug)		stack.append(int(word))		return	if word[:-1] in functions: #word is function definition. Skip it.		return	if word in functions: #custom function name		sys.stdout.write(("[PF]Func: " + word + "\n") * debug)		stack.append(word)		return	if word in variables: #var-val tuple		sys.stdout.write(("[PV]var-val: " + str((word,variables[word])) + "\n") * debug)		stack.append((word,variables[word]))		return	else: #unknown variable, probably pushed to store		sys.stdout.write(("[PUV]var: " + word + "\n") * debug)		stack.append(word)		return
def executeLine(line):	global ip	curr_line_num = ip	for word in line.split():		executeWord(word)	if curr_line_num == ip:		ip = curr_line_num+1
def run(data):	global ip	lines = data.splitlines()	line = lines[0]	while True:		executeLine(line)		line = lines[ip]
if len(sys.argv) != 2:	print "Usage: %s [filename]" % sys.argv[0]	sys.exit(-1)
f = open(sys.argv[1],'r')run(parseFile(f))```
## syscalls.py:```import sys
#pushes a number from stdin onto the stackdef read_num(stack,variables,vect,functions,ip):	stack.append(int(raw_input()))	return (stack,variables,vect,ip)
#pushes a byte (as a number) from stdin onto the stackdef read_byte(stack,variables,vect,functions,ip):	char = sys.stdin.read(1)	if char == "": #ENDOFOUTPUT		stack.append(0)	else:		stack.append(ord(char))	return (stack,variables,vect,ip)
#input:anything#duplicates the top of the stackdef dup(stack,variables,vect,functions,ip):	stack.append(stack[-1])	return (stack,variables,vect,ip)	
#input:(value(int)/(str,int))#input:(varname(str)/(str,int))#stores value in varnamedef store(stack,variables,vect,functions,ip):	varname = stack.pop()	value = stack.pop()	if (type(varname) == tuple):		varname = varname[0]	if (type(value) == tuple):		value = value[1]	assert(type(varname) == str and type(value) == int)	variables[varname] = value	return (stack,variables,vect,ip)
#input:(value(int)/variable(tuple))#input:(function)#input:(function)#conditional jump (ifzero)def ifz(stack,variables,vect,functions,ip):	f1 = stack.pop()	f2 = stack.pop()	value = stack.pop()	if (type(value) == tuple): #variable		value = value[1]	assert(f1 in functions and f2 in functions and type(value) == int)	if value != 0:		ip = functions[f1]	else:		ip = functions[f2]	return (stack,variables,vect,ip)
#input:(value(int)/variable(tuple))#input:(function)#input:(function)#conditional jump (ifgreaterthanzero)def ifg(stack,variables,vect,functions,ip):	f1 = stack.pop()	f2 = stack.pop()	value = stack.pop()	if (type(value) == tuple): #variable		value = value[1]	assert(f1 in functions and f2 in functions and type(value) == int)	if value < 0:		ip = functions[f1]	else:		ip = functions[f2]	return (stack,variables,vect,ip)
#input:(function)#pushes return address and jumps to the function line numberdef call(stack,variables,vect,functions,ip):	f = stack.pop()	assert(f in functions)	stack.append(ip+1)	ip = functions[f]	return (stack,variables,vect,ip)
#input:(int)#input:(int)#pushes a1 + a2def add(stack,variables,vect,functions,ip):	a1 = stack.pop()	a2 = stack.pop()	if (type(a1) == tuple): #variable		a1 = a1[1]	if (type(a2) == tuple): #variable		a2 = a2[1]	assert(type(a1) == int and type(a2) == int)	stack.append(a1+a2)	return (stack,variables,vect,ip)
#input:(int)#input:(int)#pushes a2-a1def sub(stack,variables,vect,functions,ip):	a1 = stack.pop()	a2 = stack.pop()	if (type(a1) == tuple): #variable		a1 = a1[1]	if (type(a2) == tuple): #variable		a2 = a2[1]	assert(type(a1) == int and type(a2) == int)	stack.append(a2-a1)	return (stack,variables,vect,ip)
#input:(int)#input:(int)#pushes a2 mod a1def mod(stack,variables,vect,functions,ip):	a1 = stack.pop()	a2 = stack.pop()	if (type(a1) == tuple): #variable		a1 = a1[1]	if (type(a2) == tuple): #variable		a2 = a2[1]	assert(type(a1) == int and type(a2) == int)	stack.append(a2%a1)	return (stack,variables,vect,ip)
#input:(int)#input:(int)#pushes a2 xor a1def xor(stack,variables,vect,functions,ip):	a1 = stack.pop()	a2 = stack.pop()	if (type(a1) == tuple): #variable		a1 = a1[1]	if (type(a2) == tuple): #variable		a2 = a2[1]	assert(type(a1) == int and type(a2) == int)	stack.append(a2^a1)	return (stack,variables,vect,ip)
#input:(int)#prints the bytedef print_byte(stack,variables,vect,functions,ip):	b = stack.pop()	assert(type(b) == int)	sys.stdout.write(chr(b))	return (stack,variables,vect,ip)
#input:(int)#prints the numberdef print_num(stack,variables,vect,functions,ip):	n = stack.pop()	if (type(n) == tuple): #variable		n = n[1]	assert(type(n) == int)	sys.stdout.write(str(n))	return (stack,variables,vect,ip)
#input:(int)/(function)#jumps to line in inputdef jump(stack,variables,vect,functions,ip):	addr = stack.pop()	if (type(addr) == tuple): #variable		addr = addr[1]	if (type(addr) == str): #function		addr = functions[addr]	assert(type(addr) == int)	ip = addr	return (stack,variables,vect,ip)
#input:(index(int)/(str,int))#input:(value(int)/(str,int))def vstore(stack,variables,vect,functions,ip):	value = stack.pop()	index = stack.pop()	if (type(index) == tuple):		index = index[1]	if (type(value) == tuple):		value = value[1]	assert(type(index) == int and type(value) == int)	vect[index] = value	return (stack,variables,vect,ip)
#input:(index(int)/(str,int))def vload(stack,variables,vect,functions,ip):	index = stack.pop()	if (type(index) == tuple):		index = index[1]	assert(type(index) == int)	stack.append(vect[index])	return (stack,variables,vect,ip)
def exit(stack,variables,vect,functions,ip):	print "Done!"	sys.exit(0)```