def main(args): if not args.data: print(enhex(getattr(sys.stdin, 'buffer', sys.stdin).read())) else: data = ' '.join(args.data) if not hasattr(data, 'decode'): data = data.encode('utf-8', 'surrogateescape') print(enhex(data))
def emulate_plt_instructions(elf, got, address, data, targets): """Emulates instructions in ``data`` Arguments: elf(ELF): ELF that we are emulating got(int): Address of the GOT, as expected in e.g. EBX address(int): Address of ``data`` for emulation data(str): Array of bytes to emulate targets(list): List of target addresses Returns: :class:`dict`: Map of ``{address: target}`` for each address which reaches one of the selected targets. """ rv = {} # Unicorn doesn't support big-endian for everything yet. if elf.endian == 'big' and elf.arch == 'mips': data = packing.unpack_many(data, bits=32, endian='little') data = packing.flat(data, bits=32, endian='big') # Brute force addresses, assume that PLT entry points are at 4-byte aligned # Do not emulate more than a handful of instructions. for i, pc in enumerate(range(address, address + len(data), 4)): if log.isEnabledFor(logging.DEBUG): log.debug('%s %#x', fiddling.enhex(data[i * 4:(i + 1) * 4]), pc) log.debug(elf.disasm(pc, 4)) target = emulate_plt_instructions_inner(elf, got, pc, data[i * 4:], targets) if target in targets: log.debug("%#x -> %#x", pc, target) rv[pc] = target return rv
def emulate_plt_instructions(elf, got, address, data, targets): """Emulates instructions in ``data`` Arguments: elf(ELF): ELF that we are emulating got(int): Address of the GOT, as expected in e.g. EBX address(int): Address of ``data`` for emulation data(str): Array of bytes to emulate targets(list): List of target addresses Returns: :class:`dict`: Map of ``{address: target}`` for each address which reaches one of the selected targets. """ rv = {} # Unicorn doesn't support big-endian for everything yet. if elf.endian == 'big' and elf.arch == 'mips': data = packing.unpack_many(data, bits=32, endian='little') data = packing.flat(data, bits=32, endian='big') # Brute force addresses, assume that PLT entry points are at 4-byte aligned # Do not emulate more than a handful of instructions. for i, pc in enumerate(range(address, address + len(data), 4)): if log.isEnabledFor(logging.DEBUG): log.debug('%s %#x', fiddling.enhex(data[i*4:(i+1) * 4]), pc) log.debug(elf.disasm(pc, 4)) target = emulate_plt_instructions_inner(elf, got, pc, data[i*4:], targets) if target in targets: log.debug("%#x -> %#x", pc, target) rv[pc] = target return rv
def unstrip_libc(filename): """ Given a path to a libc binary, attempt to download matching debug info and add them back to the given binary. This modifies the given file. Arguments: filename(str): Path to the libc binary to unstrip. Returns: :const:`True` if binary was unstripped, :const:`False` otherwise. Examples: >>> filename = search_by_build_id('2d1c5e0b85cb06ff47fa6fa088ec22cb6e06074e', unstrip=False) >>> libc = ELF(filename) >>> hex(libc.symbols.read) '0xe56c0' >>> 'main_arena' in libc.symbols False >>> unstrip_libc(filename) True >>> libc = ELF(filename) >>> hex(libc.symbols.main_arena) '0x1d57a0' >>> unstrip_libc(which('python')) False >>> filename = search_by_build_id('06a8004be6e10c4aeabbe0db74423ace392a2d6b', unstrip=True) >>> 'main_arena' in ELF(filename).symbols True """ if not which('eu-unstrip'): log.warn_once('Couldn\'t find "eu-unstrip" in PATH. Install elfutils first.') return False libc = ELF(filename, checksec=False) if not libc.buildid: log.warn_once('Given libc does not have a buildid. Cannot look for debuginfo to unstrip.') return False for server_url in DEBUGINFOD_SERVERS: libc_dbg = _search_debuginfo_by_hash(server_url, enhex(libc.buildid)) if libc_dbg: break else: log.warn_once('Couldn\'t find debug info for libc with build_id %s on any debuginfod server.', enhex(libc.buildid)) return False # Add debug info to given libc binary inplace. p = process(['eu-unstrip', '-o', filename, filename, libc_dbg]) output = p.recvall() p.close() if output: log.error('Failed to unstrip libc binary: %s', output) return False return True
def _lookup_build_id(self, lib = None): libbase = self.libbase if lib is not None: libbase = self.lookup(symb = None, lib = lib) if not libbase: self.status("Couldn't find libc base") return None for offset in libcdb.get_build_id_offsets(): address = libbase + offset if self.leak.compare(address + 0xC, "GNU\x00"): return enhex(''.join(self.leak.raw(address + 0x10, 20))) else: self.status("Magic did not match") pass
def _lookup_build_id(self, lib=None): libbase = self.libbase if not self.link_map: self.status("No linkmap found") return None if lib is not None: libbase = self.lookup(symb=None, lib=lib) if not libbase: self.status("Couldn't find libc base") return None for offset in libcdb.get_build_id_offsets(): address = libbase + offset if self.leak.compare(address + 0xC, b"GNU\x00"): return enhex(b''.join(self.leak.raw(address + 0x10, 20))) else: self.status("Build ID not found at offset %#x" % offset) pass
def _parse_stack(self): # Get a copy of the stack mapping stack = self.stack if not stack: return # If the stack does not end with zeroes, something is very wrong. if not stack.data.endswith('\x00' * 8): log.warn_once( "End of the stack is corrupted, skipping stack parsing (got: %s)", enhex(self.data[-8:])) return # AT_EXECFN is the start of the filename, e.g. '/bin/sh' # Immediately preceding is a NULL-terminated environment variable string. # We want to find the beginning of it if not self.at_execfn: address = stack.stop address -= 2 * self.bytes address -= 1 address = stack.rfind('\x00', None, address) address += 1 self.at_execfn = address address = self.at_execfn - 1 # Sanity check! try: assert stack[address] == '\x00' except AssertionError: # Something weird is happening. Just don't touch it. log.debug("Something is weird") return except ValueError: # If the stack is not actually present in the coredump, we can't # read from the stack. This will fail as: # ValueError: 'seek out of range' log.debug("ValueError") return # address is currently set to the NULL terminator of the last # environment variable. address = stack.rfind('\x00', None, address) # We've found the beginning of the last environment variable. # We should be able to search up the stack for the envp[] array to # find a pointer to this address, followed by a NULL. last_env_addr = address + 1 p_last_env_addr = stack.find(pack(last_env_addr), None, last_env_addr) if p_last_env_addr < 0: # Something weird is happening. Just don't touch it. log.warn_once("Found bad environment at %#x", last_env_addr) return # Sanity check that we did correctly find the envp NULL terminator. envp_nullterm = p_last_env_addr + context.bytes assert self.unpack(envp_nullterm) == 0 # We've successfully located the end of the envp[] array. # # It comes immediately after the argv[] array, which itself # is NULL-terminated. # # Now let's find the end of argv p_end_of_argv = stack.rfind(pack(0), None, p_last_env_addr) self.envp_address = p_end_of_argv + self.bytes # Now we can fill in the environment env_pointer_data = stack[self.envp_address:p_last_env_addr + self.bytes] for pointer in unpack_many(env_pointer_data): # If the stack is corrupted, the pointer will be outside of # the stack. if pointer not in stack: continue try: name_value = self.string(pointer) except Exception: continue name, value = name_value.split('=', 1) # "end" points at the byte after the null terminator end = pointer + len(name_value) + 1 # Do not mark things as environment variables if they point # outside of the stack itself, or we had to cross into a different # mapping (after the stack) to read it. # This may occur when the entire stack is filled with non-NUL bytes, # and we NULL-terminate on a read failure in .string(). if end not in stack: continue self.env[name] = pointer + len(name) + len('=') # May as well grab the arguments off the stack as well. # argc comes immediately before argv[0] on the stack, but # we don't know what argc is. # # It is unlikely that argc is a valid stack address. address = p_end_of_argv - self.bytes while self.unpack(address) in stack: address -= self.bytes # address now points at argc self.argc_address = address self.argc = self.unpack(self.argc_address) # we can extract all of the arguments as well self.argv_address = self.argc_address + self.bytes self.argv = unpack_many(stack[self.argv_address:p_end_of_argv])
def main(): args = parser.parse_args() if not args.data: print(enhex(sys.stdin.read())) else: print(enhex(' '.join(args.data)))
def main(): args = parser.parse_args() if not args.data: print(enhex(sys.stdin.read())) else: print(enhex(' '.join(args.data)))