Source code for pwnypack.rop

"""
The ROP module contains a function to find gadgets in ELF binaries that can
be used to create ROP chains.
"""

from __future__ import print_function
import argparse
import re
import six
import sys
import pwnypack.codec
import pwnypack.elf
import pwnypack.main
import pwnypack.asm
import pwnypack.target
import pwnypack.util

try:
    import capstone
    HAVE_CAPSTONE = True
except ImportError:
    HAVE_CAPSTONE = False


__all__ = [
    'find_gadget',
]


if HAVE_CAPSTONE:
    INVALID_GROUPS = set((capstone.CS_GRP_CALL, capstone.CS_GRP_JUMP))


[docs]def find_gadget(elf, gadget, align=1, unique=True): """ Find a ROP gadget in a the executable sections of an ELF executable or library. The ROP gadget can be either a set of bytes for an exact match or a (bytes) regular expression. Once it finds gadgets, it uses the capstone engine to verify if the gadget consists of valid instructions and doesn't contain any call or jump instructions. Args: elf(:class:`~pwnypack.elf.ELF`): The ELF instance to find a gadget in. gadget(bytes or regexp): The gadget to find. align(int): Make sure the gadget starts at a multiple of this number unique(bool): If true, only unique gadgets are returned. Returns: dict: A dictionary containing a description of the found gadget. Contains the following fields: - section: The section the gadget was found in. - offset: The offset inside the segment the gadget was found at. - addr: The virtual memory address the gadget will be located at. - gadget: The machine code of the found gadget. - asm: A list of disassembled instructions. """ if not HAVE_CAPSTONE: raise NotImplementedError('pwnypack requires capstone to find ROP gadgets') if not isinstance(elf, pwnypack.elf.ELF): elf = pwnypack.elf.ELF(elf) matches = [] gadgets = [] if isinstance(gadget, six.binary_type): gadget = re.compile(re.escape(gadget)) for section in elf.section_headers: if section.type != section.Type.progbits: continue for match in gadget.finditer(section.content): match_index = match.start() if match_index % align != 0: continue match_gadget = match.group() if match_gadget in gadgets: continue match_addr = section.addr + match_index md = pwnypack.asm.prepare_capstone(syntax=pwnypack.asm.AsmSyntax.intel, target=elf) md.detail = True match_asm = [] for insn in md.disasm(match_gadget, match_addr): if insn.id == capstone.CS_OP_INVALID or set(insn.groups) & INVALID_GROUPS: # Don't try to disassemble this particular gadget again. gadgets.append(match_gadget) break match_asm.append((insn.mnemonic + ' ' + insn.op_str).strip()) else: matches.append({ 'section': section, 'offset': match_index, 'addr': match_addr, 'gadget': match_gadget, 'asm': match_asm, }) if unique: gadgets.append(match_gadget) return matches
@pwnypack.main.register('gadget') def gadget_app(_parser, cmd, args): # pragma: no cover """ Find ROP gadgets in an ELF binary. """ parser = argparse.ArgumentParser( prog=_parser.prog, description=_parser.description, ) parser.add_argument('file', help='ELF file to find gadgets in') parser.add_argument('gadget', help='the assembler source or reghex expression') parser.add_argument( '--reghex', '-r', dest='mode', action='store_const', const='reghex', help='use reghex expression (hex bytes interspaced with ? for wildcard)', ) parser.add_argument( '--asm', '-a', dest='mode', action='store_const', const='asm', help='use assembler expression (separate lines with semi-colon)', ) parser.add_argument( '--all', '-l', dest='unique', action='store_const', const=False, default=True, help='also show non-unique gadgets', ) args = parser.parse_args(args) if args.mode is None: try: pwnypack.util.reghex(args.gadget) args.mode = 'reghex' except SyntaxError: args.mode = 'asm' elf = pwnypack.elf.ELF(args.file) if args.mode == 'reghex': try: gadget = pwnypack.util.reghex(args.gadget) except SyntaxError: print('Invalid reghex pattern.') sys.exit(1) else: try: gadget = pwnypack.util.reghex('*'.join([ pwnypack.codec.enhex(pwnypack.asm.asm(piece.replace(';', '\n'), target=elf)) for piece in ';'.join([line.strip() for line in args.gadget.split(';')]).split('*') ])) except SyntaxError as e: print('Could not assemble:', e.msg) sys.exit(1) matches = find_gadget( elf, gadget, unique=args.unique ) if not matches: print('No gadgets found.', file=sys.stdout) return longest_gadget = max(len(m['gadget']) for m in matches) fmt = ' 0x%%0%dx: [ %%-%ds ] %%s' % (elf.bits / 4, longest_gadget * 3 - 1) current_section = None for match in matches: if match['section'].name != current_section: if current_section is not None: print() print('Section: %s' % match['section'].name) current_section = match['section'].name hex_gadget = pwnypack.codec.enhex(match['gadget']) print(fmt % ( match['addr'], ' '.join( hex_gadget[i:i+2] for i in range(0, len(hex_gadget), 2) ), ' ; '.join(match['asm']) )) print()