diff --git a/docs/commands/gef-remote.md b/docs/commands/gef-remote.md index c0f16713e..fc9ab0a32 100644 --- a/docs/commands/gef-remote.md +++ b/docs/commands/gef-remote.md @@ -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: diff --git a/gef.py b/gef.py index 7782d2e35..dd10a526e 100644 --- a/gef.py +++ b/gef.py @@ -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") @@ -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 @@ -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() @@ -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.") @@ -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() diff --git a/tests/commands/gef_remote.py b/tests/commands/gef_remote.py index e46eb94ec..7c628cccc 100644 --- a/tests/commands/gef_remote.py +++ b/tests/commands/gef_remote.py @@ -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)