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.
pwndbg/pwndbg/file.py

133 lines
3.3 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Retrieve files from the debuggee's filesystem. Useful when
debugging a remote process over SSH or similar, where e.g.
/proc/FOO/maps is needed from the remote system.
"""
import binascii
import os
import tempfile
import gdb
import pwndbg.qemu
import pwndbg.remote
import pwndbg.symbol
def get_file(path):
"""
Downloads the specified file from the system where the current process is
being debugged.
Returns:
The local path to the file
"""
local_path = path
qemu_root = pwndbg.qemu.root()
if qemu_root:
return os.path.join(qemu_root, path)
elif pwndbg.remote.is_remote() and not pwndbg.qemu.is_qemu():
local_path = tempfile.mktemp(dir=pwndbg.symbol.remote_files_dir)
error = None
try:
error = gdb.execute('remote get "%s" "%s"' % (path, local_path),
to_string=True)
except gdb.error as e:
error = e
if error:
raise OSError("Could not download remote file %r:\n" \
"Error: %s" % (path, error))
return local_path
def get(path):
"""
Retrieves the contents of the specified file on the system
where the current process is being debugged.
Returns:
A byte array, or None.
"""
local_path = get_file(path)
try:
with open(local_path,'rb') as f:
return f.read()
except:
return b''
def readlink(path):
"""readlink(path) -> str
Read the link specified by 'path' on the system being debugged.
Handles local, qemu-usermode, and remote debugging cases.
"""
is_qemu = pwndbg.qemu.is_qemu_usermode()
if is_qemu:
if not os.path.exists(path):
path = os.path.join(pwndbg.qemu.root(), path)
if is_qemu or not pwndbg.remote.is_remote():
try:
return os.readlink(path)
except Exception:
return ''
#
# Hurray unexposed packets!
#
# The 'vFile:readlink:' packet does exactly what it sounds like,
# but there is no API exposed to do this and there is also no
# command exposed... so we have to send the packet manually.
#
cmd = 'maintenance packet vFile:readlink:%s'
# The path must be uppercase hex-encoded and NULL-terminated.
path += '\x00'
path = binascii.hexlify(path.encode())
path = path.upper()
path = path.decode()
result = gdb.execute(cmd % path, from_tty=False, to_string=True)
"""
sending: "vFile:readlink:2F70726F632F3130303839302F66642F3000"
received: "Fc;pipe:[98420]"
sending: "vFile:readlink:2F70726F632F3130303839302F66642F333300"
received: "F-1,2"
"""
_, data = result.split('\n', 1)
# Sanity check
expected = 'received: "F'
if not data.startswith(expected):
return ''
# Negative values are errors
data = data[len(expected):]
if data[0] == '-':
return ''
# If non-negative, there will be a hex-encoded length followed
# by a semicolon.
n, data = data.split(';', 1)
n = int(n, 16)
if n < 0:
return ''
# The result is quoted by GDB, strip the quote and newline.
# I have no idea how well it handles other crazy stuff.
ending = '"\n'
data = data[:-len(ending)]
return data