1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
|
"""Run foreign-architecture binaries
Overview
--------
So you want to exploit ARM binaries on your Intel PC?
Pwntools has a good level of integration with QEMU user-mode emulation,
in order to run, debug, and pwn foreign architecture binaries.
In general, everything magic happens "behind the scenes", and pwntools
attempts to make your life easier.
When using :class:`.process`, pwntools will attempt to blindly
execute the binary, in case your system is configured to use ``binfmt-misc``.
If this fails, pwntools will attempt to manually launch the binary under
qemu user-mode emulation. Preference is given to statically-linked variants,
i.e. ``qemu-arm-static`` will be selected before ``qemu-arm``.
Debugging
~~~~~~~~~
When debugging binaries with :func:`.gdb.debug`, pwntools automatically adds
the appropriate command-line flags to QEMU to start its GDB stub, and
automatically informs GDB of the correct architecture and sysroot.
Sysroot
~~~~~~~
You can override the default sysroot by setting the ``QEMU_LD_PREFIX``
environment variable. This affects where ``qemu`` will look for files when
``open()`` is called, e.g. when the linker is attempting to resolve ``libc.so``.
Required Setup
--------------
For Ubuntu 16.04 and newer, the setup is relatively straightforward for most
architectures.
First, install the QEMU emulator itself. If your binary is statically-linked,
this is sufficient. ::
$ sudo apt-get install qemu-user
If your binary is dynamically linked, you need to install libraries like libc.
Generally, this package is named ``libc6-$ARCH-cross``, e.g. ``libc-mips-cross``.
ARM comes in both soft-float and hard-float variants, e.g. ``armhf``. ::
$ sudo apt-get install libc6-arm64-cross
If your binary relies on additional libraries, you can generally find them
easily with ``apt-cache search``. For example, if it's a C++ binary it
may require ``libstdc++``. ::
$ apt-cache search 'libstdc++' | grep arm64
Any other libraries that you require you'll have to find some other way.
Telling QEMU Where Libraries Are
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The libraries are now installed on your system at e.g. ``/usr/aarch64-linux-gnu``.
QEMU does not know where they are, and expects them to be at e.g. ``/etc/qemu-binfmt/aarch64``.
If you try to run your library now, you'll probably see an error about ``libc.so.6`` missing.
Create the ``/etc/qemu-binfmt`` directory if it does not exist, and create a symlink to
the appropriate path. ::
$ sudo mkdir /etc/qemu-binfmt
$ sudo ln -s /usr/aarch64-linux-gnu /etc/qemu-binfmt/aarch64
Now QEMU should be able to run the libraries.
"""
from __future__ import absolute_import
from __future__ import division
import os
from pwnlib.context import LocalContext
from pwnlib.context import context
from pwnlib.log import getLogger
from pwnlib.util import misc
log = getLogger(__name__)
@LocalContext
def archname():
"""
Returns the name which QEMU uses for the currently selected
architecture.
>>> pwnlib.qemu.archname()
'i386'
>>> pwnlib.qemu.archname(arch='powerpc')
'ppc'
"""
return {
('amd64', 'little'): 'x86_64',
('arm', 'big'): 'armeb',
('mips', 'little'): 'mipsel',
('mips64', 'little'): 'mips64el',
('powerpc', 'big'): 'ppc',
('powerpc64', 'big'): 'ppc64',
('powerpc64', 'little'): 'ppc64le',
('thumb', 'little'): 'arm',
('thumb', 'big'): 'armeb',
}.get((context.arch, context.endian), context.arch)
@LocalContext
def user_path():
"""
Returns the path to the QEMU-user binary for the currently
selected architecture.
>>> pwnlib.qemu.user_path()
'qemu-i386-static'
>>> pwnlib.qemu.user_path(arch='thumb')
'qemu-arm-static'
"""
arch = archname()
system = 'qemu-system-' + arch
normal = 'qemu-' + arch
static = normal + '-static'
if context.os == 'baremetal':
if misc.which(system):
return system
else:
if misc.which(static):
return static
if misc.which(normal):
return normal
log.warn_once("Neither %r nor %r are available" % (normal, static))
@LocalContext
def ld_prefix(path=None, env=None):
"""Returns the linker prefix for the selected qemu-user binary
>>> pwnlib.qemu.ld_prefix(arch='arm') # doctest: +SKIP
'/etc/qemu-binfmt/arm'
"""
if context.os == 'baremetal':
return ""
if path is None:
path = user_path()
# Did we explicitly specify the path in an environment variable?
if env and b'QEMU_LD_PREFIX' in env:
return env[b'QEMU_LD_PREFIX'].decode()
if 'QEMU_LD_PREFIX' in os.environ:
return os.environ['QEMU_LD_PREFIX']
# Cyclic imports!
from pwnlib.tubes.process import process
with context.quiet:
with process([path, '--help'], env=env) as io:
line = io.recvline_regex(b'QEMU_LD_PREFIX *=')
_, libpath = line.split(b'=', 1)
libpath = libpath.strip()
if not isinstance(libpath, str):
libpath = libpath.decode('utf-8')
return libpath
|