diff --git a/gef.py b/gef.py index f660408b6..3b426142b 100644 --- a/gef.py +++ b/gef.py @@ -3537,6 +3537,15 @@ def is_qemu_system() -> bool: return "received: \"\"" in response +def is_target_coredump() -> bool: + global gef + if gef.session.coredump_mode is not None: + return gef.session.coredump_mode + lines = (gdb.execute("maintenance info section", to_string=True) or "").splitlines() + is_coredump_mode = any(map(lambda line: line.startswith("Core file: "), lines)) + gef.session.coredump_mode = is_coredump_mode + return is_coredump_mode + def get_filepath() -> Optional[str]: """Return the local absolute path of the file currently debugged.""" if gef.session.remote: @@ -10707,6 +10716,7 @@ def __gef_prompt__(current_prompt: Callable[[Callable], str]) -> str: if gef.config["gef.readline_compat"] is True: return GEF_PROMPT if gef.config["gef.disable_color"] is True: return GEF_PROMPT prompt = gef.session.remote.mode.prompt_string() if gef.session.remote else "" + prompt += "(core) " if is_target_coredump() else "" prompt += GEF_PROMPT_ON if is_alive() else GEF_PROMPT_OFF return prompt @@ -10817,6 +10827,12 @@ def __parse_maps(self) -> Optional[List[Section]]: if gef.arch.maps is not None: return list(gef.arch.maps()) + # Coredumps are the only case where `maintenance info sections` collected more + # info than `info proc sections`.so use this unconditionally. See #1154 + + if is_target_coredump(): + return list(self.parse_gdb_maintenance_info_sections()) + try: return list(self.parse_gdb_info_proc_maps()) except: @@ -10834,8 +10850,8 @@ def __parse_maps(self) -> Optional[List[Section]]: raise RuntimeError("Failed to get memory layout") - @classmethod - def parse_procfs_maps(cls) -> Generator[Section, None, None]: + @staticmethod + def parse_procfs_maps() -> Generator[Section, None, None]: """Get the memory mapping from procfs.""" procfs_mapfile = gef.session.maps if not procfs_mapfile: @@ -10866,9 +10882,9 @@ def parse_procfs_maps(cls) -> Generator[Section, None, None]: path=pathname) return - @classmethod - def parse_gdb_info_proc_maps(cls) -> Generator[Section, None, None]: - """Get the memory mapping from GDB's command `maintenance info sections` (limited info).""" + @staticmethod + def parse_gdb_info_proc_maps() -> Generator[Section, None, None]: + """Get the memory mapping from GDB's command `info proc mappings`.""" if GDB_VERSION < (11, 0): raise AttributeError("Disregarding old format") @@ -10925,8 +10941,8 @@ def parse_gdb_info_proc_maps(cls) -> Generator[Section, None, None]: ) return - @classmethod - def parse_monitor_info_mem(cls) -> Generator[Section, None, None]: + @staticmethod + def parse_monitor_info_mem() -> Generator[Section, None, None]: """Get the memory mapping from GDB's command `monitor info mem` This can raise an exception, which the memory manager takes to mean that this method does not work to get a map. @@ -10947,6 +10963,39 @@ def parse_monitor_info_mem(cls) -> Generator[Section, None, None]: offset=off, permission=perm) + @staticmethod + def parse_gdb_maintenance_info_sections() -> Generator[Section, None, None]: + """Get the memory mapping from GDB's command `maintenance info sections` (limited info). In some cases (i.e. coredumps), + the memory info collected by `info proc sections` is insufficent.""" + stream = StringIO(gdb.execute("maintenance info sections", to_string=True)) + + for line in stream: + if not line: + break + + try: + parts = line.split() + addr_start, addr_end = [int(x, 16) for x in parts[1].split("->")] + off = int(parts[3][:-1], 16) + path = parts[4] + perm = Permission.NONE + if "DATA" in parts[5:]: + perm |= Permission.READ | Permission.WRITE + if "CODE" in parts[5:]: + perm |= Permission.READ | Permission.EXECUTE + yield Section( + page_start=addr_start, + page_end=addr_end, + offset=off, + permission=perm, + path=path, + ) + + except IndexError: + continue + except ValueError: + continue + @staticmethod def parse_info_mem(): """Get the memory mapping from GDB's command `info mem`. This can be @@ -11267,6 +11316,7 @@ def __init__(self) -> None: self.remote: Optional["GefRemoteSessionManager"] = None self.remote_initializing: bool = False self.qemu_mode: bool = False + self.coredump_mode: bool | None = None self.convenience_vars_index: int = 0 self.heap_allocated_chunks: List[Tuple[int, int]] = [] self.heap_freed_chunks: List[Tuple[int, int]] = []