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