Ejemplo 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 = get_qemu_user(arch=binary.arch)
        qemu = which(qemu)
        if qemu:
            self._qemu = qemu

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

            return [args, qemu]

        # 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)
Ejemplo n.º 2
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 = get_qemu_user(arch=binary.arch)

        if not qemu:
            raise exception

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

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

            return [args, qemu]

        # 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)
Ejemplo 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.

        .. code-block:: python

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

        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.

        .. 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
            ''')

        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
            bash = gdb.debug(['bash'],
                            ssh=shell,
                            gdbscript='''
            break main
            continue
            ''')
    """
    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)

    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)
        args = [get_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])

    # 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)

    # 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
Ejemplo n.º 4
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.

        .. code-block:: python

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

        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.

        .. 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
            ''')

        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
            bash = gdb.debug(['bash'],
                            ssh=shell,
                            gdbscript='''
            break main
            continue
            ''')
    """
    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)

    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)
        args = [get_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])

    # 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)

    # 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