From daf032326da3e3f85372f60cf554b85a2e3afeaa Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Wed, 1 Oct 2025 22:50:13 +0200 Subject: [PATCH 1/4] GitHub Actions: Test on Python 3.14 Python v3.14 -- October 7th * https://www.python.org/download/pre-releases * https://www.python.org/downloads/release/python-3140rc3 * https://github.com/actions/setup-python/blob/main/docs/advanced-usage.md#allow-pre-releases --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 98ff13787..5708bbc40 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ jobs: test: strategy: matrix: - python_version: ['3.10', '3.12', '3.13'] + python_version: ['3.10', '3.12', '3.13', '3.14'] os: [ubuntu-latest] runs-on: ${{ matrix.os }} timeout-minutes: 30 From d8d39a230ed87db0f1d97b489d4eb998a6cad657 Mon Sep 17 00:00:00 2001 From: Peace-Maker Date: Tue, 14 Oct 2025 00:30:05 +0200 Subject: [PATCH 2/4] Allow new LOAD_SMALL_INT opcode in safeeval --- pwnlib/util/safeeval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pwnlib/util/safeeval.py b/pwnlib/util/safeeval.py index 6819c1546..7ff3f7d6d 100644 --- a/pwnlib/util/safeeval.py +++ b/pwnlib/util/safeeval.py @@ -4,7 +4,7 @@ 'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP', 'BUILD_LIST','BUILD_MAP', 'MAP_ADD', 'BUILD_TUPLE','BUILD_SET', 'BUILD_CONST_KEY_MAP', 'BUILD_STRING', - 'LOAD_CONST','RETURN_VALUE','STORE_SUBSCR', 'STORE_MAP', + 'LOAD_CONST','LOAD_SMALL_INT','RETURN_VALUE','STORE_SUBSCR', 'STORE_MAP', 'LIST_TO_TUPLE', 'LIST_EXTEND', 'SET_UPDATE', 'DICT_UPDATE', 'DICT_MERGE', 'COPY', 'RESUME', 'RETURN_CONST' ] From ed42a6d9a90ef9a6c4854f8b86cbda8b329b8bd0 Mon Sep 17 00:00:00 2001 From: Arusekk Date: Tue, 21 Oct 2025 11:34:06 +0200 Subject: [PATCH 3/4] Support lambdas in mbruteforce --- pwnlib/util/iters.py | 53 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/pwnlib/util/iters.py b/pwnlib/util/iters.py index c64f41120..629e280da 100644 --- a/pwnlib/util/iters.py +++ b/pwnlib/util/iters.py @@ -6,10 +6,12 @@ import collections import copy +import marshal import multiprocessing import operator import random import time +import types from itertools import * from pwnlib.context import context @@ -873,6 +875,54 @@ def _mbruteforcewrap(func, alphabet, length, method, start, databag): databag["result"] = res +class CustomPickler: + def __init__(self, *reduction): + self.reduction = reduction + + def __reduce__(self): + return self.reduction + + def __repr__(self): + return self.__class__.__name__ + repr(self.reduction) + + +def construct_func(*args): + return types.FunctionType(*args) + + +class PicklableFunc: + def __init__(self, f): + self.f = f + + def __call__(self, *a, **kw): + return self.f(*a, **kw) + + def __getattr__(self, a): + return getattr(self.f, a) + + def __reduce__(self): + # Constructor types.FunctionType tries to be __main__.function; + # globs could likewise be .__dict__. Neither + # type('FunctionType', (), {'__module__':'types'}) + # nor + # def __reduce__(self): return "FunctionType" + # works because of checks for identity :( + globs = CustomPickler(vars, (CustomPickler(__import__, (self.f.__module__ or '__main__',)),)) + return type(self), (CustomPickler( + construct_func, # ideally self.f.__class__ + # but types.FunctionType tries to be __main__.function + ( + CustomPickler(marshal.loads, (marshal.dumps(self.f.__code__),)), + globs, + self.f.__name__, + self.f.__defaults__, + self.f.__closure__, + self.f.__kwdefaults__, + ), + self.f.__dict__, + ),) + + def mbruteforce(func, alphabet, length, method = 'upto', start = None, threads = None): """mbruteforce(func, alphabet, length, method = 'upto', start = None, threads = None) @@ -913,6 +963,9 @@ def mbruteforce(func, alphabet, length, method = 'upto', start = None, threads = (i2, N2) = start totalchunks = threads * N2 + if isinstance(func, types.FunctionType): + func = PicklableFunc(func) + for i in range(threads): shareddata[i] = multiprocessing.Manager().dict() shareddata[i]['result'] = None From 44e5e66594c2befc416df3ae84c6d0be54fec4ee Mon Sep 17 00:00:00 2001 From: Arusekk Date: Thu, 6 Nov 2025 08:42:55 +0100 Subject: [PATCH 4/4] Drop pwnlib.util.iters.lookahead --- CHANGELOG.md | 2 ++ pwnlib/util/iters.py | 32 -------------------------------- 2 files changed, 2 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba3b1dd34..3730b0fa6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ The table below shows which release corresponds to each branch, and what date th ## 5.0.0 (`dev`) - [#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 @@ -106,6 +107,7 @@ The table below shows which release corresponds to each branch, and what date th - [#2630][2630] support `preexec_fn` in `debug()` [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 diff --git a/pwnlib/util/iters.py b/pwnlib/util/iters.py index 629e280da..0b01ed506 100644 --- a/pwnlib/util/iters.py +++ b/pwnlib/util/iters.py @@ -28,7 +28,6 @@ 'group' , 'iter_except' , 'lexicographic' , - 'lookahead' , 'nth' , 'pad' , 'pairwise' , @@ -681,37 +680,6 @@ def random_combination_with_replacement(iterable, r): indices = sorted(random.randrange(n) for i in range(r)) return tuple(pool[i] for i in indices) -def lookahead(n, iterable): - """lookahead(n, iterable) -> object - - Inspects the upcoming element at index `n` without advancing the iterator. - Raises ``IndexError`` if `iterable` has too few elements. - - Arguments: - n(int): Index of the element to return. - iterable: An iterable. - - Returns: - The element in `iterable` at index `n`. - - Examples: - - >>> i = count() - >>> lookahead(4, i) - 4 - >>> next(i) - 0 - >>> i = count() - >>> nth(4, i) - 4 - >>> next(i) - 5 - >>> lookahead(4, i) - 10 - """ - for value in islice(copy.copy(iterable), n, None): - return value - raise IndexError(n) def lexicographic(alphabet): """lexicographic(alphabet) -> iterator