Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
    def _parse_stack(self):
        # Get a copy of the stack mapping
        stack = self.stack

        if not stack:
            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 self.at_execfn:
            address = self.at_execfn-1
        else:
            log.debug('No AT_EXECFN')
            address = stack.stop
            address -= 2*self.bytes
            address -= 1
            address = stack.rfind('\x00', None, address)
            address += 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)

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

        start_of_envp = p_end_of_argv + self.bytes

        # Now we can fill in the environment
        env_pointer_data = stack[start_of_envp:p_last_env_addr+self.bytes]
        for pointer in unpack_many(env_pointer_data):
            end = stack.find('=', last_env_addr)

            if pointer in stack and end in stack:
                name = stack[pointer:end]
                self.env[name] = pointer

        # 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 = self.unpack(address)

        # we can extract all of the arguments as well
        self.argv = unpack_many(stack[address + self.bytes: p_end_of_argv])
Ejemplo n.º 4
0
    def _parse_stack(self):
        # Get a copy of the stack mapping
        stack = self.stack

        if not stack:
            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 self.at_execfn:
            address = self.at_execfn - 1
        else:
            log.debug('No AT_EXECFN')
            address = stack.stop
            address -= 2 * self.bytes
            address -= 1
            address = stack.rfind('\x00', None, address)
            address += 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)

        start_of_envp = p_end_of_argv + self.bytes

        # Now we can fill in the environment
        env_pointer_data = stack[start_of_envp: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 = self.unpack(address)

        # we can extract all of the arguments as well
        self.argv = unpack_many(stack[address + self.bytes:p_end_of_argv])
Ejemplo n.º 5
0
def read_from_address(p, address, size):
    return list(packing.unpack_many(p.read(address, size), 32, endian='little', sign=False))
Ejemplo n.º 6
0
    def _parse_stack(self):
        # Get a copy of the stack mapping
        stack = self.stack

        if not stack:
            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 self.at_execfn:
            address = self.at_execfn-1
        else:
            log.debug('No AT_EXECFN')
            address = stack.stop
            address -= 2*self.bytes
            address -= 1
            address = stack.rfind('\x00', None, address)
            address += 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)

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

        start_of_envp = p_end_of_argv + self.bytes

        # Now we can fill in the environment
        env_pointer_data = stack[start_of_envp:p_last_env_addr+self.bytes]
        for pointer in unpack_many(env_pointer_data):
            end = stack.find('=', last_env_addr)

            if pointer in stack and end in stack:
                name = stack[pointer:end]
                self.env[name] = pointer

        # 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 = self.unpack(address)

        # we can extract all of the arguments as well
        self.argv = unpack_many(stack[address + self.bytes: p_end_of_argv])
Ejemplo n.º 7
0
def unpack_many_bytes(s: bytes, n: int = None) -> int:
    '''Unpack `s` into groups of `n` bytes, unpacked.
    n=context.bytes by default'''
    if n is None: n = context.bytes
    return unpack_many(s, n * 8)