mirror of https://github.com/pwndbg/pwndbg.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
152 lines
5.2 KiB
Python
152 lines
5.2 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Command to print the virtual memory map a la /proc/self/maps.
|
|
"""
|
|
import argparse
|
|
|
|
import gdb
|
|
from elftools.elf.constants import SH_FLAGS
|
|
from elftools.elf.elffile import ELFFile
|
|
|
|
import pwndbg.color.memory as M
|
|
import pwndbg.commands
|
|
import pwndbg.elf
|
|
import pwndbg.vmmap
|
|
|
|
integer_types = (int, gdb.Value)
|
|
|
|
|
|
def pages_filter(gdbval_or_str):
|
|
# returns a module filter
|
|
if isinstance(gdbval_or_str, str):
|
|
module_name = gdbval_or_str
|
|
return lambda page: module_name in page.objfile
|
|
|
|
# returns an address filter
|
|
elif isinstance(gdbval_or_str, integer_types):
|
|
addr = gdbval_or_str
|
|
return lambda page: addr in page
|
|
|
|
else:
|
|
raise argparse.ArgumentTypeError('Unknown vmmap argument type.')
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.description = '''Print virtual memory map pages. Results can be filtered by providing address/module name.
|
|
|
|
Memory pages on QEMU targets may be inaccurate. This is because:
|
|
- for QEMU kernel on X86/X64 we fetch memory pages via `monitor info mem` and it doesn't inform if memory page is executable
|
|
- for QEMU user emulation we detected memory pages through AUXV (sometimes by finding AUXV on the stack first)
|
|
- for others, we create mempages by exploring current register values (this is least correct)
|
|
|
|
Memory pages can also be added manually, see vmmap_add, vmmap_clear and vmmap_load commands.'''
|
|
parser.formatter_class=argparse.RawDescriptionHelpFormatter
|
|
parser.add_argument('gdbval_or_str', type=pwndbg.commands.sloppy_gdb_parse, nargs='?', default=None,
|
|
help='Address or module name.')
|
|
|
|
|
|
@pwndbg.commands.ArgparsedCommand(parser, aliases=['lm', 'address', 'vprot'])
|
|
@pwndbg.commands.OnlyWhenRunning
|
|
def vmmap(gdbval_or_str=None):
|
|
pages = pwndbg.vmmap.get()
|
|
|
|
if gdbval_or_str:
|
|
pages = list(filter(pages_filter(gdbval_or_str), pages))
|
|
|
|
if not pages:
|
|
print('There are no mappings for specified address or module.')
|
|
return
|
|
|
|
print(M.legend())
|
|
|
|
if len(pages) == 1 and isinstance(gdbval_or_str, integer_types):
|
|
page = pages[0]
|
|
print(M.get(page.vaddr, text=str(page) + ' +0x%x' % (int(gdbval_or_str) - page.vaddr)))
|
|
else:
|
|
for page in pages:
|
|
print(M.get(page.vaddr, text=str(page)))
|
|
|
|
if pwndbg.qemu.is_qemu():
|
|
print("\n[QEMU target detected - vmmap result might not be accurate; see `help vmmap`]")
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.description = 'Add Print virtual memory map page.'
|
|
parser.add_argument('start', help='Starting virtual address')
|
|
parser.add_argument('size', help='Size of the address space, in bytes')
|
|
parser.add_argument('flags', nargs='?', type=str, default='', help='Flags set by the ELF file, see PF_X, PF_R, PF_W')
|
|
parser.add_argument('offset', nargs='?', default=0, help='Offset into the original ELF file that the data is loaded from')
|
|
|
|
@pwndbg.commands.ArgparsedCommand(parser)
|
|
def vmmap_add(start, size, flags, offset):
|
|
page_flags = {
|
|
'r': pwndbg.elf.PF_R,
|
|
'w': pwndbg.elf.PF_W,
|
|
'x': pwndbg.elf.PF_X,
|
|
}
|
|
perm = 0
|
|
for flag in flags:
|
|
flag_val = page_flags.get(flag, None)
|
|
if flag_val is None:
|
|
print('Invalid page flag "%s"', flag)
|
|
return
|
|
perm |= flag_val
|
|
|
|
page = pwndbg.memory.Page(start, size, perm, offset)
|
|
pwndbg.vmmap.add_custom_page(page)
|
|
|
|
print('%r added' % page)
|
|
|
|
|
|
@pwndbg.commands.ArgparsedCommand("Clear the vmmap cache.") #TODO is this accurate?
|
|
def vmmap_clear():
|
|
pwndbg.vmmap.clear_custom_page()
|
|
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.description = 'Load virtual memory map pages from ELF file.'
|
|
parser.add_argument('filename', nargs='?', type=str, help='ELF filename, by default uses current loaded filename.')
|
|
|
|
@pwndbg.commands.ArgparsedCommand(parser)
|
|
def vmmap_load(filename):
|
|
if filename is None:
|
|
filename = pwndbg.proc.exe
|
|
|
|
print('Load "%s" ...' % filename)
|
|
|
|
# TODO: Add an argument to let use to choose loading the page information from sections or segments
|
|
|
|
# Use section information to recover the segment information.
|
|
# The entry point of bare metal enviroment is often at the first segment.
|
|
# For example, assume the entry point is at 0x8000.
|
|
# In most of case, link will create a segment and starts from 0x0.
|
|
# This cause all values less than 0x8000 be considered as a valid pointer.
|
|
pages = []
|
|
with open(filename, 'rb') as f:
|
|
elffile = ELFFile(f)
|
|
|
|
for section in elffile.iter_sections():
|
|
vaddr = section['sh_addr']
|
|
memsz = section['sh_size']
|
|
sh_flags = section['sh_flags']
|
|
offset = section['sh_offset']
|
|
|
|
# Don't add the sections that aren't mapped into memory
|
|
if not sh_flags & SH_FLAGS.SHF_ALLOC:
|
|
continue
|
|
|
|
# Guess the segment flags from section flags
|
|
flags = pwndbg.elf.PF_R
|
|
if sh_flags & SH_FLAGS.SHF_WRITE:
|
|
flags |= pwndbg.elf.PF_W
|
|
if sh_flags & SH_FLAGS.SHF_EXECINSTR:
|
|
flags |= pwndbg.elf.PF_X
|
|
|
|
page = pwndbg.memory.Page(vaddr, memsz, flags, offset, filename)
|
|
pages.append(page)
|
|
|
|
for page in pages:
|
|
pwndbg.vmmap.add_custom_page(page)
|
|
print('%r added' % page)
|