feat: Back vmmap with info proc mappings on newer qemu versions

pull/1927/head
Filip Grzywok 2 years ago committed by Disconnect3d
parent 8dd9382b73
commit e5a3a50a70

@ -3,6 +3,7 @@ Command to print the virtual memory map a la /proc/self/maps.
"""
from __future__ import annotations
import re
import argparse
import gdb
@ -151,9 +152,16 @@ def vmmap(
print(M.get(page.vaddr, text=display_text, prefix=backtrace_prefix))
if pwndbg.gdblib.qemu.is_qemu():
if pwndbg.gdblib.qemu.is_qemu() and not pwndbg.gdblib.qemu.exec_file_supported():
print("\n[QEMU target detected - vmmap result might not be accurate; see `help vmmap`]")
gdb_version = tuple(map(int, re.search(r"(\d+)[^\d]+(\d+)", gdb.VERSION).groups()))
# Only GDB versions >=12 report permission info in info proc mappings. On older versions, we fallback on "rwx".
# * https://github.com/bminor/binutils-gdb/commit/29ef4c0699e1b46d41ade00ae07a54f979ea21cc
if pwndbg.gdblib.qemu.is_qemu_usermode() and gdb_version[0] < 12:
print("\n[GDB <=12.1 detected - vmmap cannot fetch permission information, defaulting to rwx]")
parser = argparse.ArgumentParser(description="Add virtual memory map page.")
parser.add_argument("start", help="Starting virtual address")

@ -58,6 +58,15 @@ def is_qemu_kernel() -> bool:
return is_qemu() and not is_usermode()
@pwndbg.lib.cache.cache_until("stop")
def exec_file_supported() -> bool:
"""Returns ``True`` if the qemu target supports exec file feature.
Used in `vmmap` to determine whether qemu supports `info proc mappings`
"""
response = gdb.execute("maintenance packet qSupported", to_string=True, from_tty=False)
return "exec-file" in response
@start
@pwndbg.lib.cache.cache_until("stop")
def root() -> Any | None:

@ -91,7 +91,10 @@ def get() -> tuple[pwndbg.lib.memory.Page, ...]:
if is_corefile():
return tuple(coredump_maps())
proc_maps = proc_pid_maps()
if pwndbg.gdblib.qemu.is_qemu_usermode():
proc_maps = info_proc_maps()
else:
proc_maps = proc_pid_maps()
# The `proc_maps` is usually a tuple of Page objects but it can also be:
# None - when /proc/$pid/maps does not exist/is not available
@ -338,6 +341,52 @@ def coredump_maps():
return tuple(pages)
@pwndbg.lib.cache.cache_until("start", "stop")
def info_proc_maps():
"""
Parse the result of info proc mappings.
Returns:
A tuple of pwndbg.lib.memory.Page objects or None if
info proc mapping is not supported on the target.
"""
try:
info_proc_mappings = pwndbg.gdblib.info.proc_mappings().splitlines()
except gdb.error:
# On qemu user emulation, we may get: gdb.error: Not supported on this target.
info_proc_mappings = []
pages = []
for line in info_proc_mappings:
# We look for lines like:
# ['0x555555555000', '0x555555556000', '0x1000', '0x1000', 'rw-p', '/home/user/a.out']
try:
split_line = line.split()
# Permission info is only available in GDB versions >=12
# https://github.com/bminor/binutils-gdb/commit/29ef4c0699e1b46d41ade00ae07a54f979ea21cc
# Assume "rw-p" on older gdb versions
if len(split_line) < 6:
start, _end, size, offset, objfile = split_line
perm = 'rwxp'
else:
start, _end, size, offset, perm, objfile = split_line
start, size, offset = int(start, 16), int(size, 16), int(offset, 16)
except (IndexError, ValueError):
continue
flags = 0
if "r" in perm:
flags |= 4
if "w" in perm:
flags |= 2
if "x" in perm:
flags |= 1
pages.append(pwndbg.lib.memory.Page(start, size, flags, offset, objfile))
return tuple(pages)
@pwndbg.lib.cache.cache_until("start", "stop")
def proc_pid_maps():
"""

Loading…
Cancel
Save