From 622b7247c5c4b857a7f3248d8484dcb49b8a435d Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Thu, 25 Jun 2020 10:23:25 -0400 Subject: [PATCH 01/35] Add Bluetooth initial Add bluetooth initial support --- .../recentactivity/ExtractRegistry.java | 113 ++++++++++++++++++ thirdparty/rr-full/plugins/system | 1 + 2 files changed, 114 insertions(+) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 80171f0672..e1c373b3cf 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -82,9 +82,12 @@ import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOC import org.sleuthkit.datamodel.BlackboardAttribute; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; @@ -337,6 +340,10 @@ class ExtractRegistry extends Extract { } catch (IOException | TskCoreException ex) { logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex); } + } else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", + this.getName(), regFileName)); } try { Report report = currentCase.addReport(regOutputFiles.fullPlugins, @@ -930,6 +937,112 @@ class ExtractRegistry extends Extract { return false; } + + private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) { + File regfile = new File(regfilePath); + try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + String line = reader.readLine(); + while (line != null) { + line = line.trim(); + + if (line.matches("^network v.*")) { + // parseNetworkInterfaces(regfile, reader); + } else if (line.matches("^bthport v..*")) { + parseBlueToothDevices(regAbstractFile, reader) ; + } + line = reader.readLine(); + } + return true; + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS + } + + return false; + + } + + /** + * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + // date format for plugin Tue Jun 23 10:27:54 2020 Z + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); + Long bthLastSeen = Long.valueOf(0); + Long bthLastConnected = Long.valueOf(0); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("Device Unique ID")) { + // Columns are seperated by colons : + // Data : Values + // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Radio Support not found")) { + // Split line on "> " which is the record delimiter between position and file + String deviceTokens[] = line.split(": "); + String deviceUniqueId = deviceTokens[1]; + line = reader.readLine(); + // Default device name to unknown as a device name may not exist. + String deviceName = "Unknown"; + if (line.contains("Name")) { + String nameTokens[] = line.split(": "); + deviceName = nameTokens[1]; + line = reader.readLine(); + } + String lastSeenTokens[] = line.split(": "); + String lastSeen = lastSeenTokens[1].replace(" Z", "");; + line = reader.readLine(); + String lastConnectedTokens[] = line.split(": "); + String lastConnected = lastConnectedTokens[1].replace(" Z", "");; + try { + Date usedSeenDate = dateFormat.parse(lastSeen); + bthLastSeen = usedSeenDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", lastSeen), ex); //NON-NLS + } + try { + Date usedConnectedDate = dateFormat.parse(lastConnected); + bthLastConnected = usedConnectedDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last connected attribute.", lastSeen), ex); //NON-NLS + } + + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_DEVICE_ID, getName(), deviceUniqueId)); + attributes.add(new BlackboardAttribute(TSK_NAME, getName(), deviceName)); + attributes.add(new BlackboardAttribute(TSK_DATETIME, getName(), bthLastSeen)); + attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), bthLastConnected)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + } + // Read blank line between records then next read line is start of next block + line = reader.readLine(); + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + /** * Parse the output of the SAM regripper plugin to get additional Account * information diff --git a/thirdparty/rr-full/plugins/system b/thirdparty/rr-full/plugins/system index f95897e19d..846b9b7a7f 100644 --- a/thirdparty/rr-full/plugins/system +++ b/thirdparty/rr-full/plugins/system @@ -4,6 +4,7 @@ appcompatcache auditfail backuprestore bam +btconfig bthport comfoo compname From 2cde791bafa16eccf087fff5db9b35001dc3e3bc Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 21 Jul 2020 14:20:56 -0400 Subject: [PATCH 02/35] Update ExtractRegistry.java --- .../recentactivity/ExtractRegistry.java | 4258 ++++++++--------- 1 file changed, 2117 insertions(+), 2141 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index e95617e69b..c98965adbe 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -1,2141 +1,2117 @@ -/* - * - * Autopsy Forensic Browser - * - * Copyright 2012-2020 Basis Technology Corp. - * - * Copyright 2012 42six Solutions. - * Contact: aebadirad 42six com - * Project Contact/Architect: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.recentactivity; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.StringReader; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.logging.Level; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import org.apache.commons.io.FilenameUtils; -import org.openide.modules.InstalledFileLocator; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.ExecUtil; -import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.coreutils.PlatformUtil; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; -import org.sleuthkit.autopsy.ingest.IngestJobContext; -import org.sleuthkit.autopsy.recentactivity.UsbDeviceIdMapper.USBInfo; -import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; -import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; -import java.nio.file.Path; -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; -import java.util.Set; -import java.util.HashSet; -import static java.util.Locale.US; -import static java.util.TimeZone.getTimeZone; -import org.openide.util.Lookup; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; -import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; -import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; -import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.Account; -import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; -import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_ACCOUNT; -import org.sleuthkit.datamodel.BlackboardAttribute; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME; -import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; -import org.sleuthkit.datamodel.Report; -import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.datamodel.TskDataException; - -/** - * Extract windows registry data using regripper. Runs two versions of - * regripper. One is the generally available set of plug-ins and the second is a - * set that were customized for Autopsy to produce a more structured output of - * XML so that we can parse and turn into blackboard artifacts. - */ -@NbBundle.Messages({ - "RegRipperNotFound=Autopsy RegRipper executable not found.", - "RegRipperFullNotFound=Full version RegRipper executable not found.", - "Progress_Message_Analyze_Registry=Analyzing Registry Files", - "Shellbag_Artifact_Display_Name=Shell Bags", - "Shellbag_Key_Attribute_Display_Name=Key", - "Shellbag_Last_Write_Attribute_Display_Name=Last Write", - "Recently_Used_Artifacts_Office_Trustrecords=Stored in TrustRecords because Office security exception was granted", - "Recently_Used_Artifacts_ArcHistory=Recently opened by 7Zip", - "Recently_Used_Artifacts_Applets=Recently opened according to Applets registry key", - "Recently_Used_Artifacts_Mmc=Recently opened according to Windows Management Console MRU", - "Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU", - "Recently_Used_Artifacts_Officedocs=Recently opened according to Office MRU", - "Recently_Used_Artifacts_Adobe=Recently opened according to Adobe MRU", - "Recently_Used_Artifacts_Mediaplayer=Recently opened according to Media Player MRU", - "Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)" -}) -class ExtractRegistry extends Extract { - - private static final String USERNAME_KEY = "Username"; //NON-NLS - private static final String SID_KEY = "SID"; //NON-NLS - private static final String RID_KEY = "RID"; //NON-NLS - private static final String ACCOUNT_CREATED_KEY = "Account Created"; //NON-NLS - private static final String LAST_LOGIN_KEY = "Last Login Date"; //NON-NLS - private static final String LOGIN_COUNT_KEY = "Login Count"; //NON-NLS - private static final String FULL_NAME_KEY = "Full Name"; //NON-NLS - private static final String USER_COMMENT_KEY = "User Comment"; //NON-NLS - private static final String ACCOUNT_TYPE_KEY = "Account Type"; //NON-NLS - private static final String NAME_KEY = "Name"; //NON-NLS - private static final String PWD_RESET_KEY = "Pwd Rest Date"; //NON-NLS - private static final String PWD_FAILE_KEY = "Pwd Fail Date"; //NON-NLS - private static final String INTERNET_NAME_KEY = "InternetName"; //NON-NLS - private static final String PWD_DOES_NOT_EXPIRE_KEY = "Password does not expire"; //NON-NLS - private static final String ACCOUNT_DISABLED_KEY = "Account Disabled"; //NON-NLS - private static final String PWD_NOT_REQUIRED_KEY = "Password not required"; //NON-NLS - private static final String NORMAL_ACCOUNT_KEY = "Normal user account"; //NON-NLS - private static final String HOME_DIRECTORY_REQUIRED_KEY = "Home directory required"; - private static final String TEMPORARY_DUPLICATE_ACCOUNT = "Temporary duplicate account"; - private static final String MNS_LOGON_ACCOUNT_KEY = "MNS logon user account"; - private static final String INTERDOMAIN_TRUST_ACCOUNT_KEY = "Interdomain trust account"; - private static final String WORKSTATION_TRUST_ACCOUNT = "Workstation trust account"; - private static final String SERVER_TRUST_ACCOUNT = "Server trust account"; - private static final String ACCOUNT_AUTO_LOCKED = "Account auto locked"; - private static final String PASSWORD_HINT = "Password Hint"; - - private static final String[] PASSWORD_SETTINGS_FLAGS = {PWD_DOES_NOT_EXPIRE_KEY, PWD_NOT_REQUIRED_KEY}; - private static final String[] ACCOUNT_SETTINGS_FLAGS = {ACCOUNT_AUTO_LOCKED, HOME_DIRECTORY_REQUIRED_KEY, ACCOUNT_DISABLED_KEY}; - private static final String[] ACCOUNT_TYPE_FLAGS = {NORMAL_ACCOUNT_KEY, SERVER_TRUST_ACCOUNT, WORKSTATION_TRUST_ACCOUNT, INTERDOMAIN_TRUST_ACCOUNT_KEY, MNS_LOGON_ACCOUNT_KEY, TEMPORARY_DUPLICATE_ACCOUNT}; - - final private static UsbDeviceIdMapper USB_MAPPER = new UsbDeviceIdMapper(); - final private static String RIP_EXE = "rip.exe"; - final private static String RIP_PL = "rip.pl"; - final private static String RIP_PL_INCLUDE_FLAG = "-I"; - final private static int MS_IN_SEC = 1000; - final private static String NEVER_DATE = "Never"; - final private static String SECTION_DIVIDER = "-------------------------"; - final private static Logger logger = Logger.getLogger(ExtractRegistry.class.getName()); - private final List rrCmd = new ArrayList<>(); - private final List rrFullCmd = new ArrayList<>(); - private final Path rrHome; // Path to the Autopsy version of RegRipper - private final Path rrFullHome; // Path to the full version of RegRipper - private Content dataSource; - private IngestJobContext context; - private Map userNameMap; - - private static final String SHELLBAG_ARTIFACT_NAME = "RA_SHELL_BAG"; //NON-NLS - private static final String SHELLBAG_ATTRIBUTE_LAST_WRITE = "RA_SHELL_BAG_LAST_WRITE"; //NON-NLS - private static final String SHELLBAG_ATTRIBUTE_KEY= "RA_SHELL_BAG_KEY"; //NON-NLS - - BlackboardArtifact.Type shellBagArtifactType = null; - BlackboardAttribute.Type shellBagKeyAttributeType = null; - BlackboardAttribute.Type shellBagLastWriteAttributeType = null; - - ExtractRegistry() throws IngestModuleException { - moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text"); - - final File rrRoot = InstalledFileLocator.getDefault().locate("rr", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS - if (rrRoot == null) { - throw new IngestModuleException(Bundle.RegRipperNotFound()); - } - - final File rrFullRoot = InstalledFileLocator.getDefault().locate("rr-full", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS - if (rrFullRoot == null) { - throw new IngestModuleException(Bundle.RegRipperFullNotFound()); - } - - String executableToRun = RIP_EXE; - if (!PlatformUtil.isWindowsOS()) { - executableToRun = RIP_PL; - } - rrHome = rrRoot.toPath(); - String rrPath = rrHome.resolve(executableToRun).toString(); - rrFullHome = rrFullRoot.toPath(); - - if (!(new File(rrPath).exists())) { - throw new IngestModuleException(Bundle.RegRipperNotFound()); - } - String rrFullPath = rrFullHome.resolve(executableToRun).toString(); - if (!(new File(rrFullPath).exists())) { - throw new IngestModuleException(Bundle.RegRipperFullNotFound()); - } - if (PlatformUtil.isWindowsOS()) { - rrCmd.add(rrPath); - rrFullCmd.add(rrFullPath); - } else { - String perl; - File usrBin = new File("/usr/bin/perl"); - File usrLocalBin = new File("/usr/local/bin/perl"); - if (usrBin.canExecute() && usrBin.exists() && !usrBin.isDirectory()) { - perl = "/usr/bin/perl"; - } else if (usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()) { - perl = "/usr/local/bin/perl"; - } else { - throw new IngestModuleException("perl not found in your system"); - } - rrCmd.add(perl); - rrCmd.add(RIP_PL_INCLUDE_FLAG); - rrCmd.add(rrHome.toString()); - rrCmd.add(rrPath); - rrFullCmd.add(perl); - rrFullCmd.add(RIP_PL_INCLUDE_FLAG); - rrFullCmd.add(rrFullHome.toString()); - rrFullCmd.add(rrFullPath); - } - } - - /** - * Search for the registry hives on the system. - */ - private List findRegistryFiles() { - List allRegistryFiles = new ArrayList<>(); - org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager(); - - // find the sam hives', process this first so we can map the user id's and sids for later use - try { - allRegistryFiles.addAll(fileManager.findFiles(dataSource, "sam", "/system32/config")); //NON-NLS - } catch (TskCoreException ex) { - String msg = NbBundle.getMessage(this.getClass(), - "ExtractRegistry.findRegFiles.errMsg.errReadingFile", "sam"); - logger.log(Level.WARNING, msg, ex); - this.addErrorMessage(this.getName() + ": " + msg); - } - - - // find the user-specific ntuser-dat files - try { - allRegistryFiles.addAll(fileManager.findFiles(dataSource, "ntuser.dat")); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error fetching 'ntuser.dat' file."); //NON-NLS - } - - // find the user-specific ntuser-dat files - try { - allRegistryFiles.addAll(fileManager.findFiles(dataSource, "usrclass.dat")); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.WARNING, String.format("Error finding 'usrclass.dat' files."), ex); //NON-NLS - } - - // find the system hives' - String[] regFileNames = new String[]{"system", "software", "security"}; //NON-NLS - for (String regFileName : regFileNames) { - try { - allRegistryFiles.addAll(fileManager.findFiles(dataSource, regFileName, "/system32/config")); //NON-NLS - } catch (TskCoreException ex) { - String msg = NbBundle.getMessage(this.getClass(), - "ExtractRegistry.findRegFiles.errMsg.errReadingFile", regFileName); - logger.log(Level.WARNING, msg, ex); - this.addErrorMessage(this.getName() + ": " + msg); - } - } - return allRegistryFiles; - } - - /** - * Identifies registry files in the database by mtimeItem, runs regripper on - * them, and parses the output. - */ - private void analyzeRegistryFiles() { - List allRegistryFiles = findRegistryFiles(); - - // open the log file - FileWriter logFile = null; - try { - logFile = new FileWriter(RAImageIngestModule.getRAOutputPath(currentCase, "reg") + File.separator + "regripper-info.txt"); //NON-NLS - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - for (AbstractFile regFile : allRegistryFiles) { - String regFileName = regFile.getName(); - long regFileId = regFile.getId(); - String regFileNameLocal = RAImageIngestModule.getRATempPath(currentCase, "reg") + File.separator + regFileName; - String outputPathBase = RAImageIngestModule.getRAOutputPath(currentCase, "reg") + File.separator + regFileName + "-regripper-" + Long.toString(regFileId); //NON-NLS - File regFileNameLocalFile = new File(regFileNameLocal); - try { - ContentUtils.writeToFile(regFile, regFileNameLocalFile, context::dataSourceIngestIsCancelled); - } catch (ReadContentInputStreamException ex) { - logger.log(Level.WARNING, String.format("Error reading registry file '%s' (id=%d).", - regFile.getName(), regFileId), ex); //NON-NLS - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp", - this.getName(), regFileName)); - continue; - } catch (IOException ex) { - logger.log(Level.SEVERE, String.format("Error writing temp registry file '%s' for registry file '%s' (id=%d).", - regFileNameLocal, regFile.getName(), regFileId), ex); //NON-NLS - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp", - this.getName(), regFileName)); - continue; - } - - if (context.dataSourceIngestIsCancelled()) { - break; - } - - try { - if (logFile != null) { - logFile.write(Long.toString(regFileId) + "\t" + regFile.getUniquePath() + "\n"); - } - } catch (TskCoreException | IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - - logger.log(Level.INFO, "{0}- Now getting registry information from {1}", new Object[]{moduleName, regFileNameLocal}); //NON-NLS - RegOutputFiles regOutputFiles = ripRegistryFile(regFileNameLocal, outputPathBase); - if (context.dataSourceIngestIsCancelled()) { - break; - } - - // parse the autopsy-specific output - if (regOutputFiles.autopsyPlugins.isEmpty() == false && parseAutopsyPluginOutput(regOutputFiles.autopsyPlugins, regFile) == false) { - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", - this.getName(), regFileName)); - } - - // create a report for the full output - if (!regOutputFiles.fullPlugins.isEmpty()) { - //parse the full regripper output from SAM hive files - if (regFileNameLocal.toLowerCase().contains("sam") && parseSamPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", - this.getName(), regFileName)); - } else if (regFileNameLocal.toLowerCase().contains("ntuser") || regFileNameLocal.toLowerCase().contains("usrclass")) { - try { - List shellbags = ShellBagParser.parseShellbagOutput(regOutputFiles.fullPlugins); - createShellBagArtifacts(regFile, shellbags); - createRecentlyUsedArtifacts(regOutputFiles.fullPlugins, regFile); - } catch (IOException | TskCoreException ex) { - logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex); - } - } else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { - this.addErrorMessage( - NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", - this.getName(), regFileName)); - } - try { - Report report = currentCase.addReport(regOutputFiles.fullPlugins, - NbBundle.getMessage(this.getClass(), "ExtractRegistry.parentModuleName.noSpace"), - "RegRipper " + regFile.getUniquePath(), regFile); //NON-NLS - - // Index the report content so that it will be available for keyword search. - KeywordSearchService searchService = Lookup.getDefault().lookup(KeywordSearchService.class); - if (null == searchService) { - logger.log(Level.WARNING, "Keyword search service not found. Report will not be indexed"); - } else { - searchService.index(report); - report.close(); - } - } catch (TskCoreException e) { - this.addErrorMessage("Error adding regripper output as Autopsy report: " + e.getLocalizedMessage()); //NON-NLS - } - } - // delete the hive - regFileNameLocalFile.delete(); - } - - try { - if (logFile != null) { - logFile.close(); - } - } catch (IOException ex) { - logger.log(Level.SEVERE, null, ex); - } - } - - /** - * Execute regripper on the given registry. - * - * @param regFilePath Path to local copy of registry - * @param outFilePathBase Path to location to save output file to. Base - * mtimeItem that will be extended on - */ - private RegOutputFiles ripRegistryFile(String regFilePath, String outFilePathBase) { - String autopsyType = ""; // Type argument for rr for autopsy-specific modules - String fullType; // Type argument for rr for full set of modules - - RegOutputFiles regOutputFiles = new RegOutputFiles(); - - if (regFilePath.toLowerCase().contains("system")) { //NON-NLS - autopsyType = "autopsysystem"; //NON-NLS - fullType = "system"; //NON-NLS - } else if (regFilePath.toLowerCase().contains("software")) { //NON-NLS - autopsyType = "autopsysoftware"; //NON-NLS - fullType = "software"; //NON-NLS - } else if (regFilePath.toLowerCase().contains("ntuser")) { //NON-NLS - autopsyType = "autopsyntuser"; //NON-NLS - fullType = "ntuser"; //NON-NLS - } else if (regFilePath.toLowerCase().contains("sam")) { //NON-NLS - //fullType sam output files are parsed for user information - fullType = "sam"; //NON-NLS - } else if (regFilePath.toLowerCase().contains("security")) { //NON-NLS - fullType = "security"; //NON-NLS - }else if (regFilePath.toLowerCase().contains("usrclass")) { //NON-NLS - fullType = "usrclass"; //NON-NLS - } else { - return regOutputFiles; - } - - // run the autopsy-specific set of modules - if (!autopsyType.isEmpty()) { - regOutputFiles.autopsyPlugins = outFilePathBase + "-autopsy.txt"; //NON-NLS - String errFilePath = outFilePathBase + "-autopsy.err.txt"; //NON-NLS - logger.log(Level.INFO, "Writing RegRipper results to: {0}", regOutputFiles.autopsyPlugins); //NON-NLS - executeRegRipper(rrCmd, rrHome, regFilePath, autopsyType, regOutputFiles.autopsyPlugins, errFilePath); - } - if (context.dataSourceIngestIsCancelled()) { - return regOutputFiles; - } - - // run the full set of rr modules - if (!fullType.isEmpty()) { - regOutputFiles.fullPlugins = outFilePathBase + "-full.txt"; //NON-NLS - String errFilePath = outFilePathBase + "-full.err.txt"; //NON-NLS - logger.log(Level.INFO, "Writing Full RegRipper results to: {0}", regOutputFiles.fullPlugins); //NON-NLS - executeRegRipper(rrFullCmd, rrFullHome, regFilePath, fullType, regOutputFiles.fullPlugins, errFilePath); - try { - scanErrorLogs(errFilePath); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS - this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); - } - } - return regOutputFiles; - } - - private void scanErrorLogs(String errFilePath) throws IOException { - File regfile = new File(errFilePath); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { - String line = reader.readLine(); - while (line != null) { - line = line.trim(); - if (line.toLowerCase().contains("error") || line.toLowerCase().contains("@inc")) { - logger.log(Level.WARNING, "Regripper file {0} contains errors from run", errFilePath); //NON-NLS - - } - line = reader.readLine(); - } - } - } - - private void executeRegRipper(List regRipperPath, Path regRipperHomeDir, String hiveFilePath, String hiveFileType, String outputFile, String errFile) { - try { - List commandLine = new ArrayList<>(); - for (String cmd : regRipperPath) { - commandLine.add(cmd); - } - commandLine.add("-r"); //NON-NLS - commandLine.add(hiveFilePath); - commandLine.add("-f"); //NON-NLS - commandLine.add(hiveFileType); - - ProcessBuilder processBuilder = new ProcessBuilder(commandLine); - processBuilder.directory(regRipperHomeDir.toFile()); // RegRipper 2.8 has to be run from its own directory - processBuilder.redirectOutput(new File(outputFile)); - processBuilder.redirectError(new File(errFile)); - ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); - } catch (IOException ex) { - logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS - this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); - } - } - - // @@@ VERIFY that we are doing the right thing when we parse multiple NTUSER.DAT - /** - * - * @param regFilePath Path to the output file produced by RegRipper. - * @param regFile File object for registry that we are parsing (to make - * blackboard artifacts with) - * - * @return - */ - private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFile) { - FileInputStream fstream = null; - List newArtifacts = new ArrayList<>(); - try { - // Read the file in and create a Document and elements - File regfile = new File(regFilePath); - fstream = new FileInputStream(regfile); - String regString = new Scanner(fstream, "UTF-8").useDelimiter("\\Z").next(); //NON-NLS - String startdoc = ""; //NON-NLS - String result = regString.replaceAll("----------------------------------------", ""); - result = result.replaceAll("\\n", ""); //NON-NLS - result = result.replaceAll("\\r", ""); //NON-NLS - result = result.replaceAll("'", "'"); //NON-NLS - result = result.replaceAll("&", "&"); //NON-NLS - result = result.replace('\0', ' '); // NON-NLS - String enddoc = ""; //NON-NLS - String stringdoc = startdoc + result + enddoc; - DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - Document doc = builder.parse(new InputSource(new StringReader(stringdoc))); - - // cycle through the elements in the doc - Element oroot = doc.getDocumentElement(); - NodeList children = oroot.getChildNodes(); - int len = children.getLength(); - for (int i = 0; i < len; i++) { - - if (context.dataSourceIngestIsCancelled()) { - return false; - } - - Element tempnode = (Element) children.item(i); - - String dataType = tempnode.getNodeName(); - NodeList timenodes = tempnode.getElementsByTagName("mtime"); //NON-NLS - Long mtime = null; - if (timenodes.getLength() > 0) { - Element timenode = (Element) timenodes.item(0); - String etime = timenode.getTextContent(); - //sometimes etime will be an empty string and therefore can not be parsed into a date - if (etime != null && !etime.isEmpty()) { - try { - mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(etime).getTime(); - String Tempdate = mtime.toString(); - mtime = Long.valueOf(Tempdate) / MS_IN_SEC; - } catch (ParseException ex) { - logger.log(Level.WARNING, "Failed to parse epoch time when parsing the registry.", ex); //NON-NLS - } - } - } - - NodeList artroots = tempnode.getElementsByTagName("artifacts"); //NON-NLS - if (artroots.getLength() == 0) { - // If there isn't an artifact node, skip this entry - continue; - } - - Element artroot = (Element) artroots.item(0); - NodeList myartlist = artroot.getChildNodes(); - String parentModuleName = RecentActivityExtracterModuleFactory.getModuleName(); - - // If all artifact nodes should really go under one Blackboard artifact, need to process it differently - switch (dataType) { - case "WinVersion": //NON-NLS - String version = ""; - String systemRoot = ""; - String productId = ""; - String regOwner = ""; - String regOrg = ""; - Long installtime = null; - for (int j = 0; j < myartlist.getLength(); j++) { - Node artchild = myartlist.item(j); - // If it has attributes, then it is an Element (based off API) - if (artchild.hasAttributes()) { - Element artnode = (Element) artchild; - - String value = artnode.getTextContent(); - if (value != null) { - value = value.trim(); - } - String name = artnode.getAttribute("name"); //NON-NLS - if (name == null) { - continue; - } - switch (name) { - case "ProductName": // NON-NLS - version = value; - break; - case "CSDVersion": // NON-NLS - // This is dependant on the fact that ProductName shows up first in the module output - version = version + " " + value; - break; - case "SystemRoot": //NON-NLS - systemRoot = value; - break; - case "ProductId": //NON-NLS - productId = value; - break; - case "RegisteredOwner": //NON-NLS - regOwner = value; - break; - case "RegisteredOrganization": //NON-NLS - regOrg = value; - break; - case "InstallDate": //NON-NLS - if (value != null && !value.isEmpty()) { - try { - installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(value).getTime(); - String Tempdate = installtime.toString(); - installtime = Long.valueOf(Tempdate) / MS_IN_SEC; - } catch (ParseException e) { - logger.log(Level.WARNING, "RegRipper::Conversion on DateTime -> ", e); //NON-NLS - } - } - break; - default: - break; - } - } - } - try { - Collection bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, version)); - if (installtime != null) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, installtime)); - } - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, parentModuleName, systemRoot)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PRODUCT_ID, parentModuleName, productId)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_OWNER, parentModuleName, regOwner)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ORGANIZATION, parentModuleName, regOrg)); - - // Check if there is already an OS_INFO artifact for this file, and add to that if possible. - ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); - if (results.isEmpty()) { - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } else { - results.get(0).addAttributes(bbattributes); - } - - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard."); //NON-NLS - } - break; - case "Profiler": // NON-NLS - String os = ""; - String procArch = ""; - String tempDir = ""; - for (int j = 0; j < myartlist.getLength(); j++) { - Node artchild = myartlist.item(j); - // If it has attributes, then it is an Element (based off API) - if (artchild.hasAttributes()) { - Element artnode = (Element) artchild; - - String value = artnode.getTextContent().trim(); - String name = artnode.getAttribute("name"); //NON-NLS - switch (name) { - case "OS": // NON-NLS - os = value; - break; - case "PROCESSOR_ARCHITECTURE": // NON-NLS - procArch = value; - break; - case "PROCESSOR_IDENTIFIER": //NON-NLS - break; - case "TEMP": //NON-NLS - tempDir = value; - break; - default: - break; - } - } - } - try { - Collection bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VERSION, parentModuleName, os)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE, parentModuleName, procArch)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TEMP_DIR, parentModuleName, tempDir)); - - // Check if there is already an OS_INFO artifact for this file and add to that if possible - ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); - if (results.isEmpty()) { - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } else { - results.get(0).addAttributes(bbattributes); - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS - } - break; - case "CompName": // NON-NLS - String compName = ""; - String domain = ""; - for (int j = 0; j < myartlist.getLength(); j++) { - Node artchild = myartlist.item(j); - // If it has attributes, then it is an Element (based off API) - if (artchild.hasAttributes()) { - Element artnode = (Element) artchild; - - String value = artnode.getTextContent().trim(); - String name = artnode.getAttribute("name"); //NON-NLS - - if (name.equals("ComputerName")) { // NON-NLS - compName = value; - } else if (name.equals("Domain")) { // NON-NLS - domain = value; - } - } - } - try { - Collection bbattributes = new ArrayList<>(); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, compName)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, parentModuleName, domain)); - - // Check if there is already an OS_INFO artifact for this file and add to that if possible - ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); - if (results.isEmpty()) { - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } else { - results.get(0).addAttributes(bbattributes); - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding os info artifact to blackboard.", ex); //NON-NLS - } - break; - default: - for (int j = 0; j < myartlist.getLength(); j++) { - Node artchild = myartlist.item(j); - // If it has attributes, then it is an Element (based off API) - if (artchild.hasAttributes()) { - Element artnode = (Element) artchild; - - String value = artnode.getTextContent().trim(); - Collection bbattributes = new ArrayList<>(); - - switch (dataType) { - case "recentdocs": //NON-NLS - // BlackboardArtifact bbart = tskCase.getContentById(orgId).newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT); - // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", dataType, mtime)); - // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "RecentActivity", dataType, mtimeItem)); - // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), "RecentActivity", dataType, value)); - // bbart.addAttributes(bbattributes); - // @@@ BC: Why are we ignoring this... - break; - case "usb": //NON-NLS - try { - Long usbMtime = Long.parseLong(artnode.getAttribute("mtime")); //NON-NLS - usbMtime = Long.valueOf(usbMtime.toString()); - - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, usbMtime)); - String dev = artnode.getAttribute("dev"); //NON-NLS - String make = ""; - String model = dev; - if (dev.toLowerCase().contains("vid")) { //NON-NLS - USBInfo info = USB_MAPPER.parseAndLookup(dev); - if (info.getVendor() != null) { - make = info.getVendor(); - } - if (info.getProduct() != null) { - model = info.getProduct(); - } - } - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, parentModuleName, make)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, parentModuleName, model)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, value)); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding device attached artifact to blackboard.", ex); //NON-NLS - } - break; - case "uninstall": //NON-NLS - Long itemMtime = null; - try { - String mTimeAttr = artnode.getAttribute("mtime"); - if (mTimeAttr != null && !mTimeAttr.isEmpty()) { - itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(mTimeAttr).getTime(); //NON-NLS - itemMtime /= MS_IN_SEC; - } - } catch (ParseException ex) { - logger.log(Level.SEVERE, "Failed to parse epoch time for installed program artifact.", ex); //NON-NLS - } - - try { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, value)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, itemMtime)); - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_INSTALLED_PROG); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard.", ex); //NON-NLS - } - break; - case "office": //NON-NLS - String officeName = artnode.getAttribute("name"); //NON-NLS - - try { - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT); - // @@@ BC: Consider removing this after some more testing. It looks like an Mtime associated with the root key and not the individual item - if (mtime != null) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, parentModuleName, mtime)); - } - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, officeName)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE, parentModuleName, value)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, artnode.getNodeName())); - bbart.addAttributes(bbattributes); - - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding recent object artifact to blackboard.", ex); //NON-NLS - } - break; - - case "ProcessorArchitecture": //NON-NLS - // Architecture is now included under Profiler - //try { - // String processorArchitecture = value; - // if (processorArchitecture.equals("AMD64")) - // processorArchitecture = "x86-64"; - - // BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); - // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE.getTypeID(), parentModuleName, processorArchitecture)); - // bbart.addAttributes(bbattributes); - //} catch (TskCoreException ex) { - // logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS - //} - break; - - case "ProfileList": //NON-NLS - try { - String homeDir = value; - String sid = artnode.getAttribute("sid"); //NON-NLS - String username = artnode.getAttribute("username"); //NON-NLS - BlackboardArtifact bbart = null; - try { - //check if any of the existing artifacts match this username - ArrayList existingArtifacts = currentCase.getSleuthkitCase().getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - for (BlackboardArtifact artifact : existingArtifacts) { - if (artifact.getDataSource().getId() == regFile.getDataSourceObjectId()) { - BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID)); - if (attribute != null && attribute.getValueString().equals(sid)) { - bbart = artifact; - break; - } - } - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting existing os account artifact", ex); - } - if (bbart == null) { - //create new artifact - bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, - parentModuleName, username)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, - parentModuleName, sid)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, - parentModuleName, homeDir)); - } else { - //add attributes to existing artifact - BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME)); - - if (bbattr == null) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, - parentModuleName, username)); - } - bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH)); - if (bbattr == null) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, - parentModuleName, homeDir)); - } - } - bbart.addAttributes(bbattributes); - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS - } - break; - - case "NtuserNetwork": // NON-NLS - try { - String localPath = artnode.getAttribute("localPath"); //NON-NLS - String remoteName = value; - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_REMOTE_DRIVE); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCAL_PATH, - parentModuleName, localPath)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REMOTE_PATH, - parentModuleName, remoteName)); - bbart.addAttributes(bbattributes); - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding network artifact to blackboard.", ex); //NON-NLS - } - break; - case "SSID": // NON-NLS - String adapter = artnode.getAttribute("adapter"); //NON-NLS - try { - Long lastWriteTime = Long.parseLong(artnode.getAttribute("writeTime")); //NON-NLS - lastWriteTime = Long.valueOf(lastWriteTime.toString()); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SSID, parentModuleName, value)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, lastWriteTime)); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, adapter)); - BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_WIFI_NETWORK); - bbart.addAttributes(bbattributes); - newArtifacts.add(bbart); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error adding SSID artifact to blackboard.", ex); //NON-NLS - } - break; - case "shellfolders": // NON-NLS - // The User Shell Folders subkey stores the paths to Windows Explorer folders for the current user of the computer - // (https://technet.microsoft.com/en-us/library/Cc962613.aspx). - // No useful information. Skip. - break; - - default: - logger.log(Level.SEVERE, "Unrecognized node name: {0}", dataType); //NON-NLS - break; - } - } - } - break; - } - } // for - return true; - } catch (FileNotFoundException ex) { - logger.log(Level.WARNING, String.format("Error finding the registry file: %s", regFilePath), ex); //NON-NLS - } catch (SAXException ex) { - logger.log(Level.WARNING, String.format("Error parsing the registry XML: %s", regFilePath), ex); //NON-NLS - } catch (IOException ex) { - logger.log(Level.WARNING, String.format("Error building the document parser: %s", regFilePath), ex); //NON-NLS - } catch (ParserConfigurationException ex) { - logger.log(Level.WARNING, String.format("Error configuring the registry parser: %s", regFilePath), ex); //NON-NLS - } finally { - try { - if (fstream != null) { - fstream.close(); - } - } catch (IOException ex) { - } - - postArtifacts(newArtifacts); - } - return false; - } - - - private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) { - File regfile = new File(regfilePath); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { - String line = reader.readLine(); - while (line != null) { - line = line.trim(); - - if (line.matches("^network v.*")) { - // parseNetworkInterfaces(regfile, reader); - } else if (line.matches("^bthport v..*")) { - parseBlueToothDevices(regAbstractFile, reader) ; - } - line = reader.readLine(); - } - return true; - } catch (FileNotFoundException ex) { - logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS - } catch (IOException ex) { - logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS - } - - return false; - - } - - /** - * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - // date format for plugin Tue Jun 23 10:27:54 2020 Z - SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); - Long bthLastSeen = Long.valueOf(0); - Long bthLastConnected = Long.valueOf(0); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.contains("Device Unique ID")) { - // Columns are seperated by colons : - // Data : Values - // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected - while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Radio Support not found")) { - // Split line on "> " which is the record delimiter between position and file - String deviceTokens[] = line.split(": "); - String deviceUniqueId = deviceTokens[1]; - line = reader.readLine(); - // Default device name to unknown as a device name may not exist. - String deviceName = "Unknown"; - if (line.contains("Name")) { - String nameTokens[] = line.split(": "); - deviceName = nameTokens[1]; - line = reader.readLine(); - } - String lastSeenTokens[] = line.split(": "); - String lastSeen = lastSeenTokens[1].replace(" Z", "");; - line = reader.readLine(); - String lastConnectedTokens[] = line.split(": "); - String lastConnected = lastConnectedTokens[1].replace(" Z", "");; - try { - Date usedSeenDate = dateFormat.parse(lastSeen); - bthLastSeen = usedSeenDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", lastSeen), ex); //NON-NLS - } - try { - Date usedConnectedDate = dateFormat.parse(lastConnected); - bthLastConnected = usedConnectedDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last connected attribute.", lastSeen), ex); //NON-NLS - } - - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_DEVICE_ID, getName(), deviceUniqueId)); - attributes.add(new BlackboardAttribute(TSK_NAME, getName(), deviceName)); - attributes.add(new BlackboardAttribute(TSK_DATETIME, getName(), bthLastSeen)); - attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), bthLastConnected)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - } - // Read blank line between records then next read line is start of next block - line = reader.readLine(); - line = reader.readLine(); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Parse the output of the SAM regripper plugin to get additional Account - * information - * - * @param regFilePath the path to the registry file being parsed - * @param regAbstractFile the file to associate newly created artifacts with - * - * @return true if successful, false if parsing failed at some point - */ - private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstractFile) { - File regfile = new File(regFilePath); - List newArtifacts = new ArrayList<>(); - try (BufferedReader bufferedReader = new BufferedReader(new FileReader(regfile))) { - // Read the file in and create a Document and elements - String userInfoSection = "User Information"; - String previousLine = null; - String line = bufferedReader.readLine(); - Set> userSet = new HashSet<>(); - Map> groupMap = null; - while (line != null) { - if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains(userInfoSection)) { - readUsers(bufferedReader, userSet); - } - - if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains("Group Membership Information")) { - groupMap = readGroups(bufferedReader); - } - - previousLine = line; - line = bufferedReader.readLine(); - } - Map> userInfoMap = new HashMap<>(); - //load all the user info which was read into a map - for (Map userInfo : userSet) { - userInfoMap.put(userInfo.get(SID_KEY), userInfo); - } - //get all existing OS account artifacts - List existingOsAccounts = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - for (BlackboardArtifact osAccount : existingOsAccounts) { - //if the OS Account artifact was from the same data source check the user id - if (osAccount.getDataSource().getId() == regAbstractFile.getDataSourceObjectId()) { - BlackboardAttribute existingUserId = osAccount.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID)); - if (existingUserId != null) { - String userID = existingUserId.getValueString().trim(); - Map userInfo = userInfoMap.remove(userID); - //if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it - if (userInfo != null) { - osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true, regAbstractFile)); - } - } - } - } - - //add remaining userinfos as accounts; - for (Map userInfo : userInfoMap.values()) { - BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); - bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false, regAbstractFile)); - // index the artifact for keyword search - newArtifacts.add(bbart); - } - // Get a mapping of user sids to user names and save globally so it can be used for other areas - // of the registry, ie: BAM key - try { - userNameMap = makeUserNameMap(dataSource); - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Unable to create OS Account user name map", ex); - // This is not the end of the world we will just continue without - // user names - userNameMap = new HashMap<>(); - } - return true; - } catch (FileNotFoundException ex) { - logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS - } catch (IOException ex) { - logger.log(Level.WARNING, "Error building the document parser: {0}", ex); //NON-NLS - } catch (ParseException ex) { - logger.log(Level.WARNING, "Error parsing the the date from the registry file", ex); //NON-NLS - } catch (TskCoreException ex) { - logger.log(Level.WARNING, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS - } finally { - postArtifacts(newArtifacts); - } - return false; - } - - /** - * Creates the attribute list for the given user information and group list. - * - * @param userInfo Map of key\value pairs of user information - * @param groupList List of the groups that user belongs - * @param existingUser - * - * @return List - * - * @throws ParseException - */ - Collection getAttributesForAccount(Map userInfo, List groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException { - Collection bbattributes = new ArrayList<>(); - - SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'"); - regRipperTimeFormat.setTimeZone(getTimeZone("GMT")); - - if (!existingUser) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, - getRAModuleName(), userInfo.get(SID_KEY))); - - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, - this.moduleName, userInfo.get(USERNAME_KEY))); - } - - String value = userInfo.get(ACCOUNT_CREATED_KEY); - if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, - getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); - } - - value = userInfo.get(LAST_LOGIN_KEY); - if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, - getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); - } - - value = userInfo.get(LOGIN_COUNT_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, - getRAModuleName(), Integer.parseInt(value))); - } - - value = userInfo.get(ACCOUNT_TYPE_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, - getRAModuleName(), value)); - } - - value = userInfo.get(USER_COMMENT_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DESCRIPTION, - getRAModuleName(), value)); - } - - value = userInfo.get(NAME_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, - getRAModuleName(), value)); - } - - value = userInfo.get(INTERNET_NAME_KEY); - if (value != null && !value.isEmpty()) { - try { - // Create an account for this email, if it doesn't already exist. - Case.getCurrentCaseThrows() - .getSleuthkitCase() - .getCommunicationsManager() - .createAccountFileInstance(Account.Type.EMAIL, - value, getRAModuleName(), regAbstractFile); - } catch (NoCurrentCaseException | TskCoreException ex) { - logger.log(Level.SEVERE, - String.format("Error adding email account with value " - + "%s, to the case database for file %s [objId=%d]", - value, regAbstractFile.getName(), regAbstractFile.getId()), ex); - } - - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL, - getRAModuleName(), value)); - } - - value = userInfo.get(FULL_NAME_KEY); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DISPLAY_NAME, - getRAModuleName(), value)); - } - - value = userInfo.get(PWD_RESET_KEY); - if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET, - getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); - } - - value = userInfo.get(PASSWORD_HINT); - if (value != null && !value.isEmpty()) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_HINT, - getRAModuleName(), value)); - } - - value = userInfo.get(PWD_FAILE_KEY); - if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_FAIL, - getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); - } - - String settingString = ""; - for (String setting : PASSWORD_SETTINGS_FLAGS) { - if (userInfo.containsKey(setting)) { - settingString += setting + ", "; - } - } - - if (!settingString.isEmpty()) { - settingString = settingString.substring(0, settingString.length() - 2); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_SETTINGS, - getRAModuleName(), settingString)); - } - - settingString = ""; - for (String setting : ACCOUNT_SETTINGS_FLAGS) { - if (userInfo.containsKey(setting)) { - settingString += setting + ", "; - } - } - - if (!settingString.isEmpty()) { - settingString = settingString.substring(0, settingString.length() - 2); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_SETTINGS, - getRAModuleName(), settingString)); - } - - settingString = ""; - for (String setting : ACCOUNT_TYPE_FLAGS) { - if (userInfo.containsKey(setting)) { - settingString += setting + ", "; - } - } - - if (!settingString.isEmpty()) { - settingString = settingString.substring(0, settingString.length() - 2); - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_FLAG, - getRAModuleName(), settingString)); - } - - if (groupList != null && groupList.isEmpty()) { - String groups = ""; - for (String group : groupList) { - groups += group + ", "; - } - - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GROUPS, - getRAModuleName(), groups.substring(0, groups.length() - 2))); - } - - return bbattributes; - } - - /** - * Read the User Information section of the SAM regripper plugin's output - * and collect user account information from the file. - * - * @param bufferedReader a buffered reader for the file which contains the - * user information - * @param users the set to add UserInfo objects representing the - * users found to - * - * @throws IOException - */ - private void readUsers(BufferedReader bufferedReader, Set> users) throws IOException { - String line = bufferedReader.readLine(); - //read until end of file or next section divider - String userName = ""; - String user_rid = ""; - while (line != null && !line.contains(SECTION_DIVIDER)) { - //when a user name field exists read the name and id number - if (line.contains(USERNAME_KEY)) { - String regx = USERNAME_KEY + "\\s*?:"; - String userNameAndIdString = line.replaceAll(regx, ""); - userName = userNameAndIdString.substring(0, userNameAndIdString.lastIndexOf('[')).trim(); - user_rid = userNameAndIdString.substring(userNameAndIdString.lastIndexOf('['), userNameAndIdString.lastIndexOf(']')); - } else if (line.contains(SID_KEY) && !userName.isEmpty()) { - Map.Entry entry = getSAMKeyValue(line); - - HashMap userInfo = new HashMap<>(); - userInfo.put(USERNAME_KEY, userName); - userInfo.put(RID_KEY, user_rid); - userInfo.put(entry.getKey(), entry.getValue()); - - //continue reading this users information until end of file or a blank line between users - line = bufferedReader.readLine(); - while (line != null && !line.isEmpty()) { - entry = getSAMKeyValue(line); - if (entry != null) { - userInfo.put(entry.getKey(), entry.getValue()); - } - line = bufferedReader.readLine(); - } - users.add(userInfo); - - userName = ""; - } - line = bufferedReader.readLine(); - } - } - - /** - * Create recently used artifacts from NTUSER regripper files - * - * @param regFileName name of the regripper output file - * - * @param regFile registry file the artifact is associated with - * - * @throws FileNotFound and IOException - */ - private void createRecentlyUsedArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException { - File regfile = new File(regFileName); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { - String line = reader.readLine(); - while (line != null) { - line = line.trim(); - - if (line.matches("^adoberdr v.*")) { - parseAdobeMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Adobe()); - } else if (line.matches("^mpmru v.*")) { - parseMediaPlayerMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mediaplayer()); - } else if (line.matches("^trustrecords v.*")) { - parseOfficeTrustRecords(regFile, reader, Bundle.Recently_Used_Artifacts_Office_Trustrecords()); - } else if (line.matches("^ArcHistory:")) { - parse7ZipMRU(regFile, reader, Bundle.Recently_Used_Artifacts_ArcHistory()); - } else if (line.matches("^applets v.*")) { - parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Applets()); - } else if (line.matches("^mmc v.*")) { - parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mmc()); - } else if (line.matches("^winrar v.*")) { - parseWinRARMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Winrar()); - } else if (line.matches("^officedocs2010 v.*")) { - parseOfficeDocs2010MRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Officedocs()); - } - line = reader.readLine(); - } - } - } - - /** - * Create artifacts from the System registry Hive - * - * @param regFileName name of the regripper output file - * - * @param regFile registry file the artifact is associated with - * - * @throws FileNotFound and IOException - */ - private void createSystemArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException { - File regfile = new File(regFileName); - try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { - String line = reader.readLine(); - while (line != null) { - line = line.trim(); - - if (line.matches("^bam v.*")) { - parseBamKey(regFile, reader, Bundle.Registry_System_Bam()); - } - line = reader.readLine(); - } - } - } - - /** - * Create artifacts from BAM Regripper Plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseBamKey(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - // Read thru first bam output to get to second bam output which is the same but delimited - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - } - line = reader.readLine(); - line = line.trim(); - while (!line.contains(SECTION_DIVIDER)) { - // Split the line into it parts based on delimiter of "|" - // 1570493613|BAM|||\Device\HarddiskVolume3\Program Files\TechSmith\Snagit 2018\Snagit32.exe (S-1-5-21-3042408413-2583535980-1301764466-1001) - String tokens[] = line.split("\\|"); - Long progRunDateTime = Long.valueOf(tokens[0]); - // Split on " (S-" as this signifies a User SID, if S- not used then may have issues becuase of (x86) in path is valid. - // We can add the S- back to the string that we split on since S- is a valid beginning of a User SID - String fileNameSid[] = tokens[4].split("\\s+\\(S-"); - String userSid = "S-" + fileNameSid[1].substring(0, fileNameSid[1].length() - 1); - String userName = userNameMap.get(userSid); - if (userName == null) { - userName = userSid; - } - String fileName = fileNameSid[0]; - if (fileName.startsWith("\\Device\\HarddiskVolume")) { - // Start at point past the 2nd slash - int fileNameStart = fileName.indexOf('\\', 16); - fileName = fileName.substring(fileNameStart, fileName.length()); - - } - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, getName(), userName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), progRunDateTime)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_PROG_RUN, regFile, attributes); - if (bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts from adobemru Regripper Plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseAdobeMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - SimpleDateFormat adobePluginDateFormat = new SimpleDateFormat("yyyyMMddHHmmssZ", US); - Long adobeUsedTime = Long.valueOf(0); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.matches("^Key name,file name,sDate,uFileSize,uPageCount")) { - line = reader.readLine(); - // Columns are - // Key name, file name, sDate, uFileSize, uPageCount - while (!line.contains(SECTION_DIVIDER)) { - // Split csv line, handles double quotes around individual file names - // since file names can contain commas - String tokens[] = line.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); - String fileName = tokens[1].substring(0, tokens[1].length() -1); - fileName = fileName.replace("\"", ""); - if (fileName.charAt(0) == '/') { - fileName = fileName.substring(1,fileName.length() - 1); - fileName = fileName.replaceFirst("/", ":/"); - } - // Check to see if more then 2 tokens, Date may not be populated, will default to 0 - if (tokens.length > 2) { - // Time in the format of 20200131104456-05'00' - try { - String fileUsedTime = tokens[2].replaceAll("'",""); - Date usedDate = adobePluginDateFormat.parse(fileUsedTime); - adobeUsedTime = usedDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for adobe file artifact.", tokens[2]), ex); //NON-NLS - } - } - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), adobeUsedTime)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - fileName = fileName.replace("\0", ""); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the Media Player MRU regripper (mpmru) records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseMediaPlayerMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.contains("LastWrite")) { - line = reader.readLine(); - // Columns are - // FileX -> - while (!line.contains(SECTION_DIVIDER) && !line.contains("RecentFileList has no values.")) { - // Split line on "> " which is the record delimiter between position and file - String tokens[] = line.split("> "); - String fileName = tokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(fileName, bba); - if (bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - } - line = reader.readLine(); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseGenericMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.contains("LastWrite")) { - line = reader.readLine(); - // Columns are - // FileX -> - while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Applets") - && !line.contains(("Recent File List"))) { - // Split line on "> " which is the record delimiter between position and file - String tokens[] = line.split("> "); - String fileName = tokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the WinRAR Regripper plugin output - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseWinRARMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - if (line.contains("LastWrite")) { - line = reader.readLine(); - // Columns are - // FileX -> - if (!line.isEmpty()) { - while (!line.contains(SECTION_DIVIDER)) { - // Split line on "> " which is the record delimiter between position and file - String tokens[] = line.split("> "); - String fileName = tokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - } - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the runmru ArcHistory (7Zip) regripper plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parse7ZipMRU(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - line = line.trim(); - if (!line.contains("PathHistory:")) { - while (!line.contains("PathHistory:") && !line.isEmpty()) { - // Columns are - // - String fileName = line; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if (bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the Office Documents 2010 records Regripper Plugin output - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseOfficeDocs2010MRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - List bbartifacts = new ArrayList<>(); - String line = reader.readLine(); - line = line.trim(); - // Reading to the SECTION DIVIDER to get next section of records to process. Dates appear to have - // multiple spaces in them that makes it harder to parse so next section will be easier to parse - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - } - line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - // record has the following format - // 1294283922|REG|||OfficeDocs2010 - F:\Windows_time_Rules_xp.doc - String tokens[] = line.split("\\|"); - Long docDate = Long.valueOf(tokens[0]); - String fileNameTokens[] = tokens[4].split(" - "); - String fileName = fileNameTokens[1]; - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = reader.readLine(); - line = line.trim(); - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create recently used artifacts to parse the Office trust records (trustrecords) Regipper plugin records - * - * @param regFile registry file the artifact is associated with - * - * @param reader buffered reader to parse adobemru records - * - * @param comment string that will populate attribute TSK_COMMENT - * - * @throws FileNotFound and IOException - */ - private void parseOfficeTrustRecords(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { - String userProfile = regFile.getParentPath(); - userProfile = userProfile.substring(0, userProfile.length() - 1); - List bbartifacts = new ArrayList<>(); - SimpleDateFormat pluginDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", US); - Long usedTime = Long.valueOf(0); - String line = reader.readLine(); - while (!line.contains(SECTION_DIVIDER)) { - line = reader.readLine(); - line = line.trim(); - usedTime = Long.valueOf(0); - if (!line.contains("**") && !line.contains("----------") && !line.contains("LastWrite") - && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("TrustRecords")) { - // Columns are - // Date : / - // Split line on " : " which is the record delimiter between position and file - String fileName = null; - String tokens[] = line.split(" : "); - fileName = tokens[1]; - fileName = fileName.replace("%USERPROFILE%", userProfile); - // Time in the format of Wed May 31 14:33:03 2017 Z - try { - String fileUsedTime = tokens[0].replaceAll(" Z",""); - Date usedDate = pluginDateFormat.parse(fileUsedTime); - usedTime = usedDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for TrustRecords artifact.", tokens[0]), ex); //NON-NLS - } - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); - attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), usedTime)); - attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); - BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); - if(bba != null) { - bbartifacts.add(bba); - bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); - if (bba != null) { - bbartifacts.add(bba); - } - } - line = line.trim(); - } - } - if (!bbartifacts.isEmpty()) { - postArtifacts(bbartifacts); - } - } - - /** - * Create associated artifacts using file name and path and the artifact it associates with - * - * @param filePathName file and path of object being associated with - * - * @param bba blackboard artifact to associate with - * - * @returnv BlackboardArtifact or a null value - */ - private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) { - org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager(); - String fileName = FilenameUtils.getName(filePathName); - String filePath = FilenameUtils.getPath(filePathName); - List sourceFiles; - try { - sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS - if (!sourceFiles.isEmpty()) { - for (AbstractFile sourceFile : sourceFiles) { - if (sourceFile.getParentPath().endsWith(filePath)) { - Collection bbattributes2 = new ArrayList<>(); - bbattributes2.addAll(Arrays.asList( - new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, this.getName(), - bba.getArtifactID()))); - - BlackboardArtifact associatedObjectBba = createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, sourceFile, bbattributes2); - if (associatedObjectBba != null) { - return associatedObjectBba; - } - } - } - } - } catch (TskCoreException ex) { - // only catching the error and displaying the message as the file may not exist on the - // system anymore - logger.log(Level.WARNING, String.format("Error finding actual file %s. file may not exist", filePathName)); //NON-NLS - } - - return null; - } - - /** - * Create a map of userids to usernames from the OS Accounts. - * - * @param dataSource - * - * @return A Map of userIDs and userNames - * - * @throws TskCoreException - */ - private Map makeUserNameMap(Content dataSource) throws TskCoreException { - Map userNameMap = new HashMap<>(); - - List accounts = blackboard.getArtifacts(TSK_OS_ACCOUNT.getTypeID(), dataSource.getId()); - - for (BlackboardArtifact account : accounts) { - BlackboardAttribute nameAttribute = getAttributeForArtifact(account, TSK_USER_NAME); - BlackboardAttribute idAttribute = getAttributeForArtifact(account, TSK_USER_ID); - - String userName = nameAttribute != null ? nameAttribute.getDisplayString() : ""; - String userID = idAttribute != null ? idAttribute.getDisplayString() : ""; - - if (!userID.isEmpty()) { - userNameMap.put(userID, userName); - } - } - - return userNameMap; - } - - /** - * Gets the attribute for the given type from the given artifact. - * - * @param artifact BlackboardArtifact to get the attribute from - * @param type The BlackboardAttribute Type to get - * - * @return BlackboardAttribute for given artifact and type - * - * @throws TskCoreException - */ - private BlackboardAttribute getAttributeForArtifact(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException { - return artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(type.getTypeID()))); - } - - /** - * Create the shellbag artifacts from the list of ShellBag objects. - * - * @param regFile The data source file - * @param shellbags List of shellbags from source file - * - * @throws TskCoreException - */ - void createShellBagArtifacts(AbstractFile regFile, List shellbags) throws TskCoreException { - List artifacts = new ArrayList<>(); - try{ - for (ShellBag bag : shellbags) { - Collection attributes = new ArrayList<>(); - BlackboardArtifact artifact = regFile.newArtifact(getShellBagArtifact().getTypeID()); - attributes.add(new BlackboardAttribute(TSK_PATH, getName(), bag.getResource())); - attributes.add(new BlackboardAttribute(getKeyAttribute(), getName(), bag.getKey())); - - long time; - time = bag.getLastWrite(); - if (time != 0) { - attributes.add(new BlackboardAttribute(getLastWriteAttribute(), getName(), time)); - } - - time = bag.getModified(); - if (time != 0) { - attributes.add(new BlackboardAttribute(TSK_DATETIME_MODIFIED, getName(), time)); - } - - time = bag.getCreated(); - if (time != 0) { - attributes.add(new BlackboardAttribute(TSK_DATETIME_CREATED, getName(), time)); - } - - time = bag.getAccessed(); - if (time != 0) { - attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), time)); - } - - artifact.addAttributes(attributes); - - artifacts.add(artifact); - } - } finally { - postArtifacts(artifacts); - } - } - - /** - * Returns the custom Shellbag artifact type or creates it if it does not - * currently exist. - * - * @return BlackboardArtifact.Type for shellbag artifacts - * - * @throws TskCoreException - */ - private BlackboardArtifact.Type getShellBagArtifact() throws TskCoreException { - if (shellBagArtifactType == null) { - shellBagArtifactType = tskCase.getArtifactType(SHELLBAG_ARTIFACT_NAME); - - if(shellBagArtifactType == null) { - try { - tskCase.addBlackboardArtifactType(SHELLBAG_ARTIFACT_NAME, Bundle.Shellbag_Artifact_Display_Name()); //NON-NLS - } catch (TskDataException ex) { - // Artifact already exists - logger.log(Level.INFO, String.format("%s may have already been defined for this case", SHELLBAG_ARTIFACT_NAME)); - } - - shellBagArtifactType = tskCase.getArtifactType(SHELLBAG_ARTIFACT_NAME); - } - } - - return shellBagArtifactType; - } - - /** - * Gets the custom BlackboardAttribute type. The attribute type is created - * if it does not currently exist. - * - * @return The BlackboardAttribute type - * - * @throws TskCoreException - */ - private BlackboardAttribute.Type getLastWriteAttribute() throws TskCoreException { - if (shellBagLastWriteAttributeType == null) { - try { - shellBagLastWriteAttributeType = tskCase.addArtifactAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE, - BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME, - Bundle.Shellbag_Last_Write_Attribute_Display_Name()); - } catch (TskDataException ex) { - // Attribute already exists get it from the case - shellBagLastWriteAttributeType = tskCase.getAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE); - } - } - return shellBagLastWriteAttributeType; - } - - /** - * Gets the custom BlackboardAttribute type. The attribute type is created - * if it does not currently exist. - * - * @return The BlackboardAttribute type - * - * @throws TskCoreException - */ - private BlackboardAttribute.Type getKeyAttribute() throws TskCoreException { - if (shellBagKeyAttributeType == null) { - try { - shellBagKeyAttributeType = tskCase.addArtifactAttributeType(SHELLBAG_ATTRIBUTE_KEY, - BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, - Bundle.Shellbag_Key_Attribute_Display_Name()); - } catch (TskDataException ex) { - // The attribute already exists get it from the case - shellBagKeyAttributeType = tskCase.getAttributeType(SHELLBAG_ATTRIBUTE_KEY); - } - } - return shellBagKeyAttributeType; - } - - /** - * Maps the user groups to the sid that are a part of them. - * - * @param bufferedReader - * - * @return A map if sid and the groups they map too - * - * @throws IOException - */ - Map> readGroups(BufferedReader bufferedReader) throws IOException { - Map> groupMap = new HashMap<>(); - - String line = bufferedReader.readLine(); - - int userCount = 0; - String groupName = null; - - while (line != null && !line.contains(SECTION_DIVIDER)) { - - if (line.contains("Group Name")) { - String value = line.replaceAll("Group Name\\s*?:", "").trim(); - groupName = (value.replaceAll("\\[\\d*?\\]", "")).trim(); - int startIndex = value.indexOf('['); - int endIndex = value.indexOf(']'); - - if (startIndex != -1 && endIndex != -1) { - String countStr = value.substring(startIndex + 1, endIndex); - userCount = Integer.parseInt(countStr); - } - } else if (line.matches("Users\\s*?:")) { - for (int i = 0; i < userCount; i++) { - line = bufferedReader.readLine(); - if (line != null) { - String sid = line.trim(); - List groupList = groupMap.get(sid); - if (groupList == null) { - groupList = new ArrayList<>(); - groupMap.put(sid, groupList); - } - groupList.add(groupName); - } - } - groupName = null; - } - line = bufferedReader.readLine(); - } - return groupMap; - } - - /** - * Gets the key value from user account strings of the format - * key:value or - * --> value - * - * @param line String to parse - * - * @return key value pair - */ - private Map.Entry getSAMKeyValue(String line) { - int index = line.indexOf(':'); - Map.Entry returnValue = null; - String key = null; - String value = null; - - if (index != -1) { - key = line.substring(0, index).trim(); - if (index + 1 < line.length()) { - value = line.substring(index + 1).trim(); - } else { - value = ""; - } - - } else if (line.contains("-->")) { - key = line.replace("-->", "").trim(); - value = "true"; - } - - if (key != null) { - returnValue = new AbstractMap.SimpleEntry<>(key, value); - } - - return returnValue; - } - - @Override - public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) { - this.dataSource = dataSource; - this.context = context; - - progressBar.progress(Bundle.Progress_Message_Analyze_Registry()); - analyzeRegistryFiles(); - - } - - /** - * Private wrapper class for Registry output files - */ - private class RegOutputFiles { - - public String autopsyPlugins = ""; - public String fullPlugins = ""; - } -} +/* + * + * Autopsy Forensic Browser + * + * Copyright 2012-2020 Basis Technology Corp. + * + * Copyright 2012 42six Solutions. + * Contact: aebadirad 42six com + * Project Contact/Architect: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.recentactivity; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.StringReader; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.logging.Level; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.apache.commons.io.FilenameUtils; +import org.openide.modules.InstalledFileLocator; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.ExecUtil; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; +import org.sleuthkit.autopsy.datamodel.ContentUtils; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator; +import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.recentactivity.UsbDeviceIdMapper.USBInfo; +import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; +import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import java.nio.file.Path; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.Set; +import java.util.HashSet; +import static java.util.Locale.US; +import static java.util.TimeZone.getTimeZone; +import org.openide.util.Lookup; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; +import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; +import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; +import org.sleuthkit.autopsy.recentactivity.ShellBagParser.ShellBag; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_ACCOUNT; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_MODIFIED; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_ID; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_USER_NAME; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException; +import org.sleuthkit.datamodel.Report; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskDataException; + +/** + * Extract windows registry data using regripper. Runs two versions of + * regripper. One is the generally available set of plug-ins and the second is a + * set that were customized for Autopsy to produce a more structured output of + * XML so that we can parse and turn into blackboard artifacts. + */ +@NbBundle.Messages({ + "RegRipperNotFound=Autopsy RegRipper executable not found.", + "RegRipperFullNotFound=Full version RegRipper executable not found.", + "Progress_Message_Analyze_Registry=Analyzing Registry Files", + "Shellbag_Artifact_Display_Name=Shell Bags", + "Shellbag_Key_Attribute_Display_Name=Key", + "Shellbag_Last_Write_Attribute_Display_Name=Last Write", + "Recently_Used_Artifacts_Office_Trustrecords=Stored in TrustRecords because Office security exception was granted", + "Recently_Used_Artifacts_ArcHistory=Recently opened by 7Zip", + "Recently_Used_Artifacts_Applets=Recently opened according to Applets registry key", + "Recently_Used_Artifacts_Mmc=Recently opened according to Windows Management Console MRU", + "Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU", + "Recently_Used_Artifacts_Officedocs=Recently opened according to Office MRU", + "Recently_Used_Artifacts_Adobe=Recently opened according to Adobe MRU", + "Recently_Used_Artifacts_Mediaplayer=Recently opened according to Media Player MRU", + "Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM)" +}) +class ExtractRegistry extends Extract { + + private static final String USERNAME_KEY = "Username"; //NON-NLS + private static final String SID_KEY = "SID"; //NON-NLS + private static final String RID_KEY = "RID"; //NON-NLS + private static final String ACCOUNT_CREATED_KEY = "Account Created"; //NON-NLS + private static final String LAST_LOGIN_KEY = "Last Login Date"; //NON-NLS + private static final String LOGIN_COUNT_KEY = "Login Count"; //NON-NLS + private static final String FULL_NAME_KEY = "Full Name"; //NON-NLS + private static final String USER_COMMENT_KEY = "User Comment"; //NON-NLS + private static final String ACCOUNT_TYPE_KEY = "Account Type"; //NON-NLS + private static final String NAME_KEY = "Name"; //NON-NLS + private static final String PWD_RESET_KEY = "Pwd Rest Date"; //NON-NLS + private static final String PWD_FAILE_KEY = "Pwd Fail Date"; //NON-NLS + private static final String INTERNET_NAME_KEY = "InternetName"; //NON-NLS + private static final String PWD_DOES_NOT_EXPIRE_KEY = "Password does not expire"; //NON-NLS + private static final String ACCOUNT_DISABLED_KEY = "Account Disabled"; //NON-NLS + private static final String PWD_NOT_REQUIRED_KEY = "Password not required"; //NON-NLS + private static final String NORMAL_ACCOUNT_KEY = "Normal user account"; //NON-NLS + private static final String HOME_DIRECTORY_REQUIRED_KEY = "Home directory required"; + private static final String TEMPORARY_DUPLICATE_ACCOUNT = "Temporary duplicate account"; + private static final String MNS_LOGON_ACCOUNT_KEY = "MNS logon user account"; + private static final String INTERDOMAIN_TRUST_ACCOUNT_KEY = "Interdomain trust account"; + private static final String WORKSTATION_TRUST_ACCOUNT = "Workstation trust account"; + private static final String SERVER_TRUST_ACCOUNT = "Server trust account"; + private static final String ACCOUNT_AUTO_LOCKED = "Account auto locked"; + private static final String PASSWORD_HINT = "Password Hint"; + + private static final String[] PASSWORD_SETTINGS_FLAGS = {PWD_DOES_NOT_EXPIRE_KEY, PWD_NOT_REQUIRED_KEY}; + private static final String[] ACCOUNT_SETTINGS_FLAGS = {ACCOUNT_AUTO_LOCKED, HOME_DIRECTORY_REQUIRED_KEY, ACCOUNT_DISABLED_KEY}; + private static final String[] ACCOUNT_TYPE_FLAGS = {NORMAL_ACCOUNT_KEY, SERVER_TRUST_ACCOUNT, WORKSTATION_TRUST_ACCOUNT, INTERDOMAIN_TRUST_ACCOUNT_KEY, MNS_LOGON_ACCOUNT_KEY, TEMPORARY_DUPLICATE_ACCOUNT}; + + final private static UsbDeviceIdMapper USB_MAPPER = new UsbDeviceIdMapper(); + final private static String RIP_EXE = "rip.exe"; + final private static String RIP_PL = "rip.pl"; + final private static String RIP_PL_INCLUDE_FLAG = "-I"; + final private static int MS_IN_SEC = 1000; + final private static String NEVER_DATE = "Never"; + final private static String SECTION_DIVIDER = "-------------------------"; + final private static Logger logger = Logger.getLogger(ExtractRegistry.class.getName()); + private final List rrCmd = new ArrayList<>(); + private final List rrFullCmd = new ArrayList<>(); + private final Path rrHome; // Path to the Autopsy version of RegRipper + private final Path rrFullHome; // Path to the full version of RegRipper + private Content dataSource; + private IngestJobContext context; + private Map userNameMap; + + private static final String SHELLBAG_ARTIFACT_NAME = "RA_SHELL_BAG"; //NON-NLS + private static final String SHELLBAG_ATTRIBUTE_LAST_WRITE = "RA_SHELL_BAG_LAST_WRITE"; //NON-NLS + private static final String SHELLBAG_ATTRIBUTE_KEY= "RA_SHELL_BAG_KEY"; //NON-NLS + + BlackboardArtifact.Type shellBagArtifactType = null; + BlackboardAttribute.Type shellBagKeyAttributeType = null; + BlackboardAttribute.Type shellBagLastWriteAttributeType = null; + + ExtractRegistry() throws IngestModuleException { + moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractRegistry.moduleName.text"); + + final File rrRoot = InstalledFileLocator.getDefault().locate("rr", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS + if (rrRoot == null) { + throw new IngestModuleException(Bundle.RegRipperNotFound()); + } + + final File rrFullRoot = InstalledFileLocator.getDefault().locate("rr-full", ExtractRegistry.class.getPackage().getName(), false); //NON-NLS + if (rrFullRoot == null) { + throw new IngestModuleException(Bundle.RegRipperFullNotFound()); + } + + String executableToRun = RIP_EXE; + if (!PlatformUtil.isWindowsOS()) { + executableToRun = RIP_PL; + } + rrHome = rrRoot.toPath(); + String rrPath = rrHome.resolve(executableToRun).toString(); + rrFullHome = rrFullRoot.toPath(); + + if (!(new File(rrPath).exists())) { + throw new IngestModuleException(Bundle.RegRipperNotFound()); + } + String rrFullPath = rrFullHome.resolve(executableToRun).toString(); + if (!(new File(rrFullPath).exists())) { + throw new IngestModuleException(Bundle.RegRipperFullNotFound()); + } + if (PlatformUtil.isWindowsOS()) { + rrCmd.add(rrPath); + rrFullCmd.add(rrFullPath); + } else { + String perl; + File usrBin = new File("/usr/bin/perl"); + File usrLocalBin = new File("/usr/local/bin/perl"); + if (usrBin.canExecute() && usrBin.exists() && !usrBin.isDirectory()) { + perl = "/usr/bin/perl"; + } else if (usrLocalBin.canExecute() && usrLocalBin.exists() && !usrLocalBin.isDirectory()) { + perl = "/usr/local/bin/perl"; + } else { + throw new IngestModuleException("perl not found in your system"); + } + rrCmd.add(perl); + rrCmd.add(RIP_PL_INCLUDE_FLAG); + rrCmd.add(rrHome.toString()); + rrCmd.add(rrPath); + rrFullCmd.add(perl); + rrFullCmd.add(RIP_PL_INCLUDE_FLAG); + rrFullCmd.add(rrFullHome.toString()); + rrFullCmd.add(rrFullPath); + } + } + + /** + * Search for the registry hives on the system. + */ + private List findRegistryFiles() { + List allRegistryFiles = new ArrayList<>(); + org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager(); + + // find the sam hives', process this first so we can map the user id's and sids for later use + try { + allRegistryFiles.addAll(fileManager.findFiles(dataSource, "sam", "/system32/config")); //NON-NLS + } catch (TskCoreException ex) { + String msg = NbBundle.getMessage(this.getClass(), + "ExtractRegistry.findRegFiles.errMsg.errReadingFile", "sam"); + logger.log(Level.WARNING, msg, ex); + this.addErrorMessage(this.getName() + ": " + msg); + } + + + // find the user-specific ntuser-dat files + try { + allRegistryFiles.addAll(fileManager.findFiles(dataSource, "ntuser.dat")); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error fetching 'ntuser.dat' file."); //NON-NLS + } + + // find the user-specific ntuser-dat files + try { + allRegistryFiles.addAll(fileManager.findFiles(dataSource, "usrclass.dat")); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Error finding 'usrclass.dat' files."), ex); //NON-NLS + } + + // find the system hives' + String[] regFileNames = new String[]{"system", "software", "security"}; //NON-NLS + for (String regFileName : regFileNames) { + try { + allRegistryFiles.addAll(fileManager.findFiles(dataSource, regFileName, "/system32/config")); //NON-NLS + } catch (TskCoreException ex) { + String msg = NbBundle.getMessage(this.getClass(), + "ExtractRegistry.findRegFiles.errMsg.errReadingFile", regFileName); + logger.log(Level.WARNING, msg, ex); + this.addErrorMessage(this.getName() + ": " + msg); + } + } + return allRegistryFiles; + } + + /** + * Identifies registry files in the database by mtimeItem, runs regripper on + * them, and parses the output. + */ + private void analyzeRegistryFiles() { + List allRegistryFiles = findRegistryFiles(); + + // open the log file + FileWriter logFile = null; + try { + logFile = new FileWriter(RAImageIngestModule.getRAOutputPath(currentCase, "reg") + File.separator + "regripper-info.txt"); //NON-NLS + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + + for (AbstractFile regFile : allRegistryFiles) { + String regFileName = regFile.getName(); + long regFileId = regFile.getId(); + String regFileNameLocal = RAImageIngestModule.getRATempPath(currentCase, "reg") + File.separator + regFileName; + String outputPathBase = RAImageIngestModule.getRAOutputPath(currentCase, "reg") + File.separator + regFileName + "-regripper-" + Long.toString(regFileId); //NON-NLS + File regFileNameLocalFile = new File(regFileNameLocal); + try { + ContentUtils.writeToFile(regFile, regFileNameLocalFile, context::dataSourceIngestIsCancelled); + } catch (ReadContentInputStreamException ex) { + logger.log(Level.WARNING, String.format("Error reading registry file '%s' (id=%d).", + regFile.getName(), regFileId), ex); //NON-NLS + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp", + this.getName(), regFileName)); + continue; + } catch (IOException ex) { + logger.log(Level.SEVERE, String.format("Error writing temp registry file '%s' for registry file '%s' (id=%d).", + regFileNameLocal, regFile.getName(), regFileId), ex); //NON-NLS + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.errMsg.errWritingTemp", + this.getName(), regFileName)); + continue; + } + + if (context.dataSourceIngestIsCancelled()) { + break; + } + + try { + if (logFile != null) { + logFile.write(Long.toString(regFileId) + "\t" + regFile.getUniquePath() + "\n"); + } + } catch (TskCoreException | IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + + logger.log(Level.INFO, "{0}- Now getting registry information from {1}", new Object[]{moduleName, regFileNameLocal}); //NON-NLS + RegOutputFiles regOutputFiles = ripRegistryFile(regFileNameLocal, outputPathBase); + if (context.dataSourceIngestIsCancelled()) { + break; + } + + // parse the autopsy-specific output + if (regOutputFiles.autopsyPlugins.isEmpty() == false && parseAutopsyPluginOutput(regOutputFiles.autopsyPlugins, regFile) == false) { + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", + this.getName(), regFileName)); + } + + // create a report for the full output + if (!regOutputFiles.fullPlugins.isEmpty()) { + //parse the full regripper output from SAM hive files + if (regFileNameLocal.toLowerCase().contains("sam") && parseSamPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", + this.getName(), regFileName)); + } else if (regFileNameLocal.toLowerCase().contains("ntuser") || regFileNameLocal.toLowerCase().contains("usrclass")) { + try { + List shellbags = ShellBagParser.parseShellbagOutput(regOutputFiles.fullPlugins); + createShellBagArtifacts(regFile, shellbags); + createRecentlyUsedArtifacts(regOutputFiles.fullPlugins, regFile); + } catch (IOException | TskCoreException ex) { + logger.log(Level.WARNING, String.format("Unable to get shell bags from file %s", regOutputFiles.fullPlugins), ex); + } + } else if (regFileNameLocal.toLowerCase().contains("system") && parseSystemPluginOutput(regOutputFiles.fullPlugins, regFile) == false) { + this.addErrorMessage( + NbBundle.getMessage(this.getClass(), "ExtractRegistry.analyzeRegFiles.failedParsingResults", + this.getName(), regFileName)); + } + try { + Report report = currentCase.addReport(regOutputFiles.fullPlugins, + NbBundle.getMessage(this.getClass(), "ExtractRegistry.parentModuleName.noSpace"), + "RegRipper " + regFile.getUniquePath(), regFile); //NON-NLS + + // Index the report content so that it will be available for keyword search. + KeywordSearchService searchService = Lookup.getDefault().lookup(KeywordSearchService.class); + if (null == searchService) { + logger.log(Level.WARNING, "Keyword search service not found. Report will not be indexed"); + } else { + searchService.index(report); + report.close(); + } + } catch (TskCoreException e) { + this.addErrorMessage("Error adding regripper output as Autopsy report: " + e.getLocalizedMessage()); //NON-NLS + } + } + // delete the hive + regFileNameLocalFile.delete(); + } + + try { + if (logFile != null) { + logFile.close(); + } + } catch (IOException ex) { + logger.log(Level.SEVERE, null, ex); + } + } + + /** + * Execute regripper on the given registry. + * + * @param regFilePath Path to local copy of registry + * @param outFilePathBase Path to location to save output file to. Base + * mtimeItem that will be extended on + */ + private RegOutputFiles ripRegistryFile(String regFilePath, String outFilePathBase) { + String autopsyType = ""; // Type argument for rr for autopsy-specific modules + String fullType; // Type argument for rr for full set of modules + + RegOutputFiles regOutputFiles = new RegOutputFiles(); + + if (regFilePath.toLowerCase().contains("system")) { //NON-NLS + autopsyType = "autopsysystem"; //NON-NLS + fullType = "system"; //NON-NLS + } else if (regFilePath.toLowerCase().contains("software")) { //NON-NLS + autopsyType = "autopsysoftware"; //NON-NLS + fullType = "software"; //NON-NLS + } else if (regFilePath.toLowerCase().contains("ntuser")) { //NON-NLS + autopsyType = "autopsyntuser"; //NON-NLS + fullType = "ntuser"; //NON-NLS + } else if (regFilePath.toLowerCase().contains("sam")) { //NON-NLS + //fullType sam output files are parsed for user information + fullType = "sam"; //NON-NLS + } else if (regFilePath.toLowerCase().contains("security")) { //NON-NLS + fullType = "security"; //NON-NLS + }else if (regFilePath.toLowerCase().contains("usrclass")) { //NON-NLS + fullType = "usrclass"; //NON-NLS + } else { + return regOutputFiles; + } + + // run the autopsy-specific set of modules + if (!autopsyType.isEmpty()) { + regOutputFiles.autopsyPlugins = outFilePathBase + "-autopsy.txt"; //NON-NLS + String errFilePath = outFilePathBase + "-autopsy.err.txt"; //NON-NLS + logger.log(Level.INFO, "Writing RegRipper results to: {0}", regOutputFiles.autopsyPlugins); //NON-NLS + executeRegRipper(rrCmd, rrHome, regFilePath, autopsyType, regOutputFiles.autopsyPlugins, errFilePath); + } + if (context.dataSourceIngestIsCancelled()) { + return regOutputFiles; + } + + // run the full set of rr modules + if (!fullType.isEmpty()) { + regOutputFiles.fullPlugins = outFilePathBase + "-full.txt"; //NON-NLS + String errFilePath = outFilePathBase + "-full.err.txt"; //NON-NLS + logger.log(Level.INFO, "Writing Full RegRipper results to: {0}", regOutputFiles.fullPlugins); //NON-NLS + executeRegRipper(rrFullCmd, rrFullHome, regFilePath, fullType, regOutputFiles.fullPlugins, errFilePath); + try { + scanErrorLogs(errFilePath); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); + } + } + return regOutputFiles; + } + + private void scanErrorLogs(String errFilePath) throws IOException { + File regfile = new File(errFilePath); + try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + String line = reader.readLine(); + while (line != null) { + line = line.trim(); + if (line.toLowerCase().contains("error") || line.toLowerCase().contains("@inc")) { + logger.log(Level.WARNING, "Regripper file {0} contains errors from run", errFilePath); //NON-NLS + + } + line = reader.readLine(); + } + } + } + + private void executeRegRipper(List regRipperPath, Path regRipperHomeDir, String hiveFilePath, String hiveFileType, String outputFile, String errFile) { + try { + List commandLine = new ArrayList<>(); + for (String cmd : regRipperPath) { + commandLine.add(cmd); + } + commandLine.add("-r"); //NON-NLS + commandLine.add(hiveFilePath); + commandLine.add("-f"); //NON-NLS + commandLine.add(hiveFileType); + + ProcessBuilder processBuilder = new ProcessBuilder(commandLine); + processBuilder.directory(regRipperHomeDir.toFile()); // RegRipper 2.8 has to be run from its own directory + processBuilder.redirectOutput(new File(outputFile)); + processBuilder.redirectError(new File(errFile)); + ExecUtil.execute(processBuilder, new DataSourceIngestModuleProcessTerminator(context)); + } catch (IOException ex) { + logger.log(Level.SEVERE, "Unable to run RegRipper", ex); //NON-NLS + this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); + } + } + + // @@@ VERIFY that we are doing the right thing when we parse multiple NTUSER.DAT + /** + * + * @param regFilePath Path to the output file produced by RegRipper. + * @param regFile File object for registry that we are parsing (to make + * blackboard artifacts with) + * + * @return + */ + private boolean parseAutopsyPluginOutput(String regFilePath, AbstractFile regFile) { + FileInputStream fstream = null; + List newArtifacts = new ArrayList<>(); + try { + // Read the file in and create a Document and elements + File regfile = new File(regFilePath); + fstream = new FileInputStream(regfile); + String regString = new Scanner(fstream, "UTF-8").useDelimiter("\\Z").next(); //NON-NLS + String startdoc = ""; //NON-NLS + String result = regString.replaceAll("----------------------------------------", ""); + result = result.replaceAll("\\n", ""); //NON-NLS + result = result.replaceAll("\\r", ""); //NON-NLS + result = result.replaceAll("'", "'"); //NON-NLS + result = result.replaceAll("&", "&"); //NON-NLS + result = result.replace('\0', ' '); // NON-NLS + String enddoc = ""; //NON-NLS + String stringdoc = startdoc + result + enddoc; + DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = builder.parse(new InputSource(new StringReader(stringdoc))); + + // cycle through the elements in the doc + Element oroot = doc.getDocumentElement(); + NodeList children = oroot.getChildNodes(); + int len = children.getLength(); + for (int i = 0; i < len; i++) { + + if (context.dataSourceIngestIsCancelled()) { + return false; + } + + Element tempnode = (Element) children.item(i); + + String dataType = tempnode.getNodeName(); + NodeList timenodes = tempnode.getElementsByTagName("mtime"); //NON-NLS + Long mtime = null; + if (timenodes.getLength() > 0) { + Element timenode = (Element) timenodes.item(0); + String etime = timenode.getTextContent(); + //sometimes etime will be an empty string and therefore can not be parsed into a date + if (etime != null && !etime.isEmpty()) { + try { + mtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(etime).getTime(); + String Tempdate = mtime.toString(); + mtime = Long.valueOf(Tempdate) / MS_IN_SEC; + } catch (ParseException ex) { + logger.log(Level.WARNING, "Failed to parse epoch time when parsing the registry.", ex); //NON-NLS + } + } + } + + NodeList artroots = tempnode.getElementsByTagName("artifacts"); //NON-NLS + if (artroots.getLength() == 0) { + // If there isn't an artifact node, skip this entry + continue; + } + + Element artroot = (Element) artroots.item(0); + NodeList myartlist = artroot.getChildNodes(); + String parentModuleName = RecentActivityExtracterModuleFactory.getModuleName(); + + // If all artifact nodes should really go under one Blackboard artifact, need to process it differently + switch (dataType) { + case "WinVersion": //NON-NLS + String version = ""; + String systemRoot = ""; + String productId = ""; + String regOwner = ""; + String regOrg = ""; + Long installtime = null; + for (int j = 0; j < myartlist.getLength(); j++) { + Node artchild = myartlist.item(j); + // If it has attributes, then it is an Element (based off API) + if (artchild.hasAttributes()) { + Element artnode = (Element) artchild; + + String value = artnode.getTextContent(); + if (value != null) { + value = value.trim(); + } + String name = artnode.getAttribute("name"); //NON-NLS + if (name == null) { + continue; + } + switch (name) { + case "ProductName": // NON-NLS + version = value; + break; + case "CSDVersion": // NON-NLS + // This is dependant on the fact that ProductName shows up first in the module output + version = version + " " + value; + break; + case "SystemRoot": //NON-NLS + systemRoot = value; + break; + case "ProductId": //NON-NLS + productId = value; + break; + case "RegisteredOwner": //NON-NLS + regOwner = value; + break; + case "RegisteredOrganization": //NON-NLS + regOrg = value; + break; + case "InstallDate": //NON-NLS + if (value != null && !value.isEmpty()) { + try { + installtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(value).getTime(); + String Tempdate = installtime.toString(); + installtime = Long.valueOf(Tempdate) / MS_IN_SEC; + } catch (ParseException e) { + logger.log(Level.WARNING, "RegRipper::Conversion on DateTime -> ", e); //NON-NLS + } + } + break; + default: + break; + } + } + } + try { + Collection bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, version)); + if (installtime != null) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, installtime)); + } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, parentModuleName, systemRoot)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PRODUCT_ID, parentModuleName, productId)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_OWNER, parentModuleName, regOwner)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ORGANIZATION, parentModuleName, regOrg)); + + // Check if there is already an OS_INFO artifact for this file, and add to that if possible. + ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); + if (results.isEmpty()) { + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } else { + results.get(0).addAttributes(bbattributes); + } + + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard."); //NON-NLS + } + break; + case "Profiler": // NON-NLS + String os = ""; + String procArch = ""; + String tempDir = ""; + for (int j = 0; j < myartlist.getLength(); j++) { + Node artchild = myartlist.item(j); + // If it has attributes, then it is an Element (based off API) + if (artchild.hasAttributes()) { + Element artnode = (Element) artchild; + + String value = artnode.getTextContent().trim(); + String name = artnode.getAttribute("name"); //NON-NLS + switch (name) { + case "OS": // NON-NLS + os = value; + break; + case "PROCESSOR_ARCHITECTURE": // NON-NLS + procArch = value; + break; + case "PROCESSOR_IDENTIFIER": //NON-NLS + break; + case "TEMP": //NON-NLS + tempDir = value; + break; + default: + break; + } + } + } + try { + Collection bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VERSION, parentModuleName, os)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE, parentModuleName, procArch)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_TEMP_DIR, parentModuleName, tempDir)); + + // Check if there is already an OS_INFO artifact for this file and add to that if possible + ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); + if (results.isEmpty()) { + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } else { + results.get(0).addAttributes(bbattributes); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS + } + break; + case "CompName": // NON-NLS + String compName = ""; + String domain = ""; + for (int j = 0; j < myartlist.getLength(); j++) { + Node artchild = myartlist.item(j); + // If it has attributes, then it is an Element (based off API) + if (artchild.hasAttributes()) { + Element artnode = (Element) artchild; + + String value = artnode.getTextContent().trim(); + String name = artnode.getAttribute("name"); //NON-NLS + + if (name.equals("ComputerName")) { // NON-NLS + compName = value; + } else if (name.equals("Domain")) { // NON-NLS + domain = value; + } + } + } + try { + Collection bbattributes = new ArrayList<>(); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, compName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, parentModuleName, domain)); + + // Check if there is already an OS_INFO artifact for this file and add to that if possible + ArrayList results = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_INFO, regFile.getId()); + if (results.isEmpty()) { + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } else { + results.get(0).addAttributes(bbattributes); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding os info artifact to blackboard.", ex); //NON-NLS + } + break; + default: + for (int j = 0; j < myartlist.getLength(); j++) { + Node artchild = myartlist.item(j); + // If it has attributes, then it is an Element (based off API) + if (artchild.hasAttributes()) { + Element artnode = (Element) artchild; + + String value = artnode.getTextContent().trim(); + Collection bbattributes = new ArrayList<>(); + + switch (dataType) { + case "recentdocs": //NON-NLS + // BlackboardArtifact bbart = tskCase.getContentById(orgId).newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT); + // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LAST_ACCESSED.getTypeID(), "RecentActivity", dataType, mtime)); + // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), "RecentActivity", dataType, mtimeItem)); + // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), "RecentActivity", dataType, value)); + // bbart.addAttributes(bbattributes); + // @@@ BC: Why are we ignoring this... + break; + case "usb": //NON-NLS + try { + Long usbMtime = Long.parseLong(artnode.getAttribute("mtime")); //NON-NLS + usbMtime = Long.valueOf(usbMtime.toString()); + + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_DEVICE_ATTACHED); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, usbMtime)); + String dev = artnode.getAttribute("dev"); //NON-NLS + String make = ""; + String model = dev; + if (dev.toLowerCase().contains("vid")) { //NON-NLS + USBInfo info = USB_MAPPER.parseAndLookup(dev); + if (info.getVendor() != null) { + make = info.getVendor(); + } + if (info.getProduct() != null) { + model = info.getProduct(); + } + } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, parentModuleName, make)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, parentModuleName, model)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, value)); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding device attached artifact to blackboard.", ex); //NON-NLS + } + break; + case "uninstall": //NON-NLS + Long itemMtime = null; + try { + String mTimeAttr = artnode.getAttribute("mtime"); + if (mTimeAttr != null && !mTimeAttr.isEmpty()) { + itemMtime = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy").parse(mTimeAttr).getTime(); //NON-NLS + itemMtime /= MS_IN_SEC; + } + } catch (ParseException ex) { + logger.log(Level.SEVERE, "Failed to parse epoch time for installed program artifact.", ex); //NON-NLS + } + + try { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, value)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, itemMtime)); + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_INSTALLED_PROG); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding installed program artifact to blackboard.", ex); //NON-NLS + } + break; + case "office": //NON-NLS + String officeName = artnode.getAttribute("name"); //NON-NLS + + try { + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_RECENT_OBJECT); + // @@@ BC: Consider removing this after some more testing. It looks like an Mtime associated with the root key and not the individual item + if (mtime != null) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, parentModuleName, mtime)); + } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, parentModuleName, officeName)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_VALUE, parentModuleName, value)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, parentModuleName, artnode.getNodeName())); + bbart.addAttributes(bbattributes); + + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding recent object artifact to blackboard.", ex); //NON-NLS + } + break; + + case "ProcessorArchitecture": //NON-NLS + // Architecture is now included under Profiler + //try { + // String processorArchitecture = value; + // if (processorArchitecture.equals("AMD64")) + // processorArchitecture = "x86-64"; + + // BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_INFO); + // bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROCESSOR_ARCHITECTURE.getTypeID(), parentModuleName, processorArchitecture)); + // bbart.addAttributes(bbattributes); + //} catch (TskCoreException ex) { + // logger.log(Level.SEVERE, "Error adding os info artifact to blackboard."); //NON-NLS + //} + break; + + case "ProfileList": //NON-NLS + try { + String homeDir = value; + String sid = artnode.getAttribute("sid"); //NON-NLS + String username = artnode.getAttribute("username"); //NON-NLS + BlackboardArtifact bbart = null; + try { + //check if any of the existing artifacts match this username + ArrayList existingArtifacts = currentCase.getSleuthkitCase().getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT); + for (BlackboardArtifact artifact : existingArtifacts) { + if (artifact.getDataSource().getId() == regFile.getDataSourceObjectId()) { + BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID)); + if (attribute != null && attribute.getValueString().equals(sid)) { + bbart = artifact; + break; + } + } + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting existing os account artifact", ex); + } + if (bbart == null) { + //create new artifact + bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + parentModuleName, username)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, + parentModuleName, sid)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, + parentModuleName, homeDir)); + } else { + //add attributes to existing artifact + BlackboardAttribute bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_NAME)); + + if (bbattr == null) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + parentModuleName, username)); + } + bbattr = bbart.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_PATH)); + if (bbattr == null) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PATH, + parentModuleName, homeDir)); + } + } + bbart.addAttributes(bbattributes); + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding account artifact to blackboard.", ex); //NON-NLS + } + break; + + case "NtuserNetwork": // NON-NLS + try { + String localPath = artnode.getAttribute("localPath"); //NON-NLS + String remoteName = value; + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_REMOTE_DRIVE); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_LOCAL_PATH, + parentModuleName, localPath)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_REMOTE_PATH, + parentModuleName, remoteName)); + bbart.addAttributes(bbattributes); + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding network artifact to blackboard.", ex); //NON-NLS + } + break; + case "SSID": // NON-NLS + String adapter = artnode.getAttribute("adapter"); //NON-NLS + try { + Long lastWriteTime = Long.parseLong(artnode.getAttribute("writeTime")); //NON-NLS + lastWriteTime = Long.valueOf(lastWriteTime.toString()); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SSID, parentModuleName, value)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, parentModuleName, lastWriteTime)); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_ID, parentModuleName, adapter)); + BlackboardArtifact bbart = regFile.newArtifact(ARTIFACT_TYPE.TSK_WIFI_NETWORK); + bbart.addAttributes(bbattributes); + newArtifacts.add(bbart); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error adding SSID artifact to blackboard.", ex); //NON-NLS + } + break; + case "shellfolders": // NON-NLS + // The User Shell Folders subkey stores the paths to Windows Explorer folders for the current user of the computer + // (https://technet.microsoft.com/en-us/library/Cc962613.aspx). + // No useful information. Skip. + break; + + default: + logger.log(Level.SEVERE, "Unrecognized node name: {0}", dataType); //NON-NLS + break; + } + } + } + break; + } + } // for + return true; + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, String.format("Error finding the registry file: %s", regFilePath), ex); //NON-NLS + } catch (SAXException ex) { + logger.log(Level.WARNING, String.format("Error parsing the registry XML: %s", regFilePath), ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, String.format("Error building the document parser: %s", regFilePath), ex); //NON-NLS + } catch (ParserConfigurationException ex) { + logger.log(Level.WARNING, String.format("Error configuring the registry parser: %s", regFilePath), ex); //NON-NLS + } finally { + try { + if (fstream != null) { + fstream.close(); + } + } catch (IOException ex) { + } + + postArtifacts(newArtifacts); + } + return false; + } + + + private boolean parseSystemPluginOutput(String regfilePath, AbstractFile regAbstractFile) { + File regfile = new File(regfilePath); + try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + String line = reader.readLine(); + while (line != null) { + line = line.trim(); + + if (line.matches("^bam v.*")) { + parseBamKey(regAbstractFile, reader, Bundle.Registry_System_Bam()); + } else if (line.matches("^bthport v..*")) { + parseBlueToothDevices(regAbstractFile, reader) ; + } + line = reader.readLine(); + } + return true; + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Error reading the system hive: {0}", ex); //NON-NLS + } + + return false; + + } + + /** + * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + // date format for plugin Tue Jun 23 10:27:54 2020 Z + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); + Long bthLastSeen = Long.valueOf(0); + Long bthLastConnected = Long.valueOf(0); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("Device Unique ID")) { + // Columns are seperated by colons : + // Data : Values + // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Radio Support not found")) { + // Split line on "> " which is the record delimiter between position and file + String deviceTokens[] = line.split(": "); + String deviceUniqueId = deviceTokens[1]; + line = reader.readLine(); + // Default device name to unknown as a device name may not exist. + String deviceName = "Unknown"; + if (line.contains("Name")) { + String nameTokens[] = line.split(": "); + deviceName = nameTokens[1]; + line = reader.readLine(); + } + String lastSeenTokens[] = line.split(": "); + String lastSeen = lastSeenTokens[1].replace(" Z", "");; + line = reader.readLine(); + String lastConnectedTokens[] = line.split(": "); + String lastConnected = lastConnectedTokens[1].replace(" Z", "");; + try { + Date usedSeenDate = dateFormat.parse(lastSeen); + bthLastSeen = usedSeenDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", lastSeen), ex); //NON-NLS + } + try { + Date usedConnectedDate = dateFormat.parse(lastConnected); + bthLastConnected = usedConnectedDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last connected attribute.", lastSeen), ex); //NON-NLS + } + + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_DEVICE_ID, getName(), deviceUniqueId)); + attributes.add(new BlackboardAttribute(TSK_NAME, getName(), deviceName)); + attributes.add(new BlackboardAttribute(TSK_DATETIME, getName(), bthLastSeen)); + attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), bthLastConnected)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + } + // Read blank line between records then next read line is start of next block + line = reader.readLine(); + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Parse the output of the SAM regripper plugin to get additional Account + * information + * + * @param regFilePath the path to the registry file being parsed + * @param regAbstractFile the file to associate newly created artifacts with + * + * @return true if successful, false if parsing failed at some point + */ + private boolean parseSamPluginOutput(String regFilePath, AbstractFile regAbstractFile) { + File regfile = new File(regFilePath); + List newArtifacts = new ArrayList<>(); + try (BufferedReader bufferedReader = new BufferedReader(new FileReader(regfile))) { + // Read the file in and create a Document and elements + String userInfoSection = "User Information"; + String previousLine = null; + String line = bufferedReader.readLine(); + Set> userSet = new HashSet<>(); + Map> groupMap = null; + while (line != null) { + if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains(userInfoSection)) { + readUsers(bufferedReader, userSet); + } + + if (line.contains(SECTION_DIVIDER) && previousLine != null && previousLine.contains("Group Membership Information")) { + groupMap = readGroups(bufferedReader); + } + + previousLine = line; + line = bufferedReader.readLine(); + } + Map> userInfoMap = new HashMap<>(); + //load all the user info which was read into a map + for (Map userInfo : userSet) { + userInfoMap.put(userInfo.get(SID_KEY), userInfo); + } + //get all existing OS account artifacts + List existingOsAccounts = tskCase.getBlackboardArtifacts(ARTIFACT_TYPE.TSK_OS_ACCOUNT); + for (BlackboardArtifact osAccount : existingOsAccounts) { + //if the OS Account artifact was from the same data source check the user id + if (osAccount.getDataSource().getId() == regAbstractFile.getDataSourceObjectId()) { + BlackboardAttribute existingUserId = osAccount.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_USER_ID)); + if (existingUserId != null) { + String userID = existingUserId.getValueString().trim(); + Map userInfo = userInfoMap.remove(userID); + //if the existing user id matches a user id which we parsed information for check if that information exists and if it doesn't add it + if (userInfo != null) { + osAccount.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userID), true, regAbstractFile)); + } + } + } + } + + //add remaining userinfos as accounts; + for (Map userInfo : userInfoMap.values()) { + BlackboardArtifact bbart = regAbstractFile.newArtifact(ARTIFACT_TYPE.TSK_OS_ACCOUNT); + bbart.addAttributes(getAttributesForAccount(userInfo, groupMap.get(userInfo.get(SID_KEY)), false, regAbstractFile)); + // index the artifact for keyword search + newArtifacts.add(bbart); + } + // Get a mapping of user sids to user names and save globally so it can be used for other areas + // of the registry, ie: BAM key + try { + userNameMap = makeUserNameMap(dataSource); + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Unable to create OS Account user name map", ex); + // This is not the end of the world we will just continue without + // user names + userNameMap = new HashMap<>(); + } + return true; + } catch (FileNotFoundException ex) { + logger.log(Level.WARNING, "Error finding the registry file.", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Error building the document parser: {0}", ex); //NON-NLS + } catch (ParseException ex) { + logger.log(Level.WARNING, "Error parsing the the date from the registry file", ex); //NON-NLS + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error updating TSK_OS_ACCOUNT artifacts to include newly parsed data.", ex); //NON-NLS + } finally { + postArtifacts(newArtifacts); + } + return false; + } + + /** + * Creates the attribute list for the given user information and group list. + * + * @param userInfo Map of key\value pairs of user information + * @param groupList List of the groups that user belongs + * @param existingUser + * + * @return List + * + * @throws ParseException + */ + Collection getAttributesForAccount(Map userInfo, List groupList, boolean existingUser, AbstractFile regAbstractFile) throws ParseException { + Collection bbattributes = new ArrayList<>(); + + SimpleDateFormat regRipperTimeFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy 'Z'"); + regRipperTimeFormat.setTimeZone(getTimeZone("GMT")); + + if (!existingUser) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_ID, + getRAModuleName(), userInfo.get(SID_KEY))); + + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, + this.moduleName, userInfo.get(USERNAME_KEY))); + } + + String value = userInfo.get(ACCOUNT_CREATED_KEY); + if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, + getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); + } + + value = userInfo.get(LAST_LOGIN_KEY); + if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED, + getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); + } + + value = userInfo.get(LOGIN_COUNT_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COUNT, + getRAModuleName(), Integer.parseInt(value))); + } + + value = userInfo.get(ACCOUNT_TYPE_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE, + getRAModuleName(), value)); + } + + value = userInfo.get(USER_COMMENT_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DESCRIPTION, + getRAModuleName(), value)); + } + + value = userInfo.get(NAME_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, + getRAModuleName(), value)); + } + + value = userInfo.get(INTERNET_NAME_KEY); + if (value != null && !value.isEmpty()) { + try { + // Create an account for this email, if it doesn't already exist. + Case.getCurrentCaseThrows() + .getSleuthkitCase() + .getCommunicationsManager() + .createAccountFileInstance(Account.Type.EMAIL, + value, getRAModuleName(), regAbstractFile); + } catch (NoCurrentCaseException | TskCoreException ex) { + logger.log(Level.SEVERE, + String.format("Error adding email account with value " + + "%s, to the case database for file %s [objId=%d]", + value, regAbstractFile.getName(), regAbstractFile.getId()), ex); + } + + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_EMAIL, + getRAModuleName(), value)); + } + + value = userInfo.get(FULL_NAME_KEY); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DISPLAY_NAME, + getRAModuleName(), value)); + } + + value = userInfo.get(PWD_RESET_KEY); + if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_RESET, + getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); + } + + value = userInfo.get(PASSWORD_HINT); + if (value != null && !value.isEmpty()) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_HINT, + getRAModuleName(), value)); + } + + value = userInfo.get(PWD_FAILE_KEY); + if (value != null && !value.isEmpty() && !value.equals(NEVER_DATE)) { + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_PASSWORD_FAIL, + getRAModuleName(), regRipperTimeFormat.parse(value).getTime() / MS_IN_SEC)); + } + + String settingString = ""; + for (String setting : PASSWORD_SETTINGS_FLAGS) { + if (userInfo.containsKey(setting)) { + settingString += setting + ", "; + } + } + + if (!settingString.isEmpty()) { + settingString = settingString.substring(0, settingString.length() - 2); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PASSWORD_SETTINGS, + getRAModuleName(), settingString)); + } + + settingString = ""; + for (String setting : ACCOUNT_SETTINGS_FLAGS) { + if (userInfo.containsKey(setting)) { + settingString += setting + ", "; + } + } + + if (!settingString.isEmpty()) { + settingString = settingString.substring(0, settingString.length() - 2); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_ACCOUNT_SETTINGS, + getRAModuleName(), settingString)); + } + + settingString = ""; + for (String setting : ACCOUNT_TYPE_FLAGS) { + if (userInfo.containsKey(setting)) { + settingString += setting + ", "; + } + } + + if (!settingString.isEmpty()) { + settingString = settingString.substring(0, settingString.length() - 2); + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_FLAG, + getRAModuleName(), settingString)); + } + + if (groupList != null && groupList.isEmpty()) { + String groups = ""; + for (String group : groupList) { + groups += group + ", "; + } + + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GROUPS, + getRAModuleName(), groups.substring(0, groups.length() - 2))); + } + + return bbattributes; + } + + /** + * Read the User Information section of the SAM regripper plugin's output + * and collect user account information from the file. + * + * @param bufferedReader a buffered reader for the file which contains the + * user information + * @param users the set to add UserInfo objects representing the + * users found to + * + * @throws IOException + */ + private void readUsers(BufferedReader bufferedReader, Set> users) throws IOException { + String line = bufferedReader.readLine(); + //read until end of file or next section divider + String userName = ""; + String user_rid = ""; + while (line != null && !line.contains(SECTION_DIVIDER)) { + //when a user name field exists read the name and id number + if (line.contains(USERNAME_KEY)) { + String regx = USERNAME_KEY + "\\s*?:"; + String userNameAndIdString = line.replaceAll(regx, ""); + userName = userNameAndIdString.substring(0, userNameAndIdString.lastIndexOf('[')).trim(); + user_rid = userNameAndIdString.substring(userNameAndIdString.lastIndexOf('['), userNameAndIdString.lastIndexOf(']')); + } else if (line.contains(SID_KEY) && !userName.isEmpty()) { + Map.Entry entry = getSAMKeyValue(line); + + HashMap userInfo = new HashMap<>(); + userInfo.put(USERNAME_KEY, userName); + userInfo.put(RID_KEY, user_rid); + userInfo.put(entry.getKey(), entry.getValue()); + + //continue reading this users information until end of file or a blank line between users + line = bufferedReader.readLine(); + while (line != null && !line.isEmpty()) { + entry = getSAMKeyValue(line); + if (entry != null) { + userInfo.put(entry.getKey(), entry.getValue()); + } + line = bufferedReader.readLine(); + } + users.add(userInfo); + + userName = ""; + } + line = bufferedReader.readLine(); + } + } + + /** + * Create recently used artifacts from NTUSER regripper files + * + * @param regFileName name of the regripper output file + * + * @param regFile registry file the artifact is associated with + * + * @throws FileNotFound and IOException + */ + private void createRecentlyUsedArtifacts(String regFileName, AbstractFile regFile) throws FileNotFoundException, IOException { + File regfile = new File(regFileName); + try (BufferedReader reader = new BufferedReader(new FileReader(regfile))) { + String line = reader.readLine(); + while (line != null) { + line = line.trim(); + + if (line.matches("^adoberdr v.*")) { + parseAdobeMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Adobe()); + } else if (line.matches("^mpmru v.*")) { + parseMediaPlayerMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mediaplayer()); + } else if (line.matches("^trustrecords v.*")) { + parseOfficeTrustRecords(regFile, reader, Bundle.Recently_Used_Artifacts_Office_Trustrecords()); + } else if (line.matches("^ArcHistory:")) { + parse7ZipMRU(regFile, reader, Bundle.Recently_Used_Artifacts_ArcHistory()); + } else if (line.matches("^applets v.*")) { + parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Applets()); + } else if (line.matches("^mmc v.*")) { + parseGenericMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Mmc()); + } else if (line.matches("^winrar v.*")) { + parseWinRARMRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Winrar()); + } else if (line.matches("^officedocs2010 v.*")) { + parseOfficeDocs2010MRUList(regFile, reader, Bundle.Recently_Used_Artifacts_Officedocs()); + } + line = reader.readLine(); + } + } + } + + /** + * Create artifacts from BAM Regripper Plugin records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseBamKey(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + // Read thru first bam output to get to second bam output which is the same but delimited + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + } + line = reader.readLine(); + line = line.trim(); + while (!line.contains(SECTION_DIVIDER)) { + // Split the line into it parts based on delimiter of "|" + // 1570493613|BAM|||\Device\HarddiskVolume3\Program Files\TechSmith\Snagit 2018\Snagit32.exe (S-1-5-21-3042408413-2583535980-1301764466-1001) + String tokens[] = line.split("\\|"); + Long progRunDateTime = Long.valueOf(tokens[0]); + // Split on " (S-" as this signifies a User SID, if S- not used then may have issues becuase of (x86) in path is valid. + // We can add the S- back to the string that we split on since S- is a valid beginning of a User SID + String fileNameSid[] = tokens[4].split("\\s+\\(S-"); + String userSid = "S-" + fileNameSid[1].substring(0, fileNameSid[1].length() - 1); + String userName = userNameMap.get(userSid); + if (userName == null) { + userName = userSid; + } + String fileName = fileNameSid[0]; + if (fileName.startsWith("\\Device\\HarddiskVolume")) { + // Start at point past the 2nd slash + int fileNameStart = fileName.indexOf('\\', 16); + fileName = fileName.substring(fileNameStart, fileName.length()); + + } + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, getName(), fileName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_USER_NAME, getName(), userName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), progRunDateTime)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_PROG_RUN, regFile, attributes); + if (bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts from adobemru Regripper Plugin records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseAdobeMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + SimpleDateFormat adobePluginDateFormat = new SimpleDateFormat("yyyyMMddHHmmssZ", US); + Long adobeUsedTime = Long.valueOf(0); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.matches("^Key name,file name,sDate,uFileSize,uPageCount")) { + line = reader.readLine(); + // Columns are + // Key name, file name, sDate, uFileSize, uPageCount + while (!line.contains(SECTION_DIVIDER)) { + // Split csv line, handles double quotes around individual file names + // since file names can contain commas + String tokens[] = line.split(",(?=([^\"]*\"[^\"]*\")*[^\"]*$)"); + String fileName = tokens[1].substring(0, tokens[1].length() -1); + fileName = fileName.replace("\"", ""); + if (fileName.charAt(0) == '/') { + fileName = fileName.substring(1,fileName.length() - 1); + fileName = fileName.replaceFirst("/", ":/"); + } + // Check to see if more then 2 tokens, Date may not be populated, will default to 0 + if (tokens.length > 2) { + // Time in the format of 20200131104456-05'00' + try { + String fileUsedTime = tokens[2].replaceAll("'",""); + Date usedDate = adobePluginDateFormat.parse(fileUsedTime); + adobeUsedTime = usedDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for adobe file artifact.", tokens[2]), ex); //NON-NLS + } + } + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), adobeUsedTime)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + fileName = fileName.replace("\0", ""); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the Media Player MRU regripper (mpmru) records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseMediaPlayerMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("LastWrite")) { + line = reader.readLine(); + // Columns are + // FileX -> + while (!line.contains(SECTION_DIVIDER) && !line.contains("RecentFileList has no values.")) { + // Split line on "> " which is the record delimiter between position and file + String tokens[] = line.split("> "); + String fileName = tokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(fileName, bba); + if (bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + } + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the regripper plugin output, this format is used in several diffent plugins + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseGenericMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("LastWrite")) { + line = reader.readLine(); + // Columns are + // FileX -> + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Applets") + && !line.contains(("Recent File List"))) { + // Split line on "> " which is the record delimiter between position and file + String tokens[] = line.split("> "); + String fileName = tokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the WinRAR Regripper plugin output + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseWinRARMRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + if (line.contains("LastWrite")) { + line = reader.readLine(); + // Columns are + // FileX -> + if (!line.isEmpty()) { + while (!line.contains(SECTION_DIVIDER)) { + // Split line on "> " which is the record delimiter between position and file + String tokens[] = line.split("> "); + String fileName = tokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + } + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the runmru ArcHistory (7Zip) regripper plugin records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parse7ZipMRU(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + line = line.trim(); + if (!line.contains("PathHistory:")) { + while (!line.contains("PathHistory:") && !line.isEmpty()) { + // Columns are + // + String fileName = line; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if (bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the Office Documents 2010 records Regripper Plugin output + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseOfficeDocs2010MRUList(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + List bbartifacts = new ArrayList<>(); + String line = reader.readLine(); + line = line.trim(); + // Reading to the SECTION DIVIDER to get next section of records to process. Dates appear to have + // multiple spaces in them that makes it harder to parse so next section will be easier to parse + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + } + line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + // record has the following format + // 1294283922|REG|||OfficeDocs2010 - F:\Windows_time_Rules_xp.doc + String tokens[] = line.split("\\|"); + Long docDate = Long.valueOf(tokens[0]); + String fileNameTokens[] = tokens[4].split(" - "); + String fileName = fileNameTokens[1]; + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), docDate)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = reader.readLine(); + line = line.trim(); + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create recently used artifacts to parse the Office trust records (trustrecords) Regipper plugin records + * + * @param regFile registry file the artifact is associated with + * + * @param reader buffered reader to parse adobemru records + * + * @param comment string that will populate attribute TSK_COMMENT + * + * @throws FileNotFound and IOException + */ + private void parseOfficeTrustRecords(AbstractFile regFile, BufferedReader reader, String comment) throws FileNotFoundException, IOException { + String userProfile = regFile.getParentPath(); + userProfile = userProfile.substring(0, userProfile.length() - 1); + List bbartifacts = new ArrayList<>(); + SimpleDateFormat pluginDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy", US); + Long usedTime = Long.valueOf(0); + String line = reader.readLine(); + while (!line.contains(SECTION_DIVIDER)) { + line = reader.readLine(); + line = line.trim(); + usedTime = Long.valueOf(0); + if (!line.contains("**") && !line.contains("----------") && !line.contains("LastWrite") + && !line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("TrustRecords")) { + // Columns are + // Date : / + // Split line on " : " which is the record delimiter between position and file + String fileName = null; + String tokens[] = line.split(" : "); + fileName = tokens[1]; + fileName = fileName.replace("%USERPROFILE%", userProfile); + // Time in the format of Wed May 31 14:33:03 2017 Z + try { + String fileUsedTime = tokens[0].replaceAll(" Z",""); + Date usedDate = pluginDateFormat.parse(fileUsedTime); + usedTime = usedDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for TrustRecords artifact.", tokens[0]), ex); //NON-NLS + } + Collection attributes = new ArrayList<>(); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), fileName)); + attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME, getName(), usedTime)); + attributes.add(new BlackboardAttribute(TSK_COMMENT, getName(), comment)); + BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_RECENT_OBJECT, regFile, attributes); + if(bba != null) { + bbartifacts.add(bba); + bba = createAssociatedArtifact(FilenameUtils.normalize(fileName, true), bba); + if (bba != null) { + bbartifacts.add(bba); + } + } + line = line.trim(); + } + } + if (!bbartifacts.isEmpty()) { + postArtifacts(bbartifacts); + } + } + + /** + * Create associated artifacts using file name and path and the artifact it associates with + * + * @param filePathName file and path of object being associated with + * + * @param bba blackboard artifact to associate with + * + * @returnv BlackboardArtifact or a null value + */ + private BlackboardArtifact createAssociatedArtifact(String filePathName, BlackboardArtifact bba) { + org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = currentCase.getServices().getFileManager(); + String fileName = FilenameUtils.getName(filePathName); + String filePath = FilenameUtils.getPath(filePathName); + List sourceFiles; + try { + sourceFiles = fileManager.findFiles(dataSource, fileName, filePath); //NON-NLS + if (!sourceFiles.isEmpty()) { + for (AbstractFile sourceFile : sourceFiles) { + if (sourceFile.getParentPath().endsWith(filePath)) { + Collection bbattributes2 = new ArrayList<>(); + bbattributes2.addAll(Arrays.asList( + new BlackboardAttribute(TSK_ASSOCIATED_ARTIFACT, this.getName(), + bba.getArtifactID()))); + + BlackboardArtifact associatedObjectBba = createArtifactWithAttributes(TSK_ASSOCIATED_OBJECT, sourceFile, bbattributes2); + if (associatedObjectBba != null) { + return associatedObjectBba; + } + } + } + } + } catch (TskCoreException ex) { + // only catching the error and displaying the message as the file may not exist on the + // system anymore + logger.log(Level.WARNING, String.format("Error finding actual file %s. file may not exist", filePathName)); //NON-NLS + } + + return null; + } + + /** + * Create a map of userids to usernames from the OS Accounts. + * + * @param dataSource + * + * @return A Map of userIDs and userNames + * + * @throws TskCoreException + */ + private Map makeUserNameMap(Content dataSource) throws TskCoreException { + Map userNameMap = new HashMap<>(); + + List accounts = blackboard.getArtifacts(TSK_OS_ACCOUNT.getTypeID(), dataSource.getId()); + + for (BlackboardArtifact account : accounts) { + BlackboardAttribute nameAttribute = getAttributeForArtifact(account, TSK_USER_NAME); + BlackboardAttribute idAttribute = getAttributeForArtifact(account, TSK_USER_ID); + + String userName = nameAttribute != null ? nameAttribute.getDisplayString() : ""; + String userID = idAttribute != null ? idAttribute.getDisplayString() : ""; + + if (!userID.isEmpty()) { + userNameMap.put(userID, userName); + } + } + + return userNameMap; + } + + /** + * Gets the attribute for the given type from the given artifact. + * + * @param artifact BlackboardArtifact to get the attribute from + * @param type The BlackboardAttribute Type to get + * + * @return BlackboardAttribute for given artifact and type + * + * @throws TskCoreException + */ + private BlackboardAttribute getAttributeForArtifact(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) throws TskCoreException { + return artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(type.getTypeID()))); + } + + /** + * Create the shellbag artifacts from the list of ShellBag objects. + * + * @param regFile The data source file + * @param shellbags List of shellbags from source file + * + * @throws TskCoreException + */ + void createShellBagArtifacts(AbstractFile regFile, List shellbags) throws TskCoreException { + List artifacts = new ArrayList<>(); + try{ + for (ShellBag bag : shellbags) { + Collection attributes = new ArrayList<>(); + BlackboardArtifact artifact = regFile.newArtifact(getShellBagArtifact().getTypeID()); + attributes.add(new BlackboardAttribute(TSK_PATH, getName(), bag.getResource())); + attributes.add(new BlackboardAttribute(getKeyAttribute(), getName(), bag.getKey())); + + long time; + time = bag.getLastWrite(); + if (time != 0) { + attributes.add(new BlackboardAttribute(getLastWriteAttribute(), getName(), time)); + } + + time = bag.getModified(); + if (time != 0) { + attributes.add(new BlackboardAttribute(TSK_DATETIME_MODIFIED, getName(), time)); + } + + time = bag.getCreated(); + if (time != 0) { + attributes.add(new BlackboardAttribute(TSK_DATETIME_CREATED, getName(), time)); + } + + time = bag.getAccessed(); + if (time != 0) { + attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), time)); + } + + artifact.addAttributes(attributes); + + artifacts.add(artifact); + } + } finally { + postArtifacts(artifacts); + } + } + + /** + * Returns the custom Shellbag artifact type or creates it if it does not + * currently exist. + * + * @return BlackboardArtifact.Type for shellbag artifacts + * + * @throws TskCoreException + */ + private BlackboardArtifact.Type getShellBagArtifact() throws TskCoreException { + if (shellBagArtifactType == null) { + shellBagArtifactType = tskCase.getArtifactType(SHELLBAG_ARTIFACT_NAME); + + if(shellBagArtifactType == null) { + try { + tskCase.addBlackboardArtifactType(SHELLBAG_ARTIFACT_NAME, Bundle.Shellbag_Artifact_Display_Name()); //NON-NLS + } catch (TskDataException ex) { + // Artifact already exists + logger.log(Level.INFO, String.format("%s may have already been defined for this case", SHELLBAG_ARTIFACT_NAME)); + } + + shellBagArtifactType = tskCase.getArtifactType(SHELLBAG_ARTIFACT_NAME); + } + } + + return shellBagArtifactType; + } + + /** + * Gets the custom BlackboardAttribute type. The attribute type is created + * if it does not currently exist. + * + * @return The BlackboardAttribute type + * + * @throws TskCoreException + */ + private BlackboardAttribute.Type getLastWriteAttribute() throws TskCoreException { + if (shellBagLastWriteAttributeType == null) { + try { + shellBagLastWriteAttributeType = tskCase.addArtifactAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE, + BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME, + Bundle.Shellbag_Last_Write_Attribute_Display_Name()); + } catch (TskDataException ex) { + // Attribute already exists get it from the case + shellBagLastWriteAttributeType = tskCase.getAttributeType(SHELLBAG_ATTRIBUTE_LAST_WRITE); + } + } + return shellBagLastWriteAttributeType; + } + + /** + * Gets the custom BlackboardAttribute type. The attribute type is created + * if it does not currently exist. + * + * @return The BlackboardAttribute type + * + * @throws TskCoreException + */ + private BlackboardAttribute.Type getKeyAttribute() throws TskCoreException { + if (shellBagKeyAttributeType == null) { + try { + shellBagKeyAttributeType = tskCase.addArtifactAttributeType(SHELLBAG_ATTRIBUTE_KEY, + BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, + Bundle.Shellbag_Key_Attribute_Display_Name()); + } catch (TskDataException ex) { + // The attribute already exists get it from the case + shellBagKeyAttributeType = tskCase.getAttributeType(SHELLBAG_ATTRIBUTE_KEY); + } + } + return shellBagKeyAttributeType; + } + + /** + * Maps the user groups to the sid that are a part of them. + * + * @param bufferedReader + * + * @return A map if sid and the groups they map too + * + * @throws IOException + */ + Map> readGroups(BufferedReader bufferedReader) throws IOException { + Map> groupMap = new HashMap<>(); + + String line = bufferedReader.readLine(); + + int userCount = 0; + String groupName = null; + + while (line != null && !line.contains(SECTION_DIVIDER)) { + + if (line.contains("Group Name")) { + String value = line.replaceAll("Group Name\\s*?:", "").trim(); + groupName = (value.replaceAll("\\[\\d*?\\]", "")).trim(); + int startIndex = value.indexOf('['); + int endIndex = value.indexOf(']'); + + if (startIndex != -1 && endIndex != -1) { + String countStr = value.substring(startIndex + 1, endIndex); + userCount = Integer.parseInt(countStr); + } + } else if (line.matches("Users\\s*?:")) { + for (int i = 0; i < userCount; i++) { + line = bufferedReader.readLine(); + if (line != null) { + String sid = line.trim(); + List groupList = groupMap.get(sid); + if (groupList == null) { + groupList = new ArrayList<>(); + groupMap.put(sid, groupList); + } + groupList.add(groupName); + } + } + groupName = null; + } + line = bufferedReader.readLine(); + } + return groupMap; + } + + /** + * Gets the key value from user account strings of the format + * key:value or + * --> value + * + * @param line String to parse + * + * @return key value pair + */ + private Map.Entry getSAMKeyValue(String line) { + int index = line.indexOf(':'); + Map.Entry returnValue = null; + String key = null; + String value = null; + + if (index != -1) { + key = line.substring(0, index).trim(); + if (index + 1 < line.length()) { + value = line.substring(index + 1).trim(); + } else { + value = ""; + } + + } else if (line.contains("-->")) { + key = line.replace("-->", "").trim(); + value = "true"; + } + + if (key != null) { + returnValue = new AbstractMap.SimpleEntry<>(key, value); + } + + return returnValue; + } + + @Override + public void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) { + this.dataSource = dataSource; + this.context = context; + + progressBar.progress(Bundle.Progress_Message_Analyze_Registry()); + analyzeRegistryFiles(); + + } + + /** + * Private wrapper class for Registry output files + */ + private class RegOutputFiles { + + public String autopsyPlugins = ""; + public String fullPlugins = ""; + } +} From 9132ceeede036550924d8789d0e5b715da41a89b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 21 Jul 2020 14:42:35 -0400 Subject: [PATCH 03/35] fix empty statement Fix empty statement codacy --- .../org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index c98965adbe..4c0e2080c5 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -1017,10 +1017,10 @@ class ExtractRegistry extends Extract { line = reader.readLine(); } String lastSeenTokens[] = line.split(": "); - String lastSeen = lastSeenTokens[1].replace(" Z", "");; + String lastSeen = lastSeenTokens[1].replace(" Z", ""); line = reader.readLine(); String lastConnectedTokens[] = line.split(": "); - String lastConnected = lastConnectedTokens[1].replace(" Z", "");; + String lastConnected = lastConnectedTokens[1].replace(" Z", ""); try { Date usedSeenDate = dateFormat.parse(lastSeen); bthLastSeen = usedSeenDate.getTime()/1000; From f171de25c6d4785ca5634cfd18c9e284c093a6fe Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 23 Jul 2020 16:36:01 -0400 Subject: [PATCH 04/35] Added ContactCache --- .../autopsy/communications/ContactCache.java | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100755 Core/src/org/sleuthkit/autopsy/communications/ContactCache.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java new file mode 100755 index 0000000000..f711634d4e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java @@ -0,0 +1,182 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestManager; +import static org.sleuthkit.autopsy.ingest.IngestManager.IngestModuleEvent.DATA_ADDED; +import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * A singleton cache of the Contact artifacts for accounts. This list of + * TSK_CONTACT artifacts for a given Account retrieved on first access and + * evicted from the ache after 10 minutes. + * + */ +final class ContactCache { + + private static final Logger logger = Logger.getLogger(ContactCache.class.getName()); + + private final LoadingCache> contactCache; + + private static ContactCache instance; + + private final PropertyChangeListener ingestListener; + + /** + * Returns the list of Contacts for the given Account. + * + * @param account Account instance. + * + * @return List of TSK_CONTACT artifacts that references the given Account. + * An empty list is returned if no contacts are found. + * + * @throws ExecutionException + */ + static synchronized List getContacts(Account account) throws ExecutionException { + return getInstance().contactCache.get(account); + } + + /** + * Force the cache to invalidate all entries. + */ + static synchronized void invalidateCache() { + getInstance().contactCache.invalidateAll(); + } + + /** + * Construct a new instance. + */ + private ContactCache() { + contactCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( + new CacheLoader>() { + @Override + public List load(Account key) { + try { + List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); + return findContactForAccount(contactList, key); + + } catch (TskCoreException ex) { + logger.log(Level.WARNING, String.format("Failed to load contacts for account %d", key.getAccountID()), ex); + } + return new ArrayList<>(); + } + }); + + this.ingestListener = pce -> { + String eventType = pce.getPropertyName(); + if (eventType.equals(DATA_ADDED.toString())) { + ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); + if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT.getTypeID()) { + invalidateCache(); + } + } + }; + + IngestManager.getInstance().addIngestModuleEventListener(EnumSet.of(DATA_ADDED), ingestListener); + } + + /** + * Returns the singleton instance of the cache object. + * + * @return AccountCache instance. + */ + private static synchronized ContactCache getInstance() { + if (instance == null) { + instance = new ContactCache(); + } + + return instance; + } + + /** + * Returns a list of TSK_CONTACT artifacts that reference the given account. + * + * @param allContactList List of existing TSK_CONTACT artifacts. + * @param account Account reference. + * + * @return A list of TSK_CONTACT artifact that reference the given account + * or empty list of none were found. + * + * @throws TskCoreException + */ + private List findContactForAccount(List allContactList, Account account) throws TskCoreException { + List accountContacts = new ArrayList<>(); + + for (BlackboardArtifact contact : allContactList) { + if (isAccountInAttributeList(contact.getAttributes(), account)) { + accountContacts.add(contact); + } + } + + return accountContacts; + } + + /** + * Determine if there is an attribute in the given list that references the + * given account. + * + * @param contactAttributes List of attributes. + * @param account Account object. + * + * @return True if one of the attributes in the list reference the account. + */ + private boolean isAccountInAttributeList(List contactAttributes, Account account) { + for (BlackboardAttribute attribute : contactAttributes) { + if (isAccountInAttribute(attribute, account)) { + return true; + } + } + return false; + } + + /** + * Checks the given attribute to see if it references the given account. + * + * @param attribute BlackboardAttribute to check. + * @param account References account. + * + * @return True if the attribute references the account. + */ + private boolean isAccountInAttribute(BlackboardAttribute attribute, Account account) { + + String typeName = attribute.getAttributeType().getTypeName(); + return (typeName.startsWith("TSK_EMAIL") + || typeName.startsWith("TSK_PHONE") + || typeName.startsWith("TSK_NAME") + || typeName.startsWith("TSK_ID")) + && attribute.getValueString().equals(account.getTypeSpecificID()); + } + +} From 692b4604fa511fc3ea376a615d48fdd01e69940d Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Thu, 23 Jul 2020 16:47:23 -0400 Subject: [PATCH 05/35] Address codacy issues --- .../autopsy/communications/ContactCache.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java index f711634d4e..1f34b3cf62 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java @@ -48,12 +48,10 @@ final class ContactCache { private static final Logger logger = Logger.getLogger(ContactCache.class.getName()); - private final LoadingCache> contactCache; + private final LoadingCache> accountMap; private static ContactCache instance; - private final PropertyChangeListener ingestListener; - /** * Returns the list of Contacts for the given Account. * @@ -65,21 +63,21 @@ final class ContactCache { * @throws ExecutionException */ static synchronized List getContacts(Account account) throws ExecutionException { - return getInstance().contactCache.get(account); + return getInstance().accountMap.get(account); } /** * Force the cache to invalidate all entries. */ static synchronized void invalidateCache() { - getInstance().contactCache.invalidateAll(); + getInstance().accountMap.invalidateAll(); } /** * Construct a new instance. */ private ContactCache() { - contactCache = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( + accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( new CacheLoader>() { @Override public List load(Account key) { @@ -94,7 +92,7 @@ final class ContactCache { } }); - this.ingestListener = pce -> { + PropertyChangeListener ingestListener = pce -> { String eventType = pce.getPropertyName(); if (eventType.equals(DATA_ADDED.toString())) { ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); From 4abbbd9dcc27b1b25fe290286a801873a9c78cee Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 28 Jul 2020 10:16:00 -0400 Subject: [PATCH 06/35] Initial commit Initial commit of Chrome based browsers --- .../recentactivity/Bundle.properties-MERGED | 20 +- .../autopsy/recentactivity/Chrome.java | 180 ++++++++++-------- 2 files changed, 108 insertions(+), 92 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED index 5ea28d7eec..839ccdd039 100755 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Bundle.properties-MERGED @@ -5,15 +5,10 @@ ChromeCacheExtract_adding_artifacts_msg=Chrome Cache: Adding %d artifacts for an ChromeCacheExtract_adding_extracted_files_msg=Chrome Cache: Adding %d extracted files for analysis. ChromeCacheExtract_loading_files_msg=Chrome Cache: Loading files from %s. ChromeCacheExtractor.moduleName=ChromeCacheExtractor -# {0} - module name -# {1} - row number -# {2} - table length -# {3} - cache path ChromeCacheExtractor.progressMsg={0}: Extracting cache entry {1} of {2} entries from {3} DataSourceUsage_AndroidMedia=Android Media Card DataSourceUsage_DJU_Drone_DAT=DJI Internal SD Card DataSourceUsage_FlashDrive=Flash Drive -# {0} - OS name DataSourceUsageAnalyzer.customVolume.label=OS Drive ({0}) DataSourceUsageAnalyzer.parentModuleName=Recent Activity Extract.indexError.message=Failed to index artifact for keyword search. @@ -77,7 +72,7 @@ ExtractZone_progress_Msg=Extracting :Zone.Identifer files ExtractZone_Restricted=Restricted Sites Zone ExtractZone_Trusted=Trusted Sites Zone OpenIDE-Module-Display-Category=Ingest Module -OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\n\The module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy. +OpenIDE-Module-Long-Description=Recent Activity ingest module.\n\nThe module extracts useful information about the recent user activity on the disk image being ingested, such as:\n\n- Recently open documents,\n- Web activity (sites visited, stored cookies, book marked sites, search engine queries, file downloads),\n- Recently attached devices,\n- Installed programs.\n\nThe module currently supports Windows only disk images.\nThe plugin is also fully functional when deployed on Windows version of Autopsy. OpenIDE-Module-Name=RecentActivity OpenIDE-Module-Short-Description=Recent Activity finder ingest module Chrome.moduleName=Chrome @@ -148,14 +143,14 @@ Firefox.getDlV24.errMsg.errAnalyzeFile={0}: Error while trying to analyze file:{ Firefox.getDlV24.errMsg.errParsingArtifacts={0}: Error parsing {1} Firefox web download artifacts. Progress_Message_Analyze_Registry=Analyzing Registry Files Progress_Message_Analyze_Usage=Data Sources Usage Analysis -Progress_Message_Chrome_AutoFill=Chrome Auto Fill -Progress_Message_Chrome_Bookmarks=Chrome Bookmarks +Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0} +Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0} Progress_Message_Chrome_Cache=Chrome Cache -Progress_Message_Chrome_Cookies=Chrome Cookies -Progress_Message_Chrome_Downloads=Chrome Downloads +Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0} +Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0} Progress_Message_Chrome_FormHistory=Chrome Form History -Progress_Message_Chrome_History=Chrome History -Progress_Message_Chrome_Logins=Chrome Logins +Progress_Message_Chrome_History=Chrome History Browser {0} +Progress_Message_Chrome_Logins=Chrome Logins Browser {0} Progress_Message_Edge_Bookmarks=Microsoft Edge Bookmarks Progress_Message_Edge_Cookies=Microsoft Edge Cookies Progress_Message_Edge_History=Microsoft Edge History @@ -209,7 +204,6 @@ Recently_Used_Artifacts_Winrar=Recently opened according to WinRAR MRU Registry_System_Bam=Recently Executed according to Background Activity Moderator (BAM) RegRipperFullNotFound=Full version RegRipper executable not found. RegRipperNotFound=Autopsy RegRipper executable not found. -# {0} - file name SearchEngineURLQueryAnalyzer.init.exception.msg=Unable to find {0}. SearchEngineURLQueryAnalyzer.moduleName.text=Search Engine SearchEngineURLQueryAnalyzer.engineName.none=NONE diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index 2c7c5dfcb0..f7949bdf78 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -22,6 +22,7 @@ */ package org.sleuthkit.autopsy.recentactivity; +import com.google.common.collect.ImmutableMap; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonIOException; @@ -84,14 +85,25 @@ class Chrome extends Extract { private Content dataSource; private IngestJobContext context; + private static final Map BROWSERS_MAP = ImmutableMap.builder() + .put("Microsoft Edge", "Microsoft/Edge") +// .put("Yandex Browser", "YandexBrowser") + .put("Opera", "Opera Software") +// .put("SalamWeb", "SalamWeb") +// .put("UC Browser", "UCBrowser") +// .put("Brave", "BraveSoftware") +// .put("Google Chrome", "Chrome") + .build(); + + @Messages({ - "Progress_Message_Chrome_History=Chrome History", - "Progress_Message_Chrome_Bookmarks=Chrome Bookmarks", - "Progress_Message_Chrome_Cookies=Chrome Cookies", - "Progress_Message_Chrome_Downloads=Chrome Downloads", + "Progress_Message_Chrome_History=Chrome History Browser {0}", + "Progress_Message_Chrome_Bookmarks=Chrome Bookmarks Browser {0}", + "Progress_Message_Chrome_Cookies=Chrome Cookies Browser {0}", + "Progress_Message_Chrome_Downloads=Chrome Downloads Browser {0}", "Progress_Message_Chrome_FormHistory=Chrome Form History", - "Progress_Message_Chrome_AutoFill=Chrome Auto Fill", - "Progress_Message_Chrome_Logins=Chrome Logins", + "Progress_Message_Chrome_AutoFill=Chrome Auto Fill Browser {0}", + "Progress_Message_Chrome_Logins=Chrome Logins Browser {0}", "Progress_Message_Chrome_Cache=Chrome Cache", }) @@ -105,55 +117,60 @@ class Chrome extends Extract { this.context = context; dataFound = false; - progressBar.progress(Bundle.Progress_Message_Chrome_History()); - this.getHistory(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_Bookmarks()); - this.getBookmark(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_Cookies()); - this.getCookie(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_Logins()); - this.getLogins(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_AutoFill()); - this.getAutofill(); - if (context.dataSourceIngestIsCancelled()) { - return; - } - - progressBar.progress(Bundle.Progress_Message_Chrome_Downloads()); - this.getDownload(); - if (context.dataSourceIngestIsCancelled()) { - return; + for (Map.Entry browser : BROWSERS_MAP.entrySet()) { + String browserName = browser.getKey(); + String browserLocation = browser.getValue(); + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_History", browserName)); + this.getHistory(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Bookmarks", browserName)); + this.getBookmark(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Cookies", browserName)); + this.getCookie(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Logins", browserName)); + this.getLogins(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_AutoFill", browserName)); + this.getAutofill(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } + + progressBar.progress(NbBundle.getMessage(this.getClass(), "Progress_Message_Chrome_Downloads", browserName)); + this.getDownload(browser.getKey(), browser.getValue()); + if (context.dataSourceIngestIsCancelled()) { + return; + } } progressBar.progress(Bundle.Progress_Message_Chrome_Cache()); ChromeCacheExtractor chromeCacheExtractor = new ChromeCacheExtractor(dataSource, context, progressBar); chromeCacheExtractor.processCaches(); + } /** * Query for history databases and add artifacts */ - private void getHistory() { + private void getHistory(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List historyFiles; try { - historyFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS + historyFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -179,10 +196,11 @@ class Chrome extends Extract { dataFound = true; Collection bbartifacts = new ArrayList<>(); int j = 0; - while (j < historyFiles.size()) { - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + historyFiles.get(j).getName() + j + ".db"; //NON-NLS - final AbstractFile historyFile = historyFiles.get(j++); - if (historyFile.getSize() == 0) { + while (j < allocatedHistoryFiles.size()) { + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + allocatedHistoryFiles.get(j).getName() + j + ".db"; //NON-NLS + final AbstractFile historyFile = allocatedHistoryFiles.get(j++); + if ((historyFile.getSize() == 0) || (historyFile.getName().toLowerCase().contains("-slack")) + || (historyFile.getName().toLowerCase().contains("cache"))) { continue; } try { @@ -223,8 +241,7 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), ((result.get("title").toString() != null) ? result.get("title").toString() : ""))); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); + RecentActivityExtracterModuleFactory.getModuleName(), browser)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, RecentActivityExtracterModuleFactory.getModuleName(), (NetworkUtils.extractDomain((result.get("url").toString() != null) ? result.get("url").toString() : "")))); //NON-NLS @@ -245,11 +262,11 @@ class Chrome extends Extract { /** * Search for bookmark files and make artifacts. */ - private void getBookmark() { + private void getBookmark(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List bookmarkFiles; try { - bookmarkFiles = fileManager.findFiles(dataSource, "Bookmarks", "Chrome"); //NON-NLS + bookmarkFiles = fileManager.findFiles(dataSource, "%Bookmarks%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getBookmark.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -268,10 +285,10 @@ class Chrome extends Extract { while (j < bookmarkFiles.size()) { AbstractFile bookmarkFile = bookmarkFiles.get(j++); - if (bookmarkFile.getSize() == 0) { + if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(bookmarkFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -359,8 +376,7 @@ class Chrome extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, RecentActivityExtracterModuleFactory.getModuleName(), (date / 1000000) - Long.valueOf("11644473600"))); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); + RecentActivityExtracterModuleFactory.getModuleName(), browser)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, RecentActivityExtracterModuleFactory.getModuleName(), domain)); bbart.addAttributes(bbattributes); @@ -381,12 +397,12 @@ class Chrome extends Extract { /** * Queries for cookie files and adds artifacts */ - private void getCookie() { + private void getCookie(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List cookiesFiles; try { - cookiesFiles = fileManager.findFiles(dataSource, "Cookies", "Chrome"); //NON-NLS + cookiesFiles = fileManager.findFiles(dataSource, "%Cookies%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getCookie.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -404,10 +420,10 @@ class Chrome extends Extract { int j = 0; while (j < cookiesFiles.size()) { AbstractFile cookiesFile = cookiesFiles.get(j++); - if (cookiesFile.getSize() == 0) { + if ((cookiesFile.getSize() == 0) || (cookiesFile.getName().toLowerCase().contains("-slack"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + cookiesFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(cookiesFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -447,8 +463,7 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), ((result.get("value").toString() != null) ? result.get("value").toString() : ""))); //NON-NLS bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); + RecentActivityExtracterModuleFactory.getModuleName(), browser)); String domain = result.get("host_key").toString(); //NON-NLS domain = domain.replaceFirst("^\\.+(?!$)", ""); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, @@ -471,11 +486,11 @@ class Chrome extends Extract { /** * Queries for download files and adds artifacts */ - private void getDownload() { + private void getDownload(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List downloadFiles; try { - downloadFiles = fileManager.findFiles(dataSource, "History", "Chrome"); //NON-NLS + downloadFiles = fileManager.findFiles(dataSource, "%History%", "Chrome"); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -493,10 +508,12 @@ class Chrome extends Extract { int j = 0; while (j < downloadFiles.size()) { AbstractFile downloadFile = downloadFiles.get(j++); - if (downloadFile.getSize() == 0) { + if ((downloadFile.getSize() == 0) || (downloadFile.getName().toLowerCase().contains("-slack")) + || (downloadFile.getName().toLowerCase().contains("cache"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS + + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + downloadFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(downloadFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -552,8 +569,7 @@ class Chrome extends Extract { bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DOMAIN, RecentActivityExtracterModuleFactory.getModuleName(), domain)); bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, - RecentActivityExtracterModuleFactory.getModuleName(), - NbBundle.getMessage(this.getClass(), "Chrome.moduleName"))); + RecentActivityExtracterModuleFactory.getModuleName(), browser)); BlackboardArtifact webDownloadArtifact = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_DOWNLOAD, downloadFile, bbattributes); if (webDownloadArtifact != null) { @@ -561,7 +577,7 @@ class Chrome extends Extract { // find the downloaded file and create a TSK_ASSOCIATED_OBJECT for it, associating it with the TSK_WEB_DOWNLOAD artifact. try { - String normalizedFullPath = FilenameUtils.normalize(fullPath, true); + String normalizedFullPath = FilenameUtils.normalize(fullPath, true); for (AbstractFile downloadedFile : fileManager.findFiles(dataSource, FilenameUtils.getName(normalizedFullPath), FilenameUtils.getPath(normalizedFullPath))) { BlackboardArtifact associatedObjectArtifact = downloadedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); associatedObjectArtifact.addAttribute( @@ -588,12 +604,12 @@ class Chrome extends Extract { /** * Gets user logins from Login Data sqlite database */ - private void getLogins() { + private void getLogins(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List loginDataFiles; try { - loginDataFiles = fileManager.findFiles(dataSource, "Login Data", "Chrome"); //NON-NLS + loginDataFiles = fileManager.findFiles(dataSource, "%Login Data%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getLogin.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -611,10 +627,10 @@ class Chrome extends Extract { int j = 0; while (j < loginDataFiles.size()) { AbstractFile loginDataFile = loginDataFiles.get(j++); - if (loginDataFile.getSize() == 0) { + if ((loginDataFile.getSize() == 0) || (loginDataFile.getName().toLowerCase().contains("-slack"))) { continue; } - String temps = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS + String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + loginDataFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(loginDataFile, new File(temps), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -661,6 +677,9 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), ((result.get("signon_realm").toString() != null) ? result.get("signon_realm").toString() : ""))); //NON-NLS + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), browser)); + BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_SERVICE_ACCOUNT, loginDataFile, bbattributes); if (bbart != null) { bbartifacts.add(bbart); @@ -679,12 +698,12 @@ class Chrome extends Extract { * Gets and parses Autofill data from 'Web Data' database, * and creates TSK_WEB_FORM_AUTOFILL, TSK_WEB_FORM_ADDRESS artifacts */ - private void getAutofill() { + private void getAutofill(String browser, String browserLocation) { FileManager fileManager = currentCase.getServices().getFileManager(); List webDataFiles; try { - webDataFiles = fileManager.findFiles(dataSource, "Web Data", "Chrome"); //NON-NLS + webDataFiles = fileManager.findFiles(dataSource, "%Web Data%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getAutofills.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); @@ -702,10 +721,10 @@ class Chrome extends Extract { int j = 0; while (j < webDataFiles.size()) { AbstractFile webDataFile = webDataFiles.get(j++); - if (webDataFile.getSize() == 0) { + if ((webDataFile.getSize() == 0) || (webDataFile.getName().toLowerCase().contains("-slack"))) { continue; } - String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, "chrome") + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS + String tempFilePath = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + webDataFile.getName() + j + ".db"; //NON-NLS try { ContentUtils.writeToFile(webDataFile, new File(tempFilePath), context::dataSourceIngestIsCancelled); } catch (ReadContentInputStreamException ex) { @@ -731,7 +750,7 @@ class Chrome extends Extract { boolean isSchemaV8X = Util.checkColumn("date_created", "autofill", tempFilePath); // get form autofill artifacts - bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X)); + bbartifacts.addAll(getFormAutofillArtifacts(webDataFile, tempFilePath, isSchemaV8X, browser)); try { // get form address atifacts getFormAddressArtifacts(webDataFile, tempFilePath, isSchemaV8X); @@ -757,7 +776,7 @@ class Chrome extends Extract { * * @return collection of TSK_WEB_FORM_AUTOFILL artifacts */ - private Collection getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X ) { + private Collection getFormAutofillArtifacts (AbstractFile webDataFile, String dbFilePath , boolean isSchemaV8X, String browser ) { Collection bbartifacts = new ArrayList<>(); @@ -783,7 +802,7 @@ class Chrome extends Extract { RecentActivityExtracterModuleFactory.getModuleName(), (Integer.valueOf(result.get("count").toString())))); //NON-NLS - bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, RecentActivityExtracterModuleFactory.getModuleName(), Long.valueOf(result.get("date_created").toString()))); //NON-NLS @@ -794,6 +813,9 @@ class Chrome extends Extract { Long.valueOf(result.get("date_last_used").toString()))); //NON-NLS } + bbattributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PROG_NAME, + RecentActivityExtracterModuleFactory.getModuleName(), browser)); + // Add an artifact BlackboardArtifact bbart = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_WEB_FORM_AUTOFILL, webDataFile, bbattributes); if (bbart != null) { @@ -886,7 +908,7 @@ class Chrome extends Extract { use_count, otherAttributes); } } - + private boolean isChromePreVersion30(String temps) { String query = "PRAGMA table_info(downloads)"; //NON-NLS List> columns = this.dbConnect(temps, query); From 5aa868b0a35b22b485d9337446a6b98568f7f53b Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 28 Jul 2020 11:47:43 -0400 Subject: [PATCH 07/35] Update ExtractRegistry.java Address comments and codacy --- .../recentactivity/ExtractRegistry.java | 85 ++++++++++--------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java index 4c0e2080c5..4fcfcf9477 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractRegistry.java @@ -961,9 +961,9 @@ class ExtractRegistry extends Extract { while (line != null) { line = line.trim(); - if (line.matches("^bam v.*")) { + if (line.toLowerCase().matches("^bam v.*")) { parseBamKey(regAbstractFile, reader, Bundle.Registry_System_Bam()); - } else if (line.matches("^bthport v..*")) { + } else if (line.toLowerCase().matches("^bthport v..*")) { parseBlueToothDevices(regAbstractFile, reader) ; } line = reader.readLine(); @@ -993,56 +993,25 @@ class ExtractRegistry extends Extract { private void parseBlueToothDevices(AbstractFile regFile, BufferedReader reader) throws FileNotFoundException, IOException { List bbartifacts = new ArrayList<>(); String line = reader.readLine(); - // date format for plugin Tue Jun 23 10:27:54 2020 Z - SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); - Long bthLastSeen = Long.valueOf(0); - Long bthLastConnected = Long.valueOf(0); while (!line.contains(SECTION_DIVIDER)) { line = reader.readLine(); line = line.trim(); - if (line.contains("Device Unique ID")) { + if (line.toLowerCase().contains("device unique id")) { // Columns are seperated by colons : // Data : Values // Record is 4 lines in length (Device Unique Id, Name, Last Seen, LastConnected - while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.contains("Radio Support not found")) { - // Split line on "> " which is the record delimiter between position and file - String deviceTokens[] = line.split(": "); - String deviceUniqueId = deviceTokens[1]; + while (!line.contains(SECTION_DIVIDER) && !line.isEmpty() && !line.toLowerCase().contains("radio support not found")) { + Collection attributes = new ArrayList<>(); + addBlueToothAttribute(line, attributes, TSK_DEVICE_ID); line = reader.readLine(); - // Default device name to unknown as a device name may not exist. - String deviceName = "Unknown"; - if (line.contains("Name")) { - String nameTokens[] = line.split(": "); - deviceName = nameTokens[1]; + // Name may not exist, check for it to make sure. + if (line.toLowerCase().contains("name")) { + addBlueToothAttribute(line, attributes, TSK_NAME); line = reader.readLine(); } - String lastSeenTokens[] = line.split(": "); - String lastSeen = lastSeenTokens[1].replace(" Z", ""); + addBlueToothAttribute(line, attributes, TSK_DATETIME); line = reader.readLine(); - String lastConnectedTokens[] = line.split(": "); - String lastConnected = lastConnectedTokens[1].replace(" Z", ""); - try { - Date usedSeenDate = dateFormat.parse(lastSeen); - bthLastSeen = usedSeenDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", lastSeen), ex); //NON-NLS - } - try { - Date usedConnectedDate = dateFormat.parse(lastConnected); - bthLastConnected = usedConnectedDate.getTime()/1000; - } catch (ParseException ex) { - // catching error and displaying date that could not be parsed - // we set the timestamp to 0 and continue on processing - logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last connected attribute.", lastSeen), ex); //NON-NLS - } - - Collection attributes = new ArrayList<>(); - attributes.add(new BlackboardAttribute(TSK_DEVICE_ID, getName(), deviceUniqueId)); - attributes.add(new BlackboardAttribute(TSK_NAME, getName(), deviceName)); - attributes.add(new BlackboardAttribute(TSK_DATETIME, getName(), bthLastSeen)); - attributes.add(new BlackboardAttribute(TSK_DATETIME_ACCESSED, getName(), bthLastConnected)); + addBlueToothAttribute(line, attributes, TSK_DATETIME_ACCESSED); BlackboardArtifact bba = createArtifactWithAttributes(ARTIFACT_TYPE.TSK_BLUETOOTH_PAIRING, regFile, attributes); if(bba != null) { bbartifacts.add(bba); @@ -1059,6 +1028,38 @@ class ExtractRegistry extends Extract { } } + + private void addBlueToothAttribute(String line, Collection attributes, ATTRIBUTE_TYPE attributeType) { + if(line == null) { + return; + } + + String tokens[] = line.split(": "); + if(tokens.length > 1 && !tokens[1].isEmpty()) { + String tokenString = tokens[1]; + if (attributeType.getDisplayName().toLowerCase().contains("date")) { + String dateString = tokenString.toLowerCase().replace(" z", ""); + // date format for plugin Tue Jun 23 10:27:54 2020 Z + SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss yyyy", US); + Long dateLong = Long.valueOf(0); + try { + Date newDate = dateFormat.parse(dateString); + dateLong = newDate.getTime()/1000; + } catch (ParseException ex) { + // catching error and displaying date that could not be parsed + // we set the timestamp to 0 and continue on processing + logger.log(Level.WARNING, String.format("Failed to parse date/time %s for Bluetooth Last Seen attribute.", dateString), ex); //NON-NLS + } + attributes.add(new BlackboardAttribute(attributeType, getName(), dateLong)); + + + } else { + attributes.add(new BlackboardAttribute(attributeType, getName(), tokenString)); + } + } +} + + /** * Parse the output of the SAM regripper plugin to get additional Account * information From 122015f3e7d53b8964bd8ee423c7850a70630ba1 Mon Sep 17 00:00:00 2001 From: Mark McKinnon Date: Tue, 28 Jul 2020 15:08:15 -0400 Subject: [PATCH 08/35] Update Chrome.java Add bookmarks and history files to ignore. --- .../autopsy/recentactivity/Chrome.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java index f7949bdf78..524511a308 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Chrome.java @@ -87,12 +87,12 @@ class Chrome extends Extract { private static final Map BROWSERS_MAP = ImmutableMap.builder() .put("Microsoft Edge", "Microsoft/Edge") -// .put("Yandex Browser", "YandexBrowser") + .put("Yandex Browser", "YandexBrowser") .put("Opera", "Opera Software") -// .put("SalamWeb", "SalamWeb") -// .put("UC Browser", "UCBrowser") -// .put("Brave", "BraveSoftware") -// .put("Google Chrome", "Chrome") + .put("SalamWeb", "SalamWeb") + .put("UC Browser", "UCBrowser") + .put("Brave", "BraveSoftware") + .put("Google Chrome", "Chrome") .build(); @@ -200,7 +200,7 @@ class Chrome extends Extract { String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + allocatedHistoryFiles.get(j).getName() + j + ".db"; //NON-NLS final AbstractFile historyFile = allocatedHistoryFiles.get(j++); if ((historyFile.getSize() == 0) || (historyFile.getName().toLowerCase().contains("-slack")) - || (historyFile.getName().toLowerCase().contains("cache"))) { + || (historyFile.getName().toLowerCase().contains("cache")) || (historyFile.getName().toLowerCase().contains("media"))) { continue; } try { @@ -285,7 +285,9 @@ class Chrome extends Extract { while (j < bookmarkFiles.size()) { AbstractFile bookmarkFile = bookmarkFiles.get(j++); - if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack"))) { + if ((bookmarkFile.getSize() == 0) || (bookmarkFile.getName().toLowerCase().contains("-slack")) + || (bookmarkFile.getName().toLowerCase().contains("extras")) || (bookmarkFile.getName().toLowerCase().contains("log")) + || (bookmarkFile.getName().toLowerCase().contains("backup")) || (bookmarkFile.getName().toLowerCase().contains("visualized"))) { continue; } String temps = RAImageIngestModule.getRATempPath(currentCase, browser) + File.separator + bookmarkFile.getName() + j + ".db"; //NON-NLS @@ -490,7 +492,7 @@ class Chrome extends Extract { FileManager fileManager = currentCase.getServices().getFileManager(); List downloadFiles; try { - downloadFiles = fileManager.findFiles(dataSource, "%History%", "Chrome"); //NON-NLS + downloadFiles = fileManager.findFiles(dataSource, "%History%", browserLocation); //NON-NLS } catch (TskCoreException ex) { String msg = NbBundle.getMessage(this.getClass(), "Chrome.getDownload.errMsg.errGettingFiles"); logger.log(Level.SEVERE, msg, ex); From d028db66dd1e687590e20d56acf05932e3e82fcc Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Wed, 29 Jul 2020 14:46:52 -0400 Subject: [PATCH 09/35] Modified cvt account cache to be more efficent --- .../autopsy/communications/ContactCache.java | 92 ++++++++++++++----- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java index 1f34b3cf62..5ee6e6a563 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java +++ b/Core/src/org/sleuthkit/autopsy/communications/ContactCache.java @@ -22,9 +22,13 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import java.beans.PropertyChangeListener; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.ArrayList; import java.util.EnumSet; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -36,21 +40,22 @@ import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.TskCoreException; /** - * A singleton cache of the Contact artifacts for accounts. This list of - * TSK_CONTACT artifacts for a given Account retrieved on first access and - * evicted from the ache after 10 minutes. + * A singleton cache of the Contact artifacts for accounts. The map of account + * unique ids to list of contact artifacts is stored in a LoadingCache which + * expires after 10 of non-use. * */ final class ContactCache { private static final Logger logger = Logger.getLogger(ContactCache.class.getName()); - private final LoadingCache> accountMap; - private static ContactCache instance; + + private final LoadingCache>> accountMap; /** * Returns the list of Contacts for the given Account. @@ -63,7 +68,7 @@ final class ContactCache { * @throws ExecutionException */ static synchronized List getContacts(Account account) throws ExecutionException { - return getInstance().accountMap.get(account); + return getInstance().accountMap.get("realMap").get(account.getTypeSpecificID()); } /** @@ -77,18 +82,17 @@ final class ContactCache { * Construct a new instance. */ private ContactCache() { + accountMap = CacheBuilder.newBuilder().expireAfterAccess(10, TimeUnit.MINUTES).build( - new CacheLoader>() { + new CacheLoader>>() { @Override - public List load(Account key) { + public Map> load(String key) { try { - List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); - return findContactForAccount(contactList, key); - - } catch (TskCoreException ex) { - logger.log(Level.WARNING, String.format("Failed to load contacts for account %d", key.getAccountID()), ex); - } - return new ArrayList<>(); + return buildMap(); + } catch (SQLException | TskCoreException ex) { + logger.log(Level.WARNING, "Failed to build account to contact map", ex); + } + return new HashMap<>(); // Return an empty map if there is an exception to avoid NPE and continual trying. } }); @@ -117,23 +121,43 @@ final class ContactCache { return instance; } + + /** + * Builds the map of account IDs to contacts that reference them. + * + * @return A map of account IDs to contact artifacts. + * + * @throws TskCoreException + * @throws SQLException + */ + private Map> buildMap() throws TskCoreException, SQLException { + Map> acctMap = new HashMap<>(); + List accountIdList = getAccountList(); + List contactList = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT); + + for(String id: accountIdList) { + acctMap.put(id, findContactForAccount(contactList, id)); + } + + return acctMap; + } /** * Returns a list of TSK_CONTACT artifacts that reference the given account. * * @param allContactList List of existing TSK_CONTACT artifacts. - * @param account Account reference. + * @param account String account unique id. * * @return A list of TSK_CONTACT artifact that reference the given account * or empty list of none were found. * * @throws TskCoreException */ - private List findContactForAccount(List allContactList, Account account) throws TskCoreException { + private List findContactForAccount(List allContactList, String accountId) throws TskCoreException { List accountContacts = new ArrayList<>(); for (BlackboardArtifact contact : allContactList) { - if (isAccountInAttributeList(contact.getAttributes(), account)) { + if (isAccountRelatedToArtifact(contact, accountId)) { accountContacts.add(contact); } } @@ -146,13 +170,14 @@ final class ContactCache { * given account. * * @param contactAttributes List of attributes. - * @param account Account object. + * @param account String account uniqueID. * * @return True if one of the attributes in the list reference the account. */ - private boolean isAccountInAttributeList(List contactAttributes, Account account) { + private boolean isAccountRelatedToArtifact(BlackboardArtifact artifact, String accountId) throws TskCoreException { + List contactAttributes = artifact.getAttributes(); for (BlackboardAttribute attribute : contactAttributes) { - if (isAccountInAttribute(attribute, account)) { + if (isAccountInAttribute(attribute, accountId)) { return true; } } @@ -167,14 +192,35 @@ final class ContactCache { * * @return True if the attribute references the account. */ - private boolean isAccountInAttribute(BlackboardAttribute attribute, Account account) { + private boolean isAccountInAttribute(BlackboardAttribute attribute, String accountId) { String typeName = attribute.getAttributeType().getTypeName(); return (typeName.startsWith("TSK_EMAIL") || typeName.startsWith("TSK_PHONE") || typeName.startsWith("TSK_NAME") || typeName.startsWith("TSK_ID")) - && attribute.getValueString().equals(account.getTypeSpecificID()); + && attribute.getValueString().equals(accountId); + } + + /** + * Gets a list of all accounts unique IDs from the db. + * + * @return A list of unique account ids or empty list if no accounts were found. + * + * @throws TskCoreException + * @throws SQLException + */ + private List getAccountList() throws TskCoreException, SQLException { + List uniqueIdList = new ArrayList<>(); + + CaseDbQuery caseDbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery("SELECT account_unique_indenifier FROM accounts"); + ResultSet resultSet = caseDbQuery.getResultSet(); + + while(resultSet.next()) { + uniqueIdList.add(resultSet.getString(1)); + } + + return uniqueIdList; } } From b14add6c25764e248a83472007fb3fcc81dae2c7 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 10:54:51 -0400 Subject: [PATCH 10/35] beginnings of top program items --- .../datasourcesummary/Bundle.properties | 1 + .../Bundle.properties-MERGED | 4 + .../datasourcesummary/Bundle_ja.properties | 1 + .../DataSourceInfoUtilities.java | 87 +++++++++++++++---- .../DataSourceSummaryTabbedPane.java | 5 ++ .../autoingest/Bundle.properties-MERGED | 2 - 6 files changed, 82 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties index 2cb4756460..24a312f733 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties @@ -36,3 +36,4 @@ DataSourceSummaryDetailsPanel.unallocatedSizeValue.text= DataSourceSummaryCountsPanel.byMimeTypeLabel.text=Files by MIME Type DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category DataSourceSummaryCountsPanel.jLabel1.text=Results by Type +DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED index eb7affe9ee..4682d7d76f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle.properties-MERGED @@ -72,4 +72,8 @@ DataSourceSummaryNode.column.status.header=Ingest Status DataSourceSummaryNode.column.tags.header=Tags DataSourceSummaryNode.column.type.header=Type DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source +DataSourceSummaryUserActivityPanel.programsRunLabel.text=Top Programs Run +DataSourceSummaryUserActivityPanel_tab_title=User Activity +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times +DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties index 99fd24c7ef..a4e60c4f36 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/Bundle_ja.properties @@ -71,3 +71,4 @@ DataSourceSummaryNode.column.tags.header=\u30bf\u30b0 DataSourceSummaryNode.column.type.header=\u30bf\u30a4\u30d7 DataSourceSummaryNode.viewDataSourceAction.text=\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u306b\u79fb\u52d5 ViewSummaryInformationAction.name.text=\u30b5\u30de\u30ea\u30fc\u60c5\u5831\u3092\u8868\u793a +DataSourceSummaryUserActivityPanel.programsRunLabel.text=\u30bf\u30a4\u30d7\u5225\u7d50\u679c diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java index 662f3739f0..da991e26fc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceInfoUtilities.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2019 Basis Technology Corp. + * Copyright 2019 - 2020 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.casemodule.datasourcesummary; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Level; @@ -29,6 +28,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Set; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; @@ -194,6 +194,33 @@ final class DataSourceInfoUtilities { return getBaseQueryResult(query, handler, errorMessage); } + /** + * Generates a result set handler that will return a map of string to long. + * + * @param keyParam The named parameter in the result set representing the + * key. + * @param valueParam The named parameter in the result set representing the + * value. + * + * @return The result set handler to generate the map of string to long. + */ + private static ResultSetHandler> getStringLongResultSetHandler(String keyParam, String valueParam) { + ResultSetHandler> handler = (resultSet) -> { + LinkedHashMap toRet = new LinkedHashMap<>(); + while (resultSet.next()) { + try { + toRet.put(resultSet.getString(keyParam), resultSet.getLong(valueParam)); + } catch (SQLException ex) { + logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex); + } + } + + return toRet; + }; + + return handler; + } + /** * Retrieves counts for each artifact type in a data source. * @@ -216,22 +243,50 @@ final class DataSourceInfoUtilities { + " WHERE bba.data_source_obj_id =" + selectedDataSource.getId() + " GROUP BY bbt.display_name"; - ResultSetHandler> handler = (resultSet) -> { - Map toRet = new HashMap<>(); - while (resultSet.next()) { - try { - toRet.put(resultSet.getString(nameParam), resultSet.getLong(valueParam)); - } catch (SQLException ex) { - logger.log(Level.WARNING, "Failed to get a result pair from the result set.", ex); - } - } - - return toRet; - }; - String errorMessage = "Unable to get artifact type counts; returning null."; + return getBaseQueryResult(query, getStringLongResultSetHandler(nameParam, valueParam), errorMessage); + } - return getBaseQueryResult(query, handler, errorMessage); + /** + * Retrieves a list of the top programs used on the data source. Currently + * determines this based off of which prefetch results return the highest + * count. + * + * @param dataSource The data source. + * @param count The number of programs to return. + * + * @return + */ + static Map getTopPrograms(DataSource dataSource, int count) { + if (dataSource == null || count <= 0) { + return Collections.emptyMap(); + } + + String progNameParam = "prog_name"; + String runTimesParam = "run_times"; + String prefetchIdentifier = "Windows Prefetch Extractor"; + + String query = "SELECT program_artifacts." + progNameParam + ", MAX(attr.value_int32) AS " + runTimesParam + "\n" + + "FROM blackboard_artifacts bba\n" + + "INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" + + "INNER JOIN (\n" + + "-- get all the different artifacts coming from prefetch\n" + + " SELECT \n" + + " bba.artifact_id as artifact_id, \n" + + " attr.value_text as " + progNameParam + "\n" + + " FROM blackboard_artifacts bba\n" + + " INNER JOIN blackboard_attributes attr ON bba.artifact_id = attr.artifact_id\n" + + " WHERE bba.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_PROG_RUN.getTypeID() + "\n" + + " AND attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID() + "\n" + + " AND attr.source = '" + prefetchIdentifier + "'\n" + + ") program_artifacts ON bba.artifact_id = program_artifacts.artifact_id\n" + + "WHERE attr.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COUNT.getTypeID() + "\n" + + "GROUP BY program_artifacts." + progNameParam + "\n" + + "ORDER BY " + runTimesParam + " DESC\n" + + "LIMIT " + count + " OFFSET 0"; + + String errorMessage = "Unable to get top program counts; returning null."; + return getBaseQueryResult(query, getStringLongResultSetHandler(progNameParam, runTimesParam), errorMessage); } /** diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java index 8ef4d62c5d..78dc38190f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryTabbedPane.java @@ -33,6 +33,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { private static final long serialVersionUID = 1L; private final DataSourceSummaryCountsPanel countsPanel; private final DataSourceSummaryDetailsPanel detailsPanel; + private final DataSourceSummaryUserActivityPanel userActivityPanel; // ingest panel requires an open case in order to properly initialize. // So it will be instantiated when a data source is selected. @@ -46,6 +47,7 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { public DataSourceSummaryTabbedPane() { countsPanel = new DataSourceSummaryCountsPanel(); detailsPanel = new DataSourceSummaryDetailsPanel(); + userActivityPanel = new DataSourceSummaryUserActivityPanel(); } /** @@ -63,6 +65,9 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { addTab(Bundle.DataSourceSummaryDialog_countsTab_title(), countsPanel); countsPanel.setDataSource(dataSource); + addTab(Bundle.DataSourceSummaryUserActivityPanel_tab_title(), userActivityPanel); + userActivityPanel.setDataSource(dataSource); + if (ingestHistoryPanel == null) { ingestHistoryPanel = new IngestJobInfoPanel(); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index 823399e0d0..56a675e256 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -205,9 +205,7 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}... DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.startMessage=Starting deletion... DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes -# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0} -# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0} DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service # {0} - node path From 5c9f1a8c9f609ca12c9508a83eadf73844d61f50 Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 10:56:36 -0400 Subject: [PATCH 11/35] beginnings of top program items --- .../DataSourceSummaryUserActivityPanel.form | 80 +++++++++ .../DataSourceSummaryUserActivityPanel.java | 166 ++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form new file mode 100644 index 0000000000..02cda9a4b3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -0,0 +1,80 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java new file mode 100644 index 0000000000..f556ebc7f3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -0,0 +1,166 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2020 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.casemodule.datasourcesummary; + +import java.util.Map; +import javax.swing.JLabel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.datamodel.DataSource; + +/** + * A panel to display user activity. + */ +@Messages({ + "DataSourceSummaryUserActivityPanel_tab_title=User Activity", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program", + "DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times" +}) +public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { + // Result returned for a data model if no data found. + private static final Object[][] EMPTY_PAIRS = new Object[][]{}; + private static final int TOP_PROGS_COUNT = 10; + + private static final DefaultTableCellRenderer RIGHT_ALIGNED_RENDERER = new DefaultTableCellRenderer(); + + static { + RIGHT_ALIGNED_RENDERER.setHorizontalAlignment(JLabel.RIGHT); + } + + // column headers for artifact counts table + private static final Object[] TOP_PROGS_COLUMN_HEADERS = new Object[]{ + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header(), + Bundle.DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header() + }; + + private DataSource dataSource; + + /** + * Creates new form DataSourceUserActivityPanel + */ + public DataSourceSummaryUserActivityPanel() { + initComponents(); + } + + /** + * The datasource currently used as the model in this panel. + * + * @return The datasource currently being used as the model in this panel. + */ + public DataSource getDataSource() { + return dataSource; + } + + /** + * Sets datasource to visualize in the panel. + * + * @param dataSource The datasource to use in this panel. + */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + if (dataSource == null || !Case.isCaseOpen()) { + updateTopPrograms(EMPTY_PAIRS); + } else { + updateTopPrograms(getTopProgramsModel(dataSource)); + } + } + + /** + * Updates the Top Programs Table in the gui. + * @param data The data in Object[][] form to be used by the DefaultTableModel. + */ + private void updateTopPrograms(Object[][] data) { + topProgramsTable.setModel(new DefaultTableModel(data, TOP_PROGS_COLUMN_HEADERS)); + topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); + topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); + this.repaint(); + } + + /** + * The counts of top programs run. + * + * @param selectedDataSource The DataSource. + * + * @return The JTable data model of counts of program runs. + */ + private static Object[][] getTopProgramsModel(DataSource selectedDataSource) { + Map artifactMapping = DataSourceInfoUtilities.getTopPrograms(selectedDataSource, TOP_PROGS_COUNT); + if (artifactMapping == null) { + return EMPTY_PAIRS; + } + + return artifactMapping.entrySet().stream() + .filter((entrySet) -> entrySet != null && entrySet.getKey() != null) + .sorted((a, b) -> -Long.compare(a.getValue(), b.getValue())) + .map((entrySet) -> new Object[]{entrySet.getKey(), entrySet.getValue()}) + .toArray(Object[][]::new); + } + + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + javax.swing.JLabel programsRunLabel = new javax.swing.JLabel(); + javax.swing.JScrollPane topProgramsScrollPane = new javax.swing.JScrollPane(); + topProgramsTable = new javax.swing.JTable(); + + setMinimumSize(new java.awt.Dimension(256, 300)); + + org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N + + topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(290, 187)); + + topProgramsTable.setAutoCreateRowSorter(true); + topProgramsScrollPane.setViewportView(topProgramsTable); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(programsRunLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addContainerGap(47, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(programsRunLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(topProgramsScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JTable topProgramsTable; + // End of variables declaration//GEN-END:variables +} From 11fc7e24d2d0b8151111420190eeeeca8ea1074b Mon Sep 17 00:00:00 2001 From: Greg DiCristofaro Date: Thu, 30 Jul 2020 11:46:44 -0400 Subject: [PATCH 12/35] worked through some table editing issues --- .../DataSourceSummaryUserActivityPanel.form | 3 --- .../DataSourceSummaryUserActivityPanel.java | 5 ++--- .../communications/relationships/Bundle.properties-MERGED | 2 +- .../core.jar/org/netbeans/core/startup/Bundle.properties | 2 +- .../org/netbeans/core/windows/view/ui/Bundle.properties | 2 +- 5 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form index 02cda9a4b3..0ad3ca0349 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.form @@ -70,9 +70,6 @@ - - - diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java index f556ebc7f3..696e6e904b 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/datasourcesummary/DataSourceSummaryUserActivityPanel.java @@ -58,6 +58,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { */ public DataSourceSummaryUserActivityPanel() { initComponents(); + topProgramsTable.getTableHeader().setReorderingAllowed(false); } /** @@ -88,7 +89,7 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { * @param data The data in Object[][] form to be used by the DefaultTableModel. */ private void updateTopPrograms(Object[][] data) { - topProgramsTable.setModel(new DefaultTableModel(data, TOP_PROGS_COLUMN_HEADERS)); + topProgramsTable.setModel(new NonEditableTableModel(data, TOP_PROGS_COLUMN_HEADERS)); topProgramsTable.getColumnModel().getColumn(0).setPreferredWidth(230); topProgramsTable.getColumnModel().getColumn(1).setCellRenderer(RIGHT_ALIGNED_RENDERER); this.repaint(); @@ -133,8 +134,6 @@ public class DataSourceSummaryUserActivityPanel extends javax.swing.JPanel { org.openide.awt.Mnemonics.setLocalizedText(programsRunLabel, org.openide.util.NbBundle.getMessage(DataSourceSummaryUserActivityPanel.class, "DataSourceSummaryUserActivityPanel.programsRunLabel.text")); // NOI18N topProgramsScrollPane.setPreferredSize(new java.awt.Dimension(290, 187)); - - topProgramsTable.setAutoCreateRowSorter(true); topProgramsScrollPane.setViewportView(topProgramsTable); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 6778d8dc53..616408978d 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -73,5 +73,5 @@ SummaryViewer.referencesLabel.text=Communication References: SummaryViewer.referencesDataLabel.text= SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= -SummaryViewer.fileRefPane.border.title=File Referernce(s) in Current Case +SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text= diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED index 56a675e256..823399e0d0 100755 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/Bundle.properties-MERGED @@ -205,7 +205,9 @@ DeleteCaseTask.progress.parsingManifest=Parsing manifest file {0}... DeleteCaseTask.progress.releasingManifestLock=Releasing lock on the manifest file {0}... DeleteCaseTask.progress.startMessage=Starting deletion... DeleteOrphanCaseNodesAction.progressDisplayName=Cleanup Case Znodes +# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.lblNodeCount.text=Znodes found: {0} +# {0} - item count DeleteOrphanCaseNodesDialog.additionalInit.znodesTextArea.countMessage=ZNODES FOUND: {0} DeleteOrphanCaseNodesTask.progress.connectingToCoordSvc=Connecting to the coordination service # {0} - node path diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties index 333baeeca2..35138509d8 100644 --- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties +++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties @@ -1,5 +1,5 @@ #Updated by build script -#Thu, 30 Jul 2020 11:24:48 -0400 +#Wed, 08 Jul 2020 15:15:46 -0400 LBL_splash_window_title=Starting Autopsy SPLASH_HEIGHT=314 SPLASH_WIDTH=538 diff --git a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties index 044fad190f..cf36e85b33 100644 --- a/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties +++ b/branding/modules/org-netbeans-core-windows.jar/org/netbeans/core/windows/view/ui/Bundle.properties @@ -1,4 +1,4 @@ #Updated by build script -#Thu, 30 Jul 2020 11:24:48 -0400 +#Wed, 08 Jul 2020 15:15:46 -0400 CTL_MainWindow_Title=Autopsy 4.16.0 CTL_MainWindow_Title_No_Project=Autopsy 4.16.0 From 4f0ceb4e7783852f204b996b3d7f39a19bb12d95 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Mon, 3 Aug 2020 10:37:57 -0400 Subject: [PATCH 14/35] inital pass --- .../AccountDeviceInstanceNode.java | 25 +++++++++++++++++-- .../relationships/Bundle.properties-MERGED | 2 +- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 4a50969ef3..24d190bf2d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -35,8 +35,12 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.BlackboardAttribute; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.TskCoreException; /** * Node to represent an Account Device Instance in the CVT @@ -49,6 +53,8 @@ final class AccountDeviceInstanceNode extends AbstractNode { private final CommunicationsManager commsManager; private final Account account; + private static final BlackboardAttribute.Type NAME_ATTRIBUTE = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(TSK_NAME.getTypeID())); + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); this.accountDeviceInstanceKey = accountDeviceInstanceKey; @@ -122,15 +128,17 @@ final class AccountDeviceInstanceNode extends AbstractNode { @Override public String getShortDescription() { List personaList; + List contactArtifactList; try { personaList = CVTPersonaCache.getPersonaAccounts(account); + contactArtifactList = ContactCache.getContacts(account); } catch (ExecutionException ex) { logger.log(Level.WARNING, "Failed to retrieve Persona details for node.", ex); return getDisplayName(); } String personaName; - if (!personaList.isEmpty()) { + if (personaList != null && !personaList.isEmpty()) { personaName = personaList.get(0).getPersona().getName(); if (personaList.size() > 1) { personaName += Bundle.AccountInstanceNode_Tooltip_suffix(Integer.toString(personaList.size())); @@ -139,6 +147,19 @@ final class AccountDeviceInstanceNode extends AbstractNode { personaName = "None"; } - return Bundle.AccountInstanceNode_Tooltip_Template(getDisplayName(), personaName); + String contactName = getDisplayName(); + if (contactArtifactList != null && !contactArtifactList.isEmpty()) { + try { + BlackboardArtifact contactArtifact = contactArtifactList.get(0); + BlackboardAttribute attribute = contactArtifact.getAttribute(NAME_ATTRIBUTE); + if (attribute != null) { + contactName = attribute.getValueString(); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Failed to retrive name attribute from contact artifact.", ex); + } + } + + return Bundle.AccountInstanceNode_Tooltip_Template(contactName, personaName); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED index 6778d8dc53..616408978d 100755 --- a/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/communications/relationships/Bundle.properties-MERGED @@ -73,5 +73,5 @@ SummaryViewer.referencesLabel.text=Communication References: SummaryViewer.referencesDataLabel.text= SummaryViewer.contactsLabel.text=Book Entries: SummaryViewer.accountCountry.text= -SummaryViewer.fileRefPane.border.title=File Referernce(s) in Current Case +SummaryViewer.fileRefPane.border.title=File References in Current Case SummaryViewer.selectAccountFileRefLabel.text=