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