• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Gallopsled / pwntools / 5190673800

pending completion
5190673800

Pull #2205

github-actions

web-flow
Merge 38675c90e into c72886a9b
Pull Request #2205: Fix stable Python 2 installation from a built wheel

3936 of 6604 branches covered (59.6%)

12074 of 16876 relevant lines covered (71.55%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.32
/pwnlib/context/__init__.py
1
# -*- coding: utf-8 -*-
2
"""
1✔
3
Implements context management so that nested/scoped contexts and threaded
4
contexts work properly and as expected.
5
"""
6
from __future__ import absolute_import
1✔
7
from __future__ import division
1✔
8

9
import atexit
1✔
10
import collections
1✔
11
import errno
1✔
12
import functools
1✔
13
import logging
1✔
14
import os
1✔
15
import os.path
1✔
16
import platform
1✔
17
import shutil
1✔
18
import six
1✔
19
import socket
1✔
20
import string
1✔
21
import sys
1✔
22
import tempfile
1✔
23
import threading
1✔
24
import time
1✔
25

26
import socks
1✔
27

28
from pwnlib.config import register_config
1✔
29
from pwnlib.device import Device
1✔
30
from pwnlib.timeout import Timeout
1✔
31

32
try:
1✔
33
    from collections.abc import Iterable
1✔
34
except ImportError:
×
35
    from collections import Iterable
×
36

37
__all__ = ['context', 'ContextType', 'Thread']
1✔
38

39
_original_socket = socket.socket
1✔
40

41
class _devnull(object):
1✔
42
    name = None
1✔
43
    def write(self, *a, **kw): pass
1!
44
    def read(self, *a, **kw):  return ''
1!
45
    def flush(self, *a, **kw): pass
1✔
46
    def close(self, *a, **kw): pass
1✔
47

48
class _defaultdict(dict):
1✔
49
    """
50
    Dictionary which loads missing keys from another dictionary.
51

52
    This is neccesary because the ``default_factory`` method of
53
    :class:`collections.defaultdict` does not provide the key.
54

55
    Examples:
56

57
        >>> a = {'foo': 'bar'}
58
        >>> b = pwnlib.context._defaultdict(a)
59
        >>> b['foo']
60
        'bar'
61
        >>> 'foo' in b
62
        False
63
        >>> b['foo'] = 'baz'
64
        >>> b['foo']
65
        'baz'
66
        >>> del b['foo']
67
        >>> b['foo']
68
        'bar'
69

70
        >>> a = {'foo': 'bar'}
71
        >>> b = pwnlib.context._defaultdict(a)
72
        >>> b['baz'] #doctest: +ELLIPSIS
73
        Traceback (most recent call last):
74
        ...
75
        KeyError: 'baz'
76
    """
77
    def __init__(self, default=None):
1✔
78
        super(_defaultdict, self).__init__()
1✔
79
        if default is None:
1!
80
            default = {}
×
81

82
        self.default = default
1✔
83

84

85
    def __missing__(self, key):
1✔
86
        return self.default[key]
1✔
87

88
class _DictStack(object):
1✔
89
    """
90
    Manages a dictionary-like object, permitting saving and restoring from
91
    a stack of states via :func:`push` and :func:`pop`.
92

93
    The underlying object used as ``default`` must implement ``copy``, ``clear``,
94
    and ``update``.
95

96
    Examples:
97

98
        >>> t = pwnlib.context._DictStack(default={})
99
        >>> t['key'] = 'value'
100
        >>> t
101
        {'key': 'value'}
102
        >>> t.push()
103
        >>> t
104
        {'key': 'value'}
105
        >>> t['key'] = 'value2'
106
        >>> t
107
        {'key': 'value2'}
108
        >>> t.pop()
109
        >>> t
110
        {'key': 'value'}
111
    """
112
    def __init__(self, default):
1✔
113
        self._current = _defaultdict(default)
1✔
114
        self.__stack  = []
1✔
115

116
    def push(self):
1✔
117
        self.__stack.append(self._current.copy())
1✔
118

119
    def pop(self):
1✔
120
        self._current.clear()
1✔
121
        self._current.update(self.__stack.pop())
1✔
122

123
    def copy(self):
1✔
124
        return self._current.copy()
1✔
125

126
    # Pass-through container emulation routines
127
    def __len__(self):              return self._current.__len__()
1!
128
    def __delitem__(self, k):       return self._current.__delitem__(k)
1✔
129
    def __getitem__(self, k):       return self._current.__getitem__(k)
1✔
130
    def __setitem__(self, k, v):    return self._current.__setitem__(k, v)
1✔
131
    def __contains__(self, k):      return self._current.__contains__(k)
1✔
132
    def __iter__(self):             return self._current.__iter__()
1!
133
    def __repr__(self):             return self._current.__repr__()
1!
134
    def __eq__(self, other):        return self._current.__eq__(other)
1!
135

136
    # Required for keyword expansion operator ** to work
137
    def keys(self):                 return self._current.keys()
1!
138
    def values(self):               return self._current.values()
1!
139
    def items(self):                return self._current.items()
1!
140

141

142
class _Tls_DictStack(threading.local, _DictStack):
1✔
143
    """
144
    Per-thread implementation of :class:`_DictStack`.
145

146
    Examples:
147

148
        >>> t = pwnlib.context._Tls_DictStack({})
149
        >>> t['key'] = 'value'
150
        >>> print(t)
151
        {'key': 'value'}
152
        >>> def p(): print(t)
153
        >>> thread = threading.Thread(target=p)
154
        >>> _ = (thread.start(), thread.join())
155
        {}
156
    """
157
    pass
1✔
158

159

160
def _validator(validator):
1✔
161
    """
162
    Validator that is tightly coupled to the implementation
163
    of the classes here.
164

165
    This expects that the object has a ._tls property which
166
    is of type _DictStack.
167
    """
168

169
    name = validator.__name__
1✔
170
    doc  = validator.__doc__
1✔
171

172
    def fget(self):
1✔
173
        return self._tls[name]
1✔
174

175
    def fset(self, val):
1✔
176
        self._tls[name] = validator(self, val)
1✔
177

178
    def fdel(self):
1✔
179
        self._tls._current.pop(name,None)
×
180

181
    return property(fget, fset, fdel, doc)
1✔
182

183
class Thread(threading.Thread):
1✔
184
    """
185
    Instantiates a context-aware thread, which inherit its context when it is
186
    instantiated. The class can be accessed both on the context module as
187
    `pwnlib.context.Thread` and on the context singleton object inside the
188
    context module as `pwnlib.context.context.Thread`.
189

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

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

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

201
    Examples:
202

203
        >>> context.clear()
204
        >>> context.update(arch='arm')
205
        >>> def p():
206
        ...     print(context.arch)
207
        ...     context.arch = 'mips'
208
        ...     print(context.arch)
209
        >>> # Note that a normal Thread starts with a clean context
210
        >>> # (i386 is the default architecture)
211
        >>> t = threading.Thread(target=p)
212
        >>> _=(t.start(), t.join())
213
        i386
214
        mips
215
        >>> # Note that the main Thread's context is unchanged
216
        >>> print(context.arch)
217
        arm
218
        >>> # Note that a context-aware Thread receives a copy of the context
219
        >>> t = pwnlib.context.Thread(target=p)
220
        >>> _=(t.start(), t.join())
221
        arm
222
        mips
223
        >>> # Again, the main thread is unchanged
224
        >>> print(context.arch)
225
        arm
226

227
    Implementation Details:
228

229
        This class implemented by hooking the private function
230
        :func:`threading.Thread._Thread_bootstrap`, which is called before
231
        passing control to :func:`threading.Thread.run`.
232

233
        This could be done by overriding ``run`` itself, but we would have to
234
        ensure that all uses of the class would only ever use the keyword
235
        ``target=`` for ``__init__``, or that all subclasses invoke
236
        ``super(Subclass.self).set_up_context()`` or similar.
237
    """
238
    def __init__(self, *args, **kwargs):
1✔
239
        super(Thread, self).__init__(*args, **kwargs)
1✔
240
        self.old = context.copy()
1✔
241

242
    def __bootstrap(self):
1✔
243
        """
244
        Implementation Details:
245
            This only works because the class is named ``Thread``.
246
            If its name is changed, we have to implement this hook
247
            differently.
248
        """
249
        context.update(**self.old)
×
250
        sup = super(Thread, self)
×
251
        bootstrap = getattr(sup, '_bootstrap', None)
×
252
        if bootstrap is None:
×
253
            sup.__bootstrap()
×
254
        else:
255
            bootstrap()
×
256
    _bootstrap = __bootstrap
1✔
257

258
def _longest(d):
1✔
259
    """
260
    Returns an OrderedDict with the contents of the input dictionary ``d``
261
    sorted by the length of the keys, in descending order.
262

263
    This is useful for performing substring matching via ``str.startswith``,
264
    as it ensures the most complete match will be found.
265

266
    >>> data = {'a': 1, 'bb': 2, 'ccc': 3}
267
    >>> pwnlib.context._longest(data) == data
268
    True
269
    >>> for i in pwnlib.context._longest(data):
270
    ...     print(i)
271
    ccc
272
    bb
273
    a
274
    """
275
    return collections.OrderedDict((k,d[k]) for k in sorted(d, key=len, reverse=True))
1✔
276

277
class ContextType(object):
1✔
278
    r"""
279
    Class for specifying information about the target machine.
280
    Intended for use as a pseudo-singleton through the global
281
    variable :data:`.context`, available via
282
    ``from pwn import *`` as ``context``.
283

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

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

289
    Currently supported properties and their defaults are listed below.
290
    The defaults are inherited from :data:`pwnlib.context.ContextType.defaults`.
291

292
    Additionally, the context is thread-aware when using
293
    :class:`pwnlib.context.Thread` instead of :class:`threading.Thread`
294
    (all internal ``pwntools`` threads use the former).
295

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

298
    Examples:
299

300
        >>> context.clear()
301
        >>> context.update(os='linux') # doctest: +ELLIPSIS
302
        >>> context.os == 'linux'
303
        True
304
        >>> context.arch = 'arm'
305
        >>> vars(context) == {'arch': 'arm', 'bits': 32, 'endian': 'little', 'os': 'linux'}
306
        True
307
        >>> context.endian
308
        'little'
309
        >>> context.bits
310
        32
311
        >>> def nop():
312
        ...   print(enhex(pwnlib.asm.asm('nop')))
313
        >>> nop()
314
        00f020e3
315
        >>> with context.local(arch = 'i386'):
316
        ...   nop()
317
        90
318
        >>> from pwnlib.context import Thread as PwnThread
319
        >>> from threading      import Thread as NormalThread
320
        >>> with context.local(arch = 'mips'):
321
        ...     pwnthread = PwnThread(target=nop)
322
        ...     thread    = NormalThread(target=nop)
323
        >>> # Normal thread uses the default value for arch, 'i386'
324
        >>> _=(thread.start(), thread.join())
325
        90
326
        >>> # Pwnthread uses the correct context from creation-time
327
        >>> _=(pwnthread.start(), pwnthread.join())
328
        00000000
329
        >>> nop()
330
        00f020e3
331
    """
332

333
    #
334
    # Use of 'slots' is a heavy-handed way to prevent accidents
335
    # like 'context.architecture=' instead of 'context.arch='.
336
    #
337
    # Setting any properties on a ContextType object will throw an
338
    # exception.
339
    #
340
    __slots__ = '_tls',
1✔
341

342
    #: Default values for :class:`pwnlib.context.ContextType`
343
    defaults = {
1✔
344
        'adb_host': 'localhost',
345
        'adb_port': 5037,
346
        'arch': 'i386',
347
        'aslr': True,
348
        'binary': None,
349
        'bits': 32,
350
        'buffer_size': 4096,
351
        'cache_dir_base': os.environ.get(
352
            'XDG_CACHE_HOME',
353
            os.path.join(os.path.expanduser('~'), '.cache')
354
        ),
355
        'cyclic_alphabet': string.ascii_lowercase.encode(),
356
        'cyclic_size': 4,
357
        'delete_corefiles': False,
358
        'device': os.getenv('ANDROID_SERIAL', None) or None,
359
        'encoding': 'auto',
360
        'endian': 'little',
361
        'gdbinit': "",
362
        'kernel': None,
363
        'log_level': logging.INFO,
364
        'log_file': _devnull(),
365
        'log_console': sys.stdout,
366
        'randomize': False,
367
        'rename_corefiles': True,
368
        'newline': b'\n',
369
        'noptrace': False,
370
        'os': 'linux',
371
        'proxy': None,
372
        'ssh_session': None,
373
        'signed': False,
374
        'terminal': tuple(),
375
        'timeout': Timeout.maximum,
376
    }
377

378
    #: Valid values for :meth:`pwnlib.context.ContextType.os`
379
    oses = sorted(('linux','freebsd','windows','cgc','android','baremetal'))
1✔
380

381
    big_32    = {'endian': 'big', 'bits': 32}
1✔
382
    big_64    = {'endian': 'big', 'bits': 64}
1✔
383
    little_8  = {'endian': 'little', 'bits': 8}
1✔
384
    little_16 = {'endian': 'little', 'bits': 16}
1✔
385
    little_32 = {'endian': 'little', 'bits': 32}
1✔
386
    little_64 = {'endian': 'little', 'bits': 64}
1✔
387

388
    #: Keys are valid values for :meth:`pwnlib.context.ContextType.arch`.
389
    #
390
    #: Values are defaults which are set when
391
    #: :attr:`pwnlib.context.ContextType.arch` is set
392
    architectures = _longest({
1✔
393
        'aarch64':   little_64,
394
        'alpha':     little_64,
395
        'avr':       little_8,
396
        'amd64':     little_64,
397
        'arm':       little_32,
398
        'cris':      little_32,
399
        'i386':      little_32,
400
        'ia64':      big_64,
401
        'm68k':      big_32,
402
        'mips':      little_32,
403
        'mips64':    little_64,
404
        'msp430':    little_16,
405
        'powerpc':   big_32,
406
        'powerpc64': big_64,
407
        'riscv':     little_32,
408
        's390':      big_32,
409
        'sparc':     big_32,
410
        'sparc64':   big_64,
411
        'thumb':     little_32,
412
        'vax':       little_32,
413
        'none':      {},
414
    })
415

416
    #: Valid values for :attr:`endian`
417
    endiannesses = _longest({
1✔
418
        'be':     'big',
419
        'eb':     'big',
420
        'big':    'big',
421
        'le':     'little',
422
        'el':     'little',
423
        'little': 'little'
424
    })
425

426
    #: Valid string values for :attr:`signed`
427
    signednesses = {
1✔
428
        'unsigned': False,
429
        'no':       False,
430
        'yes':      True,
431
        'signed':   True
432
    }
433

434
    valid_signed = sorted(signednesses)
1✔
435

436
    def __init__(self, **kwargs):
1✔
437
        """
438
        Initialize the ContextType structure.
439

440
        All keyword arguments are passed to :func:`update`.
441
        """
442
        self._tls = _Tls_DictStack(_defaultdict(self.defaults))
1✔
443
        self.update(**kwargs)
1✔
444

445

446
    def copy(self):
1✔
447
        """copy() -> dict
448
        Returns a copy of the current context as a dictionary.
449

450
        Examples:
451

452
            >>> context.clear()
453
            >>> context.os   = 'linux'
454
            >>> vars(context) == {'os': 'linux'}
455
            True
456
        """
457
        return self._tls.copy()
1✔
458

459

460
    @property
1✔
461
    def __dict__(self):
1✔
462
        return self.copy()
1✔
463

464
    def update(self, *args, **kwargs):
1✔
465
        """
466
        Convenience function, which is shorthand for setting multiple
467
        variables at once.
468

469
        It is a simple shorthand such that::
470

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

473
        is equivalent to::
474

475
            context.os   = 'linux'
476
            context.arch = 'arm'
477
            ...
478

479
        The following syntax is also valid::
480

481
            context.update({'os': 'linux', 'arch': 'arm'})
482

483
        Arguments:
484
          kwargs: Variables to be assigned in the environment.
485

486
        Examples:
487

488
            >>> context.clear()
489
            >>> context.update(arch = 'i386', os = 'linux')
490
            >>> context.arch, context.os
491
            ('i386', 'linux')
492
        """
493
        for arg in args:
1!
494
            self.update(**arg)
×
495

496
        for k,v in kwargs.items():
1✔
497
            setattr(self,k,v)
1✔
498

499
    def __repr__(self):
1✔
500
        v = sorted("%s = %r" % (k,v) for k,v in self._tls._current.items())
1✔
501
        return '%s(%s)' % (self.__class__.__name__, ', '.join(v))
1✔
502

503
    def local(self, function=None, **kwargs):
1✔
504
        """local(**kwargs) -> context manager
505

506
        Create a context manager for use with the ``with`` statement.
507

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

510
        Arguments:
511
          kwargs: Variables to be assigned in the new environment.
512

513
        Returns:
514
          ContextType manager for managing the old and new environment.
515

516
        Examples:
517

518
            >>> context.clear()
519
            >>> context.timeout = 1
520
            >>> context.timeout == 1
521
            True
522
            >>> print(context.timeout)
523
            1.0
524
            >>> with context.local(timeout = 2):
525
            ...     print(context.timeout)
526
            ...     context.timeout = 3
527
            ...     print(context.timeout)
528
            2.0
529
            3.0
530
            >>> print(context.timeout)
531
            1.0
532
        """
533
        class LocalContext(object):
1✔
534
            def __enter__(a):
1✔
535
                self._tls.push()
1✔
536
                self.update(**{k:v for k,v in kwargs.items() if v is not None})
1✔
537
                return self
1✔
538

539
            def __exit__(a, *b, **c):
1✔
540
                self._tls.pop()
1✔
541

542
            def __call__(self, function, *a, **kw):
1✔
543
                @functools.wraps(function)
1✔
544
                def inner(*a, **kw):
1✔
545
                    with self:
1✔
546
                        return function(*a, **kw)
1✔
547
                return inner
1✔
548

549
        return LocalContext()
1✔
550

551
    @property
1✔
552
    def silent(self, function=None):
1✔
553
        """Disable all non-error logging within the enclosed scope.
554
        """
555
        return self.local(function, log_level='error')
1✔
556

557
    @property
1✔
558
    def quiet(self, function=None):
1✔
559
        """Disables all non-error logging within the enclosed scope,
560
        *unless* the debugging level is set to 'debug' or lower.
561

562
        Example:
563

564
            Let's assume the normal situation, where log_level is INFO.
565

566
            >>> context.clear(log_level='info')
567

568
            Note that only the log levels below ERROR do not print anything.
569

570
            >>> with context.quiet:
571
            ...     log.debug("DEBUG")
572
            ...     log.info("INFO")
573
            ...     log.warn("WARN")
574

575
            Next let's try with the debugging level set to 'debug' before we
576
            enter the context handler:
577

578
            >>> with context.local(log_level='debug'):
579
            ...     with context.quiet:
580
            ...         log.debug("DEBUG")
581
            ...         log.info("INFO")
582
            ...         log.warn("WARN")
583
            [DEBUG] DEBUG
584
            [*] INFO
585
            [!] WARN
586
        """
587
        level = 'error'
1✔
588
        if context.log_level <= logging.DEBUG:
1✔
589
            level = None
1✔
590
        return self.local(function, log_level=level)
1✔
591

592
    def quietfunc(self, function):
1✔
593
        """Similar to :attr:`quiet`, but wraps a whole function.
594

595
        Example:
596

597
            Let's set up two functions, which are the same but one is
598
            wrapped with :attr:`quietfunc`.
599

600
            >>> def loud(): log.info("Loud")
601
            >>> @context.quietfunc
602
            ... def quiet(): log.info("Quiet")
603

604
            If we set the logging level to 'info', the loud function
605
            prints its contents.
606

607
            >>> with context.local(log_level='info'): loud()
608
            [*] Loud
609

610
            However, the quiet function does not, since :attr:`quietfunc`
611
            silences all output unless the log level is DEBUG.
612

613
            >>> with context.local(log_level='info'): quiet()
614

615
            Now let's try again with debugging enabled.
616

617
            >>> with context.local(log_level='debug'): quiet()
618
            [*] Quiet
619
        """
620
        @functools.wraps(function)
1✔
621
        def wrapper(*a, **kw):
1✔
622
            level = 'error'
1✔
623
            if context.log_level <= logging.DEBUG:
1✔
624
                level = None
1✔
625
            with self.local(function, log_level=level):
1✔
626
                return function(*a, **kw)
1✔
627
        return wrapper
1✔
628

629

630
    @property
1✔
631
    def verbose(self):
1✔
632
        """Enable all logging within the enclosed scope.
633

634
        This is the opposite of :attr:`.quiet` and functionally equivalent to:
635

636
        .. code-block:: python
637

638
            with context.local(log_level='debug'):
639
                ...
640

641
        Example:
642

643
            Note that the function does not emit any information by default
644

645
            >>> context.clear()
646
            >>> def func(): log.debug("Hello")
647
            >>> func()
648

649
            But if we put it inside a :attr:`.verbose` context manager, the
650
            information is printed.
651

652
            >>> with context.verbose: func()
653
            [DEBUG] Hello
654

655
        """
656
        return self.local(log_level='debug')
1✔
657

658
    def clear(self, *a, **kw):
1✔
659
        """
660
        Clears the contents of the context.
661
        All values are set to their defaults.
662

663
        Arguments:
664

665
            a: Arguments passed to ``update``
666
            kw: Arguments passed to ``update``
667

668
        Examples:
669

670
            >>> # Default value
671
            >>> context.clear()
672
            >>> context.arch == 'i386'
673
            True
674
            >>> context.arch = 'arm'
675
            >>> context.arch == 'i386'
676
            False
677
            >>> context.clear()
678
            >>> context.arch == 'i386'
679
            True
680
        """
681
        self._tls._current.clear()
1✔
682

683
        if a or kw:
1✔
684
            self.update(*a, **kw)
1✔
685

686
    @property
1✔
687
    def native(self):
1✔
688
        if context.os in ('android', 'baremetal', 'cgc'):
1!
689
            return False
×
690

691
        arch = context.arch
1✔
692
        with context.local(arch = platform.machine()):
1✔
693
            platform_arch = context.arch
1✔
694

695
            if arch in ('i386', 'amd64') and platform_arch in ('i386', 'amd64'):
1✔
696
                return True
1✔
697

698
            return arch == platform_arch
1✔
699

700
    @_validator
1✔
701
    def arch(self, arch):
1✔
702
        """
703
        Target binary architecture.
704

705
        Allowed values are listed in :attr:`pwnlib.context.ContextType.architectures`.
706

707
        Side Effects:
708

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

714
            The following properties may be modified.
715

716
            - :attr:`bits`
717
            - :attr:`endian`
718

719
        Raises:
720
            AttributeError: An invalid architecture was specified
721

722
        Examples:
723

724
            >>> context.clear()
725
            >>> context.arch == 'i386' # Default architecture
726
            True
727

728
            >>> context.arch = 'mips'
729
            >>> context.arch == 'mips'
730
            True
731

732
            >>> context.arch = 'doge' #doctest: +ELLIPSIS
733
            Traceback (most recent call last):
734
             ...
735
            AttributeError: arch must be one of ['aarch64', ..., 'thumb']
736

737
            >>> context.arch = 'ppc'
738
            >>> context.arch == 'powerpc' # Aliased architecture
739
            True
740

741
            >>> context.clear()
742
            >>> context.bits == 32 # Default value
743
            True
744
            >>> context.arch = 'amd64'
745
            >>> context.bits == 64 # New value
746
            True
747

748
            Note that expressly setting :attr:`bits` means that we use
749
            that value instead of the default
750

751
            >>> context.clear()
752
            >>> context.bits = 32
753
            >>> context.arch = 'amd64'
754
            >>> context.bits == 32
755
            True
756

757
            Setting the architecture can override the defaults for
758
            both :attr:`endian` and :attr:`bits`
759

760
            >>> context.clear()
761
            >>> context.arch = 'powerpc64'
762
            >>> vars(context) == {'arch': 'powerpc64', 'bits': 64, 'endian': 'big'}
763
            True
764
        """
765
        # Lowercase
766
        arch = arch.lower()
1✔
767

768
        # Attempt to perform convenience and legacy compatibility transformations.
769
        # We have to make sure that x86_64 appears before x86 for this to work correctly.
770
        transform = [('ppc64', 'powerpc64'),
1✔
771
                     ('ppc', 'powerpc'),
772
                     ('x86-64', 'amd64'),
773
                     ('x86_64', 'amd64'),
774
                     ('x86', 'i386'),
775
                     ('i686', 'i386'),
776
                     ('armv7l', 'arm'),
777
                     ('armeabi', 'arm'),
778
                     ('arm64', 'aarch64')]
779
        for k, v in transform:
1✔
780
            if arch.startswith(k):
1✔
781
                arch = v
1✔
782
                break
1✔
783

784
        try:
1✔
785
            defaults = self.architectures[arch]
1✔
786
        except KeyError:
1✔
787
            raise AttributeError('AttributeError: arch must be one of %r' % sorted(self.architectures))
1✔
788

789
        for k,v in defaults.items():
1✔
790
            if k not in self._tls:
1✔
791
                self._tls[k] = v
1✔
792

793
        return arch
1✔
794

795
    @_validator
1✔
796
    def aslr(self, aslr):
1✔
797
        """
798
        ASLR settings for new processes.
799

800
        If :const:`False`, attempt to disable ASLR in all processes which are
801
        created via ``personality`` (``setarch -R``) and ``setrlimit``
802
        (``ulimit -s unlimited``).
803

804
        The ``setarch`` changes are lost if a ``setuid`` binary is executed.
805
        """
806
        return bool(aslr)
1✔
807

808
    @_validator
1✔
809
    def kernel(self, arch):
1✔
810
        """
811
        Target machine's kernel architecture.
812

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

816
        Even then, this doesn't matter much -- only when the the segment
817
        registers need to be known
818
        """
819
        with self.local(arch=arch):
1✔
820
            return self.arch
1✔
821

822
    @_validator
1✔
823
    def bits(self, bits):
1✔
824
        """
825
        Target machine word size, in bits (i.e. the size of general purpose registers).
826

827
        The default value is ``32``, but changes according to :attr:`arch`.
828

829
        Examples:
830
            >>> context.clear()
831
            >>> context.bits == 32
832
            True
833
            >>> context.bits = 64
834
            >>> context.bits == 64
835
            True
836
            >>> context.bits = -1 #doctest: +ELLIPSIS
837
            Traceback (most recent call last):
838
            ...
839
            AttributeError: bits must be > 0 (-1)
840
        """
841
        bits = int(bits)
1✔
842

843
        if bits <= 0:
1✔
844
            raise AttributeError("bits must be > 0 (%r)" % bits)
1✔
845

846
        return bits
1✔
847

848
    @_validator
1✔
849
    def binary(self, binary):
1✔
850
        """
851
        Infer target architecture, bit-with, and endianness from a binary file.
852
        Data type is a :class:`pwnlib.elf.ELF` object.
853

854
        Examples:
855

856
            >>> context.clear()
857
            >>> context.arch, context.bits
858
            ('i386', 32)
859
            >>> context.binary = '/bin/bash'
860
            >>> context.arch, context.bits
861
            ('amd64', 64)
862
            >>> context.binary
863
            ELF('/bin/bash')
864

865
        """
866
        # Cyclic imports... sorry Idolf.
867
        from pwnlib.elf     import ELF
1✔
868

869
        if not isinstance(binary, ELF):
1✔
870
            binary = ELF(binary)
1✔
871

872
        self.arch   = binary.arch
1✔
873
        self.bits   = binary.bits
1✔
874
        self.endian = binary.endian
1✔
875
        self.os     = binary.os
1✔
876

877
        return binary
1✔
878

879
    @property
1✔
880
    def bytes(self):
1✔
881
        """
882
        Target machine word size, in bytes (i.e. the size of general purpose registers).
883

884
        This is a convenience wrapper around ``bits // 8``.
885

886
        Examples:
887

888
            >>> context.bytes = 1
889
            >>> context.bits == 8
890
            True
891

892
            >>> context.bytes = 0 #doctest: +ELLIPSIS
893
            Traceback (most recent call last):
894
            ...
895
            AttributeError: bits must be > 0 (0)
896
        """
897
        return self.bits // 8
1✔
898
    @bytes.setter
1✔
899
    def bytes(self, value):
1✔
900
        self.bits = value*8
1✔
901

902
    @_validator
1✔
903
    def encoding(self, charset):
1✔
904
        if charset == 'auto':
×
905
            return charset
×
906

907
        if (  b'aA'.decode(charset) != 'aA'
×
908
            or 'aA'.encode(charset) != b'aA'):
909
            raise ValueError('Strange encoding!')
×
910

911
        return charset
×
912

913
    @_validator
1✔
914
    def endian(self, endianness):
1✔
915
        """
916
        Endianness of the target machine.
917

918
        The default value is ``'little'``, but changes according to :attr:`arch`.
919

920
        Raises:
921
            AttributeError: An invalid endianness was provided
922

923
        Examples:
924

925
            >>> context.clear()
926
            >>> context.endian == 'little'
927
            True
928

929
            >>> context.endian = 'big'
930
            >>> context.endian
931
            'big'
932

933
            >>> context.endian = 'be'
934
            >>> context.endian == 'big'
935
            True
936

937
            >>> context.endian = 'foobar' #doctest: +ELLIPSIS
938
            Traceback (most recent call last):
939
             ...
940
            AttributeError: endian must be one of ['be', 'big', 'eb', 'el', 'le', 'little']
941
        """
942
        endian = endianness.lower()
1✔
943

944
        if endian not in self.endiannesses:
1✔
945
            raise AttributeError("endian must be one of %r" % sorted(self.endiannesses))
1✔
946

947
        return self.endiannesses[endian]
1✔
948

949

950
    @_validator
1✔
951
    def log_level(self, value):
1✔
952
        """
953
        Sets the verbosity of ``pwntools`` logging mechanism.
954

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

959
        Valid values are specified by the standard Python ``logging`` module.
960

961
        Default value is set to ``INFO``.
962

963
        Examples:
964

965
            >>> context.log_level = 'error'
966
            >>> context.log_level == logging.ERROR
967
            True
968
            >>> context.log_level = 10
969
            >>> context.log_level = 'foobar' #doctest: +ELLIPSIS
970
            Traceback (most recent call last):
971
            ...
972
            AttributeError: log_level must be an integer or one of ['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
973
        """
974
        # If it can be converted into an int, success
975
        try:                    return int(value)
1✔
976
        except ValueError:  pass
1✔
977

978
        # If it is defined in the logging module, success
979
        try:                    return getattr(logging, value.upper())
1✔
980
        except AttributeError:  pass
1✔
981

982
        # Otherwise, fail
983
        try:
1✔
984
            level_names = logging._levelToName.values()
1✔
985
        except AttributeError:
×
986
            level_names = filter(lambda x: isinstance(x,str), logging._levelNames)
×
987
        permitted = sorted(level_names)
1✔
988
        raise AttributeError('log_level must be an integer or one of %r' % permitted)
1✔
989

990
    @_validator
1✔
991
    def log_file(self, value):
1✔
992
        r"""
993
        Sets the target file for all logging output.
994

995
        Works in a similar fashion to :attr:`log_level`.
996

997
        Examples:
998

999

1000
            >>> foo_txt = tempfile.mktemp()
1001
            >>> bar_txt = tempfile.mktemp()
1002
            >>> context.log_file = foo_txt
1003
            >>> log.debug('Hello!')
1004
            >>> with context.local(log_level='ERROR'): #doctest: +ELLIPSIS
1005
            ...     log.info('Hello again!')
1006
            >>> with context.local(log_file=bar_txt):
1007
            ...     log.debug('Hello from bar!')
1008
            >>> log.info('Hello from foo!')
1009
            >>> open(foo_txt).readlines()[-3] #doctest: +ELLIPSIS
1010
            '...:DEBUG:...:Hello!\n'
1011
            >>> open(foo_txt).readlines()[-2] #doctest: +ELLIPSIS
1012
            '...:INFO:...:Hello again!\n'
1013
            >>> open(foo_txt).readlines()[-1] #doctest: +ELLIPSIS
1014
            '...:INFO:...:Hello from foo!\n'
1015
            >>> open(bar_txt).readlines()[-1] #doctest: +ELLIPSIS
1016
            '...:DEBUG:...:Hello from bar!\n'
1017
        """
1018
        if isinstance(value, (bytes, six.text_type)):
1!
1019
            # check if mode was specified as "[value],[mode]"
1020
            if ',' not in value:
1!
1021
                value += ',a'
1✔
1022
            filename, mode = value.rsplit(',', 1)
1✔
1023
            value = open(filename, mode)
1✔
1024

1025
        elif not hasattr(value, "fileno"):
×
1026
            raise AttributeError('log_file must be a file')
×
1027

1028
        # Is this the same file we already have open?
1029
        # If so, don't re-print the banner.
1030
        if self.log_file and not isinstance(self.log_file, _devnull):
1✔
1031
            a = os.fstat(value.fileno()).st_ino
1✔
1032
            b = os.fstat(self.log_file.fileno()).st_ino
1✔
1033

1034
            if a == b:
1!
1035
                return self.log_file
×
1036

1037
        iso_8601 = '%Y-%m-%dT%H:%M:%S'
1✔
1038
        lines = [
1✔
1039
            '=' * 78,
1040
            ' Started at %s ' % time.strftime(iso_8601),
1041
            ' sys.argv = [',
1042
            ]
1043
        for arg in sys.argv:
1✔
1044
            lines.append('   %r,' % arg)
1✔
1045
        lines.append(' ]')
1✔
1046
        lines.append('=' * 78)
1✔
1047
        for line in lines:
1✔
1048
            value.write('=%-78s=\n' % line)
1✔
1049
        value.flush()
1✔
1050
        return value
1✔
1051

1052
    @_validator
1✔
1053
    def log_console(self, stream):
1✔
1054
        """
1055
        Sets the default logging console target.
1056

1057
        Examples:
1058

1059
            >>> context.log_level = 'warn'
1060
            >>> log.warn("Hello")
1061
            [!] Hello
1062
            >>> context.log_console=open('/dev/null', 'w')
1063
            >>> log.warn("Hello")
1064
            >>> context.clear()
1065
        """
1066
        if isinstance(stream, str):
1!
1067
            stream = open(stream, 'wt')
×
1068
        return stream
1✔
1069

1070
    @property
1✔
1071
    def mask(self):
1✔
1072
        return (1 << self.bits) - 1
1✔
1073

1074
    @_validator
1✔
1075
    def os(self, os):
1✔
1076
        """
1077
        Operating system of the target machine.
1078

1079
        The default value is ``linux``.
1080

1081
        Allowed values are listed in :attr:`pwnlib.context.ContextType.oses`.
1082

1083
        Examples:
1084

1085
            >>> context.os = 'linux'
1086
            >>> context.os = 'foobar' #doctest: +ELLIPSIS
1087
            Traceback (most recent call last):
1088
            ...
1089
            AttributeError: os must be one of ['android', 'baremetal', 'cgc', 'freebsd', 'linux', 'windows']
1090
        """
1091
        os = os.lower()
1✔
1092

1093
        if os not in self.oses:
1✔
1094
            raise AttributeError("os must be one of %r" % self.oses)
1✔
1095

1096
        return os
1✔
1097

1098
    @_validator
1✔
1099
    def randomize(self, r):
1✔
1100
        """
1101
        Global flag that lots of things should be randomized.
1102
        """
1103
        return bool(r)
×
1104

1105
    @_validator
1✔
1106
    def signed(self, signed):
1✔
1107
        """
1108
        Signed-ness for packing operation when it's not explicitly set.
1109

1110
        Can be set to any non-string truthy value, or the specific string
1111
        values ``'signed'`` or ``'unsigned'`` which are converted into
1112
        :const:`True` and :const:`False` correspondingly.
1113

1114
        Examples:
1115

1116
            >>> context.signed
1117
            False
1118
            >>> context.signed = 1
1119
            >>> context.signed
1120
            True
1121
            >>> context.signed = 'signed'
1122
            >>> context.signed
1123
            True
1124
            >>> context.signed = 'unsigned'
1125
            >>> context.signed
1126
            False
1127
            >>> context.signed = 'foobar' #doctest: +ELLIPSIS
1128
            Traceback (most recent call last):
1129
            ...
1130
            AttributeError: signed must be one of ['no', 'signed', 'unsigned', 'yes'] or a non-string truthy value
1131
        """
1132
        try:             signed = self.signednesses[signed]
1✔
1133
        except KeyError: pass
1✔
1134

1135
        if isinstance(signed, str):
1✔
1136
            raise AttributeError('signed must be one of %r or a non-string truthy value' % sorted(self.signednesses))
1✔
1137

1138
        return bool(signed)
1✔
1139

1140
    @_validator
1✔
1141
    def timeout(self, value=Timeout.default):
1✔
1142
        """
1143
        Default amount of time to wait for a blocking operation before it times out,
1144
        specified in seconds.
1145

1146
        The default value is to have an infinite timeout.
1147

1148
        See :class:`pwnlib.timeout.Timeout` for additional information on
1149
        valid values.
1150
        """
1151
        return Timeout(value).timeout
1✔
1152

1153
    @_validator
1✔
1154
    def terminal(self, value):
1✔
1155
        """
1156
        Default terminal used by :meth:`pwnlib.util.misc.run_in_new_terminal`.
1157
        Can be a string or an iterable of strings.  In the latter case the first
1158
        entry is the terminal and the rest are default arguments.
1159
        """
1160
        if isinstance(value, (bytes, six.text_type)):
1!
1161
            return [value]
×
1162
        return value
1✔
1163

1164
    @property
1✔
1165
    def abi(self):
1✔
1166
        return self._abi
×
1167

1168
    @_validator
1✔
1169
    def proxy(self, proxy):
1✔
1170
        """
1171
        Default proxy for all socket connections.
1172

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

1177
        >>> context.proxy = 'localhost' #doctest: +ELLIPSIS
1178
        >>> r=remote('google.com', 80)
1179
        Traceback (most recent call last):
1180
        ...
1181
        ProxyConnectionError: Error connecting to SOCKS5 proxy localhost:1080: [Errno 111] Connection refused
1182

1183
        >>> context.proxy = None
1184
        >>> r=remote('google.com', 80, level='error')
1185
        """
1186

1187
        if not proxy:
1✔
1188
            socket.socket = _original_socket
1✔
1189
            return None
1✔
1190

1191
        if isinstance(proxy, str):
1!
1192
            proxy = (socks.SOCKS5, proxy)
1✔
1193

1194
        if not isinstance(proxy, Iterable):
1!
1195
            raise AttributeError('proxy must be a string hostname, or tuple of arguments for socks.set_default_proxy')
×
1196

1197
        socks.set_default_proxy(*proxy)
1✔
1198
        socket.socket = socks.socksocket
1✔
1199

1200
        return proxy
1✔
1201

1202
    @_validator
1✔
1203
    def noptrace(self, value):
1✔
1204
        """Disable all actions which rely on ptrace.
1205

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

1209
        This option can be set with the ``NOPTRACE`` command-line argument.
1210
        """
1211
        return bool(value)
×
1212

1213

1214
    @_validator
1✔
1215
    def adb_host(self, value):
1✔
1216
        """Sets the target host which is used for ADB.
1217

1218
        This is useful for Android exploitation.
1219

1220
        The default value is inherited from ANDROID_ADB_SERVER_HOST, or set
1221
        to the default 'localhost'.
1222
        """
1223
        return str(value)
×
1224

1225

1226
    @_validator
1✔
1227
    def adb_port(self, value):
1✔
1228
        """Sets the target port which is used for ADB.
1229

1230
        This is useful for Android exploitation.
1231

1232
        The default value is inherited from ANDROID_ADB_SERVER_PORT, or set
1233
        to the default 5037.
1234
        """
1235
        return int(value)
×
1236

1237
    @_validator
1✔
1238
    def device(self, device):
1✔
1239
        """Sets the device being operated on.
1240
        """
1241
        if isinstance(device, (bytes, six.text_type)):
×
1242
            device = Device(device)
×
1243
        if isinstance(device, Device):
×
1244
            self.arch = device.arch or self.arch
×
1245
            self.bits = device.bits or self.bits
×
1246
            self.endian = device.endian or self.endian
×
1247
            self.os = device.os or self.os
×
1248
        elif device is not None:
×
1249
            raise AttributeError("device must be either a Device object or a serial number as a string")
×
1250

1251
        return device
×
1252

1253
    @property
1✔
1254
    def adb(self):
1✔
1255
        """Returns an argument array for connecting to adb.
1256

1257
        Unless ``$ADB_PATH`` is set, uses the default ``adb`` binary in ``$PATH``.
1258
        """
1259
        ADB_PATH = os.environ.get('ADB_PATH', 'adb')
×
1260

1261
        command = [ADB_PATH]
×
1262

1263
        if self.adb_host != self.defaults['adb_host']:
×
1264
            command += ['-H', self.adb_host]
×
1265

1266
        if self.adb_port != self.defaults['adb_port']:
×
1267
            command += ['-P', str(self.adb_port)]
×
1268

1269
        if self.device:
×
1270
            command += ['-s', str(self.device)]
×
1271

1272
        return command
×
1273

1274
    @_validator
1✔
1275
    def buffer_size(self, size):
1✔
1276
        """Internal buffer size to use for :class:`pwnlib.tubes.tube.tube` objects.
1277

1278
        This is not the maximum size of the buffer, but this is the amount of data
1279
        which is passed to each raw ``read`` syscall (or equivalent).
1280
        """
1281
        return int(size)
1✔
1282

1283
    @_validator
1✔
1284
    def cache_dir_base(self, new_base):
1✔
1285
        """Base directory to use for caching content.
1286

1287
        Changing this to a different value will clear the `cache_dir` path
1288
        stored in TLS since a new path will need to be generated to respect the
1289
        new `cache_dir_base` value.
1290
        """
1291

1292
        if new_base != self.cache_dir_base:
×
1293
            del self._tls["cache_dir"]
×
1294
        if os.access(new_base, os.F_OK) and not os.access(new_base, os.W_OK):
×
1295
            raise OSError(errno.EPERM, "Cache base dir is not writable")
×
1296
        return new_base
×
1297

1298
    @property
1✔
1299
    def cache_dir(self):
1✔
1300
        """Directory used for caching data.
1301

1302
        Note:
1303
            May be either a path string, or :const:`None`.
1304

1305
        Example:
1306

1307
            >>> cache_dir = context.cache_dir
1308
            >>> cache_dir is not None
1309
            True
1310
            >>> os.chmod(cache_dir, 0o000)
1311
            >>> del context._tls['cache_dir']
1312
            >>> context.cache_dir is None
1313
            True
1314
            >>> os.chmod(cache_dir, 0o755)
1315
            >>> cache_dir == context.cache_dir
1316
            True
1317
        """
1318
        try:
1✔
1319
            # If the TLS already has a cache directory path, we return it
1320
            # without any futher checks since it must have been valid when it
1321
            # was set and if that has changed, hiding the TOCTOU here would be
1322
            # potentially confusing
1323
            return self._tls["cache_dir"]
1✔
1324
        except KeyError:
1✔
1325
            pass
1✔
1326

1327
        # Attempt to create a Python version specific cache dir and its parents
1328
        cache_dirname = '.pwntools-cache-%d.%d' % sys.version_info[:2]
1✔
1329
        cache_dirpath = os.path.join(self.cache_dir_base, cache_dirname)
1✔
1330
        try:
1✔
1331
            os.makedirs(cache_dirpath)
1✔
1332
        except OSError as exc:
1✔
1333
            # If we failed for any reason other than the cache directory
1334
            # already existing then we'll fall back to a temporary directory
1335
            # object which doesn't respect the `cache_dir_base`
1336
            if exc.errno != errno.EEXIST:
1!
1337
                try:
×
1338
                    cache_dirpath = tempfile.mkdtemp(prefix=".pwntools-tmp")
×
1339
                except IOError:
×
1340
                    # This implies no good candidates for temporary files so we
1341
                    # have to return `None`
1342
                    return None
×
1343
                else:
1344
                    # Ensure the temporary cache dir is cleaned up on exit. A
1345
                    # `TemporaryDirectory` would do this better upon garbage
1346
                    # collection but this is necessary for Python 2 support.
1347
                    atexit.register(shutil.rmtree, cache_dirpath)
×
1348
        # By this time we have a cache directory which exists but we don't know
1349
        # if it is actually writable. Some wargames e.g. pwnable.kr have
1350
        # created dummy directories which cannot be modified by the user
1351
        # account (owned by root).
1352
        if os.access(cache_dirpath, os.W_OK):
1✔
1353
            # Stash this in TLS for later reuse
1354
            self._tls["cache_dir"] = cache_dirpath
1✔
1355
            return cache_dirpath
1✔
1356
        else:
1357
            return None
1✔
1358

1359
    @cache_dir.setter
1✔
1360
    def cache_dir(self, v):
1✔
1361
        if os.access(v, os.W_OK):
×
1362
            # Stash this in TLS for later reuse
1363
            self._tls["cache_dir"] = v
×
1364

1365
    @_validator
1✔
1366
    def delete_corefiles(self, v):
1✔
1367
        """Whether pwntools automatically deletes corefiles after exiting.
1368
        This only affects corefiles accessed via :attr:`.process.corefile`.
1369

1370
        Default value is ``False``.
1371
        """
1372
        return bool(v)
×
1373

1374
    @_validator
1✔
1375
    def rename_corefiles(self, v):
1✔
1376
        """Whether pwntools automatically renames corefiles.
1377

1378
        This is useful for two things:
1379

1380
        - Prevent corefiles from being overwritten, if ``kernel.core_pattern``
1381
          is something simple like ``"core"``.
1382
        - Ensure corefiles are generated, if ``kernel.core_pattern`` uses ``apport``,
1383
          which refuses to overwrite any existing files.
1384

1385
        This only affects corefiles accessed via :attr:`.process.corefile`.
1386

1387
        Default value is ``True``.
1388
        """
1389
        return bool(v)
×
1390

1391
    @_validator
1✔
1392
    def newline(self, v):
1✔
1393
        """Line ending used for Tubes by default.
1394

1395
        This configures the newline emitted by e.g. ``sendline`` or that is used
1396
        as a delimiter for e.g. ``recvline``.
1397
        """
1398
        # circular imports
1399
        from pwnlib.util.packing import _need_bytes
1✔
1400
        return _need_bytes(v)
1✔
1401

1402

1403
    @_validator
1✔
1404
    def gdbinit(self, value):
1✔
1405
        """Path to the gdbinit that is used when running GDB locally.
1406

1407
        This is useful if you want pwntools-launched GDB to include some additional modules,
1408
        like PEDA but you do not want to have GDB include them by default.
1409

1410
        The setting will only apply when GDB is launched locally since remote hosts may not have
1411
        the necessary requirements for the gdbinit.
1412

1413
        If set to an empty string, GDB will use the default `~/.gdbinit`.
1414

1415
        Default value is ``""``.
1416
        """
1417
        return str(value)
×
1418

1419
    @_validator
1✔
1420
    def cyclic_alphabet(self, alphabet):
1✔
1421
        """Cyclic alphabet.
1422

1423
        Default value is `string.ascii_lowercase`.
1424
        """
1425

1426
        # Do not allow multiple occurrences
1427
        if len(set(alphabet)) != len(alphabet):
1!
1428
            raise AttributeError("cyclic alphabet cannot contain duplicates")
×
1429

1430
        return alphabet.encode()
1✔
1431

1432
    @_validator
1✔
1433
    def cyclic_size(self, size):
1✔
1434
        """Cyclic pattern size.
1435

1436
        Default value is `4`.
1437
        """
1438
        size = int(size)
×
1439

1440
        if size > self.bytes:
×
1441
            raise AttributeError("cyclic pattern size cannot be larger than word size")
×
1442

1443
        return size
×
1444

1445
    @_validator
1✔
1446
    def ssh_session(self, shell):
1✔
1447
        from pwnlib.tubes.ssh import ssh
1✔
1448

1449
        if not isinstance(shell, ssh):
1!
1450
            raise AttributeError("context.ssh_session must be an ssh tube") 
×
1451

1452
        return shell
1✔
1453

1454
    #*************************************************************************
1455
    #                               ALIASES
1456
    #*************************************************************************
1457
    #
1458
    # These fields are aliases for fields defined above, either for
1459
    # convenience or compatibility.
1460
    #
1461
    #*************************************************************************
1462

1463
    def __call__(self, **kwargs):
1✔
1464
        """
1465
        Alias for :meth:`pwnlib.context.ContextType.update`
1466
        """
1467
        return self.update(**kwargs)
1✔
1468

1469
    def reset_local(self):
1✔
1470
        """
1471
        Deprecated.  Use :meth:`clear`.
1472
        """
1473
        self.clear()
1✔
1474

1475
    @property
1✔
1476
    def endianness(self):
1✔
1477
        """
1478
        Legacy alias for :attr:`endian`.
1479

1480
        Examples:
1481

1482
            >>> context.endian == context.endianness
1483
            True
1484
        """
1485
        return self.endian
1✔
1486
    @endianness.setter
1✔
1487
    def endianness(self, value):
1✔
1488
        self.endian = value
1✔
1489

1490

1491
    @property
1✔
1492
    def sign(self):
1✔
1493
        """
1494
        Alias for :attr:`signed`
1495
        """
1496
        return self.signed
1✔
1497

1498
    @sign.setter
1✔
1499
    def sign(self, value):
1✔
1500
        self.signed = value
1✔
1501

1502
    @property
1✔
1503
    def signedness(self):
1✔
1504
        """
1505
        Alias for :attr:`signed`
1506
        """
1507
        return self.signed
×
1508

1509
    @signedness.setter
1✔
1510
    def signedness(self, value):
1✔
1511
        self.signed = value
×
1512

1513

1514
    @property
1✔
1515
    def word_size(self):
1✔
1516
        """
1517
        Alias for :attr:`bits`
1518
        """
1519
        return self.bits
1✔
1520

1521
    @word_size.setter
1✔
1522
    def word_size(self, value):
1✔
1523
        self.bits = value
1✔
1524

1525
    Thread = Thread
1✔
1526

1527

1528
#: Global :class:`.ContextType` object, used to store commonly-used pwntools settings.
1529
#:
1530
#: In most cases, the context is used to infer default variables values.
1531
#: For example, :func:`.asm` can take an ``arch`` parameter as a
1532
#: keyword argument.
1533
#:
1534
#: If it is not supplied, the ``arch`` specified by ``context`` is used instead.
1535
#:
1536
#: Consider it a shorthand to passing ``os=`` and ``arch=`` to every single
1537
#: function call.
1538
context = ContextType()
1✔
1539

1540
# Inherit default ADB values
1541
if 'ANDROID_ADB_SERVER_HOST' in os.environ:
1!
1542
    context.adb_host = os.environ.get('ANDROID_ADB_SERVER_HOST')
×
1543

1544
if 'ANDROID_ADB_SERVER_PORT' in os.environ:
1!
1545
    context.adb_port = int(os.getenv('ANDROID_ADB_SERVER_PORT'))
×
1546

1547
def LocalContext(function):
1✔
1548
    """
1549
    Wraps the specified function on a context.local() block, using kwargs.
1550

1551
    Example:
1552

1553
        >>> context.clear()
1554
        >>> @LocalContext
1555
        ... def printArch():
1556
        ...     print(context.arch)
1557
        >>> printArch()
1558
        i386
1559
        >>> printArch(arch='arm')
1560
        arm
1561
    """
1562
    @functools.wraps(function)
1✔
1563
    def setter(*a, **kw):
1✔
1564
        with context.local(**{k:kw.pop(k) for k,v in tuple(kw.items()) if isinstance(getattr(ContextType, k, None), property)}):
1✔
1565
            arch = context.arch
1✔
1566
            bits = context.bits
1✔
1567
            endian = context.endian
1✔
1568

1569
            # Prevent the user from doing silly things with invalid
1570
            # architecture / bits / endianness combinations.
1571
            if (arch == 'i386' and bits != 32) \
1!
1572
              or (arch == 'amd64' and bits != 64):
1573
                raise AttributeError("Invalid arch/bits combination: %s/%s" % (arch, bits))
×
1574

1575
            if arch in ('i386', 'amd64') and endian == 'big':
1!
1576
                raise AttributeError("Invalid arch/endianness combination: %s/%s" % (arch, endian))
×
1577

1578
            return function(*a, **kw)
1✔
1579
    return setter
1✔
1580

1581
def LocalNoarchContext(function):
1✔
1582
    """
1583
    Same as LocalContext, but resets arch to :const:`'none'` by default
1584

1585
    Example:
1586

1587
        >>> @LocalNoarchContext
1588
        ... def printArch():
1589
        ...     print(context.arch)
1590
        >>> printArch()
1591
        none
1592
    """
1593
    @functools.wraps(function)
1✔
1594
    def setter(*a, **kw):
1✔
1595
        kw.setdefault('arch', 'none')
1✔
1596
        with context.local(**{k:kw.pop(k) for k,v in tuple(kw.items()) if isinstance(getattr(ContextType, k, None), property)}):
1✔
1597
            return function(*a, **kw)
1✔
1598
    return setter
1✔
1599

1600
# Read configuration options from the context section
1601
def update_context_defaults(section):
1✔
1602
    # Circular imports FTW!
1603
    from pwnlib.util import safeeval
×
1604
    from pwnlib.log import getLogger
×
1605
    log = getLogger(__name__)
×
1606
    for key, value in section.items():
×
1607
        if key not in ContextType.defaults:
×
1608
            log.warn("Unknown configuration option %r in section %r" % (key, 'context'))
×
1609
            continue
×
1610

1611
        default = ContextType.defaults[key]
×
1612

1613
        if isinstance(default, six.string_types + six.integer_types + (tuple, list, dict)):
×
1614
            value = safeeval.expr(value)
×
1615
        else:
1616
            log.warn("Unsupported configuration option %r in section %r" % (key, 'context'))
×
1617

1618
        # Attempt to set the value, to see if it is value:
1619
        try:
×
1620
            with context.local(**{key: value}):
×
1621
                value = getattr(context, key)
×
1622
        except (ValueError, AttributeError) as e:
×
1623
            log.warn("Could not set context.%s=%s via pwn.conf (%s)", key, section[key], e)
×
1624
            continue
×
1625

1626
        ContextType.defaults[key] = value
×
1627

1628
register_config('context', update_context_defaults)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc