From 8877f101f3a6d795c800522e0484a4fd1a78c1d3 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Fri, 31 May 2024 22:52:34 +0200 Subject: [PATCH 01/26] [cmd] Add a `arch` command for managing `gef.arch` Available subcommands: - get -> prints the current arch and the reason - set -> manually set the current arch (or auto-detect if no arg is provided) - list -> list available architectures --- gef.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/gef.py b/gef.py index b8d310c33..0ea1b30e0 100644 --- a/gef.py +++ b/gef.py @@ -2277,6 +2277,13 @@ def get_zone_base_address(name: str) -> Optional[int]: # # Architecture classes # +class ArchitectureReason(enum.StrEnum): + DEFAULT = "This default architecture" + MANUAL = "The architecture has been set manually" + GDB = "The architecture has been detected by GDB" + ELF = "The architecture has been detected via the ELF headers" + + @deprecated("Using the decorator `register_architecture` is unecessary") def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]: return cls @@ -3785,6 +3792,7 @@ def reset_architecture(arch: Optional[str] = None) -> None: if arch: try: gef.arch = arches[arch]() + gef.arch_reason = ArchitectureReason.MANUAL except KeyError: raise OSError(f"Specified arch {arch.upper()} is not supported") return @@ -3795,12 +3803,14 @@ def reset_architecture(arch: Optional[str] = None) -> None: preciser_arch = next((a for a in arches.values() if a.supports_gdb_arch(gdb_arch)), None) if preciser_arch: gef.arch = preciser_arch() + gef.arch_reason = ArchitectureReason.GDB return # last resort, use the info from elf header to find it from the known architectures if gef.binary and isinstance(gef.binary, Elf): try: gef.arch = arches[gef.binary.e_machine]() + gef.arch_reason = ArchitectureReason.ELF except KeyError: raise OSError(f"CPU type is currently not supported: {gef.binary.e_machine}") return @@ -4758,6 +4768,36 @@ def __set_repeat_count(self, argv: List[str], from_tty: bool) -> None: return +@register +class ArchCommand(GenericCommand): + """Manage the current loaded architecture""" + + _cmdline_ = "arch" + _syntax_ = f"{_cmdline_} (list|get|set) ..." + _example_ = f"{_cmdline_}" + + def do_invoke(self, args: List[str]) -> None: + if len(args) == 0 or args[0] == 'get': + self._get_cmd() + elif args[0] == 'set': + self._set_cmd(*args[1:]) + elif args[0] == 'list': + self._list_cmd() + + def _get_cmd(self) -> None: + gef_print(f"{Color.greenify("Arch")}: {gef.arch}") + gef_print(f"{Color.greenify("Reason")}: {gef.arch_reason}") + + def _set_cmd(self, arch = None, *args) -> None: + reset_architecture(arch) + + def _list_cmd(self) -> None: + gef_print(Color.greenify("Available architectures:")) + for arch in __registered_architectures__.keys(): + if type(arch) == str: + gef_print(f"- {arch}") + + @register class VersionCommand(GenericCommand): """Display GEF version info.""" @@ -11554,6 +11594,7 @@ class Gef: def __init__(self) -> None: self.binary: Optional[FileFormat] = None self.arch: Architecture = GenericArchitecture() # see PR #516, will be reset by `new_objfile_handler` + self.arch_reason = ArchitectureReason.DEFAULT self.config = GefSettingsManager() self.ui = GefUiManager() self.libc = GefLibcManager() From d12dbe04e3dc685e756c29738ef4a9f6e5b13b94 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Fri, 31 May 2024 23:05:49 +0200 Subject: [PATCH 02/26] [doc] Add documentation for the `arch` command --- docs/commands/arch.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/commands/arch.md diff --git a/docs/commands/arch.md b/docs/commands/arch.md new file mode 100644 index 000000000..fb6ff7212 --- /dev/null +++ b/docs/commands/arch.md @@ -0,0 +1,12 @@ +## Command `arch` + +`arch` manages the loaded architecture. + +There are 3 available sub-commands: +- `list`: List the installed architectures. +- `get`: This prints the currently loaded architecture, and why it is selected. +- `set`: You can manually set the loaded architecture by providing its name as an argument, or let + gef do magic to detect the architecture by not providing arguments. + +![arch](https://imgur.com/a/6JUoOmS) + From 95b0343df10a753aad3b36a1d11e52bc9ebaed03 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sat, 1 Jun 2024 00:46:06 +0200 Subject: [PATCH 03/26] [cmd] Split the command on multiple subcommands for autocompletion --- docs/commands/arch.md | 1 - gef.py | 52 ++++++++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/docs/commands/arch.md b/docs/commands/arch.md index fb6ff7212..9dd638e12 100644 --- a/docs/commands/arch.md +++ b/docs/commands/arch.md @@ -9,4 +9,3 @@ There are 3 available sub-commands: gef do magic to detect the architecture by not providing arguments. ![arch](https://imgur.com/a/6JUoOmS) - diff --git a/gef.py b/gef.py index 0ea1b30e0..2458d5714 100644 --- a/gef.py +++ b/gef.py @@ -4769,29 +4769,55 @@ def __set_repeat_count(self, argv: List[str], from_tty: bool) -> None: @register -class ArchCommand(GenericCommand): +class PieCommand(GenericCommand): """Manage the current loaded architecture""" _cmdline_ = "arch" _syntax_ = f"{_cmdline_} (list|get|set) ..." _example_ = f"{_cmdline_}" + def __init__(self) -> None: + super().__init__(prefix=True) + return + + def do_invoke(self, argv: List[str]) -> None: + if not argv: + self.usage() + return + +@register +class ArchGetCommand(GenericCommand): + """Get the current loaded architecture""" + + _cmdline_ = "arch get" + _syntax_ = f"{_cmdline_}" + _example_ = f"{_cmdline_}" + def do_invoke(self, args: List[str]) -> None: - if len(args) == 0 or args[0] == 'get': - self._get_cmd() - elif args[0] == 'set': - self._set_cmd(*args[1:]) - elif args[0] == 'list': - self._list_cmd() + gef_print(f"{Color.greenify('Arch')}: {gef.arch}") + gef_print(f"{Color.greenify('Reason')}: {gef.arch_reason}") - def _get_cmd(self) -> None: - gef_print(f"{Color.greenify("Arch")}: {gef.arch}") - gef_print(f"{Color.greenify("Reason")}: {gef.arch_reason}") - def _set_cmd(self, arch = None, *args) -> None: - reset_architecture(arch) +@register +class ArchSetCommand(GenericCommand): + """Set the current loaded architecture""" + + _cmdline_ = "arch set" + _syntax_ = f"{_cmdline_} " + _example_ = f"{_cmdline_}" + + def do_invoke(self, args: List[str]) -> None: + reset_architecture(args[0] if args else None) - def _list_cmd(self) -> None: +@register +class ArchListCommand(GenericCommand): + """List the available architectures""" + + _cmdline_ = "arch list" + _syntax_ = f"{_cmdline_}" + _example_ = f"{_cmdline_}" + + def do_invoke(self, args: List[str]) -> None: gef_print(Color.greenify("Available architectures:")) for arch in __registered_architectures__.keys(): if type(arch) == str: From 5a134dc69e2a2230f82a0f804d1a52baa335ad13 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sat, 1 Jun 2024 00:49:39 +0200 Subject: [PATCH 04/26] Fix docs syntax --- docs/commands/arch.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/commands/arch.md b/docs/commands/arch.md index 9dd638e12..4740d5437 100644 --- a/docs/commands/arch.md +++ b/docs/commands/arch.md @@ -3,9 +3,11 @@ `arch` manages the loaded architecture. There are 3 available sub-commands: -- `list`: List the installed architectures. -- `get`: This prints the currently loaded architecture, and why it is selected. -- `set`: You can manually set the loaded architecture by providing its name as an argument, or let + +- `list`: List the installed architectures. +- `get`: This prints the currently loaded architecture, and why it is selected. +- `set`: You can manually set the loaded architecture by providing its name as an argument, or let gef do magic to detect the architecture by not providing arguments. + ![arch](https://imgur.com/a/6JUoOmS) From fa743e9d8b58e9a12a579f61350a08578ee7ead6 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sat, 1 Jun 2024 01:04:31 +0200 Subject: [PATCH 05/26] StrEnum doesn't seem to exists in all python versions --- gef.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/gef.py b/gef.py index 2458d5714..7d0e2680c 100644 --- a/gef.py +++ b/gef.py @@ -2277,12 +2277,6 @@ def get_zone_base_address(name: str) -> Optional[int]: # # Architecture classes # -class ArchitectureReason(enum.StrEnum): - DEFAULT = "This default architecture" - MANUAL = "The architecture has been set manually" - GDB = "The architecture has been detected by GDB" - ELF = "The architecture has been detected via the ELF headers" - @deprecated("Using the decorator `register_architecture` is unecessary") def register_architecture(cls: Type["Architecture"]) -> Type["Architecture"]: @@ -3792,7 +3786,7 @@ def reset_architecture(arch: Optional[str] = None) -> None: if arch: try: gef.arch = arches[arch]() - gef.arch_reason = ArchitectureReason.MANUAL + gef.arch_reason = "The architecture has been set manually" except KeyError: raise OSError(f"Specified arch {arch.upper()} is not supported") return @@ -3803,14 +3797,14 @@ def reset_architecture(arch: Optional[str] = None) -> None: preciser_arch = next((a for a in arches.values() if a.supports_gdb_arch(gdb_arch)), None) if preciser_arch: gef.arch = preciser_arch() - gef.arch_reason = ArchitectureReason.GDB + gef.arch_reason = "The architecture has been detected by GDB" return # last resort, use the info from elf header to find it from the known architectures if gef.binary and isinstance(gef.binary, Elf): try: gef.arch = arches[gef.binary.e_machine]() - gef.arch_reason = ArchitectureReason.ELF + gef.arch_reason = "The architecture has been detected via the ELF headers" except KeyError: raise OSError(f"CPU type is currently not supported: {gef.binary.e_machine}") return @@ -11620,7 +11614,7 @@ class Gef: def __init__(self) -> None: self.binary: Optional[FileFormat] = None self.arch: Architecture = GenericArchitecture() # see PR #516, will be reset by `new_objfile_handler` - self.arch_reason = ArchitectureReason.DEFAULT + self.arch_reason = "This default architecture" self.config = GefSettingsManager() self.ui = GefUiManager() self.libc = GefLibcManager() From b6194e749db046992cbabe5d24a0cc16feb23a75 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sat, 1 Jun 2024 03:46:07 +0200 Subject: [PATCH 06/26] Fix nits --- gef.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/gef.py b/gef.py index 7d0e2680c..dd6b75a28 100644 --- a/gef.py +++ b/gef.py @@ -4763,12 +4763,12 @@ def __set_repeat_count(self, argv: List[str], from_tty: bool) -> None: @register -class PieCommand(GenericCommand): - """Manage the current loaded architecture""" +class ArchCommand(GenericCommand): + """Manage the current loaded architecture.""" _cmdline_ = "arch" _syntax_ = f"{_cmdline_} (list|get|set) ..." - _example_ = f"{_cmdline_}" + _example_ = f"{_cmdline_} set X86" def __init__(self) -> None: super().__init__(prefix=True) @@ -4781,7 +4781,7 @@ def do_invoke(self, argv: List[str]) -> None: @register class ArchGetCommand(GenericCommand): - """Get the current loaded architecture""" + """Get the current loaded architecture.""" _cmdline_ = "arch get" _syntax_ = f"{_cmdline_}" @@ -4794,18 +4794,18 @@ 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_} " - _example_ = f"{_cmdline_}" + _example_ = f"{_cmdline_} X86" def do_invoke(self, args: List[str]) -> None: reset_architecture(args[0] if args else None) @register class ArchListCommand(GenericCommand): - """List the available architectures""" + """List the available architectures.""" _cmdline_ = "arch list" _syntax_ = f"{_cmdline_}" From 932f064d215f245a597722122983ebfef927fb97 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 18:05:31 +0200 Subject: [PATCH 07/26] Add tests --- tests/commands/arch.py | 48 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/commands/arch.py diff --git a/tests/commands/arch.py b/tests/commands/arch.py new file mode 100644 index 000000000..4b4322893 --- /dev/null +++ b/tests/commands/arch.py @@ -0,0 +1,48 @@ +""" +Arch commands test module +""" + +from tests.base import RemoteGefUnitTestGeneric +from tests.utils import ( + ARCH, + ERROR_INACTIVE_SESSION_MESSAGE, + debug_target, + findlines, + is_32b, + is_64b, +) + + +class ArchCommand(RemoteGefUnitTestGeneric): + """Generic class for command testing, that defines all helpers""" + + def setUp(self) -> None: + return super().setUp() + + def test_cmd_arch_get(self): + gdb = self._gdb + + res = gdb.execute("arch get", to_string=True) + self.assertIn(" Architecture(Generic, None, LITTLE_ENDIAN)", res) + self.assertIn(" This default architecture", res) + + def test_cmd_arch_set(self): + gdb = self._gdb + + res = gdb.execute("arch get", to_string=True) + self.assertIn(" Architecture(Generic, None, LITTLE_ENDIAN)", res) + self.assertIn(" This default architecture", res) + + gdb.execute("arch set X86") + + res = gdb.execute("arch get", to_string=True) + self.assertIn(" Architecture(Generic, None, LITTLE_ENDIAN)", res) + self.assertIn(" The architecture has been set manually", res) + + def test_cmd_arch_list(self): + gdb = self._gdb + + res = gdb.execute("arch list", to_string=True) + self.assertIn("- GenericArchitecture", res) + self.assertIn("- X86", res) + self.assertIn("- X86_64", res) From 4ae1b6b944976179061ff506154460d32d60010b Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 19:01:28 +0200 Subject: [PATCH 08/26] Fix tests --- tests/commands/arch.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/commands/arch.py b/tests/commands/arch.py index 4b4322893..65efc02f8 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -23,15 +23,15 @@ def test_cmd_arch_get(self): gdb = self._gdb res = gdb.execute("arch get", to_string=True) - self.assertIn(" Architecture(Generic, None, LITTLE_ENDIAN)", res) - self.assertIn(" This default architecture", res) + self.assertIn(" Architecture(X86, 64, LITTLE_ENDIAN)", res) + self.assertIn(" The architecture has been detected via the ELF headers", res) def test_cmd_arch_set(self): gdb = self._gdb res = gdb.execute("arch get", to_string=True) - self.assertIn(" Architecture(Generic, None, LITTLE_ENDIAN)", res) - self.assertIn(" This default architecture", res) + self.assertIn(" Architecture(X86, 64, LITTLE_ENDIAN)", res) + self.assertIn(" The architecture has been detected via the ELF headers", res) gdb.execute("arch set X86") From bc7e51833844f8ba117ab76d44015857211ca156 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 19:49:54 +0200 Subject: [PATCH 09/26] Fix tests bis :) --- tests/commands/arch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/arch.py b/tests/commands/arch.py index 65efc02f8..660d7fb1c 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -36,7 +36,7 @@ def test_cmd_arch_set(self): gdb.execute("arch set X86") res = gdb.execute("arch get", to_string=True) - self.assertIn(" Architecture(Generic, None, LITTLE_ENDIAN)", res) + self.assertIn(" Architecture(X86, 32, LITTLE_ENDIAN)", res) self.assertIn(" The architecture has been set manually", res) def test_cmd_arch_list(self): From 92276b3696e65a9101f0e8f0072a36dc39e0d20b Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 21:31:27 +0200 Subject: [PATCH 10/26] Add completion --- gef.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gef.py b/gef.py index dd6b75a28..427c3be27 100644 --- a/gef.py +++ b/gef.py @@ -4800,9 +4800,17 @@ class ArchSetCommand(GenericCommand): _syntax_ = f"{_cmdline_} " _example_ = f"{_cmdline_} X86" + def __init__(self): + super().__init__(complete=-1) # -1 is the default for gdb but not for gef. + # It means we use the complete method for autocompletion. + def do_invoke(self, args: List[str]) -> None: reset_architecture(args[0] if args else None) + def complete(self, text: str, word: str) -> List[str]: + return sorted(x for x in __registered_architectures__.keys() if + isinstance(x, str) and x.startswith(text.strip())) + @register class ArchListCommand(GenericCommand): """List the available architectures.""" From 43f38e764eb8eb3b16b3eec97e08b10d5258e901 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 21:50:59 +0200 Subject: [PATCH 11/26] Remove unnecessary __init__ --- gef.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gef.py b/gef.py index 427c3be27..bc3ce51b3 100644 --- a/gef.py +++ b/gef.py @@ -4800,10 +4800,6 @@ class ArchSetCommand(GenericCommand): _syntax_ = f"{_cmdline_} " _example_ = f"{_cmdline_} X86" - def __init__(self): - super().__init__(complete=-1) # -1 is the default for gdb but not for gef. - # It means we use the complete method for autocompletion. - def do_invoke(self, args: List[str]) -> None: reset_architecture(args[0] if args else None) From fc077ff1dabc98c9c3a71dd8d036026242bd6782 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:02:46 +0200 Subject: [PATCH 12/26] Update docs/commands/arch.md Co-authored-by: crazy hugsy --- docs/commands/arch.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/commands/arch.md b/docs/commands/arch.md index 4740d5437..5773c24d2 100644 --- a/docs/commands/arch.md +++ b/docs/commands/arch.md @@ -9,5 +9,9 @@ There are 3 available sub-commands: - `set`: You can manually set the loaded architecture by providing its name as an argument, or let gef do magic to detect the architecture by not providing arguments. +> [!WARNING] +> Setting manually should be done as a last resort as GEF expects to find the architecture +> automatically. Force-setting the architecture can lead to unexpected behavior if not done correctly. + ![arch](https://imgur.com/a/6JUoOmS) From a0c081bdbd6d2ce9bd5a461a214235c7dcb4bed6 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:02:55 +0200 Subject: [PATCH 13/26] Update docs/commands/arch.md Co-authored-by: crazy hugsy --- docs/commands/arch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands/arch.md b/docs/commands/arch.md index 5773c24d2..32cf62cad 100644 --- a/docs/commands/arch.md +++ b/docs/commands/arch.md @@ -14,4 +14,4 @@ There are 3 available sub-commands: > automatically. Force-setting the architecture can lead to unexpected behavior if not done correctly. -![arch](https://imgur.com/a/6JUoOmS) +![arch](https://github.com/hugsy/gef/assets/590234/c4481a78-9311-43ba-929f-2817c5c9290e) From ae82b39d39d88dc38d5ceac7e78d973202a3a2b3 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:14:46 +0200 Subject: [PATCH 14/26] Fixes for hugsy :D --- gef.py | 6 +++--- tests/commands/arch.py | 15 ++------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/gef.py b/gef.py index bc3ce51b3..d04f93626 100644 --- a/gef.py +++ b/gef.py @@ -4805,7 +4805,7 @@ def do_invoke(self, args: List[str]) -> None: def complete(self, text: str, word: str) -> List[str]: return sorted(x for x in __registered_architectures__.keys() if - isinstance(x, str) and x.startswith(text.strip())) + isinstance(x, str) and x.lower().startswith(text.lower().strip())) @register class ArchListCommand(GenericCommand): @@ -4818,7 +4818,7 @@ class ArchListCommand(GenericCommand): def do_invoke(self, args: List[str]) -> None: gef_print(Color.greenify("Available architectures:")) for arch in __registered_architectures__.keys(): - if type(arch) == str: + if type(arch) == str and arch != "GenericArchitecture": gef_print(f"- {arch}") @@ -11618,7 +11618,7 @@ class Gef: def __init__(self) -> None: self.binary: Optional[FileFormat] = None self.arch: Architecture = GenericArchitecture() # see PR #516, will be reset by `new_objfile_handler` - self.arch_reason = "This default architecture" + self.arch_reason: str = "This default architecture" self.config = GefSettingsManager() self.ui = GefUiManager() self.libc = GefLibcManager() diff --git a/tests/commands/arch.py b/tests/commands/arch.py index 660d7fb1c..76c116825 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -3,14 +3,6 @@ """ from tests.base import RemoteGefUnitTestGeneric -from tests.utils import ( - ARCH, - ERROR_INACTIVE_SESSION_MESSAGE, - debug_target, - findlines, - is_32b, - is_64b, -) class ArchCommand(RemoteGefUnitTestGeneric): @@ -19,6 +11,7 @@ class ArchCommand(RemoteGefUnitTestGeneric): def setUp(self) -> None: return super().setUp() + @pytest.mark.skipif(ARCH != "x86_64", reason=f"Skipped for {ARCH}") def test_cmd_arch_get(self): gdb = self._gdb @@ -29,10 +22,6 @@ def test_cmd_arch_get(self): def test_cmd_arch_set(self): gdb = self._gdb - res = gdb.execute("arch get", to_string=True) - self.assertIn(" Architecture(X86, 64, LITTLE_ENDIAN)", res) - self.assertIn(" The architecture has been detected via the ELF headers", res) - gdb.execute("arch set X86") res = gdb.execute("arch get", to_string=True) @@ -43,6 +32,6 @@ def test_cmd_arch_list(self): gdb = self._gdb res = gdb.execute("arch list", to_string=True) - self.assertIn("- GenericArchitecture", res) + self.assertNotIn("- GenericArchitecture", res) self.assertIn("- X86", res) self.assertIn("- X86_64", res) From 928f51a4114498aad5c0b232aa489a7e481b848d Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:17:06 +0200 Subject: [PATCH 15/26] Fix style --- docs/commands/arch.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands/arch.md b/docs/commands/arch.md index 32cf62cad..8a175be9c 100644 --- a/docs/commands/arch.md +++ b/docs/commands/arch.md @@ -10,7 +10,7 @@ There are 3 available sub-commands: gef do magic to detect the architecture by not providing arguments. > [!WARNING] -> Setting manually should be done as a last resort as GEF expects to find the architecture +> Setting manually should be done as a last resort as GEF expects to find the architecture > automatically. Force-setting the architecture can lead to unexpected behavior if not done correctly. From 08578f8129d8be0bb66833eb26d43e6dc7dfc8d1 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:19:52 +0200 Subject: [PATCH 16/26] Fix missing pytest import --- tests/commands/arch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/commands/arch.py b/tests/commands/arch.py index 76c116825..bc3f202ef 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -3,6 +3,7 @@ """ from tests.base import RemoteGefUnitTestGeneric +import pytest class ArchCommand(RemoteGefUnitTestGeneric): From 32ef59f480c27710dca9e366a8f76f918749ab60 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:25:59 +0200 Subject: [PATCH 17/26] Fix missing ARCH import --- tests/commands/arch.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/commands/arch.py b/tests/commands/arch.py index bc3f202ef..2d2c9d0e6 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -3,6 +3,7 @@ """ from tests.base import RemoteGefUnitTestGeneric +from tests.utils import ARCH import pytest From 0d0b57a9383c760468e48b268e66fc6ad476a6f1 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:41:01 +0200 Subject: [PATCH 18/26] Improve arch list and arch set --- gef.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/gef.py b/gef.py index d04f93626..f7ff5b1cd 100644 --- a/gef.py +++ b/gef.py @@ -2291,7 +2291,10 @@ def __init_subclass__(cls: Type["ArchitectureBase"], **kwargs): super().__init_subclass__(**kwargs) for key in getattr(cls, "aliases"): if issubclass(cls, Architecture): - __registered_architectures__[key] = cls + if isinstance(key, str): + __registered_architectures__[key.lower()] = cls + else: + __registered_architectures__[key] = cls return @@ -4801,7 +4804,7 @@ class ArchSetCommand(GenericCommand): _example_ = f"{_cmdline_} X86" def do_invoke(self, args: List[str]) -> None: - reset_architecture(args[0] if args else None) + reset_architecture(args[0].lower() if args else None) def complete(self, text: str, word: str) -> List[str]: return sorted(x for x in __registered_architectures__.keys() if @@ -4817,9 +4820,11 @@ class ArchListCommand(GenericCommand): def do_invoke(self, args: List[str]) -> None: gef_print(Color.greenify("Available architectures:")) - for arch in __registered_architectures__.keys(): - if type(arch) == str and arch != "GenericArchitecture": - gef_print(f"- {arch}") + for arch in set(__registered_architectures__.values()): + if arch != GenericArchitecture: + gef_print(' ' + Color.yellowify(arch())) + for alias in filter(lambda x: isinstance(x, str), arch.aliases): + gef_print(f" {alias}") @register From 797da2480a03e2d17f069c2142cdc008bada3395 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:43:28 +0200 Subject: [PATCH 19/26] Add warn in reset_architecture if no arch found with file or debug session --- gef.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gef.py b/gef.py index f7ff5b1cd..184cd1d58 100644 --- a/gef.py +++ b/gef.py @@ -3812,6 +3812,8 @@ def reset_architecture(arch: Optional[str] = None) -> None: raise OSError(f"CPU type is currently not supported: {gef.binary.e_machine}") return + warn("Did not find any way to guess the correct architecture :(") + @lru_cache() def cached_lookup_type(_type: str) -> Optional[gdb.Type]: From 46f44f475d11d37c216bf5c9d82319b952cab9bb Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:48:25 +0200 Subject: [PATCH 20/26] Make order of arch list output persistant --- gef.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 184cd1d58..7a0072d55 100644 --- a/gef.py +++ b/gef.py @@ -4822,7 +4822,7 @@ class ArchListCommand(GenericCommand): def do_invoke(self, args: List[str]) -> None: gef_print(Color.greenify("Available architectures:")) - for arch in set(__registered_architectures__.values()): + for arch in sorted(set(__registered_architectures__.values()), key=lambda x: x.arch): if arch != GenericArchitecture: gef_print(' ' + Color.yellowify(arch())) for alias in filter(lambda x: isinstance(x, str), arch.aliases): From b16c8fd5dde9cbfce1b04516d8fb38a26ee64504 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:53:52 +0200 Subject: [PATCH 21/26] Add a test --- gef.py | 2 +- tests/commands/arch.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/gef.py b/gef.py index 7a0072d55..d686f184e 100644 --- a/gef.py +++ b/gef.py @@ -4824,7 +4824,7 @@ 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 != GenericArchitecture: - gef_print(' ' + Color.yellowify(arch())) + gef_print(' ' + Color.yellowify(str(arch()))) for alias in filter(lambda x: isinstance(x, str), arch.aliases): gef_print(f" {alias}") diff --git a/tests/commands/arch.py b/tests/commands/arch.py index 2d2c9d0e6..8ec2dc40d 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -30,6 +30,13 @@ def test_cmd_arch_set(self): self.assertIn(" Architecture(X86, 32, LITTLE_ENDIAN)", res) self.assertIn(" The architecture has been set manually", res) + + gdb.execute("arch set ppc") + + res = gdb.execute("arch get", to_string=True) + self.assertIn(" Architecture(PPC, PPC32, LITTLE_ENDIAN)", res) + self.assertIn(" The architecture has been set manually", res) + def test_cmd_arch_list(self): gdb = self._gdb From cca59653dfea2bda4fb70fb0cd1f753140be169b Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:57:33 +0200 Subject: [PATCH 22/26] Fix list tests --- tests/commands/arch.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/commands/arch.py b/tests/commands/arch.py index 8ec2dc40d..51bb0db01 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -42,5 +42,6 @@ def test_cmd_arch_list(self): res = gdb.execute("arch list", to_string=True) self.assertNotIn("- GenericArchitecture", res) - self.assertIn("- X86", res) - self.assertIn("- X86_64", res) + self.assertIn(" Architecture(X86, 64, LITTLE_ENDIAN)", res) + self.assertIn(" x86", res) + self.assertIn(" x86_64", res) From f7269e000a8682f996f60a3fbe6846e038fcc128 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 22:59:55 +0200 Subject: [PATCH 23/26] Fix list tests --- tests/commands/arch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/commands/arch.py b/tests/commands/arch.py index 51bb0db01..2b727dd83 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -43,5 +43,5 @@ def test_cmd_arch_list(self): res = gdb.execute("arch list", to_string=True) self.assertNotIn("- GenericArchitecture", res) self.assertIn(" Architecture(X86, 64, LITTLE_ENDIAN)", res) - self.assertIn(" x86", res) - self.assertIn(" x86_64", res) + self.assertIn(" X86", res) + self.assertIn(" X86_64", res) From 2696943a2127ff92d59f20f6875ccc329cf632b5 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 23:07:35 +0200 Subject: [PATCH 24/26] Imperative in docs --- docs/commands/arch.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands/arch.md b/docs/commands/arch.md index 8a175be9c..df7b3ce8d 100644 --- a/docs/commands/arch.md +++ b/docs/commands/arch.md @@ -5,8 +5,8 @@ There are 3 available sub-commands: - `list`: List the installed architectures. -- `get`: This prints the currently loaded architecture, and why it is selected. -- `set`: You can manually set the loaded architecture by providing its name as an argument, or let +- `get`: Print the currently loaded architecture, and why it is selected. +- `set`: Manually set the loaded architecture by providing its name as an argument, or let gef do magic to detect the architecture by not providing arguments. > [!WARNING] From bef498b5092f089db7852b4b091c655e52698784 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 23:10:02 +0200 Subject: [PATCH 25/26] Clean code --- gef.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/gef.py b/gef.py index d686f184e..64e6845b6 100644 --- a/gef.py +++ b/gef.py @@ -4823,9 +4823,12 @@ class ArchListCommand(GenericCommand): 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 != GenericArchitecture: - gef_print(' ' + Color.yellowify(str(arch()))) - for alias in filter(lambda x: isinstance(x, str), arch.aliases): + if arch is GenericArchitecture: + continue + + gef_print(' ' + Color.yellowify(str(arch()))) + for alias in arch.aliases: + if isinstance(alias, str): gef_print(f" {alias}") @@ -11625,7 +11628,7 @@ class Gef: def __init__(self) -> None: self.binary: Optional[FileFormat] = None self.arch: Architecture = GenericArchitecture() # see PR #516, will be reset by `new_objfile_handler` - self.arch_reason: str = "This default architecture" + self.arch_reason: str = "This is the default architecture" self.config = GefSettingsManager() self.ui = GefUiManager() self.libc = GefLibcManager() From 54a5a3262f7ab3bf1f1ecba3cf95fcaa504f3243 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Sun, 2 Jun 2024 23:25:21 +0200 Subject: [PATCH 26/26] Clean tests --- tests/commands/arch.py | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/tests/commands/arch.py b/tests/commands/arch.py index 2b727dd83..9fb31acc8 100644 --- a/tests/commands/arch.py +++ b/tests/commands/arch.py @@ -2,24 +2,22 @@ Arch commands test module """ +import pytest + from tests.base import RemoteGefUnitTestGeneric from tests.utils import ARCH -import pytest class ArchCommand(RemoteGefUnitTestGeneric): - """Generic class for command testing, that defines all helpers""" - - def setUp(self) -> None: - return super().setUp() + """Class for `arch` command testing.""" @pytest.mark.skipif(ARCH != "x86_64", reason=f"Skipped for {ARCH}") def test_cmd_arch_get(self): gdb = self._gdb res = gdb.execute("arch get", to_string=True) - self.assertIn(" Architecture(X86, 64, LITTLE_ENDIAN)", res) - self.assertIn(" The architecture has been detected via the ELF headers", res) + assert " Architecture(X86, 64, LITTLE_ENDIAN)" in res + assert " The architecture has been detected via the ELF headers" in res def test_cmd_arch_set(self): gdb = self._gdb @@ -27,21 +25,21 @@ def test_cmd_arch_set(self): gdb.execute("arch set X86") res = gdb.execute("arch get", to_string=True) - self.assertIn(" Architecture(X86, 32, LITTLE_ENDIAN)", res) - self.assertIn(" The architecture has been set manually", res) + assert " Architecture(X86, 32, LITTLE_ENDIAN)" in res + assert " The architecture has been set manually" in res gdb.execute("arch set ppc") res = gdb.execute("arch get", to_string=True) - self.assertIn(" Architecture(PPC, PPC32, LITTLE_ENDIAN)", res) - self.assertIn(" The architecture has been set manually", res) + assert " Architecture(PPC, PPC32, LITTLE_ENDIAN)" in res + assert " The architecture has been set manually" in res def test_cmd_arch_list(self): gdb = self._gdb res = gdb.execute("arch list", to_string=True) - self.assertNotIn("- GenericArchitecture", res) - self.assertIn(" Architecture(X86, 64, LITTLE_ENDIAN)", res) - self.assertIn(" X86", res) - self.assertIn(" X86_64", res) + assert "- GenericArchitecture" not in res + assert " Architecture(X86, 64, LITTLE_ENDIAN)" in res + assert " X86" in res + assert " X86_64" in res