From eac7308cc02c39115ee9fc4a8954f0e4edff513c Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Fri, 12 Jan 2024 20:55:17 +0100 Subject: [PATCH 01/11] Catch fetch error to handle it correctly --- gef.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 3300b1c85..f2f16a674 100644 --- a/gef.py +++ b/gef.py @@ -11056,7 +11056,12 @@ def sync(self, src: str, dst: Optional[str] = None) -> bool: return True tgt.parent.mkdir(parents=True, exist_ok=True) dbg(f"[remote] downloading '{src}' -> '{tgt}'") - gdb.execute(f"remote get '{src}' '{tgt.absolute()}'") + + try: + gdb.execute(f"remote get '{src}' '{tgt.absolute()}'") + except gdb.error: + return False + return tgt.exists() def connect(self, pid: int) -> bool: From b694a81d7ad5611907c6e76dc5157c6ad70866da Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Fri, 12 Jan 2024 21:13:51 +0100 Subject: [PATCH 02/11] Check if setup of qemu/remote worked before continuing setting up the env --- gef.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gef.py b/gef.py index f2f16a674..338fbff5d 100644 --- a/gef.py +++ b/gef.py @@ -11092,10 +11092,12 @@ def setup(self) -> bool: # setup remote adequately depending on remote or qemu mode if self.in_qemu_user(): dbg(f"Setting up as qemu session, target={self.__qemu}") - self.__setup_qemu() + if not self.__setup_qemu(): + return False else: dbg(f"Setting up as remote session") - self.__setup_remote() + if not self.__setup_remote(): + return False # refresh gef to consider the binary reset_all_caches() From b36d9072dd9e638d2f1e4d48e5b0bcf693237f8b Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Fri, 12 Jan 2024 21:19:52 +0100 Subject: [PATCH 03/11] Better error handling in calling functions --- gef.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/gef.py b/gef.py index 338fbff5d..420fc301b 100644 --- a/gef.py +++ b/gef.py @@ -6045,12 +6045,12 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: dbg(f"[remote] initializing remote session with {gef.session.remote.target} under {gef.session.remote.root}") if not gef.session.remote.connect(args.pid): + gef.session.remote_initializing = False raise EnvironmentError(f"Cannot connect to remote target {gef.session.remote.target}") if not gef.session.remote.setup(): + gef.session.remote_initializing = False 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") return @@ -11288,7 +11288,10 @@ def target_remote_posthook(): gef.session.remote = GefRemoteSessionManager("", 0) if not gef.session.remote.setup(): - raise EnvironmentError(f"Failed to create a proper environment for {gef.session.remote}") + warn(f"Failed to create a proper environment for {gef.session.remote}") + return False + + return True if __name__ == "__main__": if sys.version_info[0] == 2: @@ -11379,8 +11382,7 @@ def target_remote_posthook(): f"`{disable_tr_overwrite_setting}` in the config.") hook = f""" define target hookpost-{{}} - pi target_remote_posthook() - context + pi if target_remote_posthook(): gdb.execute("context") pi if calling_function() != "connect": warn("{warnmsg}") end """ From 68036b26288dad362bec910974323c60aa543652 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sat, 13 Jan 2024 00:07:34 +0100 Subject: [PATCH 04/11] Try your best in initializing the env :) In case of failure, we still want to initialize every possible thing to still provide the best user experience as possible. The only consequences of an early return here is to make everything un-initialized, even if we could init other things after the return --- gef.py | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/gef.py b/gef.py index 420fc301b..f1375dfac 100644 --- a/gef.py +++ b/gef.py @@ -11090,20 +11090,26 @@ def connect(self, pid: int) -> bool: def setup(self) -> bool: # setup remote adequately depending on remote or qemu mode + success = True + if self.in_qemu_user(): dbg(f"Setting up as qemu session, target={self.__qemu}") if not self.__setup_qemu(): - return False + success = False else: dbg(f"Setting up as remote session") if not self.__setup_remote(): - return False + success = False # refresh gef to consider the binary reset_all_caches() - gef.binary = Elf(self.lfile) + + print(self.file) + if self.file.exists(): + gef.binary = Elf(self.lfile) + reset_architecture() - return True + return success def __setup_qemu(self) -> bool: # setup emulated file in the chroot @@ -11140,27 +11146,32 @@ def __setup_qemu(self) -> bool: return True def __setup_remote(self) -> bool: + success = True + # get the file - fpath = f"/proc/{self.pid}/exe" - if not self.sync(fpath, str(self.file)): - err(f"'{fpath}' could not be fetched on the remote system.") - return False + if not self.sync(str(self.file)): + warn(f"'{fpath}' could not be fetched on the remote system.") + success = False - # pseudo procfs - for _file in ("maps", "environ", "cmdline"): - fpath = f"/proc/{self.pid}/{_file}" - if not self.sync(fpath): - err(f"'{fpath}' could not be fetched on the remote system.") - return False + if not self.sync(f"/proc/{self.pid}/maps"): + warn(f"'{fpath}' could not be fetched on the remote system.") + success = False - # makeup a fake mem mapping in case we failed to retrieve it - maps = self.root / f"proc/{self.pid}/maps" - if not maps.exists(): + # makeup a fake mem mapping in case we failed to retrieve it + maps = self.root / f"proc/{self.pid}/maps" with maps.open("w") as fd: fname = self.file.absolute() mem_range = "00000000-ffffffff" if is_32bit() else "0000000000000000-ffffffffffffffff" fd.write(f"{mem_range} rwxp 00000000 00:00 0 {fname}\n") - return True + + # pseudo procfs + for _file in ("environ", "cmdline"): + fpath = f"/proc/{self.pid}/{_file}" + if not self.sync(fpath): + warn(f"'{fpath}' could not be fetched on the remote system.") + success = False + + return success def remote_objfile_event_handler(self, evt: "gdb.NewObjFileEvent") -> None: dbg(f"[remote] in remote_objfile_handler({evt.new_objfile.filename if evt else 'None'}))") From 54c818b6d79275fe9fc12b2a51b5c2adfd191f11 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sat, 13 Jan 2024 00:15:17 +0100 Subject: [PATCH 05/11] Remove debug print --- gef.py | 1 - 1 file changed, 1 deletion(-) diff --git a/gef.py b/gef.py index f1375dfac..9f45b7290 100644 --- a/gef.py +++ b/gef.py @@ -11104,7 +11104,6 @@ def setup(self) -> bool: # refresh gef to consider the binary reset_all_caches() - print(self.file) if self.file.exists(): gef.binary = Elf(self.lfile) From 129f8ab7bbbe6a3e20b04a9e91fdf826cebbde05 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sat, 13 Jan 2024 01:15:44 +0100 Subject: [PATCH 06/11] Resync maps at each stops --- gef.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gef.py b/gef.py index 9f45b7290..6be34451f 100644 --- a/gef.py +++ b/gef.py @@ -3551,6 +3551,10 @@ def continue_handler(_: "gdb.ContinueEvent") -> None: def hook_stop_handler(_: "gdb.StopEvent") -> None: """GDB event handler for stop cases.""" + if gef.session.remote: + gef.session.remote.maps.unlink() + gef.session.remote.sync(f"/proc/{gef.session.remote.pid}/maps") + reset_all_caches() gdb.execute("context") return From cec56e116d61912927c35d740155ffe48e31d59d Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Wed, 31 Jan 2024 00:12:32 +0100 Subject: [PATCH 07/11] Fix https://github.com/hugsy/gef/pull/1045#discussion_r1451639217 --- gef.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/gef.py b/gef.py index 6be34451f..c03d0a8af 100644 --- a/gef.py +++ b/gef.py @@ -6049,10 +6049,10 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: dbg(f"[remote] initializing remote session with {gef.session.remote.target} under {gef.session.remote.root}") if not gef.session.remote.connect(args.pid): - gef.session.remote_initializing = False + gef.session.reset_remote() raise EnvironmentError(f"Cannot connect to remote target {gef.session.remote.target}") if not gef.session.remote.setup(): - gef.session.remote_initializing = False + gef.session.reset_remote() raise EnvironmentError(f"Failed to create a proper environment for {gef.session.remote.target}") gdb.execute("context") @@ -10878,6 +10878,12 @@ def reset_caches(self) -> None: self._root: Optional[pathlib.Path] = None return + def reset_remote(self) -> None: + self.remote_initializing = False + self.remote = None + return + + def __str__(self) -> str: return f"Session({'Local' if self.remote is None else 'Remote'}, pid={self.pid or 'Not running'}, os='{self.os}')" From 72659766e697b3911a213e0e395ba89fab1439b4 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Wed, 31 Jan 2024 00:15:00 +0100 Subject: [PATCH 08/11] Fix https://github.com/hugsy/gef/pull/1045#discussion_r1451641759 --- gef.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/gef.py b/gef.py index c03d0a8af..30f1f15f5 100644 --- a/gef.py +++ b/gef.py @@ -11159,26 +11159,40 @@ def __setup_remote(self) -> bool: # get the file if not self.sync(str(self.file)): - warn(f"'{fpath}' could not be fetched on the remote system.") + warn(f"'{str(self.file)}' could not be fetched on the remote system.") success = False - if not self.sync(f"/proc/{self.pid}/maps"): + fpath = f"/proc/{self.pid}/maps" + if not self.sync(fpath): warn(f"'{fpath}' could not be fetched on the remote system.") success = False # makeup a fake mem mapping in case we failed to retrieve it - maps = self.root / f"proc/{self.pid}/maps" + maps = self.root / fpath with maps.open("w") as fd: fname = self.file.absolute() mem_range = "00000000-ffffffff" if is_32bit() else "0000000000000000-ffffffffffffffff" fd.write(f"{mem_range} rwxp 00000000 00:00 0 {fname}\n") - # pseudo procfs - for _file in ("environ", "cmdline"): - fpath = f"/proc/{self.pid}/{_file}" - if not self.sync(fpath): - warn(f"'{fpath}' could not be fetched on the remote system.") - success = False + fpath = f"/proc/{self.pid}/environ" + if not self.sync(fpath): + warn(f"'{fpath}' could not be fetched on the remote system.") + success = False + + # makeup a fake mem mapping in case we failed to retrieve it + environ = self.root / fpath + with environ.open("wb") as fd: + fd.write(b"PATH=/bin\x00HOME=/tmp\x00") + + fpath = f"/proc/{self.pid}/cmdline" + if not self.sync(fpath): + warn(f"'{fpath}' could not be fetched on the remote system.") + success = False + + # makeup a fake mem mapping in case we failed to retrieve it + cmdline = self.root / fpath + with cmdline.open("w") as fd: + fd.write("") return success From 98519c9bd297616b51b1460a3552f8bff85c70e3 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Wed, 31 Jan 2024 00:16:18 +0100 Subject: [PATCH 09/11] About https://github.com/hugsy/gef/pull/1045#discussion_r1451642223 I still think we don't want to raise anything here, but there was a reset missing :) --- gef.py | 1 + 1 file changed, 1 insertion(+) diff --git a/gef.py b/gef.py index 30f1f15f5..109e929be 100644 --- a/gef.py +++ b/gef.py @@ -11322,6 +11322,7 @@ def target_remote_posthook(): gef.session.remote = GefRemoteSessionManager("", 0) if not gef.session.remote.setup(): + gef.session.reset_remote() warn(f"Failed to create a proper environment for {gef.session.remote}") return False From 814e198eb117a793123e24d2c1dc51ab4c60231d Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Wed, 31 Jan 2024 00:17:53 +0100 Subject: [PATCH 10/11] Fix https://github.com/hugsy/gef/pull/1045#discussion_r1451640332 Even if we don't raise an exception (still discussing about it) we need to manage properly the cases were the binary was not setup. --- gef.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gef.py b/gef.py index 109e929be..15a1568a7 100644 --- a/gef.py +++ b/gef.py @@ -3335,7 +3335,7 @@ class MIPS64(MIPS): @staticmethod def supports_gdb_arch(gdb_arch: str) -> Optional[bool]: - return gdb_arch.startswith("mips") and gef.binary.e_class == Elf.Class.ELF_64_BITS + return gdb_arch.startswith("mips") and gef.binary and gef.binary.e_class == Elf.Class.ELF_64_BITS def copy_to_clipboard(data: bytes) -> None: @@ -3722,7 +3722,7 @@ def reset_architecture(arch: Optional[str] = None) -> None: return # last resort, use the info from elf header to find it from the known architectures - if gef.binary.e_machine: + if gef.binary and gef.binary.e_machine: try: gef.arch = arches[gef.binary.e_machine]() except KeyError: @@ -7222,7 +7222,7 @@ def do_invoke(self, argv: List[str]) -> None: return if not os.access(fpath, os.X_OK): - warn(f"The file '{fpath}' is not executable.") + warn(f"The file '{fpath}' does not exists or is not executable.") return if is_alive() and not gef.session.qemu_mode: @@ -11114,9 +11114,10 @@ def setup(self) -> bool: # refresh gef to consider the binary reset_all_caches() - if self.file.exists(): + if self.lfile.exists(): gef.binary = Elf(self.lfile) + reset_architecture() return success From 47bb65c27794f7fe262fcd5c69cc971ce521998a Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Tue, 20 Feb 2024 11:15:14 +0100 Subject: [PATCH 11/11] Fix typos - https://github.com/hugsy/gef/pull/1045#discussion_r1472123288 - https://github.com/hugsy/gef/pull/1045#discussion_r1472157184 - https://github.com/hugsy/gef/pull/1045#discussion_r1472158008 - https://github.com/hugsy/gef/pull/1045#discussion_r1472158765 --- gef.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/gef.py b/gef.py index 15a1568a7..5abf9a1ba 100644 --- a/gef.py +++ b/gef.py @@ -11117,7 +11117,6 @@ def setup(self) -> bool: if self.lfile.exists(): gef.binary = Elf(self.lfile) - reset_architecture() return success @@ -11168,7 +11167,7 @@ def __setup_remote(self) -> bool: warn(f"'{fpath}' could not be fetched on the remote system.") success = False - # makeup a fake mem mapping in case we failed to retrieve it + # make up a fake mem mapping in case we failed to retrieve it maps = self.root / fpath with maps.open("w") as fd: fname = self.file.absolute() @@ -11180,7 +11179,7 @@ def __setup_remote(self) -> bool: warn(f"'{fpath}' could not be fetched on the remote system.") success = False - # makeup a fake mem mapping in case we failed to retrieve it + # make up a fake environ in case we failed to retrieve it environ = self.root / fpath with environ.open("wb") as fd: fd.write(b"PATH=/bin\x00HOME=/tmp\x00") @@ -11190,9 +11189,8 @@ def __setup_remote(self) -> bool: warn(f"'{fpath}' could not be fetched on the remote system.") success = False - # makeup a fake mem mapping in case we failed to retrieve it - cmdline = self.root / fpath - with cmdline.open("w") as fd: + # make up a fake cmdline in case we failed to retrieve it + with (self.root / fpath).open("w") as fd: fd.write("") return success