Esempio n. 1
0
def debug_shellcode(data, gdbscript=None, vma=None):
    """
    Creates an ELF file, and launches it under a debugger.

    Arguments:
        data(str): Assembled shellcode bytes
        gdbscript(str): Script to run in GDB
        vma(int): Base address to load the shellcode at
        **kwargs: Override any :obj:`.context` values.

    Returns:
        :class:`.process`
    """
    if isinstance(data, unicode):
        log.error(
            "Shellcode is cannot be unicode.  Did you mean debug_assembly?")
    tmp_elf = make_elf(data, extract=False, vma=vma)
    os.chmod(tmp_elf, 0777)

    atexit.register(lambda: os.unlink(tmp_elf))

    if context.os == 'android':
        android_path = '/data/data/%s' % os.path.basename(tmp_elf)
        adb.push(tmp_elf, android_path)
        tmp_elf = android_path

    return debug(tmp_elf, gdbscript=gdbscript, arch=context.arch)
Esempio n. 2
0
def debug_shellcode(data, gdbscript=None, vma=None):
    """
    Creates an ELF file, and launches it under a debugger.

    Arguments:
        data(str): Assembled shellcode bytes
        gdbscript(str): Script to run in GDB
        vma(int): Base address to load the shellcode at
        **kwargs: Override any :obj:`.context` values.

    Returns:
        :class:`.process`
    """
    if isinstance(data, unicode):
        log.error("Shellcode is cannot be unicode.  Did you mean debug_assembly?")
    tmp_elf = make_elf(data, extract=False, vma=vma)
    os.chmod(tmp_elf, 0777)

    atexit.register(lambda: os.unlink(tmp_elf))

    if context.os == 'android':
        android_path = '/data/data/%s' % os.path.basename(tmp_elf)
        adb.push(tmp_elf, android_path)
        tmp_elf = android_path

    return debug(tmp_elf, gdbscript=gdbscript, arch=context.arch)
Esempio n. 3
0
def debug_shellcode(data, gdbscript=None, vma=None):
    r"""debug_shellcode(data, gdbscript=None, vma=None) -> tube
    Creates an ELF file, and launches it under a debugger.

    Arguments:
        data(str): Assembled shellcode bytes
        gdbscript(str): Script to run in GDB
        vma(int): Base address to load the shellcode at
        \**kwargs: Override any :obj:`pwnlib.context.context` values.

    Returns:
        :class:`.process`

    Example:

    >>> assembly = shellcraft.echo("Hello world!\n")
    >>> shellcode = asm(assembly)
    >>> io = gdb.debug_shellcode(shellcode)
    >>> io.recvline()
    b'Hello world!\n'
    """
    if isinstance(data, six.text_type):
        log.error(
            "Shellcode is cannot be unicode.  Did you mean debug_assembly?")
    tmp_elf = make_elf(data, extract=False, vma=vma)
    os.chmod(tmp_elf, 0o777)

    atexit.register(lambda: os.unlink(tmp_elf))

    if context.os == 'android':
        android_path = '/data/data/%s' % os.path.basename(tmp_elf)
        adb.push(tmp_elf, android_path)
        tmp_elf = android_path

    return debug(tmp_elf, gdbscript=gdbscript, arch=context.arch)
Esempio n. 4
0
def debug_assembly(asm, gdbscript=None, vma=None):
    """debug_assembly(asm, gdbscript=None, vma=None) -> tube

    Creates an ELF file, and launches it under a debugger.

    This is identical to debug_shellcode, except that
    any defined symbols are available in GDB, and it
    saves you the explicit call to asm().

    Arguments:
        asm(str): Assembly code to debug
        gdbscript(str): Script to run in GDB
        vma(int): Base address to load the shellcode at
        **kwargs: Override any :obj:`.context` values.

    Returns:
        :class:`.process`
    """
    tmp_elf = make_elf_from_assembly(asm, vma=vma, extract=False)
    os.chmod(tmp_elf, 0777)

    atexit.register(lambda: os.unlink(tmp_elf))

    if context.os == 'android':
        android_path = '/data/data/%s' % os.path.basename(tmp_elf)
        adb.push(tmp_elf, android_path)
        tmp_elf = android_path

    return debug(tmp_elf, gdbscript=gdbscript, arch=context.arch)
Esempio n. 5
0
def debug_assembly(asm, gdbscript=None, vma=None):
    """debug_assembly(asm, gdbscript=None, vma=None) -> tube

    Creates an ELF file, and launches it under a debugger.

    This is identical to debug_shellcode, except that
    any defined symbols are available in GDB, and it
    saves you the explicit call to asm().

    Arguments:
        asm(str): Assembly code to debug
        gdbscript(str): Script to run in GDB
        vma(int): Base address to load the shellcode at
        **kwargs: Override any :obj:`.context` values.

    Returns:
        :class:`.process`
    """
    tmp_elf = make_elf_from_assembly(asm, vma=vma, extract=False)
    os.chmod(tmp_elf, 0777)

    atexit.register(lambda: os.unlink(tmp_elf))

    if context.os == 'android':
        android_path = '/data/data/%s' % os.path.basename(tmp_elf)
        adb.push(tmp_elf, android_path)
        tmp_elf = android_path

    return debug(tmp_elf, gdbscript=gdbscript, arch=context.arch)
Esempio n. 6
0
    def __init__(self, timeout=default, level=None, *a, **kw):
        super(tube, self).__init__(timeout)

        Logger.__init__(self, None)
        if level is not None:
            self.setLevel(level)

        self.buffer = Buffer(*a, **kw)
        atexit.register(self.close)
Esempio n. 7
0
def debug_assembly(asm, gdbscript=None, vma=None, api=False):
    r"""debug_assembly(asm, gdbscript=None, vma=None, api=False) -> tube

    Creates an ELF file, and launches it under a debugger.

    This is identical to debug_shellcode, except that
    any defined symbols are available in GDB, and it
    saves you the explicit call to asm().

    Arguments:
        asm(str): Assembly code to debug
        gdbscript(str): Script to run in GDB
        vma(int): Base address to load the shellcode at
        api(bool): Enable access to GDB Python API
        \**kwargs: Override any :obj:`pwnlib.context.context` values.

    Returns:
        :class:`.process`

    Example:

    >>> assembly = shellcraft.echo("Hello world!\n")
    >>> io = gdb.debug_assembly(assembly)
    >>> io.recvline()
    b'Hello world!\n'
    """
    tmp_elf = make_elf_from_assembly(asm, vma=vma, extract=False)
    os.chmod(tmp_elf, 0o777)

    atexit.register(lambda: os.unlink(tmp_elf))

    if context.os == 'android':
        android_path = '/data/data/%s' % os.path.basename(tmp_elf)
        adb.push(tmp_elf, android_path)
        tmp_elf = android_path

    return debug(tmp_elf, gdbscript=gdbscript, arch=context.arch, api=api)
Esempio n. 8
0
def disasm(data, vma=0, byte=True, offset=True, instructions=True):
    """disasm(data, ...) -> str

    Disassembles a bytestring into human readable assembler.

    To see which architectures are supported,
    look in :mod:`pwnlib.contex`.

    To support all these architecture, we bundle the GNU objcopy
    and objdump with pwntools.

    Arguments:
      data(str): Bytestring to disassemble.
      vma(int): Passed through to the --adjust-vma argument of objdump
      byte(bool): Include the hex-printed bytes in the disassembly
      offset(bool): Include the virtual memory address in the disassembly

    Kwargs:
      Any arguments/properties that can be set on ``context``

    Examples:

        >>> print disasm('b85d000000'.decode('hex'), arch = 'i386')
           0:   b8 5d 00 00 00          mov    eax,0x5d
        >>> print disasm('b85d000000'.decode('hex'), arch = 'i386', byte = 0)
           0:   mov    eax,0x5d
        >>> print disasm('b85d000000'.decode('hex'), arch = 'i386', byte = 0, offset = 0)
        mov    eax,0x5d
        >>> print disasm('b817000000'.decode('hex'), arch = 'amd64')
           0:   b8 17 00 00 00          mov    eax,0x17
        >>> print disasm('48c7c017000000'.decode('hex'), arch = 'amd64')
           0:   48 c7 c0 17 00 00 00    mov    rax,0x17
        >>> print disasm('04001fe552009000'.decode('hex'), arch = 'arm')
           0:   e51f0004        ldr     r0, [pc, #-4]   ; 0x4
           4:   00900052        addseq  r0, r0, r2, asr r0
        >>> print disasm('4ff00500'.decode('hex'), arch = 'thumb', bits=32)
           0:   f04f 0005       mov.w   r0, #5
    """
    result = ''

    arch = context.arch
    os = context.os

    tmpdir = tempfile.mkdtemp(prefix='pwn-disasm-')
    step1 = path.join(tmpdir, 'step1')
    step2 = path.join(tmpdir, 'step2')

    bfdarch = _bfdarch()
    bfdname = _bfdname()
    objdump = _objdump() + ['-d', '--adjust-vma', str(vma), '-b', bfdname]
    objcopy = _objcopy() + [
        '-I',
        'binary',
        '-O',
        bfdname,
        '-B',
        bfdarch,
        '--set-section-flags',
        '.data=code',
        '--rename-section',
        '.data=.text',
    ]

    if arch == 'thumb':
        objcopy += ['--prefix-symbol=$t.']
    else:
        objcopy += ['-w', '-N', '*']

    try:

        with open(step1, 'w') as fd:
            fd.write(data)

        res = _run(objcopy + [step1, step2])

        output0 = _run(objdump + [step2])
        output1 = output0.split('<.text>:\n')

        if len(output1) != 2:
            log.error('Could not find .text in objdump output:\n%s' % output0)

        result = output1[1].strip('\n').rstrip().expandtabs()
    except Exception:
        log.exception("An error occurred while disassembling:\n%s" % data)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))

    lines = []
    pattern = '^( *[0-9a-f]+: *)((?:[0-9a-f]+ )+ *)(.*)'
    for line in result.splitlines():
        try:
            o, b, i = re.search(pattern, line).groups()
        except:
            lines.append(line)
            continue

        line = ''

        if offset:
            line += o
        if byte:
            line += b
        if instructions:
            line += i
        lines.append(line)

    return '\n'.join(lines)
Esempio n. 9
0
def make_elf(data, vma=None, strip=True, extract=True, shared=False):
    r"""
    Builds an ELF file with the specified binary data as its
    executable code.

    Arguments:
        data(str): Assembled code
        vma(int):  Load address for the ELF file

    Examples:

        This example creates an i386 ELF that just does
        execve('/bin/sh',...).

        >>> context.clear()
        >>> context.arch = 'i386'
        >>> context.bits = 32
        >>> filename = tempfile.mktemp()
        >>> bin_sh = '6a68682f2f2f73682f62696e89e331c96a0b5899cd80'.decode('hex')
        >>> data = make_elf(bin_sh)
        >>> with open(filename,'wb+') as f:
        ...     f.write(data)
        ...     f.flush()
        >>> os.chmod(filename,0777)
        >>> p = process(filename)
        >>> p.sendline('echo Hello; exit')
        >>> p.recvline()
        'Hello\n'
    """
    retval = None

    if shared and vma:
        log.error("Cannot specify a VMA for a shared library.")

    if context.arch == 'thumb':
        to_thumb = asm(shellcraft.arm.to_thumb(), arch='arm')

        if not data.startswith(to_thumb):
            data = to_thumb + data

    assembler = _assembler()
    linker = _linker()
    code = _arch_header()
    code += '.string "%s"' % ''.join('\\x%02x' % ord(c) for c in data)
    code += '\n'

    log.debug("Building ELF:\n" + code)

    tmpdir = tempfile.mkdtemp(prefix='pwn-asm-')
    step1 = path.join(tmpdir, 'step1-asm')
    step2 = path.join(tmpdir, 'step2-obj')
    step3 = path.join(tmpdir, 'step3-elf')

    try:
        with open(step1, 'wb+') as f:
            f.write(code)

        _run(assembler + ['-o', step2, step1])

        linker_options = ['-z', 'execstack']
        if vma:
            linker_options += [
                '--section-start=.shellcode=%#x' % vma,
                '--entry=%#x' % vma
            ]
        elif shared:
            linker_options += ['-shared', '-init=_start']

        linker_options += ['-o', step3, step2]

        _run(linker + linker_options)

        if strip:
            _run([which_binutils('objcopy'), '-Sg', step3])
            _run([which_binutils('strip'), '--strip-unneeded', step3])

        if not extract:
            os.chmod(step3, 0755)
            retval = step3

        else:
            with open(step3, 'r') as f:
                retval = f.read()
    except Exception:
        log.exception("An error occurred while building an ELF:\n%s" % code)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))

    return retval
Esempio n. 10
0
def make_elf(data, vma=None, strip=True, extract=True, shared=False):
    r"""make_elf(data, vma=None, strip=True, extract=True, shared=False, **kwargs) -> str

    Builds an ELF file with the specified binary data as its executable code.

    Arguments:
        data(str): Assembled code
        vma(int):  Load address for the ELF file
        strip(bool): Strip the resulting ELF file. Only matters if ``extract=False``.
            (Default: ``True``)
        extract(bool): Extract the assembly from the ELF file.
            If ``False``, the path of the ELF file is returned.
            (Default: ``True``)
        shared(bool): Create a Dynamic Shared Object (DSO, i.e. a ``.so``)
            which can be loaded via ``dlopen`` or ``LD_PRELOAD``.

    Examples:
        This example creates an i386 ELF that just does
        execve('/bin/sh',...).

        >>> context.clear(arch='i386')
        >>> bin_sh = '6a68682f2f2f73682f62696e89e331c96a0b5899cd80'.decode('hex')
        >>> filename = make_elf(bin_sh, extract=False)
        >>> p = process(filename)
        >>> p.sendline('echo Hello; exit')
        >>> p.recvline()
        'Hello\n'
    """
    retval = None

    if shared and vma:
        log.error("Cannot specify a VMA for a shared library.")

    if context.arch == 'thumb':
        to_thumb = asm(shellcraft.arm.to_thumb(), arch='arm')

        if not data.startswith(to_thumb):
            data = to_thumb + data

    assembler = _assembler()
    linker = _linker()
    code = _arch_header()
    code += '.string "%s"' % ''.join('\\x%02x' % ord(c) for c in data)
    code += '\n'

    log.debug("Building ELF:\n" + code)

    tmpdir = tempfile.mkdtemp(prefix='pwn-asm-')
    step1 = path.join(tmpdir, 'step1-asm')
    step2 = path.join(tmpdir, 'step2-obj')
    step3 = path.join(tmpdir, 'step3-elf')

    try:
        with open(step1, 'wb+') as f:
            f.write(code)

        _run(assembler + ['-o', step2, step1])

        linker_options = ['-z', 'execstack']
        if vma:
            linker_options += [
                '--section-start=.shellcode=%#x' % vma,
                '--entry=%#x' % vma
            ]
        elif shared:
            linker_options += ['-shared', '-init=_start']

        linker_options += ['-o', step3, step2]

        _run(linker + linker_options)

        if strip:
            _run([which_binutils('objcopy'), '-Sg', step3])
            _run([which_binutils('strip'), '--strip-unneeded', step3])

        if not extract:
            os.chmod(step3, 0o755)
            retval = step3

        else:
            with open(step3, 'r') as f:
                retval = f.read()
    except Exception:
        log.exception("An error occurred while building an ELF:\n%s" % code)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))

    return retval
Esempio n. 11
0
def asm(shellcode, vma=0, extract=True, shared=False):
    r"""asm(code, vma = 0, extract = True, shared = False, ...) -> str

    Runs :func:`cpp` over a given shellcode and then assembles it into bytes.

    To see which architectures or operating systems are supported,
    look in :mod:`pwnlib.context`.

    Assembling shellcode requires that the GNU assembler is installed
    for the target architecture.
    See :doc:`Installing Binutils </install/binutils>` for more information.

    Arguments:
        shellcode(str): Assembler code to assemble.
        vma(int):       Virtual memory address of the beginning of assembly
        extract(bool):  Extract the raw assembly bytes from the assembled
                        file.  If :const:`False`, returns the path to an ELF file
                        with the assembly embedded.
        shared(bool):   Create a shared object.
        kwargs(dict):   Any attributes on :data:`.context` can be set, e.g.set
                        ``arch='arm'``.

    Examples:

        >>> asm("mov eax, SYS_select", arch = 'i386', os = 'freebsd')
        '\xb8]\x00\x00\x00'
        >>> asm("mov eax, SYS_select", arch = 'amd64', os = 'linux')
        '\xb8\x17\x00\x00\x00'
        >>> asm("mov rax, SYS_select", arch = 'amd64', os = 'linux')
        'H\xc7\xc0\x17\x00\x00\x00'
        >>> asm("mov r0, #SYS_select", arch = 'arm', os = 'linux', bits=32)
        'R\x00\xa0\xe3'
    """
    result = ''

    assembler = _assembler()
    linker = _linker()
    objcopy = _objcopy() + ['-j', '.shellcode', '-Obinary']
    code = ''
    code += _arch_header()
    code += cpp(shellcode)

    log.debug('Assembling\n%s' % code)

    tmpdir = tempfile.mkdtemp(prefix='pwn-asm-')
    step1 = path.join(tmpdir, 'step1')
    step2 = path.join(tmpdir, 'step2')
    step3 = path.join(tmpdir, 'step3')
    step4 = path.join(tmpdir, 'step4')

    try:
        with open(step1, 'w') as fd:
            fd.write(code)

        _run(assembler + ['-o', step2, step1])

        if not vma:
            shutil.copy(step2, step3)

        if vma or not extract:
            ldflags = ['-z', 'execstack', '-o', step3, step2]
            if vma:
                ldflags += [
                    '--section-start=.shellcode=%#x' % vma,
                    '--entry=%#x' % vma
                ]
            elif shared:
                ldflags += ['-shared', '-init=_start']

            # In order to ensure that we generate ELF files with 4k pages,
            # and not e.g. 65KB pages (AArch64), force the page size.
            # This is a result of GNU Gold being silly.
            #
            # Introduced in commit dd58f409 without supporting evidence,
            # this shouldn't do anything except keep consistent page granularity
            # across architectures.
            ldflags += [
                '-z', 'max-page-size=4096', '-z', 'common-page-size=4096'
            ]

            _run(linker + ldflags)

        elif file(step2, 'rb').read(4) == '\x7fELF':
            # Sanity check for seeing if the output has relocations
            relocs = subprocess.check_output(
                [which_binutils('readelf'), '-r', step2]).strip()
            if extract and len(relocs.split('\n')) > 1:
                log.error('Shellcode contains relocations:\n%s' % relocs)
        else:
            shutil.copy(step2, step3)

        if not extract:
            return step3

        _run(objcopy + [step3, step4])

        with open(step4) as fd:
            result = fd.read()

    except Exception:
        lines = '\n'.join('%4i: %s' % (i + 1, line)
                          for (i, line) in enumerate(code.splitlines()))
        log.exception("An error occurred while assembling:\n%s" % lines)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))

    return result
Esempio n. 12
0
    def __init__(self, proc):
        if proc.poll() is None:
            log.error("Process %i has not exited" % (process.pid))

        self.process = proc
        self.pid = proc.pid
        self.uid = proc.suid
        self.gid = proc.sgid
        self.exe = proc.executable
        self.basename = os.path.basename(self.exe)
        self.cwd = proc.cwd

        # XXX: Should probably break out all of this logic into
        #      its own class, so that we can support "file ops"
        #      locally, via SSH, and over ADB, in a transparent way.
        if isinstance(proc, process):
            self.read = read
            self.unlink = os.unlink
        elif isinstance(proc, ssh_channel):
            self.read = proc.parent.read
            self.unlink = proc.parent.unlink

        self.kernel_core_pattern = self.read('/proc/sys/kernel/core_pattern').strip()
        self.kernel_core_uses_pid = bool(int(self.read('/proc/sys/kernel/core_uses_pid')))

        log.debug("core_pattern: %r" % self.kernel_core_pattern)
        log.debug("core_uses_pid: %r" % self.kernel_core_uses_pid)

        self.interpreter = self.binfmt_lookup()

        log.debug("interpreter: %r" % self.interpreter)

        # If we have already located the corefile, we will
        # have renamed it to 'core.<pid>'
        core_path = 'core.%i' % (proc.pid)
        self.core_path = None

        if os.path.isfile(core_path):
            log.debug("Found core immediately: %r" % core_path)
            self.core_path = core_path

        # Try QEMU first, since it's unlikely to be a false-positive unless
        # there is a PID *and* filename collision.
        if not self.core_path:
            log.debug("Looking for QEMU corefile")
            self.core_path = self.qemu_corefile()

        # Check for native coredumps as a last resort
        if not self.core_path:
            log.debug("Looking for native corefile")
            self.core_path = self.native_corefile()

        if not self.core_path:
            return

        core_pid = self.load_core_check_pid()

        # Move the corefile if we're configured that way
        if context.rename_corefiles:
            new_path = 'core.%i' % core_pid
            if core_pid > 0 and new_path != self.core_path:
                write(new_path, self.read(self.core_path))
                self.core_path = new_path

        # Check the PID
        if core_pid != self.pid:
            log.warn("Corefile PID does not match! (got %i)" % core_pid)

        # Register the corefile for removal only if it's an exact match
        elif context.delete_corefiles:
            atexit.register(lambda: os.unlink(self.core_path))
Esempio n. 13
0
def run_in_new_terminal(command, terminal=None, args=None, kill_at_exit=True):
    """run_in_new_terminal(command, terminal = None, args = None, kill_at_exit = True) -> int

    Run a command in a new terminal.

    When ``terminal`` is not set:
        - If ``context.terminal`` is set it will be used.
          If it is an iterable then ``context.terminal[1:]`` are default arguments.
        - If a ``pwntools-terminal`` command exists in ``$PATH``, it is used
        - If tmux is detected (by the presence of the ``$TMUX`` environment
          variable), a new pane will be opened.
        - If GNU Screen is detected (by the presence of the ``$STY`` environment
          variable), a new screen will be opened.
        - If ``$TERM_PROGRAM`` is set, that is used.
        - If X11 is detected (by the presence of the ``$DISPLAY`` environment
          variable), ``x-terminal-emulator`` is used.
        - If WSL (Windows Subsystem for Linux) is detected (by the presence of
          a ``wsl.exe`` binary in the ``$PATH`` and ``/proc/sys/kernel/osrelease``
          containing ``Microsoft``), a new ``cmd.exe`` window will be opened.

    If `kill_at_exit` is :const:`True`, try to close the command/terminal when the
    current process exits. This may not work for all terminal types.

    Arguments:
        command (str): The command to run.
        terminal (str): Which terminal to use.
        args (list): Arguments to pass to the terminal
        kill_at_exit (bool): Whether to close the command/terminal on process exit.

    Note:
        The command is opened with ``/dev/null`` for stdin, stdout, stderr.

    Returns:
      PID of the new terminal process
    """
    if not terminal:
        if context.terminal:
            terminal = context.terminal[0]
            args = context.terminal[1:]
        elif which('pwntools-terminal'):
            terminal = 'pwntools-terminal'
            args = []
        elif 'TMUX' in os.environ and which('tmux'):
            terminal = 'tmux'
            args = ['splitw', '-F' '#{pane_pid}', '-P']
        elif 'STY' in os.environ and which('screen'):
            terminal = 'screen'
            args = ['-t', 'pwntools-gdb', 'bash', '-c']
        elif 'TERM_PROGRAM' in os.environ:
            terminal = os.environ['TERM_PROGRAM']
            args = []
        elif 'DISPLAY' in os.environ and which('x-terminal-emulator'):
            terminal = 'x-terminal-emulator'
            args = ['-e']
        else:
            is_wsl = False
            if os.path.exists('/proc/sys/kernel/osrelease'):
                with open('/proc/sys/kernel/osrelease', 'rb') as f:
                    is_wsl = b'icrosoft' in f.read()
            if is_wsl and which('cmd.exe') and which('wsl.exe') and which(
                    'bash.exe'):
                terminal = 'cmd.exe'
                args = ['/c', 'start', 'bash.exe', '-c']

    if not terminal:
        log.error(
            'Could not find a terminal binary to use. Set context.terminal to your terminal.'
        )
    elif not which(terminal):
        log.error(
            'Could not find terminal binary %r. Set context.terminal to your terminal.'
            % terminal)

    if isinstance(args, tuple):
        args = list(args)

    argv = [which(terminal)] + args

    if isinstance(command, six.string_types):
        if ';' in command:
            log.error(
                "Cannot use commands with semicolon.  Create a script and invoke that directly."
            )
        argv += [command]
    elif isinstance(command, (list, tuple)):
        if any(';' in c for c in command):
            log.error(
                "Cannot use commands with semicolon.  Create a script and invoke that directly."
            )
        argv += list(command)

    log.debug("Launching a new terminal: %r" % argv)

    stdin = stdout = stderr = open(os.devnull, 'r+b')
    if terminal == 'tmux':
        stdout = subprocess.PIPE

    p = subprocess.Popen(argv, stdin=stdin, stdout=stdout, stderr=stderr)

    if terminal == 'tmux':
        out, _ = p.communicate()
        pid = int(out)
    else:
        pid = p.pid

    if kill_at_exit:
        if terminal == 'tmux':
            atexit.register(lambda: os.kill(pid, signal.SIGTERM))
        else:
            atexit.register(lambda: p.terminate())

    return pid
Esempio n. 14
0
def asm(shellcode, vma = 0, extract = True, shared = False):
    r"""asm(code, vma = 0, extract = True, shared = False, ...) -> str

    Runs :func:`cpp` over a given shellcode and then assembles it into bytes.

    To see which architectures or operating systems are supported,
    look in :mod:`pwnlib.contex`.

    Assembling shellcode requires that the GNU assembler is installed
    for the target architecture.
    See :doc:`Installing Binutils </install/binutils>` for more information.

    Arguments:
        shellcode(str): Assembler code to assemble.
        vma(int):       Virtual memory address of the beginning of assembly
        extract(bool):  Extract the raw assembly bytes from the assembled
                        file.  If :const:`False`, returns the path to an ELF file
                        with the assembly embedded.
        shared(bool):   Create a shared object.
        kwargs(dict):   Any attributes on :data:`.context` can be set, e.g.set
                        ``arch='arm'``.

    Examples:

        >>> asm("mov eax, SYS_select", arch = 'i386', os = 'freebsd')
        '\xb8]\x00\x00\x00'
        >>> asm("mov eax, SYS_select", arch = 'amd64', os = 'linux')
        '\xb8\x17\x00\x00\x00'
        >>> asm("mov rax, SYS_select", arch = 'amd64', os = 'linux')
        'H\xc7\xc0\x17\x00\x00\x00'
        >>> asm("mov r0, #SYS_select", arch = 'arm', os = 'linux', bits=32)
        'R\x00\xa0\xe3'
    """
    result = ''

    assembler = _assembler()
    linker    = _linker()
    objcopy   = _objcopy() + ['-j', '.shellcode', '-Obinary']
    code      = ''
    code      += _arch_header()
    code      += cpp(shellcode)

    log.debug('Assembling\n%s' % code)

    tmpdir    = tempfile.mkdtemp(prefix = 'pwn-asm-')
    step1     = path.join(tmpdir, 'step1')
    step2     = path.join(tmpdir, 'step2')
    step3     = path.join(tmpdir, 'step3')
    step4     = path.join(tmpdir, 'step4')

    try:
        with open(step1, 'w') as fd:
            fd.write(code)

        _run(assembler + ['-o', step2, step1])

        if not vma:
            shutil.copy(step2, step3)

        if vma or not extract:
            ldflags = ['-z', 'execstack', '-o', step3, step2]
            if vma:
                ldflags += ['--section-start=.shellcode=%#x' % vma,
                            '--entry=%#x' % vma]
            elif shared:
                ldflags += ['-shared', '-init=_start']

            # In order to ensure that we generate ELF files with 4k pages,
            # and not e.g. 65KB pages (AArch64), force the page size.
            # This is a result of GNU Gold being silly.
            #
            # Introduced in commit dd58f409 without supporting evidence,
            # this shouldn't do anything except keep consistent page granularity
            # across architectures.
            ldflags += ['-z', 'max-page-size=4096',
                        '-z', 'common-page-size=4096']

            _run(linker + ldflags)

        elif file(step2,'rb').read(4) == '\x7fELF':
            # Sanity check for seeing if the output has relocations
            relocs = subprocess.check_output(
                [which_binutils('readelf'), '-r', step2]
            ).strip()
            if extract and len(relocs.split('\n')) > 1:
                log.error('Shellcode contains relocations:\n%s' % relocs)
        else:
            shutil.copy(step2, step3)

        if not extract:
            return step3

        _run(objcopy + [step3, step4])

        with open(step4) as fd:
            result = fd.read()

    except Exception:
        lines = '\n'.join('%4i: %s' % (i+1,line) for (i,line) in enumerate(code.splitlines()))
        log.exception("An error occurred while assembling:\n%s" % lines)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))

    return result
Esempio n. 15
0
def forward(port):
    """Sets up a port to forward to the device."""
    tcp_port = 'tcp:%s' % port
    start_forwarding = adb(['forward', tcp_port, tcp_port])
    atexit.register(lambda: adb(['forward', '--remove', tcp_port]))
Esempio n. 16
0
def asm(shellcode, vma=0, extract=True, shared=False):
    r"""asm(code, vma = 0, extract = True, ...) -> str

    Runs :func:`cpp` over a given shellcode and then assembles it into bytes.

    To see which architectures or operating systems are supported,
    look in :mod:`pwnlib.contex`.

    To support all these architecture, we bundle the GNU assembler
    and objcopy with pwntools.

    Arguments:
      shellcode(str): Assembler code to assemble.
      vma(int):       Virtual memory address of the beginning of assembly
      extract(bool):  Extract the raw assembly bytes from the assembled
                      file.  If :const:`False`, returns the path to an ELF file
                      with the assembly embedded.

    Kwargs:
        Any arguments/properties that can be set on ``context``

    Examples:

        .. doctest::

            >>> asm("mov eax, SYS_select", arch = 'i386', os = 'freebsd')
            '\xb8]\x00\x00\x00'
            >>> asm("mov eax, SYS_select", arch = 'amd64', os = 'linux')
            '\xb8\x17\x00\x00\x00'
            >>> asm("mov rax, SYS_select", arch = 'amd64', os = 'linux')
            'H\xc7\xc0\x17\x00\x00\x00'
            >>> asm("mov r0, #SYS_select", arch = 'arm', os = 'linux', bits=32)
            'R\x00\xa0\xe3'
    """
    result = ''

    assembler = _assembler()
    linker = _linker()
    objcopy = _objcopy() + ['-j', '.shellcode', '-Obinary']
    code = ''
    code += _arch_header()
    code += cpp(shellcode)

    log.debug('Assembling\n%s' % code)

    tmpdir = tempfile.mkdtemp(prefix='pwn-asm-')
    step1 = path.join(tmpdir, 'step1')
    step2 = path.join(tmpdir, 'step2')
    step3 = path.join(tmpdir, 'step3')
    step4 = path.join(tmpdir, 'step4')

    try:
        with open(step1, 'w') as fd:
            fd.write(code)

        _run(assembler + ['-o', step2, step1])

        if not vma:
            shutil.copy(step2, step3)

        if vma or not extract:
            ldflags = ['-z', 'execstack', '-o', step3, step2]
            if vma:
                ldflags += [
                    '--section-start=.shellcode=%#x' % vma,
                    '--entry=%#x' % vma, '-z', 'max-page-size=4096', '-z',
                    'common-page-size=4096'
                ]
            elif shared:
                ldflags += ['-shared', '-init=_start']
            _run(linker + ldflags)

        elif file(step2, 'rb').read(4) == '\x7fELF':
            # Sanity check for seeing if the output has relocations
            relocs = subprocess.check_output(
                [which_binutils('readelf'), '-r', step2]).strip()
            if extract and len(relocs.split('\n')) > 1:
                log.error('Shellcode contains relocations:\n%s' % relocs)
        else:
            shutil.copy(step2, step3)

        if not extract:
            return step3

        _run(objcopy + [step3, step4])

        with open(step4) as fd:
            result = fd.read()

    except Exception:
        lines = '\n'.join('%4i: %s' % (i + 1, line)
                          for (i, line) in enumerate(code.splitlines()))
        log.exception("An error occurred while assembling:\n%s" % lines)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))

    return result
Esempio n. 17
0
def make_elf(data,
             vma=None,
             strip=True,
             extract=True,
             shared=False):
    r"""make_elf(data, vma=None, strip=True, extract=True, shared=False, **kwargs) -> str

    Builds an ELF file with the specified binary data as its executable code.

    Arguments:
        data(str): Assembled code
        vma(int):  Load address for the ELF file
        strip(bool): Strip the resulting ELF file. Only matters if ``extract=False``.
            (Default: ``True``)
        extract(bool): Extract the assembly from the ELF file.
            If ``False``, the path of the ELF file is returned.
            (Default: ``True``)
        shared(bool): Create a Dynamic Shared Object (DSO, i.e. a ``.so``)
            which can be loaded via ``dlopen`` or ``LD_PRELOAD``.

    Examples:
        This example creates an i386 ELF that just does
        execve('/bin/sh',...).

        >>> context.clear(arch='i386')
        >>> bin_sh = '6a68682f2f2f73682f62696e89e331c96a0b5899cd80'.decode('hex')
        >>> filename = make_elf(bin_sh, extract=False)
        >>> p = process(filename)
        >>> p.sendline('echo Hello; exit')
        >>> p.recvline()
        'Hello\n'
    """
    retval = None

    if shared and vma:
        log.error("Cannot specify a VMA for a shared library.")

    if context.arch == 'thumb':
        to_thumb = asm(shellcraft.arm.to_thumb(), arch='arm')

        if not data.startswith(to_thumb):
            data = to_thumb + data


    assembler = _assembler()
    linker    = _linker()
    code      = _arch_header()
    code      += '.string "%s"' % ''.join('\\x%02x' % ord(c) for c in data)
    code      += '\n'

    log.debug("Building ELF:\n" + code)

    tmpdir    = tempfile.mkdtemp(prefix = 'pwn-asm-')
    step1     = path.join(tmpdir, 'step1-asm')
    step2     = path.join(tmpdir, 'step2-obj')
    step3     = path.join(tmpdir, 'step3-elf')

    try:
        with open(step1, 'wb+') as f:
            f.write(code)

        _run(assembler + ['-o', step2, step1])

        linker_options = ['-z', 'execstack']
        if vma:
            linker_options += ['--section-start=.shellcode=%#x' % vma,
                               '--entry=%#x' % vma]
        elif shared:
            linker_options += ['-shared', '-init=_start']

        linker_options += ['-o', step3, step2]

        _run(linker + linker_options)

        if strip:
            _run([which_binutils('objcopy'), '-Sg', step3])
            _run([which_binutils('strip'), '--strip-unneeded', step3])

        if not extract:
            os.chmod(step3, 0755)
            retval = step3

        else:
            with open(step3, 'r') as f:
                retval = f.read()
    except Exception:
        log.exception("An error occurred while building an ELF:\n%s" % code)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))

    return retval
Esempio n. 18
0
    def __init__(self, proc):
        if proc.poll() is None:
            log.error("Process %i has not exited" % (process.pid))

        self.process = proc
        self.pid = proc.pid
        self.uid = proc.suid
        self.gid = proc.sgid
        self.exe = proc.executable
        self.basename = os.path.basename(self.exe)
        self.cwd = proc.cwd

        # XXX: Should probably break out all of this logic into
        #      its own class, so that we can support "file ops"
        #      locally, via SSH, and over ADB, in a transparent way.
        if isinstance(proc, process):
            self.read = read
            self.unlink = os.unlink
        elif isinstance(proc, ssh_channel):
            self.read = proc.parent.read
            self.unlink = proc.parent.unlink

        self.kernel_core_pattern = self.read(
            '/proc/sys/kernel/core_pattern').strip()
        self.kernel_core_uses_pid = bool(
            int(self.read('/proc/sys/kernel/core_uses_pid')))

        log.debug("core_pattern: %r" % self.kernel_core_pattern)
        log.debug("core_uses_pid: %r" % self.kernel_core_uses_pid)

        self.interpreter = self.binfmt_lookup()

        log.debug("interpreter: %r" % self.interpreter)

        # If we have already located the corefile, we will
        # have renamed it to 'core.<pid>'
        core_path = 'core.%i' % (proc.pid)
        self.core_path = None

        if os.path.isfile(core_path):
            log.debug("Found core immediately: %r" % core_path)
            self.core_path = core_path

        # Check for native coredumps if we don't 100% know the target
        # is running under qemu-user emulation.
        if not self.core_path:
            log.debug("Looking for native corefile")
            self.core_path = self.native_corefile()

        # If we still have not found the corefile, the process may have
        # been running under qemu-user emulation and we just can't tell
        # (e.g. can't enumerate binfmt_misc in a Docker container).
        if not self.core_path:
            log.debug("Looking for QEMU corefile")
            self.core_path = self.qemu_corefile()

        if not self.core_path:
            return

        core_pid = self.load_core_check_pid()

        # Move the corefile if we're configured that way
        if context.rename_corefiles:
            new_path = 'core.%i' % core_pid
            if core_pid > 0 and new_path != self.core_path:
                write(new_path, self.read(self.core_path))
                self.core_path = new_path

        # Check the PID
        if core_pid != self.pid:
            log.warn("Corefile PID does not match! (got %i)" % core_pid)

        # Register the corefile for removal only if it's an exact match
        elif context.delete_corefiles:
            atexit.register(lambda: os.unlink(self.core_path))
Esempio n. 19
0
def run_in_new_terminal(command,
                        terminal=None,
                        args=None,
                        kill_at_exit=True,
                        preexec_fn=None):
    """run_in_new_terminal(command, terminal=None, args=None, kill_at_exit=True, preexec_fn=None) -> int

    Run a command in a new terminal.

    When ``terminal`` is not set:
        - If ``context.terminal`` is set it will be used.
          If it is an iterable then ``context.terminal[1:]`` are default arguments.
        - If a ``pwntools-terminal`` command exists in ``$PATH``, it is used
        - If tmux is detected (by the presence of the ``$TMUX`` environment
          variable), a new pane will be opened.
        - If GNU Screen is detected (by the presence of the ``$STY`` environment
          variable), a new screen will be opened.
        - If ``$TERM_PROGRAM`` is set, that is used.
        - If X11 is detected (by the presence of the ``$DISPLAY`` environment
          variable), ``x-terminal-emulator`` is used.
        - If WSL (Windows Subsystem for Linux) is detected (by the presence of
          a ``wsl.exe`` binary in the ``$PATH`` and ``/proc/sys/kernel/osrelease``
          containing ``Microsoft``), a new ``cmd.exe`` window will be opened.

    If `kill_at_exit` is :const:`True`, try to close the command/terminal when the
    current process exits. This may not work for all terminal types.

    Arguments:
        command (str): The command to run.
        terminal (str): Which terminal to use.
        args (list): Arguments to pass to the terminal
        kill_at_exit (bool): Whether to close the command/terminal on process exit.
        preexec_fn (callable): Callable to invoke before exec().

    Note:
        The command is opened with ``/dev/null`` for stdin, stdout, stderr.

    Returns:
      PID of the new terminal process
    """
    if not terminal:
        if context.terminal:
            terminal = context.terminal[0]
            args = context.terminal[1:]
        elif which('pwntools-terminal'):
            terminal = 'pwntools-terminal'
            args = []
        elif 'TMUX' in os.environ and which('tmux'):
            terminal = 'tmux'
            args = ['splitw']
        elif 'STY' in os.environ and which('screen'):
            terminal = 'screen'
            args = ['-t', 'pwntools-gdb', 'bash', '-c']
        elif 'TERM_PROGRAM' in os.environ:
            terminal = os.environ['TERM_PROGRAM']
            args = []
        elif 'DISPLAY' in os.environ and which('x-terminal-emulator'):
            terminal = 'x-terminal-emulator'
            args = ['-e']
        else:
            is_wsl = False
            if os.path.exists('/proc/sys/kernel/osrelease'):
                with open('/proc/sys/kernel/osrelease', 'rb') as f:
                    is_wsl = b'icrosoft' in f.read()
            if is_wsl and which('cmd.exe') and which('wsl.exe') and which(
                    'bash.exe'):
                terminal = 'cmd.exe'
                args = ['/c', 'start', 'bash.exe', '-c']

    if not terminal:
        log.error(
            'Could not find a terminal binary to use. Set context.terminal to your terminal.'
        )
    elif not which(terminal):
        log.error(
            'Could not find terminal binary %r. Set context.terminal to your terminal.'
            % terminal)

    if isinstance(args, tuple):
        args = list(args)

    # When not specifying context.terminal explicitly, we used to set these flags above.
    # However, if specifying terminal=['tmux', 'splitw', '-h'], we would be lacking these flags.
    # Instead, set them here and hope for the best.
    if terminal == 'tmux':
        args += ['-F' '#{pane_pid}', '-P']

    argv = [which(terminal)] + args

    if isinstance(command, six.string_types):
        if ';' in command:
            log.error(
                "Cannot use commands with semicolon.  Create a script and invoke that directly."
            )
        argv += [command]
    elif isinstance(command, (list, tuple)):
        # Dump the full command line to a temporary file so we can be sure that
        # it is parsed correctly, and we do not need to account for shell expansion
        script = '''
#!{executable!s}
import os
os.execve({argv0!r}, {argv!r}, os.environ)
'''
        script = script.format(executable=sys.executable,
                               argv=command,
                               argv0=which(command[0]))
        script = script.lstrip()

        log.debug("Created script for new terminal:\n%s" % script)

        with tempfile.NamedTemporaryFile(delete=False, mode='wt+') as tmp:
            tmp.write(script)
            tmp.flush()
            os.chmod(tmp.name, 0o700)
            argv += [tmp.name]

    log.debug("Launching a new terminal: %r" % argv)

    stdin = stdout = stderr = open(os.devnull, 'r+b')
    if terminal == 'tmux':
        stdout = subprocess.PIPE

    p = subprocess.Popen(argv,
                         stdin=stdin,
                         stdout=stdout,
                         stderr=stderr,
                         preexec_fn=preexec_fn)

    if terminal == 'tmux':
        out, _ = p.communicate()
        pid = int(out)
    else:
        pid = p.pid

    if kill_at_exit:

        def kill():
            try:
                os.kill(pid, signal.SIGTERM)
            except OSError:
                pass

        atexit.register(kill)

    return pid
Esempio n. 20
0
def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
    """disasm(data, ...) -> str

    Disassembles a bytestring into human readable assembler.

    To see which architectures are supported,
    look in :mod:`pwnlib.contex`.

    Arguments:
      data(str): Bytestring to disassemble.
      vma(int): Passed through to the --adjust-vma argument of objdump
      byte(bool): Include the hex-printed bytes in the disassembly
      offset(bool): Include the virtual memory address in the disassembly

    Kwargs:
      Any arguments/properties that can be set on ``context``

    Examples:

        >>> print(disasm(unhex('b85d000000'), arch = 'i386'))
           0:   b8 5d 00 00 00          mov    eax, 0x5d
        >>> print(disasm(unhex('b85d000000'), arch = 'i386', byte = 0))
           0:   mov    eax, 0x5d
        >>> print(disasm(unhex('b85d000000'), arch = 'i386', byte = 0, offset = 0))
        mov    eax, 0x5d
        >>> print(disasm(unhex('b817000000'), arch = 'amd64'))
           0:   b8 17 00 00 00          mov    eax, 0x17
        >>> print(disasm(unhex('48c7c017000000'), arch = 'amd64'))
           0:   48 c7 c0 17 00 00 00    mov    rax, 0x17
        >>> print(disasm(unhex('04001fe552009000'), arch = 'arm'))
           0:   e51f0004        ldr     r0, [pc, #-4]   ; 0x4
           4:   00900052        addseq  r0, r0, r2, asr r0
        >>> print(disasm(unhex('4ff00500'), arch = 'thumb', bits=32))
           0:   f04f 0005       mov.w   r0, #5
        >>> print(disasm(unhex('656664676665400F18A4000000000051'), byte=0, arch='amd64'))
           0:   gs data16 fs data16 rex nop/reserved BYTE PTR gs:[eax+eax*1+0x0]
           f:   push   rcx
        >>> print(disasm(unhex('01000000'), arch='sparc64'))
           0:   01 00 00 00     nop
        >>> print(disasm(unhex('60000000'), arch='powerpc64'))
           0:   60 00 00 00     nop
        >>> print(disasm(unhex('00000000'), arch='mips64'))
           0:   00000000        nop
    """
    result = ''

    arch   = context.arch

    tmpdir = tempfile.mkdtemp(prefix = 'pwn-disasm-')
    step1  = path.join(tmpdir, 'step1')
    step2  = path.join(tmpdir, 'step2')

    bfdarch = _bfdarch()
    bfdname = _bfdname()
    objdump = _objdump() + ['-d', '--adjust-vma', str(vma), '-b', bfdname]
    objcopy = _objcopy() + [
        '-I', 'binary',
        '-O', bfdname,
        '-B', bfdarch,
        '--set-section-flags', '.data=code',
        '--rename-section', '.data=.text',
    ]

    if not byte:
        objdump += ['--no-show-raw-insn']

    if arch == 'thumb':
        objcopy += ['--prefix-symbol=$t.']
    else:
        objcopy += ['-w', '-N', '*']

    try:

        with open(step1, 'wb') as fd:
            fd.write(data)

        _run(objcopy + [step1, step2])

        output0 = _run(objdump + [step2])
        output1 = output0.split('<.text>:\n')

        if len(output1) != 2:
            log.error('Could not find .text in objdump output:\n%s' % output0)

        result = output1[1].strip('\n').rstrip().expandtabs()
    except Exception:
        log.exception("An error occurred while disassembling:\n%s" % data)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))


    lines = []
    pattern = '^( *[0-9a-f]+: *)', '((?:[0-9a-f]+ )+ *)', '(.*)'
    if not byte:
        pattern = pattern[::2]
    pattern = ''.join(pattern)
    for line in result.splitlines():
        match = re.search(pattern, line)
        if not match:
            lines.append(line)
            continue

        groups = match.groups()
        if byte:
            o, b, i = groups
        else:
            o, i = groups

        line = ''

        if offset:
            line += o
        if byte:
            line += b
        if instructions:
            line += i
        lines.append(line)

    return re.sub(',([^ ])', r', \1', '\n'.join(lines))
Esempio n. 21
0
def run_in_new_terminal(command,
                        terminal=None,
                        args=None,
                        kill_at_exit=True,
                        preexec_fn=None):
    """run_in_new_terminal(command, terminal=None, args=None, kill_at_exit=True, preexec_fn=None) -> int

    Run a command in a new terminal.

    When ``terminal`` is not set:
        - If ``context.terminal`` is set it will be used.
          If it is an iterable then ``context.terminal[1:]`` are default arguments.
        - If a ``pwntools-terminal`` command exists in ``$PATH``, it is used
        - If tmux is detected (by the presence of the ``$TMUX`` environment
          variable), a new pane will be opened.
        - If GNU Screen is detected (by the presence of the ``$STY`` environment
          variable), a new screen will be opened.
        - If ``$TERM_PROGRAM`` is set, that is used.
        - If X11 is detected (by the presence of the ``$DISPLAY`` environment
          variable), ``x-terminal-emulator`` is used.
        - If KDE Konsole is detected (by the presence of the ``$KONSOLE_VERSION``
          environment variable), a terminal will be split.
        - If WSL (Windows Subsystem for Linux) is detected (by the presence of
          a ``wsl.exe`` binary in the ``$PATH`` and ``/proc/sys/kernel/osrelease``
          containing ``Microsoft``), a new ``cmd.exe`` window will be opened.

    If `kill_at_exit` is :const:`True`, try to close the command/terminal when the
    current process exits. This may not work for all terminal types.

    Arguments:
        command (str): The command to run.
        terminal (str): Which terminal to use.
        args (list): Arguments to pass to the terminal
        kill_at_exit (bool): Whether to close the command/terminal on process exit.
        preexec_fn (callable): Callable to invoke before exec().

    Note:
        The command is opened with ``/dev/null`` for stdin, stdout, stderr.

    Returns:
      PID of the new terminal process
    """
    if not terminal:
        if context.terminal:
            terminal = context.terminal[0]
            args = context.terminal[1:]
        elif which('pwntools-terminal'):
            terminal = 'pwntools-terminal'
            args = []
        elif 'TMUX' in os.environ and which('tmux'):
            terminal = 'tmux'
            args = ['splitw']
        elif 'STY' in os.environ and which('screen'):
            terminal = 'screen'
            args = ['-t', 'pwntools-gdb', 'bash', '-c']
        elif 'TERM_PROGRAM' in os.environ:
            terminal = os.environ['TERM_PROGRAM']
            args = []
        elif 'DISPLAY' in os.environ and which('x-terminal-emulator'):
            terminal = 'x-terminal-emulator'
            args = ['-e']
        elif 'KONSOLE_VERSION' in os.environ and which('qdbus'):
            qdbus = which('qdbus')
            window_id = os.environ['WINDOWID']
            konsole_dbus_service = os.environ['KONSOLE_DBUS_SERVICE']

            with subprocess.Popen((qdbus, konsole_dbus_service),
                                  stdout=subprocess.PIPE) as proc:
                lines = proc.communicate()[0].decode().split('\n')

            # Iterate over all MainWindows
            for line in lines:
                parts = line.split('/')
                if len(parts) == 3 and parts[2].startswith('MainWindow_'):
                    name = parts[2]
                    with subprocess.Popen(
                        (qdbus, konsole_dbus_service, '/konsole/' + name,
                         'org.kde.KMainWindow.winId'),
                            stdout=subprocess.PIPE) as proc:
                        target_window_id = proc.communicate()[0].decode(
                        ).strip()
                        if target_window_id == window_id:
                            break
            else:
                log.error('MainWindow not found')

            # Split
            subprocess.run((qdbus, konsole_dbus_service, '/konsole/' + name,
                            'org.kde.KMainWindow.activateAction',
                            'split-view-left-right'),
                           stdout=subprocess.DEVNULL)

            # Find new session
            with subprocess.Popen((qdbus, konsole_dbus_service,
                                   os.environ['KONSOLE_DBUS_WINDOW'],
                                   'org.kde.konsole.Window.sessionList'),
                                  stdout=subprocess.PIPE) as proc:
                session_list = map(int, proc.communicate()[0].decode().split())
            last_konsole_session = max(session_list)

            terminal = 'qdbus'
            args = [
                konsole_dbus_service,
                '/Sessions/{}'.format(last_konsole_session),
                'org.kde.konsole.Session.runCommand'
            ]

        else:
            is_wsl = False
            if os.path.exists('/proc/sys/kernel/osrelease'):
                with open('/proc/sys/kernel/osrelease', 'rb') as f:
                    is_wsl = b'icrosoft' in f.read()
            if is_wsl and which('cmd.exe') and which('wsl.exe') and which(
                    'bash.exe'):
                terminal = 'cmd.exe'
                args = ['/c', 'start']
                distro_name = os.getenv('WSL_DISTRO_NAME')

                # Split pane in Windows Terminal
                if 'WT_SESSION' in os.environ and which('wt.exe'):
                    args.extend(['wt.exe', '-w', '0', 'split-pane', '-d', '.'])

                if distro_name:
                    args.extend(['wsl.exe', '-d', distro_name, 'bash', '-c'])
                else:
                    args.extend(['bash.exe', '-c'])

    if not terminal:
        log.error(
            'Could not find a terminal binary to use. Set context.terminal to your terminal.'
        )
    elif not which(terminal):
        log.error(
            'Could not find terminal binary %r. Set context.terminal to your terminal.'
            % terminal)

    if isinstance(args, tuple):
        args = list(args)

    # When not specifying context.terminal explicitly, we used to set these flags above.
    # However, if specifying terminal=['tmux', 'splitw', '-h'], we would be lacking these flags.
    # Instead, set them here and hope for the best.
    if terminal == 'tmux':
        args += ['-F' '#{pane_pid}', '-P']

    argv = [which(terminal)] + args

    if isinstance(command, six.string_types):
        if ';' in command:
            log.error(
                "Cannot use commands with semicolon.  Create a script and invoke that directly."
            )
        argv += [command]
    elif isinstance(command, (list, tuple)):
        # Dump the full command line to a temporary file so we can be sure that
        # it is parsed correctly, and we do not need to account for shell expansion
        script = '''
#!{executable!s}
import os
os.execve({argv0!r}, {argv!r}, os.environ)
'''
        script = script.format(executable=sys.executable,
                               argv=command,
                               argv0=which(command[0]))
        script = script.lstrip()

        log.debug("Created script for new terminal:\n%s" % script)

        with tempfile.NamedTemporaryFile(delete=False, mode='wt+') as tmp:
            tmp.write(script)
            tmp.flush()
            os.chmod(tmp.name, 0o700)
            argv += [tmp.name]

    log.debug("Launching a new terminal: %r" % argv)

    stdin = stdout = stderr = open(os.devnull, 'r+b')
    if terminal == 'tmux':
        stdout = subprocess.PIPE

    p = subprocess.Popen(argv,
                         stdin=stdin,
                         stdout=stdout,
                         stderr=stderr,
                         preexec_fn=preexec_fn)

    if terminal == 'tmux':
        out, _ = p.communicate()
        pid = int(out)
    elif terminal == 'qdbus':
        with subprocess.Popen((qdbus, konsole_dbus_service,
                               '/Sessions/{}'.format(last_konsole_session),
                               'org.kde.konsole.Session.processId'),
                              stdout=subprocess.PIPE) as proc:
            pid = int(proc.communicate()[0].decode())
    else:
        pid = p.pid

    if kill_at_exit:

        def kill():
            try:
                if terminal == 'qdbus':
                    os.kill(pid, signal.SIGHUP)
                else:
                    os.kill(pid, signal.SIGTERM)
            except OSError:
                pass

        atexit.register(kill)

    return pid
Esempio n. 22
0
def disasm(data, vma = 0, byte = True, offset = True, instructions = True):
    """disasm(data, ...) -> str

    Disassembles a bytestring into human readable assembler.

    To see which architectures are supported,
    look in :mod:`pwnlib.contex`.

    To support all these architecture, we bundle the GNU objcopy
    and objdump with pwntools.

    Arguments:
      data(str): Bytestring to disassemble.
      vma(int): Passed through to the --adjust-vma argument of objdump
      byte(bool): Include the hex-printed bytes in the disassembly
      offset(bool): Include the virtual memory address in the disassembly

    Kwargs:
      Any arguments/properties that can be set on ``context``

    Examples:

        >>> print disasm('b85d000000'.decode('hex'), arch = 'i386')
           0:   b8 5d 00 00 00          mov    eax,0x5d
        >>> print disasm('b85d000000'.decode('hex'), arch = 'i386', byte = 0)
           0:   mov    eax,0x5d
        >>> print disasm('b85d000000'.decode('hex'), arch = 'i386', byte = 0, offset = 0)
        mov    eax,0x5d
        >>> print disasm('b817000000'.decode('hex'), arch = 'amd64')
           0:   b8 17 00 00 00          mov    eax,0x17
        >>> print disasm('48c7c017000000'.decode('hex'), arch = 'amd64')
           0:   48 c7 c0 17 00 00 00    mov    rax,0x17
        >>> print disasm('04001fe552009000'.decode('hex'), arch = 'arm')
           0:   e51f0004        ldr     r0, [pc, #-4]   ; 0x4
           4:   00900052        addseq  r0, r0, r2, asr r0
        >>> print disasm('4ff00500'.decode('hex'), arch = 'thumb', bits=32)
           0:   f04f 0005       mov.w   r0, #5
    """
    result = ''

    arch   = context.arch
    os     = context.os

    tmpdir = tempfile.mkdtemp(prefix = 'pwn-disasm-')
    step1  = path.join(tmpdir, 'step1')
    step2  = path.join(tmpdir, 'step2')

    bfdarch = _bfdarch()
    bfdname = _bfdname()
    objdump = _objdump() + ['-d', '--adjust-vma', str(vma), '-b', bfdname]
    objcopy = _objcopy() + [
        '-I', 'binary',
        '-O', bfdname,
        '-B', bfdarch,
        '--set-section-flags', '.data=code',
        '--rename-section', '.data=.text',
    ]

    if arch == 'thumb':
        objcopy += ['--prefix-symbol=$t.']
    else:
        objcopy += ['-w', '-N', '*']

    try:

        with open(step1, 'w') as fd:
            fd.write(data)

        res = _run(objcopy + [step1, step2])

        output0 = _run(objdump + [step2])
        output1 = output0.split('<.text>:\n')

        if len(output1) != 2:
            log.error('Could not find .text in objdump output:\n%s' % output0)

        result = output1[1].strip('\n').rstrip().expandtabs()
    except Exception:
        log.exception("An error occurred while disassembling:\n%s" % data)
    else:
        atexit.register(lambda: shutil.rmtree(tmpdir))


    lines = []
    pattern = '^( *[0-9a-f]+: *)((?:[0-9a-f]+ )+ *)(.*)'
    for line in result.splitlines():
        try:
            o, b, i = re.search(pattern, line).groups()
        except:
            lines.append(line)
            continue

        line = ''

        if offset:
            line += o
        if byte:
            line += b
        if instructions:
            line += i
        lines.append(line)

    return '\n'.join(lines)
Esempio n. 23
0
def forward(port):
    """Sets up a port to forward to the device."""
    tcp_port = 'tcp:%s' % port
    adb(['forward', tcp_port, tcp_port])
    atexit.register(lambda: adb(['forward', '--remove', tcp_port]))
Esempio n. 24
0
    def __init__(self, proc):
        if proc.poll() is None:
            log.error("Process %i has not exited" % (process.pid))

        self.process = proc
        self.pid = proc.pid
        self.uid = proc.suid
        self.gid = proc.sgid
        self.exe = proc.executable
        self.basename = os.path.basename(self.exe)
        self.cwd = proc.cwd

        # XXX: Should probably break out all of this logic into
        #      its own class, so that we can support "file ops"
        #      locally, via SSH, and over ADB, in a transparent way.
        if isinstance(proc, process):
            self.read = read
            self.unlink = os.unlink
        elif isinstance(proc, ssh_channel):
            self.read = proc.parent.read
            self.unlink = proc.parent.unlink

        self.kernel_core_pattern = self.read('/proc/sys/kernel/core_pattern').strip()
        self.kernel_core_uses_pid = bool(int(self.read('/proc/sys/kernel/core_uses_pid')))

        log.debug("core_pattern: %r" % self.kernel_core_pattern)
        log.debug("core_uses_pid: %r" % self.kernel_core_uses_pid)

        self.interpreter = self.binfmt_lookup()

        log.debug("interpreter: %r" % self.interpreter)

        # If we have already located the corefile, we will
        # have renamed it to 'core.<pid>'
        core_path = 'core.%i' % (proc.pid)
        self.core_path = None

        if os.path.isfile(core_path):
            log.debug("Found core immediately: %r" % core_path)
            self.core_path = core_path

        # Try QEMU first, since it's unlikely to be a false-positive unless
        # there is a PID *and* filename collision.
        if not self.core_path:
            log.debug("Looking for QEMU corefile")
            self.core_path = self.qemu_corefile()

        # Check for native coredumps as a last resort
        if not self.core_path:
            log.debug("Looking for native corefile")
            self.core_path = self.native_corefile()

        if not self.core_path:
            return

        core_pid = self.load_core_check_pid()

        # Move the corefile if we're configured that way
        if context.rename_corefiles:
            new_path = 'core.%i' % core_pid
            if core_pid > 0 and new_path != self.core_path:
                write(new_path, self.read(self.core_path))
                self.core_path = new_path

        # Check the PID
        if core_pid != self.pid:
            log.warn("Corefile PID does not match! (got %i)" % core_pid)

        # Register the corefile for removal only if it's an exact match
        elif context.delete_corefiles:
            atexit.register(lambda: os.unlink(self.core_path))