def parse_userassist_data(self, reg_val): """Reads the raw data of a _CM_KEY_VALUE and returns a dict of userassist fields.""" item = { "id": renderers.UnparsableValue(), "count": renderers.UnparsableValue(), "focus": renderers.UnparsableValue(), "time": renderers.UnparsableValue(), "lastupdated": renderers.UnparsableValue(), "rawdata": renderers.UnparsableValue(), } userassist_data = reg_val.decode_data() if userassist_data is None: return item item["rawdata"] = userassist_data if self._win7 is None: # if OS is still unknown at this point, return the default item which just has the rawdata return item if len(userassist_data) < self._userassist_size: return item userassist_layer_name = self.context.layers.free_layer_name( "userassist_buffer") buffer = BufferDataLayer(self.context, self._config_path, userassist_layer_name, userassist_data) self.context.add_layer(buffer) userassist_obj = self.context.object(object_type=self._reg_table_name + constants.BANG + self._userassist_type_name, layer_name=userassist_layer_name, offset=0) if self._win7: item["id"] = renderers.NotApplicableValue() item["count"] = int(userassist_obj.Count) seconds = (userassist_obj.FocusTime + 500) / 1000.0 time = datetime.timedelta( seconds=seconds) if seconds > 0 else userassist_obj.FocusTime item["focus"] = int(userassist_obj.FocusCount) item["time"] = str(time) else: item["id"] = int(userassist_obj.ID) item["count"] = int(userassist_obj.CountStartingAtFive if userassist_obj.CountStartingAtFive < 5 else userassist_obj.CountStartingAtFive - 5) item["focus"] = renderers.NotApplicableValue() item["time"] = renderers.NotApplicableValue() item["lastupdated"] = conversion.wintime_to_datetime( userassist_obj.LastUpdated.QuadPart) return item
def key_iterator( cls, hive: RegistryHive, node_path: Sequence[objects.StructType] = None, recurse: bool = False ) -> Iterable[Tuple[int, bool, datetime.datetime, str, bool, interfaces.objects.ObjectInterface]]: """Walks through a set of nodes from a given node (last one in node_path). Avoids loops by not traversing into nodes already present in the node_path. Args: hive: The registry hive to walk node_path: The list of nodes that make up the recurse: Traverse down the node tree or stay only on the same level Yields: A tuple of results (depth, is_key, last write time, path, volatile, and the node). """ if not node_path: node_path = [hive.get_node(hive.root_cell_offset)] if not isinstance(node_path, list) or len(node_path) < 1: vollog.warning( "Hive walker was not passed a valid node_path (or None)") return node = node_path[-1] key_path_items = [hive] + node_path[1:] key_path = '\\'.join([k.get_name() for k in key_path_items]) if node.vol.type_name.endswith(constants.BANG + '_CELL_DATA'): raise RegistryFormatException( hive.name, "Encountered _CELL_DATA instead of _CM_KEY_NODE") last_write_time = conversion.wintime_to_datetime( node.LastWriteTime.QuadPart) for key_node in node.get_subkeys(): result = (len(node_path), True, last_write_time, key_path, key_node.get_volatile(), key_node) yield result if recurse: if key_node.vol.offset not in [ x.vol.offset for x in node_path ]: try: key_node.get_name() except exceptions.InvalidAddressException as excp: vollog.debug(excp) continue yield from cls.key_iterator(hive, node_path + [key_node], recurse=recurse) for value_node in node.get_values(): result = (len(node_path), False, last_write_time, key_path, node.get_volatile(), value_node) yield result
def get_create_time(self): dt_obj = conversion.wintime_to_datetime(self.CreateTime.QuadPart) if isinstance(dt_obj, interfaces.renderers.BaseAbsentValue): return dt_obj # return None if the timestamp seems invalid if not (self.MIN_CREATETIME_YEAR < dt_obj.year < self.MAX_CREATETIME_YEAR): return None else: return dt_obj
def _generator(self, layer: crash.WindowsCrashDump32Layer): header = layer.get_header() uptime = datetime.timedelta(microseconds=int(header.SystemUpTime) / 10) if header.DumpType == 0x1: dump_type = "Full Dump (0x1)" elif header.DumpType == 0x5: dump_type = "Bitmap Dump (0x5)" else: # this should never happen since the crash layer only accepts 0x1 and 0x5 dump_type = "Unknown/Unsupported ({:#x})".format(header.DumpType) if header.DumpType == 0x5: summary_header = layer.get_summary_header() bitmap_header_size = format_hints.Hex(summary_header.HeaderSize) bitmap_size = format_hints.Hex(summary_header.BitmapSize) bitmap_pages = format_hints.Hex(summary_header.Pages) else: bitmap_header_size = bitmap_size = bitmap_pages = renderers.NotApplicableValue( ) yield (0, ( utility.array_to_string(header.Signature), header.MajorVersion, header.MinorVersion, format_hints.Hex(header.DirectoryTableBase), format_hints.Hex(header.PfnDataBase), format_hints.Hex(header.PsLoadedModuleList), format_hints.Hex(header.PsActiveProcessHead), header.MachineImageType, header.NumberProcessors, format_hints.Hex(header.KdDebuggerDataBlock), dump_type, str(uptime), utility.array_to_string(header.Comment), conversion.wintime_to_datetime(header.SystemTime), bitmap_header_size, bitmap_size, bitmap_pages, ))
def _generator(self, procs): pe_table_name = intermed.IntermediateSymbolTable.create( self.context, self.config_path, "windows", "pe", class_types=pe.class_types) kuser = info.Info.get_kuser_structure(self.context, self.config['primary'], self.config['nt_symbols']) nt_major_version = int(kuser.NtMajorVersion) nt_minor_version = int(kuser.NtMinorVersion) # LoadTime only applies to versions higher or equal to Window 7 (6.1 and higher) dll_load_time_field = (nt_major_version > 6) or ( nt_major_version == 6 and nt_minor_version >= 1) for proc in procs: proc_id = proc.UniqueProcessId proc_layer_name = proc.add_process_layer() for entry in proc.load_order_modules(): BaseDllName = FullDllName = renderers.UnreadableValue() try: BaseDllName = entry.BaseDllName.get_string() # We assume that if the BaseDllName points to an invalid buffer, so will FullDllName FullDllName = entry.FullDllName.get_string() except exceptions.InvalidAddressException: pass if dll_load_time_field: # Versions prior to 6.1 won't have the LoadTime attribute # and 32bit version shouldn't have the Quadpart according to MSDN try: DllLoadTime = conversion.wintime_to_datetime( entry.LoadTime.QuadPart) except exceptions.InvalidAddressException: DllLoadTime = renderers.UnreadableValue() else: DllLoadTime = renderers.NotApplicableValue() file_output = "Disabled" if self.config['dump']: file_handle = self.dump_pe( self.context, pe_table_name, entry, self.open, proc_layer_name, prefix="pid.{}.".format(proc_id)) file_output = "Error outputting file" if file_handle: file_handle.close() file_output = file_handle.preferred_filename yield (0, (proc.UniqueProcessId, proc.ImageFileName.cast( "string", max_length=proc.ImageFileName.vol.count, errors='replace'), format_hints.Hex(entry.DllBase), format_hints.Hex(entry.SizeOfImage), BaseDllName, FullDllName, DllLoadTime, file_output))
def get_exit_time(self): return conversion.wintime_to_datetime(self.ExitTime.QuadPart)
def get_create_time(self): return conversion.wintime_to_datetime(self.CreateTime.QuadPart)
def get_time(self): wintime = (self.High1Time << 32) | self.LowPart return conversion.wintime_to_datetime(wintime)
def list_userassist( self, hive: RegistryHive) -> Generator[Tuple[int, Tuple], None, None]: """Generate userassist data for a registry hive.""" hive_name = hive.hive.cast(self.config["nt_symbols"] + constants.BANG + "_CMHIVE").get_name() if self._win7 is None: try: self._win7 = self._win7_or_later() except exceptions.SymbolError: # self._win7 will be None and only registry value rawdata will be output pass self._determine_userassist_type() userassist_node_path = hive.get_key( "software\\microsoft\\windows\\currentversion\\explorer\\userassist", return_list=True) if not userassist_node_path: vollog.warning( "list_userassist did not find a valid node_path (or None)") return if not isinstance(userassist_node_path, list): vollog.warning( "userassist_node_path did not return a list as expected") return userassist_node = userassist_node_path[-1] # iterate through the GUIDs under the userassist key for guidkey in userassist_node.get_subkeys(): # each guid key should have a Count key in it for countkey in guidkey.get_subkeys(): countkey_path = countkey.get_key_path() countkey_last_write_time = conversion.wintime_to_datetime( countkey.LastWriteTime.QuadPart) # output the parent Count key result = ( 0, (renderers.format_hints.Hex(hive.hive_offset), hive_name, countkey_path, countkey_last_write_time, "Key", renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue()) ) # type: Tuple[int, Tuple[format_hints.Hex, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any, Any]] yield result # output any subkeys under Count for subkey in countkey.get_subkeys(): subkey_name = subkey.get_name() result = (1, ( renderers.format_hints.Hex(hive.hive_offset), hive_name, countkey_path, countkey_last_write_time, "Subkey", subkey_name, renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), renderers.NotApplicableValue(), )) yield result # output any values under Count for value in countkey.get_values(): value_name = value.get_name() try: value_name = codecs.encode(value_name, "rot_13") except UnicodeDecodeError: pass if self._win7: guid = value_name.split("\\")[0] if guid in self._folder_guids: value_name = value_name.replace( guid, self._folder_guids[guid]) userassist_data_dict = self.parse_userassist_data(value) result = (1, ( renderers.format_hints.Hex(hive.hive_offset), hive_name, countkey_path, countkey_last_write_time, "Value", value_name, userassist_data_dict["id"], userassist_data_dict["count"], userassist_data_dict["focus"], userassist_data_dict["time"], userassist_data_dict["lastupdated"], format_hints.HexBytes(userassist_data_dict["rawdata"]), )) yield result
def _generator(self): layer = self.context.layers[self.config['primary']] # Yara Rule to scan for MFT Header Signatures rules = yarascan.YaraScan.process_yara_options( {'yara_rules': '/FILE0|FILE\*|BAAD/'}) # Read in the Symbol File symbol_table = intermed.IntermediateSymbolTable.create( context=self.context, config_path=self.config_path, sub_path="windows", filename="mft", class_types={ 'FILE_NAME_ENTRY': mft.MFTFileName, 'MFT_ENTRY': mft.MFTEntry }) # get each of the individual Field Sets mft_object = symbol_table + constants.BANG + "MFT_ENTRY" attribute_object = symbol_table + constants.BANG + "ATTRIBUTE" header_object = symbol_table + constants.BANG + "ATTR_HEADER" si_object = symbol_table + constants.BANG + "STANDARD_INFORMATION_ENTRY" fn_object = symbol_table + constants.BANG + "FILE_NAME_ENTRY" # Scan the layer for Raw MFT records and parse the fields for offset, _rule_name, _name, _value in layer.scan( context=self.context, scanner=yarascan.YaraScanner(rules=rules)): try: mft_record = self.context.object(mft_object, offset=offset, layer_name=layer.name) # We will update this on each pass in the next loop and use it as the new offset. attr_base_offset = mft_record.FirstAttrOffset attr_header = self.context.object(header_object, offset=offset + attr_base_offset, layer_name=layer.name) # There is no field that has a count of Attributes # Keep Attempting to read attributes until we get an invalid attr_header.AttrType while attr_header.AttrType.is_valid_choice: vollog.debug(f"Attr Type: {attr_header.AttrType.lookup()}") # Offset past the headers to the attribute data attr_data_offset = offset + attr_base_offset + self.context.symbol_space.get_type( attribute_object).relative_child_offset("Attr_Data") # MFT Flags determine the file type or dir # If we don't have a valid enum, coerce to hex so we can keep the record try: mft_flag = mft_record.Flags.lookup() except ValueError: mft_flag = hex(mft_record.Flags) # Standard Information Attribute if attr_header.AttrType.lookup() == 'STANDARD_INFORMATION': attr_data = self.context.object( si_object, offset=attr_data_offset, layer_name=layer.name) yield 0, ( format_hints.Hex(attr_data_offset), mft_record.get_signature(), mft_record.RecordNumber, mft_record.LinkCount, mft_flag, renderers.NotApplicableValue(), attr_header.AttrType.lookup(), conversion.wintime_to_datetime( attr_data.CreationTime), conversion.wintime_to_datetime( attr_data.ModifiedTime), conversion.wintime_to_datetime( attr_data.UpdatedTime), conversion.wintime_to_datetime( attr_data.AccessedTime), renderers.NotApplicableValue(), ) # File Name Attribute if attr_header.AttrType.lookup() == 'FILE_NAME': attr_data = self.context.object( fn_object, offset=attr_data_offset, layer_name=layer.name) file_name = attr_data.get_full_name() # If we don't have a valid enum, coerce to hex so we can keep the record try: permissions = attr_data.Flags.lookup() except ValueError: permissions = hex(attr_data.Flags) yield 1, (format_hints.Hex(attr_data_offset), mft_record.get_signature(), mft_record.RecordNumber, mft_record.LinkCount, mft_flag, permissions, attr_header.AttrType.lookup(), conversion.wintime_to_datetime( attr_data.CreationTime), conversion.wintime_to_datetime( attr_data.ModifiedTime), conversion.wintime_to_datetime( attr_data.UpdatedTime), conversion.wintime_to_datetime( attr_data.AccessedTime), file_name) # If there's no advancement the loop will never end, so break it now if attr_header.Length == 0: break # Update the base offset to point to the next attribute attr_base_offset += attr_header.Length # Get the next attribute attr_header = self.context.object(header_object, offset=offset + attr_base_offset, layer_name=layer.name) except exceptions.PagedInvalidAddressException: pass