-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathqemu.py
173 lines (127 loc) · 5.05 KB
/
qemu.py
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
"""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