Esempio n. 1
0
    def __on_enoexec(self, exception):
        """We received an 'exec format' error (ENOEXEC)

        This implies that the user tried to execute e.g.
        an ARM binary on a non-ARM system, and does not have
        binfmt helpers installed for QEMU.
        """
        # Get the ELF binary for the target executable
        with context.quiet:
            # XXX: Cyclic imports :(
            from pwnlib.elf import ELF
            binary = ELF(self.executable)

        # If we're on macOS, this will never work.  Bail now.
        # if platform.mac_ver()[0]:
            # self.error("Cannot run ELF binaries on macOS")

        # Determine what architecture the binary is, and find the
        # appropriate qemu binary to run it.
        qemu_path = qemu.user_path(arch=binary.arch)

        if not qemu_path:
            raise exception

        qemu_path = which(qemu_path)
        if qemu_path:
            self._qemu = qemu_path

            args = [qemu_path]
            if self.argv:
                args += ['-0', self.argv[0]]
            args += ['--']

            return [args, qemu_path]

        # If we get here, we couldn't run the binary directly, and
        # we don't have a qemu which can run it.
        self.exception(exception)
Esempio n. 2
0
def debug(args,
          gdbscript=None,
          exe=None,
          ssh=None,
          env=None,
          sysroot=None,
          **kwargs):
    r"""
    Launch a GDB server with the specified command line,
    and launches GDB to attach to it.

    Arguments:
        args(list): Arguments to the process, similar to :class:`.process`.
        gdbscript(str): GDB script to run.
        exe(str): Path to the executable on disk
        env(dict): Environment to start the binary in
        ssh(:class:`.ssh`): Remote ssh session to use to launch the process.
        sysroot(str): Foreign-architecture sysroot, used for QEMU-emulated binaries
            and Android targets.

    Returns:
        :class:`.process` or :class:`.ssh_channel`: A tube connected to the target process

    Notes:

        The debugger is attached automatically, and you can debug everything
        from the very beginning.  This requires that both ``gdb`` and ``gdbserver``
        are installed on your machine.

        When GDB opens via :func:`debug`, it will initially be stopped on the very first
        instruction of the dynamic linker (``ld.so``) for dynamically-linked binaries.

        Only the target binary and the linker will be loaded in memory, so you cannot
        set breakpoints on shared library routines like ``malloc`` since ``libc.so``
        has not even been loaded yet.

        There are several ways to handle this:

        1. Set a breakpoint on the executable's entry point (generally, ``_start``)
            - This is only invoked after all of the required shared libraries
              are loaded.
            - You can generally get the address via the GDB command ``info file``.
        2. Use pending breakpoints via ``set breakpoint pending on``
            - This has the side-effect of setting breakpoints for **every** function
              which matches the name.  For ``malloc``, this will generally set a
              breakpoint in the executable's PLT, in the linker's internal ``malloc``,
              and eventaully in ``libc``'s malloc.
        3. Wait for libraries to be loaded with ``set stop-on-solib-event 1``
            - There is no way to stop on any specific library being loaded, and sometimes
              multiple libraries are loaded and only a single breakpoint is issued.
            - Generally, you just add a few ``continue`` commands until things are set up
              the way you want it to be.

    Examples:

    >>> # Create a new process, and stop it at 'main'
    >>> io = gdb.debug('bash', '''
    ... break main
    ... continue
    ... ''')
    >>> # Send a command to Bash
    >>> io.sendline("echo hello")
    >>> io.recvline()
    b'hello\n'
    >>> # Interact with the process
    >>> io.interactive() # doctest: +SKIP
    >>> io.close()

    >>> # Create a new process, and stop it at '_start'
    >>> io = gdb.debug('bash', '''
    ... # Wait until we hit the main executable's entry point
    ... break _start
    ... continue
    ...
    ... # Now set breakpoint on shared library routines
    ... break malloc
    ... break free
    ... continue
    ... ''')
    >>> # Send a command to Bash
    >>> io.sendline("echo hello")
    >>> io.recvline()
    b'hello\n'
    >>> # Interact with the process
    >>> io.interactive() # doctest: +SKIP
    >>> io.close()

    You can use :func:`debug` to spawn new processes on remote machines as well,
    by using the ``ssh=`` keyword to pass in your :class:`.ssh` instance.

    >>> # Connect to the SSH server
    >>> # Start a process on the server
    >>> shell = ssh('travis', 'example.pwnme', password='******')
    >>> io = gdb.debug(['bash'],
    ...                 ssh = shell,
    ...                 gdbscript = '''
    ... break main
    ... continue
    ... ''')
    >>> # Send a command to Bash
    >>> io.sendline("echo hello")
    >>> # Interact with the process
    >>> io.interactive() # doctest: +SKIP
    >>> io.close()
    """
    if isinstance(
            args, six.integer_types +
        (tubes.process.process, tubes.ssh.ssh_channel)):
        log.error("Use gdb.attach() to debug a running process")

    if isinstance(args, (bytes, six.text_type)):
        args = [args]

    orig_args = args

    runner = _get_runner(ssh)
    which = _get_which(ssh)
    gdbscript = gdbscript or ''

    if context.noptrace:
        log.warn_once("Skipping debugger since context.noptrace==True")
        return runner(args, executable=exe, env=env)

    if ssh or context.native or (context.os == 'android'):
        args = _gdbserver_args(args=args, which=which, env=env)
    else:
        qemu_port = random.randint(1024, 65535)
        qemu_user = qemu.user_path()
        sysroot = sysroot or qemu.ld_prefix(env=env)
        if not qemu_user:
            log.error(
                "Cannot debug %s binaries without appropriate QEMU binaries" %
                context.arch)
        qemu_args = [qemu_user, '-g', str(qemu_port)]
        if sysroot:
            qemu_args += ['-L', sysroot]
        args = qemu_args + args

    # Use a sane default sysroot for Android
    if not sysroot and context.os == 'android':
        sysroot = 'remote:/'

    # Make sure gdbserver/qemu is installed
    if not which(args[0]):
        log.error("%s is not installed" % args[0])

    if not ssh:
        exe = exe or which(orig_args[0])
        if not (exe and os.path.exists(exe)):
            log.error("%s does not exist" % exe)

    # Start gdbserver/qemu
    # (Note: We override ASLR here for the gdbserver process itself.)
    gdbserver = runner(args, env=env, aslr=1, **kwargs)

    # Set the .executable on the process object.
    gdbserver.executable = exe

    # Find what port we need to connect to
    if context.native or (context.os == 'android'):
        port = _gdbserver_port(gdbserver, ssh)
    else:
        port = qemu_port

    host = '127.0.0.1'
    if not ssh and context.os == 'android':
        host = context.adb_host

    attach((host, port),
           exe=exe,
           gdbscript=gdbscript,
           ssh=ssh,
           sysroot=sysroot)

    # gdbserver outputs a message when a client connects
    garbage = gdbserver.recvline(timeout=1)

    # Some versions of gdbserver output an additional message
    garbage2 = gdbserver.recvline_startswith(b"Remote debugging from host ",
                                             timeout=1)

    return gdbserver
Esempio n. 3
0
def debug(args, gdbscript=None, exe=None, ssh=None, env=None, **kwargs):
    """debug(args) -> tube

    Launch a GDB server with the specified command line,
    and launches GDB to attach to it.

    Arguments:
        args(list): Arguments to the process, similar to :class:`.process`.
        gdbscript(str): GDB script to run.
        exe(str): Path to the executable on disk
        env(dict): Environment to start the binary in
        ssh(:class:`.ssh`): Remote ssh session to use to launch the process.

    Returns:
        :class:`.process` or :class:`.ssh_channel`: A tube connected to the target process

    Notes:

        The debugger is attached automatically, and you can debug everything
        from the very beginning.  This requires that both ``gdb`` and ``gdbserver``
        are installed on your machine.

        When GDB opens via :func:`debug`, it will initially be stopped on the very first
        instruction of the dynamic linker (``ld.so``) for dynamically-linked binaries.

        Only the target binary and the linker will be loaded in memory, so you cannot
        set breakpoints on shared library routines like ``malloc`` since ``libc.so``
        has not even been loaded yet.

        There are several ways to handle this:

        1. Set a breakpoint on the executable's entry point (generally, ``_start``)
            - This is only invoked after all of the required shared libraries
              are loaded.
            - You can generally get the address via the GDB command ``info file``.
        2. Use pending breakpoints via ``set breakpoint pending on``
            - This has the side-effect of setting breakpoints for **every** function
              which matches the name.  For ``malloc``, this will generally set a
              breakpoint in the executable's PLT, in the linker's internal ``malloc``,
              and eventaully in ``libc``'s malloc.
        3. Wait for libraries to be loaded with ``set stop-on-solib-event 1``
            - There is no way to stop on any specific library being loaded, and sometimes
              multiple libraries are loaded and only a single breakpoint is issued.
            - Generally, you just add a few ``continue`` commands until things are set up
              the way you want it to be.

    Examples:

        .. code-block:: python

            # Create a new process, and stop it at 'main'
            io = gdb.debug('bash', '''
            break main
            continue
            ''')

            # Send a command to Bash
            io.sendline("echo hello")

            # Interact with the process
            io.interactive()

        .. code-block:: python

            # Create a new process, and stop it at 'main'
            io = gdb.debug('bash', '''
            # Wait until we hit the main executable's entry point
            break _start
            continue

            # Now set breakpoint on shared library routines
            break malloc
            break free
            continue
            ''')

            # Send a command to Bash
            io.sendline("echo hello")

            # Interact with the process
            io.interactive()

        You can use :func:`debug` to spawn new processes on remote machines as well,
        by using the ``ssh=`` keyword to pass in your :class:`.ssh` instance.

        .. code-block:: python

            # Connect to the SSH server
            shell = ssh('passcode', 'pwnable.kr', 2222, password='******')

            # Start a process on the server
            io = gdb.debug(['bash'],
                            ssh=shell,
                            gdbscript='''
            break main
            continue
            ''')

            # Send a command to Bash
            io.sendline("echo hello")

            # Interact with the process
            io.interactive()
    """
    if isinstance(args, (int, tubes.process.process, tubes.ssh.ssh_channel)):
        log.error("Use gdb.attach() to debug a running process")

    if env is None:
        env = os.environ

    if isinstance(args, (str, unicode)):
        args = [args]

    orig_args = args

    runner = _get_runner(ssh)
    which = _get_which(ssh)
    sysroot = None
    gdbscript = gdbscript or ''

    if context.noptrace:
        log.warn_once("Skipping debugger since context.noptrace==True")
        return runner(args, executable=exe, env=env)

    if ssh or context.native or (context.os == 'android'):
        args = _gdbserver_args(args=args, which=which)
    else:
        qemu_port = random.randint(1024, 65535)
        qemu_user = qemu.user_path()
        sysroot = qemu.ld_prefix(env)
        if not qemu_user:
            log.error(
                "Cannot debug %s binaries without appropriate QEMU binaries" %
                context.arch)
        args = [qemu_user, '-g', str(qemu_port)] + args

    # Make sure gdbserver/qemu is installed
    if not which(args[0]):
        log.error("%s is not installed" % args[0])

    exe = exe or which(orig_args[0])
    if not exe:
        log.error("%s does not exist" % orig_args[0])
    else:
        gdbscript = 'file %s\n%s' % (exe, gdbscript)

    # Start gdbserver/qemu
    # (Note: We override ASLR here for the gdbserver process itself.)
    gdbserver = runner(args, env=env, aslr=1, **kwargs)

    # Set the .executable on the process object.
    gdbserver.executable = which(orig_args[0])

    # Find what port we need to connect to
    if context.native or (context.os == 'android'):
        port = _gdbserver_port(gdbserver, ssh)
    else:
        port = qemu_port

    host = '127.0.0.1'
    if not ssh and context.os == 'android':
        host = context.adb_host

    attach((host, port),
           exe=exe,
           gdbscript=gdbscript,
           need_ptrace_scope=False,
           ssh=ssh,
           sysroot=sysroot)

    # gdbserver outputs a message when a client connects
    garbage = gdbserver.recvline(timeout=1)

    if "Remote debugging from host" not in garbage:
        gdbserver.unrecv(garbage)

    return gdbserver