From d9e74401f377ec17a419fd6df16bf3f3c1817263 Mon Sep 17 00:00:00 2001
From: pompolic <pompolic@special-circumstanc.es>
Date: Thu, 17 Jun 2021 00:30:11 +0200
Subject: [PATCH] Rudimentary GUI

- left hand side shows the parser about to be invoked (top of current parser stack), as well as its address
- right hand side shows the current position in the input + the next 32 bytes
- step button executes hammer-parse-step 1

Start the GUI by sourcing gui.py after parser-name-instrumentation-gdb.py and utility-commands.py have been loaded
---
 gdb-port/gui.py                             | 55 +++++++++++++++++++++
 gdb-port/parser-name-instrumentation-gdb.py | 14 ++++++
 2 files changed, 69 insertions(+)
 create mode 100644 gdb-port/gui.py

diff --git a/gdb-port/gui.py b/gdb-port/gui.py
new file mode 100644
index 0000000..e680e9c
--- /dev/null
+++ b/gdb-port/gui.py
@@ -0,0 +1,55 @@
+from tkinter import *
+from tkinter import ttk
+
+class PresentationLayer():
+	def __init__(self, parser, name_var, address_var, input_chunk_var):
+		self.top_parser = parser
+		self.top_parser_name = name_var
+		self.top_parser_address = address_var
+		self.input_chunk = input_chunk_var
+
+	def set_top_parser(self,parser):
+		self.top_parser = parser
+		self.top_parser_name.set(self.top_parser.name)
+		self.top_parser_address.set(hex(self.top_parser.address))
+
+	def set_input_chunk(self, input_chunk):
+		self.input_chunk.set(input_chunk)
+
+def step(*args):
+	gdb.execute("hammer-parse-step 1")
+	presentation_layer.set_top_parser(top_level_parse.peek_parser())
+	presentation_layer.set_input_chunk(top_level_parse.get_input_chunk())
+
+if top_level_parse is None:
+	print("Please import parser name script")
+else:
+	root = Tk()
+	root.title("Parser visualization")
+
+	frame = ttk.Frame(root, padding="12 12 12 12")
+	frame.grid(column=0, row=0, sticky=(N, W, E, S))
+	root.columnconfigure(0, weight=1)
+	root.rowconfigure(0, weight=1)
+
+	parser = StringVar()
+	parser_addr = StringVar()
+	input_chunk = StringVar()
+	presentation_layer = PresentationLayer(top_level_parse.peek_parser(), parser, parser_addr, input_chunk)
+	presentation_layer.set_top_parser(top_level_parse.peek_parser())
+	presentation_layer.set_input_chunk(top_level_parse.get_input_chunk())
+	#presentation_layer.top_parser_name = parser
+	#presentation_layer.top_parser_addr = parser_addr
+	#presentation_layer.top_parser = top_level_parse.peek_parser()
+	#presentation_layer.set_top_parser(top_level_parse.peek_parser())
+
+	#presentation_layer.top_parser = top_level_parse.peek_parser()
+	ttk.Label(frame, textvariable=presentation_layer.top_parser_name).grid(column=1, row=1)
+	#ttk.Label(frame, text="Placeholder name").grid(column=1, row=1)
+	ttk.Label(frame, textvariable=presentation_layer.top_parser_address).grid(column=1, row=2)
+	#ttk.Label(frame, text="Placeholder address").grid(column=1, row=2)
+	ttk.Label(frame, textvariable=presentation_layer.input_chunk).grid(column=2, row=1)
+	ttk.Button(frame, text="Step", command=step).grid(column=1, row=3)
+
+	root.mainloop()
+
diff --git a/gdb-port/parser-name-instrumentation-gdb.py b/gdb-port/parser-name-instrumentation-gdb.py
index 782b4da..999a6da 100644
--- a/gdb-port/parser-name-instrumentation-gdb.py
+++ b/gdb-port/parser-name-instrumentation-gdb.py
@@ -112,6 +112,8 @@ class TopLevelParse:
 		self.parser_stacks = []
 		self.parser_objs = {}
 		self.unclaimed_mem_use = 0
+		# Holds 32 characters starting at state->input_stream[index], used by the GUI
+		self.current_input_chunk = ''
 
 	# Called from h_packrat_parse()'s handler, where the parse state and arena get initialized
 	def enter_h_packrat_parse(self, parser):
@@ -207,6 +209,12 @@ class TopLevelParse:
 		else:
 			return None
 
+	def set_input_chunk(self, chunk):
+		self.input_chunk = chunk
+
+	def get_input_chunk(self):
+		return self.input_chunk
+
 
 top_level_parse = TopLevelParse()
 # Approach 1: load the application, set breakpoints, execute stack commands on breakpoint hit, continue
@@ -239,13 +247,19 @@ class HDoParseBreakpoint(gdb.Breakpoint):
 			if val.name == 'state':
 				#TODO: rename these variables to make it clear they're pointers
 				state = int(val.value(frame))
+				state_obj = val.value(frame)
 				index = val.value(frame).dereference()['input_stream']['index']
+				input_ptr = val.value(frame).dereference()['input_stream']['input']
 				# If you want to printf debug the parse state
 				#print(val.value(frame).dereference())
 			if val.name == 'arena':
 				arena = int(val.value(frame))
 		top_level_parse.enter_h_do_parse(state, None, parser)
 
+		input_chunk = input_ptr + index
+		top_level_parse.set_input_chunk(input_chunk.string('UTF-8','replace',32))
+		#print(input_chunk.string('ascii','backslashreplace',10))
+
 
 		# Check if we need to stop after a number of steps
 		step_counter = gdb.convenience_variable("hammer_step_counter")
-- 
GitLab