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":
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.ArgparsedCommand(parser)
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:
r2 = pwndbg.radare2.r2pipe()
# LD list supported decompilers (e cmd.pdc=?)
# Outputs for example:: pdc\npdg
if not "pdg" in r2.cmd("LD").split("\n"):
return ["radare2 plugin r2ghidra-dec must be installed and available from r2"]
except ImportError: # no r2pipe present
return ["r2pipe not available, but required for r2->ghidra-bridge"]
if func is None:
try:
func = hex(pwndbg.regs[pwndbg.regs.current.pc])
except:
func = "main"
except ImportError:
raise Exception('r2pipe not available, but required for r2->ghidra bridge')
# LD -> list supported decompilers (e cmd.pdc=?)
# Outputs for example: pdc\npdg
if "pdg" not in r2.cmd("LD").split("\n"):
raise Exception('radare2 plugin r2ghidra must be installed and available from r2')
if not func:
func = hex(pwndbg.regs[pwndbg.regs.current.pc]) if pwndbg.proc.alive else 'main'
src = r2.cmdj("pdgj @" + func)
# Early exit if decompile command failed horribly, like unknown addr/func
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", "")
curline = None
try:
cur = pwndbg.regs[pwndbg.regs.current.pc]
except AttributeError:
cur = None # If not running there is no current.pc
if cur is not None:
# If not running there is no current pc to mark
if pwndbg.proc.alive:
pc = pwndbg.regs[pwndbg.regs.current.pc]
closest = 0
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
pos_annotations = sorted([a for a in src.get("annotations", []) if a.get("offset") == closest],
key=lambda a: a["start"])
pos_annotations = sorted([a for a in src.get("annotations", []) if a.get("offset") == closest], key=lambda a: a["start"])
# Append code prefix marker for the current line and replace it later
if pos_annotations:
curline = source.count("\n", 0, pos_annotations[0]["start"])
source = source.split("\n")
# 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:
# 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 = gdb.selected_frame().find_sal().symtab.fullname()
except: # if non, take the original filename and maybe append .c (just assuming is was c)
src_filename = pwndbg.symbol.selected_frame_source_absolute_filename()
if not src_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)
# Replace code prefix marker after syntax highlighting
source = source.replace(current_line_marker, C.prefix(pwndbg.config.code_prefix), 1)
source = source.split("\n")
return source

@ -262,5 +262,33 @@ def add_main_exe_to_symbols():
except gdb.error:
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():
set_directory(get_directory() + ':/usr/lib/debug')

Loading…
Cancel
Save