From ba70548a3ee24355c1c6daca0aa302d326dc2335 Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Thu, 24 Oct 2024 10:21:44 +0200 Subject: [PATCH] [cmd] Add support for flags specifying filters type in vmmap, and allow multiple filters (#1120) Parse args: - `-a` / `--addr`: - filter by address -> parses the next arg as an int or asks gdb the value - `-n` / `--name`: - filter based on section name - If nothing is specified, print a warning and guess the type (previous behavior) --------- Co-authored-by: Grazfather --- docs/commands/vmmap.md | 20 +++++++++++---- gef.py | 55 ++++++++++++++++++++++++++++++++--------- tests/commands/vmmap.py | 28 +++++++++++++++++++-- 3 files changed, 84 insertions(+), 19 deletions(-) diff --git a/docs/commands/vmmap.md b/docs/commands/vmmap.md index 73f20b36e..597decb8d 100644 --- a/docs/commands/vmmap.md +++ b/docs/commands/vmmap.md @@ -9,13 +9,23 @@ differs from one architecture to another (this is one of the main reasons I star place). For example, you can learn that ELF running on SPARC architectures always have their `.data` and `heap` sections set as Read/Write/Execute. -`vmmap` accepts one argument, either a pattern to match again mapping names, or an address to -determine which section it belongs to. +`vmmap` can accept multiple arguments, either patterns to match again mapping names, or addresses +to determine which section it belongs to: -![vmmap-grep](https://i.imgur.com/ZFF4QVf.png) +1. `-a` / `--addr`: + - filter by address -> parses the next argument as an integer or asks gdb to interpret the value +2. `-n` / `--name`: + - filter based on section name +3. If nothing is specified, it prints a warning and guesses the type -![vmmap-address](https://i.imgur.com/hfcs1jH.png) +![vmmap-grep](https://github.com/hugsy/gef/assets/11377623/a3dbaa3e-88b0-407f-a0dd-07e65c4a3f73) + +![vmmap-address](https://github.com/hugsy/gef/assets/11377623/4dffe491-f927-4f03-b842-4d941140e66c) The address can be also be given in the form of a register or variable. -![vmmap-register](https://i.imgur.com/RlZA6NU.png) +![vmmap-register](https://github.com/hugsy/gef/assets/11377623/aed7ecdc-7ad9-4ba5-ae03-329e66432731) + +And you can do all of them in one command 🙂 + +![vmmap-all-in-one](https://github.com/hugsy/gef/assets/11377623/b043f61b-48b3-4316-9f84-eb83822149ac) diff --git a/gef.py b/gef.py index 80c18c676..ac9389003 100644 --- a/gef.py +++ b/gef.py @@ -8898,12 +8898,35 @@ class VMMapCommand(GenericCommand): _example_ = f"{_cmdline_} libc" @only_if_gdb_running - def do_invoke(self, argv: List[str]) -> None: + @parse_arguments({"unknown_types": [""]}, {("--addr", "-a"): [""], ("--name", "-n"): [""]}) + 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] + + for arg in args.unknown_types: + if not arg: + continue + + if self.is_integer(arg): + addr = int(arg, 0) + else: + addr = safe_parse_and_eval(arg) + + if addr is None: + names.append(arg) + warn(f"`{arg}` has no type specified. We guessed it was a name filter.") + else: + addrs[arg] = int(addr) + warn(f"`{arg}` has no type specified. We guessed it was an address filter.") + warn("You can use --name or --addr before the filter value for specifying its type manually.") + gef_print() + if not gef.config["gef.disable_color"]: self.show_legend() @@ -8912,22 +8935,30 @@ def do_invoke(self, argv: List[str]) -> None: headers = ["Start", "End", "Offset", "Perm", "Path"] gef_print(Color.colorify("{:<{w}s}{:<{w}s}{:<{w}s}{:<4s} {:s}".format(*headers, w=gef.arch.ptrsize*2+3), color)) + last_printed_filter = None + for entry in vmmap: - if not argv: + names_filter = [f"name = '{x}'" for x in names if x in entry.path] + addrs_filter = [f"addr = {self.format_addr_filter(arg, addr)}" for arg, addr in addrs.items() + if entry.page_start <= addr < entry.page_end] + filter_content = f"[{' & '.join([*names_filter, *addrs_filter])}]" + + if not names and not addrs: self.print_entry(entry) - continue - if argv[0] in entry.path: + + elif names_filter or addrs_filter: + if filter_content != last_printed_filter: + gef_print() # skip a line between different filters + gef_print(Color.greenify(filter_content)) + last_printed_filter = filter_content self.print_entry(entry) - elif self.is_integer(argv[0]): - addr = int(argv[0], 0) - if addr >= entry.page_start and addr < entry.page_end: - self.print_entry(entry) - else: - addr = safe_parse_and_eval(argv[0]) - if addr is not None and addr >= entry.page_start and addr < entry.page_end: - self.print_entry(entry) + + gef_print() return + def format_addr_filter(self, arg: str, addr: int): + return f"`{arg}`" if self.is_integer(arg) else f"`{arg}` ({addr:#x})" + def print_entry(self, entry: Section) -> None: line_color = "" if entry.path == "[stack]": diff --git a/tests/commands/vmmap.py b/tests/commands/vmmap.py index 1ac0c5a2e..fbc00a46a 100644 --- a/tests/commands/vmmap.py +++ b/tests/commands/vmmap.py @@ -20,7 +20,31 @@ def test_cmd_vmmap(self): self.assertGreater(len(res.splitlines()), 1) res = gdb.execute("vmmap stack", to_string=True) - self.assertGreater(len(res.splitlines()), 1) + assert "`stack` has no type specified. We guessed it was a name filter." in res + self.assertEqual(len(res.splitlines()), 9) res = gdb.execute("vmmap $pc", to_string=True) - self.assertEqual(len(res.splitlines()), 2) + assert "`$pc` has no type specified. We guessed it was an address filter." in res + self.assertEqual(len(res.splitlines()), 8) + + def test_cmd_vmmap_addr(self): + gef, gdb = self._gef, self._gdb + gdb.execute("start") + + pc = gef.arch.register("pc") + + res = gdb.execute(f"vmmap -a {pc:#x}", to_string=True) + self.assertEqual(len(res.splitlines()), 5) + + res = gdb.execute("vmmap --addr $pc", to_string=True) + self.assertEqual(len(res.splitlines()), 5) + + def test_cmd_vmmap_name(self): + gdb = self._gdb + gdb.execute("start") + + res = gdb.execute("vmmap -n stack", to_string=True) + self.assertEqual(len(res.splitlines()), 5) + + res = gdb.execute("vmmap --name stack", to_string=True) + self.assertEqual(len(res.splitlines()), 5)