chore(ghidra): simplify logic and clean up code flow

Exception driven code flow for expected code paths is not great for
readability and suffers some performance degeneration that can be
circumvented with conditional checks.

Use exceptions exclusively for fatal failure handling and either return
a simple string from the decompile function or throw an exception.
pull/901/head
anthraxx 5 years ago committed by Disconnect3d
parent 707fe12e3d
commit 87bf6ac0f9

@ -224,14 +224,16 @@ def context_ghidra(target=sys.stdout, with_banner=True, width=None):
if config_context_ghidra == "never": if config_context_ghidra == "never":
return [] return []
elif config_context_ghidra == "if-no-source":
try:
with open(gdb.selected_frame().find_sal().symtab.fullname()) as _:
return [] # return nothing if we found the source
except Exception: # a lot can go wrong in search of source code.
pass # we don't care what, just that it did not work out well...
return banner + pwndbg.ghidra.decompile() if config_context_ghidra == "if-no-source":
source_filename = pwndbg.symbol.selected_frame_source_absolute_filename()
if source_filename and os.path.exists(source_filename):
return []
try:
return banner + pwndbg.ghidra.decompile().split('\n')
except Exception as e:
return banner + [message.error(e)]

@ -12,4 +12,7 @@ parser.add_argument("func", type=str, default=None, nargs="?", help="Function to
@pwndbg.commands.OnlyWithFile @pwndbg.commands.OnlyWithFile
@pwndbg.commands.ArgparsedCommand(parser) @pwndbg.commands.ArgparsedCommand(parser)
def ghidra(func): def ghidra(func):
print("\n".join(pwndbg.ghidra.decompile(func))) try:
print(pwndbg.ghidra.decompile(func))
except Exception as e:
print(message.error(e))

@ -19,58 +19,52 @@ def decompile(func=None):
""" """
try: try:
r2 = pwndbg.radare2.r2pipe() r2 = pwndbg.radare2.r2pipe()
# LD list supported decompilers (e cmd.pdc=?) except ImportError:
# Outputs for example:: pdc\npdg raise Exception('r2pipe not available, but required for r2->ghidra bridge')
if not "pdg" in r2.cmd("LD").split("\n"):
return ["radare2 plugin r2ghidra-dec must be installed and available from r2"] # LD -> list supported decompilers (e cmd.pdc=?)
except ImportError: # no r2pipe present # Outputs for example: pdc\npdg
return ["r2pipe not available, but required for r2->ghidra-bridge"] if "pdg" not in r2.cmd("LD").split("\n"):
if func is None: raise Exception('radare2 plugin r2ghidra must be installed and available from r2')
try:
func = hex(pwndbg.regs[pwndbg.regs.current.pc]) if not func:
except: func = hex(pwndbg.regs[pwndbg.regs.current.pc]) if pwndbg.proc.alive else 'main'
func = "main"
src = r2.cmdj("pdgj @" + func) src = r2.cmdj("pdgj @" + func)
# Early exit if decompile command failed horribly, like unknown addr/func
if not src: if not src:
return [] raise Exception("Decompile command failed, check if '{}' is a valid target".format(func))
current_line_marker = '/*%%PWNDBG_CODE_MARKER%%*/'
source = src.get("code", "") source = src.get("code", "")
curline = None
try: # If not running there is no current pc to mark
cur = pwndbg.regs[pwndbg.regs.current.pc] if pwndbg.proc.alive:
except AttributeError: pc = pwndbg.regs[pwndbg.regs.current.pc]
cur = None # If not running there is no current.pc
if cur is not None:
closest = 0 closest = 0
for off in (a.get("offset", 0) for a in src.get("annotations", [])): for off in (a.get("offset", 0) for a in src.get("annotations", [])):
if abs(cur - closest) > abs(cur - off): if abs(pc - closest) > abs(pc - off):
closest = off closest = off
pos_annotations = sorted([a for a in src.get("annotations", []) if a.get("offset") == closest], pos_annotations = sorted([a for a in src.get("annotations", []) if a.get("offset") == closest], key=lambda a: a["start"])
key=lambda a: a["start"])
# Append code prefix marker for the current line and replace it later
if pos_annotations: if pos_annotations:
curline = source.count("\n", 0, pos_annotations[0]["start"]) curline = source.count("\n", 0, pos_annotations[0]["start"])
source = source.split("\n") source = source.split("\n")
line = source[curline]
if line.startswith(' '):
line = line[min(4, len(pwndbg.config.code_prefix) + 1):]
source[curline] = current_line_marker + ' ' + line
source = "\n".join(source)
# Append code prefix marker for the current line and replace it later
current_line_marker = '/*%%PWNDBG_CODE_MARKER%%*/'
if curline is not None:
line = source[curline]
if line.startswith(' '):
line = line[min(4, len(pwndbg.config.code_prefix) + 1):]
source[curline] = current_line_marker + ' ' + line
# Join the source for highlighting
source = "\n".join(source)
if pwndbg.config.syntax_highlight: if pwndbg.config.syntax_highlight:
# highlighting depends on the file extension to guess the language, so try to get one... # highlighting depends on the file extension to guess the language, so try to get one...
try: # try to read the source filename from debug information src_filename = pwndbg.symbol.selected_frame_source_absolute_filename()
src_filename = gdb.selected_frame().find_sal().symtab.fullname() if not src_filename:
except: # if non, take the original filename and maybe append .c (just assuming is was c)
filename = gdb.current_progspace().filename filename = gdb.current_progspace().filename
src_filename = filename+".c" if os.path.basename(filename).find(".") < 0 else filename src_filename = filename + ".c" if os.path.basename(filename).find(".") < 0 else filename
source = H.syntax_highlight(source, src_filename) source = H.syntax_highlight(source, src_filename)
# Replace code prefix marker after syntax highlighting # Replace code prefix marker after syntax highlighting
source = source.replace(current_line_marker, C.prefix(pwndbg.config.code_prefix), 1) source = source.replace(current_line_marker, C.prefix(pwndbg.config.code_prefix), 1)
source = source.split("\n")
return source return source

@ -262,5 +262,33 @@ def add_main_exe_to_symbols():
except gdb.error: except gdb.error:
pass pass
@pwndbg.memoize.reset_on_stop
@pwndbg.memoize.reset_on_start
def selected_frame_source_absolute_filename():
"""
Retrieve the symbol tables source absolute file name from the selected frame.
In case of missing symbol table or frame information, None is returned.
"""
try:
frame = gdb.selected_frame()
except gdb.error:
return None
if not frame:
return None
sal = frame.find_sal()
if not sal:
return None
symtab = sal.symtab
if not symtab:
return None
return symtab.fullname()
if '/usr/lib/debug' not in get_directory(): if '/usr/lib/debug' not in get_directory():
set_directory(get_directory() + ':/usr/lib/debug') set_directory(get_directory() + ':/usr/lib/debug')

Loading…
Cancel
Save