Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[target-remote] Basic support for the target remote command #1020

Merged
merged 8 commits into from
Dec 16, 2023
6 changes: 4 additions & 2 deletions docs/commands/gef-remote.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ does a limited job (80's bandwith FTW) to collect more information about the tar
process of debugging more cumbersome. GEF greatly improves that state with the `gef-remote` command.

📝 **Note**: If using GEF, `gef-remote` **must** be your way or debugging remote processes, never
`target remote`. Maintainers will not provide support or help if you decide to use the traditional
`target remote` command. For many reasons, you **cannot** use `target remote` alone with GEF.
`target remote`. Maintainers will provide minimal support or help if you decide to use the
traditional `target remote` command. For many reasons, you **should not** use `target remote` alone
with GEF. It is still important to note that the default `target remote` command has been
overwritten by a minimal copy `gef-remote`, in order to make most tools relying on this command work.

`gef-remote` can function in 2 ways:

Expand Down
53 changes: 42 additions & 11 deletions gef.py
Original file line number Diff line number Diff line change
Expand Up @@ -6006,6 +6006,13 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None:
# This prevents some spurious errors being thrown during startup
gef.session.remote_initializing = True
gef.session.remote = GefRemoteSessionManager(args.host, args.port, args.pid, qemu_binary)

dbg(f"[remote] initializing remote session with {gef.session.remote.target} under {gef.session.remote.root}")
if not gef.session.remote.connect(args.pid):
raise EnvironmentError(f"Cannot connect to remote target {gef.session.remote.target}")
if not gef.session.remote.setup():
raise EnvironmentError(f"Failed to create a proper environment for {gef.session.remote.target}")

gef.session.remote_initializing = False
reset_all_caches()
gdb.execute("context")
Expand Down Expand Up @@ -9503,6 +9510,7 @@ def __init__(self) -> None:
gef.config["gef.readline_compat"] = GefSetting(False, bool, "Workaround for readline SOH/ETX issue (SEGV)")
gef.config["gef.debug"] = GefSetting(False, bool, "Enable debug mode for gef")
gef.config["gef.autosave_breakpoints_file"] = GefSetting("", str, "Automatically save and restore breakpoints")
gef.config["gef.disable_target_remote_overwrite"] = GefSetting(False, bool, "Disable the overwrite of `target remote`")
plugins_dir = GefSetting("", str, "Autoload additional GEF commands from external directory", hooks={"on_write": GefSetting.no_spaces})
plugins_dir.add_hook("on_write", lambda _: self.load_extra_plugins())
gef.config["gef.extra_plugins_dir"] = plugins_dir
Expand Down Expand Up @@ -10861,12 +10869,6 @@ def __init__(self, host: str, port: int, pid: int =-1, qemu: Optional[pathlib.Pa
self.__local_root_fd = tempfile.TemporaryDirectory()
self.__local_root_path = pathlib.Path(self.__local_root_fd.name)
self.__qemu = qemu
dbg(f"[remote] initializing remote session with {self.target} under {self.root}")
if not self.connect(pid):
raise EnvironmentError(f"Cannot connect to remote target {self.target}")
if not self.setup():
raise EnvironmentError(f"Failed to create a proper environment for {self.target}")
return

def close(self) -> None:
self.__local_root_fd.cleanup()
Expand Down Expand Up @@ -11136,6 +11138,14 @@ def reset_caches(self) -> None:
return


def target_remote_posthook():
if gef.session.remote_initializing:
return

gef.session.remote = GefRemoteSessionManager("", 0)
if not gef.session.remote.setup():
raise EnvironmentError(f"Failed to create a proper environment for {gef.session.remote}")

if __name__ == "__main__":
if sys.version_info[0] == 2:
err("GEF has dropped Python2 support for GDB when it reached EOL on 2020/01/01.")
Expand Down Expand Up @@ -11214,11 +11224,32 @@ def reset_caches(self) -> None:

GefTmuxSetup()

# `target remote` commands cannot be disabled, so print a warning message instead
errmsg = "Using `target remote` with GEF does not work, use `gef-remote` instead. You've been warned."
hook = f"""pi if calling_function() != "connect": err("{errmsg}")"""
gdb.execute(f"define target hook-remote\n{hook}\nend")
gdb.execute(f"define target hook-extended-remote\n{hook}\nend")

disable_tr_overwrite_setting = "gef.disable_target_remote_overwrite"

if not gef.config[disable_tr_overwrite_setting]:
warnmsg = ("Using `target remote` with GEF should work in most cases, "
"but use `gef-remote` if you can. You can disable the "
"overwrite of the `target remote` command by toggling "
f"`{disable_tr_overwrite_setting}` in the config.")
hook = f"""
define target hookpost-{{}}
pi target_remote_posthook()
context
pi if calling_function() != "connect": warn("{warnmsg}")
end
"""

# Register a post-hook for `target remote` that initialize the remote session
gdb.execute(hook.format("remote"))
gdb.execute(hook.format("extended-remote"))
else:
errmsg = ("Using `target remote` does not work, use `gef-remote` "
f"instead. You can toggle `{disable_tr_overwrite_setting}` "
"if this is not desired.")
hook = f"""pi if calling_function() != "connect": err("{errmsg}")"""
gdb.execute(f"define target hook-remote\n{hook}\nend")
gdb.execute(f"define target hook-extended-remote\n{hook}\nend")

# restore saved breakpoints (if any)
bkp_fpath = pathlib.Path(gef.config["gef.autosave_breakpoints_file"]).expanduser().absolute()
Expand Down
12 changes: 12 additions & 0 deletions tests/commands/gef_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,15 @@ def test_cmd_gef_remote_qemu_user(self):
self.assertIn(
f"RemoteSession(target='{GDBSERVER_PREFERED_HOST}:{port}', local='/tmp/", res)
self.assertIn(", qemu_user=True)", res)


def test_cmd_target_remote(self):
port = GDBSERVER_PREFERED_PORT + 3
before = [f"target remote {GDBSERVER_PREFERED_HOST}:{port}"]
with gdbserver_session(port=port) as _:
res = gdb_run_cmd(
"pi print(gef.session.remote)", before=before)
self.assertNoException(res)
self.assertIn(
f"RemoteSession(target=':0', local='/tmp/", res)
self.assertIn(", qemu_user=False)", res)
Loading