You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pwndbg/pwndbg/ghidra.py

105 lines
3.8 KiB
Python

from __future__ import annotations
import os
import gdb
import pwndbg.color.context as C
import pwndbg.color.syntax_highlight as H
import pwndbg.gdblib.regs
import pwndbg.radare2
import pwndbg.rizin
from pwndbg.color import message
r2decompiler = pwndbg.config.add_param(
"r2decompiler", "radare2", "framework that your ghidra plugin installed (radare2/rizin)"
)
@pwndbg.config.trigger(r2decompiler)
def set_r2decompiler() -> None:
if r2decompiler.value in ["radare2", "rizin"]:
return
print(
message.warn(
f"Invalid r2decompiler: `{r2decompiler.value}`, please select from radare2 or rizin"
)
)
r2decompiler.revert_default()
def decompile(func=None):
"""
Return the source of the given function decompiled by ghidra.
If no function is given, decompile the function within the current pc.
This function requires radare2, r2pipe and r2ghidra, or their related rizin counterparts.
Raises Exception if any fatal error occurs.
"""
try:
if r2decompiler == "radare2":
r2 = pwndbg.radare2.r2pipe()
# 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")
elif r2decompiler == "rizin":
r2 = pwndbg.rizin.rzpipe()
# Lc -> list core plugins
if "ghidra" not in r2.cmd("Lc"):
raise Exception("rizin plugin rzghidra must be installed and available from rz")
else:
raise Exception(f"{r2decompiler} not support. Plz select from (radare2/rizin)")
except ImportError:
raise Exception("r2pipe or rzpipe not available, but required for r2/rz->ghidra bridge")
if not func:
func = (
hex(pwndbg.gdblib.regs[pwndbg.gdblib.regs.current.pc])
if pwndbg.gdblib.proc.alive
else "main"
)
src = r2.cmdj("pdgj @ " + func)
if not src:
raise Exception(f"Decompile command failed, check if '{func}' is a valid target")
current_line_marker = "/*%%PWNDBG_CODE_MARKER%%*/"
source = src.get("code", "")
# If not running there is no current pc to mark
if pwndbg.gdblib.proc.alive:
pc = pwndbg.gdblib.regs[pwndbg.gdblib.regs.current.pc]
closest = 0
for off in (a.get("offset", 0) for a in src.get("annotations", [])):
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"],
)
# 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")
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)
if pwndbg.config.syntax_highlight:
# highlighting depends on the file extension to guess the language, so try to get one...
src_filename = pwndbg.gdblib.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
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)
return source