# `pwnlib.encoders` — Encoding Shellcode

pwnlib.encoders.encoder.alphanumeric(raw_bytes) → str

  Encode the shellcode `raw_bytes` such that it does not contain any bytes except for [A-Za-z0-9].

  Accepts the same arguments as `encode()`.

pwnlib.encoders.encoder.encode(raw_bytes, avoid, expr, force) → str

  Encode shellcode `raw_bytes` such that it does not contain any bytes in `avoid` or `expr`.

  Parameters:

    - raw_bytes (str) – Sequence of shellcode bytes to encode.

    - avoid (str) – Bytes to avoid

    - expr (str) – Regular expression which matches bad characters.

    - force (bool) – Force re-encoding of the shellcode, even if it doesn’t contain any bytes in `avoid`.

pwnlib.encoders.encoder.line(raw_bytes) → str

  Encode the shellcode `raw_bytes` such that it does not contain any NULL bytes or whitespace.

  Accepts the same arguments as `encode()`.

pwnlib.encoders.encoder.null(raw_bytes) → str

  Encode the shellcode `raw_bytes` such that it does not contain any NULL bytes.

  Accepts the same arguments as `encode()`.

pwnlib.encoders.encoder.printable(raw_bytes) → str

  Encode the shellcode `raw_bytes` such that it only contains non-space printable bytes.

  Accepts the same arguments as `encode()`.

pwnlib.encoders.encoder.scramble(raw_bytes) → str

  Encodes the input data with a random encoder.

  Accepts the same arguments as `encode()`.

Encoder to convert shellcode to shellcode that contains only ascii characters

class pwnlib.encoders.i386.ascii_shellcode.AsciiShellcodeEncoder(slop=20, max_subs=4)

  Pack shellcode into only ascii characters that unpacks itself and executes (on the stack)

  The original paper this encoder is based on: https://julianor.tripod.com/bc/bypass-msb.txt

  A more visual explanation as well as an implementation in C: https://vincentdary.github.io/blog-posts/polyasciishellgen-caezar-ascii-shellcode-generator/index.html#22-mechanism

  Init

  Parameters:

    - slop (int, optional) – The amount esp will be increased by in the allocation phase (In addition to the length of the packed shellcode) as well as defines the size of the NOP sled (you can increase/ decrease the size of the NOP sled by adding/removing b’P’-s to/ from the end of the packed shellcode). Defaults to 20.

    - max_subs (int, optional) – The maximum amount of subtractions allowed to be taken. This may be increased if you have a relatively restrictive `avoid` set. The more subtractions there are, the bigger the packed shellcode will be. Defaults to 4.

  __call__(raw_bytes, avoid=None, pcreg=None)

    Pack shellcode into only ascii characters that unpacks itself and executes (on the stack)

    Parameters:

      - raw_bytes (bytes) – The shellcode to be packed

      - avoid (set, optional) – Characters to avoid. Defaults to allow printable ascii (0x21-0x7e).

      - pcreg (NoneType, optional) – Ignored

    Raises:

      - RuntimeError – A required character is in `avoid` (required characters are characters which assemble into assembly instructions and are used to unpack the shellcode onto the stack, more details in the paper linked above `\ - % T X P`).

      - RuntimeError – Not supported architecture

      - ArithmeticError – The allowed character set does not contain two characters that when they are bitwise-anded with eachother their result is 0

      - ArithmeticError – Could not find a correct subtraction sequence to get to the the desired target value with the given `avoid` parameter

    Returns:

      bytes – The packed shellcode

    Examples

        >>> context.update(arch='i386', os='linux')
        >>> sc = b"\x83\xc4\x181\xc01\xdb\xb0\x06\xcd\x80Sh/ttyh/dev\x89\xe31\xc9f\xb9\x12'\xb0\x05\xcd\x80j\x17X1\xdb\xcd\x80j.XS\xcd\x801\xc0Ph//shh/bin\x89\xe3PS\x89\xe1\x99\xb0\x0b\xcd\x80"
        >>> encoders.i386.ascii_shellcode.encode(sc)
        b'TX-!!!!-"_``-~~~~P\\%!!!!%@@@@-!6!!-V~!!-~~<-P-!mha-a~~~P-!!L`-a^~~-~~~~P-!!if-9`~~P-!!!!-aOaf-~~~~P-!&!<-!~`~--~~~P-!!!!-!!H^-+A~~P-U!![-~A1~P-,<V!-~~~!-~~~GP-!2!8-j~O~P-!]!!-!~!r-y~w~P-c!!!-~<(+P-N!_W-~1~~P-!!]!-Mn~!-~~~<P-!<!!-r~!P-~~x~P-fe!$-~~S~-~~~~P-!!\'$-%z~~P-A!!!-~!#!-~*~=P-!7!!-T~!!-~~E^PPPPPPPPPPPPPPPPPPPPP'
        >>> avoid = {'\x00', '\x83', '\x04', '\x87', '\x08', '\x8b', '\x0c', '\x8f', '\x10', '\x93', '\x14', '\x97', '\x18', '\x9b', '\x1c', '\x9f', ' ', '\xa3', '\xa7', '\xab', '\xaf', '\xb3', '\xb7', '\xbb', '\xbf', '\xc3', '\xc7', '\xcb', '\xcf', '\xd3', '\xd7', '\xdb', '\xdf', '\xe3', '\xe7', '\xeb', '\xef', '\xf3', '\xf7', '\xfb', '\xff', '\x80', '\x03', '\x84', '\x07', '\x88', '\x0b', '\x8c', '\x0f', '\x90', '\x13', '\x94', '\x17', '\x98', '\x1b', '\x9c', '\x1f', '\xa0', '\xa4', '\xa8', '\xac', '\xb0', '\xb4', '\xb8', '\xbc', '\xc0', '\xc4', '\xc8', '\xcc', '\xd0', '\xd4', '\xd8', '\xdc', '\xe0', '\xe4', '\xe8', '\xec', '\xf0', '\xf4', '\xf8', '\xfc', '\x7f', '\x81', '\x02', '\x85', '\x06', '\x89', '\n', '\x8d', '\x0e', '\x91', '\x12', '\x95', '\x16', '\x99', '\x1a', '\x9d', '\x1e', '\xa1', '\xa5', '\xa9', '\xad', '\xb1', '\xb5', '\xb9', '\xbd', '\xc1', '\xc5', '\xc9', '\xcd', '\xd1', '\xd5', '\xd9', '\xdd', '\xe1', '\xe5', '\xe9', '\xed', '\xf1', '\xf5', '\xf9', '\xfd', '\x01', '\x82', '\x05', '\x86', '\t', '\x8a', '\r', '\x8e', '\x11', '\x92', '\x15', '\x96', '\x19', '\x9a', '\x1d', '\x9e', '\xa2', '\xa6', '\xaa', '\xae', '\xb2', '\xb6', '\xba', '\xbe', '\xc2', '\xc6', '\xca', '\xce', '\xd2', '\xd6', '\xda', '\xde', '\xe2', '\xe6', '\xea', '\xee', '\xf2', '\xf6', '\xfa', '\xfe'}
        >>> sc = shellcraft.echo("Hello world") + shellcraft.exit()
        >>> ascii = encoders.i386.ascii_shellcode.encode(asm(sc), avoid)
        >>> ascii += asm('jmp esp') # just for testing, the unpacker should also run on the stack
        >>> ELF.from_bytes(ascii).process().recvall()
        b'Hello world'

  _calc_subtractions(last, target, vocab)
    Given target and last, return a list of integers that when

      subtracted from last will equal target while only constructing integers from bytes in vocab

    int_size is taken from the context

    Parameters:

      - last (bytearray) – Original value

      - target (bytearray) – Desired value

      - vocab (bytearray) – Allowed characters

    Raises:

      ArithmeticError – If a sequence of subtractions could not be found

    Returns:

      List[bytearray] – List of numbers that would need to be subtracted from last to get to target

    Examples

        >>> context.update(arch='i386', os='linux')
        >>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
        >>> 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))
        [bytearray(b'~}}}'), bytearray(b'~~~~')]

  _find_negatives(vocab)

    Find two bitwise negatives in the vocab so that when they are and-ed the result is 0.

    int_size is taken from the context

    Parameters:

      vocab (bytearray) – Allowed characters

    Returns:

      Tuple[int, int] – value A, value B

    Raises:

      ArithmeticError – The allowed character set does not contain two characters that when they are bitwise-and-ed with eachother the result is 0

    Examples

        >>> context.update(arch='i386', os='linux')
        >>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
        >>> a, b = encoders.i386.ascii_shellcode.encode._find_negatives(vocab)
        >>> a & b
        0

  _get_allocator(size, vocab)

    Allocate enough space on the stack for the shellcode

    int_size is taken from the context

    Parameters:

      - size (int) – The allocation size

      - vocab (bytearray) – Allowed characters

    Returns:

      bytearray – The allocator shellcode

    Examples

        >>> context.update(arch='i386', os='linux')
        >>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
        >>> encoders.i386.ascii_shellcode.encode._get_allocator(300, vocab)
        bytearray(b'TX-!!!!-!_``-t~~~P\\%!!!!%@@@@')

  _get_subtractions(shellcode, vocab)

    Covert the sellcode to sub eax and posh eax instructions

    int_size is taken from the context

    Parameters:

      - shellcode (bytearray) – The shellcode to pack

      - vocab (bytearray) – Allowed characters

    Returns:

      bytearray – packed shellcode

    Examples

        >>> context.update(arch='i386', os='linux')
        >>> sc = bytearray(b'ABCDEFGHIGKLMNOPQRSTUVXYZ')
        >>> vocab = bytearray(b'!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~')
        >>> encoders.i386.ascii_shellcode.encode._get_subtractions(sc, vocab)
        bytearray(b'-(!!!-~NNNP-!=;:-f~~~-~~~~P-!!!!-edee-~~~~P-!!!!-eddd-~~~~P-!!!!-egdd-~~~~P-!!!!-eadd-~~~~P-!!!!-eddd-~~~~P')

class pwnlib.encoders.i386.xor.i386XorEncoder

  Generates an XOR decoder for i386.

      >>> context.clear(arch='i386')
      >>> shellcode = asm(shellcraft.sh())
      >>> avoid = b'/bin/sh\xcc\xcd\x80'
      >>> encoded = pwnlib.encoders.i386.xor.encode(shellcode, avoid)
      >>> assert not any(c in encoded for c in avoid)
      >>> p = run_shellcode(encoded)
      >>> p.sendline(b'echo hello; exit')
      >>> p.recvline()
      b'hello\n'
      >>> encoders.i386.xor.encode(asm(shellcraft.execve('/bin/sh')), avoid=bytearray([0x31]))
      b'\xd9\xd0\xd9t$\xf4^\xfcj\x07Y\x83\xc6\x19\x89\xf7\xad\x93\xad1\xd8\xabIu\xf7\x00\x00\x00\x00h\x01\x01\x01\x00\x00\x00\x00\x01\x814$\x00\x00\x00\x00.ri\x01\x00\x00\x00\x00h/bi\x00\x00\x00\x01n\x89\xe30\x00\x01\x00\x00\xc90\xd2j\x00\x00\x00\x00\x0bX\xcd\x80'

  Shellcode encoder class

  Implements an architecture-specific shellcode encoder

  __call__(raw_bytes, avoid, pcreg='')

    avoid(raw_bytes, avoid)

    Parameters:

      - raw_bytes (str) – String of bytes to encode

      - avoid (set) – Set of bytes to avoid

      - pcreg (str) – Register which contains the address of the shellcode. May be necessary for some shellcode.

  arch = 'i386'

    Architecture which this encoder works on

  blacklist = {'\x14', '$', '1', 'I', '^', 't', 'u', '\x83', '\x89', '\x93', '«', '\xad', 'Æ', 'Ø', 'Ù', 'ô', '÷', 'ü'}

    Blacklist of bytes which are known not to be supported

class pwnlib.encoders.i386.delta.i386DeltaEncoder

  i386 encoder built on delta-encoding.

  In addition to the loader stub, doubles the size of the shellcode.

  Example

      >>> sc = pwnlib.encoders.i386.delta.encode(b'\xcc', b'\x00\xcc')
      >>> e  = ELF.from_bytes(sc)
      >>> e.process().poll(True)
      -5

  Shellcode encoder class

  Implements an architecture-specific shellcode encoder

  __call__(raw_bytes, avoid, pcreg='')

    avoid(raw_bytes, avoid)

    Parameters:

      - raw_bytes (str) – String of bytes to encode

      - avoid (set) – Set of bytes to avoid

      - pcreg (str) – Register which contains the address of the shellcode. May be necessary for some shellcode.

  arch = 'i386'

    Architecture which this encoder works on

  blacklist = {24, 36, 40, 94, 116, 117, 128, 131, 137, 147, 170, 172, 198, 208, 216, 217, 235, 244, 245, 247, 252}

    Blacklist of bytes which are known not to be supported

class pwnlib.encoders.arm.xor.ArmXorEncoder

  Generates an XOR decoder for ARM.

      >>> context.clear(arch='arm')
      >>> shellcode = asm(shellcraft.sh())
      >>> avoid = b'binsh\x00\n'
      >>> encoded = pwnlib.encoders.arm.xor.encode(shellcode, avoid)
      >>> assert not any(c in encoded for c in avoid)
      >>> p = run_shellcode(encoded)
      >>> p.sendline(b'echo hello; exit')
      >>> p.recvline()
      b'hello\n'

  Shellcode encoder class

  Implements an architecture-specific shellcode encoder

  __call__(raw_bytes, avoid, pcreg='')

    avoid(raw_bytes, avoid)

    Parameters:

      - raw_bytes (str) – String of bytes to encode

      - avoid (set) – Set of bytes to avoid

      - pcreg (str) – Register which contains the address of the shellcode. May be necessary for some shellcode.

  arch = 'arm'

    Architecture which this encoder works on

  blacklist = {'\x01', '\x03', '\x04', '\x07', '\x0c', '\x0f', '\x16', '\x1c', '$', '%', "'", '-', '/', '@', 'A', 'D', 'G', 'O', 'P', 'T', '_', '`', 'p', '\x80', '\x81', '\x84', '\x85', '\x87', '\x8f', '\x9f', '\xa0', '°', '½', 'Â', 'Æ', 'È', 'Ø', 'à', 'á', 'â', 'ã', 'å', 'ç', 'è', 'é', 'ê', '÷'}

    Blacklist of bytes which are known not to be supported

class pwnlib.encoders.mips.xor.MipsXorEncoder

  Generates an XOR decoder for MIPS.

      >>> context.clear(arch='mips')
      >>> shellcode = asm(shellcraft.sh())
      >>> avoid = b'/bin/sh\x00'
      >>> encoded = pwnlib.encoders.mips.xor.encode(shellcode, avoid)
      >>> assert not any(c in encoded for c in avoid)
      >>> p = run_shellcode(encoded)
      >>> p.sendline(b'echo hello; exit')
      >>> p.recvline()
      b'hello\n'

  Shellcode encoder class

  Implements an architecture-specific shellcode encoder

  __call__(raw_bytes, avoid, pcreg='')

    avoid(raw_bytes, avoid)

    Parameters:

      - raw_bytes (str) – String of bytes to encode

      - avoid (set) – Set of bytes to avoid

      - pcreg (str) – Register which contains the address of the shellcode. May be necessary for some shellcode.

  arch = 'mips'

    Architecture which this encoder works on

  blacklist = {1, 2, 3, 5, 8, 11, 12, 14, 16, 17, 20, 23, 24, 30, 33, 35, 36, 38, 39, 40, 43, 44, 49, 50, 64, 70, 73, 74, 83, 84, 88, 90, 96, 112, 128, 130, 134, 143, 163, 164, 165, 166, 175, 184, 192, 200, 206, 224, 235, 238, 239, 240, 248, 250, 251, 252, 253, 255}

    Blacklist of bytes which are known not to be supported