Skip to content

Use format strings and raw strings #2554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions pwnlib/adb/adb.py
Original file line number Diff line number Diff line change
Expand Up @@ -887,15 +887,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()
Expand Down Expand Up @@ -1562,9 +1562,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)
Expand All @@ -1585,7 +1585,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()

Expand Down
15 changes: 7 additions & 8 deletions pwnlib/asm.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
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__
Expand Down Expand Up @@ -133,11 +134,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):
Expand Down Expand Up @@ -624,8 +624,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)

Expand Down Expand Up @@ -693,7 +692,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)

Expand Down Expand Up @@ -802,7 +801,7 @@ def asm(shellcode, vma = 0, extract = True, shared = False):
os.makedirs(cache_dir)

# Include the context in the hash in addition to the shellcode
hash_params = '{}_{}_{}_{}'.format(vma, extract, shared, __version__)
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)
Expand Down
2 changes: 1 addition & 1 deletion pwnlib/commandline/checksec.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def main(args):
try:
e = ELF(f)
except Exception as e:
print("{name}: {error}".format(name=f, error=e))
print(f"{f}: {e}")

if __name__ == '__main__':
common.main(__file__, main)
16 changes: 3 additions & 13 deletions pwnlib/commandline/shellcraft.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from pwn import *
from pwnlib.commandline import common
from pwnlib.util.fiddling import hexstr


# ____ _ _ _ __ _
Expand All @@ -19,17 +20,6 @@
# ___) | | | | __/ | | (__| | | (_| | _| |_
# |____/|_| |_|\___|_|_|\___|_| \__,_|_| \__|

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',
Expand Down Expand Up @@ -366,15 +356,15 @@ 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']:
code = pwnlib.util.fiddling.enhex(code) + '\n'
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 = hexstr(code, force=True) + '\n'
if not sys.stdin.isatty():
args.out.write(getattr(sys.stdin, 'buffer', sys.stdin).read())

Expand Down
4 changes: 2 additions & 2 deletions pwnlib/constants/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,10 @@ def eval(self, string):
val = safeeval.values(string, self._env_store[key])

# if the expression is not assembly-safe, it is not so vital to preserve it
if set(string) & (set(bytearray(range(32)).decode()) | set('"#$\',.;@[\\]`{}')):
if set(string) & (set(bytearray(range(32)).decode()) | set(r'"#$\',.;@[]`{}')):
string = val

return Constant('(%s)' % string, val)
return Constant(f'({string})', val)


# To prevent garbage collection
Expand Down
2 changes: 1 addition & 1 deletion pwnlib/data/includes/generator/load_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@

if paren:
val = '(%s)' % val
print("{key} = Constant({key!r},{val})".format(**locals()), file=python)
print(f"{key} = Constant({key!r},{val})", file=python)
if re.search(r'0o[0-7]', val) or re.match(r'[^0-9a-fA-Fx]0[0-9]', val):
print("#define %s %s" % (key, hex(safeeval.expr(val))), file=header)
else:
Expand Down
7 changes: 1 addition & 6 deletions pwnlib/data/syscalls/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,7 @@ def generate_one(target):
string_arguments.append(argname)

argtype = str(arg.type) + ('*' * arg.derefcnt)
arg_docs.append(
' {argname_}({argtype}): {argname}'.format(
argname_=argname_,
argname=argname,
argtype=argtype,
))
arg_docs.append(f' {argname_}({argtype}): {argname}')

# Mako is unable to use *vararg and *kwarg, so we just stub in
# a whole bunch of additional arguments.
Expand Down
4 changes: 2 additions & 2 deletions pwnlib/data/templates/pwnup.mako
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def start(argv=[], *a, **kw):
# GDB will be launched if the exploit is run via e.g.
# ./exploit.py GDB
%endif
gdbscript = '''
gdbscript = f'''
%if ctx.binary:
%if 'main' in ctx.binary.symbols:
tbreak main
Expand All @@ -164,7 +164,7 @@ tbreak *0x{exe.entry:x}
%endif
%endif
continue
'''.format(**locals())
'''
%endif


Expand Down
18 changes: 9 additions & 9 deletions pwnlib/elf/elf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1196,7 +1196,7 @@ def find_ret_main_addr(lines, calls):
return 0

def search(self, needle, writable = False, executable = False):
"""search(needle, writable = False, executable = False) -> generator
r"""search(needle, writable = False, executable = False) -> generator

Search the ELF's virtual address space for the specified string.

Expand All @@ -1216,7 +1216,7 @@ def search(self, needle, writable = False, executable = False):

Examples:

An ELF header starts with the bytes ``\\x7fELF``, so we
An ELF header starts with the bytes ``\x7fELF``, so we
sould be able to find it easily.

>>> bash = ELF('/bin/bash')
Expand Down Expand Up @@ -1725,7 +1725,7 @@ def relro(self):

@property
def nx(self):
""":class:`bool`: Whether the current binary uses NX protections.
r""":class:`bool`: Whether the current binary uses NX protections.

Specifically, we are checking for ``READ_IMPLIES_EXEC`` being set
by the kernel, as a result of honoring ``PT_GNU_STACK`` in the kernel.
Expand Down Expand Up @@ -1788,7 +1788,7 @@ def nx(self):
| the rest | [#the_rest]_ | exec / non-exec / missing | | enabled |
+-----------+--------------+---------------------------+------------------------------------------------+----------+

\\* Hardware limitations are ignored.
\* 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
Expand All @@ -1802,15 +1802,15 @@ def nx(self):

.. code-block:: c

#define elf_read_implies_exec(ex, executable_stack) \\
#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

#define elf_read_implies_exec(ex, executable_stack) \\
#define elf_read_implies_exec(ex, executable_stack) \
(mmap_is_ia32() && executable_stack == EXSTACK_DEFAULT)

`mmap_is_ia32()`__:
Expand Down Expand Up @@ -1889,7 +1889,7 @@ def nx(self):

#ifdef __powerpc64__
/* stripped */
# define elf_read_implies_exec(ex, exec_stk) (is_32bit_task() ? \\
# 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)
Expand All @@ -1900,7 +1900,7 @@ def nx(self):

.. code-block:: c

#define elf_read_implies_exec(ex, executable_stack) \\
#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__:
Expand Down Expand Up @@ -2153,7 +2153,7 @@ def checksec(self, banner=True, color=True):
for name, message in sorted(values):
line = '{} = {}'.format(name, red(str(self.config.get(name, None))))
if message:
line += ' ({})'.format(message)
line += f' ({message})'
res.append(' ' + line)

# res.extend(sorted(config_opts))
Expand Down
13 changes: 6 additions & 7 deletions pwnlib/encoders/i386/ascii_shellcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,9 @@ def __call__(self, raw_bytes, avoid=None, pcreg=None):
b'Hello world'
"""
if not avoid:
vocab = bytearray(
b"!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
vocab = bytes(range(0x21, 0x7f))
else:
required_chars = set('\\-%TXP')
required_chars = set(r'\-%TXP')
allowed = set(all_chars)
if avoid.intersection(required_chars):
raise RuntimeError(
Expand Down Expand Up @@ -127,7 +126,7 @@ def _get_allocator(self, size, vocab):
Examples:

>>> context.update(arch='i386', os='linux')
>>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
>>> vocab = bytes(range(0x21, 0x7f))
>>> encoders.i386.ascii_shellcode.encode._get_allocator(300, vocab)
bytearray(b'TX-!!!!-!_``-t~~~P\\%!!!!%@@@@')
"""
Expand Down Expand Up @@ -173,7 +172,7 @@ def _find_negatives(self, vocab):
Examples:

>>> context.update(arch='i386', os='linux')
>>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
>>> vocab = bytes(range(0x21, 0x7f))
>>> a, b = encoders.i386.ascii_shellcode.encode._find_negatives(vocab)
>>> a & b
0
Expand Down Expand Up @@ -207,7 +206,7 @@ def _get_subtractions(self, shellcode, vocab):

>>> context.update(arch='i386', os='linux')
>>> sc = bytearray(b'ABCDEFGHIGKLMNOPQRSTUVXYZ')
>>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
>>> vocab = bytes(range(0x21, 0x7f))
>>> encoders.i386.ascii_shellcode.encode._get_subtractions(sc, vocab)
bytearray(b'-(!!!-~NNNP-!=;:-f~~~-~~~~P-!!!!-edee-~~~~P-!!!!-eddd-~~~~P-!!!!-egdd-~~~~P-!!!!-eadd-~~~~P-!!!!-eddd-~~~~P')
"""
Expand Down Expand Up @@ -250,7 +249,7 @@ def _calc_subtractions(self, last, target, vocab):
Examples:

>>> context.update(arch='i386', os='linux')
>>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
>>> vocab = bytes(range(0x21, 0x7f))
>>> print(encoders.i386.ascii_shellcode.encode._calc_subtractions(bytearray(b'\x10'*4), bytearray(b'\x11'*4), vocab))
[bytearray(b'!!!!'), bytearray(b'`___'), bytearray(b'~~~~')]
>>> print(encoders.i386.ascii_shellcode.encode._calc_subtractions(bytearray(b'\x11\x12\x13\x14'), bytearray(b'\x15\x16\x17\x18'), vocab))
Expand Down
8 changes: 4 additions & 4 deletions pwnlib/libcdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def provider_libcdb(hex_encoded_id, search_type):
import urllib.parse

# Build the URL using the requested hash type
url_base = "{}/libcdb/libcdb/raw/master/hashes/{}/".format(GITLAB_LIBCDB_URL, search_type)
url_base = f"{GITLAB_LIBCDB_URL}/libcdb/libcdb/raw/master/hashes/{search_type}/"
url = urllib.parse.urljoin(url_base, hex_encoded_id)

data = b""
Expand All @@ -114,7 +114,7 @@ def query_libc_rip(params):
# Deferred import because it's slow
import requests

url = "{}/api/find".format(LIBC_RIP_URL)
url = f"{LIBC_RIP_URL}/api/find"
try:
result = requests.post(url, json=params, timeout=20)
result.raise_for_status()
Expand Down Expand Up @@ -316,7 +316,7 @@ def _search_debuginfo_by_hash(base_url, hex_encoded_id):
return None

# Try to find separate debuginfo.
url = '/buildid/{}/debuginfo'.format(hex_encoded_id)
url = f'/buildid/{hex_encoded_id}/debuginfo'
url = urllib.parse.urljoin(base_url, url)
data = b""
log.debug("Downloading data from debuginfod: %s", url)
Expand Down Expand Up @@ -513,7 +513,7 @@ def _find_libc_package_lib_url(libc):
version = re.search(br'GNU C Library \(Ubuntu E?GLIBC ([^\)]+)\)', libc.data)
if version is not None:
libc_version = version.group(1).decode()
yield 'https://launchpad.net/ubuntu/+archive/primary/+files/libc6_{}_{}.deb'.format(libc_version, libc.arch)
yield f'https://launchpad.net/ubuntu/+archive/primary/+files/libc6_{libc_version}_{libc.arch}.deb'

def download_libraries(libc_path, unstrip=True):
"""download_libraries(str, bool) -> str
Expand Down
6 changes: 3 additions & 3 deletions pwnlib/memleak.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
__all__ = ['MemLeak', 'RelativeMemLeak']

class MemLeak(object):
"""MemLeak is a caching and heuristic tool for exploiting memory leaks.
r"""MemLeak is a caching and heuristic tool for exploiting memory leaks.

It can be used as a decorator, around functions of the form:

Expand Down Expand Up @@ -48,9 +48,9 @@ def some_leaker(addr):
>>> leaker.s(0)[:4]
leaking 0x0
leaking 0x4
b'\\x7fELF'
b'\x7fELF'
>>> leaker[:4]
b'\\x7fELF'
b'\x7fELF'
>>> hex(leaker.d(0))
'0x464c457f'
>>> hex(leaker.clearb(1))
Expand Down
2 changes: 1 addition & 1 deletion pwnlib/rop/srop.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

Let's just print a message out using SROP.

>>> message = "Hello, World\\n"
>>> message = r"Hello, World\n"

First, we'll create our example binary.
It just reads some data onto the stack, and invokes
Expand Down
Loading
Loading