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.

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