pwnlib.context — Setting runtime variables

Many settings in pwntools are controlled via the global variable :data:`.context, such as the selected target operating system, architecture, and bit-width.

In general, exploits will start with something like:

from pwn import *
context.arch = 'amd64'

Which sets up everything in the exploit for exploiting a 64-bit Intel binary.

The recommended method is to use context.binary to automagically set all of the appropriate values.

from pwn import *
context.binary = './challenge-binary'

Module Members

Implements context management so that nested/scoped contexts and threaded contexts work properly and as expected.

class pwnlib.context.ContextType(**kwargs)[source]

Class for specifying information about the target machine. Intended for use as a pseudo-singleton through the global variable context, available via from pwn import * as context.

The context is usually specified at the top of the Python file for clarity.

#!/usr/bin/env python
context.update(arch='i386', os='linux')

Currently supported properties and their defaults are listed below. The defaults are inherited from pwnlib.context.ContextType.defaults.

Additionally, the context is thread-aware when using pwnlib.context.Thread instead of threading.Thread (all internal pwntools threads use the former).

The context is also scope-aware by using the with keyword.

Examples

>>> context.clear()
>>> context.update(os='linux') 
>>> context.os == 'linux'
True
>>> context.arch = 'arm'
>>> vars(context) == {'arch': 'arm', 'bits': 32, 'endian': 'little', 'os': 'linux'}
True
>>> context.endian
'little'
>>> context.bits
32
>>> def nop():
...   print pwnlib.asm.asm('nop').encode('hex')
>>> nop()
00f020e3
>>> with context.local(arch = 'i386'):
...   nop()
90
>>> from pwnlib.context import Thread as PwnThread
>>> from threading      import Thread as NormalThread
>>> with context.local(arch = 'mips'):
...     pwnthread = PwnThread(target=nop)
...     thread    = NormalThread(target=nop)
>>> # Normal thread uses the default value for arch, 'i386'
>>> _=(thread.start(), thread.join())
90
>>> # Pwnthread uses the correct context from creation-time
>>> _=(pwnthread.start(), pwnthread.join())
00000000
>>> nop()
00f020e3

Initialize the ContextType structure.

All keyword arguments are passed to update().

class Thread(*args, **kwargs)[source]

Instantiates a context-aware thread, which inherit its context when it is instantiated. The class can be accessed both on the context module as pwnlib.context.Thread and on the context singleton object inside the context module as pwnlib.context.context.Thread.

Threads created by using the native :class`threading`.Thread` will have a clean (default) context.

Regardless of the mechanism used to create any thread, the context is de-coupled from the parent thread, so changes do not cascade to child or parent.

Saves a copy of the context when instantiated (at __init__) and updates the new thread’s context before passing control to the user code via run or target=.

Examples

>>> context.clear()
>>> context.update(arch='arm')
>>> def p():
...     print context.arch
...     context.arch = 'mips'
...     print context.arch
>>> # Note that a normal Thread starts with a clean context
>>> # (i386 is the default architecture)
>>> t = threading.Thread(target=p)
>>> _=(t.start(), t.join())
i386
mips
>>> # Note that the main Thread's context is unchanged
>>> print context.arch
arm
>>> # Note that a context-aware Thread receives a copy of the context
>>> t = pwnlib.context.Thread(target=p)
>>> _=(t.start(), t.join())
arm
mips
>>> # Again, the main thread is unchanged
>>> print context.arch
arm

Implementation Details:

This class implemented by hooking the private function threading.Thread._Thread_bootstrap(), which is called before passing control to threading.Thread.run().

This could be done by overriding run itself, but we would have to ensure that all uses of the class would only ever use the keyword target= for __init__, or that all subclasses invoke super(Subclass.self).set_up_context() or similar.

ContextType.clear(*a, **kw)[source]

Clears the contents of the context. All values are set to their defaults.

Parameters:
  • a – Arguments passed to update
  • kw – Arguments passed to update

Examples

>>> # Default value
>>> context.clear()
>>> context.arch == 'i386'
True
>>> context.arch = 'arm'
>>> context.arch == 'i386'
False
>>> context.clear()
>>> context.arch == 'i386'
True
ContextType.copy() → dict[source]

Returns a copy of the current context as a dictionary.

Examples

>>> context.clear()
>>> context.os   = 'linux'
>>> vars(context) == {'os': 'linux'}
True
ContextType.local(**kwargs) → context manager[source]

Create a context manager for use with the with statement.

For more information, see the example below or PEP 343.

Parameters:kwargs – Variables to be assigned in the new environment.
Returns:ContextType manager for managing the old and new environment.

Examples

>>> context.clear()
>>> context.timeout = 1
>>> context.timeout == 1
True
>>> print context.timeout
1.0
>>> with context.local(timeout = 2):
...     print context.timeout
...     context.timeout = 3
...     print context.timeout
2.0
3.0
>>> print context.timeout
1.0
ContextType.quietfunc(function)[source]

Similar to quiet, but wraps a whole function.

ContextType.reset_local()[source]

Deprecated. Use clear().

ContextType.update(*args, **kwargs)[source]

Convenience function, which is shorthand for setting multiple variables at once.

It is a simple shorthand such that:

context.update(os = 'linux', arch = 'arm', ...)

is equivalent to:

context.os   = 'linux'
context.arch = 'arm'
...

The following syntax is also valid:

context.update({'os': 'linux', 'arch': 'arm'})
Parameters:kwargs – Variables to be assigned in the environment.

Examples

>>> context.clear()
>>> context.update(arch = 'i386', os = 'linux')
>>> context.arch, context.os
('i386', 'linux')
ContextType.adb[source]

Returns an argument array for connecting to adb.

Unless $ADB_PATH is set, uses the default adb binary in $PATH.

ContextType.adb_host[source]

Sets the target host which is used for ADB.

This is useful for Android exploitation.

The default value is inherited from ANDROID_ADB_SERVER_HOST, or set to the default ‘localhost’.

ContextType.adb_port[source]

Sets the target port which is used for ADB.

This is useful for Android exploitation.

The default value is inherited from ANDROID_ADB_SERVER_PORT, or set to the default 5037.

ContextType.arch[source]

Target binary architecture.

Allowed values are listed in pwnlib.context.ContextType.architectures.

Side Effects:

If an architecture is specified which also implies additional attributes (e.g. ‘amd64’ implies 64-bit words, ‘powerpc’ implies big-endian), these attributes will be set on the context if a user has not already set a value.

The following properties may be modified.

Raises:AttributeError – An invalid architecture was specified

Examples

>>> context.clear()
>>> context.arch == 'i386' # Default architecture
True
>>> context.arch = 'mips'
>>> context.arch == 'mips'
True
>>> context.arch = 'doge' 
Traceback (most recent call last):
 ...
AttributeError: arch must be one of ['aarch64', ..., 'thumb']
>>> context.arch = 'ppc'
>>> context.arch == 'powerpc' # Aliased architecture
True
>>> context.clear()
>>> context.bits == 32 # Default value
True
>>> context.arch = 'amd64'
>>> context.bits == 64 # New value
True

Note that expressly setting bits means that we use that value instead of the default

>>> context.clear()
>>> context.bits = 32
>>> context.arch = 'amd64'
>>> context.bits == 32
True

Setting the architecture can override the defaults for both endian and bits

>>> context.clear()
>>> context.arch = 'powerpc64'
>>> vars(context) == {'arch': 'powerpc64', 'bits': 64, 'endian': 'big'}
True
ContextType.architectures = OrderedDict([('powerpc64', {'bits': 64, 'endian': 'big'}), ('aarch64', {'bits': 64, 'endian': 'little'}), ('sparc64', {'bits': 64, 'endian': 'big'}), ('powerpc', {'bits': 32, 'endian': 'big'}), ('mips64', {'bits': 64, 'endian': 'little'}), ('msp430', {'bits': 16, 'endian': 'little'}), ('thumb', {'bits': 32, 'endian': 'little'}), ('amd64', {'bits': 64, 'endian': 'little'}), ('sparc', {'bits': 32, 'endian': 'big'}), ('alpha', {'bits': 64, 'endian': 'little'}), ('s390', {'bits': 32, 'endian': 'big'}), ('i386', {'bits': 32, 'endian': 'little'}), ('m68k', {'bits': 32, 'endian': 'big'}), ('mips', {'bits': 32, 'endian': 'little'}), ('ia64', {'bits': 64, 'endian': 'big'}), ('cris', {'bits': 32, 'endian': 'little'}), ('vax', {'bits': 32, 'endian': 'little'}), ('avr', {'bits': 8, 'endian': 'little'}), ('arm', {'bits': 32, 'endian': 'little'})])[source]

Keys are valid values for pwnlib.context.ContextType.arch(). Values are defaults which are set when pwnlib.context.ContextType.arch is set

ContextType.aslr[source]

ASLR settings for new processes.

If False, attempt to disable ASLR in all processes which are created via personality (setarch -R) and setrlimit (ulimit -s unlimited).

The setarch changes are lost if a setuid binary is executed.

ContextType.binary[source]

Infer target architecture, bit-with, and endianness from a binary file. Data type is a pwnlib.elf.ELF object.

Examples

>>> context.clear()
>>> context.arch, context.bits
('i386', 32)
>>> context.binary = '/bin/bash'
>>> context.arch, context.bits
('amd64', 64)
>>> context.binary
ELF('/bin/bash')
ContextType.bits[source]

Target machine word size, in bits (i.e. the size of general purpose registers).

The default value is 32, but changes according to arch.

Examples

>>> context.clear()
>>> context.bits == 32
True
>>> context.bits = 64
>>> context.bits == 64
True
>>> context.bits = -1 
Traceback (most recent call last):
...
AttributeError: bits must be > 0 (-1)
ContextType.buffer_size[source]

Internal buffer size to use for pwnlib.tubes.tube.tube objects.

This is not the maximum size of the buffer, but this is the amount of data which is passed to each raw read syscall (or equivalent).

ContextType.bytes[source]

Target machine word size, in bytes (i.e. the size of general purpose registers).

This is a convenience wrapper around bits / 8.

Examples

>>> context.bytes = 1
>>> context.bits == 8
True
>>> context.bytes = 0 
Traceback (most recent call last):
...
AttributeError: bits must be > 0 (0)
ContextType.cache_dir[source]

Directory used for caching data.

Note

May be either a path string, or None.

Example

>>> cache_dir = context.cache_dir
>>> cache_dir is not None
True
>>> os.chmod(cache_dir, 0o000)
>>> context.cache_dir is None
True
>>> os.chmod(cache_dir, 0o755)
>>> cache_dir == context.cache_dir
True
ContextType.defaults = {'kernel': None, 'noptrace': False, 'delete_corefiles': False, 'randomize': False, 'binary': None, 'log_level': 20, 'terminal': (), 'bits': 32, 'rename_corefiles': True, 'newline': '\n', 'proxy': None, 'device': None, 'buffer_size': 4096, 'arch': 'i386', 'aslr': True, 'adb_port': 5037, 'signed': False, 'adb_host': 'localhost', 'timeout': pwnlib.timeout.maximum, 'endian': 'little', 'log_file': <pwnlib.context._devnull object>, 'os': 'linux', 'log_console': <open file '<stdout>', mode 'w'>}[source]

Default values for pwnlib.context.ContextType

ContextType.delete_corefiles[source]

Whether pwntools automatically deletes corefiles after exiting. This only affects corefiles accessed via process.corefile.

Default value is False.

ContextType.device[source]

Sets the device being operated on.

ContextType.endian[source]

Endianness of the target machine.

The default value is 'little', but changes according to arch.

Raises:AttributeError – An invalid endianness was provided

Examples

>>> context.clear()
>>> context.endian == 'little'
True
>>> context.endian = 'big'
>>> context.endian
'big'
>>> context.endian = 'be'
>>> context.endian == 'big'
True
>>> context.endian = 'foobar' 
Traceback (most recent call last):
 ...
AttributeError: endian must be one of ['be', 'big', 'eb', 'el', 'le', 'little']
ContextType.endianness[source]

Legacy alias for endian.

Examples

>>> context.endian == context.endianness
True
ContextType.endiannesses = OrderedDict([('little', 'little'), ('big', 'big'), ('el', 'little'), ('le', 'little'), ('be', 'big'), ('eb', 'big')])[source]

Valid values for endian

ContextType.kernel[source]

Target machine’s kernel architecture.

Usually, this is the same as arch, except when running a 32-bit binary on a 64-bit kernel (e.g. i386-on-amd64).

Even then, this doesn’t matter much – only when the the segment registers need to be known

ContextType.log_console[source]

Sets the default logging console target.

Examples

>>> context.log_level = 'warn'
>>> log.warn("Hello")
[!] Hello
>>> context.log_console=open('/dev/null', 'w')
>>> log.warn("Hello")
>>> context.clear()
ContextType.log_file[source]

Sets the target file for all logging output.

Works in a similar fashion to log_level.

Examples

>>> context.log_file = 'foo.txt' 
>>> log.debug('Hello!') 
>>> with context.local(log_level='ERROR'): 
...     log.info('Hello again!')
>>> with context.local(log_file='bar.txt'):
...     log.debug('Hello from bar!')
>>> log.info('Hello from foo!')
>>> file('foo.txt').readlines()[-3] 
'...:DEBUG:...:Hello!\n'
>>> file('foo.txt').readlines()[-2] 
'...:INFO:...:Hello again!\n'
>>> file('foo.txt').readlines()[-1] 
'...:INFO:...:Hello from foo!\n'
>>> file('bar.txt').readlines()[-1] 
'...:DEBUG:...:Hello from bar!\n'
ContextType.log_level[source]

Sets the verbosity of pwntools logging mechanism.

More specifically it controls the filtering of messages that happens inside the handler for logging to the screen. So if you want e.g. log all messages to a file, then this attribute makes no difference to you.

Valid values are specified by the standard Python logging module.

Default value is set to INFO.

Examples

>>> context.log_level = 'error'
>>> context.log_level == logging.ERROR
True
>>> context.log_level = 10
>>> context.log_level = 'foobar' 
Traceback (most recent call last):
...
AttributeError: log_level must be an integer or one of ['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
ContextType.noptrace[source]

Disable all actions which rely on ptrace.

This is useful for switching between local exploitation with a debugger, and remote exploitation (without a debugger).

This option can be set with the NOPTRACE command-line argument.

ContextType.os[source]

Operating system of the target machine.

The default value is linux.

Allowed values are listed in pwnlib.context.ContextType.oses.

Examples

>>> context.os = 'linux'
>>> context.os = 'foobar' 
Traceback (most recent call last):
...
AttributeError: os must be one of ['android', 'cgc', 'freebsd', 'linux', 'windows']
ContextType.oses = ['android', 'cgc', 'freebsd', 'linux', 'windows'][source]

Valid values for pwnlib.context.ContextType.os()

ContextType.proxy[source]

Default proxy for all socket connections.

Accepts either a string (hostname or IP address) for a SOCKS5 proxy on the default port, or a tuple passed to socks.set_default_proxy, e.g. (socks.SOCKS4, 'localhost', 1234).

>>> context.proxy = 'localhost' 
>>> r=remote('google.com', 80)
Traceback (most recent call last):
...
ProxyConnectionError: Error connecting to SOCKS5 proxy localhost:1080: [Errno 111] Connection refused
>>> context.proxy = None
>>> r=remote('google.com', 80, level='error')
ContextType.quiet[source]

Disables all non-error logging within the enclosed scope, unless the debugging level is set to ‘debug’ or lower.

ContextType.randomize[source]

Global flag that lots of things should be randomized.

ContextType.rename_corefiles[source]

Whether pwntools automatically renames corefiles.

This is useful for two things:

  • Prevent corefiles from being overwritten, if kernel.core_pattern is something simple like "core".
  • Ensure corefiles are generated, if kernel.core_pattern uses apport, which refuses to overwrite any existing files.

This only affects corefiles accessed via process.corefile.

Default value is True.

ContextType.sign[source]

Alias for signed

ContextType.signed[source]

Signed-ness for packing operation when it’s not explicitly set.

Can be set to any non-string truthy value, or the specific string values 'signed' or 'unsigned' which are converted into True and False correspondingly.

Examples

>>> context.signed
False
>>> context.signed = 1
>>> context.signed
True
>>> context.signed = 'signed'
>>> context.signed
True
>>> context.signed = 'unsigned'
>>> context.signed
False
>>> context.signed = 'foobar' 
Traceback (most recent call last):
...
AttributeError: signed must be one of ['no', 'signed', 'unsigned', 'yes'] or a non-string truthy value
ContextType.signedness[source]

Alias for signed

ContextType.signednesses = {'yes': True, 'unsigned': False, 'signed': True, 'no': False}[source]

Valid string values for signed

ContextType.silent[source]

Disable all non-error logging within the enclosed scope.

ContextType.terminal[source]

Default terminal used by pwnlib.util.misc.run_in_new_terminal(). Can be a string or an iterable of strings. In the latter case the first entry is the terminal and the rest are default arguments.

ContextType.timeout[source]

Default amount of time to wait for a blocking operation before it times out, specified in seconds.

The default value is to have an infinite timeout.

See pwnlib.timeout.Timeout for additional information on valid values.

ContextType.verbose[source]

Enable all logging within the enclosed scope.

ContextType.word_size[source]

Alias for bits

class pwnlib.context.Thread(*args, **kwargs)[source]

Instantiates a context-aware thread, which inherit its context when it is instantiated. The class can be accessed both on the context module as pwnlib.context.Thread and on the context singleton object inside the context module as pwnlib.context.context.Thread.

Threads created by using the native :class`threading`.Thread` will have a clean (default) context.

Regardless of the mechanism used to create any thread, the context is de-coupled from the parent thread, so changes do not cascade to child or parent.

Saves a copy of the context when instantiated (at __init__) and updates the new thread’s context before passing control to the user code via run or target=.

Examples

>>> context.clear()
>>> context.update(arch='arm')
>>> def p():
...     print context.arch
...     context.arch = 'mips'
...     print context.arch
>>> # Note that a normal Thread starts with a clean context
>>> # (i386 is the default architecture)
>>> t = threading.Thread(target=p)
>>> _=(t.start(), t.join())
i386
mips
>>> # Note that the main Thread's context is unchanged
>>> print context.arch
arm
>>> # Note that a context-aware Thread receives a copy of the context
>>> t = pwnlib.context.Thread(target=p)
>>> _=(t.start(), t.join())
arm
mips
>>> # Again, the main thread is unchanged
>>> print context.arch
arm

Implementation Details:

This class implemented by hooking the private function threading.Thread._Thread_bootstrap(), which is called before passing control to threading.Thread.run().

This could be done by overriding run itself, but we would have to ensure that all uses of the class would only ever use the keyword target= for __init__, or that all subclasses invoke super(Subclass.self).set_up_context() or similar.

pwnlib.context.context = ContextType()[source]

Global ContextType object, used to store commonly-used pwntools settings.

In most cases, the context is used to infer default variables values. For example, asm() can take an arch parameter as a keyword argument.

If it is not supplied, the arch specified by context is used instead.

Consider it a shorthand to passing os= and arch= to every single function call.