Compare commits

...
12 Commits
Author SHA1 Message Date
Mathias Svensson d39f9a145e Release 3.2.1 2016-12-24 10:53:02 +01:00
Zach Riggle 87491bf6dc Fix install script for new branches, where TRAVIS_COMMIT_RANGE is empty 2016-12-22 14:40:43 -05:00
Zach Riggle b30835ec5b Fix regression introduced in 386e68691f 2016-12-22 14:03:09 -05:00
Zach Riggle 036397c861 Fix adb.uninstall() typo (#816)
Closes Gallopsled/pwntools#783
2016-12-15 14:52:25 -06:00
Zach Riggle dcf03fd44d Bugfix for None data from field (#815) 2016-12-15 14:13:36 -06:00
Zach Riggle df54bf2bb3 Dont barf if home directory is not writable (#814)
* Handle the home directory being read-only / cache directory being unavailable

Fixes Gallopsled/pwntools#799

* Add doctest for context.cache_dir
2016-12-15 14:04:45 -06:00
Zach Riggle 386e68691f Multiple ADB performance and functionality fixes (#811)
* Fix ADB wait_for_device with ANDROID_SERIAL set, and fix performance

* Fix QEMU usage for native binaries

* Fix adb.write and adb.listdir operating on the wrong type of object

* Fix failing unit test with new behavior in adb.protocol.Client.list

* Fix adb server startup issue

* Rework deferred initialization to work around class-scope superclass attributes, and add test for AdbDevice
2016-12-14 17:34:18 -06:00
Zach Riggle 22f170abc7 Check the name before attempting to call startswith() (#805) 2016-12-14 16:47:38 -06:00
Zach Riggle 350610fbee Fix error message with StrictHostKeyChecking (#813) 2016-12-14 16:37:05 -06:00
Zach Riggle c7b3852eae Fix exception thrown under 32-bit Python interpreters. (#804)
Fixes Gallopsled/pwntools#518
2016-12-14 16:26:29 -06:00
Zach Riggle eaf6402198 Fix adb.partitions listing (#802)
* Fix adb.partitions listing

* Add adb.walk and adb.find functions to ensure adb.partitions works on devices without a find binary

* Add adb.readlink to simplify logic for reading symlinks
2016-12-14 15:37:47 -06:00
Ricky Zhou 2f597f41d4 Properly handle exceptions in remote._connect. (#793)
Previously, if getaddrinfo or socket.socket threw an exception,
_connect would return without ever calling h.success or h.failure. As a
result, progress indicators would continue to update indefinitely.

Fix this by using a with statement instead.

(Also remove an unused variable and fix a typo.)
2016-11-28 14:45:20 -07:00
17 changed files with 342 additions and 89 deletions

No files matched your search

+31 -3
View File
@@ -11,7 +11,8 @@ The table below shows which release corresponds to each branch, and what date th
| ---------------- | -------- | ---------------------- |
| [3.4.0](#340) | `dev` | Feb 4, 2017 (planned)
| [3.3.0](#330) | `beta` | Dec 24, 2016 (planned)
| [3.2.0](#320) | `stable` | Nov 12, 2016
| [3.2.1](#321) | `stable` | Dec 24, 2016
| [3.2.0](#320) | | Nov 12, 2016
| [3.1.1](#311) | | Oct 23, 2016
| [3.1.0](#310) | | Oct 2, 2016
| [3.0.4](#304) | | Sept 19, 2016
@@ -29,11 +30,37 @@ To be released on Feb 4, 2017.
To be released on Dec 24, 2016.
## 3.2.1
Multiple bug fixes.
- [#783][783] Fix `adb.uninstall` typo
- [#787][787] Added error handling for `ssh.process` argument `preexec_fn`
- [#793][793] Fixed progress message in `remote()` when connections failed
- [#802][802] Fixed partition listing in `adb.partitions`, which accidentally shelled out to the `adb` binary
- [#804][804] Fix error message for 32-bit distributions
- [#805][805] Fix exception in `Core.segments` when a segment has no name
- [#811][811] Fixes and performance improvements for `adb.wait_for_device()`
- [#813][813] Fixed a release script
- [#814][814] Fixed exceptions thrown if the `$HOME` directory is not writable
- [#815][815] Properly handle `None` in `MemLeak`
[783]: https://github.com/Gallopsled/pwntools/pull/783
[787]: https://github.com/Gallopsled/pwntools/pull/787
[793]: https://github.com/Gallopsled/pwntools/pull/793
[802]: https://github.com/Gallopsled/pwntools/pull/802
[804]: https://github.com/Gallopsled/pwntools/pull/804
[805]: https://github.com/Gallopsled/pwntools/pull/805
[811]: https://github.com/Gallopsled/pwntools/pull/811
[813]: https://github.com/Gallopsled/pwntools/pull/813
[814]: https://github.com/Gallopsled/pwntools/pull/814
[815]: https://github.com/Gallopsled/pwntools/pull/815
## 3.2.0
- [#695][695] Fixed a performance regression in `phd`.
- [452605e][452605e] Fixed [#629][#629] related to correct removal of temporary files.
- [ea94ee4][ea94ee4] Disallows semi-colons in for the `run_in_terminal` function, since it did not work properly in all cases.
- [452605e][452605e] Fixed [#629][629] related to correct removal of temporary files.
- [ea94ee4][ea94ee4] Disallows semi-colons in for the `run_in_terminal` function, since it did not work properly in all cases.
- [6376d07][6376d07] Added the mips shellcode `pushstr_array`.
- [#700][700] Added missing MIPS shellcode documentation to readthedocs, and enabled unit tests
- [#701][701] Command line tools refactored to have a common `pwn` entry point.
@@ -65,6 +92,7 @@ To be released on Dec 24, 2016.
+ It is now possible to e.g. `map(lambda d: d.process(['id']).recvall(), adb.devices())`
[629]: https://github.com/Gallopsled/pwntools/issues/629
[695]: https://github.com/Gallopsled/pwntools/pull/695
[700]: https://github.com/Gallopsled/pwntools/pull/700
[701]: https://github.com/Gallopsled/pwntools/pull/701
+1 -1
View File
@@ -2,7 +2,7 @@
![pwntools logo](https://github.com/Gallopsled/pwntools/blob/stable/docs/source/logo.png?raw=true)
[![Docs](https://readthedocs.org/projects/pwntools/badge/?version=stable)](https://docs.pwntools.com/)
[![PyPI](https://img.shields.io/badge/pypi-v3.2.0-green.svg?style=flat)](https://pypi.python.org/pypi/pwntools/)
[![PyPI](https://img.shields.io/badge/pypi-v3.2.1-green.svg?style=flat)](https://pypi.python.org/pypi/pwntools/)
[![Travis](https://travis-ci.org/Gallopsled/pwntools.svg)](https://travis-ci.org/Gallopsled/pwntools)
[![Coveralls](https://img.shields.io/coveralls/Gallopsled/pwntools/dev.svg)](https://coveralls.io/github/Gallopsled/pwntools?branch=dev)
[![Twitter](https://img.shields.io/badge/twitter-pwntools-4099FF.svg?style=flat)](https://twitter.com/pwntools)
+105 -13
View File
@@ -163,7 +163,22 @@ def reboot_bootloader():
c.reboot_bootloader()
class AdbDevice(Device):
"""Encapsulates information about a connected device."""
"""Encapsulates information about a connected device.
Example:
>>> device = adb.wait_for_device()
>>> device.arch
'arm'
>>> device.bits
32
>>> device.os
'android'
>>> device.product
'sdk_phone_armv7'
>>> device.serial
'emulator-5554'
"""
def __init__(self, serial, type, port=None, product='unknown', model='unknown', device='unknown', features=None, **kw):
self.serial = serial
self.type = type
@@ -176,13 +191,45 @@ class AdbDevice(Device):
if product == 'unknown':
return
with context.local(device=serial):
# Deferred fields
self._initialized = False
self._arch = None
self._bits = None
self._endian = None
self._avd = None
@property
def arch(self):
self.__do_deferred_initialization()
return self._arch
@property
def avd(self):
self.__do_deferred_initialization()
return self._avd
@property
def bits(self):
self.__do_deferred_initialization()
return self._bits
@property
def endian(self):
self.__do_deferred_initialization()
return self._endian
def __do_deferred_initialization(self):
if self._initialized:
return
with context.local(device=self.serial):
abi = str(properties.ro.product.cpu.abi)
context.clear()
context.arch = str(abi)
self.arch = context.arch
self.bits = context.bits
self.endian = context.endian
self._arch = context.arch
self._bits = context.bits
self._endian = context.endian
if self.port == 'emulator':
emulator, port = self.serial.split('-')
@@ -195,7 +242,8 @@ class AdbDevice(Device):
self.avd = r.recvline().strip()
except:
pass
# r = remote('localhost')
self._initialized = True
def __str__(self):
return self.serial
@@ -296,7 +344,9 @@ def wait_for_device(kick=False):
for device in devices():
if context.device == device:
return device
break
if not serial:
break
else:
log.error("Could not find any devices")
@@ -850,7 +900,7 @@ _android_mk_template = '''
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := poc
LOCAL_MODULE := %(local_module)s
LOCAL_SRC_FILES := %(local_src_files)s
include $(BUILD_EXECUTABLE)
@@ -880,6 +930,8 @@ def _generate_ndk_project(file_list, abi='arm-v7a', platform_version=21):
# Create the directories
# Populate Android.mk
local_module = os.path.basename(file_list[0])
local_module, _ = os.path.splitext(local_module)
local_src_files = ' '.join(list(map(os.path.basename, file_list)))
Android_mk = os.path.join(jni_directory, 'Android.mk')
with open(Android_mk, 'w+') as f:
@@ -914,7 +966,7 @@ def compile(source):
abi = 'armeabi-v7a'
sdk = '21'
# If we have an atatched device, use its settings.
# If we have an attached device, use its settings.
if context.device:
abi = str(properties.ro.product.cpu.abi)
sdk = str(properties.ro.build.version.sdk)
@@ -951,12 +1003,51 @@ class Partition(object):
with log.waitfor('Fetching %r partition (%s)' % (self.name, self.path)):
return read(self.path)
@with_device
def walk(top, topdown=True):
join = os.path.join
isdir = lambda x: stat.S_ISDIR(x['mode'])
client = Client()
names = client.list(top)
dirs, nondirs = [], []
for name, metadata in names.items():
if isdir(metadata):
dirs.append(name)
else:
nondirs.append(name)
if topdown:
yield top, dirs, nondirs
for name in dirs:
new_path = join(top, name)
for x in walk(new_path, topdown):
yield x
if not topdown:
yield top, dirs, nondirs
@with_device
def find(top, name):
for root, dirs, files in walk(top):
if name in files or name in dirs:
yield os.path.join(root, name)
@with_device
def readlink(path):
path = process(['readlink', path]).recvall()
# Readlink will emit a single newline
# We can't use the '-n' flag since old versions don't support it
if path.endswith('\n'):
path = path[:-1]
return path
class Partitions(object):
@property
@context.quiet
def by_name_dir(self):
cmd = ['shell','find /dev/block/platform -type d -name by-name']
return adb(cmd).strip()
return next(find('/dev/block/platform','by-name'))
@context.quiet
def __dir__(self):
@@ -972,6 +1063,7 @@ class Partitions(object):
yield name
@context.quiet
@with_device
def __getattr__(self, attr):
for name in self:
if name == attr:
@@ -982,7 +1074,7 @@ class Partitions(object):
path = os.path.join(self.by_name_dir, name)
# Find the actual path of the device
devpath = process(['readlink', '-n', path]).recvall()
devpath = readlink(path)
devname = os.path.basename(devpath)
# Get the size of the partition
@@ -1038,7 +1130,7 @@ def uninstall(package, *arguments):
"""
with log.progress("Installing package {}".format(package)):
with context.quiet:
return process(['pm','uninstall',apk] + list(arguments)).recvall()
return process(['pm','uninstall',package] + list(arguments)).recvall()
@context.quiet
def packages():
+46 -6
View File
@@ -5,6 +5,7 @@ Documentation is available here:
https://android.googlesource.com/platform/system/core/+/master/adb/protocol.txt
"""
import functools
import stat
import time
from ..context import context
@@ -42,11 +43,11 @@ class Message(object):
class Connection(remote):
"""Connection to the ADB server"""
def __init__(self, host, port, level=None, *a, **kw):
# Try to make sure ADB is running if it's on the default host and port.
if host == context.defaults['adb_host'] \
and port == context.defaults['adb_port']:
process(context.adb + ['start-server'], level='error').wait_for_close()
with context.quiet:
process(context.adb + ['start-server']).recvall()
with context.quiet:
super(Connection, self).__init__(host, port, level=level, *a, **kw)
@@ -296,8 +297,6 @@ class Client(Logger):
return rv
return wrapper
@_with_transport
@_sync
def list(self, path):
"""Execute the ``LIST`` command of the ``SYNC`` API.
@@ -328,8 +327,23 @@ class Client(Logger):
>>> pprint(adb.Client().list('/data/user'))
{'0': {'mode': 41471, 'size': 11, 'time': ...}}
>>> adb.Client().list('/does/not/exist')
{}
Traceback (most recent call last):
...
PwnlibException: Cannot list directory '/does/not/exist': Does not exist
"""
st = self.stat(path)
if not st:
log.error("Cannot list directory %r: Does not exist" % path)
if not stat.S_ISDIR(st['mode']):
log.error("Cannot list directory %r: Path is not a directory" % path)
return self._list(path)
@_with_transport
@_sync
def _list(self, path):
self.c.flat('LIST', len(path), path)
files = {}
while True:
@@ -389,9 +403,35 @@ class Client(Logger):
return {'mode': mode, 'size': size, 'time': time}
def write(self, path, data, mode=0o755, timestamp=None, callback=None):
"""Execute the ``WRITE`` command of the ``SYNC`` API.
Arguments:
path(str): Path to the file to write
data(str): Data to write to the file
mode(int): File mode to set (e.g. ``0o755``)
timestamp(int): Unix timestamp to set the file date to
callback(callable): Callback function invoked as data
is written. Arguments provided are:
- File path
- All data
- Expected size of all data
- Current chunk
- Expected size of chunk
"""
# We must ensure that 'path' is not a directory
# Writing to a directory is supported, but creates a temporary file
st = self.stat(path)
if st and stat.S_ISDIR(st['mode']):
log.error("Cannot write to %r: Path is a directory" % path)
return self._write(path, data, mode=0o755, timestamp=None, callback=None)
@_with_transport
@_sync
def write(self, path, data, mode=0o755, timestamp=None, callback=None):
def _write(self, path, data, mode=0o755, timestamp=None, callback=None):
path += ',' + str(mode)
self.c.flat('SEND', len(path), path)
+47 -2
View File
@@ -10,6 +10,7 @@ import logging
import os
import platform
import socket
import stat
import string
import subprocess
import sys
@@ -1107,8 +1108,13 @@ class ContextType(object):
@property
def adb(self):
"""Returns an argument array for connecting to adb."""
command = ['adb']
"""Returns an argument array for connecting to adb.
Unless ``$ADB_PATH`` is set, uses the default ``adb`` binary in ``$PATH``.
"""
ADB_PATH = os.environ.get('ADB_PATH', 'adb')
command = [ADB_PATH]
if self.adb_host != self.defaults['adb_host']:
command += ['-H', self.adb_host]
@@ -1121,6 +1127,45 @@ class ContextType(object):
return command
@property
def cache_dir(self):
"""Directory used for caching data.
Note:
May be either a path string, or ``None``.
Example:
>>> cache_dir = context.cache_dir
>>> cache_dir is not None
True
>>> os.chmod(cache_dir, 0o000)
>>> context.cache_dir is None
True
>>> os.chmod(cache_dir, 0o755)
>>> cache_dir == context.cache_dir
True
"""
home = os.path.expanduser('~')
if not os.access(home, os.W_OK):
return None
cache = os.path.join(home, '.pwntools-cache')
if not os.path.exists(cache):
try:
os.mkdir(cache)
except OSError:
return None
# Some wargames e.g. pwnable.kr have created dummy directories
# which cannot be modified by the user account (owned by root).
if not os.access(cache, os.W_OK):
return None
return cache
#*************************************************************************
# ALIASES
+1 -1
View File
@@ -235,7 +235,7 @@ class Core(ELF):
def libc(self):
"""Return the first mapping in libc"""
for m in self.mappings:
if m.name.startswith('libc') and m.name.endswith('.so'):
if m.name and m.name.startswith('libc') and m.name.endswith('.so'):
return m
@property
+2 -1
View File
@@ -539,7 +539,8 @@ class elf_prstatus_i386(ctypes.Structure):
assert ctypes.sizeof(elf_prstatus_i386) == 0x90
class elf_prstatus_amd64(ctypes.Structure):
_fields_ = generate_prstatus_common(64, user_regs_struct_amd64)
_fields_ = generate_prstatus_common(64, user_regs_struct_amd64) \
+ [('padding', ctypes.c_uint32)]
assert ctypes.sizeof(elf_prstatus_amd64) == 0x150
+1 -2
View File
@@ -166,7 +166,6 @@ class Progress(object):
using :meth:`Logger.progress`.
"""
def __init__(self, logger, msg, status, level, args, kwargs):
global _progressid
self._logger = logger
self._msg = msg
self._status = status
@@ -264,7 +263,7 @@ class Logger(object):
# This is a minor hack to permit user-defined classes which inherit
# from a tube (which do not actually reside in the pwnlib library)
# to receive logging abilities that behave as they would expect from
# the resto f the library
# the rest of the library
module = self.__module__
if not module.startswith('pwnlib'):
module = 'pwnlib.' + module
+2
View File
@@ -129,6 +129,8 @@ class MemLeak(object):
size = obj.size
offset = obj.offset
data = self.n(address + offset, size)
if not data:
return None
return unpack(data, size*8)
def field_compare(self, address, obj, expected):
+7 -1
View File
@@ -1,6 +1,8 @@
import os
from collections import defaultdict
from ..context import context
__all__ = ['make_function']
loaded = {}
@@ -33,10 +35,14 @@ def init_mako():
render_global = IsInside()
cache = context.cache_dir
if cache:
cache = os.path.join(cache, 'mako')
curdir = os.path.dirname(os.path.abspath(__file__))
lookup = TemplateLookup(
directories = [os.path.join(curdir, 'templates')],
module_directory = os.path.expanduser('~/.pwntools-cache/mako')
module_directory = cache
)
# The purpose of this definition is to create a new Tag.
+50 -26
View File
@@ -3,6 +3,7 @@ import errno
import fcntl
import logging
import os
import platform
import pty
import resource
import select
@@ -190,6 +191,9 @@ class process(tube):
>>> io = process(['sh','-c','sleep 10; exit 7'], alarm=2)
>>> io.poll(block=True) == -signal.SIGALRM
True
>>> binary = ELF.from_assembly('nop', arch='mips')
>>> p = process(binary.path)
"""
PTY = PTY
@@ -276,7 +280,7 @@ class process(tube):
with self.progress(message) as p:
if not self.aslr:
log.warn_once("ASLR is disabled!")
self.warn_once("ASLR is disabled!")
# In the event the binary is a foreign architecture,
# and binfmt is not installed (e.g. when running on
@@ -286,12 +290,6 @@ class process(tube):
executables = [executable]
exception = None
try:
if not context.native:
qemu = get_qemu_user()
prefixes.append(([qemu], qemu))
except: pass
for prefix, executable in prefixes:
try:
self.proc = subprocess.Popen(args = prefix + argv,
@@ -308,11 +306,7 @@ class process(tube):
except OSError as exception:
if exception.errno != errno.ENOEXEC:
raise
else:
try:
raise exception
except:
log.exception(str(prefixes))
prefixes.append(self.__on_enoexec(exception))
if self.pty is not None:
if stdin is slave:
@@ -349,7 +343,7 @@ class process(tube):
resource.setrlimit(resource.RLIMIT_STACK, (-1, -1))
except:
log.exception("Could not disable ASLR")
self.exception("Could not disable ASLR")
# Assume that the user would prefer to have core dumps.
resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
@@ -373,13 +367,43 @@ class process(tube):
self.preexec_fn()
def __on_enoexec(self, exception):
"""We received an 'exec format' error (ENOEXEC)
This implies that the user tried to execute e.g.
an ARM binary on a non-ARM system, and does not have
binfmt helpers installed for QEMU.
"""
# Get the ELF binary for the target executable
with context.quiet:
# XXX: Cyclic imports :(
from ..elf import ELF
binary = ELF(self.executable)
# If we're on macOS, this will never work. Bail now.
# if platform.mac_ver()[0]:
# self.error("Cannot run ELF binaries on macOS")
# Determine what architecture the binary is, and find the
# appropriate qemu binary to run it.
qemu = get_qemu_user(arch=binary.arch)
if qemu:
args = [qemu]
if self.argv:
args += ['-0', self.argv[0]]
args += ['--']
return [args, qemu]
# If we get here, we couldn't run the binary directly, and
# we don't have a qemu which can run it.
self.exception(exception)
@property
def program(self):
"""Alias for ``executable``, for backward compatibility"""
return self.executable
@staticmethod
def _validate(cwd, executable, argv, env):
def _validate(self, cwd, executable, argv, env):
"""
Perform extended validation on the executable path, argv, and envp.
@@ -398,14 +422,14 @@ class process(tube):
argv = [argv]
if not all(isinstance(arg, (str, unicode)) for arg in argv):
log.error("argv must be strings: %r" % argv)
self.error("argv must be strings: %r" % argv)
# Create a duplicate so we can modify it
argv = list(argv or [])
for i, arg in enumerate(argv):
if '\x00' in arg[:-1]:
log.error('Inappropriate nulls in argv[%i]: %r' % (i, arg))
self.error('Inappropriate nulls in argv[%i]: %r' % (i, arg))
argv[i] = arg.rstrip('\x00')
@@ -417,7 +441,7 @@ class process(tube):
#
if not executable:
if not argv:
log.error("Must specify argv or executable")
self.error("Must specify argv or executable")
executable = argv[0]
# Do not change absolute paths to binaries
@@ -437,11 +461,11 @@ class process(tube):
executable = os.path.join(cwd, executable)
if not os.path.exists(executable):
log.error("%r does not exist" % executable)
self.error("%r does not exist" % executable)
if not os.path.isfile(executable):
log.error("%r is not a file" % executable)
self.error("%r is not a file" % executable)
if not os.access(executable, os.X_OK):
log.error("%r is not marked as executable (+x)" % executable)
self.error("%r is not marked as executable (+x)" % executable)
#
# Validate environment
@@ -455,13 +479,13 @@ class process(tube):
for k,v in env.items():
if not isinstance(k, (str, unicode)):
log.error('Environment keys must be strings: %r' % k)
self.error('Environment keys must be strings: %r' % k)
if not isinstance(k, (str, unicode)):
log.error('Environment values must be strings: %r=%r' % (k,v))
self.error('Environment values must be strings: %r=%r' % (k,v))
if '\x00' in k[:-1]:
log.error('Inappropriate nulls in env key: %r' % (k))
self.error('Inappropriate nulls in env key: %r' % (k))
if '\x00' in v[:-1]:
log.error('Inappropriate nulls in env value: %r=%r' % (k, v))
self.error('Inappropriate nulls in env value: %r=%r' % (k, v))
env[k.rstrip('\x00')] = v.rstrip('\x00')
@@ -771,7 +795,7 @@ class process(tube):
"""
# If it's running under qemu-user, don't leak anything.
if 'qemu-' in os.path.realpath('/proc/%i/exe' % self.pid):
log.error("Cannot use leaker on binaries under QEMU.")
self.error("Cannot use leaker on binaries under QEMU.")
with open('/proc/%i/mem' % self.pid, 'rb') as mem:
mem.seek(address)
+17 -25
View File
@@ -108,38 +108,30 @@ class remote(sock):
sock = None
timeout = self.timeout
h = self.waitfor('Opening connection to %s on port %d' % (self.rhost, self.rport))
with self.waitfor('Opening connection to %s on port %d' % (self.rhost, self.rport)) as h:
for res in socket.getaddrinfo(self.rhost, self.rport, fam, typ, 0, socket.AI_PASSIVE):
self.family, self.type, self.proto, _canonname, sockaddr = res
for res in socket.getaddrinfo(self.rhost, self.rport, fam, typ, 0, socket.AI_PASSIVE):
self.family, self.type, self.proto, _canonname, sockaddr = res
if self.type not in [socket.SOCK_STREAM, socket.SOCK_DGRAM]:
continue
if self.type not in [socket.SOCK_STREAM, socket.SOCK_DGRAM]:
continue
h.status("Trying %s" % sockaddr[0])
h.status("Trying %s" % sockaddr[0])
sock = socket.socket(self.family, self.type, self.proto)
sock = socket.socket(self.family, self.type, self.proto)
if timeout != None and timeout <= 0:
sock.setblocking(0)
else:
sock.setblocking(1)
sock.settimeout(timeout)
if timeout != None and timeout <= 0:
sock.setblocking(0)
else:
sock.setblocking(1)
sock.settimeout(timeout)
try:
sock.connect(sockaddr)
break
except socket.error:
pass
else:
h.failure()
try:
sock.connect(sockaddr)
return sock
except socket.error:
pass
self.error("Could not connect to %s on port %d" % (self.rhost, self.rport))
h.success()
return sock
@classmethod
def fromsocket(cls, socket):
"""
+22 -4
View File
@@ -53,7 +53,11 @@ def available_on_pypi(prerelease=current_version.is_prerelease):
def cache_file():
"""Returns the path of the file used to cache update data, and ensures that it exists."""
cache_dir = os.path.expanduser('~/.pwntools-cache')
cache_dir = context.cache_dir
if not cache_dir:
return None
cache_file = os.path.join(cache_dir, 'update')
if not os.path.isdir(cache_dir):
@@ -66,12 +70,23 @@ def cache_file():
def last_check():
"""Return the date of the last check"""
return os.path.getmtime(cache_file())
cache = cache_file()
if cache:
return os.path.getmtime(cache_file())
# Fallback
return time.time()
def should_check():
"""Return True if we should check for an update"""
if read(cache_file()).strip() == 'never':
filename = cache_file()
if not filename:
return False
if read(filename).strip() == 'never':
return False
return time.time() > (last_check() + update_freq)
def perform_check(prerelease=current_version.is_prerelease):
@@ -109,7 +124,10 @@ def perform_check(prerelease=current_version.is_prerelease):
where = None
command = None
os.utime(cache_file(), None)
cache = cache_file()
if cache:
os.utime(cache, None)
if best == current_version:
log.info("You have the latest version of Pwntools (%s)" % best)
+1 -1
View File
@@ -1 +1 @@
__version__ = '3.2.0'
__version__ = '3.2.1'
+1 -1
View File
@@ -83,7 +83,7 @@ except ImportError:
setup(
name = 'pwntools',
packages = find_packages(),
version = '3.2.0',
version = '3.2.1',
data_files = [('',
glob.glob('*.md') + glob.glob('*.txt')),
],
+7 -1
View File
@@ -72,7 +72,13 @@ setup_android_emulator()
# If we are running on Travis CI, and there were no changes to Android
# or ADB code, then we do not need the emulator
if [ -n "$TRAVIS" ]; then
if ! (git log --stat "$TRAVIS_COMMIT_RANGE" | grep -E "android|adb"); then
if [ -z "$TRAVIS_COMMIT_RANGE" ]; then
echo "TRAVIS_COMMIT_RANGE is empty, forcing Android Emulator installation"
elif ! git show "$TRAVIS_COMMIT_RANGE" ; then
echo "TRAVIS_COMMIT_RANGE is invalid, forcing Android Emulator installation"
elif (git log --stat "$TRAVIS_COMMIT_RANGE" | grep -iE "android|adb"); then
echo "Found Android-related commits, forcing Android Emulator installation"
else
# In order to avoid running the doctests that require the Android
# emulator, while still leaving the code intact, we remove the
# RST file that Sphinx searches.
+1 -1
View File
@@ -11,5 +11,5 @@ if [ "$TRAVIS_BRANCH" = "stable" ] && [ ! -z "$TRY_KEY" ]; then
ssh-add ~/.ssh/try_key
# Update!
ssh [email protected] -- 'docker/update'
ssh -o "StrictHostKeyChecking no" [email protected] -- 'docker/update'
fi