diff --git a/CHANGELOG.md b/CHANGELOG.md index 347a26c66..2fb9e31d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,12 +76,14 @@ The table below shows which release corresponds to each branch, and what date th - [#2374][2374] libcdb.unstrip_libc: debug symbols are fetched only if not present - [#2327][2327] Add basic support to debug processes on Windows - [#2322][2322] Add basic RISCV64 shellcraft support +- [#2330][2330] Change `context.newline` when setting `context.os` to `"windows"` [2360]: https://github.com/Gallopsled/pwntools/pull/2360 [2356]: https://github.com/Gallopsled/pwntools/pull/2356 [2374]: https://github.com/Gallopsled/pwntools/pull/2374 [2327]: https://github.com/Gallopsled/pwntools/pull/2327 [2322]: https://github.com/Gallopsled/pwntools/pull/2322 +[2330]: https://github.com/Gallopsled/pwntools/pull/2330 ## 4.13.0 (`beta`) diff --git a/pwnlib/context/__init__.py b/pwnlib/context/__init__.py index 9d6ebe71d..3d090e138 100644 --- a/pwnlib/context/__init__.py +++ b/pwnlib/context/__init__.py @@ -302,7 +302,7 @@ class ContextType(object): >>> context.os == 'linux' True >>> context.arch = 'arm' - >>> vars(context) == {'arch': 'arm', 'bits': 32, 'endian': 'little', 'os': 'linux'} + >>> vars(context) == {'arch': 'arm', 'bits': 32, 'endian': 'little', 'os': 'linux', 'newline': b'\n'} True >>> context.endian 'little' @@ -376,8 +376,19 @@ class ContextType(object): 'timeout': Timeout.maximum, } - #: Valid values for :meth:`pwnlib.context.ContextType.os` - oses = sorted(('linux','freebsd','windows','cgc','android','baremetal','darwin')) + unix_like = {'newline': b'\n'} + windows_like = {'newline': b'\r\n'} + + #: Keys are valid values for :meth:`pwnlib.context.ContextType.os` + oses = _longest({ + 'linux': unix_like, + 'freebsd': unix_like, + 'windows': windows_like, + 'cgc': unix_like, + 'android': unix_like, + 'baremetal': unix_like, + 'darwin': unix_like, + }) big_32 = {'endian': 'big', 'bits': 32} big_64 = {'endian': 'big', 'bits': 64} @@ -446,14 +457,14 @@ def __init__(self, **kwargs): def copy(self): - """copy() -> dict + r"""copy() -> dict Returns a copy of the current context as a dictionary. Examples: >>> context.clear() >>> context.os = 'linux' - >>> vars(context) == {'os': 'linux'} + >>> vars(context) == {'os': 'linux', 'newline': b'\n'} True """ return self._tls.copy() @@ -1104,25 +1115,73 @@ def mask(self): @_validator def os(self, os): - """ + r""" Operating system of the target machine. The default value is ``linux``. Allowed values are listed in :attr:`pwnlib.context.ContextType.oses`. + Side Effects: + + If an os is specified some attributes will be set on the context + if a user has not already set a value. + + The following property may be modified: + + - :attr:`newline` + + Raises: + AttributeError: An invalid os was specified + Examples: - >>> context.os = 'linux' + >>> context.clear() + >>> context.os == 'linux' # Default os + True + + >>> context.os = 'freebsd' + >>> context.os == 'freebsd' + True + >>> context.os = 'foobar' #doctest: +ELLIPSIS Traceback (most recent call last): ... AttributeError: os must be one of ['android', 'baremetal', 'cgc', 'freebsd', 'linux', 'windows'] + + >>> context.clear() + >>> context.newline == b'\n' # Default value + True + >>> context.os = 'windows' + >>> context.newline == b'\r\n' # New value + True + + Note that expressly setting :attr:`newline` means that we use + that value instead of the default + + >>> context.clear() + >>> context.newline = b'\n' + >>> context.os = 'windows' + >>> context.newline == b'\n' + True + + Setting the os can override the default for :attr:`newline` + + >>> context.clear() + >>> context.os = 'windows' + >>> vars(context) == {'os': 'windows', 'newline': b'\r\n'} + True """ os = os.lower() - if os not in self.oses: - raise AttributeError("os must be one of %r" % self.oses) + try: + defaults = self.oses[os] + except KeyError: + raise AttributeError("os must be one of %r" % sorted(self.oses)) + + for k,v in defaults.items(): + if k not in self._tls: + self._tls[k] = v return os