def normalize_argv_env(argv, env, log, level=2): # # Validate argv # # - Must be a list/tuple of strings # - Each string must not contain '\x00' # argv = argv or [] if isinstance(argv, (six.text_type, six.binary_type)): argv = [argv] if not isinstance(argv, (list, tuple)): log.error('argv must be a list or tuple: %r' % argv) if not all( isinstance(arg, (six.text_type, bytes, bytearray)) for arg in argv): log.error("argv must be strings or bytes: %r" % argv) # Create a duplicate so we can modify it argv = list(argv) for i, oarg in enumerate(argv): arg = packing._need_bytes(oarg, level, 0x80) # ASCII text is okay if b'\x00' in arg[:-1]: log.error('Inappropriate nulls in argv[%i]: %r' % (i, oarg)) argv[i] = bytearray(arg.rstrip(b'\x00')) # # Validate environment # # - Must be a dictionary of {string:string} # - No strings may contain '\x00' # # Create a duplicate so we can modify it safely env2 = [] if hasattr(env, 'items'): env_items = env.items() else: env_items = env if env: for k, v in env_items: if not isinstance(k, (bytes, six.text_type)): log.error('Environment keys must be strings: %r' % k) if not isinstance(k, (bytes, six.text_type)): log.error('Environment values must be strings: %r=%r' % (k, v)) k = packing._need_bytes(k, level, 0x80) # ASCII text is okay v = packing._need_bytes(v, level, 0x80) # ASCII text is okay if b'\x00' in k[:-1]: log.error('Inappropriate nulls in env key: %r' % (k)) if b'\x00' in v[:-1]: log.error('Inappropriate nulls in env value: %r=%r' % (k, v)) env2.append( (bytearray(k.rstrip(b'\x00')), bytearray(v.rstrip(b'\x00')))) return argv, env2 or env
def resolve(self, addr=None): """ Return a flat list of ``int`` or ``bytes`` objects which can be passed to :func:`.flat`. Arguments: addr(int): Address at which the data starts in memory. If :const:`None`, ``self.addr`` is used. """ if addr is None: addr = self.address with self.local(addr): self.address = addr rv = [None] * len(self.values) for i, value in enumerate(self.values): if isinstance(value, six.integer_types): rv[i] = value elif isinstance(value, six.text_type): value = packing._need_bytes(value) if isinstance(value, (bytes, bytearray)): value += b'\x00' while len(value) % context.bytes: value += b'$' rv[i] = value elif isinstance(value, Unresolved): rv[i] = value.address rv.extend(value.resolve()) assert rv[i] is not None return rv
def recvline_regex(self, regex, exact=False, keepends=False, timeout=default): """recvline_regex(regex, exact=False, keepends=False, timeout=default) -> bytes Wrapper around :func:`recvline_pred`, which will return when a regex matches a line. By default :func:`re.RegexObject.search` is used, but if `exact` is set to True, then :func:`re.RegexObject.match` will be used instead. If the request is not satisfied before ``timeout`` seconds pass, all data is buffered and an empty string (``''``) is returned. """ if isinstance(regex, (bytes, bytearray, six.text_type)): regex = packing._need_bytes(regex) regex = re.compile(regex) if exact: pred = regex.match else: pred = regex.search return self.recvline_pred(pred, keepends=keepends, timeout=timeout)
def __init__(self, elf, symbol, args, data_addr=None): self.elf = elf self.elf_load_address_fixup = self.elf.address - self.elf.load_addr self.strtab = elf.dynamic_value_by_tag( "DT_STRTAB") + self.elf_load_address_fixup self.symtab = elf.dynamic_value_by_tag( "DT_SYMTAB") + self.elf_load_address_fixup self.jmprel = elf.dynamic_value_by_tag( "DT_JMPREL") + self.elf_load_address_fixup self.versym = elf.dynamic_value_by_tag( "DT_VERSYM") + self.elf_load_address_fixup self.symbol = _need_bytes(symbol, min_wrong=0x80) self.args = args self.real_args = self._format_args() self.unreliable = False self.data_addr = data_addr if data_addr is not None else self._get_recommended_address( ) # Will be set when built self.reloc_index = -1 self.payload = b"" # PIE is untested, gcc forces FULL-RELRO when PIE is set if self.elf.pie and self.elf_load_address_fixup == 0: log.warning("WARNING: ELF is PIE but has no base address set") self._build()
def recvline_endswith(self, delims, keepends=False, timeout=default): r"""recvline_endswith(delims, keepends=False, timeout=default) -> bytes Keep receiving lines until one is found that ends with one of `delims`. Returns the last line received. If the request is not satisfied before ``timeout`` seconds pass, all data is buffered and an empty string (``''``) is returned. See :meth:`recvline_startswith` for more details. Examples: >>> t = tube() >>> t.recv_raw = lambda n: b'Foo\nBar\nBaz\nKaboodle\n' >>> t.recvline_endswith(b'r') b'Bar' >>> t.recvline_endswith((b'a',b'b',b'c',b'd',b'e'), True) b'Kaboodle\n' >>> t.recvline_endswith(b'oodle') b'Kaboodle' """ # Convert string into singleton tupple if isinstance(delims, (bytes, bytearray, six.text_type)): delims = (delims, ) delims = tuple( packing._need_bytes(delim) + self.newline for delim in delims) return self.recvline_pred(lambda line: any(map(line.endswith, delims)), keepends=keepends, timeout=timeout)
def send(self, data): """send(data) Sends data. If log level ``DEBUG`` is enabled, also prints out the data received. If it is not possible to send anymore because of a closed connection, it raises ``exceptions.EOFError`` Examples: >>> def p(x): print(repr(x)) >>> t = tube() >>> t.send_raw = p >>> t.send(b'hello') b'hello' """ data = packing._need_bytes(data) if self.isEnabledFor(logging.DEBUG): self.debug('Sent %#x bytes:' % len(data)) if len(set(data)) == 1: self.indented('%r * %#x' % (data[0], len(data))) elif all(c in string.printable.encode() for c in data): for line in data.splitlines(True): self.indented(repr(line), level = logging.DEBUG) else: self.indented(fiddling.hexdump(data), level = logging.DEBUG) self.send_raw(data)
def send(self, data): """send(data) Sends data. If log level ``DEBUG`` is enabled, also prints out the data received. If it is not possible to send anymore because of a closed connection, it raises ``exceptions.EOFError`` Examples: >>> def p(x): print(repr(x)) >>> t = tube() >>> t.send_raw = p >>> t.send(b'hello') b'hello' """ data = packing._need_bytes(data) if self.isEnabledFor(logging.DEBUG): self.debug('Sent %#x bytes:' % len(data)) self.maybe_hexdump(data, level=logging.DEBUG) self.send_raw(data)
def sendlinethen(self, delim, data, timeout=default): """sendlinethen(delim, data, timeout = default) -> str A combination of ``sendline(data)`` and ``recvuntil(delim, timeout=timeout)``.""" data = packing._need_bytes(data) self.sendline(data) return self.recvuntil(delim, timeout=timeout)
def newline(self, v): """Line ending used for Tubes by default. This configures the newline emitted by e.g. ``sendline`` or that is used as a delimiter for e.g. ``recvline``. """ # circular imports from pwnlib.util.packing import _need_bytes return _need_bytes(v)
def gnu_hash(s): """gnu_hash(str) -> int Function used to generated GNU-style hashes for strings. """ s = bytearray(_need_bytes(s, 4, 0x80)) h = 5381 for c in s: h = h * 33 + c return h & 0xffffffff
def sendafter(self, delim, data, timeout=default): """sendafter(delim, data, timeout = default) -> str A combination of ``recvuntil(delim, timeout=timeout)`` and ``send(data)``. """ data = packing._need_bytes(data) res = self.recvuntil(delim, timeout=timeout) self.send(data) return res
def js_escape(data, padding=context.cyclic_alphabet[0:1], **kwargs): r"""js_escape(data, padding=context.cyclic_alphabet[0:1], endian = None, **kwargs) -> str Pack data as an escaped Unicode string for use in JavaScript's `unescape()` function Arguments: data (bytes): Bytes to pack padding (bytes): A single byte to use as padding if data is of uneven length endian (str): Endianness with which to pack the string ("little"/"big") Returns: A string representation of the packed data >>> js_escape(b'\xde\xad\xbe\xef') '%uadde%uefbe' >>> js_escape(b'\xde\xad\xbe\xef', endian='big') '%udead%ubeef' >>> js_escape(b'\xde\xad\xbe') '%uadde%u61be' >>> js_escape(b'aaaa') '%u6161%u6161' """ data = packing._need_bytes(data) padding = packing._need_bytes(padding) if len(padding) != 1: raise ValueError("Padding must be a single byte") if len(data) % 2: data += padding[0:1] data = bytearray(data) if context.endian == 'little': return ''.join('%u{a:02x}{b:02x}'.format(a=a, b=b) for b, a in iters.group(2, data)) else: return ''.join('%u{a:02x}{b:02x}'.format(a=a, b=b) for a, b in iters.group(2, data))
def sysv_hash(symbol): """sysv_hash(str) -> int Function used to generate SYSV-style hashes for strings. """ h = 0 g = 0 for c in bytearray(_need_bytes(symbol, 4, 0x80)): h = (h << 4) + c g = h & 0xf0000000 h ^= (g >> 24) h &= ~g return h & 0xffffffff
def _dynamic_load_dynelf(self, libname): """_dynamic_load_dynelf(libname) -> DynELF Looks up information about a loaded library via the link map. Arguments: libname(str): Name of the library to resolve, or a substring (e.g. 'libc.so') Returns: A DynELF instance for the loaded library, or None. """ cur = self.link_map leak = self.leak LinkMap = { 32: elf.Elf32_Link_Map, 64: elf.Elf64_Link_Map }[self.elfclass] # make sure we rewind to the beginning! while leak.field(cur, LinkMap.l_prev): cur = leak.field(cur, LinkMap.l_prev) libname = _need_bytes(libname, 2, 0x80) while cur: self.status("link_map entry %#x" % cur) p_name = leak.field(cur, LinkMap.l_name) name = leak.s(p_name) if libname in name: break if name: self.status('Skipping %s' % name) cur = leak.field(cur, LinkMap.l_next) else: self.failure("Could not find library with name containing %r" % libname) return None libbase = leak.field(cur, LinkMap.l_addr) self.status("Resolved library %r at %#x" % (libname, libbase)) lib = DynELF(leak, libbase) lib._dynamic = leak.field(cur, LinkMap.l_ld) lib._waitfor = self._waitfor return lib
def sendline(self, line=b''): r"""sendline(data) Shorthand for ``t.send(data + t.newline)``. Examples: >>> def p(x): print(repr(x)) >>> t = tube() >>> t.send_raw = p >>> t.sendline(b'hello') b'hello\n' >>> t.newline = b'\r\n' >>> t.sendline(b'hello') b'hello\r\n' """ line = packing._need_bytes(line) self.send(line + self.newline)
def __init__(self, value, address=0): if not isinstance(value, (list, tuple)): value = [value] self.values = [] self.address = address for v in value: if isinstance(v, (list, tuple)): self.size += context.bytes else: if isinstance(v, six.text_type): v = packing._need_bytes(v) try: self.size += align(context.bytes, len(v)) except TypeError: # no 'len' self.size += context.bytes for v in value: if isinstance(v, (list, tuple)): arg = AppendedArgument(v, self.address + self.size) self.size += arg.size self.values.append(arg) else: self.values.append(v)
def unrecv(self, data): """unrecv(data) Puts the specified data back at the beginning of the receive buffer. Examples: >>> t = tube() >>> t.recv_raw = lambda n: b'hello' >>> t.recv() b'hello' >>> t.recv() b'hello' >>> t.unrecv(b'world') >>> t.recv() b'world' >>> t.recv() b'hello' """ data = packing._need_bytes(data) self.buffer.unget(data)
def attach(target, gdbscript='', exe=None, gdb_args=None, ssh=None, sysroot=None, api=False): r""" Start GDB in a new terminal and attach to `target`. Arguments: target: The target to attach to. gdbscript(:obj:`str` or :obj:`file`): GDB script to run after attaching. exe(str): The path of the target binary. arch(str): Architechture of the target binary. If `exe` known GDB will detect the architechture automatically (if it is supported). gdb_args(list): List of additional arguments to pass to GDB. sysroot(str): Foreign-architecture sysroot, used for QEMU-emulated binaries and Android targets. api(bool): Enable access to GDB Python API. Returns: PID of the GDB process (or the window which it is running in). When ``api=True``, a (PID, :class:`Gdb`) tuple. Notes: The ``target`` argument is very robust, and can be any of the following: :obj:`int` PID of a process :obj:`str` Process name. The youngest process is selected. :obj:`tuple` Host, port pair of a listening ``gdbserver`` :class:`.process` Process to connect to :class:`.sock` Connected socket. The executable on the other end of the connection is attached to. Can be any socket type, including :class:`.listen` or :class:`.remote`. :class:`.ssh_channel` Remote process spawned via :meth:`.ssh.process`. This will use the GDB installed on the remote machine. If a password is required to connect, the ``sshpass`` program must be installed. Examples: Attach to a process by PID >>> pid = gdb.attach(1234) # doctest: +SKIP Attach to the youngest process by name >>> pid = gdb.attach('bash') # doctest: +SKIP Attach a debugger to a :class:`.process` tube and automate interaction >>> io = process('bash') >>> pid = gdb.attach(io, gdbscript=''' ... call puts("Hello from process debugger!") ... detach ... quit ... ''') >>> io.recvline() b'Hello from process debugger!\n' >>> io.sendline(b'echo Hello from bash && exit') >>> io.recvall() b'Hello from bash\n' Using GDB Python API: .. doctest :skipif: six.PY2 >>> io = process('bash') Attach a debugger >>> pid, io_gdb = gdb.attach(io, api=True) Force the program to write something it normally wouldn't >>> io_gdb.execute('call puts("Hello from process debugger!")') Resume the program >>> io_gdb.continue_nowait() Observe the forced line >>> io.recvline() b'Hello from process debugger!\n' Interact with the program in a regular way >>> io.sendline(b'echo Hello from bash && exit') Observe the results >>> io.recvall() b'Hello from bash\n' Attach to the remote process from a :class:`.remote` or :class:`.listen` tube, as long as it is running on the same machine. >>> server = process(['socat', 'tcp-listen:12345,reuseaddr,fork', 'exec:/bin/bash,nofork']) >>> sleep(1) # Wait for socat to start >>> io = remote('127.0.0.1', 12345) >>> sleep(1) # Wait for process to fork >>> pid = gdb.attach(io, gdbscript=''' ... call puts("Hello from remote debugger!") ... detach ... quit ... ''') >>> io.recvline() b'Hello from remote debugger!\n' >>> io.sendline(b'echo Hello from bash && exit') >>> io.recvall() b'Hello from bash\n' Attach to processes running on a remote machine via an SSH :class:`.ssh` process >>> shell = ssh('travis', 'example.pwnme', password='******') >>> io = shell.process(['cat']) >>> pid = gdb.attach(io, gdbscript=''' ... call sleep(5) ... call puts("Hello from ssh debugger!") ... detach ... quit ... ''') >>> io.recvline(timeout=5) # doctest: +SKIP b'Hello from ssh debugger!\n' >>> io.sendline(b'This will be echoed back') >>> io.recvline() b'This will be echoed back\n' >>> io.close() """ if context.noptrace: log.warn_once("Skipping debug attach since context.noptrace==True") return # if gdbscript is a file object, then read it; we probably need to run some # more gdb script anyway if hasattr(gdbscript, 'read'): with gdbscript: gdbscript = gdbscript.read() # enable gdb.attach(p, 'continue') if gdbscript and not gdbscript.endswith('\n'): gdbscript += '\n' # Use a sane default sysroot for Android if not sysroot and context.os == 'android': sysroot = 'remote:/' # gdb script to run before `gdbscript` pre = '' if not context.native: pre += 'set endian %s\n' % context.endian pre += 'set architecture %s\n' % get_gdb_arch() if sysroot: pre += 'set sysroot %s\n' % sysroot if context.os == 'android': pre += 'set gnutarget ' + _bfdname() + '\n' if exe and context.os != 'baremetal': pre += 'file "%s"\n' % exe # let's see if we can find a pid to attach to pid = None if isinstance(target, six.integer_types): # target is a pid, easy peasy pid = target elif isinstance(target, str): # pidof picks the youngest process pidof = proc.pidof if context.os == 'android': pidof = adb.pidof pids = list(pidof(target)) if not pids: log.error('No such process: %s', target) pid = pids[0] log.info('Attaching to youngest process "%s" (PID = %d)' % (target, pid)) elif isinstance(target, tubes.ssh.ssh_channel): if not target.pid: log.error("PID unknown for channel") shell = target.parent tmpfile = shell.mktemp() gdbscript = b'shell rm %s\n%s' % ( tmpfile, packing._need_bytes(gdbscript, 2, 0x80)) shell.upload_data(gdbscript or b'', tmpfile) cmd = [ 'ssh', '-C', '-t', '-p', str(shell.port), '-l', shell.user, shell.host ] if shell.password: if not misc.which('sshpass'): log.error("sshpass must be installed to debug ssh processes") cmd = ['sshpass', '-p', shell.password] + cmd if shell.keyfile: cmd += ['-i', shell.keyfile] cmd += ['gdb', '-q', target.executable, str(target.pid), '-x', tmpfile] misc.run_in_new_terminal(cmd) return elif isinstance(target, tubes.sock.sock): pids = proc.pidof(target) if not pids: log.error('Could not find remote process (%s:%d) on this machine' % target.sock.getpeername()) pid = pids[0] # Specifically check for socat, since it has an intermediary process # if you do not specify "nofork" to the EXEC: argument # python(2640)───socat(2642)───socat(2643)───bash(2644) if proc.exe(pid).endswith('/socat') and time.sleep( 0.1) and proc.children(pid): pid = proc.children(pid)[0] # We may attach to the remote process after the fork but before it performs an exec. # If an exe is provided, wait until the process is actually running the expected exe # before we attach the debugger. t = Timeout() with t.countdown(2): while exe and os.path.realpath( proc.exe(pid)) != os.path.realpath(exe) and t.timeout: time.sleep(0.1) elif isinstance(target, tubes.process.process): pid = proc.pidof(target)[0] exe = exe or target.executable elif isinstance(target, tuple) and len(target) == 2: host, port = target if context.os != 'android': pre += 'target remote %s:%d\n' % (host, port) else: # Android debugging is done over gdbserver, which can't follow # new inferiors (tldr; follow-fork-mode child) unless it is run # in extended-remote mode. pre += 'target extended-remote %s:%d\n' % (host, port) pre += 'set detach-on-fork off\n' def findexe(): for spid in proc.pidof(target): sexe = proc.exe(spid) name = os.path.basename(sexe) # XXX: parse cmdline if name.startswith('qemu-') or name.startswith('gdbserver'): exe = proc.cmdline(spid)[-1] return os.path.join(proc.cwd(spid), exe) exe = exe or findexe() elif isinstance(target, elf.corefile.Corefile): pre += 'target core "%s"\n' % target.path else: log.error("don't know how to attach to target: %r", target) # if we have a pid but no exe, just look it up in /proc/ if pid and not exe: exe_fn = proc.exe if context.os == 'android': exe_fn = adb.proc_exe exe = exe_fn(pid) if not pid and not exe and not ssh: log.error('could not find target process') gdb_binary = binary() cmd = [gdb_binary] if gdb_args: cmd += gdb_args if context.gdbinit: cmd += ['-nh'] # ignore ~/.gdbinit cmd += ['-x', context.gdbinit] # load custom gdbinit cmd += ['-q'] if exe and context.native: if not ssh and not os.path.isfile(exe): log.error('No such file: %s', exe) cmd += [exe] if pid and not context.os == 'android': cmd += [str(pid)] if context.os == 'android' and pid: runner = _get_runner() which = _get_which() gdb_cmd = _gdbserver_args(pid=pid, which=which) gdbserver = runner(gdb_cmd) port = _gdbserver_port(gdbserver, None) host = context.adb_host pre += 'target extended-remote %s:%i\n' % (context.adb_host, port) # gdbserver on Android sets 'detach-on-fork on' which breaks things # when you're trying to debug anything that forks. pre += 'set detach-on-fork off\n' if api: # create a UNIX socket for talking to GDB socket_dir = tempfile.mkdtemp() socket_path = os.path.join(socket_dir, 'socket') bridge = os.path.join(os.path.dirname(__file__), 'gdb_api_bridge.py') # inject the socket path and the GDB Python API bridge pre = 'python socket_path = ' + repr(socket_path) + '\n' + \ 'source ' + bridge + '\n' + \ pre gdbscript = pre + (gdbscript or '') if gdbscript: tmp = tempfile.NamedTemporaryFile(prefix='pwn', suffix='.gdb', delete=False, mode='w+') log.debug('Wrote gdb script to %r\n%s', tmp.name, gdbscript) gdbscript = 'shell rm %s\n%s' % (tmp.name, gdbscript) tmp.write(gdbscript) tmp.close() cmd += ['-x', tmp.name] log.info('running in new terminal: %s', cmd) if api: # prevent gdb_faketerminal.py from messing up api doctests def preexec_fn(): os.environ['GDB_FAKETERMINAL'] = '0' else: preexec_fn = None gdb_pid = misc.run_in_new_terminal(cmd, preexec_fn=preexec_fn) if pid and context.native: proc.wait_for_debugger(pid, gdb_pid) if not api: return gdb_pid # connect to the GDB Python API bridge from rpyc import BgServingThread from rpyc.utils.factory import unix_connect if six.PY2: retriable = socket.error else: retriable = ConnectionRefusedError, FileNotFoundError t = Timeout() with t.countdown(10): while t.timeout: try: conn = unix_connect(socket_path) break except retriable: time.sleep(0.1) else: # Check to see if RPyC is installed at all in GDB rpyc_check = [ gdb_binary, '--nx', '-batch', '-ex', 'python import rpyc; import sys; sys.exit(123)' ] if 123 != tubes.process.process(rpyc_check).poll(block=True): log.error('Failed to connect to GDB: rpyc is not installed') # Check to see if the socket ever got created if not os.path.exists(socket_path): log.error( 'Failed to connect to GDB: Unix socket %s was never created', socket_path) # Check to see if the remote RPyC client is a compatible version version_check = [ gdb_binary, '--nx', '-batch', '-ex', 'python import platform; print(platform.python_version())' ] gdb_python_version = tubes.process.process( version_check).recvall().strip() python_version = str(platform.python_version()) if gdb_python_version != python_version: log.error( 'Failed to connect to GDB: Version mismatch (%s vs %s)', gdb_python_version, python_version) # Don't know what happened log.error('Failed to connect to GDB: Unknown error') # now that connection is up, remove the socket from the filesystem os.unlink(socket_path) os.rmdir(socket_dir) # create a thread for receiving breakpoint notifications BgServingThread(conn, callback=lambda: None) return gdb_pid, Gdb(conn)
def cyclic_find(subseq, alphabet = None, n = None): """cyclic_find(subseq, alphabet = None, n = None) -> int Calculates the position of a substring into a De Bruijn sequence. .. todo: "Calculates" is an overstatement. It simply traverses the list. There exists better algorithms for this, but they depend on generating the De Bruijn sequence in another fashion. Somebody should look at it: https://www.sciencedirect.com/science/article/pii/S0012365X00001175 Arguments: subseq: The subsequence to look for. This can be a string, a list or an integer. If an integer is provided it will be packed as a little endian integer. alphabet: List or string to generate the sequence over. By default, uses :obj:`.context.cyclic_alphabet`. n(int): The length of subsequences that should be unique. By default, uses :obj:`.context.cyclic_size`. Examples: Let's generate an example cyclic pattern. >>> cyclic(16) b'aaaabaaacaaadaaa' Note that 'baaa' starts at offset 4. The `cyclic_find` routine shows us this: >>> cyclic_find(b'baaa') 4 The *default* length of a subsequence generated by `cyclic` is `4`. If a longer value is submitted, it is automatically truncated to four bytes. >>> cyclic_find(b'baaacaaa') 4 If you provided e.g. `n=8` to `cyclic` to generate larger subsequences, you must explicitly provide that argument. >>> cyclic_find(b'baaacaaa', n=8) 3515208 We can generate a large cyclic pattern, and grab a subset of it to check a deeper offset. >>> cyclic_find(cyclic(1000)[514:518]) 514 Instead of passing in the byte representation of the pattern, you can also pass in the integer value. Note that this is sensitive to the selected endianness via `context.endian`. >>> cyclic_find(0x61616162) 4 >>> cyclic_find(0x61616162, endian='big') 1 You can use anything for the cyclic pattern, including non-printable characters. >>> cyclic_find(0x00000000, alphabet=unhex('DEADBEEF00')) 621 """ if n is None: n = context.cyclic_size if isinstance(subseq, six.integer_types): subseq = packing.pack(subseq, bytes=n) subseq = packing._need_bytes(subseq, 2, 0x80) if len(subseq) != n: log.warn_once("cyclic_find() expects %i-byte subsequences by default, you gave %r\n" "Unless you specified cyclic(..., n=%i), you probably just want the first 4 bytes.\n" "Truncating the data at 4 bytes. Specify cyclic_find(..., n=%i) to override this.", n, subseq, len(subseq), len(subseq)) subseq = subseq[:n] if alphabet is None: alphabet = context.cyclic_alphabet alphabet = packing._need_bytes(alphabet, 2, 0x80) if any(c not in alphabet for c in subseq): return -1 n = n or len(subseq) return _gen_find(subseq, de_bruijn(alphabet, n))
def aux(args): for i, arg in enumerate(args): if isinstance(arg, (str, bytes)): args[i] = _need_bytes(args[i], min_wrong=0x80) + b"\x00" elif isinstance(arg, (list, tuple)): aux(arg)
def lookup(self, symb=None, lib=None): """lookup(symb = None, lib = None) -> int Find the address of ``symbol``, which is found in ``lib``. Arguments: symb(str): Named routine to look up If omitted, the base address of the library will be returned. lib(str): Substring to match for the library name. If omitted, the current library is searched. If set to ``'libc'``, ``'libc.so'`` is assumed. Returns: Address of the named symbol, or :const:`None`. """ result = None if lib == 'libc': lib = 'libc.so' if symb: symb = _need_bytes(symb, min_wrong=0x80) # # Get a pretty name for the symbol to show the user # if symb and lib: pretty = '%r in %r' % (symb, lib) else: pretty = repr(symb or lib) if not pretty: self.failure("Must specify a library or symbol") self.waitfor('Resolving %s' % pretty) # # If we are loading from a different library, create # a DynELF instance for it. # if lib is not None: dynlib = self._dynamic_load_dynelf(lib) else: dynlib = self if dynlib is None: log.failure("Could not find %r", lib) return None # # If we are resolving a symbol in the library, find it. # if symb and self.libcdb: # Try a quick lookup by build ID self.status("Trying lookup based on Build ID") build_id = dynlib._lookup_build_id(lib=lib) if build_id: log.info("Trying lookup based on Build ID: %s", build_id) path = libcdb.search_by_build_id(build_id) if path: with context.local(log_level='error'): e = ELF(path) e.address = dynlib.libbase result = e.symbols[symb] if symb and not result: self.status("Trying remote lookup") result = dynlib._lookup(symb) if not symb: result = dynlib.libbase # # Did we win? # if result: self.success("%#x" % result) else: self.failure("Could not find %s" % pretty) return result
def sendlines(self, lines=[]): for line in lines: line = packing._need_bytes(line) self.sendline(line)
def cyclic_find(subseq, alphabet=None, n=None): """cyclic_find(subseq, alphabet = None, n = None) -> int Calculates the position of a substring into a De Bruijn sequence. .. todo: "Calculates" is an overstatement. It simply traverses the list. There exists better algorithms for this, but they depend on generating the De Bruijn sequence in another fashion. Somebody should look at it: https://www.sciencedirect.com/science/article/pii/S0012365X00001175 Arguments: subseq: The subsequence to look for. This can be a string, a list or an integer. If an integer is provided it will be packed as a little endian integer. alphabet: List or string to generate the sequence over. By default, uses :obj:`.context.cyclic_alphabet`. n(int): The length of subsequences that should be unique. By default, uses :obj:`.context.cyclic_size`. Examples: Let's generate an example cyclic pattern. >>> cyclic(16) b'aaaabaaacaaadaaa' Note that 'baaa' starts at offset 4. The `cyclic_find` routine shows us this: >>> cyclic_find(b'baaa') 4 The *default* length of a subsequence generated by `cyclic` is `4`. If a longer value is submitted, it is automatically truncated to four bytes. >>> cyclic_find(b'baaacaaa') 4 If you provided e.g. `n=8` to `cyclic` to generate larger subsequences, you must explicitly provide that argument. >>> cyclic_find(b'baaacaaa', n=8) 3515208 We can generate a large cyclic pattern, and grab a subset of it to check a deeper offset. >>> cyclic_find(cyclic(1000)[514:518]) 514 Instead of passing in the byte representation of the pattern, you can also pass in the integer value. Note that this is sensitive to the selected endianness via `context.endian`. >>> cyclic_find(0x61616162) 4 >>> cyclic_find(0x61616162, endian='big') 1 Similarly to the case where you can provide a bytes value that is longer than four bytes, you can provided an integer value that is larger than what can be held in four bytes. If such a large value is given, it is automatically truncated. >>> cyclic_find(0x6161616361616162) 4 >>> cyclic_find(0x6261616163616161, endian='big') 4 You can use anything for the cyclic pattern, including non-printable characters. >>> cyclic_find(0x00000000, alphabet=unhex('DEADBEEF00')) 621 """ if n is None: n = context.cyclic_size if isinstance(subseq, six.integer_types): if subseq >= 2**(8 * n): # Assumption: The user has given an integer that is more than 2**(8n) bits, but would otherwise fit within # a register of size 2**(8m) where m is a multiple of four notice = ( "cyclic_find() expected an integer argument <= {cap:#x}, you gave {gave:#x}\n" "Unless you specified cyclic(..., n={fits}), you probably just want the first {n} bytes.\n" "Truncating the data at {n} bytes. Specify cyclic_find(..., n={fits}) to override this." ).format( cap=2**(8 * n) - 1, gave=subseq, # The number of bytes needed to represent subseq, rounded to the next 4 fits=int(round(float(subseq.bit_length()) / 32 + 0.5) * 32) // 8, n=n, ) log.warn_once(notice) if context.endian == 'little': subseq &= 2**(8 * n) - 1 else: while subseq >= 2**(8 * n): subseq >>= 8 * n subseq = packing.pack(subseq, bytes=n) subseq = packing._need_bytes(subseq, 2, 0x80) if len(subseq) != n: log.warn_once( "cyclic_find() expected a %i-byte subsequence, you gave %r\n" "Unless you specified cyclic(..., n=%i), you probably just want the first %d bytes.\n" "Truncating the data at %d bytes. Specify cyclic_find(..., n=%i) to override this.", n, subseq, len(subseq), n, n, len(subseq)) subseq = subseq[:n] if alphabet is None: alphabet = context.cyclic_alphabet alphabet = packing._need_bytes(alphabet, 2, 0x80) if any(c not in alphabet for c in subseq): return -1 n = n or len(subseq) return _gen_find(subseq, de_bruijn(alphabet, n))
def newline(self, newline): self._newline = packing._need_bytes(newline)