diff --git a/docs/commands/vmmap.md b/docs/commands/vmmap.md index 597decb8d..457a71c97 100644 --- a/docs/commands/vmmap.md +++ b/docs/commands/vmmap.md @@ -10,22 +10,28 @@ place). For example, you can learn that ELF running on SPARC architectures alway and `heap` sections set as Read/Write/Execute. `vmmap` can accept multiple arguments, either patterns to match again mapping names, or addresses -to determine which section it belongs to: +to determine which section it belongs to, or the permissions of the sections to match: 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 +3. `-p` / `--perms`: + - filter based on section permissions +4. If nothing is specified, it prints a warning and guesses the type ![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. +The address can be also be given in the form of a register or variable: ![vmmap-register](https://github.com/hugsy/gef/assets/11377623/aed7ecdc-7ad9-4ba5-ae03-329e66432731) +Using permissions: + +![vmmap-perms](https://github.com/user-attachments/assets/c34822d3-2369-464f-aafa-87113b293174) + 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 7ce988f7a..f53dd3c2e 100644 --- a/gef.py +++ b/gef.py @@ -664,6 +664,21 @@ def from_info_mem(cls, perm_str: str) -> "Permission": if "x" in perm_str: perm |= Permission.EXECUTE return perm + @classmethod + def from_filter_repr(cls, filter_str: str) -> list["Permission"]: + perms: list["Permission"] = [cls(0)] + + for k in range(3): + for i in range(len(perms)): + p = cls(1 << (2-k)) + if filter_str[k] == "rwx"[k]: + perms[i] |= p + elif filter_str[k] == "?": + perms.append(perms[i] | p) + + return perms + + class Section: """GEF representation of process memory sections.""" @@ -8910,7 +8925,9 @@ class VMMapCommand(GenericCommand): _example_ = f"{_cmdline_} libc" @only_if_gdb_running - @parse_arguments({"unknown_types": [""]}, {("--addr", "-a"): [""], ("--name", "-n"): [""]}) + @parse_arguments({"unknown_types": [""]}, {("--addr", "-a"): [""], + ("--name", "-n"): [""], + ("--perms", "-p"): [""]}) def do_invoke(self, _: list[str], **kwargs: Any) -> None: args : argparse.Namespace = kwargs["arguments"] vmmap = gef.memory.maps @@ -8920,6 +8937,10 @@ def do_invoke(self, _: list[str], **kwargs: Any) -> None: addrs: dict[str, int] = {x: parse_address(x) for x in args.addr} names: list[str] = [x for x in args.name] + perms: set[Permission] = set() + + for p in args.perms: + perms = perms.union(Permission.from_filter_repr(p)) for arg in args.unknown_types: if not arg: @@ -8931,12 +8952,19 @@ def do_invoke(self, _: list[str], **kwargs: Any) -> None: addr = safe_parse_and_eval(arg) if addr is None: + if arg[0] in "r-?" and \ + arg[1] in "w-?" and \ + arg[2] in "x-?": + perms = perms.union(Permission.from_filter_repr(arg)) + warn(f"`{arg}` has no type specified. We guessed it was a perm filter.") + continue + 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.") + warn("You can use --name, --addr or --perms before the filter value for specifying its type manually.") gef_print() if not gef.config["gef.disable_color"]: @@ -8953,12 +8981,13 @@ def do_invoke(self, _: list[str], **kwargs: Any) -> None: 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])}]" + perms_filter = [f"perms = {x}" for x in perms if entry.permission == x] + filter_content = f"[{' & '.join([*names_filter, *addrs_filter, *perms_filter])}]" - if not names and not addrs: + if not names and not addrs and not perms: self.print_entry(entry) - elif names_filter or addrs_filter: + elif names_filter or addrs_filter or perms_filter: if filter_content != last_printed_filter: gef_print() # skip a line between different filters gef_print(Color.greenify(filter_content)) diff --git a/tests/api/misc.py b/tests/api/misc.py index 9f9a0f183..adc81b7f5 100644 --- a/tests/api/misc.py +++ b/tests/api/misc.py @@ -77,3 +77,23 @@ def test_func_process_lookup_path(self): assert "libc" in pathlib.Path(libc.path).name assert root.eval("process_lookup_path('stack')") is not None + + def test_func_from_filter_repr(self): + root = self._conn.root + Permission = root.eval("Permission") + + none = Permission.from_filter_repr("---") + assert len(none) == 1 + assert none[0] == Permission.NONE + + all_readable_perms = Permission.from_filter_repr("r??") + assert all(x & Permission.READ for x in all_readable_perms) + assert len(all_readable_perms) == 4 + + all_writable_perms = Permission.from_filter_repr("?w?") + assert all(x & Permission.WRITE for x in all_writable_perms) + assert len(all_writable_perms) == 4 + + all_executable_perms = Permission.from_filter_repr("??x") + assert all(x & Permission.EXECUTE for x in all_executable_perms) + assert len(all_executable_perms) == 4 diff --git a/tests/commands/vmmap.py b/tests/commands/vmmap.py index fbc00a46a..e10b2bb4b 100644 --- a/tests/commands/vmmap.py +++ b/tests/commands/vmmap.py @@ -27,6 +27,10 @@ def test_cmd_vmmap(self): assert "`$pc` has no type specified. We guessed it was an address filter." in res self.assertEqual(len(res.splitlines()), 8) + res = gdb.execute("vmmap r-?", to_string=True) + assert "`r-?` has no type specified. We guessed it was a perm filter." in res + self.assertGreater(len(res.splitlines()), 3) + def test_cmd_vmmap_addr(self): gef, gdb = self._gef, self._gdb gdb.execute("start") @@ -48,3 +52,27 @@ def test_cmd_vmmap_name(self): res = gdb.execute("vmmap --name stack", to_string=True) self.assertEqual(len(res.splitlines()), 5) + + def test_cmd_vmmap_perm(self): + gdb = self._gdb + gdb.execute("start") + + res1 = gdb.execute("vmmap -p r?-", to_string=True) + lines1 = res1.splitlines() + self.assertGreater(len(lines1), 5) + for line in lines1: + if line[:2] != "0x": + continue + perm_str = line.split()[3] + assert perm_str[0] == "r" + assert perm_str[1] in ("w", "-") + assert perm_str[2] == "-" + + res2 = gdb.execute("vmmap --perms r?-", to_string=True) + assert res1 == res2 + + res = gdb.execute("vmmap -p rw-", to_string=True) + self.assertGreater(len(res.splitlines()), 5) + + res = gdb.execute("vmmap --perms rw-", to_string=True) + self.assertGreater(len(res.splitlines()), 5)