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