mirror of
https://github.com/Gallopsled/pwntools.git
synced 2026-11-08 01:58:16 -10:00
Compare commits
51
Commits
4.15.0beta0
...
beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8d47977ac | ||
|
|
3daf28b6be | ||
|
|
f4b3e6e1c4 | ||
|
|
2b39684912 | ||
|
|
7e5e79d00f | ||
|
|
e551b4ae56 | ||
|
|
43ef1b494c | ||
|
|
be92aa1b4c | ||
|
|
07f3caadc4 | ||
|
|
3ad86ec5ad | ||
|
|
b5076d9262 | ||
|
|
77b4f06b07 | ||
|
|
4968499d11 | ||
|
|
f38991bc02 | ||
|
|
d0e6ee4b37 | ||
|
|
48ea09b27f | ||
|
|
9f22a2f202 | ||
|
|
7413a3da31 | ||
|
|
95aac6e367 | ||
|
|
9ed93a5281 | ||
|
|
3579fb1384 | ||
|
|
485bf0a0b7 | ||
|
|
b133b76b4e | ||
|
|
ffc46d277a | ||
|
|
dd3a5f6974 | ||
|
|
9a275b2db0 | ||
|
|
1c28222fe6 | ||
|
|
b93c5ace10 | ||
|
|
da9797c1c2 | ||
|
|
6dfe8b6f72 | ||
|
|
425221fb30 | ||
|
|
004a6dab88 | ||
|
|
6098f28bfa | ||
|
|
1205b1aaa3 | ||
|
|
ce6d2ca847 | ||
|
|
9e0ca3a415 | ||
|
|
b60b2e897f | ||
|
|
2c3e783f96 | ||
|
|
15bdd4a766 | ||
|
|
7c95e838f4 | ||
|
|
92aa64ce01 | ||
|
|
ae4bb08c3e | ||
|
|
6739525ebe | ||
|
|
d10b7aee4c | ||
|
|
aeb4c26bf0 | ||
|
|
ec6b8cd87c | ||
|
|
b2345f214e | ||
|
|
e9d9bd9ddb | ||
|
|
d7a7376eca | ||
|
|
9472ec1e93 | ||
|
|
e4b939dc1a |
No files matched your search
@@ -22,8 +22,9 @@ jobs:
|
||||
- 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/
|
||||
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/
|
||||
@@ -120,6 +121,7 @@ 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 \
|
||||
@@ -148,6 +150,8 @@ 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)'
|
||||
@@ -283,7 +287,7 @@ jobs:
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install --upgrade --editable .
|
||||
|
||||
|
||||
- name: Sanity checks
|
||||
run: |
|
||||
python -bb -c 'from pwn import *'
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: PyLint
|
||||
run: |
|
||||
set -x
|
||||
pip install pylint
|
||||
pip install 'pylint<4'
|
||||
pip install --upgrade -e .
|
||||
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
|
||||
|
||||
+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
|
||||
|
||||
+122
-7
@@ -10,8 +10,9 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
| Version | Branch | Release Date |
|
||||
| ---------------- | -------- | ---------------------- |
|
||||
| [5.0.0](#500-dev) | `dev` |
|
||||
| [4.15.0](#4150-beta) | `beta` |
|
||||
| [4.14.0](#4140-stable) | `stable` | Jan 15, 2025
|
||||
| [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
|
||||
@@ -74,8 +75,78 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
|
||||
## 5.0.0 (`dev`)
|
||||
|
||||
- [#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`
|
||||
- [#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
|
||||
|
||||
[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
|
||||
[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
|
||||
|
||||
## 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`)
|
||||
|
||||
## 4.15.0 (`beta`)
|
||||
- [#2508][2508] Ignore a warning when compiling with asm on nix
|
||||
- [#2471][2471] Properly close spawned kitty window
|
||||
- [#2358][2358] Cache output of `asm()`
|
||||
@@ -93,6 +164,19 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
- [#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
|
||||
@@ -111,8 +195,39 @@ The table below shows which release corresponds to each branch, and what date th
|
||||
[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 (`stable`)
|
||||
## 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
|
||||
@@ -183,7 +298,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
|
||||
@@ -270,7 +385,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`
|
||||
@@ -545,7 +660,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
|
||||
|
||||
|
||||
@@ -19,6 +19,6 @@ psutil
|
||||
requests>=2.5.1
|
||||
ropgadget>=5.3
|
||||
sphinx==1.8.6; python_version<'3'
|
||||
sphinx>=7.0.0; python_version>='3'
|
||||
sphinx>=8.1.3, <9; python_version>='3'
|
||||
sphinx_rtd_theme
|
||||
sphinxcontrib-autoprogram<=0.1.5
|
||||
@@ -34,6 +34,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 ...'
|
||||
|
||||
@@ -32,8 +32,10 @@ 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 \
|
||||
tmux \
|
||||
|
||||
+9
-1
@@ -89,7 +89,15 @@ try:
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
colored_traceback.add_hook()
|
||||
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
|
||||
|
||||
# Equivalence with the default behavior of "from import *"
|
||||
# __all__ = [x for x in tuple(globals()) if not x.startswith('_')]
|
||||
+21
-1
@@ -1275,8 +1275,28 @@ properties = Property()
|
||||
|
||||
def _build_date():
|
||||
"""Returns the build date in the form YYYY-MM-DD as a string"""
|
||||
import datetime
|
||||
|
||||
# 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()), dateutil.tz.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:
|
||||
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):
|
||||
|
||||
+14
-3
@@ -170,6 +170,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):
|
||||
@@ -178,6 +180,12 @@ 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] + {
|
||||
@@ -185,11 +193,14 @@ def which_binutils(util, check_version=False):
|
||||
'i386': ['x86_64', 'amd64'],
|
||||
'i686': ['x86_64', 'amd64'],
|
||||
'amd64': ['x86_64', 'i386'],
|
||||
'mips64': ['mips'],
|
||||
'mips': ['mipsel'],
|
||||
'mipsel': ['mips'],
|
||||
'mips64': ['mips', 'mipsel'],
|
||||
'mips64el': ['mipsel', 'mips'],
|
||||
'powerpc64': ['powerpc'],
|
||||
'sparc64': ['sparc'],
|
||||
'riscv32': ['riscv32', 'riscv64', 'riscv'],
|
||||
'riscv64': ['riscv64', 'riscv32', 'riscv'],
|
||||
'riscv32': ['riscv64', 'riscv'],
|
||||
'riscv64': ['riscv32', 'riscv'],
|
||||
}.get(arch, [])
|
||||
|
||||
# If one of the candidate architectures matches the native
|
||||
|
||||
@@ -115,6 +115,18 @@ def get_docker_image_libraries():
|
||||
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
|
||||
@@ -138,7 +150,19 @@ def detect_missing_binaries(args):
|
||||
elif len(other_files) > 1:
|
||||
log.warning("Failed to find challenge binary. There are multiple binaries in the current directory: %s", other_files)
|
||||
|
||||
if has_dockerfile and exe and not (libc or ld):
|
||||
# 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:
|
||||
|
||||
@@ -359,6 +359,7 @@ class ContextType(object):
|
||||
'encoding': 'auto',
|
||||
'endian': 'little',
|
||||
'gdbinit': "",
|
||||
'gdb_binary': "",
|
||||
'kernel': None,
|
||||
'local_libcdb': "/var/lib/libc-database",
|
||||
'log_level': logging.INFO,
|
||||
@@ -1539,6 +1540,20 @@ class ContextType(object):
|
||||
"""
|
||||
return str(value)
|
||||
|
||||
@_validator
|
||||
def gdb_binary(self, value):
|
||||
"""Path to the binary that is used when running GDB locally.
|
||||
|
||||
This is useful when you have multiple versions of gdb installed or the gdb binary is
|
||||
called something different.
|
||||
|
||||
If set to an empty string, pwntools will try to search for a reasonable gdb binary from
|
||||
the path.
|
||||
|
||||
Default value is ``""``.
|
||||
"""
|
||||
return str(value)
|
||||
|
||||
@_validator
|
||||
def cyclic_alphabet(self, alphabet):
|
||||
"""Cyclic alphabet.
|
||||
|
||||
+17
-1
@@ -555,6 +555,9 @@ class Corefile(ELF):
|
||||
# Pointer to the entry point
|
||||
self.at_entry = 0
|
||||
|
||||
# Pointer to the vdso
|
||||
self.at_sysinfo_ehdr = None
|
||||
|
||||
try:
|
||||
super(Corefile, self).__init__(*a, **kw)
|
||||
except IOError:
|
||||
@@ -611,6 +614,8 @@ class Corefile(ELF):
|
||||
|
||||
if not self.stack and self.mappings:
|
||||
self.stack = self.mappings[-1].stop
|
||||
if self.mappings[-1].start == 0xffffffffff600000 and len(self.mappings) > 1:
|
||||
self.stack = self.mappings[-2].stop
|
||||
|
||||
if self.stack and self.mappings:
|
||||
for mapping in self.mappings:
|
||||
@@ -1510,7 +1515,18 @@ class CorefileFinder(object):
|
||||
# should be unique enough that we can just glob.
|
||||
|
||||
boot_id = read('/proc/sys/kernel/random/boot_id').strip().decode()
|
||||
path = self.exe.replace('/', '_')
|
||||
|
||||
# Use the absolute path of the executable
|
||||
# Apport uses the executable's path to determine the core dump filename
|
||||
#
|
||||
# Reference source:
|
||||
# https://github.com/canonical/apport/blob/4bbb179b8f92989bf7c1ee3692074f35d70ef3e8/data/apport#L110
|
||||
# https://github.com/canonical/apport/blob/4bbb179b8f92989bf7c1ee3692074f35d70ef3e8/apport/fileutils.py#L599
|
||||
#
|
||||
# Apport calls `get_core_path` with `options.executable_path`, which corresponds to
|
||||
# the executable's pathname, as specified by the `%E` placeholder
|
||||
# in the core pattern (see `man core` and `apport --help`).
|
||||
path = os.path.abspath(self.exe).replace('/', '_').replace('.', '_')
|
||||
|
||||
# Format the name
|
||||
corefile_name = 'core.{path}.{uid}.{boot_id}.{pid}.*'.format(
|
||||
|
||||
+118
-80
@@ -715,29 +715,56 @@ class ELF(ELFFile):
|
||||
|
||||
@property
|
||||
def libs(self):
|
||||
"""Dictionary of ``{path: address}`` for every library loaded for this ELF."""
|
||||
"""Dictionary of ``{path: address}`` for every library loaded for this ELF.
|
||||
|
||||
.. warning::
|
||||
|
||||
Getting this attribute actually runs the executable.
|
||||
Make sure that you trust the binary you are exploiting.
|
||||
If it adds itself as ``DT_NEEDED``, has overlapping segments,
|
||||
ambiguous headers, or employs text relocations, it can run arbitrary
|
||||
code even though you are just inspecting it.
|
||||
Running is the only reliable way to ensure all the libraries are
|
||||
loaded from the correct paths, because some of them may change
|
||||
loading logic.
|
||||
|
||||
Exploitability first noticed at CWTE CTF 2025.
|
||||
"""
|
||||
if self._libs is None:
|
||||
self._populate_libraries()
|
||||
return self._libs
|
||||
|
||||
@property
|
||||
def maps(self):
|
||||
"""Dictionary of ``{name: address}`` for every mapping in this ELF's address space."""
|
||||
"""Dictionary of ``{name: address}`` for every mapping in this ELF's address space.
|
||||
|
||||
.. warning::
|
||||
|
||||
Getting this attribute actually runs the executable.
|
||||
Make sure that you trust the binary you are exploiting
|
||||
(see :attr:`.ELF.libs`).
|
||||
"""
|
||||
if self._maps is None:
|
||||
self._populate_libraries()
|
||||
return self._maps
|
||||
|
||||
@property
|
||||
def libc(self):
|
||||
""":class:`.ELF`: If this :class:`.ELF` imports any libraries which contain ``'libc[.-]``,
|
||||
""":class:`.ELF`: If this :class:`.ELF` imports any libraries which contain ``/libc[.-]``,
|
||||
and we can determine the appropriate path to it on the local
|
||||
system, returns a new :class:`.ELF` object pertaining to that library.
|
||||
Prints the `checksec` output of the library if it was printed for the original ELF too.
|
||||
|
||||
If not found, the value will be :const:`None`.
|
||||
|
||||
.. warning::
|
||||
|
||||
Getting this attribute actually runs the executable.
|
||||
Make sure that you trust the binary you are exploiting
|
||||
(see :attr:`.ELF.libs`).
|
||||
"""
|
||||
for lib in self.libs:
|
||||
if '/libc.' in lib or '/libc-' in lib:
|
||||
if ('/libc.' in lib or '/libc-' in lib) and lib != self.path:
|
||||
return ELF(lib, self._print_checksec)
|
||||
|
||||
def _populate_libraries(self):
|
||||
@@ -852,6 +879,13 @@ class ELF(ELFFile):
|
||||
log.warn_once("Injected /proc/self/maps code did not execute correctly")
|
||||
return {}
|
||||
|
||||
# Sometimes the original binary already fail to run, for example, in case glibc mismatches.
|
||||
try:
|
||||
int(data.split('-', 1)[0], 16)
|
||||
except ValueError:
|
||||
log.warn_once("Cannot execute `%s` to get /proc/self/maps", self.path)
|
||||
return {}
|
||||
|
||||
# Swap in the original ELF name
|
||||
data = data.replace(path, self.path)
|
||||
|
||||
@@ -919,10 +953,9 @@ class ELF(ELFFile):
|
||||
continue
|
||||
|
||||
for symbol in _iter_symbols(section):
|
||||
value = symbol.entry.st_value
|
||||
if not value:
|
||||
if not symbol.name or symbol.entry.st_shndx == 'SHN_UNDEF':
|
||||
continue
|
||||
self.symbols[symbol.name] = value
|
||||
self.symbols[symbol.name] = symbol.entry.st_value
|
||||
|
||||
def _populate_synthetic_symbols(self):
|
||||
"""Adds symbols from the GOT and PLT to the symbols dictionary.
|
||||
@@ -1791,131 +1824,136 @@ class ELF(ELFFile):
|
||||
\\* Hardware limitations are ignored.
|
||||
|
||||
If ``READ_IMPLIES_EXEC`` is set, then `all readable pages are executable`__.
|
||||
.. __: https://github.com/torvalds/linux/blob/v6.3/fs/binfmt_elf.c#L1008-L1009
|
||||
.. code-block:: c
|
||||
|
||||
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
|
||||
current->personality |= READ_IMPLIES_EXEC;
|
||||
.. __: https://github.com/torvalds/linux/blob/v6.3/fs/binfmt_elf.c#L1008-L1009
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
|
||||
current->personality |= READ_IMPLIES_EXEC;
|
||||
|
||||
.. [#x86_5.7]
|
||||
`source <https://github.com/torvalds/linux/blob/v5.7/arch/x86/include/asm/elf.h#L285-L286>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
#define elf_read_implies_exec(ex, executable_stack) \\
|
||||
(executable_stack != EXSTACK_DISABLE_X)
|
||||
#define elf_read_implies_exec(ex, executable_stack) \\
|
||||
(executable_stack != EXSTACK_DISABLE_X)
|
||||
|
||||
.. [#x86_5.8]
|
||||
`source <https://github.com/torvalds/linux/blob/v5.8/arch/x86/include/asm/elf.h#L305-L306>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
#define elf_read_implies_exec(ex, executable_stack) \\
|
||||
(mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)
|
||||
#define elf_read_implies_exec(ex, executable_stack) \\
|
||||
(mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)
|
||||
|
||||
`mmap_is_ia32()`__:
|
||||
.. __: https://github.com/torvalds/linux/blob/v5.8/arch/x86/include/asm/elf.h#L318-L321
|
||||
.. code-block:: c
|
||||
`mmap_is_ia32()`__:
|
||||
|
||||
/*
|
||||
* True on X86_32 or when emulating IA32 on X86_64
|
||||
*/
|
||||
static inline int mmap_is_ia32(void)
|
||||
.. __: https://github.com/torvalds/linux/blob/v5.8/arch/x86/include/asm/elf.h#L318-L321
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/*
|
||||
* True on X86_32 or when emulating IA32 on X86_64
|
||||
*/
|
||||
static inline int mmap_is_ia32(void)
|
||||
|
||||
.. [#arm_5.7]
|
||||
`source <https://github.com/torvalds/linux/blob/v5.7/arch/arm/kernel/elf.c#L85-L92>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
int arm_elf_read_implies_exec(int executable_stack)
|
||||
{
|
||||
if (executable_stack != EXSTACK_DISABLE_X)
|
||||
return 1;
|
||||
if (cpu_architecture() < CPU_ARCH_ARMv6)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
int arm_elf_read_implies_exec(int executable_stack)
|
||||
{
|
||||
if (executable_stack != EXSTACK_DISABLE_X)
|
||||
return 1;
|
||||
if (cpu_architecture() < CPU_ARCH_ARMv6)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
.. [#arm_5.8]
|
||||
`source <https://github.com/torvalds/linux/blob/v5.8/arch/arm/kernel/elf.c#L104-L111>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
int arm_elf_read_implies_exec(int executable_stack)
|
||||
{
|
||||
if (executable_stack == EXSTACK_DEFAULT)
|
||||
return 1;
|
||||
if (cpu_architecture() < CPU_ARCH_ARMv6)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
int arm_elf_read_implies_exec(int executable_stack)
|
||||
{
|
||||
if (executable_stack == EXSTACK_DEFAULT)
|
||||
return 1;
|
||||
if (cpu_architecture() < CPU_ARCH_ARMv6)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
.. [#mips_5.17]
|
||||
`source <https://github.com/torvalds/linux/blob/v5.17/arch/mips/kernel/elf.c#L329-L342>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
int mips_elf_read_implies_exec(void *elf_ex, int exstack)
|
||||
{
|
||||
if (exstack != EXSTACK_DISABLE_X) {
|
||||
/* The binary doesn't request a non-executable stack */
|
||||
return 1;
|
||||
}
|
||||
if (!cpu_has_rixi) {
|
||||
/* The CPU doesn't support non-executable memory */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
int mips_elf_read_implies_exec(void *elf_ex, int exstack)
|
||||
{
|
||||
if (exstack != EXSTACK_DISABLE_X) {
|
||||
/* The binary doesn't request a non-executable stack */
|
||||
return 1;
|
||||
}
|
||||
if (!cpu_has_rixi) {
|
||||
/* The CPU doesn't support non-executable memory */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
.. [#mips_5.18]
|
||||
`source <https://github.com/torvalds/linux/blob/v5.18/arch/mips/kernel/elf.c#L329-L336>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
int mips_elf_read_implies_exec(void *elf_ex, int exstack)
|
||||
{
|
||||
/*
|
||||
* Set READ_IMPLIES_EXEC only on non-NX systems that
|
||||
* do not request a specific state via PT_GNU_STACK.
|
||||
*/
|
||||
return (!cpu_has_rixi && exstack == EXSTACK_DEFAULT);
|
||||
}
|
||||
int mips_elf_read_implies_exec(void *elf_ex, int exstack)
|
||||
{
|
||||
/*
|
||||
* Set READ_IMPLIES_EXEC only on non-NX systems that
|
||||
* do not request a specific state via PT_GNU_STACK.
|
||||
*/
|
||||
return (!cpu_has_rixi && exstack == EXSTACK_DEFAULT);
|
||||
}
|
||||
|
||||
.. [#powerpc]
|
||||
`source <https://github.com/torvalds/linux/blob/v6.3/arch/powerpc/include/asm/elf.h#L82-L108>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
#ifdef __powerpc64__
|
||||
/* stripped */
|
||||
# define elf_read_implies_exec(ex, exec_stk) (is_32bit_task() ? \\
|
||||
(exec_stk == EXSTACK_DEFAULT) : 0)
|
||||
#else
|
||||
# define elf_read_implies_exec(ex, exec_stk) (exec_stk == EXSTACK_DEFAULT)
|
||||
#endif /* __powerpc64__ */
|
||||
#ifdef __powerpc64__
|
||||
/* stripped */
|
||||
# define elf_read_implies_exec(ex, exec_stk) (is_32bit_task() ? \\
|
||||
(exec_stk == EXSTACK_DEFAULT) : 0)
|
||||
#else
|
||||
# define elf_read_implies_exec(ex, exec_stk) (exec_stk == EXSTACK_DEFAULT)
|
||||
#endif /* __powerpc64__ */
|
||||
|
||||
.. [#ia64]
|
||||
`source <https://github.com/torvalds/linux/blob/v6.3/arch/ia64/include/asm/elf.h#L203-L204>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
#define elf_read_implies_exec(ex, executable_stack) \\
|
||||
((executable_stack!=EXSTACK_DISABLE_X) && ((ex).e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK) != 0)
|
||||
#define elf_read_implies_exec(ex, executable_stack) \\
|
||||
((executable_stack!=EXSTACK_DISABLE_X) && ((ex).e_flags & EF_IA_64_LINUX_EXECUTABLE_STACK) != 0)
|
||||
|
||||
EF_IA_64_LINUX_EXECUTABLE_STACK__:
|
||||
.. __: https://github.com/torvalds/linux/blob/v6.3/arch/ia64/include/asm/elf.h#L33
|
||||
EF_IA_64_LINUX_EXECUTABLE_STACK__:
|
||||
|
||||
.. code-block:: c
|
||||
.. __: https://github.com/torvalds/linux/blob/v6.3/arch/ia64/include/asm/elf.h#L33
|
||||
|
||||
#define EF_IA_64_LINUX_EXECUTABLE_STACK 0x1 /* is stack (& heap) executable by default? */
|
||||
.. code-block:: c
|
||||
|
||||
#define EF_IA_64_LINUX_EXECUTABLE_STACK 0x1 /* is stack (& heap) executable by default? */
|
||||
|
||||
.. [#the_rest]
|
||||
`source <https://github.com/torvalds/linux/blob/v6.3/include/linux/elf.h#L13>`__
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
# define elf_read_implies_exec(ex, have_pt_gnu_stack) 0
|
||||
# define elf_read_implies_exec(ex, have_pt_gnu_stack) 0
|
||||
"""
|
||||
if not self.executable:
|
||||
return True
|
||||
|
||||
+8
-1
@@ -65,7 +65,7 @@ def __ensure_memory_to_run_unicorn():
|
||||
from mmap import mmap, MAP_ANON, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE
|
||||
|
||||
mm = mmap(
|
||||
-1, 1024 * 1024 * 1024, MAP_PRIVATE | MAP_ANON, PROT_WRITE | PROT_READ | PROT_EXEC
|
||||
-1, 1024 * 1024 * 1024, MAP_PRIVATE | MAP_ANON, PROT_WRITE | PROT_READ
|
||||
)
|
||||
mm.close()
|
||||
except OSError:
|
||||
@@ -78,6 +78,13 @@ def __ensure_memory_to_run_unicorn():
|
||||
def prepare_unicorn_and_context(elf, got, address, data):
|
||||
import unicorn as U
|
||||
|
||||
if elf.arch in ('mips', 'mips64'):
|
||||
# Unicorn 2.1.3 and 2.1.4 have a crashing bug when trying to emulate MIPS.
|
||||
# https://github.com/unicorn-engine/unicorn/issues/2134
|
||||
if U.UC_VERSION_MAJOR == 2 and U.UC_VERSION_MINOR == 1 and U.UC_VERSION_PATCH in (3, 4):
|
||||
raise OSError("Unicorn Engine 2.1.3 and 2.1.4 have a crashing bug when emulating MIPS, please install an earlier or later version: https://github.com/unicorn-engine/unicorn/issues/2134")
|
||||
|
||||
|
||||
__ensure_memory_to_run_unicorn()
|
||||
|
||||
# Instantiate the emulator with the correct arguments for the current
|
||||
|
||||
+14
-2
@@ -156,6 +156,18 @@ class FileStructure(object):
|
||||
_wide_data: 0xdeadbeef
|
||||
unknown2: 0x0
|
||||
vtable: 0x0}
|
||||
|
||||
Bytes fields are padded to the correct field size.
|
||||
For example, unknown2 is 56 bytes on i386, so a short value gets
|
||||
zero-padded to 56, not to context.bytes (4):
|
||||
|
||||
>>> context.clear(arch='i386')
|
||||
>>> fileStr2 = FileStructure(null=0)
|
||||
>>> fileStr2.vtable = 0x561859f0
|
||||
>>> old_len = len(bytes(fileStr2))
|
||||
>>> fileStr2.unknown2 = b'AB'
|
||||
>>> len(bytes(fileStr2)) == old_len
|
||||
True
|
||||
"""
|
||||
|
||||
vars_=[]
|
||||
@@ -186,7 +198,7 @@ class FileStructure(object):
|
||||
structure = b''
|
||||
for val in self.vars_:
|
||||
if isinstance(getattr(self, val), bytes):
|
||||
structure += getattr(self, val).ljust(context.bytes, b'\x00')
|
||||
structure += getattr(self, val).ljust(self.length[val], b'\x00')
|
||||
else:
|
||||
if self.length[val] > 0:
|
||||
structure += pack(getattr(self, val), self.length[val]*8)
|
||||
@@ -215,7 +227,7 @@ class FileStructure(object):
|
||||
structure = b''
|
||||
for val in self.vars_:
|
||||
if isinstance(getattr(self, val), bytes):
|
||||
structure += getattr(self, val).ljust(context.bytes, b'\x00')
|
||||
structure += getattr(self, val).ljust(self.length[val], b'\x00')
|
||||
else:
|
||||
structure += pack(getattr(self, val), self.length[val]*8)
|
||||
if val == v:
|
||||
|
||||
+30
-24
@@ -143,7 +143,6 @@ from __future__ import division
|
||||
|
||||
import os
|
||||
import platform
|
||||
import psutil
|
||||
import random
|
||||
import re
|
||||
import six
|
||||
@@ -353,10 +352,6 @@ def _gdbserver_args(pid=None, path=None, port=0, gdbserver_args=None, args=None,
|
||||
gdbserver_args += ['--wrapper', python_wrapper_script, '--']
|
||||
elif env is not None:
|
||||
gdbserver_args += ['--wrapper', which('env'), '-i'] + env_args + ['--']
|
||||
# --no-startup-with-shell is required for forking shells like SHELL=/bin/fish
|
||||
# https://github.com/Gallopsled/pwntools/issues/2377
|
||||
else:
|
||||
gdbserver_args += ['--no-startup-with-shell']
|
||||
|
||||
gdbserver_args += ['localhost:%d' % port]
|
||||
gdbserver_args += args
|
||||
@@ -750,6 +745,12 @@ def binary():
|
||||
>>> gdb.binary() # doctest: +SKIP
|
||||
'/usr/bin/gdb'
|
||||
"""
|
||||
if context.gdb_binary:
|
||||
gdb = misc.which(context.gdb_binary)
|
||||
if not gdb:
|
||||
log.warn_once('Path to gdb binary `{}` not found'.format(context.gdb_binary))
|
||||
return gdb
|
||||
|
||||
gdb = misc.which('pwntools-gdb') or misc.which('gdb')
|
||||
|
||||
if not context.native:
|
||||
@@ -1520,28 +1521,33 @@ def corefile(process):
|
||||
'contain all of the data in the process.\n'
|
||||
'Upgrade to GDB >= 7.11 for better core-dumps.' % binary())
|
||||
|
||||
# This is effectively the same as what the 'gcore' binary does
|
||||
gdb_args = ['-batch',
|
||||
'-q',
|
||||
'-nx',
|
||||
'-ex', 'set pagination off',
|
||||
'-ex', 'set height 0',
|
||||
'-ex', 'set width 0',
|
||||
'-ex', 'set use-coredump-filter on',
|
||||
'-ex', 'generate-core-file %s' % corefile_path,
|
||||
'-ex', 'detach']
|
||||
# This is effectively the same as what the 'gcore' binary does.
|
||||
# Run GDB directly instead of going through attach(), which has to
|
||||
# race GDB to observe the tracer before it already detached again.
|
||||
cmd = [binary(),
|
||||
'-batch',
|
||||
'-q',
|
||||
'-nx',
|
||||
'-ex', 'set pagination off',
|
||||
'-ex', 'set height 0',
|
||||
'-ex', 'set width 0',
|
||||
'-ex', 'set use-coredump-filter on',
|
||||
'-ex', 'attach %d' % process.pid,
|
||||
'-ex', 'generate-core-file %s' % corefile_path,
|
||||
'-ex', 'detach']
|
||||
|
||||
with context.local(terminal = ['sh', '-c']):
|
||||
with context.quiet:
|
||||
pid = attach(process, gdb_args=gdb_args)
|
||||
log.debug("Got GDB pid %d", pid)
|
||||
try:
|
||||
psutil.Process(pid).wait()
|
||||
except psutil.Error:
|
||||
pass
|
||||
with context.quiet:
|
||||
gdb = tubes.process.process(cmd, stdout=tubes.process.PIPE)
|
||||
gdb.wait_for_close(timeout=60)
|
||||
timed_out = gdb.poll() is None
|
||||
output = gdb.recvallS(timeout=0)
|
||||
|
||||
if timed_out:
|
||||
log.error("GDB did not finish dumping process %d within 60 seconds", process.pid)
|
||||
|
||||
if not os.path.exists(corefile_path):
|
||||
log.error("Could not generate a corefile for process %d", process.pid)
|
||||
log.error("Could not generate a corefile for process %d (maybe check /proc/sys/kernel/yama/ptrace_scope)\n%s",
|
||||
process.pid, output)
|
||||
|
||||
return elf.corefile.Core(corefile_path)
|
||||
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ class PwntoolsLexer(RegexLexer):
|
||||
'whitespace': [
|
||||
(r'\n', Text),
|
||||
(r'\s+', Text),
|
||||
(r'/\*.*?\*/', Comment),
|
||||
(r'(?ms)/\*.*?\*/', Comment),
|
||||
(r';.*$', Comment)
|
||||
],
|
||||
'punctuation': [
|
||||
|
||||
+44
-55
@@ -9,6 +9,7 @@ import time
|
||||
import six
|
||||
import tempfile
|
||||
import struct
|
||||
import sys
|
||||
|
||||
from pwnlib.context import context
|
||||
from pwnlib.elf import ELF
|
||||
@@ -65,6 +66,8 @@ MAP_TYPES = {
|
||||
}
|
||||
|
||||
DEBUGINFOD_SERVERS = [
|
||||
'https://debuginfod.ubuntu.com/',
|
||||
'https://debuginfod.debian.net/',
|
||||
'https://debuginfod.elfutils.org/',
|
||||
]
|
||||
|
||||
@@ -293,49 +296,6 @@ def search_by_hash(search_target, search_type='build_id', unstrip=True, offline_
|
||||
|
||||
return cache
|
||||
|
||||
def _search_debuginfo_by_hash(base_url, hex_encoded_id):
|
||||
"""
|
||||
Given a hex-encoded build_id, attempt to download a matching debuginfo from the debuginfod server.
|
||||
|
||||
>>> debuginfo_file = _search_debuginfo_by_hash(DEBUGINFOD_SERVERS[0], 'd1704d25fbbb72fa95d517b883131828c0883fe9')
|
||||
>>> debuginfo_file is not None
|
||||
True
|
||||
>>> 'main_arena' in ELF(debuginfo_file).symbols
|
||||
True
|
||||
"""
|
||||
# Deferred import because it's slow
|
||||
import requests
|
||||
from six.moves import urllib
|
||||
|
||||
# Check if we tried this buildid before.
|
||||
cache, cache_valid = _check_elf_cache('libcdb_dbg', hex_encoded_id, 'build_id')
|
||||
if cache_valid:
|
||||
return cache
|
||||
|
||||
# We searched for this buildid before, but didn't find anything.
|
||||
if cache is None:
|
||||
return None
|
||||
|
||||
# Try to find separate debuginfo.
|
||||
url = '/buildid/{}/debuginfo'.format(hex_encoded_id)
|
||||
url = urllib.parse.urljoin(base_url, url)
|
||||
data = b""
|
||||
log.debug("Downloading data from debuginfod: %s", url)
|
||||
try:
|
||||
data = wget(url, timeout=20)
|
||||
except requests.RequestException as e:
|
||||
log.warn_once("Failed to fetch libc debuginfo for build_id %s from %s: %s", hex_encoded_id, base_url, e)
|
||||
|
||||
# Save whatever we got to the cache
|
||||
write(cache, data or b'')
|
||||
|
||||
# Return ``None`` if we did not get a valid ELF file
|
||||
if not data or not data.startswith(b'\x7FELF'):
|
||||
log.warn_once("Could not fetch libc debuginfo for build_id %s from %s", hex_encoded_id, base_url)
|
||||
return None
|
||||
|
||||
return cache
|
||||
|
||||
def _check_elf_cache(cache_type, search_target, search_type):
|
||||
"""
|
||||
Check if there already is an ELF file for this hash in the cache.
|
||||
@@ -394,15 +354,15 @@ def unstrip_libc(filename):
|
||||
>>> libc = ELF(filename)
|
||||
>>> 'main_arena' in libc.symbols
|
||||
False
|
||||
>>> unstrip_libc(filename)
|
||||
>>> unstrip_libc(filename) # doctest: +SKIP
|
||||
True
|
||||
>>> libc = ELF(filename)
|
||||
>>> hex(libc.symbols.main_arena)
|
||||
>>> hex(libc.symbols.main_arena) # doctest: +SKIP
|
||||
'0x219c80'
|
||||
>>> unstrip_libc(pwnlib.data.elf.get('test-x86'))
|
||||
False
|
||||
>>> filename = search_by_build_id('d1704d25fbbb72fa95d517b883131828c0883fe9', unstrip=True)
|
||||
>>> 'main_arena' in ELF(filename).symbols
|
||||
>>> 'main_arena' in ELF(filename).symbols # doctest: +SKIP
|
||||
True
|
||||
"""
|
||||
if not which('eu-unstrip'):
|
||||
@@ -420,16 +380,45 @@ def unstrip_libc(filename):
|
||||
|
||||
log.debug('Trying debuginfod servers: %r', DEBUGINFOD_SERVERS)
|
||||
|
||||
for server_url in DEBUGINFOD_SERVERS:
|
||||
libc_dbg = _search_debuginfo_by_hash(server_url, enhex(libc.buildid))
|
||||
if libc_dbg:
|
||||
break
|
||||
else:
|
||||
log.warn_once('Couldn\'t find debug info for libc with build_id %s on any debuginfod server.', enhex(libc.buildid))
|
||||
return False
|
||||
# Deferred import because it's slow
|
||||
import requests
|
||||
from six.moves import urllib
|
||||
|
||||
hex_encoded_id = enhex(libc.buildid)
|
||||
|
||||
# Check if we tried this buildid before.
|
||||
cache, cache_valid = _check_elf_cache('libcdb_dbg', hex_encoded_id, 'build_id')
|
||||
if not cache_valid:
|
||||
# Cached negative result so we don't look for missing debug info everytime.
|
||||
if cache is None:
|
||||
return False
|
||||
else:
|
||||
for server_url in DEBUGINFOD_SERVERS:
|
||||
# Try to find separate debuginfo.
|
||||
url = '/buildid/{}/debuginfo'.format(hex_encoded_id)
|
||||
url = urllib.parse.urljoin(server_url, url)
|
||||
data = b""
|
||||
log.debug("Downloading data from debuginfod: %s", url)
|
||||
try:
|
||||
data = wget(url, timeout=20)
|
||||
|
||||
# Try next server if we didn't get a valid ELF file
|
||||
if not data or not data.startswith(b'\x7FELF'):
|
||||
log.warn_once("Could not fetch libc debuginfo for build_id %s from %s", hex_encoded_id, server_url)
|
||||
continue
|
||||
break
|
||||
except requests.RequestException as e:
|
||||
log.warn_once("Failed to fetch libc debuginfo for build_id %s from %s: %s", hex_encoded_id, server_url, e)
|
||||
else:
|
||||
write(cache, data or b'')
|
||||
log.warn_once('Couldn\'t find debug info for libc with build_id %s on any debuginfod server.', enhex(libc.buildid))
|
||||
return False
|
||||
|
||||
# Save whatever we got to the cache
|
||||
write(cache, data or b'')
|
||||
|
||||
# Add debug info to given libc binary inplace.
|
||||
p = process(['eu-unstrip', '-o', filename, filename, libc_dbg])
|
||||
p = process(['eu-unstrip', '-o', filename, filename, cache])
|
||||
output = p.recvall()
|
||||
p.close()
|
||||
|
||||
@@ -498,7 +487,7 @@ def _extract_tarfile(cache_dir, data_filename, tarball):
|
||||
|
||||
def _extract_debfile(cache_dir, package_filename, package):
|
||||
# Extract data.tar in the .deb archive.
|
||||
if six.PY2:
|
||||
if sys.version_info < (3, 6):
|
||||
if not which('ar'):
|
||||
log.error('Missing command line tool "ar" to extract .deb archive. Please install "ar" first.')
|
||||
|
||||
|
||||
+39
-15
@@ -15,7 +15,7 @@ from pwnlib.log import getLogger
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
def check_cycle(reg, assignments):
|
||||
def check_cycle(reg, assignments, mapping=None):
|
||||
"""Walk down the assignment list of a register,
|
||||
return the path walked if it is encountered again.
|
||||
|
||||
@@ -37,31 +37,38 @@ def check_cycle(reg, assignments):
|
||||
>>> check_cycle('a', {'a': 'b', 'b': 'c', 'c': 'd', 'd': 'a'})
|
||||
['a', 'b', 'c', 'd']
|
||||
"""
|
||||
return check_cycle_(reg, assignments, [])
|
||||
if mapping is None:
|
||||
mapping = {}
|
||||
return check_cycle_(reg, assignments, [], mapping)
|
||||
|
||||
def check_cycle_(reg, assignments, path):
|
||||
target = assignments[reg]
|
||||
def check_cycle_(reg, assignments, path, mapping):
|
||||
target = assignments.get(reg)
|
||||
if target is None:
|
||||
real_reg = mapping.get(reg, reg)
|
||||
reg, target = next((k, v) for k,v in assignments.items() if mapping.get(k, k) == real_reg)
|
||||
path.append(reg)
|
||||
|
||||
real_target = mapping.get(target, target)
|
||||
|
||||
# No cycle, some other value (e.g. 1)
|
||||
if target not in assignments:
|
||||
if all(real_target != mapping.get(k, k) for k in assignments):
|
||||
return []
|
||||
|
||||
# Found a cycle
|
||||
if target in path:
|
||||
if any(real_target == mapping.get(k, k) for k in path):
|
||||
# Does the cycle *start* with target?
|
||||
# This determines whether the original register is
|
||||
# in the cycle, or just depends on registers in one.
|
||||
if target == path[0]:
|
||||
if real_target == mapping.get(path[0], path[0]):
|
||||
return path
|
||||
|
||||
# Just depends on one.
|
||||
return []
|
||||
|
||||
# Recurse
|
||||
return check_cycle_(target, assignments, path)
|
||||
return check_cycle_(target, assignments, path, mapping)
|
||||
|
||||
def extract_dependencies(reg, assignments):
|
||||
def extract_dependencies(reg, assignments, mapping=None):
|
||||
"""Return a list of all registers which directly
|
||||
depend on the specified register.
|
||||
|
||||
@@ -77,7 +84,9 @@ def extract_dependencies(reg, assignments):
|
||||
['b', 'c']
|
||||
"""
|
||||
# sorted() is only for determinism
|
||||
return sorted([k for k,v in assignments.items() if v == reg])
|
||||
if mapping is None:
|
||||
mapping = {}
|
||||
return sorted([k for k,v in assignments.items() if mapping.get(v, v) == mapping.get(reg, reg)])
|
||||
|
||||
|
||||
def resolve_order(reg, deps):
|
||||
@@ -110,7 +119,7 @@ def depends_on_cycle(reg, assignments, in_cycles):
|
||||
reg = assignments.get(reg, None)
|
||||
return False
|
||||
|
||||
def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
|
||||
def regsort(in_out, all_regs, mapping = None, tmp = None, xchg = True, randomize = None):
|
||||
"""
|
||||
Sorts register dependencies.
|
||||
|
||||
@@ -233,6 +242,12 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
|
||||
if randomize is None:
|
||||
randomize = context.randomize
|
||||
|
||||
if mapping is None:
|
||||
if hasattr(all_regs, 'keys'):
|
||||
mapping = all_regs
|
||||
else:
|
||||
mapping = {}
|
||||
|
||||
sentinel = object()
|
||||
|
||||
# Drop all registers which will be set to themselves.
|
||||
@@ -242,7 +257,7 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
|
||||
|
||||
# Collapse constant values
|
||||
#
|
||||
# For eaxmple, {'eax': 0, 'ebx': 0} => {'eax': 0, 'ebx': 'eax'}
|
||||
# For example, {'eax': 0, 'ebx': 0} => {'eax': 0, 'ebx': 'eax'}
|
||||
v_k = defaultdict(list)
|
||||
for k,v in sorted(in_out.items()):
|
||||
if v not in all_regs and v != 0:
|
||||
@@ -263,7 +278,9 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
|
||||
# which are also 'outputs'.
|
||||
#
|
||||
# For example, {'eax': 1, 'ebx': 2, 'ecx': 'edx'}
|
||||
if not any(v in in_out for k,v in in_out.items()):
|
||||
inps = {mapping.get(k, k) for k in in_out}
|
||||
outs = {mapping.get(v, v) for v in in_out.values()}
|
||||
if not inps & outs:
|
||||
result = [('mov', k,in_out[k]) for k in sorted(in_out)]
|
||||
|
||||
if randomize:
|
||||
@@ -280,7 +297,7 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
|
||||
# Output: {'A': [], 'B': ['A', 'C'], 'C': []}
|
||||
#
|
||||
# In this case, both A and C must be set before B.
|
||||
deps = {r: extract_dependencies(r, in_out) for r in in_out}
|
||||
deps = {r: extract_dependencies(r, in_out, mapping) for r in in_out}
|
||||
|
||||
# Final result which will be returned
|
||||
result = []
|
||||
@@ -299,7 +316,7 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
|
||||
|
||||
while cycle_candidates:
|
||||
reg = cycle_candidates[0]
|
||||
cycle = check_cycle(reg, in_out)
|
||||
cycle = check_cycle(reg, in_out, mapping)
|
||||
|
||||
if cycle:
|
||||
if randomize:
|
||||
@@ -395,6 +412,10 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
|
||||
|
||||
if tmp:
|
||||
for cycle in cycles:
|
||||
if len(cycle) == 1:
|
||||
[reg] = cycle
|
||||
result.append(('mov', reg, in_out[reg]))
|
||||
continue
|
||||
|
||||
first = cycle[0]
|
||||
last = cycle[-1]
|
||||
@@ -411,6 +432,9 @@ def regsort(in_out, all_regs, tmp = None, xchg = True, randomize = None):
|
||||
else:
|
||||
for cycle in cycles:
|
||||
size = len(cycle)
|
||||
if size == 1:
|
||||
[reg] = cycle
|
||||
result.append(('mov', reg, in_out[reg]))
|
||||
for i in range(size-1):
|
||||
result.append(('xchg', cycle[i], cycle[(i+1) % size]))
|
||||
|
||||
|
||||
+1
-1
@@ -841,7 +841,7 @@ class ROP(object):
|
||||
"""
|
||||
if isinstance(object, enums):
|
||||
return str(object)
|
||||
if isinstance(object, six.integer_types):
|
||||
if isinstance(object, six.integer_types) and object:
|
||||
return self.unresolve(object)
|
||||
if isinstance(object, (bytes, six.text_type)):
|
||||
return repr(object)
|
||||
|
||||
@@ -116,10 +116,10 @@ i386_ordered = [
|
||||
['rbx', 'ebx', 'bx', 'bl'],
|
||||
['rcx', 'ecx', 'cx', 'cl'],
|
||||
['rdx', 'edx', 'dx', 'dl'],
|
||||
['rdi', 'edi', 'di'],
|
||||
['rsi', 'esi', 'si'],
|
||||
['rbp', 'ebp', 'bp'],
|
||||
['rsp', 'esp', 'sp'],
|
||||
['rdi', 'edi', 'di', 'dil'],
|
||||
['rsi', 'esi', 'si', 'sil'],
|
||||
['rbp', 'ebp', 'bp', 'bpl'],
|
||||
['rsp', 'esp', 'sp', 'spl'],
|
||||
['r8', 'r8d', 'r8w', 'r8b'],
|
||||
['r9', 'r9d', 'r9w', 'r9b'],
|
||||
['r10', 'r10d', 'r10w', 'r10b'],
|
||||
|
||||
@@ -16,4 +16,4 @@ ${memcpy_loop}:
|
||||
ldrb w3, [x1], #1
|
||||
strb w3, [x0], #1
|
||||
subs x2, x2, #1
|
||||
bge ${memcpy_loop}
|
||||
bgt ${memcpy_loop}
|
||||
@@ -51,8 +51,8 @@ Example:
|
||||
xor eax, eax
|
||||
mov ax, 0xc0c0
|
||||
>>> print(shellcraft.amd64.mov('rdi', 0xff).rstrip())
|
||||
mov edi, 0x1010101 /* 255 == 0xff */
|
||||
xor edi, 0x10101fe
|
||||
xor edi, edi
|
||||
mov dil, 0xff
|
||||
>>> print(shellcraft.amd64.mov('rax', 0xdead00ff).rstrip())
|
||||
mov eax, 0x1010101 /* 3735879935 == 0xdead00ff */
|
||||
xor eax, 0xdfac01fe
|
||||
|
||||
@@ -55,7 +55,7 @@ if isinstance(eax, six.integer_types) and isinstance(edx, six.integer_types) and
|
||||
cdq = True
|
||||
reg_context.pop('rdx')
|
||||
|
||||
sorted_regs = regsort(reg_context, registers.amd64)
|
||||
sorted_regs = regsort(reg_context, registers.amd64, registers.native64)
|
||||
%>
|
||||
% if not sorted_regs:
|
||||
/* setregs noop */
|
||||
|
||||
@@ -1,24 +1,30 @@
|
||||
<%
|
||||
from pwnlib.shellcraft import amd64, pretty
|
||||
from pwnlib.shellcraft import amd64, common, pretty
|
||||
from pwnlib.util.packing import u64, _need_bytes
|
||||
%>
|
||||
<%docstring>Find the address of an exported function in a dll
|
||||
by manually iterating through the PE export table.
|
||||
|
||||
The dll must be `b'kernel32.dll'` or `b'ntdll.dll'` at the moment.
|
||||
Behavior is undefined if the function is not found.
|
||||
|
||||
Args:
|
||||
function_name (str): The name of the function to find.
|
||||
dll(str): The name of the DLL to find the function in.
|
||||
function_name (bytes): The name of the function to find.
|
||||
dll(bytes): The name of the DLL to find the function in.
|
||||
dest (str): The register to load the function address into.
|
||||
</%docstring>
|
||||
<%page args="function_name,dll='kernel32.dll',dest='rax'"/>
|
||||
<%page args="function_name,dll=b'kernel32.dll',dest='rax'"/>
|
||||
<%
|
||||
function_name = _need_bytes(function_name)
|
||||
dll = _need_bytes(dll)
|
||||
if len(function_name) > 8:
|
||||
raise ValueError('function_name must be <= 8 bytes')
|
||||
assert dll == b'kernel32.dll'
|
||||
assert dll in (b'kernel32.dll', b'ntdll.dll')
|
||||
find_function_loop = common.label('find_function').strip()
|
||||
%>
|
||||
% if dll == b'kernel32.dll':
|
||||
${amd64.windows.kernel32base('rbx')} /* rbx = kernel32.dll PE base */
|
||||
% elif dll == b'ntdll.dll':
|
||||
${amd64.windows.ntdllbase('rbx')} /* rbx = ntdll.dll PE base */
|
||||
% endif
|
||||
mov r8d, [rbx + 0x3c]
|
||||
mov rdx, r8
|
||||
add rdx, rbx
|
||||
@@ -26,25 +32,42 @@ assert dll == b'kernel32.dll'
|
||||
add rdx, r9
|
||||
mov r8d, [rdx]
|
||||
add r8, rbx /* r8 = export table */
|
||||
mov esi, [r8 + 0x20]
|
||||
add rsi, rbx /* rsi = names table */
|
||||
xor rcx, rcx
|
||||
mov edx, [r8 + 0x20]
|
||||
add rdx, rbx /* rdx = names table */
|
||||
% if len(function_name) <= 8:
|
||||
mov r9, ${pretty(u64(function_name.ljust(8, b'\x00')))}
|
||||
% else:
|
||||
${amd64.pushstr(function_name)}
|
||||
mov r9, rsp
|
||||
% endif
|
||||
push r8
|
||||
xor r8, r8
|
||||
|
||||
/* Loop through the names table */
|
||||
FindFunction:
|
||||
inc rcx
|
||||
mov eax, [rsi + rcx * 4]
|
||||
${find_function_loop}:
|
||||
inc r8
|
||||
mov eax, [rdx + r8 * 4]
|
||||
add rax, rbx
|
||||
## TODO: implement strcmp properly for function names > 8 bytes
|
||||
## strcmp
|
||||
% if len(function_name) <= 8:
|
||||
cmp qword ptr [rax], r9
|
||||
jnz FindFunction
|
||||
% else:
|
||||
${amd64.setregs({
|
||||
'rdi': 'rax',
|
||||
'rsi': 'r9',
|
||||
'rcx': len(function_name),
|
||||
})}
|
||||
repe cmpsb
|
||||
movzx rax, byte ptr [rsi-1]
|
||||
movzx rcx, byte ptr [rdi-1]
|
||||
sub rax, rcx
|
||||
% endif
|
||||
jnz ${find_function_loop}
|
||||
|
||||
## Assume we find the function, we'll crash walking past the
|
||||
## end of the names table otherwise.
|
||||
mov rcx, r8
|
||||
pop r8
|
||||
mov esi, [r8 + 0x24]
|
||||
add rsi, rbx /* rsi = ordinals table */
|
||||
mov cx, [rsi + rcx * 2]
|
||||
@@ -52,4 +75,4 @@ assert dll == b'kernel32.dll'
|
||||
add rsi, rbx /* rsi = address table */
|
||||
mov eax, [rsi + rcx * 4]
|
||||
add rax, rbx /* rax = function address */
|
||||
mov ${dest}, rax
|
||||
${amd64.mov(dest, 'rax')}
|
||||
@@ -11,7 +11,7 @@ Args:
|
||||
dll(str): The name of the DLL to find the function in.
|
||||
dest (str): The register to load the function address into.
|
||||
</%docstring>
|
||||
<%page args="function_name,dll='kernel32.dll',dest='rax'"/>
|
||||
<%page args="function_name,dll=b'kernel32.dll',dest='rax'"/>
|
||||
<%
|
||||
function_name = _need_bytes(function_name)
|
||||
dll = _need_bytes(dll)
|
||||
@@ -25,4 +25,4 @@ assert dll == b'kernel32.dll'
|
||||
sub rsp, 0x30
|
||||
call rdi
|
||||
add rsp, ${pretty(0x30+align(8, len(function_name)))}
|
||||
mov ${dest}, rax
|
||||
${amd64.mov(dest, 'rax')}
|
||||
@@ -16,4 +16,4 @@ ${memcpy_loop}:
|
||||
ldrb r3, [r1], #1
|
||||
strb r3, [r0], #1
|
||||
subs r2, r2, #1
|
||||
bge ${memcpy_loop}
|
||||
bgt ${memcpy_loop}
|
||||
@@ -58,12 +58,9 @@ Example:
|
||||
>>> print(shellcraft.i386.mov('eax', 0xdead00ff).rstrip())
|
||||
mov eax, -0xdead00ff
|
||||
neg eax
|
||||
>>> print(shellcraft.i386.mov('eax', 0xc0).rstrip())
|
||||
xor eax, eax
|
||||
mov al, 0xc0
|
||||
>>> print(shellcraft.i386.mov('edi', 0xc0).rstrip())
|
||||
mov edi, -0xc0
|
||||
neg edi
|
||||
xor edi, edi
|
||||
mov dil, 0xc0
|
||||
>>> print(shellcraft.i386.mov('eax', 0xc000).rstrip())
|
||||
xor eax, eax
|
||||
mov ah, 0xc000 >> 8
|
||||
|
||||
@@ -54,7 +54,7 @@ sorted_regs = regsort(reg_context, registers.i386)
|
||||
% if not sorted_regs:
|
||||
/* setregs noop */
|
||||
% else:
|
||||
% for how, src, dst in regsort(reg_context, registers.i386):
|
||||
% for how, src, dst in regsort(reg_context, registers.i386, registers.native32):
|
||||
% if how == 'xchg':
|
||||
xchg ${src}, ${dst}
|
||||
% else:
|
||||
|
||||
@@ -16,4 +16,4 @@ ${memcpy_loop}:
|
||||
ldrb r3, [r1], #1
|
||||
strb r3, [r4], #1
|
||||
subs r2, r2, #1
|
||||
bge ${memcpy_loop}
|
||||
bgt ${memcpy_loop}
|
||||
@@ -380,7 +380,7 @@ def readline(_size=-1, prompt='', float=True, priority=10):
|
||||
from pwnlib.term import term_mode
|
||||
if not term_mode:
|
||||
six.print_(prompt, end='', flush=True)
|
||||
return getattr(sys.stdin, 'buffer', sys.stdin).readline(_size).rstrip(b'\n')
|
||||
return force_to_bytes(getattr(sys.stdin, 'buffer', sys.stdin).readline(_size)).rstrip(b'\n')
|
||||
show_suggestions = False
|
||||
eof = False
|
||||
if prompt:
|
||||
|
||||
+13
-14
@@ -1259,6 +1259,9 @@ class process(tube):
|
||||
|
||||
>>> from pwn import *
|
||||
>>> p = process(['cat'])
|
||||
>>> p.send(b'meow')
|
||||
>>> p.recvuntil(b'meow')
|
||||
b'meow'
|
||||
>>> libc_size = p.lib_size(p.libc.path)
|
||||
>>> hex(libc_size) # doctest: +SKIP
|
||||
'0x1d5000'
|
||||
@@ -1325,8 +1328,7 @@ class process(tube):
|
||||
by the process to the address it is loaded at in the process' address
|
||||
space.
|
||||
"""
|
||||
from pwnlib.util.proc import memory_maps
|
||||
maps_raw = memory_maps(self.pid)
|
||||
maps_raw = self.poll() is None and self.maps()
|
||||
|
||||
if not maps_raw:
|
||||
import pwnlib.elf.elf
|
||||
@@ -1335,23 +1337,15 @@ class process(tube):
|
||||
return pwnlib.elf.elf.ELF(self.executable).maps
|
||||
|
||||
# Enumerate all of the libraries actually loaded right now.
|
||||
maps = {}
|
||||
libs = {}
|
||||
for mapping in maps_raw:
|
||||
path = mapping.path
|
||||
if os.sep not in path: continue
|
||||
path = os.path.realpath(path)
|
||||
if path not in maps:
|
||||
maps[path]=0
|
||||
if path not in libs:
|
||||
libs[path] = mapping.addr
|
||||
|
||||
for lib in maps:
|
||||
path = os.path.realpath(lib)
|
||||
for mapping in maps_raw:
|
||||
if mapping.path == path:
|
||||
address = mapping.addr.split('-')[0]
|
||||
maps[lib] = int(address, 16)
|
||||
break
|
||||
|
||||
return maps
|
||||
return libs
|
||||
|
||||
@property
|
||||
def libc(self):
|
||||
@@ -1364,7 +1358,12 @@ class process(tube):
|
||||
Example:
|
||||
|
||||
>>> p = process("/bin/cat")
|
||||
>>> p.send(b"meow")
|
||||
>>> p.recvuntil(b"meow")
|
||||
b'meow'
|
||||
>>> libc = p.libc
|
||||
>>> libc is not None
|
||||
True
|
||||
>>> libc # doctest: +SKIP
|
||||
ELF('/lib64/libc-...so')
|
||||
>>> p.close()
|
||||
|
||||
@@ -127,7 +127,7 @@ class server(sock):
|
||||
return
|
||||
|
||||
self.rhost, self.rport = rhost[:2]
|
||||
r = remote(self.rhost, self.rport, sock = sock)
|
||||
r = remote(self.rhost, self.rport, sock = sock, level = self.level)
|
||||
h.success('Got connection from %s on port %d' % (self.rhost, self.rport))
|
||||
if callback:
|
||||
if not blocking:
|
||||
|
||||
+10
-2
@@ -1359,7 +1359,11 @@ from ctypes import *; libc = CDLL('libc.so.6'); print(libc.getenv(%r))
|
||||
update(len(data), total)
|
||||
|
||||
result = c.wait()
|
||||
if result != 0:
|
||||
|
||||
if result == -1:
|
||||
self.warn_once("Could not verify success of file download %r, no error code" % (remote))
|
||||
|
||||
if result != 0 and result != -1:
|
||||
h.failure('Could not download file %r (%r)' % (remote, result))
|
||||
return
|
||||
|
||||
@@ -1539,7 +1543,11 @@ from ctypes import *; libc = CDLL('libc.so.6'); print(libc.getenv(%r))
|
||||
s.shutdown('send')
|
||||
data = s.recvall()
|
||||
result = s.wait()
|
||||
if result != 0:
|
||||
|
||||
if result == -1:
|
||||
self.warn_once("Could not verify success of file upload %r, no error code" % (remote))
|
||||
|
||||
if result != 0 and result != -1:
|
||||
self.error("Could not upload file %r (%r)\n%s" % (remote, result, data))
|
||||
|
||||
def upload_file(self, filename, remote = None):
|
||||
|
||||
+11
-1
@@ -988,6 +988,11 @@ def dd(dst, src, count = 0, skip = 0, seek = 0, truncate = False):
|
||||
('H', 'e', 'l', 'l', 'o', b'?')
|
||||
>>> dd(list('Hello!'), (63,), skip = 5)
|
||||
['H', 'e', 'l', 'l', 'o', b'?']
|
||||
>>> buf = bytearray(b'Hello!')
|
||||
>>> dd(buf, b'?', skip = 5)
|
||||
bytearray(b'Hello?')
|
||||
>>> buf
|
||||
bytearray(b'Hello?')
|
||||
>>> _ = open('/tmp/foo', 'w').write('A' * 10)
|
||||
>>> dd(open('/tmp/foo'), open('/dev/zero'), skip = 3, count = 4).read()
|
||||
'AAA\\x00\\x00\\x00\\x00AAA'
|
||||
@@ -1105,7 +1110,12 @@ def dd(dst, src, count = 0, skip = 0, seek = 0, truncate = False):
|
||||
dst = real_dst
|
||||
|
||||
elif isinstance(dst, (list, bytearray)):
|
||||
dst[skip : skip + len(src)] = list(map(p8, bytearray(src)))
|
||||
# A bytearray slice takes a byte string, while a list slice takes the
|
||||
# single-byte bytes objects that p8() returns.
|
||||
if isinstance(dst, bytearray):
|
||||
dst[skip : skip + len(src)] = src
|
||||
else:
|
||||
dst[skip : skip + len(src)] = list(map(p8, bytearray(src)))
|
||||
if truncate:
|
||||
while len(dst) > truncate:
|
||||
dst.pop()
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
__version__ = '4.15.0beta0'
|
||||
__version__ = '4.15.0'
|
||||
+1
-1
@@ -56,7 +56,7 @@ dependencies = [
|
||||
"colored_traceback<0.4; python_version < '3'",
|
||||
"colored_traceback; python_version >= '3'",
|
||||
"pathlib2; python_version < '3.4'",
|
||||
"unix-ar; python_version >= '3'",
|
||||
"unix-ar; python_version >= '3.6'",
|
||||
"zstandard",
|
||||
]
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ if not os.path.exists(PythonH):
|
||||
sys.exit(-1)
|
||||
|
||||
setup(
|
||||
version = '4.15.0beta0',
|
||||
version = '4.15.0',
|
||||
data_files = [('pwntools-doc',
|
||||
glob.glob('*.md') + glob.glob('*.txt')),
|
||||
],
|
||||
|
||||
@@ -15,6 +15,7 @@ http {
|
||||
|
||||
location / {
|
||||
proxy_set_header Host debuginfod.elfutils.org;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_cache_revalidate on;
|
||||
proxy_cache_key $scheme://$host$uri$is_args$query_string;
|
||||
proxy_cache_valid 200 404 12w;
|
||||
@@ -29,6 +30,7 @@ http {
|
||||
|
||||
location / {
|
||||
proxy_set_header Host libc.rip;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_cache_methods GET HEAD POST;
|
||||
proxy_cache_revalidate on;
|
||||
proxy_cache_key $scheme://$host$uri$is_args$query_string$request_body;
|
||||
@@ -66,4 +68,19 @@ http {
|
||||
proxy_pass https://gitlab.com/;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 3004;
|
||||
proxy_cache my_cache;
|
||||
|
||||
location / {
|
||||
proxy_set_header Host debuginfod.ubuntu.com;
|
||||
proxy_ssl_server_name on;
|
||||
proxy_cache_revalidate on;
|
||||
proxy_cache_key $scheme://$host$uri$is_args$query_string;
|
||||
proxy_cache_valid 200 404 12w;
|
||||
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504 http_429;
|
||||
proxy_pass https://debuginfod.ubuntu.com/;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user