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.
133 lines
3.3 KiB
Python
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
|