From f0b6d1d3e3a8a367341fb17dbae5202f9f10d7d8 Mon Sep 17 00:00:00 2001 From: Gordon Messmer Date: Sat, 1 Jun 2024 10:07:33 -0700 Subject: [PATCH] Add an "--all" option to the got command (#1101) ## Description This change adds a `got --all` option which expands on the existing "got" command by providing data about relocations in mapped shared object files in addition to the relocations specific to the main executable. Particularly for auditing purposes, users may be interested in the state of all relocations, not only those for the primary executable file. --- docs/commands/got.md | 13 ++++++++++++- gef.py | 21 +++++++++++++++++---- tests/commands/got.py | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/docs/commands/got.md b/docs/commands/got.md index 04aeef685..fccbf9a0e 100644 --- a/docs/commands/got.md +++ b/docs/commands/got.md @@ -6,9 +6,11 @@ The `got` command optionally takes function names and filters the output display matching functions. ```text -gef➤ got +gef➤ got [--all] [filters] ``` +`--all` Print the GOT for all shared objects in addition to the executable file + ![gef-got](https://i.imgur.com/554ebM3.png) The applied filter partially matches the name of the functions, so you can do something like this. @@ -28,3 +30,12 @@ gef➤ got str get ``` ![gef-got-multi-filter](https://i.imgur.com/7L2uLt8.png) + +```text +gef➤ got --all str get +``` + +Print relocatable symbols matching "str" or "get" in the executable and all shared object files. + +**Note**: Because gdbserver does not canonicalize paths, the --all option does not work correctly +for remote debugging. See gdb bug [23764](https://sourceware.org/bugzilla/show_bug.cgi?id=23764) diff --git a/gef.py b/gef.py index 22b9b5e94..11a466fd4 100644 --- a/gef.py +++ b/gef.py @@ -9290,11 +9290,24 @@ def build_line(self, name: str, color: str, address_val: int, got_address: int) return line @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + @parse_arguments({"symbols": [""]}, {"--all": False}) + def do_invoke(self, _: List[str], **kwargs: Any) -> None: + args : argparse.Namespace = kwargs["arguments"] + if args.all: + vmmap = gef.memory.maps + mapfiles = set(mapfile.path for mapfile in vmmap if + pathlib.Path(mapfile.realpath).is_file() and + mapfile.permission & Permission.EXECUTE) + for mapfile in mapfiles: + self.print_got_for(mapfile, args.symbols) + else: + self.print_got_for(str(gef.session.file), args.symbols) + + def print_got_for(self, file: str, argv: List[str]) -> None: readelf = gef.session.constants["readelf"] - elf_file = str(gef.session.file) - elf_virtual_path = str(gef.session.file) + elf_file = file + elf_virtual_path = file func_names_filter = argv if argv else [] vmmap = gef.memory.maps @@ -9318,7 +9331,7 @@ def do_invoke(self, argv: List[str]) -> None: lines = gef_execute_external([readelf, "--wide", "--relocs", elf_file], as_list=True) jmpslots = [line for line in lines if "JUMP" in line] - gef_print(f"\nGOT protection: {relro_status} | GOT functions: {len(jmpslots)}\n ") + gef_print(f"{titlify(file)}\n\nGOT protection: {relro_status} | GOT functions: {len(jmpslots)}\n ") for line in jmpslots: address, _, _, _, name = line.split()[:5] diff --git a/tests/commands/got.py b/tests/commands/got.py index 29e49f60d..84ff68721 100644 --- a/tests/commands/got.py +++ b/tests/commands/got.py @@ -35,3 +35,36 @@ def test_cmd_got(self): res = gdb.execute("got printf", to_string=True) self.assertIn("printf", res) self.assertNotIn("strcpy", res) + self.assertNotIn("/libc", res) + + def checksyms(lines): + if not lines: + return None + if "format-string-helper.out" in lines[0]: + res = ''.join(lines) + self.assertIn(" printf", res) + self.assertNotIn(" strcpy", res) + return "format-string-helper.out" + if "/libc" in lines[0]: + res = ''.join(lines) + self.assertNotIn(" printf", res) + self.assertNotIn(" strcpy", res) + return "libc" + return None + + res = gdb.execute("got --all printf", to_string=True) + # Keep a list of output blocks describing files mapped in the process + checked_sections = [] + # Iterate over lines of output and assemble blocks. When a new block + # is found, or when the end of output is reached, check the output + # block for symbols expected in that block. + lines = [] + for line in res.splitlines(): + if line.startswith("─"): + checked_sections.append(checksyms(lines)) + lines = [] + lines.append(line) + checked_sections.append(checksyms(lines)) + # Make sure that both the executable and libc sections were found. + self.assertIn("format-string-helper.out", checked_sections) + self.assertIn("libc", checked_sections)