Skip to content

Commit f0f0c36

Browse files
committed
Merge branch 'beta' into dev
2 parents 91d464b + a8e594d commit f0f0c36

File tree

25 files changed

+347
-200
lines changed

25 files changed

+347
-200
lines changed

.github/workflows/ci.yml

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
name: Continuous Integration
2+
on: [push, pull_request]
3+
4+
jobs:
5+
build:
6+
strategy:
7+
matrix:
8+
python-version: [2.7, 3.8]
9+
os: [ubuntu-latest]
10+
runs-on: ${{ matrix.os }}
11+
steps:
12+
- uses: actions/checkout@v2
13+
- name: Cache for pip
14+
uses: actions/cache@v1
15+
id: cache-pip
16+
with:
17+
path: ~/.cache/pip
18+
key: ${{ matrix.os }}-cache-pip
19+
20+
- name: Cache for dependencies
21+
uses: actions/cache@v1
22+
id: cache-deps
23+
with:
24+
path: android-?dk
25+
key: ${{ matrix.os }}-cache-deps
26+
27+
- name: Set up Python ${{ matrix.python-version }}
28+
uses: actions/setup-python@v1
29+
with:
30+
python-version: ${{ matrix.python-version }}
31+
32+
- name: Lint
33+
run: |
34+
pip install flake8
35+
flake8 . --count --select=E9,F63,F7 --show-source --statistics --exclude=android-?dk # TODO: Add F82
36+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --exclude=pwnlib/constants,android-?dk,.git,__pycache__
37+
38+
- name: Install Linux dependencies
39+
run: |
40+
sudo apt-get update
41+
sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 \
42+
ash bash-static dash ksh mksh zsh \
43+
pandoc gdb socat sshpass \
44+
binutils-multiarch qemu-user-static \
45+
binutils-aarch64-linux-gnu \
46+
binutils-arm-linux-gnueabihf \
47+
binutils-mips-linux-gnu \
48+
binutils-powerpc-linux-gnu \
49+
gcc-multilib \
50+
openjdk-8-jre-headless
51+
sudo apt-get install -y -o Acquire::Retries=3 \
52+
gcc-aarch64-linux-gnu \
53+
gcc-arm-linux-gnueabihf
54+
55+
- name: Install android avd
56+
if: steps.cache-deps.outputs.cache-hit != 'true'
57+
run: |
58+
USER=travis source travis/install.sh
59+
adb emu kill
60+
set | egrep '^(ANDROID|PATH)' >android-sdk/.android.env
61+
62+
- name: Set up SSH
63+
run: |
64+
chmod og-rw ~ # see https://stackoverflow.com/a/60367309/3869724
65+
ssh-keygen -t ed25519 -f ~/.ssh/pwntools-ci -N ''
66+
cat > ~/.ssh/config <<EOF
67+
Host example.pwnme
68+
User $USER
69+
HostName 127.0.0.1
70+
IdentityFile ~/.ssh/pwntools-ci
71+
EOF
72+
echo -n 'from="127.0.0.1" ' | cat - ~/.ssh/pwntools-ci.pub > ~/.ssh/authorized_keys
73+
ssh -o 'StrictHostKeyChecking no' example.pwnme id
74+
75+
- name: Install dependencies
76+
run: |
77+
pip install --upgrade pip
78+
pip install --upgrade flake8 appdirs
79+
python setup.py egg_info
80+
pip install --upgrade --editable .
81+
82+
- name: Sanity checks
83+
run: PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
84+
85+
- name: Install documentation dependencies
86+
run: pip install -r docs/requirements.txt
87+
88+
- name: Coverage doctests
89+
run: |
90+
source android-sdk/.android.env
91+
android-sdk/emulator/emulator -avd android-$ANDROID_ABI -no-window -no-boot-anim -read-only -no-audio -no-window -no-snapshot &
92+
adb wait-for-device
93+
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope # required by some gdb doctests
94+
PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest
95+
coverage combine
96+
97+
- name: Build source and wheel distributions
98+
run: |
99+
python setup.py sdist
100+
python setup.py bdist_wheel --universal
101+
102+
- uses: actions/upload-artifact@v2-preview
103+
with:
104+
path: dist/*
105+
106+
- name: Upload coverage to coveralls.io
107+
run: COVERALLS_REPO_TOKEN=PP20MEgztXIQJJTguQwe2jeCh6Bm4lkbv coveralls

docs/source/conf.py

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import os
1515
import doctest
16+
import signal
1617
import six
1718
import subprocess
1819
import sys
@@ -350,7 +351,7 @@ def linkcode_resolve(domain, info):
350351
if isinstance(val, (types.ModuleType, types.MethodType, types.FunctionType, types.TracebackType, types.FrameType, types.CodeType) + six.class_types):
351352
try:
352353
lines, first = inspect.getsourcelines(val)
353-
filename += '#L%d-%d' % (first, first + len(lines) - 1)
354+
filename += '#L%d-L%d' % (first, first + len(lines) - 1)
354355
except (IOError, TypeError):
355356
pass
356357

@@ -374,42 +375,11 @@ def linkcode_resolve(domain, info):
374375

375376
# -- Customization to Sphinx autodoc generation --------------------------------------------
376377
import sphinx.ext.autodoc
377-
from sphinx.util.inspect import safe_getmembers, safe_getattr
378378

379379
# Test hidden members (e.g. def _foo(...))
380380
def dont_skip_any_doctests(app, what, name, obj, skip, options):
381381
return False
382382

383-
def get_object_members_all(self, want_all):
384-
if want_all:
385-
# if not hasattr(self.object, '__all__'):
386-
# for implicit module members, check __module__ to avoid
387-
# documenting imported objects
388-
return True, safe_getmembers(self.object)
389-
# else:
390-
# memberlist = self.object.__all__
391-
# # Sometimes __all__ is broken...
392-
# if not isinstance(memberlist, (list, tuple)) or not \
393-
# all(isinstance(entry, string_types) for entry in memberlist):
394-
# self.directive.warn(
395-
# '__all__ should be a list of strings, not %r '
396-
# '(in module %s) -- ignoring __all__' %
397-
# (memberlist, self.fullname))
398-
# # fall back to all members
399-
# return True, safe_getmembers(self.object)
400-
else:
401-
memberlist = self.options.members or []
402-
ret = []
403-
for mname in memberlist:
404-
try:
405-
ret.append((mname, safe_getattr(self.object, mname)))
406-
except AttributeError:
407-
self.directive.warn(
408-
'missing attribute mentioned in :members: or __all__: '
409-
'module %s, attribute %s' % (
410-
safe_getattr(self.object, '__name__', '???'), mname))
411-
return False, ret
412-
413383
class _DummyClass(object): pass
414384

415385
class Py2OutputChecker(_DummyClass, doctest.OutputChecker):
@@ -437,11 +407,17 @@ def py2_doctest_init(self, checker=None, verbose=None, optionflags=0):
437407
checker = Py2OutputChecker()
438408
doctest.DocTestRunner.__init__(self, checker, verbose, optionflags)
439409

410+
class EndlessLoop(Exception): pass
411+
def alrm_handler(sig, frame):
412+
signal.alarm(180) # three minutes
413+
raise EndlessLoop()
414+
signal.signal(signal.SIGALRM, alrm_handler)
415+
signal.alarm(600) # ten minutes
416+
440417
if 'doctest' in sys.argv:
441418
def setup(app):
442419
app.connect('autodoc-skip-member', dont_skip_any_doctests)
443420

444421
if sys.version_info[:1] < (3,):
445422
import sphinx.ext.doctest
446423
sphinx.ext.doctest.SphinxDocTestRunner.__init__ = py2_doctest_init
447-
sphinx.ext.autodoc.ModuleDocumenter.get_object_members = get_object_members_all

docs/source/protocols/adb.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from pwn import *
44
from pwnlib.protocols.adb import AdbClient
5+
AdbClient().wait_for_device()
56

67
:mod:`pwnlib.protocols.adb` --- ADB Protocol Implementation
78
===========================================================

pwnlib/adb/adb.py

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ def exists(path):
680680
681681
>>> adb.exists('/')
682682
True
683-
>>> adb.exists('/init')
683+
>>> adb.exists('/etc/hosts')
684684
True
685685
>>> adb.exists('/does/not/exist')
686686
False
@@ -1132,10 +1132,9 @@ def __init__(self, name=None):
11321132
def __str__(self):
11331133
return str(getprop(self._name)).strip()
11341134

1135-
def __repr__(self):
1136-
return repr(str(self))
1137-
11381135
def __getattr__(self, attr):
1136+
if attr.startswith('_'):
1137+
raise AttributeError(attr)
11391138
if self._name:
11401139
attr = '%s.%s' % (self._name, attr)
11411140
return Property(attr)
@@ -1151,7 +1150,9 @@ def __setattr__(self, attr, value):
11511150
def __eq__(self, other):
11521151
# Allow simple comparison, e.g.:
11531152
# adb.properties.ro.oem_unlock_supported == "1"
1154-
return str(self) == other
1153+
if isinstance(other, six.string_types):
1154+
return str(self) == other
1155+
return super(Property, self).__eq__(other)
11551156

11561157
def __hash__(self, other):
11571158
# Allow hash indices matching on the property
@@ -1356,9 +1357,10 @@ def __iter__(self):
13561357
for name in listdir(self.by_name_dir):
13571358
yield name
13581359

1359-
@context.quietfunc
1360-
@with_device
13611360
def __getattr__(self, attr):
1361+
if name.startswith("_"):
1362+
raise AttributeError(attr)
1363+
13621364
for name in self:
13631365
if name == attr:
13641366
break
@@ -1367,19 +1369,20 @@ def __getattr__(self, attr):
13671369

13681370
path = os.path.join(self.by_name_dir, name)
13691371

1370-
# Find the actual path of the device
1371-
devpath = readlink(path)
1372-
devname = os.path.basename(devpath)
1373-
1374-
# Get the size of the partition
1375-
for line in read('/proc/partitions').splitlines():
1376-
if not line.strip():
1377-
continue
1378-
major, minor, blocks, name = line.split(None, 4)
1379-
if devname == name:
1380-
break
1381-
else:
1382-
log.error("Could not find size of partition %r" % name)
1372+
with context.quiet:
1373+
# Find the actual path of the device
1374+
devpath = readlink(path)
1375+
devname = os.path.basename(devpath)
1376+
1377+
# Get the size of the partition
1378+
for line in read('/proc/partitions').splitlines():
1379+
if not line.strip():
1380+
continue
1381+
major, minor, blocks, name = line.split(None, 4)
1382+
if devname == name:
1383+
break
1384+
else:
1385+
log.error("Could not find size of partition %r" % name)
13831386

13841387
return Partition(devpath, attr, int(blocks))
13851388

pwnlib/elf/corefile.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -381,13 +381,15 @@ class Corefile(ELF):
381381
This requires GDB to be installed, and can only be done with native
382382
processes. Getting a "complete" corefile requires GDB 7.11 or better.
383383
384-
>>> elf = ELF('/bin/bash')
384+
>>> elf = ELF('/bin/bash-static')
385385
>>> context.clear(binary=elf)
386386
>>> io = process(elf.path, env={'HELLO': 'WORLD'})
387387
>>> core = io.corefile
388388
389389
Data can also be extracted directly from the corefile.
390390
391+
>>> elf.address > 0
392+
True
391393
>>> core.exe[elf.address:elf.address+4]
392394
b'\x7fELF'
393395
>>> core.exe.data[:4]
@@ -429,13 +431,13 @@ class Corefile(ELF):
429431
430432
Corefiles can also be pulled from remote machines via SSH!
431433
432-
>>> s = ssh('travis', 'example.pwnme')
434+
>>> s = ssh(host='example.pwnme')
433435
>>> _ = s.set_working_directory()
434436
>>> elf = ELF.from_assembly(shellcraft.trap())
435437
>>> path = s.upload(elf.path)
436438
>>> _ =s.chmod('+x', path)
437439
>>> io = s.process(path)
438-
>>> io.wait()
440+
>>> io.wait(1)
439441
-1
440442
>>> io.corefile.signal == signal.SIGTRAP # doctest: +SKIP
441443
True
@@ -445,7 +447,7 @@ class Corefile(ELF):
445447
>>> context.clear(arch='amd64')
446448
>>> elf = ELF.from_assembly('push 1234; ret')
447449
>>> io = elf.process()
448-
>>> io.wait()
450+
>>> io.wait(1)
449451
>>> io.corefile.fault_addr
450452
1234
451453
@@ -459,7 +461,7 @@ class Corefile(ELF):
459461
460462
>>> elf = ELF.from_assembly(shellcraft.crash())
461463
>>> io = elf.process(env={'FOO': 'BAR=BAZ'})
462-
>>> io.wait()
464+
>>> io.wait(1)
463465
>>> core = io.corefile
464466
>>> core.getenv('FOO')
465467
b'BAR=BAZ'
@@ -480,7 +482,7 @@ class Corefile(ELF):
480482
... '''
481483
>>> elf = ELF.from_assembly(assembly)
482484
>>> io = elf.process()
483-
>>> io.wait()
485+
>>> io.wait(2)
484486
>>> core = io.corefile
485487
[!] End of the stack is corrupted, skipping stack parsing (got: 4141414141414141)
486488
>>> core.argc, core.argv, core.env
@@ -747,13 +749,13 @@ def signal(self):
747749
748750
>>> elf = ELF.from_assembly(shellcraft.trap())
749751
>>> io = elf.process()
750-
>>> io.wait()
752+
>>> io.wait(1)
751753
>>> io.corefile.signal == signal.SIGTRAP
752754
True
753755
754756
>>> elf = ELF.from_assembly(shellcraft.crash())
755757
>>> io = elf.process()
756-
>>> io.wait()
758+
>>> io.wait(1)
757759
>>> io.corefile.signal == signal.SIGSEGV
758760
True
759761
"""
@@ -774,7 +776,7 @@ def fault_addr(self):
774776
775777
>>> elf = ELF.from_assembly('mov eax, 0xdeadbeef; jmp eax', arch='i386')
776778
>>> io = elf.process()
777-
>>> io.wait()
779+
>>> io.wait(1)
778780
>>> io.corefile.fault_addr == io.corefile.eax == 0xdeadbeef
779781
True
780782
"""
@@ -1071,7 +1073,7 @@ def getenv(self, name):
10711073
10721074
>>> elf = ELF.from_assembly(shellcraft.trap())
10731075
>>> io = elf.process(env={'GREETING': 'Hello!'})
1074-
>>> io.wait()
1076+
>>> io.wait(1)
10751077
>>> io.corefile.getenv('GREETING')
10761078
b'Hello!'
10771079
"""
@@ -1090,7 +1092,7 @@ def registers(self):
10901092
10911093
>>> elf = ELF.from_assembly('mov eax, 0xdeadbeef;' + shellcraft.trap(), arch='i386')
10921094
>>> io = elf.process()
1093-
>>> io.wait()
1095+
>>> io.wait(1)
10941096
>>> io.corefile.registers['eax'] == 0xdeadbeef
10951097
True
10961098
"""

pwnlib/flag/flag.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def submit_flag(flag,
5050
>>> _ = submit_flag('flag', server='localhost', port=l.lport)
5151
>>> c = l.wait_for_connection()
5252
>>> c.recvall().split()
53-
['flag', 'unnamed-exploit', 'unknown-target', 'unknown-team']
53+
[b'flag', b'unnamed-exploit', b'unknown-target', b'unknown-team']
5454
"""
5555
flag = flag.strip()
5656

pwnlib/fmtstr.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@
3131
... write(1, my_var, sizeof(int));
3232
... return 0;
3333
... }''')
34-
>>> cmdline = ["gcc", source, "-Wno-format-security", "-m32", "-o", program]
35-
>>> process(cmdline).wait_for_close()
34+
>>> gcc = process(["gcc", source, "-Wno-format-security", "-m32", "-o", program])
35+
>>> gcc.poll(True) and gcc.recvall()
36+
0
3637
>>> def exec_fmt(payload):
3738
... p = process(program)
3839
... p.sendline(payload)

0 commit comments

Comments
 (0)