Esempio n. 1
0
    def get_object(self,
                   type_name: str,
                   use_top_down: bool,
                   executive: bool = False,
                   native_layer_name: Optional[str] = None) -> Optional[interfaces.objects.ObjectInterface]:
        """Carve an object or data structure from a kernel pool allocation

        Args:
            type_name: the data structure type name
            native_layer_name: the name of the layer where the data originally lived
            object_type: the object type (executive kernel objects only)

        Returns:
            An object as found from a POOL_HEADER
        """

        symbol_table_name = self.vol.type_name.split(constants.BANG)[0]
        if constants.BANG in type_name:
            symbol_table_name, type_name = type_name.split(constants.BANG)[0:2]

        object_header_type = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + "_OBJECT_HEADER")
        pool_header_size = self.vol.size

        # if there is no object type, then just instantiate a structure
        if not executive:
            mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                              layer_name = self.vol.layer_name,
                                              offset = self.vol.offset + pool_header_size,
                                              native_layer_name = native_layer_name)
            return mem_object

        # otherwise we have an executive object in the pool
        else:
            if symbols.symbol_table_is_64bit(self._context, symbol_table_name):
                alignment = 16
            else:
                alignment = 8

            # use the top down approach for windows 8 and later
            if use_top_down:
                body_offset = object_header_type.relative_child_offset('Body')
                infomask_offset = object_header_type.relative_child_offset('InfoMask')
                optional_headers, lengths_of_optional_headers = self._calculate_optional_header_lengths(
                    self._context, symbol_table_name)
                padding_available = None if 'PADDING_INFO' not in optional_headers else optional_headers.index(
                    'PADDING_INFO')
                max_optional_headers_length = sum(lengths_of_optional_headers)

                # define the starting and ending bounds for the scan
                start_offset = self.vol.offset + pool_header_size
                addr_limit = min(max_optional_headers_length, self.BlockSize * alignment)

                # A single read is better than lots of little one-byte reads.
                # We're ok padding this, because the byte we'd check would be 0 which would only be valid if there
                # were no optional headers in the first place (ie, if we read too much for headers that don't exist,
                # but the bit we could read were valid)
                infomask_data = self._context.layers[self.vol.layer_name].read(start_offset,
                                                                               addr_limit + infomask_offset,
                                                                               pad = True)

                # Addr stores the offset to the potential start of the OBJECT_HEADER from just after the POOL_HEADER
                # It will always be aligned to a particular alignment
                for addr in range(0, addr_limit, alignment):
                    infomask_value = infomask_data[addr + infomask_offset]

                    padding_present = False
                    optional_headers_length = 0
                    for i in range(len(lengths_of_optional_headers)):
                        if infomask_value & (1 << i):
                            optional_headers_length += lengths_of_optional_headers[i]
                            if i == padding_available:
                                padding_present = True

                    # PADDING_INFO is a special case (4 bytes that contain the total padding length)
                    padding_length = 0
                    if padding_present:
                        # Read the four bytes from just before the next optional_headers_length minus the padding_info size
                        #
                        #  ---------------
                        #  POOL_HEADER
                        #  ---------------
                        #
                        #  start of PADDING_INFO
                        #  ---------------
                        #  End of other optional headers
                        #  ---------------
                        #  OBJECT_HEADER
                        #  ---------------
                        if addr - optional_headers_length < 0:
                            continue
                        padding_length = struct.unpack(
                            "<I", infomask_data[addr - optional_headers_length:addr - optional_headers_length + 4])[0]
                        padding_length -= lengths_of_optional_headers[padding_available]

                    # Certain versions of windows have PADDING_INFO lengths that are too long
                    # So we now check that the padding length is at a minimum the right length
                    # and that it doesn't go beyond the entirety of the data
                    if addr - optional_headers_length >= padding_length > addr:
                        continue

                    try:
                        mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                                          layer_name = self.vol.layer_name,
                                                          offset = addr + body_offset + start_offset,
                                                          native_layer_name = native_layer_name)

                        if mem_object.is_valid():
                            return mem_object

                    except (TypeError, exceptions.InvalidAddressException):
                        pass

            # use the bottom up approach for windows 7 and earlier
            else:
                type_size = self._context.symbol_space.get_type(symbol_table_name + constants.BANG + type_name).size
                rounded_size = conversion.round(type_size, alignment, up = True)

                mem_object = self._context.object(symbol_table_name + constants.BANG + type_name,
                                                  layer_name = self.vol.layer_name,
                                                  offset = self.vol.offset + self.BlockSize * alignment - rounded_size,
                                                  native_layer_name = native_layer_name)

                try:
                    if mem_object.is_valid():
                        return mem_object
                except (TypeError, exceptions.InvalidAddressException):
                    return None
        return None
Esempio n. 2
0
    def reconstruct(self) -> Generator[Tuple[int, bytes], None, None]:
        """This method generates the content necessary to reconstruct a PE file
        from memory. It preserves slack space (similar to the old --memory) and
        automatically fixes the ImageBase in the output PE file.

        Returns:
            <tuple> of (<int> offset, <bytes> data)
        """

        nt_header = self.get_nt_header()

        layer_name = self.vol.layer_name
        symbol_table_name = self.get_symbol_table_name()

        section_alignment = nt_header.OptionalHeader.SectionAlignment

        sect_header_size = self._context.symbol_space.get_type(
            symbol_table_name + constants.BANG + "_IMAGE_SECTION_HEADER").size

        size_of_image = nt_header.OptionalHeader.SizeOfImage

        # no legitimate PE is going to be larger than this
        if size_of_image > (1024 * 1024 * 100):
            raise ValueError("The claimed SizeOfImage is too large: {}".format(
                size_of_image))

        read_layer = self._context.layers[layer_name]

        raw_data = read_layer.read(self.vol.offset,
                                   nt_header.OptionalHeader.SizeOfImage,
                                   pad=True)

        # fix the PE image base before yielding the initial view of the data
        fixed_data = self.fix_image_base(raw_data, nt_header)
        yield 0, fixed_data

        start_addr = nt_header.FileHeader.SizeOfOptionalHeader + \
                     (nt_header.OptionalHeader.vol.offset - self.vol.offset)

        counter = 0
        for sect in nt_header.get_sections():

            if sect.VirtualAddress > size_of_image:
                raise ValueError(
                    "Section VirtualAddress is too large: {}".format(
                        sect.VirtualAddress))

            if sect.Misc.VirtualSize > size_of_image:
                raise ValueError("Section VirtualSize is too large: {}".format(
                    sect.Misc.VirtualSize))

            if sect.SizeOfRawData > size_of_image:
                raise ValueError(
                    "Section SizeOfRawData is too large: {}".format(
                        sect.SizeOfRawData))

            if sect is not None:
                # It doesn't matter if this is too big, because it'll get overwritten by the later layers
                sect_size = conversion.round(sect.Misc.VirtualSize,
                                             section_alignment,
                                             up=True)
                sectheader = read_layer.read(sect.vol.offset, sect_header_size)
                sectheader = self.replace_header_field(sect, sectheader,
                                                       sect.PointerToRawData,
                                                       sect.VirtualAddress)
                sectheader = self.replace_header_field(sect, sectheader,
                                                       sect.SizeOfRawData,
                                                       sect_size)
                sectheader = self.replace_header_field(sect, sectheader,
                                                       sect.Misc.VirtualSize,
                                                       sect_size)

                offset = start_addr + (counter * sect_header_size)
                yield offset, sectheader
                counter += 1