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

Gallopsled / pwntools / 5500924378

pending completion
5500924378

push

github-actions

peace-maker
Update CHANGELOG

3968 of 6659 branches covered (59.59%)

12136 of 16977 relevant lines covered (71.48%)

0.71 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
        'riscv32':   little_32,
408
        'riscv64':   little_64,
409
        's390':      big_32,
410
        'sparc':     big_32,
411
        'sparc64':   big_64,
412
        'thumb':     little_32,
413
        'vax':       little_32,
414
        'none':      {},
415
    })
416

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

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

435
    valid_signed = sorted(signednesses)
1✔
436

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

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

446

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

451
        Examples:
452

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

460

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

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

470
        It is a simple shorthand such that::
471

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

474
        is equivalent to::
475

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

480
        The following syntax is also valid::
481

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

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

487
        Examples:
488

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

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

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

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

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

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

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

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

517
        Examples:
518

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

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

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

550
        return LocalContext()
1✔
551

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

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

563
        Example:
564

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

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

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

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

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

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

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

596
        Example:
597

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

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

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

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

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

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

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

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

630

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

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

637
        .. code-block:: python
638

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

642
        Example:
643

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

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

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

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

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

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

664
        Arguments:
665

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

669
        Examples:
670

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

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

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

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

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

699
            return arch == platform_arch
1✔
700

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

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

708
        Side Effects:
709

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

715
            The following properties may be modified.
716

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

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

723
        Examples:
724

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

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

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

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

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

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

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

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

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

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

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

792
        for k,v in defaults.items():
1✔
793
            if k not in self._tls:
1✔
794
                self._tls[k] = v
1✔
795

796
        return arch
1✔
797

798
    @_validator
1✔
799
    def aslr(self, aslr):
1✔
800
        """
801
        ASLR settings for new processes.
802

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

807
        The ``setarch`` changes are lost if a ``setuid`` binary is executed.
808
        """
809
        return bool(aslr)
1✔
810

811
    @_validator
1✔
812
    def kernel(self, arch):
1✔
813
        """
814
        Target machine's kernel architecture.
815

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

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

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

830
        The default value is ``32``, but changes according to :attr:`arch`.
831

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

846
        if bits <= 0:
1✔
847
            raise AttributeError("bits must be > 0 (%r)" % bits)
1✔
848

849
        return bits
1✔
850

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

857
        Examples:
858

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

868
        """
869
        # Cyclic imports... sorry Idolf.
870
        from pwnlib.elf     import ELF
1✔
871

872
        if not isinstance(binary, ELF):
1✔
873
            binary = ELF(binary)
1✔
874

875
        self.arch   = binary.arch
1✔
876
        self.bits   = binary.bits
1✔
877
        self.endian = binary.endian
1✔
878
        self.os     = binary.os
1✔
879

880
        return binary
1✔
881

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

887
        This is a convenience wrapper around ``bits // 8``.
888

889
        Examples:
890

891
            >>> context.bytes = 1
892
            >>> context.bits == 8
893
            True
894

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

905
    @_validator
1✔
906
    def encoding(self, charset):
1✔
907
        if charset == 'auto':
×
908
            return charset
×
909

910
        if (  b'aA'.decode(charset) != 'aA'
×
911
            or 'aA'.encode(charset) != b'aA'):
912
            raise ValueError('Strange encoding!')
×
913

914
        return charset
×
915

916
    @_validator
1✔
917
    def endian(self, endianness):
1✔
918
        """
919
        Endianness of the target machine.
920

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

923
        Raises:
924
            AttributeError: An invalid endianness was provided
925

926
        Examples:
927

928
            >>> context.clear()
929
            >>> context.endian == 'little'
930
            True
931

932
            >>> context.endian = 'big'
933
            >>> context.endian
934
            'big'
935

936
            >>> context.endian = 'be'
937
            >>> context.endian == 'big'
938
            True
939

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

947
        if endian not in self.endiannesses:
1✔
948
            raise AttributeError("endian must be one of %r" % sorted(self.endiannesses))
1✔
949

950
        return self.endiannesses[endian]
1✔
951

952

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

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

962
        Valid values are specified by the standard Python ``logging`` module.
963

964
        Default value is set to ``INFO``.
965

966
        Examples:
967

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

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

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

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

998
        Works in a similar fashion to :attr:`log_level`.
999

1000
        Examples:
1001

1002

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

1028
        elif not hasattr(value, "fileno"):
×
1029
            raise AttributeError('log_file must be a file')
×
1030

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

1037
            if a == b:
1!
1038
                return self.log_file
×
1039

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

1055
    @_validator
1✔
1056
    def log_console(self, stream):
1✔
1057
        """
1058
        Sets the default logging console target.
1059

1060
        Examples:
1061

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

1073
    @property
1✔
1074
    def mask(self):
1✔
1075
        return (1 << self.bits) - 1
1✔
1076

1077
    @_validator
1✔
1078
    def os(self, os):
1✔
1079
        """
1080
        Operating system of the target machine.
1081

1082
        The default value is ``linux``.
1083

1084
        Allowed values are listed in :attr:`pwnlib.context.ContextType.oses`.
1085

1086
        Examples:
1087

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

1096
        if os not in self.oses:
1✔
1097
            raise AttributeError("os must be one of %r" % self.oses)
1✔
1098

1099
        return os
1✔
1100

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

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

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

1117
        Examples:
1118

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

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

1141
        return bool(signed)
1✔
1142

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

1149
        The default value is to have an infinite timeout.
1150

1151
        See :class:`pwnlib.timeout.Timeout` for additional information on
1152
        valid values.
1153
        """
1154
        return Timeout(value).timeout
1✔
1155

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

1167
    @property
1✔
1168
    def abi(self):
1✔
1169
        return self._abi
×
1170

1171
    @_validator
1✔
1172
    def proxy(self, proxy):
1✔
1173
        """
1174
        Default proxy for all socket connections.
1175

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

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

1186
        >>> context.proxy = None
1187
        >>> r=remote('google.com', 80, level='error')
1188
        """
1189

1190
        if not proxy:
1✔
1191
            socket.socket = _original_socket
1✔
1192
            return None
1✔
1193

1194
        if isinstance(proxy, str):
1!
1195
            proxy = (socks.SOCKS5, proxy)
1✔
1196

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

1200
        socks.set_default_proxy(*proxy)
1✔
1201
        socket.socket = socks.socksocket
1✔
1202

1203
        return proxy
1✔
1204

1205
    @_validator
1✔
1206
    def noptrace(self, value):
1✔
1207
        """Disable all actions which rely on ptrace.
1208

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

1212
        This option can be set with the ``NOPTRACE`` command-line argument.
1213
        """
1214
        return bool(value)
×
1215

1216

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

1221
        This is useful for Android exploitation.
1222

1223
        The default value is inherited from ANDROID_ADB_SERVER_HOST, or set
1224
        to the default 'localhost'.
1225
        """
1226
        return str(value)
×
1227

1228

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

1233
        This is useful for Android exploitation.
1234

1235
        The default value is inherited from ANDROID_ADB_SERVER_PORT, or set
1236
        to the default 5037.
1237
        """
1238
        return int(value)
×
1239

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

1254
        return device
×
1255

1256
    @property
1✔
1257
    def adb(self):
1✔
1258
        """Returns an argument array for connecting to adb.
1259

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

1264
        command = [ADB_PATH]
×
1265

1266
        if self.adb_host != self.defaults['adb_host']:
×
1267
            command += ['-H', self.adb_host]
×
1268

1269
        if self.adb_port != self.defaults['adb_port']:
×
1270
            command += ['-P', str(self.adb_port)]
×
1271

1272
        if self.device:
×
1273
            command += ['-s', str(self.device)]
×
1274

1275
        return command
×
1276

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

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

1286
    @_validator
1✔
1287
    def cache_dir_base(self, new_base):
1✔
1288
        """Base directory to use for caching content.
1289

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

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

1301
    @property
1✔
1302
    def cache_dir(self):
1✔
1303
        """Directory used for caching data.
1304

1305
        Note:
1306
            May be either a path string, or :const:`None`.
1307

1308
        Example:
1309

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

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

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

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

1373
        Default value is ``False``.
1374
        """
1375
        return bool(v)
×
1376

1377
    @_validator
1✔
1378
    def rename_corefiles(self, v):
1✔
1379
        """Whether pwntools automatically renames corefiles.
1380

1381
        This is useful for two things:
1382

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

1388
        This only affects corefiles accessed via :attr:`.process.corefile`.
1389

1390
        Default value is ``True``.
1391
        """
1392
        return bool(v)
×
1393

1394
    @_validator
1✔
1395
    def newline(self, v):
1✔
1396
        """Line ending used for Tubes by default.
1397

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

1405

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

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

1413
        The setting will only apply when GDB is launched locally since remote hosts may not have
1414
        the necessary requirements for the gdbinit.
1415

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

1418
        Default value is ``""``.
1419
        """
1420
        return str(value)
×
1421

1422
    @_validator
1✔
1423
    def cyclic_alphabet(self, alphabet):
1✔
1424
        """Cyclic alphabet.
1425

1426
        Default value is `string.ascii_lowercase`.
1427
        """
1428

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

1433
        return alphabet.encode()
1✔
1434

1435
    @_validator
1✔
1436
    def cyclic_size(self, size):
1✔
1437
        """Cyclic pattern size.
1438

1439
        Default value is `4`.
1440
        """
1441
        size = int(size)
×
1442

1443
        if size > self.bytes:
×
1444
            raise AttributeError("cyclic pattern size cannot be larger than word size")
×
1445

1446
        return size
×
1447

1448
    @_validator
1✔
1449
    def ssh_session(self, shell):
1✔
1450
        from pwnlib.tubes.ssh import ssh
1✔
1451

1452
        if not isinstance(shell, ssh):
1!
1453
            raise AttributeError("context.ssh_session must be an ssh tube")
×
1454

1455
        return shell
1✔
1456

1457
    #*************************************************************************
1458
    #                               ALIASES
1459
    #*************************************************************************
1460
    #
1461
    # These fields are aliases for fields defined above, either for
1462
    # convenience or compatibility.
1463
    #
1464
    #*************************************************************************
1465

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

1472
    def reset_local(self):
1✔
1473
        """
1474
        Deprecated.  Use :meth:`clear`.
1475
        """
1476
        self.clear()
1✔
1477

1478
    @property
1✔
1479
    def endianness(self):
1✔
1480
        """
1481
        Legacy alias for :attr:`endian`.
1482

1483
        Examples:
1484

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

1493

1494
    @property
1✔
1495
    def sign(self):
1✔
1496
        """
1497
        Alias for :attr:`signed`
1498
        """
1499
        return self.signed
1✔
1500

1501
    @sign.setter
1✔
1502
    def sign(self, value):
1✔
1503
        self.signed = value
1✔
1504

1505
    @property
1✔
1506
    def signedness(self):
1✔
1507
        """
1508
        Alias for :attr:`signed`
1509
        """
1510
        return self.signed
×
1511

1512
    @signedness.setter
1✔
1513
    def signedness(self, value):
1✔
1514
        self.signed = value
×
1515

1516

1517
    @property
1✔
1518
    def word_size(self):
1✔
1519
        """
1520
        Alias for :attr:`bits`
1521
        """
1522
        return self.bits
1✔
1523

1524
    @word_size.setter
1✔
1525
    def word_size(self, value):
1✔
1526
        self.bits = value
1✔
1527

1528
    Thread = Thread
1✔
1529

1530

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

1543
# Inherit default ADB values
1544
if 'ANDROID_ADB_SERVER_HOST' in os.environ:
1!
1545
    context.adb_host = os.environ.get('ANDROID_ADB_SERVER_HOST')
×
1546

1547
if 'ANDROID_ADB_SERVER_PORT' in os.environ:
1!
1548
    context.adb_port = int(os.getenv('ANDROID_ADB_SERVER_PORT'))
×
1549

1550
def LocalContext(function):
1✔
1551
    """
1552
    Wraps the specified function on a context.local() block, using kwargs.
1553

1554
    Example:
1555

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

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

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

1581
            return function(*a, **kw)
1✔
1582
    return setter
1✔
1583

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

1588
    Example:
1589

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

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

1614
        default = ContextType.defaults[key]
×
1615

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

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

1629
        ContextType.defaults[key] = value
×
1630

1631
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