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)