Source code for pwnypack.util

"""
The util module contains various utility functions.
"""

from __future__ import print_function

import argparse
import binascii
import re
import sys

from six.moves import range

import pwnypack.codec
import pwnypack.main

__all__ = [
    'cycle',
    'cycle_find',
    'reghex',
]


def deBruijn(n, k):
    """
    An implementation of the FKM algorithm for generating the de Bruijn
    sequence containing all k-ary strings of length n, as described in
    "Combinatorial Generation" by Frank Ruskey.
    """

    a = [ 0 ] * (n + 1)

    def gen(t, p):
        if t > n:
            for v in a[1:p + 1]:
                yield v
        else:
            a[t] = a[t - p]
         
            for v in gen(t + 1, p):
                yield v
         
            for j in range(a[t - p] + 1, k):
                a[t] = j
                for v in gen(t + 1, t):
                    yield v

    return gen(1, 1)


[docs]def cycle(length, width=4): """ Generate a de Bruijn sequence of a given length (and width). A de Bruijn sequence is a set of varying repetitions where each sequence of *n* characters is unique within the sequence. This type of sequence can be used to easily find the offset to the return pointer when exploiting a buffer overflow. Args: length(int): The length of the sequence to generate. width(int): The width of each element in the sequence. Returns: str: The sequence. Example: >>> from pwny import * >>> cycle(80) AAAABAAACAAADAAAEAAAFAAAGAAAHAAAIAAAJAAAKAAALAAAMAAANAAAOAAAPAAAQAAARAAASAAATAAA """ iter = deBruijn(width, 26) return ''.join([chr(ord('A') + next(iter)) for i in range(length)])
[docs]def cycle_find(key, width=4): """ Given an element of a de Bruijn sequence, find its index in that sequence. Args: key(str): The piece of the de Bruijn sequence to find. width(int): The width of each element in the sequence. Returns: int: The index of ``key`` in the de Bruijn sequence. """ key_len = len(key) buf = '' it = deBruijn(width, 26) for i in range(key_len): buf += chr(ord('A') + next(it)) if buf == key: return 0 for i, c in enumerate(it): buf = buf[1:] + chr(ord('A') + c) if buf == key: return i + 1 return -1
REGHEX_PATTERN = r'(([a-fA-F0-9]{2})|(([?.])(\{\d+\})?)|(\*|\+)|\s+)' reghex_check = re.compile(REGHEX_PATTERN + '+') reghex_regex = re.compile(REGHEX_PATTERN)
[docs]def reghex(pattern): """ Compile a regular hexpression (a short form regular expression subset specifically designed for searching for binary strings). A regular hexpression consists of hex tuples interspaced with control characters. The available control characters are: - ``?``: Any byte (optional). - ``.``: Any byte (required). - ``?{n}``: A set of 0 up to *n* bytes. - ``.{n}``: A set of exactly *n* bytes. - ``*``: Any number of bytes (or no bytes at all). - ``+``: Any number of bytes (at least one byte). Args: pattern(str): The reghex pattern. Returns: regexp: A regular expression as returned by ``re.compile()``. """ if not reghex_check.match(pattern): raise SyntaxError('Invalid reghex pattern.') b_pattern = b'' for match in reghex_regex.finditer(pattern): _, match_hex, _, match_char, match_char_len, match_star_plus = match.groups() if match_hex: b_pattern += re.escape(pwnypack.codec.dehex(match_hex)) elif match_char: if match_char == '?': if match_char_len is None: b_pattern += b'.?' else: b_pattern += ('.{0,%d}?' % int(match_char_len[1:-1])).encode('ascii') else: if match_char_len is None: b_pattern += b'.' else: b_pattern += b'.' * int(match_char_len[1:-1]) elif match_star_plus: b_pattern += b'.' + match_star_plus.encode('ascii') + b'?' try: return re.compile(b_pattern) except (TypeError, binascii.Error, re.error): raise SyntaxError('Invalid reghex pattern.')
@pwnypack.main.register('cycle') def cycle_app(parser, cmd, args): # pragma: no cover """ Generate a de Bruijn sequence of a given length. """ parser.add_argument('-w', '--width', type=int, default=4, help='the length of the cycled value') parser.add_argument('length', type=int, help='the cycle length to generate') args = parser.parse_args(args) return cycle(args.length, args.width) @pwnypack.main.register('cycle-find') def cycle_find_app(_parser, cmd, args): # pragma: no cover """ Find the first position of a value in a de Bruijn sequence. """ parser = argparse.ArgumentParser( prog=_parser.prog, description=_parser.description, ) parser.add_argument('-w', '--width', type=int, default=4, help='the length of the cycled value') parser.add_argument('value', help='the value to determine the position of, read from stdin if missing', nargs='?') args = parser.parse_args(args) index = cycle_find(pwnypack.main.string_value_or_stdin(args.value), args.width) if index == -1: print('Not found.') sys.exit(1) else: print('Found at position: %d' % index)