summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMalfurious <m@lfurio.us>2021-08-10 02:56:23 -0400
committerMalfurious <m@lfurio.us>2021-08-10 02:56:23 -0400
commit58f7de7652190912ec741366f1ee1415dbb77c5c (patch)
treea4e09fe4a43914900f88e42cdcb0379d10bba0b5
parent523791c15d7775f854a5bdea3463a11f14a85bfa (diff)
downloadlib-des-gnux-58f7de7652190912ec741366f1ee1415dbb77c5c.tar.gz
lib-des-gnux-58f7de7652190912ec741366f1ee1415dbb77c5c.zip
Commit brainfuck tools
The foremost tool in this collection is the brainfuck debugger. It was written to assist with the 'boring flag checker' problem from RaRCTF 2021, but has good potential for general-purpose use. The compiler and decompiler are much more niche, given brainfuck is not typically a compiled language. They are from the same CTF and, although highly problem-specific, are kept around for posterity. A hello world program is saved under templates as a quick sanity check for the tools as well as for reference purposes, should it become useful. Signed-off-by: Malfurious <m@lfurio.us>
-rw-r--r--templates/hello.bf1
-rwxr-xr-xtools/brainfuck/bf_compile.py30
-rwxr-xr-xtools/brainfuck/bf_debug.py374
-rwxr-xr-xtools/brainfuck/bf_decompile.py30
4 files changed, 435 insertions, 0 deletions
diff --git a/templates/hello.bf b/templates/hello.bf
new file mode 100644
index 0000000..11a66a2
--- /dev/null
+++ b/templates/hello.bf
@@ -0,0 +1 @@
+>++++++++[<+++++++++>-]<.>++++[<+++++++>-]<+.+++++++..+++.>>++++++[<+++++++>-]<++.------------.>++++++[<+++++++++>-]<+.<.+++.------.--------.>>>++++[<++++++++>-]<+.
diff --git a/tools/brainfuck/bf_compile.py b/tools/brainfuck/bf_compile.py
new file mode 100755
index 0000000..453ae77
--- /dev/null
+++ b/tools/brainfuck/bf_compile.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+import sys
+
+if len(sys.argv) < 2:
+ print("Give me filename...")
+ exit()
+
+bytecode = {
+ '>': b"\x00",
+ '<': b"\x02",
+ '+': b"\x07",
+ '-': b"\x06",
+ '.': b"\x05",
+ ',': b"\x04",
+ '[': b"\x03",
+ ']': b"\x01",
+}
+
+src_buff = open(sys.argv[1]).read()
+bin_file = open(sys.argv[1]+".bin", mode="wb")
+
+for c in src_buff:
+ try:
+ b = bytecode[c]
+ bin_file.write(b)
+ except KeyError:
+ # Ignore whitespace, etc.
+ pass
+
+bin_file.close()
diff --git a/tools/brainfuck/bf_debug.py b/tools/brainfuck/bf_debug.py
new file mode 100755
index 0000000..eee13e3
--- /dev/null
+++ b/tools/brainfuck/bf_debug.py
@@ -0,0 +1,374 @@
+#!/usr/bin/env python
+import sys
+
+if len(sys.argv) < 2:
+ print("Give me filename...")
+ exit()
+
+# Parameters
+TAPE_LEN = 264 # from original interpreter
+TAPE_CONTEXT = 14
+CODE_CONTEXT = 72
+
+process_input = (sys.argv[2].encode() if len(sys.argv) > 2 else b"")
+process_output = b""
+
+# Colors
+HEADER = '\033[95m'
+OKBLUE = '\033[94m'
+OKCYAN = '\033[96m'
+OKGREEN = '\033[92m'
+WARNING = '\033[93m'
+FAIL = '\033[91m'
+ENDC = '\033[0m'
+BOLD = '\033[1m'
+UNDERLINE = '\033[4m'
+
+code = open(sys.argv[1]).read().replace("\n", "").replace(" ", "")
+
+stack = [] # track loop execution
+tape = [0]*TAPE_LEN # process memory
+ptr = 0 # current tape position
+
+rip = 0 # next instruction to execute
+lip = 0 # previous instruction (illustrate jumps)
+
+breakpoints = [] # stop exec at these rips
+breakops = [] # stop exec at these codes
+watchpoints = [] # stop exce on these ptrs
+
+
+def display_status():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ print("")
+ print("")
+ print("Tape ptr: %i"%(ptr))
+ print("Prog rip: %i"%(rip))
+ print("Prog lip: %i"%(lip))
+ print("")
+
+ # stack
+ if len(stack) == 0:
+ print(f"{FAIL}No loops{ENDC}")
+ else:
+ for l in stack:
+ print("0x%05x"%(l), end=" ")
+ print("")
+ print("")
+
+ # tape
+ addr = ""
+ hexa = ""
+ deci = ""
+ asci = ""
+ for i in range(ptr - TAPE_CONTEXT, ptr + TAPE_CONTEXT + 1):
+ oob = i < 0 or i >= TAPE_LEN
+ color = (OKGREEN if i == ptr else WARNING if i in watchpoints else OKCYAN if tape[i] != 0 else FAIL if oob else '')
+ value = (0 if oob else tape[i])
+ char = (chr(value) if value >= 0x20 and value < 0x7f else "U")
+
+ addr += f" {color}{UNDERLINE}0x%02x{ENDC}"%(i)
+ hexa += f" {color}0x%02x{ENDC}"%(value)
+ deci += f" {color}%4i{ENDC}"%(value)
+ asci += f" {color}%4s{ENDC}"%(char)
+
+ print(addr)
+ print(hexa)
+ print(deci)
+ print(asci)
+ print("")
+
+ # code
+ breaks = ""
+ ops = ""
+ for i in range(rip - CODE_CONTEXT, rip + CODE_CONTEXT + 1):
+ oob = i < 0 or i >= len(code)
+ color = (OKGREEN+BOLD+UNDERLINE if i == rip
+ else OKBLUE+BOLD if i == lip
+ else FAIL+BOLD if oob else '')
+ oper = (" " if oob else code[i])
+ brek = (" " if oob else "P" if i in breakpoints else "O" if code[i] in breakops else " ")
+
+ breaks += f"{FAIL}{brek}{ENDC}"
+ ops += f"{color}{oper}{ENDC}"
+
+ print(breaks)
+ print(ops)
+
+def display_tape():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ print("")
+ print("")
+ for i, x in enumerate(tape):
+ color = (OKGREEN if i == ptr else WARNING if i in watchpoints else OKCYAN if x != 0 else '')
+ print(f"{color}0x%02x{ENDC} "%(x), end="")
+ if (i+1) % 16 == 0:
+ print("")
+ elif (i+1) % 8 == 0:
+ print(" ", end="")
+ print("")
+
+def display_tape_ascii():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ print("")
+ print("")
+ for i, x in enumerate(tape):
+ color = (OKGREEN if i == ptr else '')
+ char = (chr(x) if x >= 0x20 and x < 0x7f else '.')
+ print(f"{color}{char}{ENDC}", end="")
+ print("")
+
+def display_code():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ print("")
+ print("")
+ for i, x in enumerate(code):
+ color = (OKGREEN+BOLD+UNDERLINE if i == rip
+ else OKBLUE+BOLD if i == lip
+ else FAIL+BOLD if i in breakpoints
+ else FAIL+BOLD if code[i] in breakops else '')
+ print(f"{color}{x}{ENDC}", end="")
+ print("")
+
+def display_input():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ global process_input
+ print("")
+ print("")
+ print(f"Input (to-be-read): {process_input}")
+
+def display_output():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ global process_output
+ print("")
+ print("")
+ print(f"Output (cumulative): {process_output}")
+
+def jump_past_loop():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ i = 1
+ for pos in range(rip, len(code)):
+ if code[pos] == '[':
+ i += 1
+ elif code[pos] == ']':
+ i -= 1
+ if i == 0:
+ rip = pos + 1
+ break
+ if i != 0:
+ raise Exception("'[' has no paring ']'")
+
+def run_code():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ global process_input
+ global process_output
+ while True:
+ if step_forward() == True:
+ break
+ if rip in breakpoints:
+ break
+ if rip < len(code) and (code[rip] in breakops):
+ break
+ if ptr in watchpoints:
+ break
+
+def step_forward():
+ global code
+ global stack
+ global tape
+ global ptr
+ global rip
+ global lip
+ global process_input
+ global process_output
+ if rip >= len(code):
+ print("Execution finished.")
+ return True
+
+ op = code[rip]
+ lip = rip
+ rip += 1
+
+ if op == '<':
+ if ptr == 0:
+ raise Exception("Tape ptr out of bounds! (underflow)")
+ ptr -= 1
+ elif op == '>':
+ if ptr == TAPE_LEN-1:
+ raise Exception("Tap ptr out of bounds! (overflow)")
+ ptr += 1
+ elif op == '+':
+ if tape[ptr] == 255:
+ tape[ptr] = 0
+ else:
+ tape[ptr] += 1
+ elif op == '-':
+ if tape[ptr] == 0:
+ tape[ptr] = 255
+ else:
+ tape[ptr] -= 1
+ elif op == '.':
+ process_output += tape[ptr].to_bytes(1, 'big')
+ display_output()
+ elif op == ',':
+ if len(process_input) == 0:
+ raise Exception("Out of input!")
+ display_input()
+ tape[ptr] = process_input[0]
+ process_input = process_input[1:]
+ elif op == '[':
+ if tape[ptr] == 0:
+ jump_past_loop()
+ else:
+ stack.append(rip) # rip currently on first op after [
+ elif op == ']':
+ if tape[ptr] == 0:
+ stack.pop()
+ else:
+ rip = stack[-1]
+ else:
+ raise Exception("Illegal instruction!")
+
+def manage_breakpoints():
+ global breakpoints
+ while True:
+ print("Current breakpoints:")
+ for i, x in enumerate(breakpoints):
+ print(f" {i}: {x}")
+ print("")
+
+ ip = input("[a]dd/[d]el/[f]inish <arg>: ")
+ cmd = ip.split()[0]
+
+ if cmd == 'f':
+ break
+ if cmd == 'a':
+ breakpoints.append(int(ip.split()[1]))
+ elif cmd == 'd':
+ del breakpoints[int(ip.split()[1])]
+
+def manage_breakops():
+ global breakops
+ while True:
+ print("Current breakops:")
+ for i, x in enumerate(breakops):
+ print(f" {i}: {x}")
+ print("")
+
+ ip = input("[a]dd/[d]el/[f]inish <arg>: ")
+ cmd = ip.split()[0]
+
+ if cmd == 'f':
+ break
+ if cmd == 'a':
+ breakops.append(ip.split()[1])
+ elif cmd == 'd':
+ del breakops[int(ip.split()[1])]
+
+def manage_watchpoints():
+ global watchpoints
+ while True:
+ print("Current watchpoints:")
+ for i, x in enumerate(watchpoints):
+ print(f" {i}: {x}")
+ print("")
+
+ ip = input("[a]dd/[d]el/[f]inish <arg>: ")
+ cmd = ip.split()[0]
+
+ if cmd == 'f':
+ break
+ if cmd == 'a':
+ watchpoints.append(int(ip.split()[1]))
+ elif cmd == 'd':
+ del watchpoints[int(ip.split()[1])]
+
+def write_tape():
+ global tape
+ addr = int(input("Address: "))
+ val = int(input("Value: "))
+ tape[addr] = val
+
+
+enter_is_step = False
+print("Hints: j: step forward t: show tape a: show tape (ascii) c: show code i: show input o: show output")
+print(" r: run p: manage breakpoints l: manage breakops w: manage watchpoints q: quit s: show status")
+print(" m: edit tape memory")
+display_status()
+while True:
+ c = input("> ")
+
+ if c == 'j' or (c == '' and enter_is_step):
+ enter_is_step = True
+ step_forward()
+ display_status()
+ else:
+ enter_is_step = False
+ if c == 's':
+ display_status()
+ elif c == 't':
+ display_tape()
+ elif c == 'a':
+ display_tape_ascii()
+ elif c == 'c':
+ display_code()
+ elif c == 'i':
+ display_input()
+ elif c == 'o':
+ display_output()
+ elif c == 'q':
+ break
+ elif c == 'r':
+ run_code()
+ display_status()
+ elif c == 'p':
+ manage_breakpoints()
+ display_status()
+ elif c == 'l':
+ manage_breakops()
+ display_status()
+ elif c == 'w':
+ manage_watchpoints()
+ display_status()
+ elif c == 'm':
+ write_tape()
diff --git a/tools/brainfuck/bf_decompile.py b/tools/brainfuck/bf_decompile.py
new file mode 100755
index 0000000..8caa97d
--- /dev/null
+++ b/tools/brainfuck/bf_decompile.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+import sys
+
+if len(sys.argv) < 2:
+ print("Give me filename...")
+ exit()
+
+bytecode = {
+ 0: '>',
+ 2: '<',
+ 7: '+',
+ 6: '-',
+ 5: '.',
+ 4: ',',
+ 3: '[',
+ 1: ']',
+}
+
+bin_buff = open(sys.argv[1], mode="rb").read()
+src_file = open(sys.argv[1]+".bf", mode="w")
+
+for b in bin_buff:
+ try:
+ c = bytecode[b & 0x07]
+ src_file.write(c)
+ except:
+ print("Error while processing file (aborting...)")
+ exit()
+
+src_file.close()