From d28c4a3eefcbdedbb4cd6477c9c4807b07b11fce Mon Sep 17 00:00:00 2001 From: hugsy Date: Sun, 22 Sep 2024 10:42:44 -0700 Subject: [PATCH 01/11] added ruff linter + fixes --- gef.py | 303 +++++++++++++++++++++++++++++------------------------- ruff.toml | 2 + 2 files changed, 164 insertions(+), 141 deletions(-) create mode 100644 ruff.toml diff --git a/gef.py b/gef.py index af6ee583e..621270212 100644 --- a/gef.py +++ b/gef.py @@ -353,7 +353,8 @@ def calling_function() -> Optional[str]: try: stack_info = traceback.extract_stack()[-3] return stack_info.name - except: + except Exception as e: + dbg(f"traceback failed with {str(e)}") return None @@ -743,7 +744,12 @@ def _search_for_realpath(self) -> Optional[str]: @property def realpath(self) -> str: # when in a `gef-remote` session, realpath returns the path to the binary on the local disk, not remote - return self.path if gef.session.remote is None else self._search_for_realpath() + if gef.session.remote is None: + return self.path + default = self._search_for_realpath() + if default: + return default + raise FileNotFoundError def __str__(self) -> str: return (f"Section(start={self.page_start:#x}, end={self.page_end:#x}, " @@ -1594,7 +1600,7 @@ class ChunkFlags(enum.IntFlag): NON_MAIN_ARENA = 4 def __str__(self) -> str: - return f" | ".join([ + return " | ".join([ Color.greenify("PREV_INUSE") if self.value & self.PREV_INUSE else Color.redify("PREV_INUSE"), Color.greenify("IS_MMAPPED") if self.value & self.IS_MMAPPED else Color.redify("IS_MMAPPED"), Color.greenify("NON_MAIN_ARENA") if self.value & self.NON_MAIN_ARENA else Color.redify("NON_MAIN_ARENA") @@ -1881,9 +1887,9 @@ def _show_code_line(fname: str, idx: int) -> str: gef_print("") exc_type, exc_value, exc_traceback = sys.exc_info() - + exc_name = exc_type.__name__ if exc_type else "Unknown" gef_print(" Exception raised ".center(80, HORIZONTAL_LINE)) - gef_print(f"{Color.colorify(exc_type.__name__, 'bold underline red')}: {exc_value}") + gef_print(f"{Color.colorify(exc_name, 'bold underline red')}: {exc_value}") gef_print(" Detailed stacktrace ".center(80, HORIZONTAL_LINE)) for fs in traceback.extract_tb(exc_traceback)[::-1]: @@ -3516,7 +3522,7 @@ def is_qemu() -> bool: if not is_remote_debug(): return False response = gdb.execute("maintenance packet Qqemu.sstepbits", to_string=True, from_tty=False) or "" - return "ENABLE=" in response + return "ENABLE=1" in response @lru_cache() @@ -3674,19 +3680,19 @@ def is_hex(pattern: str) -> bool: return len(pattern) % 2 == 0 and all(c in string.hexdigits for c in pattern[2:]) -def continue_handler(_: "gdb.events.ContinueEvent") -> None: +def continue_handler(_: "gdb.ContinueEvent") -> None: """GDB event handler for new object continue cases.""" return -def hook_stop_handler(_: "gdb.events.StopEvent") -> None: +def hook_stop_handler(_: "gdb.StopEvent") -> None: """GDB event handler for stop cases.""" reset_all_caches() gdb.execute("context") return -def new_objfile_handler(evt: Optional["gdb.events.NewObjFileEvent"]) -> None: +def new_objfile_handler(evt: Optional["gdb.NewObjFileEvent"]) -> None: """GDB event handler for new object file cases.""" reset_all_caches() progspace = gdb.current_progspace() @@ -3721,7 +3727,7 @@ def new_objfile_handler(evt: Optional["gdb.events.NewObjFileEvent"]) -> None: return -def exit_handler(_: "gdb.events.ExitedEvent") -> None: +def exit_handler(_: "gdb.ExitedEvent") -> None: """GDB event handler for exit cases.""" global gef # flush the caches @@ -3752,13 +3758,13 @@ def exit_handler(_: "gdb.events.ExitedEvent") -> None: return -def memchanged_handler(_: "gdb.events.MemoryChangedEvent") -> None: +def memchanged_handler(_: "gdb.MemoryChangedEvent") -> None: """GDB event handler for mem changes cases.""" reset_all_caches() return -def regchanged_handler(_: "gdb.events.RegisterChangedEvent") -> None: +def regchanged_handler(_: "gdb.RegisterChangedEvent") -> None: """GDB event handler for reg changes cases.""" reset_all_caches() return @@ -3888,7 +3894,7 @@ def get_memory_alignment(in_bits: bool = False) -> int: try: return gdb.parse_and_eval("$pc").type.sizeof - except: + except Exception: pass raise OSError("GEF is running under an unsupported mode") @@ -4123,72 +4129,72 @@ def set_arch(arch: Optional[str] = None, _: Optional[str] = None) -> None: # @only_if_events_supported("cont") -def gef_on_continue_hook(func: Callable[["gdb.events.ContinueEvent"], None]) -> None: +def gef_on_continue_hook(func: Callable[["gdb.ContinueEvent"], None]) -> None: gdb.events.cont.connect(func) @only_if_events_supported("cont") -def gef_on_continue_unhook(func: Callable[["gdb.events.ThreadEvent"], None]) -> None: +def gef_on_continue_unhook(func: Callable[["gdb.ThreadEvent"], None]) -> None: gdb.events.cont.disconnect(func) @only_if_events_supported("stop") -def gef_on_stop_hook(func: Callable[["gdb.events.StopEvent"], None]) -> None: +def gef_on_stop_hook(func: Callable[["gdb.StopEvent"], None]) -> None: gdb.events.stop.connect(func) @only_if_events_supported("stop") -def gef_on_stop_unhook(func: Callable[["gdb.events.StopEvent"], None]) -> None: +def gef_on_stop_unhook(func: Callable[["gdb.StopEvent"], None]) -> None: gdb.events.stop.disconnect(func) @only_if_events_supported("exited") -def gef_on_exit_hook(func: Callable[["gdb.events.ExitedEvent"], None]) -> None: +def gef_on_exit_hook(func: Callable[["gdb.ExitedEvent"], None]) -> None: gdb.events.exited.connect(func) @only_if_events_supported("exited") -def gef_on_exit_unhook(func: Callable[["gdb.events.ExitedEvent"], None]) -> None: +def gef_on_exit_unhook(func: Callable[["gdb.ExitedEvent"], None]) -> None: gdb.events.exited.disconnect(func) @only_if_events_supported("new_objfile") -def gef_on_new_hook(func: Callable[["gdb.events.NewObjFileEvent"], None]) -> None: +def gef_on_new_hook(func: Callable[["gdb.NewObjFileEvent"], None]) -> None: gdb.events.new_objfile.connect(func) @only_if_events_supported("new_objfile") -def gef_on_new_unhook(func: Callable[["gdb.events.NewObjFileEvent"], None]) -> None: +def gef_on_new_unhook(func: Callable[["gdb.NewObjFileEvent"], None]) -> None: gdb.events.new_objfile.disconnect(func) @only_if_events_supported("clear_objfiles") -def gef_on_unload_objfile_hook(func: Callable[["gdb.events.ClearObjFilesEvent"], None]) -> None: +def gef_on_unload_objfile_hook(func: Callable[["gdb.ClearObjFilesEvent"], None]) -> None: gdb.events.clear_objfiles.connect(func) @only_if_events_supported("clear_objfiles") -def gef_on_unload_objfile_unhook(func: Callable[["gdb.events.ClearObjFilesEvent"], None]) -> None: +def gef_on_unload_objfile_unhook(func: Callable[["gdb.ClearObjFilesEvent"], None]) -> None: gdb.events.clear_objfiles.disconnect(func) @only_if_events_supported("memory_changed") -def gef_on_memchanged_hook(func: Callable[["gdb.events.MemoryChangedEvent"], None]) -> None: +def gef_on_memchanged_hook(func: Callable[["gdb.MemoryChangedEvent"], None]) -> None: gdb.events.memory_changed.connect(func) @only_if_events_supported("memory_changed") -def gef_on_memchanged_unhook(func: Callable[["gdb.events.MemoryChangedEvent"], None]) -> None: +def gef_on_memchanged_unhook(func: Callable[["gdb.MemoryChangedEvent"], None]) -> None: gdb.events.memory_changed.disconnect(func) @only_if_events_supported("register_changed") -def gef_on_regchanged_hook(func: Callable[["gdb.events.RegisterChangedEvent"], None]) -> None: +def gef_on_regchanged_hook(func: Callable[["gdb.RegisterChangedEvent"], None]) -> None: gdb.events.register_changed.connect(func) @only_if_events_supported("register_changed") -def gef_on_regchanged_unhook(func: Callable[["gdb.events.RegisterChangedEvent"], None]) -> None: +def gef_on_regchanged_unhook(func: Callable[["gdb.RegisterChangedEvent"], None]) -> None: gdb.events.register_changed.disconnect(func) @@ -4757,7 +4763,7 @@ def settings(self) -> List[str]: """Return the list of settings for this command.""" return list(iter(self)) - @deprecated(f"Use `self[setting_name]` instead") + @deprecated("Use `self[setting_name]` instead") def get_setting(self, name: str) -> Any: return self.__getitem__(name) @@ -4765,14 +4771,14 @@ def __getitem__(self, name: str) -> Any: key = self.__get_setting_name(name) return gef.config[key] - @deprecated(f"Use `setting_name in self` instead") + @deprecated("Use `setting_name in self` instead") def has_setting(self, name: str) -> bool: return self.__contains__(name) def __contains__(self, name: str) -> bool: return self.__get_setting_name(name) in gef.config - @deprecated(f"Use `self[setting_name] = value` instead") + @deprecated("Use `self[setting_name] = value` instead") def add_setting(self, name: str, value: Tuple[Any, type, str], description: str = "") -> None: return self.__setitem__(name, (value, description)) @@ -4797,7 +4803,7 @@ def __setitem__(self, name: str, value: Union["GefSetting", Tuple[Any, str]]) -> gef.config[key] = GefSetting(value[0], description=value[1]) return - @deprecated(f"Use `del self[setting_name]` instead") + @deprecated("Use `del self[setting_name]` instead") def del_setting(self, name: str) -> None: return self.__delitem__(name) @@ -5239,7 +5245,7 @@ def comp2_b(x: Any) -> str: return f"{(x + (1 << off)) % (1 << off):b}" gef_print("0b" + comp2_b(res)) gef_print(f"{binascii.unhexlify(s_i)}") gef_print(f"{binascii.unhexlify(s_i)[::-1]}") - except: + except Exception: pass return @@ -5463,6 +5469,7 @@ class GefThemeCommand(GenericCommand): _cmdline_ = "theme" _syntax_ = f"{_cmdline_} [KEY [VALUE]]" + _examples_ = (f"{_cmdline_} address_stack green") def __init__(self) -> None: super().__init__(self._cmdline_) @@ -5497,7 +5504,7 @@ def do_invoke(self, args: List[str]) -> None: return setting_name = args[0] - if not setting_name in self: + if setting_name not in self: err("Invalid key") return @@ -5506,8 +5513,8 @@ def do_invoke(self, args: List[str]) -> None: gef_print(f"{setting_name:40s}: {Color.colorify(value, value)}") return - colors = [color for color in args[1:] if color in Color.colors] - self[setting_name] = " ".join(colors) + colors = (color for color in args[1:] if color in Color.colors) + self[setting_name][0] = " ".join(colors) return @@ -5574,6 +5581,7 @@ def apply_at(self, address: int, max_depth: int, depth: int = 0) -> None: ptrsize = gef.arch.ptrsize unpack = u32 if ptrsize == 4 else u64 for field in _structure._fields_: + assert len(field) == 2 _name, _type = field _value = getattr(_structure, _name) _offset = getattr(self.class_type, _name).offset @@ -6127,7 +6135,7 @@ def search_pattern(self, pattern: str, section_name: str) -> None: for section in gef.memory.maps: if not section.permission & Permission.READ: continue if section.path == "[vvar]": continue - if not section_name in section.path: continue + if section_name not in section.path: continue start = section.page_start end = section.page_end - 1 @@ -6264,7 +6272,7 @@ def __init__(self) -> None: super().__init__(prefix=False) return - @parse_arguments({"host": "", "port": 0}, {"--pid": -1, "--qemu-user": False, "--qemu-binary": ""}) + @parse_arguments({"host": "", "port": 0}, {"--pid": 0, "--qemu-user": ""}) def do_invoke(self, _: List[str], **kwargs: Any) -> None: if gef.session.remote is not None: err("You already are in remote session. Close it first before opening a new one...") @@ -6277,15 +6285,15 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: return # qemu-user support - qemu_binary: Optional[pathlib.Path] = None if args.qemu_user: + dbg("Setting up qemu-user session") try: - qemu_binary = pathlib.Path(args.qemu_binary).expanduser().absolute() if args.qemu_binary else gef.session.file + qemu_binary = pathlib.Path(args.qemu_user).expanduser().absolute() if args.qemu_user else gef.session.file if not qemu_binary or not qemu_binary.exists(): - raise FileNotFoundError(f"{qemu_binary} does not exist") + raise FileNotFoundError(f"qemu-user session was specified, but binary '{qemu_binary}' does not exist") except Exception as e: err(f"Failed to initialize qemu-user mode, reason: {str(e)}") - return + raise e # Try to establish the remote session, throw on error # Set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which @@ -6411,8 +6419,8 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: try: last_insn = gef_instruction_n(address, num_items-1) last_addr = last_insn.address - except: - err(f"Cannot patch instruction at {address:#x} reaching unmapped area") + except Exception as e: + err(f"Cannot patch instruction at {address:#x} reaching unmapped area, reason: {e}") return total_bytes = (last_addr - address) + gef_get_instruction_at(last_addr).size() @@ -7739,10 +7747,10 @@ def context_regs(self) -> None: gdb.execute(f"registers {printable_registers}") return - widest = l = max(map(len, gef.arch.all_registers)) - l += 5 - l += gef.arch.ptrsize * 2 - nb = get_terminal_size()[1] // l + widest = curlen = max(map(len, gef.arch.all_registers)) + curlen += 5 + curlen += gef.arch.ptrsize * 2 + nb = get_terminal_size()[1] // curlen i = 1 line = "" changed_color = gef.config["theme.registers_value_changed"] @@ -7831,7 +7839,7 @@ def context_code(self) -> None: breakpoints = gdb.breakpoints() or [] # breakpoint.locations was introduced in gdb 13.1 if len(breakpoints) and hasattr(breakpoints[-1], "locations"): - bp_locations = [hex(location.address) for b in breakpoints for location in b.locations if location is not None] + bp_locations = [hex(location.address) for b in breakpoints for location in b.locations if location is not None] # type: ignore else: # location relies on the user setting the breakpoints with "b *{hex(address)}" bp_locations = [b.location for b in breakpoints if b.location and b.location.startswith("*")] @@ -7939,6 +7947,8 @@ def print_arguments_from_symbol(self, function_name: str, symbol: "gdb.Symbol") args = [] fields = symbol.type.fields() if symbol.type else [] for i, f in enumerate(fields): + if not f.type: + continue _value = gef.arch.get_ith_parameter(i, in_func=False)[1] _value = RIGHT_ARROW.join(dereference_from(_value)) _name = f.name or f"var_{i}" @@ -8030,9 +8040,8 @@ def context_source(self) -> None: if not symtab.is_valid(): return - fpath = symtab.fullname() - with open(fpath, "r") as f: - lines = [l.rstrip() for l in f.readlines()] + fpath = pathlib.Path(symtab.fullname()) + lines = [curline.rstrip() for curline in fpath.read_text().splitlines()] except Exception: return @@ -8455,23 +8464,23 @@ def _hexdump(self, start_addr: int, length: int, arrange_as: str, offset: int = "word": ("H", 2), } - r, l = formats[arrange_as] - fmt_str = f"{{base}}{VERTICAL_LINE}+{{offset:#06x}} {{sym}}{{val:#0{l*2+2}x}} {{text}}" - fmt_pack = f"{endianness!s}{r}" + formatter, width = formats[arrange_as] + fmt_str = f"{{base}}{VERTICAL_LINE}+{{offset:#06x}} {{sym}}{{val:#0{width*2+2}x}} {{text}}" + fmt_pack = f"{endianness!s}{formatter}" lines = [] i = 0 text = "" while i < length: - cur_addr = start_addr + (i + offset) * l + cur_addr = start_addr + (i + offset) * width sym = gdb_get_location_from_symbol(cur_addr) sym = f"<{sym[0]:s}+{sym[1]:04x}> " if sym else "" - mem = gef.memory.read(cur_addr, l) + mem = gef.memory.read(cur_addr, width) val = struct.unpack(fmt_pack, mem)[0] if show_ascii: text = "".join([chr(b) if 0x20 <= b < 0x7F else "." for b in mem]) lines.append(fmt_str.format(base=Color.colorify(format_address(cur_addr), base_address_color), - offset=(i + offset) * l, sym=sym, val=val, text=text)) + offset=(i + offset) * width, sym=sym, val=val, text=text)) i += 1 return lines @@ -8573,9 +8582,9 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: var_name = values[0] try: values = str(gdb.parse_and_eval(var_name)).lstrip("{").rstrip("}").replace(",","").split(" ") - except: + except Exception as e: gef_print(f"Bad variable specified, check value with command: p {var_name}") - return + raise e d = str(gef.arch.endianness) for value in values: @@ -8761,7 +8770,7 @@ def pprint_dereferenced(addr: int, idx: int, base_offset: int = 0) -> str: addrs = dereference_from(current_address) addr_l = format_address(int(addrs[0], 16)) ma = (memalign*2 + 2) - l = ( + line = ( f"{Color.colorify(addr_l, base_address_color)}{VERTICAL_LINE}" f"{base_offset+offset:+#07x}: {sep.join(addrs[1:]):{ma}s}" ) @@ -8775,10 +8784,10 @@ def pprint_dereferenced(addr: int, idx: int, base_offset: int = 0) -> str: if register_hints: m = f"\t{LEFT_ARROW}{', '.join(list(register_hints))}" - l += Color.colorify(m, registers_color) + line += Color.colorify(m, registers_color) offset += memalign - return l + return line @only_if_gdb_running @parse_arguments({"address": "$sp"}, {("-r", "--reference"): "", ("-l", "--length"): 10}) @@ -8932,18 +8941,18 @@ def print_entry(self, entry: Section) -> None: elif entry.permission & Permission.READ and entry.permission & Permission.EXECUTE: line_color = gef.config["theme.address_code"] - l = [ + line_parts = [ Color.colorify(format_address(entry.page_start), line_color), Color.colorify(format_address(entry.page_end), line_color), Color.colorify(format_address(entry.offset), line_color), ] if entry.permission == Permission.ALL: - l.append(Color.colorify(str(entry.permission), "underline " + line_color)) + line_parts.append(Color.colorify(str(entry.permission), "underline " + line_color)) else: - l.append(Color.colorify(str(entry.permission), line_color)) + line_parts.append(Color.colorify(str(entry.permission), line_color)) - l.append(Color.colorify(entry.path, line_color)) - line = " ".join(l) + line_parts.append(Color.colorify(entry.path, line_color)) + line = " ".join(line_parts) gef_print(line) return @@ -8990,13 +8999,13 @@ def do_invoke(self, argv: List[str]) -> None: if filter_by_name and filter_by_name not in xfile.name: continue - l = [ + line_parts = [ format_address(xfile.zone_start), format_address(xfile.zone_end), f"{xfile.name:<21s}", xfile.filename, ] - gef_print(" ".join(l)) + gef_print(" ".join(line_parts)) return @@ -9680,7 +9689,7 @@ def dump_tracked_allocations(self) -> None: ok("No free() chunk tracked") return - def clean(self, _: "gdb.events.ExitedEvent") -> None: + def clean(self, _: "gdb.ExitedEvent") -> None: global gef ok(f"{Color.colorify('Heap-Analysis', 'yellow bold')} - Cleaning up") @@ -9911,6 +9920,7 @@ def __init__(self) -> None: gef.config["gef.libc_version"] = GefSetting("", str, "Specify libc version when auto-detection fails") gef.config["gef.main_arena_offset"] = GefSetting("", str, "Offset from libc base address to main_arena symbol (int or hex). Set to empty string to disable.") gef.config["gef.propagate_debug_exception"] = GefSetting(False, bool, "If true, when debug mode is enabled, Python exceptions will be propagated all the way.") + gef.config["gef.extra_python_package_paths"] = GefSetting("", str, "Semi-colon separate list of extra paths to include for python packages") self.commands : Dict[str, GenericCommand] = collections.OrderedDict() self.functions : Dict[str, GenericFunction] = collections.OrderedDict() @@ -10232,7 +10242,7 @@ def set_setting(self, argv: List[str]) -> bool: _type = gef.config.raw_entry(key).type # Attempt to parse specific values for known types - if _type == bool: + if _type is bool: if new_value.upper() in ("TRUE", "T", "1"): _newval = True elif new_value.upper() in ("FALSE", "F", "0"): @@ -10340,7 +10350,7 @@ def reload(self, quiet: bool): except Exception: continue new_value = cfg.get(section, optname) - if setting.type == bool: + if setting.type is bool: new_value = True if new_value.upper() in ("TRUE", "T", "1") else False setting.value = setting.type(new_value) @@ -10694,7 +10704,7 @@ def reset_caches(self) -> None: if not hasattr(obj, "cache_clear"): continue obj.cache_clear() - except: # we're reseting the cache here, we don't care if (or which) exception triggers + except Exception: # we're reseting the cache here, we don't care if (or which) exception triggers continue return @@ -10764,7 +10774,10 @@ def read_ascii_string(self, address: int) -> Optional[str]: @property def maps(self) -> List[Section]: if not self.__maps: - self.__maps = self.__parse_maps() + maps = self.__parse_maps() + if not maps: + raise RuntimeError("Failed to determine memory layout") + self.__maps = maps return self.__maps def __parse_maps(self) -> Optional[List[Section]]: @@ -10777,17 +10790,17 @@ def __parse_maps(self) -> Optional[List[Section]]: try: return list(self.parse_gdb_info_proc_maps()) - except: + except Exception: pass try: return list(self.parse_procfs_maps()) - except: + except Exception: pass try: return list(self.parse_monitor_info_mem()) - except: + except Exception: pass raise RuntimeError("Failed to get memory layout") @@ -10964,7 +10977,7 @@ def main_arena(self) -> Optional[GlibcArena]: # the initialization of `main_arena` also defined `selected_arena`, so # by default, `main_arena` == `selected_arena` self.selected_arena = self.__libc_main_arena - except: + except Exception: # the search for arena can fail when the session is not started pass return self.__libc_main_arena @@ -11030,7 +11043,8 @@ def find_main_arena_addr() -> int: try: dbg("Trying to bruteforce main_arena address") # setup search_range for `main_arena` to `.data` of glibc - search_filter = lambda f: "libc" in pathlib.Path(f.filename).name and f.name == ".data" + def search_filter(zone: Zone) -> bool: + return "libc" in pathlib.Path(zone.filename).name and zone.name == ".data" for dotdata in list(filter(search_filter, get_info_files())): search_range = range(dotdata.zone_start, dotdata.zone_end, alignment) @@ -11113,9 +11127,10 @@ def tidx2size(self, idx: int) -> int: def malloc_align_address(self, address: int) -> int: """Align addresses according to glibc's MALLOC_ALIGNMENT. See also Issue #689 on Github""" + def ceil(n: int) -> int: + return int(-1 * n // 1 * -1) malloc_alignment = self.malloc_alignment - ceil = lambda n: int(-1 * n // 1 * -1) - return malloc_alignment * ceil((address / malloc_alignment)) + return malloc_alignment * ceil((address // malloc_alignment)) class GefSetting: @@ -11387,12 +11402,13 @@ def __repr__(self): return f"RemoteMode = {str(self)} ({int(self)})" def prompt_string(self) -> str: - if self == GefRemoteSessionManager.RemoteMode.QEMU: - return Color.boldify("(qemu) ") - if self == GefRemoteSessionManager.RemoteMode.RR: - return Color.boldify("(rr) ") - if self == GefRemoteSessionManager.RemoteMode.GDBSERVER: - return Color.boldify("(remote) ") + match self: + case GefRemoteSessionManager.RemoteMode.QEMU: + return Color.boldify("(qemu) ") + case GefRemoteSessionManager.RemoteMode.RR: + return Color.boldify("(rr) ") + case GefRemoteSessionManager.RemoteMode.GDBSERVER: + return Color.boldify("(remote) ") raise AttributeError("Unknown value") def __init__(self, host: str, port: int, pid: int =-1, qemu: Optional[pathlib.Path] = None) -> None: @@ -11402,6 +11418,8 @@ 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 + if pid > 0: + self._pid = pid if self.__qemu is not None: self._mode = GefRemoteSessionManager.RemoteMode.QEMU @@ -11417,7 +11435,7 @@ def close(self) -> None: gef_on_new_hook(new_objfile_handler) except Exception as e: warn(f"Exception while restoring local context: {str(e)}") - return + raise e def __str__(self) -> str: return f"RemoteSession(target='{self.target}', local='{self.root}', pid={self.pid}, mode={self.mode})" @@ -11478,7 +11496,7 @@ def sync(self, src: str, dst: Optional[str] = None) -> bool: def connect(self, pid: int) -> bool: """Connect to remote target. If in extended mode, also attach to the given PID.""" # before anything, register our new hook to download files from the remote target - dbg(f"[remote] Installing new objfile handlers") + dbg("[remote] Installing new objfile handlers") try: gef_on_new_unhook(new_objfile_handler) except SystemError: @@ -11506,17 +11524,19 @@ def connect(self, pid: int) -> bool: def setup(self) -> bool: # setup remote adequately depending on remote or qemu mode - if self.mode == GefRemoteSessionManager.RemoteMode.QEMU: - dbg(f"Setting up as qemu session, target={self.__qemu}") - self.__setup_qemu() - elif self.mode == GefRemoteSessionManager.RemoteMode.RR: - dbg(f"Setting up as rr session") - self.__setup_rr() - elif self.mode == GefRemoteSessionManager.RemoteMode.GDBSERVER: - dbg(f"Setting up as remote session") - self.__setup_remote() - else: - raise Exception + match self.mode: + case GefRemoteSessionManager.RemoteMode.QEMU: + dbg(f"Setting up as qemu session, target={self.__qemu}") + self.__setup_qemu() + case GefRemoteSessionManager.RemoteMode.RR: + dbg("Setting up as rr session") + self.__setup_rr() + case GefRemoteSessionManager.RemoteMode.GDBSERVER: + dbg("Setting up as remote session") + self.__setup_remote() + case _: + raise ValueError + # refresh gef to consider the binary reset_all_caches() gef.binary = Elf(self.lfile) @@ -11581,7 +11601,7 @@ def __setup_rr(self) -> bool: self.__local_root_path = pathlib.Path("/") return True - def remote_objfile_event_handler(self, evt: "gdb.events.NewObjFileEvent") -> None: + 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'}))") if not evt or not evt.new_objfile.filename: return @@ -11710,10 +11730,30 @@ def reset_caches(self) -> None: def target_remote_posthook(): - if gef.session.remote_initializing: - return + conn = gdb.selected_inferior().connection + # if here, the connection should be established + if not isinstance(conn, gdb.RemoteTargetConnection): + raise EnvironmentError("Failed to create a remote session") + + # if gef.session.remote_initializing: + # dbg(f"remote session already initializing: {gef.session}") + # return + + print(f"{conn.description=}") + print(f"{conn.details=}") + print(f"{conn.type=}") + + assert conn.details + host, port = conn.details.split(":") + pid = ( + gdb.selected_inferior().pid + if False + else gdb.selected_thread().ptid[1] + ) + print(host, port, pid) + gef.session.remote = GefRemoteSessionManager(host, int(port), pid) + gef.session.remote._mode = GefRemoteSessionManager.RemoteMode.GDBSERVER - gef.session.remote = GefRemoteSessionManager("", 0) if not gef.session.remote.setup(): raise EnvironmentError(f"Failed to create a proper environment for {gef.session.remote}") @@ -11729,17 +11769,6 @@ def target_remote_posthook(): f"(with Python {'.'.join(map(str, PYTHON_MIN_VERSION))} or higher).") exit(1) - # When using a Python virtual environment, GDB still loads the system-installed Python - # so GEF doesn't load site-packages dir from environment - # In order to fix it, from the shell with venv activated we run the python binary, - # take and parse its path, add the path to the current python process using sys.path.extend - PYTHONBIN = which("python3") - PREFIX = gef_pystring(subprocess.check_output([PYTHONBIN, '-c', 'import os, sys;print((sys.prefix))'])).strip("\\n") - if PREFIX != sys.base_prefix: - SITE_PACKAGES_DIRS = subprocess.check_output( - [PYTHONBIN, "-c", "import os, sys;print(os.linesep.join(sys.path).strip())"]).decode("utf-8").split() - sys.path.extend(SITE_PACKAGES_DIRS) - # setup config gdb_initial_settings = ( "set confirm off", @@ -11771,6 +11800,14 @@ def target_remote_posthook(): # reload settings gdb.execute("gef restore") + # extend `sys.path` for venv compat + if gef.config["gef.extra_python_package_paths"]: + for path in map(pathlib.Path, gef.config["gef.extra_python_package_paths"].split(";")): + if not path.exists(): + warn(f"Skipping invalid directory {path}") + continue + sys.path.extend(str(path.absolute())) + # setup gdb prompt gdb.prompt_hook = __gef_prompt__ @@ -11789,32 +11826,16 @@ def target_remote_posthook(): GefTmuxSetup() - if GDB_VERSION > (9, 0): - 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") + # Register a post-hook for `target remote` that initialize the remote session + hook = """ + define target hookpost-{} + pi target_remote_posthook() + context + pi res = calling_function(); if res != "connect": err("Failed to initialize remote session: " + str(res)) + end + """ + gdb.execute(hook.format("remote")) + gdb.execute(hook.format("extended-remote")) # restore saved breakpoints (if any) bkp_fpath = pathlib.Path(gef.config["gef.autosave_breakpoints_file"]).expanduser().absolute() diff --git a/ruff.toml b/ruff.toml new file mode 100644 index 000000000..6a3fc3001 --- /dev/null +++ b/ruff.toml @@ -0,0 +1,2 @@ +[lint.per-file-ignores] +"gef.py" = ["E701"] \ No newline at end of file From cde95b309f124dc69cbc67646a5e3f9ea07746e1 Mon Sep 17 00:00:00 2001 From: hugsy Date: Tue, 24 Sep 2024 13:36:24 -0700 Subject: [PATCH 02/11] checkpoint --- gef.py | 338 +++++++++++++++++------------------ tests/base.py | 8 +- tests/commands/gef_remote.py | 2 +- tests/commands/theme.py | 2 +- 4 files changed, 173 insertions(+), 177 deletions(-) diff --git a/gef.py b/gef.py index 621270212..17d602c23 100644 --- a/gef.py +++ b/gef.py @@ -82,8 +82,8 @@ from functools import lru_cache from io import StringIO, TextIOWrapper from types import ModuleType -from typing import (Any, ByteString, Callable, Dict, Generator, Iterable, - Iterator, List, NoReturn, Optional, Sequence, Set, Tuple, Type, TypeVar, +from typing import (Any, ByteString, Callable, Generator, Iterable, + Iterator, NoReturn, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, cast) from urllib.request import urlopen @@ -101,7 +101,7 @@ def http_get(url: str) -> Optional[bytes]: return None -def update_gef(argv: List[str]) -> int: +def update_gef(argv: list[str]) -> int: """Obsolete. Use `gef.sh`.""" return -1 @@ -146,7 +146,7 @@ def update_gef(argv: List[str]) -> int: __registered_commands__ : Set[Type["GenericCommand"]] = set() __registered_functions__ : Set[Type["GenericFunction"]] = set() -__registered_architectures__ : Dict[Union["Elf.Abi", str], Type["Architecture"]] = {} +__registered_architectures__ : dict[Union["Elf.Abi", str], Type["Architecture"]] = {} __registered_file_formats__ : Set[ Type["FileFormat"] ] = set() GefMemoryMapProvider = Callable[[], Generator["Section", None, None]] @@ -363,7 +363,6 @@ def calling_function() -> Optional[str]: # def only_if_gdb_running(f: Callable) -> Callable: """Decorator wrapper to check if GDB is running.""" - @functools.wraps(f) def wrapper(*args: Any, **kwargs: Any) -> Any: if is_alive(): @@ -447,8 +446,8 @@ def FakeExit(*args: Any, **kwargs: Any) -> NoReturn: sys.exit = FakeExit -def parse_arguments(required_arguments: Dict[Union[str, Tuple[str, str]], Any], - optional_arguments: Dict[Union[str, Tuple[str, str]], Any]) -> Callable: +def parse_arguments(required_arguments: dict[Union[str, Tuple[str, str]], Any], + optional_arguments: dict[Union[str, Tuple[str, str]], Any]) -> Callable: """Argument parsing decorator.""" def int_wrapper(x: str) -> int: return int(x, 0) @@ -797,8 +796,8 @@ class FileFormat: name: str path: pathlib.Path entry_point: int - checksec: Dict[str, bool] - sections: List[FileFormatSection] + checksec: dict[str, bool] + sections: list[FileFormatSection] def __init__(self, path: Union[str, pathlib.Path]) -> None: raise NotImplementedError @@ -887,11 +886,11 @@ class OsAbi(enum.Enum): e_shstrndx: int path: pathlib.Path - phdrs : List["Phdr"] - shdrs : List["Shdr"] + phdrs : list["Phdr"] + shdrs : list["Shdr"] name: str = "ELF" - __checksec : Dict[str, bool] + __checksec : dict[str, bool] def __init__(self, path: Union[str, pathlib.Path]) -> None: """Instantiate an ELF object. A valid ELF must be provided, or an exception will be thrown.""" @@ -977,7 +976,7 @@ def is_valid(cls, path: pathlib.Path) -> bool: return u32(path.open("rb").read(4), e = Endianness.BIG_ENDIAN) == Elf.ELF_MAGIC @property - def checksec(self) -> Dict[str, bool]: + def checksec(self) -> dict[str, bool]: """Check the security property of the ELF binary. The following properties are: - Canary - NX @@ -1244,7 +1243,7 @@ def __str__(self) -> str: class Instruction: """GEF representation of a CPU instruction.""" - def __init__(self, address: int, location: str, mnemo: str, operands: List[str], opcodes: bytes) -> None: + def __init__(self, address: int, location: str, mnemo: str, operands: list[str], opcodes: bytes) -> None: self.address, self.location, self.mnemonic, self.operands, self.opcodes = \ address, location, mnemo, operands, opcodes return @@ -1549,7 +1548,7 @@ def heap_addr(self, allow_unaligned: bool = False) -> Optional[int]: return _addr return gef.heap.malloc_align_address(_addr) - def get_heap_info_list(self) -> Optional[List[GlibcHeapInfo]]: + def get_heap_info_list(self) -> Optional[list[GlibcHeapInfo]]: if self.is_main_arena(): return None heap_addr = self.get_heap_for_ptr(self.top) @@ -2237,7 +2236,7 @@ def gef_disassemble(addr: int, nb_insn: int, nb_prev: int = 0) -> Generator[Inst yield insn -def gef_execute_external(command: Sequence[str], as_list: bool = False, **kwargs: Any) -> Union[str, List[str]]: +def gef_execute_external(command: Sequence[str], as_list: bool = False, **kwargs: Any) -> Union[str, list[str]]: """Execute an external command and return the result.""" res = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=kwargs.get("shell", False)) return [gef_pystring(_) for _ in res.splitlines()] if as_list else gef_pystring(res) @@ -2259,7 +2258,7 @@ def gef_execute_gdb_script(commands: str) -> None: @deprecated("Use Elf(fname).checksec()") -def checksec(filename: str) -> Dict[str, bool]: +def checksec(filename: str) -> dict[str, bool]: return Elf(filename).checksec @@ -2311,7 +2310,7 @@ def is_little_endian() -> bool: return gef.arch.endianness == Endianness.LITTLE_ENDIAN -def flags_to_human(reg_value: int, value_table: Dict[int, str]) -> str: +def flags_to_human(reg_value: int, value_table: dict[int, str]) -> str: """Return a human readable string showing the flag states.""" flags = [] for bit_index, name in value_table.items(): @@ -2366,7 +2365,7 @@ class Architecture(ArchitectureBase): return_register: str flag_register: Optional[str] instruction_length: Optional[int] - flags_table: Dict[int, str] + flags_table: dict[int, str] syscall_register: Optional[str] syscall_instructions: Union[Tuple[()], Tuple[str, ...]] function_parameters: Union[Tuple[()], Tuple[str, ...]] @@ -3559,7 +3558,7 @@ def get_function_length(sym: str) -> int: @lru_cache() -def get_info_files() -> List[Zone]: +def get_info_files() -> list[Zone]: """Retrieve all the files loaded by debuggee.""" lines = (gdb.execute("info files", to_string=True) or "").splitlines() infos = [] @@ -3611,7 +3610,7 @@ def process_lookup_path(name: str, perm: Permission = Permission.ALL) -> Optiona err("Process is not running") return None - matches: Dict[str, Section] = dict() + matches: dict[str, Section] = dict() for sect in gef.memory.maps: filename = pathlib.Path(sect.path).name @@ -4116,7 +4115,7 @@ def get_register(regname) -> Optional[int]: @deprecated("Use `gef.memory.maps`") -def get_process_maps() -> List[Section]: +def get_process_maps() -> list[Section]: return gef.memory.maps @@ -4691,8 +4690,8 @@ class GenericCommand(gdb.Command): _cmdline_: str _syntax_: str - _example_: Union[str, List[str]] = "" - _aliases_: List[str] = [] + _example_: Union[str, list[str]] = "" + _aliases_: list[str] = [] def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) @@ -4740,7 +4739,7 @@ def usage(self) -> None: err(f"Syntax\n{self._syntax_}") return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: raise NotImplementedError def pre_load(self) -> None: @@ -4759,7 +4758,7 @@ def __iter__(self) -> Generator[str, None, None]: yield key.replace(f"{self._cmdline_}.", "", 1) @property - def settings(self) -> List[str]: + def settings(self) -> list[str]: """Return the list of settings for this command.""" return list(iter(self)) @@ -4811,7 +4810,7 @@ def __delitem__(self, name: str) -> None: del gef.config[self.__get_setting_name(name)] return - def __set_repeat_count(self, argv: List[str], from_tty: bool) -> None: + def __set_repeat_count(self, argv: list[str], from_tty: bool) -> None: if not from_tty: self.repeat = False self.repeat_count = 0 @@ -4836,7 +4835,7 @@ def __init__(self) -> None: super().__init__(prefix=True) return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not argv: self.usage() return @@ -4849,7 +4848,7 @@ class ArchGetCommand(GenericCommand): _syntax_ = f"{_cmdline_}" _example_ = f"{_cmdline_}" - def do_invoke(self, args: List[str]) -> None: + def do_invoke(self, args: list[str]) -> None: gef_print(f"{Color.greenify('Arch')}: {gef.arch}") gef_print(f"{Color.greenify('Reason')}: {gef.arch_reason}") @@ -4862,10 +4861,10 @@ class ArchSetCommand(GenericCommand): _syntax_ = f"{_cmdline_} " _example_ = f"{_cmdline_} X86" - def do_invoke(self, args: List[str]) -> None: + def do_invoke(self, args: list[str]) -> None: reset_architecture(args[0].lower() if args else None) - def complete(self, text: str, word: str) -> List[str]: + def complete(self, text: str, word: str) -> list[str]: return sorted(x for x in __registered_architectures__.keys() if isinstance(x, str) and x.lower().startswith(text.lower().strip())) @@ -4877,7 +4876,7 @@ class ArchListCommand(GenericCommand): _syntax_ = f"{_cmdline_}" _example_ = f"{_cmdline_}" - def do_invoke(self, args: List[str]) -> None: + def do_invoke(self, args: list[str]) -> None: gef_print(Color.greenify("Available architectures:")) for arch in sorted(set(__registered_architectures__.values()), key=lambda x: x.arch): if arch is GenericArchitecture: @@ -4897,7 +4896,7 @@ class VersionCommand(GenericCommand): _syntax_ = f"{_cmdline_}" _example_ = f"{_cmdline_}" - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: gef_fpath = pathlib.Path(inspect.stack()[0][1]).expanduser().absolute() gef_dir = gef_fpath.parent with gef_fpath.open("rb") as f: @@ -4944,7 +4943,7 @@ def __init__(self) -> None: return @property - def format_matrix(self) -> Dict[int, Tuple[str, str, str]]: + def format_matrix(self) -> dict[int, Tuple[str, str, str]]: # `gef.arch.endianness` is a runtime property, should not be defined as a class property return { 8: (f"{gef.arch.endianness}B", "char", "db"), @@ -4955,7 +4954,7 @@ def format_matrix(self) -> Dict[int, Tuple[str, str, str]]: @only_if_gdb_running @parse_arguments({"location": "$pc", }, {("--length", "-l"): 256, "--bitlen": 0, "--lang": "py", "--clip": False,}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: """Default value for print-format command.""" args: argparse.Namespace = kwargs["arguments"] args.bitlen = args.bitlen or gef.arch.ptrsize * 2 @@ -5023,7 +5022,7 @@ def __init__(self) -> None: super().__init__(prefix=True) return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not argv: self.usage() return @@ -5037,7 +5036,7 @@ class PieBreakpointCommand(GenericCommand): _syntax_ = f"{_cmdline_} OFFSET" @parse_arguments({"offset": ""}, {}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] if not args.offset: self.usage() @@ -5069,7 +5068,7 @@ class PieInfoCommand(GenericCommand): _syntax_ = f"{_cmdline_} BREAKPOINT" @parse_arguments({"breakpoints": [-1,]}, {}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] if args.breakpoints[0] == -1: # No breakpoint info needed @@ -5095,7 +5094,7 @@ class PieDeleteCommand(GenericCommand): _syntax_ = f"{_cmdline_} [BREAKPOINT]" @parse_arguments({"breakpoints": [-1,]}, {}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: global gef args : argparse.Namespace = kwargs["arguments"] if args.breakpoints[0] == -1: @@ -5110,7 +5109,7 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: @staticmethod - def delete_bp(breakpoints: List[PieVirtualBreakpoint]) -> None: + def delete_bp(breakpoints: list[PieVirtualBreakpoint]) -> None: global gef for bp in breakpoints: # delete current real breakpoints if exists @@ -5128,7 +5127,7 @@ class PieRunCommand(GenericCommand): _cmdline_ = "pie run" _syntax_ = _cmdline_ - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: global gef fpath = get_filepath() if not fpath: @@ -5171,7 +5170,7 @@ class PieAttachCommand(GenericCommand): _cmdline_ = "pie attach" _syntax_ = f"{_cmdline_} PID" - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: try: gdb.execute(f"attach {' '.join(argv)}", to_string=True) except gdb.error as e: @@ -5195,7 +5194,7 @@ class PieRemoteCommand(GenericCommand): _cmdline_ = "pie remote" _syntax_ = f"{_cmdline_} REMOTE" - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: try: gdb.execute(f"gef-remote {' '.join(argv)}") except gdb.error as e: @@ -5221,7 +5220,7 @@ class SmartEvalCommand(GenericCommand): _example_ = (f"\n{_cmdline_} $pc+1" f"\n{_cmdline_} 0x00007ffff7a10000 0x00007ffff7bce000") - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: argc = len(argv) if argc == 1: self.evaluate(argv) @@ -5231,7 +5230,7 @@ def do_invoke(self, argv: List[str]) -> None: self.distance(argv) return - def evaluate(self, expr: List[str]) -> None: + def evaluate(self, expr: list[str]) -> None: def show_as_int(i: int) -> None: off = gef.arch.ptrsize*8 def comp2_x(x: Any) -> str: return f"{(x + (1 << off)) % (1 << off):x}" @@ -5268,7 +5267,7 @@ def comp2_b(x: Any) -> str: return f"{(x + (1 << off)) % (1 << off):b}" gef_print(" ".join(parsed_expr)) return - def distance(self, args: List[str]) -> None: + def distance(self, args: list[str]) -> None: try: x = int(args[0], 16) if is_hex(args[0]) else int(args[0]) y = int(args[1], 16) if is_hex(args[1]) else int(args[1]) @@ -5286,7 +5285,7 @@ class CanaryCommand(GenericCommand): _syntax_ = _cmdline_ @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: self.dont_repeat() fname = get_filepath() @@ -5321,7 +5320,7 @@ def __init__(self) -> None: @only_if_gdb_running @only_if_gdb_target_local - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: self.show_info_proc() self.show_ancestor() self.show_descendants() @@ -5329,7 +5328,7 @@ def do_invoke(self, argv: List[str]) -> None: self.show_connections() return - def get_state_of(self, pid: int) -> Dict[str, str]: + def get_state_of(self, pid: int) -> dict[str, str]: res = {} with open(f"/proc/{pid}/status", "r") as f: file = f.readlines() @@ -5345,7 +5344,7 @@ def get_cmdline_of(self, pid: int) -> str: def get_process_path_of(self, pid: int) -> str: return os.readlink(f"/proc/{pid}/exe") - def get_children_pids(self, pid: int) -> List[int]: + def get_children_pids(self, pid: int) -> list[int]: cmd = [gef.session.constants["ps"], "-o", "pid", "--ppid", f"{pid}", "--noheaders"] try: return [int(x) for x in gef_execute_external(cmd, as_list=True)] @@ -5400,7 +5399,7 @@ def show_fds(self) -> None: gef_print(f"\t{fullpath} {RIGHT_ARROW} {os.readlink(fullpath)}") return - def list_sockets(self, pid: int) -> List[int]: + def list_sockets(self, pid: int) -> list[int]: sockets = [] path = f"/proc/{pid:d}/fd" items = os.listdir(path) @@ -5469,7 +5468,7 @@ class GefThemeCommand(GenericCommand): _cmdline_ = "theme" _syntax_ = f"{_cmdline_} [KEY [VALUE]]" - _examples_ = (f"{_cmdline_} address_stack green") + _example_ = (f"{_cmdline_} address_stack green") def __init__(self) -> None: super().__init__(self._cmdline_) @@ -5492,7 +5491,7 @@ def __init__(self) -> None: self["source_current_line"] = ("green", "Color to use for the current code line in the source window") return - def do_invoke(self, args: List[str]) -> None: + def do_invoke(self, args: list[str]) -> None: self.dont_repeat() argc = len(args) @@ -5514,8 +5513,7 @@ def do_invoke(self, args: List[str]) -> None: return colors = (color for color in args[1:] if color in Color.colors) - self[setting_name][0] = " ".join(colors) - return + self[setting_name] = " ".join(colors) # type: ignore // this is valid since we overwrote __setitem__() class ExternalStructureManager: @@ -5534,7 +5532,7 @@ def __str__(self) -> str: return self.name def pprint(self) -> None: - res: List[str] = [] + res: list[str] = [] for _name, _type in self.class_type._fields_: # type: ignore size = ctypes.sizeof(_type) name = Color.colorify(_name, gef.config["pcustom.structure_name"]) @@ -5751,7 +5749,7 @@ def __init__(self) -> None: return @parse_arguments({"type": "", "address": ""}, {}) - def do_invoke(self, *_: Any, **kwargs: Dict[str, Any]) -> None: + def do_invoke(self, *_: Any, **kwargs: dict[str, Any]) -> None: args = cast(argparse.Namespace, kwargs["arguments"]) if not args.type: gdb.execute("pcustom list") @@ -5795,7 +5793,7 @@ def __init__(self) -> None: super().__init__() return - def do_invoke(self, _: List) -> None: + def do_invoke(self, _: list) -> None: """Dump the list of all the structures and their respective.""" manager = ExternalStructureManager() info(f"Listing custom structures from '{manager.path}'") @@ -5820,7 +5818,7 @@ def __init__(self) -> None: super().__init__() return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) == 0: self.usage() return @@ -5848,7 +5846,7 @@ def __init__(self) -> None: super().__init__() return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) == 0: self.usage() return @@ -5892,7 +5890,7 @@ class ChangeFdCommand(GenericCommand): @only_if_gdb_running @only_if_gdb_target_local - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) != 2: self.usage() return @@ -5970,7 +5968,7 @@ class ScanSectionCommand(GenericCommand): _example_ = f"\n{_cmdline_} stack libc" @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) != 2: self.usage() return @@ -6067,7 +6065,7 @@ def print_loc(self, loc: Tuple[int, int, str]) -> None: gef_print(f""" {loc[0]:#x} - {loc[1]:#x} {RIGHT_ARROW} "{Color.pinkify(loc[2])}" """) return - def search_pattern_by_address(self, pattern: str, start_address: int, end_address: int) -> List[Tuple[int, int, str]]: + def search_pattern_by_address(self, pattern: str, start_address: int, end_address: int) -> list[Tuple[int, int, str]]: """Search a pattern within a range defined by arguments.""" _pattern = gef_pybytes(pattern) step = self["nr_pages_chunk"] * gef.session.pagesize @@ -6099,7 +6097,7 @@ def search_pattern_by_address(self, pattern: str, start_address: int, end_addres return locations - def search_binpattern_by_address(self, binpattern: bytes, start_address: int, end_address: int) -> List[Tuple[int, int, str]]: + def search_binpattern_by_address(self, binpattern: bytes, start_address: int, end_address: int) -> list[Tuple[int, int, str]]: """Search a binary pattern within a range defined by arguments.""" step = self["nr_pages_chunk"] * gef.session.pagesize @@ -6152,7 +6150,7 @@ def search_pattern(self, pattern: str, section_name: str) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: argc = len(argv) if argc < 1: self.usage() @@ -6217,7 +6215,7 @@ class FlagsCommand(GenericCommand): _example_ = (f"\n{_cmdline_}" f"\n{_cmdline_} +zero # sets ZERO flag") - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not gef.arch.flag_register: warn(f"The architecture {gef.arch.arch}:{gef.arch.mode} doesn't have flag register.") return @@ -6273,7 +6271,7 @@ def __init__(self) -> None: return @parse_arguments({"host": "", "port": 0}, {"--pid": 0, "--qemu-user": ""}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: if gef.session.remote is not None: err("You already are in remote session. Close it first before opening a new one...") return @@ -6335,7 +6333,7 @@ def __init__(self) -> None: @only_if_gdb_running @parse_arguments({"address": "$pc"}, {"--n": 1}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] address = parse_address(args.address) num_instructions = args.n @@ -6364,7 +6362,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: target_addr = gef_next_instruction(parse_address("$pc")).address JustSilentStopBreakpoint("".join(["*", str(target_addr)])) gdb.execute("continue") @@ -6397,7 +6395,7 @@ def __init__(self) -> None: @only_if_gdb_running @parse_arguments({"address": "$pc"}, {"--i": 1, "--b": False, "--f": False, "--n": False}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] address = parse_address(args.address) nop = gef.arch.nop_insn @@ -6480,7 +6478,7 @@ def __init__(self) -> None: @only_if_gdb_running @parse_arguments({"address": ""}, {("-r", "--retval"): 0}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] loc = args.address if args.address else f"*{gef.arch.pc:#x}" StubBreakpoint(loc, args.retval) @@ -6499,7 +6497,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: self.usage() return @@ -6518,7 +6516,7 @@ def __init__(self) -> None: @only_if_gdb_running @parse_arguments({"addr": ""}, {"--reset": False}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: global gef args: argparse.Namespace = kwargs["arguments"] @@ -6555,7 +6553,7 @@ class GlibcHeapArenaCommand(GenericCommand): _syntax_ = _cmdline_ @only_if_gdb_running - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: for arena in gef.heap.arenas: gef_print(str(arena)) return @@ -6575,7 +6573,7 @@ def __init__(self) -> None: @parse_arguments({"address": ""}, {"--allow-unaligned": False, "--number": 1}) @only_if_gdb_running - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] if not args.address: err("Missing chunk address") @@ -6680,7 +6678,7 @@ def __init__(self) -> None: @parse_arguments({"arena_address": ""}, {("--all", "-a"): False, "--allow-unaligned": False, "--min-size": 0, "--max-size": 0, ("--count", "-n"): -1, ("--summary", "-s"): False, "--resolve": False}) @only_if_gdb_running - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args = kwargs["arguments"] ctx = GlibcHeapWalkContext(print_arena=args.all, allow_unaligned=args.allow_unaligned, min_size=args.min_size, max_size=args.max_size, count=args.count, resolve_type=args.resolve, summary=args.summary) if args.all or not args.arena_address: @@ -6776,7 +6774,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not argv: for bin_t in self._bin_types_: gdb.execute(f"heap bins {bin_t}") @@ -6839,7 +6837,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: # Determine if we are using libc with tcache built in (2.26+) if gef.libc.version and gef.libc.version < (2, 26): info("No Tcache in this version of libc") @@ -6936,7 +6934,7 @@ def find_tcache(self) -> int: tcache_addr = heap_base + 0x10 return tcache_addr - def check_thread_ids(self, tids: List[int]) -> List[int]: + def check_thread_ids(self, tids: list[int]) -> list[int]: """Return the subset of tids that are currently valid.""" existing_tids = set(t.num for t in gdb.selected_inferior().threads()) return list(set(tids) & existing_tids) @@ -7093,7 +7091,7 @@ def do_invoke(self, *_: Any, **kwargs: Any) -> None: arena_address = args.arena_address or f"{gef.heap.selected_arena.address:#x}" gef_print(titlify(f"Small Bins for arena at {arena_address}")) - bins: Dict[int, int] = {} + bins: dict[int, int] = {} heap_bins_cmd = gef.gdb.commands["heap bins"] assert isinstance (heap_bins_cmd, GlibcHeapBinsCommand) for i in range(1, 63): @@ -7151,7 +7149,7 @@ class DetailRegistersCommand(GenericCommand): @only_if_gdb_running @parse_arguments({"registers": [""]}, {}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: unchanged_color = gef.config["theme.registers_register_name"] changed_color = gef.config["theme.registers_value_changed"] string_color = gef.config["theme.dereference_string"] @@ -7246,7 +7244,7 @@ def __init__(self) -> None: super().__init__(prefix=True) return - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: err("Missing sub-command (search|get)") self.usage() return @@ -7263,18 +7261,14 @@ class ShellcodeSearchCommand(GenericCommand): api_base = "http://shell-storm.org" search_url = f"{api_base}/api/?s=" - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not argv: err("Missing pattern to search") self.usage() return - self.search_shellcode(argv) - return - - def search_shellcode(self, search_options: List) -> None: # API : http://shell-storm.org/shellcode/ - args = "*".join(search_options) + args = "*".join(argv) res = http_get(self.search_url + args) if res is None: @@ -7312,7 +7306,7 @@ class ShellcodeGetCommand(GenericCommand): api_base = "http://shell-storm.org" get_url = f"{api_base}/shellcode/files/shellcode-{{:d}}.html" - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) != 1: err("Missing ID to download") self.usage() @@ -7358,7 +7352,7 @@ def __init__(self) -> None: return @parse_arguments({"pattern": ""}, {"--attach": False, "--smart-scan": False}) - def do_invoke(self, _: List, **kwargs: Any) -> None: + def do_invoke(self, _: list, **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] do_attach = args.attach smart_scan = args.smart_scan @@ -7388,7 +7382,7 @@ def do_invoke(self, _: List, **kwargs: Any) -> None: return None - def get_processes(self) -> Generator[Dict[str, str], None, None]: + def get_processes(self) -> Generator[dict[str, str], None, None]: output = gef_execute_external(self["ps_command"].split(), True) names = [x.lower().replace("%", "") for x in output[0].split()] @@ -7421,7 +7415,7 @@ def __init__(self) -> None: return @parse_arguments({}, {"--filename": ""}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] if is_qemu_system(): @@ -7500,7 +7494,7 @@ def __init__(self) -> None: self["entrypoint_symbols"] = ("main _main __libc_start_main __uClibc_main start _start", "Possible symbols for entry points") return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: fpath = get_filepath() if fpath is None: warn("No executable to debug, use `file` to load a binary") @@ -7554,7 +7548,7 @@ def set_init_tbreak(self, addr: int) -> EntryBreakBreakpoint: bp = EntryBreakBreakpoint(f"*{addr:#x}") return bp - def set_init_tbreak_pie(self, addr: int, argv: List[str]) -> EntryBreakBreakpoint: + def set_init_tbreak_pie(self, addr: int, argv: list[str]) -> EntryBreakBreakpoint: warn("PIC binary detected, retrieving text base address") gdb.execute("set stop-on-solib-events 1") hide_context() @@ -7580,7 +7574,7 @@ def __init__(self) -> None: return @parse_arguments({"name": "", "address": "*$pc"}, {}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] if not args.name: err("Missing name for breakpoint") @@ -7602,7 +7596,7 @@ class ContextCommand(GenericCommand): _syntax_ = f"{_cmdline_} [legend|regs|stack|code|args|memory|source|trace|threads|extra]" _aliases_ = ["ctx",] - old_registers: Dict[str, Optional[int]] = {} + old_registers: dict[str, Optional[int]] = {} def __init__(self) -> None: super().__init__() @@ -7630,7 +7624,7 @@ def __init__(self) -> None: self["libc_args"] = (False, "[DEPRECATED - Unused] Show libc function call args description") self["libc_args_path"] = ("", "[DEPRECATED - Unused] Path to libc function call args json files, provided via gef-extras") - self.layout_mapping: Dict[str, Tuple[Callable, Optional[Callable], Optional[Callable]]] = { + self.layout_mapping: dict[str, Tuple[Callable, Optional[Callable], Optional[Callable]]] = { "legend": (self.show_legend, None, None), "regs": (self.context_regs, None, None), "stack": (self.context_stack, None, None), @@ -7663,7 +7657,7 @@ def show_legend(self) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not self["enable"] or gef.ui.context_hidden: return @@ -7826,7 +7820,7 @@ def context_stack(self) -> None: return - def addr_has_breakpoint(self, address: int, bp_locations: List[str]) -> bool: + def addr_has_breakpoint(self, address: int, bp_locations: list[str]) -> bool: return any(hex(address) in b for b in bp_locations) def context_code(self) -> None: @@ -8026,7 +8020,7 @@ def __get_current_block_start_address() -> Optional[int]: gef_print(")") return - def line_has_breakpoint(self, file_name: str, line_number: int, bp_locations: List[str]) -> bool: + def line_has_breakpoint(self, file_name: str, line_number: int, bp_locations: list[str]) -> bool: filename_line = f"{file_name}:{line_number}" return any(filename_line in loc for loc in bp_locations) @@ -8306,7 +8300,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: self.usage() return @@ -8324,7 +8318,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) not in (1, 2, 3): self.usage() return @@ -8363,7 +8357,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not argv: self.usage() return @@ -8384,7 +8378,7 @@ class MemoryWatchResetCommand(GenericCommand): _syntax_ = f"{_cmdline_}" @only_if_gdb_running - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: gef.ui.watches.clear() ok("Memory watches cleared") return @@ -8397,7 +8391,7 @@ class MemoryWatchListCommand(GenericCommand): _syntax_ = f"{_cmdline_}" @only_if_gdb_running - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: if not gef.ui.watches: info("No memory watches") return @@ -8425,7 +8419,7 @@ def __init__(self) -> None: @only_if_gdb_running @parse_arguments({"address": "",}, {("--reverse", "-r"): False, ("--size", "-s"): 0}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: valid_formats = ["byte", "word", "dword", "qword"] if not self.format or self.format not in valid_formats: err("Invalid command") @@ -8452,7 +8446,7 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: gef_print("\n".join(lines)) return - def _hexdump(self, start_addr: int, length: int, arrange_as: str, offset: int = 0) -> List[str]: + def _hexdump(self, start_addr: int, length: int, arrange_as: str, offset: int = 0) -> list[str]: endianness = gef.arch.endianness base_address_color = gef.config["theme.dereference_base_address"] @@ -8563,7 +8557,7 @@ def __init__(self) -> None: @only_if_gdb_running @parse_arguments({"location": "", "values": ["", ]}, {}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] if not self.format or self.format not in self.SUPPORTED_SIZES: self.usage() @@ -8663,7 +8657,7 @@ class PatchStringCommand(GenericCommand): ] @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: argc = len(argv) if argc != 2: self.usage() @@ -8681,7 +8675,7 @@ def do_invoke(self, argv: List[str]) -> None: @lru_cache() -def dereference_from(address: int) -> List[str]: +def dereference_from(address: int) -> list[str]: if not is_alive(): return [format_address(address),] @@ -8791,7 +8785,7 @@ def pprint_dereferenced(addr: int, idx: int, base_offset: int = 0) -> str: @only_if_gdb_running @parse_arguments({"address": "$sp"}, {("-r", "--reference"): "", ("-l", "--length"): 10}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] nb = args.length @@ -8845,7 +8839,7 @@ class ASLRCommand(GenericCommand): _cmdline_ = "aslr" _syntax_ = f"{_cmdline_} [(on|off)]" - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: argc = len(argv) if argc == 0: @@ -8887,7 +8881,7 @@ class ResetCacheCommand(GenericCommand): _cmdline_ = "reset-cache" _syntax_ = _cmdline_ - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: reset_all_caches() return @@ -8902,7 +8896,7 @@ class VMMapCommand(GenericCommand): _example_ = f"{_cmdline_} libc" @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: vmmap = gef.memory.maps if not vmmap: err("No address mapping information found") @@ -8984,7 +8978,7 @@ class XFilesCommand(GenericCommand): _example_ = f"\n{_cmdline_} libc\n{_cmdline_} libc IO_vtables" @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: color = gef.config["theme.table_heading"] headers = ["Start", "End", "Name", "File"] gef_print(Color.colorify("{:<{w}s}{:<{w}s}{:<21s} {:s}".format(*headers, w=gef.arch.ptrsize*2+3), color)) @@ -9022,7 +9016,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not argv: err("At least one valid address must be specified") self.usage() @@ -9083,7 +9077,7 @@ def __init__(self) -> None: super().__init__(prefix=True) return - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: self.usage() return @@ -9098,7 +9092,7 @@ class XorMemoryDisplayCommand(GenericCommand): _example_ = f"{_cmdline_} $sp 16 41414141" @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) != 3: self.usage() return @@ -9127,7 +9121,7 @@ class XorMemoryPatchCommand(GenericCommand): _example_ = f"{_cmdline_} $sp 16 41414141" @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) != 3: self.usage() return @@ -9159,7 +9153,7 @@ def __init__(self) -> None: return @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) not in (1, 2): self.usage() return @@ -9246,7 +9240,7 @@ def __init__(self) -> None: self["length"] = (1024, "Default length of a cyclic buffer to generate") return - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: self.usage() return @@ -9265,7 +9259,7 @@ class PatternCreateCommand(GenericCommand): ] @parse_arguments({"length": 0}, {"-n": 0,}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] length = args.length or gef.config["pattern.length"] n = args.n or gef.arch.ptrsize @@ -9292,7 +9286,7 @@ class PatternSearchCommand(GenericCommand): @only_if_gdb_running @parse_arguments({"pattern": ""}, {("--period", "-n"): 0, ("--max-length", "-l"): 0}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args = kwargs["arguments"] if not args.pattern: warn("No pattern provided") @@ -9361,7 +9355,7 @@ def __init__(self) -> None: super().__init__(complete=gdb.COMPLETE_FILENAME) return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: argc = len(argv) if argc == 0: @@ -9426,7 +9420,7 @@ def build_line(self, name: str, color: str, address_val: int, got_address: int) @only_if_gdb_running @parse_arguments({"symbols": [""]}, {"--all": False}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] vmmap = gef.memory.maps mapfiles = [mapfile for mapfile in vmmap if @@ -9436,7 +9430,7 @@ def do_invoke(self, _: List[str], **kwargs: Any) -> None: for mapfile in mapfiles: self.print_got_for(mapfile.path, mapfile.realpath, args.symbols) - def print_got_for(self, file: str, realpath: str, argv: List[str]) -> None: + def print_got_for(self, file: str, realpath: str, argv: list[str]) -> None: readelf = gef.session.constants["readelf"] elf_file = realpath @@ -9505,7 +9499,7 @@ def __init__(self) -> None: super().__init__(prefix=True) self["regex"] = (False, "Enable regex highlighting") - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: return self.usage() @@ -9527,7 +9521,7 @@ def print_highlight_table(self) -> None: f"{Color.colorify(color, color)}") return - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: return self.print_highlight_table() @@ -9538,7 +9532,7 @@ class HighlightClearCommand(GenericCommand): _aliases_ = ["hlc"] _syntax_ = _cmdline_ - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: return gef.ui.highlight_table.clear() @@ -9550,7 +9544,7 @@ class HighlightAddCommand(GenericCommand): _aliases_ = ["highlight set", "hla"] _example_ = f"{_cmdline_} 41414141 yellow" - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) < 2: return self.usage() @@ -9573,7 +9567,7 @@ class HighlightRemoveCommand(GenericCommand): ] _example_ = f"{_cmdline_} remove 41414141" - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not argv: return self.usage() @@ -9591,7 +9585,7 @@ class FormatStringSearchCommand(GenericCommand): _syntax_ = _cmdline_ _aliases_ = ["fmtstr-helper",] - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: dangerous_functions = { "printf": 0, "sprintf": 1, @@ -9641,7 +9635,7 @@ def __init__(self) -> None: @only_if_gdb_running @experimental_feature - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if not argv: self.setup() return @@ -9742,7 +9736,7 @@ def invoke(self, *args: Any) -> int: raise gdb.GdbError("No debugging session active") return self.do_invoke(args) - def arg_to_long(self, args: List, index: int, default: int = 0) -> int: + def arg_to_long(self, args: Any, index: int, default: int = 0) -> int: try: addr = args[index] return int(addr) if addr.address is None else int(addr.address) @@ -9759,7 +9753,7 @@ class StackOffsetFunction(GenericFunction): _function_ = "_stack" _syntax_ = f"${_function_}()" - def do_invoke(self, args: List) -> int: + def do_invoke(self, args: list) -> int: base = get_section_base_address("[stack]") if not base: raise gdb.GdbError("Stack not found") @@ -9773,7 +9767,7 @@ class HeapBaseFunction(GenericFunction): _function_ = "_heap" _syntax_ = f"${_function_}()" - def do_invoke(self, args: List) -> int: + def do_invoke(self, args: list[str]) -> int: base = gef.heap.base_address if not base: base = get_section_base_address("[heap]") @@ -9790,7 +9784,7 @@ class SectionBaseFunction(GenericFunction): _syntax_ = "$_base([filepath])" _example_ = "p $_base(\\\"/usr/lib/ld-2.33.so\\\")" - def do_invoke(self, args: List) -> int: + def do_invoke(self, args: list) -> int: addr = 0 try: name = args[0].string() @@ -9818,7 +9812,7 @@ class BssBaseFunction(GenericFunction): _syntax_ = f"${_function_}([OFFSET])" _example_ = "deref $_bss(0x20)" - def do_invoke(self, args: List) -> int: + def do_invoke(self, args: list) -> int: base = get_zone_base_address(".bss") if not base: raise gdb.GdbError("BSS not found") @@ -9832,7 +9826,7 @@ class GotBaseFunction(GenericFunction): _syntax_ = f"${_function_}([OFFSET])" _example_ = "deref $_got(0x20)" - def do_invoke(self, args: List) -> int: + def do_invoke(self, args: list) -> int: base = get_zone_base_address(".got") if not base: raise gdb.GdbError("GOT not found") @@ -9922,24 +9916,24 @@ def __init__(self) -> None: gef.config["gef.propagate_debug_exception"] = GefSetting(False, bool, "If true, when debug mode is enabled, Python exceptions will be propagated all the way.") gef.config["gef.extra_python_package_paths"] = GefSetting("", str, "Semi-colon separate list of extra paths to include for python packages") - self.commands : Dict[str, GenericCommand] = collections.OrderedDict() - self.functions : Dict[str, GenericFunction] = collections.OrderedDict() - self.missing: Dict[str, Exception] = {} + self.commands : dict[str, GenericCommand] = collections.OrderedDict() + self.functions : dict[str, GenericFunction] = collections.OrderedDict() + self.missing: dict[str, Exception] = {} return @property @deprecated() - def loaded_commands(self) -> List[Tuple[str, Type[GenericCommand], Any]]: + def loaded_commands(self) -> list[Tuple[str, Type[GenericCommand], Any]]: raise ObsoleteException("Obsolete loaded_commands") @property @deprecated() - def loaded_functions(self) -> List[Type[GenericFunction]]: + def loaded_functions(self) -> list[Type[GenericFunction]]: raise ObsoleteException("Obsolete loaded_functions") @property @deprecated() - def missing_commands(self) -> Dict[str, Exception]: + def missing_commands(self) -> dict[str, Exception]: raise ObsoleteException("Obsolete missing_commands") def setup(self) -> None: @@ -10221,7 +10215,7 @@ def print_settings(self) -> None: self.print_setting(x) return - def set_setting(self, argv: List[str]) -> bool: + def set_setting(self, argv: list[str]) -> bool: global gef key, new_value = argv @@ -10257,7 +10251,7 @@ def set_setting(self, argv: List[str]) -> bool: reset_all_caches() return True - def complete(self, text: str, word: str) -> List[str]: + def complete(self, text: str, word: str) -> list[str]: settings = sorted(gef.config) if text == "": @@ -10373,7 +10367,7 @@ def __init__(self) -> None: def invoke(self, args: Any, from_tty: bool) -> None: self.dont_repeat() - missing_commands: Dict[str, Exception] = gef.gdb.missing + missing_commands: dict[str, Exception] = gef.gdb.missing if not missing_commands: ok("No missing command") return @@ -10486,7 +10480,7 @@ def __init__(self) -> None: super().__init__(prefix=True) return - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: self.usage() return @@ -10503,7 +10497,7 @@ def __init__(self) -> None: super().__init__() return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: if len(argv) < 2: self.usage() return @@ -10522,7 +10516,7 @@ def __init__(self) -> None: super().__init__() return - def do_invoke(self, argv: List[str]) -> None: + def do_invoke(self, argv: list[str]) -> None: global gef if len(argv) != 1: self.usage() @@ -10548,7 +10542,7 @@ def __init__(self) -> None: super().__init__() return - def do_invoke(self, _: List[str]) -> None: + def do_invoke(self, _: list[str]) -> None: ok("Aliases defined:") for a in gef.session.aliases: gef_print(f"{a.alias:30s} {RIGHT_ARROW} {a.command}") @@ -10717,7 +10711,7 @@ def __init__(self) -> None: def reset_caches(self) -> None: super().reset_caches() - self.__maps: Optional[List[Section]] = None + self.__maps: Optional[list[Section]] = None return def write(self, address: int, buffer: ByteString, length: Optional[int] = None) -> None: @@ -10772,7 +10766,7 @@ def read_ascii_string(self, address: int) -> Optional[str]: return None @property - def maps(self) -> List[Section]: + def maps(self) -> list[Section]: if not self.__maps: maps = self.__parse_maps() if not maps: @@ -10780,7 +10774,7 @@ def maps(self) -> List[Section]: self.__maps = maps return self.__maps - def __parse_maps(self) -> Optional[List[Section]]: + def __parse_maps(self) -> Optional[list[Section]]: """Return the mapped memory sections. If the current arch has its maps method defined, then defer to that to generated maps, otherwise, try to figure it out from procfs, then info sections, then monitor info @@ -11077,7 +11071,7 @@ def selected_arena(self, value: GlibcArena) -> None: return @property - def arenas(self) -> Union[List, Iterator[GlibcArena]]: + def arenas(self) -> list | Iterator[GlibcArena]: if not self.main_arena: return [] return iter(self.main_arena) @@ -11098,7 +11092,7 @@ def base_address(self) -> Optional[int]: return self.__heap_base @property - def chunks(self) -> Union[List, Iterator]: + def chunks(self) -> list | Iterator: if not self.base_address: return [] return iter(GlibcChunk(self.base_address, from_base=True)) @@ -11127,20 +11121,20 @@ def tidx2size(self, idx: int) -> int: def malloc_align_address(self, address: int) -> int: """Align addresses according to glibc's MALLOC_ALIGNMENT. See also Issue #689 on Github""" - def ceil(n: int) -> int: + def ceil(n: float) -> int: return int(-1 * n // 1 * -1) malloc_alignment = self.malloc_alignment - return malloc_alignment * ceil((address // malloc_alignment)) + return malloc_alignment * ceil((address / malloc_alignment)) class GefSetting: """Basic class for storing gef settings as objects""" - def __init__(self, value: Any, cls: Optional[type] = None, description: Optional[str] = None, hooks: Optional[Dict[str, List[Callable]]] = None) -> None: + def __init__(self, value: Any, cls: Optional[type] = None, description: Optional[str] = None, hooks: Optional[dict[str, list[Callable]]] = None) -> None: self.value = value self.type = cls or type(value) self.description = description or "" - self.hooks: Dict[str, List[Callable]] = collections.defaultdict(list) + self.hooks: dict[str, list[Callable]] = collections.defaultdict(list) if not hooks: hooks = {"on_read": [], "on_write": [], "on_changed": []} @@ -11153,7 +11147,7 @@ def __str__(self) -> str: f"read_hooks={len(self.hooks['on_read'])}, write_hooks={len(self.hooks['on_write'])}, "\ f"changed_hooks={len(self.hooks['on_changed'])})" - def add_hook(self, access: str, funcs: List[Callable]): + def add_hook(self, access: str, funcs: list[Callable]): if access not in ("on_read", "on_write", "on_changed"): raise ValueError("invalid access type") for func in funcs: @@ -11241,13 +11235,13 @@ def __init__(self) -> None: self.remote_initializing: bool = False self.qemu_mode: bool = False self.convenience_vars_index: int = 0 - self.heap_allocated_chunks: List[Tuple[int, int]] = [] - self.heap_freed_chunks: List[Tuple[int, int]] = [] - self.heap_uaf_watchpoints: List[UafWatchpoint] = [] - self.pie_breakpoints: Dict[int, PieVirtualBreakpoint] = {} + self.heap_allocated_chunks: list[Tuple[int, int]] = [] + self.heap_freed_chunks: list[Tuple[int, int]] = [] + self.heap_uaf_watchpoints: list[UafWatchpoint] = [] + self.pie_breakpoints: dict[int, PieVirtualBreakpoint] = {} self.pie_counter: int = 1 - self.aliases: List[GefAlias] = [] - self.modules: List[FileFormat] = [] + self.aliases: list[GefAlias] = [] + self.modules: list[FileFormat] = [] self.constants = {} # a dict for runtime constants (like 3rd party file paths) for constant in ("python3", "readelf", "nm", "file", "ps"): self.constants[constant] = which(constant) @@ -11272,7 +11266,7 @@ def __repr__(self) -> str: return str(self) @property - def auxiliary_vector(self) -> Optional[Dict[str, int]]: + def auxiliary_vector(self) -> Optional[dict[str, int]]: if not is_alive(): return None if is_qemu_system(): @@ -11621,9 +11615,9 @@ def __init__(self) -> None: self.redirect_fd : Optional[TextIOWrapper] = None self.context_hidden = False self.stream_buffer : Optional[StringIO] = None - self.highlight_table: Dict[str, str] = {} - self.watches: Dict[int, Tuple[int, str]] = {} - self.context_messages: List[Tuple[str, str]] = [] + self.highlight_table: dict[str, str] = {} + self.watches: dict[int, Tuple[int, str]] = {} + self.context_messages: list[Tuple[str, str]] = [] return diff --git a/tests/base.py b/tests/base.py index e284dd0da..fc209be0c 100644 --- a/tests/base.py +++ b/tests/base.py @@ -110,6 +110,8 @@ def tearDown(self) -> None: @property def gdb_version(self) -> Tuple[int, int]: - res = [int(d) for d in re.search(r"(\d+)\D(\d+)", self._gdb.VERSION).groups()] - assert len(res) >= 2 - return tuple(res) + res = re.search(r"(\d+)\D(\d+)", self._gdb.VERSION) + assert res + groups = [int(d) for d in res.groups()] + assert len(groups) == 2 + return (groups[0], groups[1]) diff --git a/tests/commands/gef_remote.py b/tests/commands/gef_remote.py index 578547f8b..34fd0ab60 100644 --- a/tests/commands/gef_remote.py +++ b/tests/commands/gef_remote.py @@ -71,5 +71,5 @@ def test_cmd_target_remote(self): with gdbserver_session(port=port) as _: gdb.execute(f"target remote {GDBSERVER_DEFAULT_HOST}:{port}") res: str = root.eval("str(gef.session.remote)") - assert res.startswith(f"RemoteSession(target=':0', local='/tmp/") + assert res.startswith("RemoteSession(target=':0', local='/tmp/") assert res.endswith(f"pid={gef.session.pid}, mode={gdbserver_mode})") diff --git a/tests/commands/theme.py b/tests/commands/theme.py index a09cfaa6f..44744475e 100644 --- a/tests/commands/theme.py +++ b/tests/commands/theme.py @@ -41,5 +41,5 @@ def test_cmd_theme(self): gdb.execute(f"theme {t} {v}") - res = gdb.execute(f"theme ___I_DONT_EXIST___", to_string=True) + res = gdb.execute("theme ___I_DONT_EXIST___", to_string=True) self.assertIn("Invalid key", res) From b83586e0c0c25877ffd4d2619ebf854951cdb0f6 Mon Sep 17 00:00:00 2001 From: hugsy Date: Fri, 1 Nov 2024 12:11:46 -0700 Subject: [PATCH 03/11] removed `Set` and `Tuple` types (replaced with `tuple` and `set`) --- gef.py | 132 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 66 insertions(+), 66 deletions(-) diff --git a/gef.py b/gef.py index 17d602c23..bb6054ace 100644 --- a/gef.py +++ b/gef.py @@ -115,10 +115,10 @@ def update_gef(argv: list[str]) -> int: sys.exit(1) -GDB_MIN_VERSION: Tuple[int, int] = (8, 0) -PYTHON_MIN_VERSION: Tuple[int, int] = (3, 6) -PYTHON_VERSION: Tuple[int, int] = sys.version_info[0:2] -GDB_VERSION: Tuple[int, int] = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups())) # type:ignore +GDB_MIN_VERSION: tuple[int, int] = (8, 0) +PYTHON_MIN_VERSION: tuple[int, int] = (3, 6) +PYTHON_VERSION: tuple[int, int] = sys.version_info[0:2] +GDB_VERSION: tuple[int, int] = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups())) # type:ignore DEFAULT_PAGE_ALIGN_SHIFT = 12 DEFAULT_PAGE_SIZE = 1 << DEFAULT_PAGE_ALIGN_SHIFT @@ -144,10 +144,10 @@ def update_gef(argv: list[str]) -> int: GEF_PROMPT_ON = f"\001\033[1;32m\002{GEF_PROMPT}\001\033[0m\002" GEF_PROMPT_OFF = f"\001\033[1;31m\002{GEF_PROMPT}\001\033[0m\002" -__registered_commands__ : Set[Type["GenericCommand"]] = set() -__registered_functions__ : Set[Type["GenericFunction"]] = set() +__registered_commands__ : set[Type["GenericCommand"]] = set() +__registered_functions__ : set[Type["GenericFunction"]] = set() __registered_architectures__ : dict[Union["Elf.Abi", str], Type["Architecture"]] = {} -__registered_file_formats__ : Set[ Type["FileFormat"] ] = set() +__registered_file_formats__ : set[ Type["FileFormat"] ] = set() GefMemoryMapProvider = Callable[[], Generator["Section", None, None]] @@ -446,8 +446,8 @@ def FakeExit(*args: Any, **kwargs: Any) -> NoReturn: sys.exit = FakeExit -def parse_arguments(required_arguments: dict[Union[str, Tuple[str, str]], Any], - optional_arguments: dict[Union[str, Tuple[str, str]], Any]) -> Callable: +def parse_arguments(required_arguments: dict[Union[str, tuple[str, str]], Any], + optional_arguments: dict[Union[str, tuple[str, str]], Any]) -> Callable: """Argument parsing decorator.""" def int_wrapper(x: str) -> int: return int(x, 0) @@ -953,7 +953,7 @@ def __init__(self, path: Union[str, pathlib.Path]) -> None: def read(self, size: int) -> bytes: return self.fd.read(size) - def read_and_unpack(self, fmt: str) -> Tuple[Any, ...]: + def read_and_unpack(self, fmt: str) -> tuple[Any, ...]: size = struct.calcsize(fmt) data = self.fd.read(size) return struct.unpack(fmt, data) @@ -1523,7 +1523,7 @@ def fastbin(self, i: int) -> Optional["GlibcFastChunk"]: return None return GlibcFastChunk(addr + 2 * gef.arch.ptrsize) - def bin(self, i: int) -> Tuple[int, int]: + def bin(self, i: int) -> tuple[int, int]: idx = i * 2 fd = int(self.bins[idx]) bk = int(self.bins[idx + 1]) @@ -1824,7 +1824,7 @@ class GlibcTcacheChunk(GlibcFastChunk): pass @deprecated("Use GefLibcManager.find_libc_version()") -def get_libc_version() -> Tuple[int, ...]: +def get_libc_version() -> tuple[int, ...]: return GefLibcManager.find_libc_version() def titlify(text: str, color: Optional[str] = None, msg_color: Optional[str] = None) -> str: @@ -2091,7 +2091,7 @@ def gef_makedirs(path: str, mode: int = 0o755) -> pathlib.Path: @lru_cache() -def gdb_lookup_symbol(sym: str) -> Optional[Tuple[gdb.Symtab_and_line, ...]]: +def gdb_lookup_symbol(sym: str) -> Optional[tuple[gdb.Symtab_and_line, ...]]: """Fetch the proper symbol or None if not defined.""" try: res = gdb.decode_line(sym)[1] # pylint: disable=E1136 @@ -2100,7 +2100,7 @@ def gdb_lookup_symbol(sym: str) -> Optional[Tuple[gdb.Symtab_and_line, ...]]: return None @lru_cache(maxsize=512) -def gdb_get_location_from_symbol(address: int) -> Optional[Tuple[str, int]]: +def gdb_get_location_from_symbol(address: int) -> Optional[tuple[str, int]]: """Retrieve the location of the `address` argument from the symbol table. Return a tuple with the name and offset if found, None otherwise.""" # this is horrible, ugly hack and shitty perf... @@ -2340,7 +2340,7 @@ def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]: class ArchitectureBase: """Class decorator for declaring an architecture to GEF.""" - aliases: Union[Tuple[()], Tuple[Union[str, Elf.Abi], ...]] = () + aliases: Union[tuple[()], tuple[Union[str, Elf.Abi], ...]] = () def __init_subclass__(cls: Type["ArchitectureBase"], **kwargs): global __registered_architectures__ @@ -2360,20 +2360,20 @@ class Architecture(ArchitectureBase): # Mandatory defined attributes by inheriting classes arch: str mode: str - all_registers: Union[Tuple[()], Tuple[str, ...]] + all_registers: Union[tuple[()], tuple[str, ...]] nop_insn: bytes return_register: str flag_register: Optional[str] instruction_length: Optional[int] flags_table: dict[int, str] syscall_register: Optional[str] - syscall_instructions: Union[Tuple[()], Tuple[str, ...]] - function_parameters: Union[Tuple[()], Tuple[str, ...]] + syscall_instructions: Union[tuple[()], tuple[str, ...]] + function_parameters: Union[tuple[()], tuple[str, ...]] # Optionally defined attributes _ptrsize: Optional[int] = None _endianness: Optional[Endianness] = None - special_registers: Union[Tuple[()], Tuple[str, ...]] = () + special_registers: Union[tuple[()], tuple[str, ...]] = () maps: Optional[GefMemoryMapProvider] = None def __init_subclass__(cls, **kwargs): @@ -2409,7 +2409,7 @@ def is_ret(self, insn: Instruction) -> bool: def is_conditional_branch(self, insn: Instruction) -> bool: raise NotImplementedError - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: + def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: raise NotImplementedError def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: @@ -2488,7 +2488,7 @@ def endianness(self) -> Endianness: raise OSError(f"No valid endianess found in '{output}'") return self._endianness - def get_ith_parameter(self, i: int, in_func: bool = True) -> Tuple[str, Optional[int]]: + def get_ith_parameter(self, i: int, in_func: bool = True) -> tuple[str, Optional[int]]: """Retrieves the correct parameter used for the current function call.""" reg = self.function_parameters[i] val = self.register(reg) @@ -2563,7 +2563,7 @@ def ptrsize(self) -> int: def is_conditional_branch(self, insn: Instruction) -> bool: return insn.mnemonic.startswith("b") - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: + def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: def long_to_twos_complement(v: int) -> int: """Convert a python long value to its two's complement.""" if is_32bit(): @@ -2716,7 +2716,7 @@ def is_conditional_branch(self, insn: Instruction) -> bool: conditions = {"eq", "ne", "lt", "le", "gt", "ge", "vs", "vc", "mi", "pl", "hi", "ls", "cc", "cs"} return insn.mnemonic[-2:] in conditions - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: + def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: mnemo = insn.mnemonic # ref: https://www.davespace.co.uk/arm/introduction-to-arm/conditional.html flags = dict((self.flags_table[k], k) for k in self.flags_table) @@ -2882,7 +2882,7 @@ def is_conditional_branch(self, insn: Instruction) -> bool: branch_mnemos = {"cbnz", "cbz", "tbnz", "tbz"} return mnemo.startswith("b.") or mnemo in branch_mnemos - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: + def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: mnemo, operands = insn.mnemonic, insn.operands taken, reason = False, "" @@ -2914,7 +2914,7 @@ def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: class X86(Architecture): - aliases: Tuple[Union[str, Elf.Abi], ...] = ("X86", Elf.Abi.X86_32) + aliases: tuple[Union[str, Elf.Abi], ...] = ("X86", Elf.Abi.X86_32) arch = "X86" mode = "32" @@ -2969,7 +2969,7 @@ def is_conditional_branch(self, insn: Instruction) -> bool: } return mnemo in branch_mnemos - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: + def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: mnemo = insn.mnemonic # all kudos to fG! (https://github.com/gdbinit/Gdbinit/blob/master/gdbinit#L1654) flags = dict((self.flags_table[k], k) for k in self.flags_table) @@ -3042,7 +3042,7 @@ def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: ] return "; ".join(insns) - def get_ith_parameter(self, i: int, in_func: bool = True) -> Tuple[str, Optional[int]]: + def get_ith_parameter(self, i: int, in_func: bool = True) -> tuple[str, Optional[int]]: if in_func: i += 1 # Account for RA being at the top of the stack sp = gef.arch.sp @@ -3149,7 +3149,7 @@ def is_conditional_branch(self, insn: Instruction) -> bool: branch_mnemos = {"beq", "bne", "ble", "blt", "bgt", "bge"} return mnemo in branch_mnemos - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: + def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: mnemo = insn.mnemonic flags = dict((self.flags_table[k], k) for k in self.flags_table) val = gef.arch.register(self.flag_register) @@ -3257,7 +3257,7 @@ def is_conditional_branch(self, insn: Instruction) -> bool: } return mnemo in branch_mnemos - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: + def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: mnemo = insn.mnemonic flags = dict((self.flags_table[k], k) for k in self.flags_table) val = gef.arch.register(self.flag_register) @@ -3358,7 +3358,7 @@ def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: class MIPS(Architecture): - aliases: Tuple[Union[str, Elf.Abi], ...] = ("MIPS", Elf.Abi.MIPS) + aliases: tuple[Union[str, Elf.Abi], ...] = ("MIPS", Elf.Abi.MIPS) arch = "MIPS" mode = "MIPS32" @@ -3393,7 +3393,7 @@ def is_conditional_branch(self, insn: Instruction) -> bool: branch_mnemos = {"beq", "bne", "beqz", "bnez", "bgtz", "bgez", "bltz", "blez"} return mnemo in branch_mnemos - def is_branch_taken(self, insn: Instruction) -> Tuple[bool, str]: + def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: mnemo, ops = insn.mnemonic, insn.operands taken, reason = False, "" @@ -3769,7 +3769,7 @@ def regchanged_handler(_: "gdb.RegisterChangedEvent") -> None: return -def get_terminal_size() -> Tuple[int, int]: +def get_terminal_size() -> tuple[int, int]: """Return the current terminal size.""" if is_debug(): return 600, 100 @@ -4089,7 +4089,7 @@ def gef_getpagesize() -> int: @deprecated("Use `gef.session.canary`") -def gef_read_canary() -> Optional[Tuple[int, int]]: +def gef_read_canary() -> Optional[tuple[int, int]]: return gef.session.canary @@ -4561,7 +4561,7 @@ def stop(self) -> bool: return False # software watchpoints stop after the next statement (see - # https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html) + # https://sourceware.org/gdb/onlinedocs/gdb/set-Watchpoints.html) pc = gdb_get_nth_previous_instruction_address(gef.arch.pc, 2) assert pc insn = gef_current_instruction(pc) @@ -4778,10 +4778,10 @@ def __contains__(self, name: str) -> bool: return self.__get_setting_name(name) in gef.config @deprecated("Use `self[setting_name] = value` instead") - def add_setting(self, name: str, value: Tuple[Any, type, str], description: str = "") -> None: + def add_setting(self, name: str, value: tuple[Any, type, str], description: str = "") -> None: return self.__setitem__(name, (value, description)) - def __setitem__(self, name: str, value: Union["GefSetting", Tuple[Any, str]]) -> None: + def __setitem__(self, name: str, value: Union["GefSetting", tuple[Any, str]]) -> None: # make sure settings are always associated to the root command (which derives from GenericCommand) if "GenericCommand" not in [x.__name__ for x in self.__class__.__bases__]: return @@ -4855,7 +4855,7 @@ def do_invoke(self, args: list[str]) -> None: @register class ArchSetCommand(GenericCommand): - """Set the current loaded architecture.""" + """set the current loaded architecture.""" _cmdline_ = "arch set" _syntax_ = f"{_cmdline_} " @@ -4943,7 +4943,7 @@ def __init__(self) -> None: return @property - def format_matrix(self) -> dict[int, Tuple[str, str, str]]: + def format_matrix(self) -> dict[int, tuple[str, str, str]]: # `gef.arch.endianness` is a runtime property, should not be defined as a class property return { 8: (f"{gef.arch.endianness}B", "char", "db"), @@ -5030,7 +5030,7 @@ def do_invoke(self, argv: list[str]) -> None: @register class PieBreakpointCommand(GenericCommand): - """Set a PIE breakpoint at an offset from the target binaries base address.""" + """set a PIE breakpoint at an offset from the target binaries base address.""" _cmdline_ = "pie breakpoint" _syntax_ = f"{_cmdline_} OFFSET" @@ -5410,7 +5410,7 @@ def list_sockets(self, pid: int) -> list[int]: sockets.append(int(p)) return sockets - def parse_ip_port(self, addr: str) -> Tuple[str, int]: + def parse_ip_port(self, addr: str) -> tuple[str, int]: ip, port = addr.split(":") return socket.inet_ntoa(struct.pack(" str: for name, values in struct._values_: if name != item: continue if callable(values): - return values(value) + return str(values(value)) try: for val, desc in values: if value == val: return desc @@ -5709,14 +5709,14 @@ def path(self) -> pathlib.Path: return self._path @property - def structures(self) -> Generator[Tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"], None, None]: + def structures(self) -> Generator[tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"], None, None]: for module in self.modules.values(): for structure in module.values(): yield module, structure return @lru_cache() - def find(self, structure_name: str) -> Optional[Tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"]]: + def find(self, structure_name: str) -> Optional[tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"]]: """Return the module and structure for the given structure name; `None` if the structure name was not found.""" for module in self.modules.values(): if structure_name in module: @@ -5776,7 +5776,7 @@ def do_invoke(self, *_: Any, **kwargs: dict[str, Any]) -> None: structure.apply_at(address, self["max_depth"]) return - def explode_type(self, arg: str) -> Tuple[str, str]: + def explode_type(self, arg: str) -> tuple[str, str]: modname, structname = arg.split(":", 1) if ":" in arg else (arg, arg) structname = structname.split(".", 1)[0] if "." in structname else structname return modname, structname @@ -6061,11 +6061,11 @@ def print_section(self, section: Section) -> None: ok(title) return - def print_loc(self, loc: Tuple[int, int, str]) -> None: + def print_loc(self, loc: tuple[int, int, str]) -> None: gef_print(f""" {loc[0]:#x} - {loc[1]:#x} {RIGHT_ARROW} "{Color.pinkify(loc[2])}" """) return - def search_pattern_by_address(self, pattern: str, start_address: int, end_address: int) -> list[Tuple[int, int, str]]: + def search_pattern_by_address(self, pattern: str, start_address: int, end_address: int) -> list[tuple[int, int, str]]: """Search a pattern within a range defined by arguments.""" _pattern = gef_pybytes(pattern) step = self["nr_pages_chunk"] * gef.session.pagesize @@ -6097,7 +6097,7 @@ def search_pattern_by_address(self, pattern: str, start_address: int, end_addres return locations - def search_binpattern_by_address(self, binpattern: bytes, start_address: int, end_address: int) -> list[Tuple[int, int, str]]: + def search_binpattern_by_address(self, binpattern: bytes, start_address: int, end_address: int) -> list[tuple[int, int, str]]: """Search a binary pattern within a range defined by arguments.""" step = self["nr_pages_chunk"] * gef.session.pagesize @@ -6294,7 +6294,7 @@ def do_invoke(self, _: list[str], **kwargs: Any) -> None: raise e # Try to establish the remote session, throw on error - # Set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which + # set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which # calls `is_remote_debug` which checks if `remote_initializing` is True or `.remote` is None # This prevents some spurious errors being thrown during startup gef.session.remote_initializing = True @@ -6504,7 +6504,7 @@ def do_invoke(self, _: list[str]) -> None: @register class GlibcHeapSetArenaCommand(GenericCommand): - """Set the address of the main_arena or the currently selected arena.""" + """set the address of the main_arena or the currently selected arena.""" _cmdline_ = "heap set-arena" _syntax_ = f"{_cmdline_} [address|&symbol]" @@ -6939,7 +6939,7 @@ def check_thread_ids(self, tids: list[int]) -> list[int]: existing_tids = set(t.num for t in gdb.selected_inferior().threads()) return list(set(tids) & existing_tids) - def tcachebin(self, tcache_base: int, i: int) -> Tuple[Optional[GlibcTcacheChunk], int]: + def tcachebin(self, tcache_base: int, i: int) -> tuple[Optional[GlibcTcacheChunk], int]: """Return the head chunk in tcache[i] and the number of chunks in the bin.""" if i >= self.TCACHE_MAX_BINS: err("Incorrect index value, index value must be between 0 and " @@ -7624,7 +7624,7 @@ def __init__(self) -> None: self["libc_args"] = (False, "[DEPRECATED - Unused] Show libc function call args description") self["libc_args_path"] = ("", "[DEPRECATED - Unused] Path to libc function call args json files, provided via gef-extras") - self.layout_mapping: dict[str, Tuple[Callable, Optional[Callable], Optional[Callable]]] = { + self.layout_mapping: dict[str, tuple[Callable, Optional[Callable], Optional[Callable]]] = { "legend": (self.show_legend, None, None), "regs": (self.context_regs, None, None), "stack": (self.context_stack, None, None), @@ -8668,7 +8668,7 @@ def do_invoke(self, argv: list[str]) -> None: try: msg_as_bytes = codecs.escape_decode(msg, "utf-8")[0] - gef.memory.write(addr, msg_as_bytes, len(msg_as_bytes)) + gef.memory.write(addr, msg_as_bytes, len(msg_as_bytes)) # type: ignore except (binascii.Error, gdb.error): err(f"Could not decode '\\xXX' encoded string \"{msg}\"") return @@ -9912,7 +9912,7 @@ def __init__(self) -> None: gef.config["gef.buffer"] = GefSetting(True, bool, "Internally buffer command output until completion") gef.config["gef.bruteforce_main_arena"] = GefSetting(False, bool, "Allow bruteforcing main_arena symbol if everything else fails") gef.config["gef.libc_version"] = GefSetting("", str, "Specify libc version when auto-detection fails") - gef.config["gef.main_arena_offset"] = GefSetting("", str, "Offset from libc base address to main_arena symbol (int or hex). Set to empty string to disable.") + gef.config["gef.main_arena_offset"] = GefSetting("", str, "Offset from libc base address to main_arena symbol (int or hex). set to empty string to disable.") gef.config["gef.propagate_debug_exception"] = GefSetting(False, bool, "If true, when debug mode is enabled, Python exceptions will be propagated all the way.") gef.config["gef.extra_python_package_paths"] = GefSetting("", str, "Semi-colon separate list of extra paths to include for python packages") @@ -9923,7 +9923,7 @@ def __init__(self) -> None: @property @deprecated() - def loaded_commands(self) -> list[Tuple[str, Type[GenericCommand], Any]]: + def loaded_commands(self) -> list[tuple[str, Type[GenericCommand], Any]]: raise ObsoleteException("Obsolete loaded_commands") @property @@ -10115,7 +10115,7 @@ def __rebuild(self) -> None: self.should_refresh = False return - def __add__(self, command: Tuple[str, GenericCommand]): + def __add__(self, command: tuple[str, GenericCommand]): """Add command to GEF documentation.""" cmd, class_obj = command if " " in cmd: @@ -10127,7 +10127,7 @@ def __add__(self, command: Tuple[str, GenericCommand]): self.docs.append(msg) return self - def __radd__(self, command: Tuple[str, GenericCommand]): + def __radd__(self, command: tuple[str, GenericCommand]): return self.__add__(command) def __str__(self) -> str: @@ -10460,7 +10460,7 @@ def invoke(self, args: Any, from_tty: bool) -> None: gdb.execute(f"{self.command} {args}", from_tty=from_tty) return - def lookup_command(self, cmd: str) -> Optional[Tuple[str, GenericCommand]]: + def lookup_command(self, cmd: str) -> Optional[tuple[str, GenericCommand]]: global gef for _name, _instance in gef.gdb.commands.items(): if cmd == _name: @@ -11235,8 +11235,8 @@ def __init__(self) -> None: self.remote_initializing: bool = False self.qemu_mode: bool = False self.convenience_vars_index: int = 0 - self.heap_allocated_chunks: list[Tuple[int, int]] = [] - self.heap_freed_chunks: list[Tuple[int, int]] = [] + self.heap_allocated_chunks: list[tuple[int, int]] = [] + self.heap_freed_chunks: list[tuple[int, int]] = [] self.heap_uaf_watchpoints: list[UafWatchpoint] = [] self.pie_breakpoints: dict[int, PieVirtualBreakpoint] = {} self.pie_counter: int = 1 @@ -11333,7 +11333,7 @@ def pagesize(self) -> int: return self._pagesize @property - def canary(self) -> Optional[Tuple[int, int]]: + def canary(self) -> Optional[tuple[int, int]]: """Return a tuple of the canary address and value, read from the canonical location if supported by the architecture. Otherwise, read from the auxiliary vector.""" @@ -11347,7 +11347,7 @@ def canary(self) -> Optional[Tuple[int, int]]: return canary, canary_location @property - def original_canary(self) -> Optional[Tuple[int, int]]: + def original_canary(self) -> Optional[tuple[int, int]]: """Return a tuple of the initial canary address and value, read from the auxiliary vector.""" auxval = self.auxiliary_vector @@ -11616,8 +11616,8 @@ def __init__(self) -> None: self.context_hidden = False self.stream_buffer : Optional[StringIO] = None self.highlight_table: dict[str, str] = {} - self.watches: dict[int, Tuple[int, str]] = {} - self.context_messages: list[Tuple[str, str]] = [] + self.watches: dict[int, tuple[int, str]] = {} + self.context_messages: list[tuple[str, str]] = [] return @@ -11627,7 +11627,7 @@ class GefLibcManager(GefManager): PATTERN_LIBC_VERSION_FILENAME = re.compile(r"libc6?[-_](\d+)\.(\d+)\.so") def __init__(self) -> None: - self._version : Optional[Tuple[int, int]] = None + self._version : Optional[tuple[int, int]] = None self._patch: Optional[int] = None self._release: Optional[str] = None return @@ -11636,7 +11636,7 @@ def __str__(self) -> str: return f"Libc(version='{self.version}')" @property - def version(self) -> Optional[Tuple[int, int]]: + def version(self) -> Optional[tuple[int, int]]: if not is_alive(): return None @@ -11654,7 +11654,7 @@ def version(self) -> Optional[Tuple[int, int]]: @staticmethod @lru_cache() - def find_libc_version() -> Tuple[int, int]: + def find_libc_version() -> tuple[int, int]: """Attempt to determine the libc version. This operation can be long.""" libc_sections = (m for m in gef.memory.maps if "libc" in m.path and m.permission & Permission.READ) for section in libc_sections: From 32311d7a910bd26f96347791617bc12e296d6c3c Mon Sep 17 00:00:00 2001 From: hugsy Date: Fri, 1 Nov 2024 13:24:16 -0700 Subject: [PATCH 04/11] `Union` type -> `|` --- gef.py | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/gef.py b/gef.py index bb6054ace..97b6d02c2 100644 --- a/gef.py +++ b/gef.py @@ -83,8 +83,8 @@ from io import StringIO, TextIOWrapper from types import ModuleType from typing import (Any, ByteString, Callable, Generator, Iterable, - Iterator, NoReturn, Optional, Sequence, Set, Tuple, Type, TypeVar, - Union, cast) + Iterator, NoReturn, Optional, Sequence, Type, TypeVar, + cast) from urllib.request import urlopen @@ -146,7 +146,7 @@ def update_gef(argv: list[str]) -> int: __registered_commands__ : set[Type["GenericCommand"]] = set() __registered_functions__ : set[Type["GenericFunction"]] = set() -__registered_architectures__ : dict[Union["Elf.Abi", str], Type["Architecture"]] = {} +__registered_architectures__ : dict["Elf.Abi" | str, Type["Architecture"]] = {} __registered_file_formats__ : set[ Type["FileFormat"] ] = set() GefMemoryMapProvider = Callable[[], Generator["Section", None, None]] @@ -446,8 +446,8 @@ def FakeExit(*args: Any, **kwargs: Any) -> NoReturn: sys.exit = FakeExit -def parse_arguments(required_arguments: dict[Union[str, tuple[str, str]], Any], - optional_arguments: dict[Union[str, tuple[str, str]], Any]) -> Callable: +def parse_arguments(required_arguments: dict[str | tuple[str, str], Any], + optional_arguments: dict[str | tuple[str, str], Any]) -> Callable: """Argument parsing decorator.""" def int_wrapper(x: str) -> int: return int(x, 0) @@ -799,7 +799,7 @@ class FileFormat: checksec: dict[str, bool] sections: list[FileFormatSection] - def __init__(self, path: Union[str, pathlib.Path]) -> None: + def __init__(self, path: str | pathlib.Path) -> None: raise NotImplementedError def __init_subclass__(cls: Type["FileFormat"], **kwargs): @@ -892,7 +892,7 @@ class OsAbi(enum.Enum): __checksec : dict[str, bool] - def __init__(self, path: Union[str, pathlib.Path]) -> None: + def __init__(self, path: str | pathlib.Path) -> None: """Instantiate an ELF object. A valid ELF must be provided, or an exception will be thrown.""" if isinstance(path, str): @@ -1314,7 +1314,7 @@ class heap_info_cls(ctypes.Structure): heap_info_cls._fields_ = fields return heap_info_cls - def __init__(self, addr: Union[str, int]) -> None: + def __init__(self, addr: str | int) -> None: self.__address : int = parse_address(f"&{addr}") if isinstance(addr, str) else addr self.reset() return @@ -2236,7 +2236,7 @@ def gef_disassemble(addr: int, nb_insn: int, nb_prev: int = 0) -> Generator[Inst yield insn -def gef_execute_external(command: Sequence[str], as_list: bool = False, **kwargs: Any) -> Union[str, list[str]]: +def gef_execute_external(command: Sequence[str], as_list: bool = False, **kwargs: Any) -> str | list[str]: """Execute an external command and return the result.""" res = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=kwargs.get("shell", False)) return [gef_pystring(_) for _ in res.splitlines()] if as_list else gef_pystring(res) @@ -2340,7 +2340,7 @@ def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]: class ArchitectureBase: """Class decorator for declaring an architecture to GEF.""" - aliases: Union[tuple[()], tuple[Union[str, Elf.Abi], ...]] = () + aliases: tuple[()] | tuple[str | Elf.Abi | ...] = () def __init_subclass__(cls: Type["ArchitectureBase"], **kwargs): global __registered_architectures__ @@ -2360,20 +2360,20 @@ class Architecture(ArchitectureBase): # Mandatory defined attributes by inheriting classes arch: str mode: str - all_registers: Union[tuple[()], tuple[str, ...]] + all_registers: tuple[()] | tuple[str, ...] nop_insn: bytes return_register: str flag_register: Optional[str] instruction_length: Optional[int] flags_table: dict[int, str] syscall_register: Optional[str] - syscall_instructions: Union[tuple[()], tuple[str, ...]] - function_parameters: Union[tuple[()], tuple[str, ...]] + syscall_instructions: tuple[()] | tuple[str, ...] + function_parameters: tuple[()] | tuple[str, ...] # Optionally defined attributes _ptrsize: Optional[int] = None _endianness: Optional[Endianness] = None - special_registers: Union[tuple[()], tuple[str, ...]] = () + special_registers: tuple[()] | tuple[str, ...] = () maps: Optional[GefMemoryMapProvider] = None def __init_subclass__(cls, **kwargs): @@ -2914,7 +2914,7 @@ def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: class X86(Architecture): - aliases: tuple[Union[str, Elf.Abi], ...] = ("X86", Elf.Abi.X86_32) + aliases: tuple[str | Elf.Abi, ...] = ("X86", Elf.Abi.X86_32) arch = "X86" mode = "32" @@ -3358,7 +3358,7 @@ def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: class MIPS(Architecture): - aliases: tuple[Union[str, Elf.Abi], ...] = ("MIPS", Elf.Abi.MIPS) + aliases: tuple[str | Elf.Abi, ...] = ("MIPS", Elf.Abi.MIPS) arch = "MIPS" mode = "MIPS32" @@ -4026,7 +4026,7 @@ def dereference(addr: int) -> Optional["gdb.Value"]: return None -def gef_convenience(value: Union[str, bytes]) -> str: +def gef_convenience(value: str | bytes) -> str: """Defines a new convenience value.""" global gef var_name = f"$_gef{gef.session.convenience_vars_index:d}" @@ -4048,7 +4048,7 @@ def parse_string_range(s: str) -> Iterator[int]: @lru_cache() -def is_syscall(instruction: Union[Instruction,int]) -> bool: +def is_syscall(instruction: Instruction | int) -> bool: """Checks whether an instruction or address points to a system call.""" if isinstance(instruction, int): instruction = gef_current_instruction(instruction) @@ -4214,7 +4214,7 @@ def __init__(self, set_func: Callable[[int], str], vbp_num: int, addr: int) -> N self.bp_addr = 0 # this address might be a symbol, just to know where to break if isinstance(addr, int): - self.addr: Union[int, str] = hex(addr) + self.addr: int | str = hex(addr) else: self.addr = addr return @@ -4664,7 +4664,7 @@ def register_priority_command(cls: Type["GenericCommand"]) -> Type["GenericComma ValidCommandType = TypeVar("ValidCommandType", bound="GenericCommand") ValidFunctionType = TypeVar("ValidFunctionType", bound="GenericFunction") -def register(cls: Union[Type["ValidCommandType"], Type["ValidFunctionType"]]) -> Union[Type["ValidCommandType"], Type["ValidFunctionType"]]: +def register(cls: Type["ValidCommandType"] | Type["ValidFunctionType"]) -> Type["ValidCommandType"] | Type["ValidFunctionType"]: global __registered_commands__, __registered_functions__ if issubclass(cls, GenericCommand): assert hasattr(cls, "_cmdline_") @@ -4690,7 +4690,7 @@ class GenericCommand(gdb.Command): _cmdline_: str _syntax_: str - _example_: Union[str, list[str]] = "" + _example_: str | list[str] = "" _aliases_: list[str] = [] def __init_subclass__(cls, **kwargs): @@ -4781,7 +4781,7 @@ def __contains__(self, name: str) -> bool: def add_setting(self, name: str, value: tuple[Any, type, str], description: str = "") -> None: return self.__setitem__(name, (value, description)) - def __setitem__(self, name: str, value: Union["GefSetting", tuple[Any, str]]) -> None: + def __setitem__(self, name: str, value: "GefSetting" | tuple[Any, str]) -> None: # make sure settings are always associated to the root command (which derives from GenericCommand) if "GenericCommand" not in [x.__name__ for x in self.__class__.__bases__]: return @@ -6283,6 +6283,7 @@ def do_invoke(self, _: list[str], **kwargs: Any) -> None: return # qemu-user support + qemu_binary = None if args.qemu_user: dbg("Setting up qemu-user session") try: From 741ce7cb846d3c8de74dd211f8cf699817d0fd08 Mon Sep 17 00:00:00 2001 From: hugsy Date: Fri, 1 Nov 2024 13:39:20 -0700 Subject: [PATCH 05/11] removed Tuple in tests --- tests/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/base.py b/tests/base.py index fc209be0c..868923d3a 100644 --- a/tests/base.py +++ b/tests/base.py @@ -5,7 +5,7 @@ import subprocess import tempfile import time -from typing import Tuple + import unittest import rpyc @@ -109,9 +109,9 @@ def tearDown(self) -> None: return super().tearDown() @property - def gdb_version(self) -> Tuple[int, int]: + def gdb_version(self) -> tuple[int, int]: res = re.search(r"(\d+)\D(\d+)", self._gdb.VERSION) assert res groups = [int(d) for d in res.groups()] assert len(groups) == 2 - return (groups[0], groups[1]) + return groups[0], groups[1] From f47f51e1c5126e03d7482e6bfdceeeac8e04e36f Mon Sep 17 00:00:00 2001 From: hugsy Date: Sat, 2 Nov 2024 12:39:13 -0700 Subject: [PATCH 06/11] fixed type typo --- gef.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/gef.py b/gef.py index 0dfcbc0ac..854076a15 100644 --- a/gef.py +++ b/gef.py @@ -146,8 +146,8 @@ def update_gef(argv: list[str]) -> int: __registered_commands__ : set[Type["GenericCommand"]] = set() __registered_functions__ : set[Type["GenericFunction"]] = set() -__registered_architectures__ : dict["Elf.Abi" | str, Type["Architecture"]] = {} -__registered_file_formats__ : set[ Type["FileFormat"] ] = set() +__registered_architectures__ : dict["Elf.Abi | str", Type["Architecture"]] = {} +__registered_file_formats__ : set[ Type["FileFormat"] ] = set() GefMemoryMapProvider = Callable[[], Generator["Section", None, None]] @@ -2337,7 +2337,7 @@ def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]: class ArchitectureBase: """Class decorator for declaring an architecture to GEF.""" - aliases: tuple[()] | tuple[str | Elf.Abi | ...] = () + aliases: tuple[str | Elf.Abi, ...] def __init_subclass__(cls: Type["ArchitectureBase"], **kwargs): global __registered_architectures__ @@ -8895,15 +8895,15 @@ class VMMapCommand(GenericCommand): @only_if_gdb_running @parse_arguments({"unknown_types": [""]}, {("--addr", "-a"): [""], ("--name", "-n"): [""]}) - def do_invoke(self, _: List[str], **kwargs: Any) -> None: + def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] vmmap = gef.memory.maps if not vmmap: err("No address mapping information found") return - addrs: Dict[str, int] = {x: parse_address(x) for x in args.addr} - names: List[str] = [x for x in args.name] + addrs: dict[str, int] = {x: parse_address(x) for x in args.addr} + names: list[str] = [x for x in args.name] for arg in args.unknown_types: if not arg: From d5d544db2fdc01fb38bd5fd4c07fd73afaebfd75 Mon Sep 17 00:00:00 2001 From: hugsy Date: Sat, 2 Nov 2024 13:28:54 -0700 Subject: [PATCH 07/11] `Optional` is optional --- gef.py | 319 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 154 insertions(+), 165 deletions(-) diff --git a/gef.py b/gef.py index 854076a15..af1151296 100644 --- a/gef.py +++ b/gef.py @@ -82,16 +82,15 @@ from functools import lru_cache from io import StringIO, TextIOWrapper from types import ModuleType -from typing import (Any, ByteString, Callable, Generator, Iterable, - Iterator, NoReturn, Optional, Sequence, Type, TypeVar, - cast) +from typing import (Any, ByteString, Callable, Generator, Iterable, Iterator, + NoReturn, Sequence, Type, TypeVar, cast) from urllib.request import urlopen GEF_DEFAULT_BRANCH = "main" GEF_EXTRAS_DEFAULT_BRANCH = "main" -def http_get(url: str) -> Optional[bytes]: +def http_get(url: str) -> bytes | None: """Basic HTTP wrapper for GET request. Return the body of the page if HTTP code is OK, otherwise return None.""" try: @@ -284,49 +283,49 @@ class ObsoleteException(Exception): pass class AlreadyRegisteredException(Exception): pass -def p8(x: int, s: bool = False, e: Optional["Endianness"] = None) -> bytes: +def p8(x: int, s: bool = False, e: "Endianness | None" = None) -> bytes: """Pack one byte respecting the current architecture endianness.""" endian = e or gef.arch.endianness return struct.pack(f"{endian}B", x) if not s else struct.pack(f"{endian:s}b", x) -def p16(x: int, s: bool = False, e: Optional["Endianness"] = None) -> bytes: +def p16(x: int, s: bool = False, e: "Endianness | None" = None) -> bytes: """Pack one word respecting the current architecture endianness.""" endian = e or gef.arch.endianness return struct.pack(f"{endian}H", x) if not s else struct.pack(f"{endian:s}h", x) -def p32(x: int, s: bool = False, e: Optional["Endianness"] = None) -> bytes: +def p32(x: int, s: bool = False, e: "Endianness | None" = None) -> bytes: """Pack one dword respecting the current architecture endianness.""" endian = e or gef.arch.endianness return struct.pack(f"{endian}I", x) if not s else struct.pack(f"{endian:s}i", x) -def p64(x: int, s: bool = False, e: Optional["Endianness"] = None) -> bytes: +def p64(x: int, s: bool = False, e: "Endianness | None" = None) -> bytes: """Pack one qword respecting the current architecture endianness.""" endian = e or gef.arch.endianness return struct.pack(f"{endian}Q", x) if not s else struct.pack(f"{endian:s}q", x) -def u8(x: bytes, s: bool = False, e: Optional["Endianness"] = None) -> int: +def u8(x: bytes, s: bool = False, e: "Endianness | None" = None) -> int: """Unpack one byte respecting the current architecture endianness.""" endian = e or gef.arch.endianness return struct.unpack(f"{endian}B", x)[0] if not s else struct.unpack(f"{endian:s}b", x)[0] -def u16(x: bytes, s: bool = False, e: Optional["Endianness"] = None) -> int: +def u16(x: bytes, s: bool = False, e: "Endianness | None" = None) -> int: """Unpack one word respecting the current architecture endianness.""" endian = e or gef.arch.endianness return struct.unpack(f"{endian}H", x)[0] if not s else struct.unpack(f"{endian:s}h", x)[0] -def u32(x: bytes, s: bool = False, e: Optional["Endianness"] = None) -> int: +def u32(x: bytes, s: bool = False, e: "Endianness | None" = None) -> int: """Unpack one dword respecting the current architecture endianness.""" endian = e or gef.arch.endianness return struct.unpack(f"{endian}I", x)[0] if not s else struct.unpack(f"{endian:s}i", x)[0] -def u64(x: bytes, s: bool = False, e: Optional["Endianness"] = None) -> int: +def u64(x: bytes, s: bool = False, e: "Endianness | None" = None) -> int: """Unpack one qword respecting the current architecture endianness.""" endian = e or gef.arch.endianness return struct.unpack(f"{endian}Q", x)[0] if not s else struct.unpack(f"{endian:s}q", x)[0] @@ -348,7 +347,7 @@ def is_alive() -> bool: return False -def calling_function() -> Optional[str]: +def calling_function() -> str | None: """Return the name of the calling function""" try: stack_info = traceback.extract_stack()[-3] @@ -452,7 +451,7 @@ def parse_arguments(required_arguments: dict[str | tuple[str, str], Any], def int_wrapper(x: str) -> int: return int(x, 0) - def decorator(f: Callable) -> Optional[Callable]: + def decorator(f: Callable) -> Callable | None: def wrapper(*args: Any, **kwargs: Any) -> Callable: parser = argparse.ArgumentParser(prog=args[0]._cmdline_, add_help=True) for argname in required_arguments: @@ -606,7 +605,7 @@ def is_in_stack_segment(self) -> bool: def is_in_heap_segment(self) -> bool: return hasattr(self.section, "path") and "[heap]" == self.section.path - def dereference(self) -> Optional[int]: + def dereference(self) -> int | None: addr = align_address(int(self.value)) derefed = dereference(addr) return None if derefed is None else int(derefed) @@ -693,7 +692,7 @@ def size(self) -> int: raise AttributeError return self.page_end - self.page_start - def _search_for_realpath_without_versions(self, path: pathlib.Path) -> Optional[str]: + def _search_for_realpath_without_versions(self, path: pathlib.Path) -> str | None: """Given a path, search for a file that exists without numeric suffixes.""" # Match the path string against a regex that will remove a suffix @@ -708,7 +707,7 @@ def _search_for_realpath_without_versions(self, path: pathlib.Path) -> Optional[ candidate = re.match(r"^(.*)\.(\d*)$", candidate) return None - def _search_for_realpath(self) -> Optional[str]: + def _search_for_realpath(self) -> str | None: """This function is a workaround for gdb bug #23764 path might be wrong for remote sessions, so try a simple search for files @@ -1199,7 +1198,7 @@ def _missing_(cls, _:int): sh_entsize: int name: str - def __init__(self, elf: Optional[Elf], off: int) -> None: + def __init__(self, elf: Elf | None, off: int) -> None: if elf is None: return elf.seek(off) @@ -1516,7 +1515,7 @@ def system_mem(self) -> int: def max_system_mem(self) -> int: return self.__arena.max_system_mem - def fastbin(self, i: int) -> Optional["GlibcFastChunk"]: + def fastbin(self, i: int) -> "GlibcFastChunk | None": """Return head chunk in fastbinsY[i].""" addr = int(self.fastbinsY[i]) if addr == 0: @@ -1537,7 +1536,7 @@ def bin_at(self, i) -> int: def is_main_arena(self) -> bool: return gef.heap.main_arena is not None and int(self) == int(gef.heap.main_arena) - def heap_addr(self, allow_unaligned: bool = False) -> Optional[int]: + def heap_addr(self, allow_unaligned: bool = False) -> int | None: if self.is_main_arena(): heap_section = gef.heap.base_address if not heap_section: @@ -1548,7 +1547,7 @@ def heap_addr(self, allow_unaligned: bool = False) -> Optional[int]: return _addr return gef.heap.malloc_align_address(_addr) - def get_heap_info_list(self) -> Optional[list[GlibcHeapInfo]]: + def get_heap_info_list(self) -> list[GlibcHeapInfo] | None: if self.is_main_arena(): return None heap_addr = self.get_heap_for_ptr(self.top) @@ -1827,7 +1826,7 @@ class GlibcTcacheChunk(GlibcFastChunk): def get_libc_version() -> tuple[int, ...]: return GefLibcManager.find_libc_version() -def titlify(text: str, color: Optional[str] = None, msg_color: Optional[str] = None) -> str: +def titlify(text: str, color: str | None = None, msg_color: str | None = None) -> str: """Print a centered title.""" _, cols = get_terminal_size() nb = (cols - len(text) - 2) // 2 @@ -2088,7 +2087,7 @@ def gef_makedirs(path: str, mode: int = 0o755) -> pathlib.Path: @lru_cache() -def gdb_lookup_symbol(sym: str) -> Optional[tuple[gdb.Symtab_and_line, ...]]: +def gdb_lookup_symbol(sym: str) -> tuple[gdb.Symtab_and_line, ...] | None: """Fetch the proper symbol or None if not defined.""" try: res = gdb.decode_line(sym)[1] # pylint: disable=E1136 @@ -2097,7 +2096,7 @@ def gdb_lookup_symbol(sym: str) -> Optional[tuple[gdb.Symtab_and_line, ...]]: return None @lru_cache(maxsize=512) -def gdb_get_location_from_symbol(address: int) -> Optional[tuple[str, int]]: +def gdb_get_location_from_symbol(address: int) -> tuple[str, int] | None: """Retrieve the location of the `address` argument from the symbol table. Return a tuple with the name and offset if found, None otherwise.""" # this is horrible, ugly hack and shitty perf... @@ -2148,7 +2147,7 @@ def gdb_disassemble(start_pc: int, **kwargs: int) -> Generator[Instruction, None yield Instruction(address, location, mnemo, operands, opcodes) -def gdb_get_nth_previous_instruction_address(addr: int, n: int) -> Optional[int]: +def gdb_get_nth_previous_instruction_address(addr: int, n: int) -> int | None: """Return the address (Integer) of the `n`-th instruction before `addr`.""" # fixed-length ABI if gef.arch.instruction_length: @@ -2288,7 +2287,7 @@ def get_arch() -> str: @deprecated("Use `gef.binary.entry_point` instead") -def get_entry_point() -> Optional[int]: +def get_entry_point() -> int | None: """Return the binary entry point.""" return gef.binary.entry_point if gef.binary else None @@ -2316,13 +2315,13 @@ def flags_to_human(reg_value: int, value_table: dict[int, str]) -> str: @lru_cache() -def get_section_base_address(name: str) -> Optional[int]: +def get_section_base_address(name: str) -> int | None: section = process_lookup_path(name) return section.page_start if section else None @lru_cache() -def get_zone_base_address(name: str) -> Optional[int]: +def get_zone_base_address(name: str) -> int | None: zone = file_lookup_name_path(name, get_filepath()) return zone.zone_start if zone else None @@ -2337,7 +2336,7 @@ def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]: class ArchitectureBase: """Class decorator for declaring an architecture to GEF.""" - aliases: tuple[str | Elf.Abi, ...] + aliases: tuple[str | Elf.Abi, ...] = () def __init_subclass__(cls: Type["ArchitectureBase"], **kwargs): global __registered_architectures__ @@ -2357,21 +2356,21 @@ class Architecture(ArchitectureBase): # Mandatory defined attributes by inheriting classes arch: str mode: str - all_registers: tuple[()] | tuple[str, ...] + all_registers: tuple[str, ...] nop_insn: bytes return_register: str - flag_register: Optional[str] - instruction_length: Optional[int] + flag_register: str | None + instruction_length: int | None flags_table: dict[int, str] - syscall_register: Optional[str] - syscall_instructions: tuple[()] | tuple[str, ...] - function_parameters: tuple[()] | tuple[str, ...] + syscall_register: str | None + syscall_instructions: tuple[str, ...] + function_parameters: tuple[str, ...] # Optionally defined attributes - _ptrsize: Optional[int] = None - _endianness: Optional[Endianness] = None + _ptrsize: int | None = None + _endianness: Endianness | None = None special_registers: tuple[()] | tuple[str, ...] = () - maps: Optional[GefMemoryMapProvider] = None + maps: GefMemoryMapProvider | None = None def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) @@ -2388,13 +2387,13 @@ def __repr__(self) -> str: return self.__str__() @staticmethod - def supports_gdb_arch(gdb_arch: str) -> Optional[bool]: + def supports_gdb_arch(gdb_arch: str) -> bool | None: """If implemented by a child `Architecture`, this function dictates if the current class supports the loaded ELF file (which can be accessed via `gef.binary`). This callback function will override any assumption made by GEF to determine the architecture.""" return None - def flag_register_to_human(self, val: Optional[int] = None) -> str: + def flag_register_to_human(self, val: int | None = None) -> str: raise NotImplementedError def is_call(self, insn: Instruction) -> bool: @@ -2409,7 +2408,7 @@ def is_conditional_branch(self, insn: Instruction) -> bool: def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: raise NotImplementedError - def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: + def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None: raise NotImplementedError def canary_address(self) -> int: @@ -2485,7 +2484,7 @@ def endianness(self) -> Endianness: raise OSError(f"No valid endianess found in '{output}'") return self._endianness - def get_ith_parameter(self, i: int, in_func: bool = True) -> tuple[str, Optional[int]]: + def get_ith_parameter(self, i: int, in_func: bool = True) -> tuple[str, int | None]: """Retrieves the correct parameter used for the current function call.""" reg = self.function_parameters[i] val = self.register(reg) @@ -2616,7 +2615,7 @@ def long_to_twos_complement(v: int) -> int: return taken, reason - def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: + def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None: ra = None if self.is_ret(insn): ra = gef.arch.register("$ra") @@ -2626,7 +2625,7 @@ def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: ra = to_unsigned_long(older.pc()) return ra - def flag_register_to_human(self, val: Optional[int] = None) -> str: + def flag_register_to_human(self, val: int | None = None) -> str: # RISC-V has no flags registers, return an empty string to # preserve the Architecture API return "" @@ -2660,7 +2659,7 @@ def is_thumb(self) -> bool: return is_alive() and (self.cpsr & (1 << 5) == 1) @property - def pc(self) -> Optional[int]: + def pc(self) -> int | None: pc = gef.arch.register("$pc") if self.is_thumb(): pc += 1 @@ -2677,7 +2676,7 @@ def mode(self) -> str: return "THUMB" if self.is_thumb() else "ARM" @property - def instruction_length(self) -> Optional[int]: + def instruction_length(self) -> int | None: # Thumb instructions have variable-length (2 or 4-byte) return None if self.is_thumb() else 4 @@ -2702,7 +2701,7 @@ def is_ret(self, insn: Instruction) -> bool: return insn.operands[0] == "pc" return False - def flag_register_to_human(self, val: Optional[int] = None) -> str: + def flag_register_to_human(self, val: int | None = None) -> str: # https://www.botskool.com/user-pages/tutorials/electronics/arm-7-tutorial-part-1 if val is None: reg = self.flag_register @@ -2746,7 +2745,7 @@ def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: elif mnemo.endswith("cc"): taken, reason = not val&(1< Optional[int]: + def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None: if not self.is_ret(insn): older = frame.older() if not older: @@ -2822,7 +2821,7 @@ def is_call(self, insn: Instruction) -> bool: call_mnemos = {"bl", "blr"} return mnemo in call_mnemos - def flag_register_to_human(self, val: Optional[int] = None) -> str: + def flag_register_to_human(self, val: int | None = None) -> str: # https://events.linuxfoundation.org/sites/events/files/slides/KoreaLinuxForum-2014.pdf reg = self.flag_register if not val: @@ -2942,7 +2941,7 @@ class X86(Architecture): _ptrsize = 4 _endianness = Endianness.LITTLE_ENDIAN - def flag_register_to_human(self, val: Optional[int] = None) -> str: + def flag_register_to_human(self, val: int | None = None) -> str: reg = self.flag_register if val is None: val = gef.arch.register(reg) @@ -3011,7 +3010,7 @@ def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: taken, reason = not val&(1< Optional[int]: + def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None: ra = None if self.is_ret(insn): ra = dereference(gef.arch.sp) @@ -3039,7 +3038,7 @@ def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: ] return "; ".join(insns) - def get_ith_parameter(self, i: int, in_func: bool = True) -> tuple[str, Optional[int]]: + def get_ith_parameter(self, i: int, in_func: bool = True) -> tuple[str, int | None]: if in_func: i += 1 # Account for RA being at the top of the stack sp = gef.arch.sp @@ -3128,7 +3127,7 @@ class PowerPC(Architecture): _ptrsize = 4 - def flag_register_to_human(self, val: Optional[int] = None) -> str: + def flag_register_to_human(self, val: int | None = None) -> str: # https://www.cebix.net/downloads/bebox/pem32b.pdf (% 2.1.3) if val is None: reg = self.flag_register @@ -3159,7 +3158,7 @@ def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: elif mnemo == "bgt": taken, reason = bool(val&(1< Optional[int]: + def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None: ra = None if self.is_ret(insn): ra = gef.arch.register("$lr") @@ -3232,7 +3231,7 @@ class SPARC(Architecture): syscall_register = "%g1" syscall_instructions = ("t 0x10",) - def flag_register_to_human(self, val: Optional[int] = None) -> str: + def flag_register_to_human(self, val: int | None = None) -> str: # https://www.gaisler.com/doc/sparcv8.pdf reg = self.flag_register if val is None: @@ -3278,7 +3277,7 @@ def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: elif mnemo == "bcc": taken, reason = val&(1< Optional[int]: + def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None: ra = None if self.is_ret(insn): ra = gef.arch.register("$o7") @@ -3376,7 +3375,7 @@ class MIPS(Architecture): syscall_register = "$v0" syscall_instructions = ("syscall",) - def flag_register_to_human(self, val: Optional[int] = None) -> str: + def flag_register_to_human(self, val: int | None = None) -> str: return Color.colorify("No flag register", "yellow underline") def is_call(self, insn: Instruction) -> bool: @@ -3412,7 +3411,7 @@ def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: taken, reason = gef.arch.register(ops[0]) <= 0, f"{ops[0]} <= 0" return taken, reason - def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> Optional[int]: + def get_ra(self, insn: Instruction, frame: "gdb.Frame") -> int | None: ra = None if self.is_ret(insn): ra = gef.arch.register("$ra") @@ -3446,7 +3445,7 @@ class MIPS64(MIPS): _ptrsize = 8 @staticmethod - def supports_gdb_arch(gdb_arch: str) -> Optional[bool]: + def supports_gdb_arch(gdb_arch: str) -> bool | None: if not gef.binary or not isinstance(gef.binary, Elf): return False return gdb_arch.startswith("mips") and gef.binary.e_class == Elf.Class.ELF_64_BITS @@ -3501,7 +3500,7 @@ def to_unsigned_long(v: gdb.Value) -> int: return int(v.cast(gdb.Value(mask).type)) & mask -def get_path_from_info_proc() -> Optional[str]: +def get_path_from_info_proc() -> str | None: for x in (gdb.execute("info proc", to_string=True) or "").splitlines(): if x.startswith("exe = "): return x.split(" = ")[1].replace("'", "") @@ -3537,7 +3536,7 @@ def is_qemu_system() -> bool: return "received: \"\"" in response -def get_filepath() -> Optional[str]: +def get_filepath() -> str | None: """Return the local absolute path of the file currently debugged.""" if gef.session.remote: return str(gef.session.remote.lfile.absolute()) @@ -3581,7 +3580,7 @@ def get_info_files() -> list[Zone]: return infos -def process_lookup_address(address: int) -> Optional[Section]: +def process_lookup_address(address: int) -> Section | None: """Look up for an address in memory. Return an Address object if found, None otherwise.""" if not is_alive(): @@ -3600,7 +3599,7 @@ def process_lookup_address(address: int) -> Optional[Section]: @lru_cache() -def process_lookup_path(name: str, perm: Permission = Permission.ALL) -> Optional[Section]: +def process_lookup_path(name: str, perm: Permission = Permission.ALL) -> Section | None: """Look up for a path in the process memory mapping. Return a Section object if found, None otherwise.""" if not is_alive(): @@ -3632,7 +3631,7 @@ def process_lookup_path(name: str, perm: Permission = Permission.ALL) -> Optiona @lru_cache() -def file_lookup_name_path(name: str, path: str) -> Optional[Zone]: +def file_lookup_name_path(name: str, path: str) -> Zone | None: """Look up a file by name and path. Return a Zone object if found, None otherwise.""" for xfile in get_info_files(): @@ -3642,7 +3641,7 @@ def file_lookup_name_path(name: str, path: str) -> Optional[Zone]: @lru_cache() -def file_lookup_address(address: int) -> Optional[Zone]: +def file_lookup_address(address: int) -> Zone | None: """Look up for a file by its address. Return a Zone object if found, None otherwise.""" for info in get_info_files(): @@ -3688,7 +3687,7 @@ def hook_stop_handler(_: "gdb.StopEvent") -> None: return -def new_objfile_handler(evt: Optional["gdb.NewObjFileEvent"]) -> None: +def new_objfile_handler(evt: "gdb.NewObjFileEvent | None") -> None: """GDB event handler for new object file cases.""" reset_all_caches() progspace = gdb.current_progspace() @@ -3828,7 +3827,7 @@ def is_arch(arch: Elf.Abi) -> bool: return arch in gef.arch.aliases -def reset_architecture(arch: Optional[str] = None) -> None: +def reset_architecture(arch: str | None = None) -> None: """Sets the current architecture. If an architecture is explicitly specified by parameter, try to use that one. If this fails, an `OSError` exception will occur. @@ -3869,7 +3868,7 @@ def reset_architecture(arch: Optional[str] = None) -> None: @lru_cache() -def cached_lookup_type(_type: str) -> Optional[gdb.Type]: +def cached_lookup_type(_type: str) -> gdb.Type | None: try: return gdb.lookup_type(_type).strip_typedefs() except RuntimeError: @@ -3993,7 +3992,7 @@ def generate_cyclic_pattern(length: int, cycle: int = 4) -> bytearray: return bytearray(itertools.islice(de_bruijn(charset, cycle), length)) -def safe_parse_and_eval(value: str) -> Optional["gdb.Value"]: +def safe_parse_and_eval(value: str) -> "gdb.Value | None": """GEF wrapper for gdb.parse_and_eval(): this function returns None instead of raising gdb.error if the eval failed.""" try: @@ -4004,7 +4003,7 @@ def safe_parse_and_eval(value: str) -> Optional["gdb.Value"]: @lru_cache() -def dereference(addr: int) -> Optional["gdb.Value"]: +def dereference(addr: int) -> "gdb.Value | None": """GEF wrapper for gdb dereference function.""" try: ulong_t = cached_lookup_type(use_stdtype()) or \ @@ -4086,7 +4085,7 @@ def gef_getpagesize() -> int: @deprecated("Use `gef.session.canary`") -def gef_read_canary() -> Optional[tuple[int, int]]: +def gef_read_canary() -> tuple[int, int] | None: return gef.session.canary @@ -4102,12 +4101,12 @@ def get_filename() -> str: @deprecated("Use `gef.heap.main_arena`") -def get_glibc_arena() -> Optional[GlibcArena]: +def get_glibc_arena() -> GlibcArena | None: return gef.heap.main_arena @deprecated("Use `gef.arch.register(regname)`") -def get_register(regname) -> Optional[int]: +def get_register(regname) -> int | None: return gef.arch.register(regname) @@ -4117,7 +4116,7 @@ def get_process_maps() -> list[Section]: @deprecated("Use `reset_architecture`") -def set_arch(arch: Optional[str] = None, _: Optional[str] = None) -> None: +def set_arch(arch: str | None = None, _: str | None = None) -> None: return reset_architecture(arch) # @@ -4282,7 +4281,7 @@ def stop(self) -> bool: class StubBreakpoint(gdb.Breakpoint): """Create a breakpoint to permanently disable a call (fork/alarm/signal/etc.).""" - def __init__(self, func: str, retval: Optional[int]) -> None: + def __init__(self, func: str, retval: int | None) -> None: super().__init__(func, gdb.BP_BREAKPOINT, internal=False) self.func = func self.retval = retval @@ -4612,7 +4611,7 @@ def __init__(self, loc: str) -> None: # Context Panes # -def register_external_context_pane(pane_name: str, display_pane_function: Callable[[], None], pane_title_function: Callable[[], Optional[str]], condition : Optional[Callable[[], bool]] = None) -> None: +def register_external_context_pane(pane_name: str, display_pane_function: Callable[[], None], pane_title_function: Callable[[], str | None], condition : Callable[[], bool] | None = None) -> None: """ Registering function for new GEF Context View. pane_name: a string that has no spaces (used in settings) @@ -4633,7 +4632,7 @@ def pane_title(): gef.gdb.add_context_pane(pane_name, display_pane_function, pane_title_function, condition) return -def register_external_context_layout_mapping(current_pane_name: str, display_pane_function: Callable[[], None], pane_title_function: Callable[[], Optional[str]], condition : Optional[Callable[[], bool]] = None) -> None: +def register_external_context_layout_mapping(current_pane_name: str, display_pane_function: Callable[[], None], pane_title_function: Callable[[], str | None], condition : Callable[[], bool] | None = None) -> None: gef.gdb.add_context_layout_mapping(current_pane_name, display_pane_function, pane_title_function, condition) return @@ -4778,7 +4777,7 @@ def __contains__(self, name: str) -> bool: def add_setting(self, name: str, value: tuple[Any, type, str], description: str = "") -> None: return self.__setitem__(name, (value, description)) - def __setitem__(self, name: str, value: "GefSetting" | tuple[Any, str]) -> None: + def __setitem__(self, name: str, value: "GefSetting | tuple[Any, str]") -> None: # make sure settings are always associated to the root command (which derives from GenericCommand) if "GenericCommand" not in [x.__name__ for x in self.__class__.__bases__]: return @@ -5713,7 +5712,7 @@ def structures(self) -> Generator[tuple["ExternalStructureManager.Module", "Exte return @lru_cache() - def find(self, structure_name: str) -> Optional[tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"]]: + def find(self, structure_name: str) -> tuple["ExternalStructureManager.Module", "ExternalStructureManager.Structure"] | None: """Return the module and structure for the given structure name; `None` if the structure name was not found.""" for module in self.modules.values(): if structure_name in module: @@ -6937,7 +6936,7 @@ def check_thread_ids(self, tids: list[int]) -> list[int]: existing_tids = set(t.num for t in gdb.selected_inferior().threads()) return list(set(tids) & existing_tids) - def tcachebin(self, tcache_base: int, i: int) -> tuple[Optional[GlibcTcacheChunk], int]: + def tcachebin(self, tcache_base: int, i: int) -> tuple[GlibcTcacheChunk | None, int]: """Return the head chunk in tcache[i] and the number of chunks in the bin.""" if i >= self.TCACHE_MAX_BINS: err("Incorrect index value, index value must be between 0 and " @@ -7594,7 +7593,7 @@ class ContextCommand(GenericCommand): _syntax_ = f"{_cmdline_} [legend|regs|stack|code|args|memory|source|trace|threads|extra]" _aliases_ = ["ctx",] - old_registers: dict[str, Optional[int]] = {} + old_registers: dict[str, int | None] = {} def __init__(self) -> None: super().__init__() @@ -7622,7 +7621,7 @@ def __init__(self) -> None: self["libc_args"] = (False, "[DEPRECATED - Unused] Show libc function call args description") self["libc_args_path"] = ("", "[DEPRECATED - Unused] Path to libc function call args json files, provided via gef-extras") - self.layout_mapping: dict[str, tuple[Callable, Optional[Callable], Optional[Callable]]] = { + self.layout_mapping: dict[str, tuple[Callable, Callable | None, Callable | None]] = { "legend": (self.show_legend, None, None), "regs": (self.context_regs, None, None), "stack": (self.context_stack, None, None), @@ -7705,7 +7704,7 @@ def do_invoke(self, argv: list[str]) -> None: disable_redirect_output() return - def context_title(self, m: Optional[str]) -> None: + def context_title(self, m: str | None) -> None: # allow for not displaying a title line if m is None: return @@ -7959,7 +7958,7 @@ def print_arguments_from_symbol(self, function_name: str, symbol: "gdb.Symbol") def print_guessed_arguments(self, function_name: str) -> None: """When no symbol, read the current basic block and look for "interesting" instructions.""" - def __get_current_block_start_address() -> Optional[int]: + def __get_current_block_start_address() -> int | None: pc = gef.arch.pc try: block = gdb.block_for_pc(pc) @@ -8411,7 +8410,7 @@ class HexdumpCommand(GenericCommand): def __init__(self) -> None: super().__init__(complete=gdb.COMPLETE_LOCATION, prefix=True) self["always_show_ascii"] = (False, "If true, hexdump will always display the ASCII dump") - self.format: Optional[str] = None + self.format: str | None = None self.__last_target = "$sp" return @@ -8550,7 +8549,7 @@ class PatchCommand(GenericCommand): def __init__(self) -> None: super().__init__(prefix=True, complete=gdb.COMPLETE_LOCATION) - self.format: Optional[str] = None + self.format: str | None = None return @only_if_gdb_running @@ -9945,8 +9944,8 @@ def __init__(self) -> None: gef.config["gef.propagate_debug_exception"] = GefSetting(False, bool, "If true, when debug mode is enabled, Python exceptions will be propagated all the way.") gef.config["gef.extra_python_package_paths"] = GefSetting("", str, "Semi-colon separate list of extra paths to include for python packages") - self.commands : dict[str, GenericCommand] = collections.OrderedDict() - self.functions : dict[str, GenericFunction] = collections.OrderedDict() + self.commands : dict[str, GenericCommand] = {} + self.functions : dict[str, GenericFunction] = {} self.missing: dict[str, Exception] = {} return @@ -9981,7 +9980,7 @@ def setup(self) -> None: GefRestoreCommand() return - def load_extra_plugins(self, extra_plugins_dir: Optional[pathlib.Path] = None) -> int: + def load_extra_plugins(self, extra_plugins_dir: pathlib.Path | None = None) -> int: """Load the plugins from the gef-extras setting. Returns the number of new plugins added.""" def load_plugin(fpath: pathlib.Path) -> bool: try: @@ -10035,7 +10034,7 @@ def invoke(self, args: Any, from_tty: bool) -> None: gdb.execute("gef help") return - def add_context_layout_mapping(self, current_pane_name: str, display_pane_function: Callable, pane_title_function: Callable, condition: Optional[Callable]) -> None: + def add_context_layout_mapping(self, current_pane_name: str, display_pane_function: Callable, pane_title_function: Callable, condition: Callable | None) -> None: """Add a new context layout mapping.""" context = self.commands["context"] assert isinstance(context, ContextCommand) @@ -10043,7 +10042,7 @@ def add_context_layout_mapping(self, current_pane_name: str, display_pane_functi # overload the printing of pane title context.layout_mapping[current_pane_name] = (display_pane_function, pane_title_function, condition) - def add_context_pane(self, pane_name: str, display_pane_function: Callable, pane_title_function: Callable, condition: Optional[Callable]) -> None: + def add_context_pane(self, pane_name: str, display_pane_function: Callable, pane_title_function: Callable, condition: Callable | None) -> None: """Add a new context pane to ContextCommand.""" context = self.commands["context"] assert isinstance(context, ContextCommand) @@ -10489,7 +10488,7 @@ def invoke(self, args: Any, from_tty: bool) -> None: gdb.execute(f"{self.command} {args}", from_tty=from_tty) return - def lookup_command(self, cmd: str) -> Optional[tuple[str, GenericCommand]]: + def lookup_command(self, cmd: str) -> tuple[str, GenericCommand] | None: global gef for _name, _instance in gef.gdb.commands.items(): if cmd == _name: @@ -10740,10 +10739,10 @@ def __init__(self) -> None: def reset_caches(self) -> None: super().reset_caches() - self.__maps: Optional[list[Section]] = None + self.__maps: list[Section] | None = None return - def write(self, address: int, buffer: ByteString, length: Optional[int] = None) -> None: + def write(self, address: int, buffer: ByteString, length: int | None = None) -> None: """Write `buffer` at address `address`.""" length = length or len(buffer) gdb.selected_inferior().write_memory(address, buffer, length) @@ -10762,7 +10761,7 @@ def read_integer(self, addr: int) -> int: def read_cstring(self, address: int, max_length: int = GEF_MAX_STRING_LENGTH, - encoding: Optional[str] = None) -> str: + encoding: str | None = None) -> str: """Return a C-string read from memory.""" encoding = encoding or "unicode-escape" length = min(address | (DEFAULT_PAGE_SIZE-1), max_length+1) @@ -10803,7 +10802,7 @@ def read_cstring(self, return f"{ustr[:max_length]}[...]" return ustr - def read_ascii_string(self, address: int) -> Optional[str]: + def read_ascii_string(self, address: int) -> str | None: """Read an ASCII string from memory""" cstr = self.read_cstring(address) if isinstance(cstr, str) and cstr and all(x in string.printable for x in cstr): @@ -10819,7 +10818,7 @@ def maps(self) -> list[Section]: self.__maps = maps return self.__maps - def __parse_maps(self) -> Optional[list[Section]]: + def __parse_maps(self) -> list[Section] | None: """Return the mapped memory sections. If the current arch has its maps method defined, then defer to that to generated maps, otherwise, try to figure it out from procfs, then info sections, then monitor info @@ -11002,13 +11001,13 @@ def __init__(self) -> None: return def reset_caches(self) -> None: - self.__libc_main_arena: Optional[GlibcArena] = None - self.__libc_selected_arena: Optional[GlibcArena] = None + self.__libc_main_arena: GlibcArena | None = None + self.__libc_selected_arena: GlibcArena | None = None self.__heap_base = None return @property - def main_arena(self) -> Optional[GlibcArena]: + def main_arena(self) -> GlibcArena | None: if not self.__libc_main_arena: try: __main_arena_addr = GefHeapManager.find_main_arena_addr() @@ -11104,7 +11103,7 @@ def search_filter(zone: Zone) -> bool: raise OSError(err_msg) @property - def selected_arena(self) -> Optional[GlibcArena]: + def selected_arena(self) -> GlibcArena | None: if not self.__libc_selected_arena: # `selected_arena` must default to `main_arena` self.__libc_selected_arena = self.main_arena @@ -11122,7 +11121,7 @@ def arenas(self) -> list | Iterator[GlibcArena]: return iter(self.main_arena) @property - def base_address(self) -> Optional[int]: + def base_address(self) -> int | None: if not self.__heap_base: base = 0 try: @@ -11175,7 +11174,7 @@ def ceil(n: float) -> int: class GefSetting: """Basic class for storing gef settings as objects""" - def __init__(self, value: Any, cls: Optional[type] = None, description: Optional[str] = None, hooks: Optional[dict[str, list[Callable]]] = None) -> None: + def __init__(self, value: Any, cls: type | None = None, description: str | None = None, hooks: dict[str, list[Callable]] | None = None) -> None: self.value = value self.type = cls or type(value) self.description = description or "" @@ -11276,7 +11275,7 @@ class GefSessionManager(GefManager): """Class managing the runtime properties of GEF. """ def __init__(self) -> None: self.reset_caches() - self.remote: Optional["GefRemoteSessionManager"] = None + self.remote: "GefRemoteSessionManager | None" = None self.remote_initializing: bool = False self.qemu_mode: bool = False self.convenience_vars_index: int = 0 @@ -11299,8 +11298,8 @@ def reset_caches(self) -> None: self._os = None self._pid = None self._file = None - self._maps: Optional[pathlib.Path] = None - self._root: Optional[pathlib.Path] = None + self._maps: pathlib.Path | None = None + self._root: pathlib.Path | None = None return def __str__(self) -> str: @@ -11311,7 +11310,7 @@ def __repr__(self) -> str: return str(self) @property - def auxiliary_vector(self) -> Optional[dict[str, int]]: + def auxiliary_vector(self) -> dict[str, int] | None: if not is_alive(): return None if is_qemu_system(): @@ -11351,7 +11350,7 @@ def pid(self) -> int: return self._pid @property - def file(self) -> Optional[pathlib.Path]: + def file(self) -> pathlib.Path | None: """Return a Path object of the target process.""" if self.remote is not None: return self.remote.file @@ -11363,7 +11362,7 @@ def file(self) -> Optional[pathlib.Path]: return self._file @property - def cwd(self) -> Optional[pathlib.Path]: + def cwd(self) -> pathlib.Path | None: if self.remote is not None: return self.remote.root return self.file.parent if self.file else None @@ -11378,7 +11377,7 @@ def pagesize(self) -> int: return self._pagesize @property - def canary(self) -> Optional[tuple[int, int]]: + def canary(self) -> tuple[int, int] | None: """Return a tuple of the canary address and value, read from the canonical location if supported by the architecture. Otherwise, read from the auxiliary vector.""" @@ -11392,7 +11391,7 @@ def canary(self) -> Optional[tuple[int, int]]: return canary, canary_location @property - def original_canary(self) -> Optional[tuple[int, int]]: + def original_canary(self) -> tuple[int, int] | None: """Return a tuple of the initial canary address and value, read from the auxiliary vector.""" auxval = self.auxiliary_vector @@ -11404,7 +11403,7 @@ def original_canary(self) -> Optional[tuple[int, int]]: return canary, canary_location @property - def maps(self) -> Optional[pathlib.Path]: + def maps(self) -> pathlib.Path | None: """Returns the Path to the procfs entry for the memory mapping.""" if not is_alive(): return None @@ -11416,7 +11415,7 @@ def maps(self) -> Optional[pathlib.Path]: return self._maps @property - def root(self) -> Optional[pathlib.Path]: + def root(self) -> pathlib.Path | None: """Returns the path to the process's root directory.""" if not is_alive(): return None @@ -11450,7 +11449,7 @@ def prompt_string(self) -> str: return Color.boldify("(remote) ") raise AttributeError("Unknown value") - def __init__(self, host: str, port: int, pid: int =-1, qemu: Optional[pathlib.Path] = None) -> None: + def __init__(self, host: str, port: int, pid: int =-1, qemu: pathlib.Path | None = None) -> None: super().__init__() self.__host = host self.__port = port @@ -11519,7 +11518,7 @@ def maps(self) -> pathlib.Path: def mode(self) -> RemoteMode: return self._mode - def sync(self, src: str, dst: Optional[str] = None) -> bool: + def sync(self, src: str, dst: str | None = None) -> bool: """Copy the `src` into the temporary chroot. If `dst` is provided, that path will be used instead of `src`.""" if not dst: @@ -11657,9 +11656,9 @@ def remote_objfile_event_handler(self, evt: "gdb.NewObjFileEvent") -> None: class GefUiManager(GefManager): """Class managing UI settings.""" def __init__(self) -> None: - self.redirect_fd : Optional[TextIOWrapper] = None + self.redirect_fd : TextIOWrapper | None = None self.context_hidden = False - self.stream_buffer : Optional[StringIO] = None + self.stream_buffer : StringIO | None = None self.highlight_table: dict[str, str] = {} self.watches: dict[int, tuple[int, str]] = {} self.context_messages: list[tuple[str, str]] = [] @@ -11672,16 +11671,16 @@ class GefLibcManager(GefManager): PATTERN_LIBC_VERSION_FILENAME = re.compile(r"libc6?[-_](\d+)\.(\d+)\.so") def __init__(self) -> None: - self._version : Optional[tuple[int, int]] = None - self._patch: Optional[int] = None - self._release: Optional[str] = None + self._version : tuple[int, int] | None = None + self._patch: int | None = None + self._release: str | None = None return def __str__(self) -> str: return f"Libc(version='{self.version}')" @property - def version(self) -> Optional[tuple[int, int]]: + def version(self) -> tuple[int, int] | None: if not is_alive(): return None @@ -11723,7 +11722,7 @@ def find_libc_version() -> tuple[int, int]: class Gef: """The GEF root class, which serves as a entrypoint for all the debugging session attributes (architecture, memory, settings, etc.).""" - binary: Optional[FileFormat] + binary: FileFormat | None arch: Architecture config : GefSettingsManager ui: GefUiManager @@ -11734,7 +11733,7 @@ class Gef: gdb: GefCommand def __init__(self) -> None: - self.binary: Optional[FileFormat] = None + self.binary: FileFormat | None = None self.arch: Architecture = GenericArchitecture() # see PR #516, will be reset by `new_objfile_handler` self.arch_reason: str = "This is the default architecture" self.config = GefSettingsManager() @@ -11769,32 +11768,14 @@ def reset_caches(self) -> None: def target_remote_posthook(): - conn = gdb.selected_inferior().connection - # if here, the connection should be established - if not isinstance(conn, gdb.RemoteTargetConnection): - raise EnvironmentError("Failed to create a remote session") - - # if gef.session.remote_initializing: - # dbg(f"remote session already initializing: {gef.session}") - # return - - print(f"{conn.description=}") - print(f"{conn.details=}") - print(f"{conn.type=}") - - assert conn.details - host, port = conn.details.split(":") - pid = ( - gdb.selected_inferior().pid - if False - else gdb.selected_thread().ptid[1] - ) - print(host, port, pid) - gef.session.remote = GefRemoteSessionManager(host, int(port), pid) - gef.session.remote._mode = GefRemoteSessionManager.RemoteMode.GDBSERVER + 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}") + raise EnvironmentError( + f"Failed to create a proper environment for {gef.session.remote}" + ) if __name__ == "__main__": if sys.version_info[0] == 2: @@ -11839,14 +11820,6 @@ def target_remote_posthook(): # reload settings gdb.execute("gef restore") - # extend `sys.path` for venv compat - if gef.config["gef.extra_python_package_paths"]: - for path in map(pathlib.Path, gef.config["gef.extra_python_package_paths"].split(";")): - if not path.exists(): - warn(f"Skipping invalid directory {path}") - continue - sys.path.extend(str(path.absolute())) - # setup gdb prompt gdb.prompt_hook = __gef_prompt__ @@ -11865,16 +11838,32 @@ def target_remote_posthook(): GefTmuxSetup() - # Register a post-hook for `target remote` that initialize the remote session - hook = """ - define target hookpost-{} - pi target_remote_posthook() - context - pi res = calling_function(); if res != "connect": err("Failed to initialize remote session: " + str(res)) - end - """ - gdb.execute(hook.format("remote")) - gdb.execute(hook.format("extended-remote")) + if GDB_VERSION > (9, 0): + 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() From 60be6a15cbcf9a4aa083d7251193c5ba30955d62 Mon Sep 17 00:00:00 2001 From: hugsy Date: Sat, 2 Nov 2024 13:31:04 -0700 Subject: [PATCH 08/11] fixed ruff toml --- ruff.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruff.toml b/ruff.toml index 6a3fc3001..11ea9dbb8 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,2 +1,2 @@ [lint.per-file-ignores] -"gef.py" = ["E701"] \ No newline at end of file +"gef.py" = ["E701"] From dc289385b9776540a6f7c09473852ca90ab87394 Mon Sep 17 00:00:00 2001 From: hugsy Date: Sat, 2 Nov 2024 13:41:09 -0700 Subject: [PATCH 09/11] revert changes, only focus on py3.10 improvements --- gef.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/gef.py b/gef.py index af1151296..47506d3cd 100644 --- a/gef.py +++ b/gef.py @@ -6279,16 +6279,15 @@ def do_invoke(self, _: list[str], **kwargs: Any) -> None: return # qemu-user support - qemu_binary = None + qemu_binary: pathlib.Path | None = None if args.qemu_user: - dbg("Setting up qemu-user session") try: qemu_binary = pathlib.Path(args.qemu_user).expanduser().absolute() if args.qemu_user else gef.session.file if not qemu_binary or not qemu_binary.exists(): raise FileNotFoundError(f"qemu-user session was specified, but binary '{qemu_binary}' does not exist") except Exception as e: err(f"Failed to initialize qemu-user mode, reason: {str(e)}") - raise e + return # Try to establish the remote session, throw on error # set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which @@ -11773,9 +11772,7 @@ 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}" - ) + raise EnvironmentError(f"Failed to create a proper environment for {gef.session.remote}") if __name__ == "__main__": if sys.version_info[0] == 2: From 417ac632afbbeadcce54e157ad4c4de8526ec852 Mon Sep 17 00:00:00 2001 From: hugsy Date: Sat, 2 Nov 2024 14:11:23 -0700 Subject: [PATCH 10/11] final fixes --- gef.py | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/gef.py b/gef.py index 47506d3cd..08bed01fd 100644 --- a/gef.py +++ b/gef.py @@ -114,8 +114,8 @@ def update_gef(argv: list[str]) -> int: sys.exit(1) -GDB_MIN_VERSION: tuple[int, int] = (8, 0) -PYTHON_MIN_VERSION: tuple[int, int] = (3, 6) +GDB_MIN_VERSION: tuple[int, int] = (10, 0) +PYTHON_MIN_VERSION: tuple[int, int] = (3, 10) PYTHON_VERSION: tuple[int, int] = sys.version_info[0:2] GDB_VERSION: tuple[int, int] = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups())) # type:ignore @@ -143,10 +143,10 @@ def update_gef(argv: list[str]) -> int: GEF_PROMPT_ON = f"\001\033[1;32m\002{GEF_PROMPT}\001\033[0m\002" GEF_PROMPT_OFF = f"\001\033[1;31m\002{GEF_PROMPT}\001\033[0m\002" -__registered_commands__ : set[Type["GenericCommand"]] = set() -__registered_functions__ : set[Type["GenericFunction"]] = set() -__registered_architectures__ : dict["Elf.Abi | str", Type["Architecture"]] = {} -__registered_file_formats__ : set[ Type["FileFormat"] ] = set() +__registered_commands__ : set[Type["GenericCommand"]] = set() +__registered_functions__ : set[Type["GenericFunction"]] = set() +__registered_architectures__ : dict["Elf.Abi | str", Type["Architecture"]] = {} +__registered_file_formats__ : set[ Type["FileFormat"] ] = set() GefMemoryMapProvider = Callable[[], Generator["Section", None, None]] @@ -2910,7 +2910,7 @@ def is_branch_taken(self, insn: Instruction) -> tuple[bool, str]: class X86(Architecture): - aliases: tuple[str | Elf.Abi, ...] = ("X86", Elf.Abi.X86_32) + aliases = ("X86", Elf.Abi.X86_32) arch = "X86" mode = "32" @@ -3354,7 +3354,7 @@ def mprotect_asm(cls, addr: int, size: int, perm: Permission) -> str: class MIPS(Architecture): - aliases: tuple[str | Elf.Abi, ...] = ("MIPS", Elf.Abi.MIPS) + aliases = ("MIPS", Elf.Abi.MIPS) arch = "MIPS" mode = "MIPS32" @@ -3517,7 +3517,7 @@ def is_qemu() -> bool: if not is_remote_debug(): return False response = gdb.execute("maintenance packet Qqemu.sstepbits", to_string=True, from_tty=False) or "" - return "ENABLE=1" in response + return "ENABLE=" in response @lru_cache() @@ -4557,7 +4557,7 @@ def stop(self) -> bool: return False # software watchpoints stop after the next statement (see - # https://sourceware.org/gdb/onlinedocs/gdb/set-Watchpoints.html) + # https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html) pc = gdb_get_nth_previous_instruction_address(gef.arch.pc, 2) assert pc insn = gef_current_instruction(pc) @@ -4686,7 +4686,7 @@ class GenericCommand(gdb.Command): _cmdline_: str _syntax_: str - _example_: str | list[str] = "" + _example_: str | list[str] = "" _aliases_: list[str] = [] def __init_subclass__(cls, **kwargs): @@ -4851,7 +4851,7 @@ def do_invoke(self, args: list[str]) -> None: @register class ArchSetCommand(GenericCommand): - """set the current loaded architecture.""" + """Set the current loaded architecture.""" _cmdline_ = "arch set" _syntax_ = f"{_cmdline_} " @@ -5026,7 +5026,7 @@ def do_invoke(self, argv: list[str]) -> None: @register class PieBreakpointCommand(GenericCommand): - """set a PIE breakpoint at an offset from the target binaries base address.""" + """Set a PIE breakpoint at an offset from the target binaries base address.""" _cmdline_ = "pie breakpoint" _syntax_ = f"{_cmdline_} OFFSET" @@ -5789,7 +5789,7 @@ def __init__(self) -> None: super().__init__() return - def do_invoke(self, _: list) -> None: + def do_invoke(self, _: list[str]) -> None: """Dump the list of all the structures and their respective.""" manager = ExternalStructureManager() info(f"Listing custom structures from '{manager.path}'") @@ -6266,7 +6266,7 @@ def __init__(self) -> None: super().__init__(prefix=False) return - @parse_arguments({"host": "", "port": 0}, {"--pid": 0, "--qemu-user": ""}) + @parse_arguments({"host": "", "port": 0}, {"--pid": -1, "--qemu-user": False, "--qemu-binary": ""}) def do_invoke(self, _: list[str], **kwargs: Any) -> None: if gef.session.remote is not None: err("You already are in remote session. Close it first before opening a new one...") @@ -6282,15 +6282,15 @@ def do_invoke(self, _: list[str], **kwargs: Any) -> None: qemu_binary: pathlib.Path | None = None if args.qemu_user: try: - qemu_binary = pathlib.Path(args.qemu_user).expanduser().absolute() if args.qemu_user else gef.session.file + qemu_binary = pathlib.Path(args.qemu_binary).expanduser().absolute() if args.qemu_binary else gef.session.file if not qemu_binary or not qemu_binary.exists(): - raise FileNotFoundError(f"qemu-user session was specified, but binary '{qemu_binary}' does not exist") + raise FileNotFoundError(f"{qemu_binary} does not exist") except Exception as e: err(f"Failed to initialize qemu-user mode, reason: {str(e)}") return # Try to establish the remote session, throw on error - # set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which + # Set `.remote_initializing` to True here - `GefRemoteSessionManager` invokes code which # calls `is_remote_debug` which checks if `remote_initializing` is True or `.remote` is None # This prevents some spurious errors being thrown during startup gef.session.remote_initializing = True @@ -6500,7 +6500,7 @@ def do_invoke(self, _: list[str]) -> None: @register class GlibcHeapSetArenaCommand(GenericCommand): - """set the address of the main_arena or the currently selected arena.""" + """Set the address of the main_arena or the currently selected arena.""" _cmdline_ = "heap set-arena" _syntax_ = f"{_cmdline_} [address|&symbol]" @@ -8572,9 +8572,9 @@ def do_invoke(self, _: list[str], **kwargs: Any) -> None: var_name = values[0] try: values = str(gdb.parse_and_eval(var_name)).lstrip("{").rstrip("}").replace(",","").split(" ") - except Exception as e: + except Exception: gef_print(f"Bad variable specified, check value with command: p {var_name}") - raise e + return d = str(gef.arch.endianness) for value in values: @@ -9939,9 +9939,8 @@ def __init__(self) -> None: gef.config["gef.buffer"] = GefSetting(True, bool, "Internally buffer command output until completion") gef.config["gef.bruteforce_main_arena"] = GefSetting(False, bool, "Allow bruteforcing main_arena symbol if everything else fails") gef.config["gef.libc_version"] = GefSetting("", str, "Specify libc version when auto-detection fails") - gef.config["gef.main_arena_offset"] = GefSetting("", str, "Offset from libc base address to main_arena symbol (int or hex). set to empty string to disable.") + gef.config["gef.main_arena_offset"] = GefSetting("", str, "Offset from libc base address to main_arena symbol (int or hex). Set to empty string to disable.") gef.config["gef.propagate_debug_exception"] = GefSetting(False, bool, "If true, when debug mode is enabled, Python exceptions will be propagated all the way.") - gef.config["gef.extra_python_package_paths"] = GefSetting("", str, "Semi-colon separate list of extra paths to include for python packages") self.commands : dict[str, GenericCommand] = {} self.functions : dict[str, GenericFunction] = {} @@ -10725,7 +10724,8 @@ def reset_caches(self) -> None: if not hasattr(obj, "cache_clear"): continue obj.cache_clear() - except Exception: # we're reseting the cache here, we don't care if (or which) exception triggers + except Exception: + # we're reseting the cache here, we don't care if (or which) exception triggers continue return @@ -11472,7 +11472,7 @@ def close(self) -> None: gef_on_new_hook(new_objfile_handler) except Exception as e: warn(f"Exception while restoring local context: {str(e)}") - raise e + raise def __str__(self) -> str: return f"RemoteSession(target='{self.target}', local='{self.root}', pid={self.pid}, mode={self.mode})" From 9fb1d716540736b939e22872ebfc8d66863b675e Mon Sep 17 00:00:00 2001 From: hugsy Date: Sun, 3 Nov 2024 09:00:01 -0800 Subject: [PATCH 11/11] added `untracked` dir to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6ec4d8589..09f604883 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ debug.log htmlcov .benchmarks site/ +untracked/