diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java index 63b255113b..f98e8264ac 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java @@ -31,7 +31,6 @@ import javax.swing.JPanel; import javax.swing.filechooser.FileFilter; import org.apache.commons.io.FilenameUtils; import org.openide.modules.InstalledFileLocator; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; @@ -52,8 +51,8 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; */ @ServiceProviders(value = { @ServiceProvider(service = DataSourceProcessor.class), - @ServiceProvider(service = AutoIngestDataSourceProcessor.class)} -) + @ServiceProvider(service = AutoIngestDataSourceProcessor.class) +}) @Messages({ "LocalFilesDSProcessor.logicalEvidenceFilter.desc=Logical Evidence Files (L01)" }) @@ -344,15 +343,15 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat // It should return lowest possible non-zero confidence level and be treated // as the "option of last resort" for auto ingest purposes - this.localFilePaths = Arrays.asList(new String[]{dataSourcePath.toString()}); + List filePaths = Arrays.asList(new String[]{dataSourcePath.toString()}); //If there is only 1 file check if it is an L01 file and if it is extract the //contents and replace the paths, if the contents can't be extracted return 0 - if (localFilePaths.size() == 1) { - for (final String path : localFilePaths) { + if (filePaths.size() == 1) { + for (final String path : filePaths) { if (new File(path).isFile() && LOGICAL_EVIDENCE_FILTER.accept(new File(path))) { try { //if the L01 option was chosen - localFilePaths = extractLogicalEvidenceFileContents(localFilePaths); + filePaths = extractLogicalEvidenceFileContents(filePaths); } catch (L01Exception ex) { logger.log(Level.WARNING, "File extension was .l01 but contents of logical evidence file were unable to be extracted", ex); //contents of l01 could not be extracted don't add data source or run ingest @@ -369,7 +368,8 @@ public class LocalFilesDSProcessor implements DataSourceProcessor, AutoIngestDat @Override public void process(String deviceId, Path dataSourcePath, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callBack) { - run(deviceId, deviceId, this.localFilePaths, progressMonitor, callBack); + List filePaths = Arrays.asList(new String[]{dataSourcePath.toString()}); + run(deviceId, deviceId, filePaths, progressMonitor, callBack); } /** diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 53f99e5ff8..a6e078f565 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -177,9 +177,15 @@ public abstract class AbstractSqlEamDb implements EamDb { * @returns New Case class with populated database ID */ @Override - public CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { - Connection conn = connect(); + public synchronized CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { + + // check if there is already an existing CorrelationCase for this Case + CorrelationCase cRCase = getCaseByUUID(eamCase.getCaseUUID()); + if (cRCase != null) { + return cRCase; + } + Connection conn = connect(); PreparedStatement preparedStatement = null; String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, " diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 7561e3fa75..ce2721e647 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -503,7 +503,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont rtfbodyTextPane.setText(""); htmlbodyTextPane.setText(""); textbodyTextArea.setText(""); - drp.setNode(null); showImagesToggleButton.setEnabled(false); msgbodyTabbedPane.setEnabled(false); } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 535c88ce61..245fe1744d 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -24,7 +24,7 @@ DataContentViewerString.pageLabel2.text=Page # Product Information panel LBL_Description=
\n Product Version: {0} ({9})
Sleuth Kit Version: {7}
Netbeans RCP Build: {8}
Java: {1}; {2}
System: {3}; {4}; {5}
Userdir: {6}
Format_OperatingSystem_Value={0} version {1} running on {2} -LBL_Copyright=
Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools.
Copyright © 2003-2017.
+LBL_Copyright=
Autopsy™ is a digital forensics platform based on The Sleuth Kit™ and other tools.
Copyright © 2003-2018.
URL_ON_IMG=http://www.sleuthkit.org/ URL_ON_HELP=http://sleuthkit.org/autopsy/docs/user-docs/4.6.0/ FILE_FOR_LOCAL_HELP=file:/// diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java index b5aef4ad0b..6a6d70677f 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java @@ -72,8 +72,8 @@ public final class IngestJobSettingsPanel extends javax.swing.JPanel { private static final Logger logger = Logger.getLogger(IngestJobSettingsPanel.class.getName()); /** - * Construct a panel to allow a user to make ingest job settings. - * This constructor assumes there is no ingest history. + * Construct a panel to allow a user to make ingest job settings. This + * constructor assumes there is no ingest history. * * @param settings The initial settings for the ingest job. */ @@ -88,8 +88,8 @@ public final class IngestJobSettingsPanel extends javax.swing.JPanel { } /** - * Construct a panel to allow a user to make ingest job settings. - * This constructor enables tracking of ingest job history. + * Construct a panel to allow a user to make ingest job settings. This + * constructor enables tracking of ingest job history. * * @param settings The initial settings for the ingest job. * @param dataSources The data sources ingest is being run on. @@ -423,16 +423,20 @@ public final class IngestJobSettingsPanel extends javax.swing.JPanel { } catch (FilesSetsManager.FilesSetsManagerException ex) { logger.log(Level.SEVERE, "Failed to get user created file ingest filters, only default available for selection", ex); //NON-NLS } + String filterToSelect = settings.getFileFilter().getName(); for (FilesSet filter : newFilterList) { //getting one of the recently created filters if (!oldFilterList.contains(filter.getName())) { //set newly created filter to selected filter - settings.setFileFilter(filter); + filterToSelect = filter.getName(); break; } } fileIngestFilterComboBox.setModel(new DefaultComboBoxModel<>(getComboBoxContents())); //set the selected filter after the comboBox Contents were updated to include it - fileIngestFilterComboBox.setSelectedItem(settings.getFileFilter().getName()); + fileIngestFilterComboBox.setSelectedItem(filterToSelect); + //refresh the saved filter in use case where the selected modified filter + //has the same name as a previously existing filter + updateSelectedFilter(filterToSelect); dialog.close(); } ); @@ -440,21 +444,25 @@ public final class IngestJobSettingsPanel extends javax.swing.JPanel { //return to saved selection in case they cancel out of filter creation fileIngestFilterComboBox.setSelectedItem(settings.getFileFilter().getName()); } else if (evt.getActionCommand().equals("comboBoxChanged")) { - try { - Map fileIngestFilters = FilesSetsManager.getInstance() - .getCustomFileIngestFilters(); - for (FilesSet fSet : FilesSetsManager.getStandardFileIngestFilters()) { - fileIngestFilters.put(fSet.getName(), fSet); - } - settings.setFileFilter(fileIngestFilters - .get(fileIngestFilterComboBox.getSelectedItem().toString())); - } catch (FilesSetsManager.FilesSetsManagerException ex) { - settings.setFileFilter(FilesSetsManager.getDefaultFilter()); - logger.log(Level.SEVERE, "Failed to get file ingest filter from combobox selection, default filter being used", ex); //NON-NLS - } + updateSelectedFilter(fileIngestFilterComboBox.getSelectedItem().toString()); } }//GEN-LAST:event_fileIngestFilterComboBoxActionPerformed + private void updateSelectedFilter(String filterName) { + try { + Map fileIngestFilters = FilesSetsManager.getInstance() + .getCustomFileIngestFilters(); + for (FilesSet fSet : FilesSetsManager.getStandardFileIngestFilters()) { + fileIngestFilters.put(fSet.getName(), fSet); + } + settings.setFileFilter(fileIngestFilters + .get(filterName)); + } catch (FilesSetsManager.FilesSetsManagerException ex) { + settings.setFileFilter(FilesSetsManager.getDefaultFilter()); + logger.log(Level.SEVERE, "Failed to get file ingest filter from combobox selection, default filter being used", ex); //NON-NLS + } + } + /** * Returns an array which will contain the names of all options which should * exist in the "Run Ingest Modules On:" JCombobox diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java index 54b7024790..972c3488e8 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java @@ -44,6 +44,8 @@ import org.sleuthkit.autopsy.ingest.IngestJobSettings.IngestType; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeIdModuleFactory; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule; +import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.ExtensionCondition; +import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.FullNameCondition; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.MetaTypeCondition; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.ParentPathCondition; import org.sleuthkit.autopsy.testutils.DataSourceProcessorRunner; @@ -147,8 +149,8 @@ public class IngestFileFiltersTest extends NbTestCase { //All files in dir1 should have MIME type, except '.' '..' and slack files if (file.getParentPath().equalsIgnoreCase("/dir1/")) { if (!(file.getName().equals(".") || file.getName().equals("..") || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) { - String errMsg = String.format("File %s (objId=%d) unexpectedly blocked by the file filter.", file.getName(), file.getId()); - assertTrue(errMsg, !(file.getMIMEType() == null || file.getMIMEType().isEmpty())); + String errMsg = String.format("File %s (objId=%d) unexpectedly passed by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() != null && !file.getMIMEType().isEmpty()); } } else { //All files not in dir1 shouldn't have MIME type String errMsg = String.format("File %s (objId=%d) unexpectedly passed by the file filter.", file.getName(), file.getId()); @@ -161,6 +163,98 @@ public class IngestFileFiltersTest extends NbTestCase { } } + public void testExtAndDirWithOneRule() { + HashMap rules = new HashMap<>(); + rules.put("Rule", new Rule("testExtAndDirWithOneRule", new ExtensionCondition("jpg"), new MetaTypeCondition(MetaTypeCondition.Type.FILES), new ParentPathCondition("dir1"), null, null, null)); + //Build the filter that ignore unallocated space and with one rule + FilesSet filesExtDirsFilter = new FilesSet("Filter", "Filter to find all jpg files in dir1.", false, true, rules); + + try { + Case openCase = Case.getOpenCase(); + runIngestJob(openCase.getDataSources(), filesExtDirsFilter); + FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); + List results = fileManager.findFiles("%%"); + assertEquals(62, results.size()); + for (AbstractFile file : results) { + //Files with jpg extension in dir1 should have MIME Type + if (file.getParentPath().equalsIgnoreCase("/dir1/") && file.getNameExtension().equalsIgnoreCase("jpg")) { + String errMsg = String.format("File %s (objId=%d) unexpectedly blocked by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() != null && !file.getMIMEType().isEmpty()); + } else { //All others shouldn't have MIME Type + String errMsg = String.format("File %s (objId=%d) unexpectedly passed by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() == null); + } + } + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + public void testExtAndDirWithTwoRules() { + HashMap rules = new HashMap<>(); + rules.put("rule1", new Rule("FindJpgExtention", new ExtensionCondition("jpg"), new MetaTypeCondition(MetaTypeCondition.Type.FILES), null, null, null, null)); + rules.put("rule2", new Rule("FindDir1Directory", null, new MetaTypeCondition(MetaTypeCondition.Type.FILES), new ParentPathCondition("dir1"), null, null, null)); + //Build the filter that ingnore unallocated space and with 2 rules + FilesSet filesExtDirsFilter = new FilesSet("Filter", "Filter to find all files in dir1 and all files with jpg extention.", false, true, rules); + + try { + Case openCase = Case.getOpenCase(); + runIngestJob(openCase.getDataSources(), filesExtDirsFilter); + FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); + List results = fileManager.findFiles("%%"); + assertEquals(62, results.size()); + for (AbstractFile file : results) { + if (file.getNameExtension().equalsIgnoreCase("jpg")) { //All files with .jpg extension should have MIME type + String errMsg = String.format("File %s (objId=%d) unexpectedly blocked by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() != null && !file.getMIMEType().isEmpty()); + } else if (file.getParentPath().equalsIgnoreCase("/dir1/")) { + //All files in dir1 should have MIME type except '.' '..' slack files + if (file.getName().equals(".") || file.getName().equals("..") || file.getType() == TskData.TSK_DB_FILES_TYPE_ENUM.SLACK) { + String errMsg = String.format("File %s (objId=%d) unexpectedly passed by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() == null); + } else { + String errMsg = String.format("File %s (objId=%d) unexpectedly blocked by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() != null && !file.getMIMEType().isEmpty()); + } + } else { //All files that are not in dir1 or not with .jpg extension should not have MIME type + String errMsg = String.format("File %s (objId=%d) unexpectedly passed by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() == null); + } + } + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } + + public void testFullFileNameRule() { + HashMap rules = new HashMap<>(); + rules.put("rule", new Rule("FindFileWithFullName", new FullNameCondition("file.docx"), new MetaTypeCondition(MetaTypeCondition.Type.FILES), null, null, null, null)); + //Build the filter to find file: file.docx + FilesSet fullNameFilter = new FilesSet("Filter", "Filter to find file.docx.", false, true, rules); + + try { + Case openCase = Case.getOpenCase(); + runIngestJob(openCase.getDataSources(), fullNameFilter); + FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); + List results = fileManager.findFiles("%%"); + assertEquals(62, results.size()); + for (AbstractFile file : results) { + //Only file.docx has MIME Type + if (file.getName().equalsIgnoreCase("file.docx")) { + String errMsg = String.format("File %s (objId=%d) unexpectedly blocked by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() != null && !file.getMIMEType().isEmpty()); + } else { + String errMsg = String.format("File %s (objId=%d) unexpectedly passed by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() == null); + } + } + } catch (NoCurrentCaseException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + Assert.fail(ex); + } + } private void runIngestJob(List datasources, FilesSet filter) { FileTypeIdModuleFactory factory = new FileTypeIdModuleFactory(); IngestModuleIngestJobSettings settings = factory.getDefaultIngestJobSettings(); @@ -171,6 +265,9 @@ public class IngestFileFiltersTest extends NbTestCase { IngestJobSettings ingestJobSettings = new IngestJobSettings(IngestFileFiltersTest.class.getCanonicalName(), IngestType.FILES_ONLY, templates, filter); try { List errs = IngestJobRunner.runIngestJob(datasources, ingestJobSettings); + for (IngestModuleError err : errs) { + System.out.println(String.format("Error: %s: %s.", err.getModuleDisplayName(), err.toString())); + } assertEquals(0, errs.size()); } catch (InterruptedException ex) { Exceptions.printStackTrace(ex); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java index 57d14452b5..7490c3e7b1 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * - * Copyright 2011-2016 Basis Technology Corp. + * + * Copyright 2018 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. @@ -23,80 +23,89 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.Content; /* - * A runnable that adds a raw data source to a case database. + * A runnable that adds a memory image data source to a case database. */ final class AddMemoryImageTask implements Runnable { - private static final Logger logger = Logger.getLogger(AddMemoryImageTask.class.getName()); + private final static Logger logger = Logger.getLogger(AddMemoryImageTask.class.getName()); private final String deviceId; - private final String imageFilePath; + private final String memoryImagePath; private final String timeZone; - private final List pluginsToRun; + private final List pluginsToRun; private final DataSourceProcessorProgressMonitor progressMonitor; private final DataSourceProcessorCallback callback; - private VolatilityProcessor volatilityProcessor = null; - private boolean isCancelled = false; - + private volatile VolatilityProcessor volatilityProcessor; + private volatile boolean isCancelled; + /** - * Constructs a runnable that adds a raw data source to a case database. + * Constructs a runnable that adds a memory image to a case database. * - * @param deviceId An ASCII-printable identifier for the - * device associated with the data source - * that is intended to be unique across - * multiple cases (e.g., a UUID). - * @param imageFilePath Path to a Raw data source file. - * @param timeZone The time zone to use when processing dates - * and times for the image, obtained from - * java.util.TimeZone.getID. - * @param breakupChunks 2GB or not breakup. - * @param progressMonitor Progress monitor for reporting - * progressMonitor during processing. - * @param callback Callback to call when processing is done. + * @param deviceId An ASCII-printable identifier for the device + * associated with the data source that is intended + * to be unique across multiple cases (e.g., a UUID). + * @param memoryImagePath Path to the memory image file. + * @param pluginsToRun The Volatility plugins to run. + * @param timeZone The time zone to use when processing dates and + * times for the image, obtained from + * java.util.TimeZone.getID. + * @param progressMonitor Progress monitor for reporting progressMonitor + * during processing. + * @param callback Callback to call when processing is done. */ - AddMemoryImageTask(String deviceId, String imageFilePath, List PluginsToRun, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + AddMemoryImageTask(String deviceId, String memoryImagePath, List pluginsToRun, String timeZone, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { this.deviceId = deviceId; - this.imageFilePath = imageFilePath; - this.pluginsToRun = PluginsToRun; + this.memoryImagePath = memoryImagePath; + this.pluginsToRun = pluginsToRun; this.timeZone = timeZone; this.callback = callback; this.progressMonitor = progressMonitor; } /** - * Adds a raw data source to a case database. + * Adds a memory image data source to a case database. */ - @Override + @Messages({ + "# {0} - exception message", + "AddMemoryImageTask_errorMessage_criticalException= Critical error: {0}", + }) + @Override public void run() { + if (isCancelled) { + return; + } progressMonitor.setIndeterminate(true); progressMonitor.setProgress(0); + List dataSources = new ArrayList<>(); List errorMessages = new ArrayList<>(); boolean criticalErrorOccurred = false; - Image dataSource = addImageToCase(errorMessages); - if (dataSource == null) { + try { + Image dataSource = addImageToCase(); + dataSources.add(dataSource); + volatilityProcessor = new VolatilityProcessor(memoryImagePath, dataSource, pluginsToRun, progressMonitor); + volatilityProcessor.run(); + } catch (NoCurrentCaseException | TskCoreException | VolatilityProcessor.VolatilityProcessorException ex) { criticalErrorOccurred = true; + errorMessages.add(Bundle.AddMemoryImageTask_errorMessage_criticalException(ex.getLocalizedMessage())); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, String.format("Critical error processing memory image data source at %s for device %s", memoryImagePath, deviceId), ex); } - /* call Volatility to process the image */ - else { - if (isCancelled) - return; - - volatilityProcessor = new VolatilityProcessor(imageFilePath, dataSource, pluginsToRun, progressMonitor); - if (volatilityProcessor.run()) { - criticalErrorOccurred = true; - } - errorMessages.addAll(volatilityProcessor.getErrorMessages()); - } - + errorMessages.addAll(volatilityProcessor.getErrorMessages()); progressMonitor.setProgress(100); /** @@ -110,55 +119,64 @@ final class AddMemoryImageTask implements Runnable { } else { result = DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS; } - - callback.done(result, errorMessages, new ArrayList<>(Arrays.asList(dataSource))); + callback.done(result, errorMessages, dataSources); } /** - * Attempts to add the input image to the case. + * Attempts to add the input memory image to the case as a data source. * - * @param errorMessages If there are any error messages, the error messages - * are added to this list for eventual return to the - * caller via the callback. - * @returns Image that was added to DB or null on error + * @return The Image object representation of the memeory image file data + * source. + * + * @throws NoCurrentCaseException If there is no current case. + * @throws TskCoreException If there is an error adding the data + * source to the case database. */ - @Messages({"AddMemoryImageTask.progress.add.text=Adding memory image: ", - "AddMemoryImageTask.image.critical.error.adding=Critical error adding ", - "AddMemoryImageTask.for.device=for device ", - "AddMemoryImageTask.image.notExisting=is not existing.", - "AddMemoryImageTask.image.noncritical.error.adding=Non-critical error adding "}) - private Image addImageToCase(List errorMessages) { - progressMonitor.setProgressText(Bundle.AddMemoryImageTask_progress_add_text() + imageFilePath); - - SleuthkitCase caseDatabase = Case.getCurrentCase().getSleuthkitCase(); - caseDatabase.acquireExclusiveLock(); + @Messages({ + "# {0} - image file path", + "AddMemoryImageTask_progressMessage_addingImageFile= Adding memory image {0}", + "# {0} - image file path", + "# {1} - device id", + "AddMemoryImageTask_exceptionMessage_noImageFile= Memory image file {0} for device {1} does not exist" + }) + private Image addImageToCase() throws NoCurrentCaseException, TskCoreException { + progressMonitor.setProgressText(Bundle.AddMemoryImageTask_progressMessage_addingImageFile( memoryImagePath)); - // verify it exists - File imageFile = Paths.get(imageFilePath).toFile(); - if (!imageFile.exists()) { - errorMessages.add(Bundle.AddMemoryImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddMemoryImageTask_for_device() - + deviceId + Bundle.AddMemoryImageTask_image_notExisting()); - return null; - } - + SleuthkitCase caseDatabase = Case.getOpenCase().getSleuthkitCase(); + caseDatabase.acquireSingleUserCaseWriteLock(); try { - // add it to the DB - List imageFilePaths = new ArrayList<>(); - imageFilePaths.add(imageFilePath); - Image dataSource = caseDatabase.addImageInfo(0, imageFilePaths, timeZone); //TODO: change hard coded deviceId. + /* + * Verify the memory image file exists. + */ + File imageFile = Paths.get(memoryImagePath).toFile(); + if (!imageFile.exists()) { + throw new TskCoreException(Bundle.AddMemoryImageTask_exceptionMessage_noImageFile(memoryImagePath, deviceId)); + } + + /* + * Add the data source. + * + * NOTE: The object id for device passed to + * SleuthkitCase.addImageInfo is hard-coded to zero for now. This + * will need to be changed when a Device abstraction is added to the + * SleuthKit data model. + */ + Image dataSource = caseDatabase.addImageInfo(0, new ArrayList<>(Arrays.asList(memoryImagePath)), timeZone); return dataSource; - } catch (TskCoreException ex) { - errorMessages.add(Bundle.AddMemoryImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddMemoryImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage()); - return null; + } finally { - caseDatabase.releaseExclusiveLock(); - } + caseDatabase.releaseSingleUserCaseWriteLock(); + } } + /** + * Requests cancellation of this task by setting a cancelled flag. + */ void cancelTask() { isCancelled = true; if (volatilityProcessor != null) { volatilityProcessor.cancel(); } } + } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSInputPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSInputPanel.java index b2a893964a..f96a9cc801 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSInputPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSInputPanel.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * - * Copyright 2011-2016 Basis Technology Corp. + * + * Copyright 2018 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. @@ -37,11 +37,13 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.PathValidator; final class MemoryDSInputPanel extends JPanel implements DocumentListener { + private static final long serialVersionUID = 1L; //default private final String PROP_LASTINPUT_PATH = "LBL_LastInputFile_PATH"; private final JFileChooser fc = new JFileChooser(); @@ -52,14 +54,14 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { private final List PluginListNames = new ArrayList<>(); private final Map pluginListStates = new HashMap<>(); // is set by listeners when users select and deselect items private final Boolean isEnabled = true; - + /** - * Creates new MemoryDSInputPanel panel for user input + * Creates new MemoryDSInputPanel panel for user input */ private MemoryDSInputPanel(String context) { - this.pluginList = new String[]{"amcache","cmdline","cmdscan","consoles","malfind","netscan","notepad","pslist","psxview","shellbags","shimcache","shutdown","userassist", "apihooks","connscan","devicetree","dlllist","envars","filescan","gahti","getservicesids","getsids","handles","hashdump","hivelist","hivescan","impscan","ldrmodules","lsadump","modules","mutantscan","privs","psscan","pstree","sockets","svcscan","shimcache","timeliner","unloadedmodules","userhandles","vadinfo","verinfo"}; + this.pluginList = new String[]{"amcache", "cmdline", "cmdscan", "consoles", "malfind", "netscan", "notepad", "pslist", "psxview", "shellbags", "shimcache", "shutdown", "userassist", "apihooks", "connscan", "devicetree", "dlllist", "envars", "filescan", "gahti", "getservicesids", "getsids", "handles", "hashdump", "hivelist", "hivescan", "impscan", "ldrmodules", "lsadump", "modules", "mutantscan", "privs", "psscan", "pstree", "sockets", "svcscan", "shimcache", "timeliner", "unloadedmodules", "userhandles", "vadinfo", "verinfo"}; Arrays.sort(this.pluginList); - + initComponents(); errorLabel.setVisible(false); @@ -91,7 +93,7 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { private void postInit() { pathTextField.getDocument().addDocumentListener(this); } - + private void customizePluginListTable() { PluginList.setModel(tableModel); PluginList.setTableHeader(null); @@ -135,14 +137,14 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { // set the selected timezone timeZoneComboBox.setSelectedItem(formatted); } - + private void createVolatilityVersionList() { - + volExecutableComboBox.addItem("2.6"); volExecutableComboBox.addItem("2.5"); - + } - + private void createPluginList() { PluginListNames.clear(); pluginListStates.clear(); @@ -150,19 +152,19 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { // if the config file doesn't exist, then set them all to enabled boolean allEnabled = !ModuleSettings.configExists(this.contextName); Map pluginMap = ModuleSettings.getConfigSettings(this.contextName); - + for (String plugin : pluginList) { PluginListNames.add(plugin); - if (allEnabled) + if (allEnabled) { pluginListStates.put(plugin, true); - else + } else { pluginListStates.put(plugin, pluginMap.containsKey(plugin)); + } } tableModel.fireTableDataChanged(); //this.tableModel = pluginsToRun.getModel(); } - /** * 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 @@ -282,18 +284,18 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { }// //GEN-END:initComponents @SuppressWarnings("deprecation") private void browseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseButtonActionPerformed - String oldText = pathTextField.getText(); - // set the current directory of the FileChooser if the ImagePath Field is valid - File currentDir = new File(oldText); - if (currentDir.exists()) { - fc.setCurrentDirectory(currentDir); - } + String oldText = pathTextField.getText(); + // set the current directory of the FileChooser if the ImagePath Field is valid + File currentDir = new File(oldText); + if (currentDir.exists()) { + fc.setCurrentDirectory(currentDir); + } - int retval = fc.showOpenDialog(this); - if (retval == JFileChooser.APPROVE_OPTION) { - String path = fc.getSelectedFile().getPath(); - pathTextField.setText(path); - } + int retval = fc.showOpenDialog(this); + if (retval == JFileChooser.APPROVE_OPTION) { + String path = fc.getSelectedFile().getPath(); + pathTextField.setText(path); + } }//GEN-LAST:event_browseButtonActionPerformed private void volExecutableComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_volExecutableComboBoxActionPerformed @@ -322,7 +324,7 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { String getImageFilePath() { return pathTextField.getText(); } - + List getPluginsToRun() { List enabledPlugins = new ArrayList<>(); Map pluginMap = new HashMap<>(); @@ -332,7 +334,7 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { pluginMap.put(plugin, ""); } } - + ModuleSettings.setConfigSettings(this.contextName, pluginMap); // @@ Could return keys of set return enabledPlugins; @@ -374,11 +376,19 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { * * @param path Absolute path to the selected data source */ - @Messages({"MemoryDSInputPanel.error.text=Path to multi-user data source is on \"C:\" drive"}) + @Messages({ + "MemoryDSInputPanel_errorMsg_noOpenCase=No open case", + "MemoryDSInputPanel_errorMsg_dataSourcePathOnCdrive=Path to multi-user data source is on \"C:\" drive" + }) private void warnIfPathIsInvalid(String path) { - if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + try { + if (!PathValidator.isValid(path, Case.getOpenCase().getCaseType())) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.MemoryDSInputPanel_errorMsg_dataSourcePathOnCdrive()); + } + } catch (NoCurrentCaseException unused) { errorLabel.setVisible(true); - errorLabel.setText(Bundle.MemoryDSInputPanel_error_text()); + errorLabel.setText(Bundle.MemoryDSInputPanel_errorMsg_dataSourcePathOnCdrive()); } } @@ -470,5 +480,4 @@ final class MemoryDSInputPanel extends JPanel implements DocumentListener { } } - } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSProcessor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSProcessor.java index cfe2978fe0..9791ad1f09 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSProcessor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/MemoryDSProcessor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,21 +28,21 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; /** - * A MEmory data source processor that implements the DataSourceProcessor service - * provider interface to allow integration with the add data source wizard. It - * also provides a run method overload to allow it to be used independently of - * the wizard. + * A memory image data source processor that implements the DataSourceProcessor + * service provider interface to allow integration with the Add Data Source + * wizard. It also provides a run method overload to allow it to be used + * independently of the wizard. */ @ServiceProvider(service = DataSourceProcessor.class) public class MemoryDSProcessor implements DataSourceProcessor { private final MemoryDSInputPanel configPanel; - private AddMemoryImageTask addImageTask = null; + private AddMemoryImageTask addImageTask; /* - * Constructs a Memory data source processor that implements the + * Constructs a memory data source processor that implements the * DataSourceProcessor service provider interface to allow integration with - * the add data source wizard. It also provides a run method overload to + * the Add Data source wizard. It also provides a run method overload to * allow it to be used independently of the wizard. */ public MemoryDSProcessor() { @@ -117,37 +117,40 @@ public class MemoryDSProcessor implements DataSourceProcessor { @Override public void run(DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { configPanel.storeSettings(); - run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getPluginsToRun(), configPanel.getTimeZone(), 0, progressMonitor, callback); + run(UUID.randomUUID().toString(), configPanel.getImageFilePath(), configPanel.getPluginsToRun(), configPanel.getTimeZone(), progressMonitor, callback); } /** - * Adds a "memory" data source to the case database using a background task in - * a separate thread and the given settings instead of those provided by the - * selection and configuration panel. Returns as soon as the background task - * is started and uses the callback object to signal task completion and - * return results. + * Adds a memory image data source to the case database using a background + * task in a separate thread and the given settings instead of those + * provided by the selection and configuration panel. Returns as soon as the + * background task is started and uses the callback object to signal task + * completion and return results. * - * @param deviceId An ASCII-printable identifier for the device - * associated with the data source that is - * intended to be unique across multiple cases - * (e.g., a UUID). - * @param imageFilePath Path to the image file. - * @param timeZone The time zone to use when processing dates - * and times for the image, obtained from - * java.util.TimeZone.getID. - * @param chunkSize The maximum size of each chunk of the raw - * data source as it is divided up into virtual - * unallocated space files. - * @param progressMonitor Progress monitor for reporting progress - * during processing. - * @param callback Callback to call when processing is done. + * @param deviceId An ASCII-printable identifier for the device + * associated with the data source that is intended + * to be unique across multiple cases (e.g., a UUID). + * @param memoryImagePath Path to the memory image file. + * @param pluginsToRun The Volatility plugins to run. + * @param timeZone The time zone to use when processing dates and + * times for the image, obtained from + * java.util.TimeZone.getID. + * @param progressMonitor Progress monitor for reporting progress during + * processing. + * @param callback Callback to call when processing is done. */ - private void run(String deviceId, String imageFilePath, List pluginsToRun, String timeZone, long chunkSize, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { - addImageTask = new AddMemoryImageTask(deviceId, imageFilePath, pluginsToRun, timeZone, 0, progressMonitor, callback); + private void run(String deviceId, String memoryImagePath, List pluginsToRun, String timeZone, DataSourceProcessorProgressMonitor progressMonitor, DataSourceProcessorCallback callback) { + addImageTask = new AddMemoryImageTask(deviceId, memoryImagePath, pluginsToRun, timeZone, progressMonitor, callback); new Thread(addImageTask).start(); - //new Thread(new AddLocalFilesTask(deviceId, rootVirtualDirectoryName, localFilePaths, progressMonitor, callback)).start(); } + /** + * Requests cancellation of the background task that adds a data source to + * the case database, after the task is started using the run method. This + * is a "best effort" cancellation, with no guarantees that the case + * database will be unchanged. If cancellation succeeded, the list of new + * data sources returned by the background task will be empty. + */ @Override public void cancel() { if (addImageTask != null) { @@ -165,4 +168,3 @@ public class MemoryDSProcessor implements DataSourceProcessor { } } - diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java index 31e2e5b8af..fd285ae1aa 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java @@ -1,15 +1,15 @@ /* - * Autopsy - * + * Autopsy + * * Copyright 2018 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. @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.experimental.volatilityDSP; import java.io.BufferedReader; import java.io.FileReader; import java.io.File; -import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Paths; import java.util.ArrayList; @@ -30,9 +29,10 @@ import java.util.List; import java.util.Set; import java.util.logging.Level; import org.openide.modules.InstalledFileLocator; -import org.openide.util.Exceptions; import org.openide.util.Lookup; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; @@ -50,159 +50,204 @@ import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; - /** - * Runs Volatility and parses output + * Runs Volatility on a given memory image file and parses the output to create + * artifacts. */ class VolatilityProcessor { - private static final String VOLATILITY_DIRECTORY = "Volatility"; //NON-NLS + + private final static Logger logger = Logger.getLogger(VolatilityProcessor.class.getName()); + private static final String VOLATILITY = "Volatility"; //NON-NLS private static final String VOLATILITY_EXECUTABLE = "volatility_2.6_win64_standalone.exe"; //NON-NLS - private final String memoryImagePath; - private final List pluginsToRun; - private final Image dataSource; - private static final String SEP = System.getProperty("line.separator"); - private static final Logger logger = Logger.getLogger(VolatilityProcessor.class.getName()); - private String moduleOutputPath; - private File executableFile; private final IngestServices services = IngestServices.getInstance(); + private final List errorMsgs = new ArrayList<>(); + private final String memoryImagePath; + private final Image dataSource; + private final List pluginsToRun; private final DataSourceProcessorProgressMonitor progressMonitor; - private boolean isCancelled; + private Case currentCase; + private File executableFile; + private String moduleOutputPath; private FileManager fileManager; - private final List errorMsgs = new ArrayList<>(); + private volatile boolean isCancelled; /** - * - * @param ImagePath String path to memory image file - * @param dataSource Object for memory image that was added to case DB - * @param plugInToRuns list of Volatility plugins to run - * @param progressMonitor DSP progress monitor to report status + * Constructs a processor that runs Volatility on a given memory image file + * and parses the output to create artifacts. + * + * @param memoryImagePath Path to memory image file. + * @param dataSource The memory image data source. + * @param plugInToRuns Volatility plugins to run. + * @param progressMonitor Progress monitor for reporting progress during + * processing. */ - VolatilityProcessor(String ImagePath, Image dataSource, List plugInToRun, DataSourceProcessorProgressMonitor progressMonitor) { - this.memoryImagePath = ImagePath; + VolatilityProcessor(String memoryImagePath, Image dataSource, List plugInToRun, DataSourceProcessorProgressMonitor progressMonitor) { + this.memoryImagePath = memoryImagePath; this.pluginsToRun = plugInToRun; this.dataSource = dataSource; this.progressMonitor = progressMonitor; } - - + /** - * Run volatility and parse the outputs - * @returns true if there was a critical error + * Runs Volatility on a given memory image file and parses the output to + * create artifacts. + * + * @throws VolatilityProcessorException If there is a critical error during + * processing. */ - boolean run() { - executableFile = locateExecutable(); - if (executableFile == null) { - logger.log(Level.SEVERE, "Volatility exe not found"); - return true; + @NbBundle.Messages({ + "VolatilityProcessor_progressMessage_noCurrentCase=Failed to get current case", + "VolatilityProcessor_exceptionMessage_volatilityExeNotFound=Volatility executable not found", + "# {0} - plugin name", + "VolatilityProcessor_progressMessage_runningImageInfo=Running {0} plugin" + }) + void run() throws VolatilityProcessorException { + this.errorMsgs.clear(); + + try { + this.currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_progressMessage_noCurrentCase(), ex); } - - final Case currentCase = Case.getCurrentCase(); + + executableFile = locateVolatilityExecutable(); + if (executableFile == null) { + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_exceptionMessage_volatilityExeNotFound()); + } + fileManager = currentCase.getServices().getFileManager(); - // make a unique folder for this image - moduleOutputPath = currentCase.getModulesOutputDirAbsPath() + File.separator + "Volatility" + File.separator + dataSource.getId(); File directory = new File(String.valueOf(moduleOutputPath)); - if(!directory.exists()){ + /* + * Make an output folder unique to this data source. + */ + Long dataSourceId = dataSource.getId(); + moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), VOLATILITY, dataSourceId.toString()).toString(); + File directory = new File(String.valueOf(moduleOutputPath)); + if (!directory.exists()) { directory.mkdirs(); - progressMonitor.setProgressText("Running imageinfo"); - executeAndParseVolatility("imageinfo"); + progressMonitor.setProgressText(Bundle.VolatilityProcessor_progressMessage_runningImageInfo("imageinfo")); //NON-NLS + runVolatilityPlugin("imageinfo"); //NON-NLS } progressMonitor.setIndeterminate(false); progressMonitor.setProgressMax(pluginsToRun.size()); for (int i = 0; i < pluginsToRun.size(); i++) { - if (isCancelled) + if (isCancelled) { break; + } String pluginToRun = pluginsToRun.get(i); - progressMonitor.setProgressText("Processing " + pluginToRun + " module"); - executeAndParseVolatility(pluginToRun); + progressMonitor.setProgressText(Bundle.VolatilityProcessor_progressMessage_runningImageInfo(pluginToRun)); + runVolatilityPlugin(pluginToRun); progressMonitor.setProgress(i); - } - return false; + } } - + /** - * Get list of error messages that were generated during call to run() - * @return + * Gets a list of error messages that were generated during the processing. + * + * @return The list of error messages. */ List getErrorMessages() { - return errorMsgs; + return new ArrayList<>(errorMsgs); } - private void executeAndParseVolatility(String pluginToRun) { - try { - List commandLine = new ArrayList<>(); - commandLine.add("\"" + executableFile + "\""); - File memoryImage = new File(memoryImagePath); - commandLine.add("--filename=" + memoryImage.getName()); //NON-NLS - - // run imginfo if we haven't run it yet - File imageInfoOutputFile = new File(moduleOutputPath + "\\imageinfo.txt"); - if (imageInfoOutputFile.exists()) { - String memoryProfile = parseImageInfoOutput(imageInfoOutputFile); - if (memoryProfile == null) { - String msg = "Error parsing Volatility imginfo output"; - logger.log(Level.SEVERE, msg); - errorMsgs.add(msg); - return; - } - commandLine.add("--profile=" + memoryProfile); - } - - commandLine.add(pluginToRun); //NON-NLS - - String outputFile = moduleOutputPath + "\\" + pluginToRun + ".txt"; - ProcessBuilder processBuilder = new ProcessBuilder(commandLine); - // Add environment variable to force Volatility to run with the same permissions Autopsy uses - processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS - processBuilder.redirectOutput(new File(outputFile)); - processBuilder.redirectError(new File(moduleOutputPath + "\\Volatility_Run.err")); - processBuilder.directory(new File(memoryImage.getParent())); - + /** + * Runs a given Volatility plugin and parses its output to create artifacts. + * + * @param pluginToRun The name of the Volatility plugin to run. + * + * @throws VolatilityProcessorException If there is a critical error, add + * messages to the error messages list + * for non-critical errors. + */ + @NbBundle.Messages({ + "VolatilityProcessor_exceptionMessage_failedToRunVolatilityExe=Could not run Volatility", + "# {0} - plugin name", + "VolatilityProcessor_exceptionMessage_errorRunningPlugin=Volatility error running {0} plugin", + "# {0} - plugin name", + "VolatilityProcessor_exceptionMessage_errorAddingOutput=Failed to add output for {0} to case", + "# {0} - plugin name", + "VolatilityProcessor_exceptionMessage_searchServiceNotFound=Keyword search service not found, output for {0} plugin not indexed", + "# {0} - plugin name", + "VolatilityProcessor_exceptionMessage_errorIndexingOutput=Error indexing output for {0} plugin" + }) + private void runVolatilityPlugin(String pluginToRun) throws VolatilityProcessorException { + List commandLine = new ArrayList<>(); + commandLine.add("\"" + executableFile + "\""); //NON-NLS + File memoryImage = new File(memoryImagePath); + commandLine.add("--filename=" + memoryImage.getName()); //NON-NLS + + File imageInfoOutputFile = new File(moduleOutputPath + "\\imageinfo.txt"); //NON-NLS + if (imageInfoOutputFile.exists()) { + String memoryProfile = parseImageInfoOutput(imageInfoOutputFile); + commandLine.add("--profile=" + memoryProfile); //NON-NLS + } + + commandLine.add(pluginToRun); + + String outputFile = moduleOutputPath + "\\" + pluginToRun + ".txt"; //NON-NLS + ProcessBuilder processBuilder = new ProcessBuilder(commandLine); + /* + * Add an environment variable to force Volatility to run with the same + * permissions Autopsy uses. + */ + processBuilder.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS + processBuilder.redirectOutput(new File(outputFile)); + processBuilder.redirectError(new File(moduleOutputPath + "\\Volatility_Run.err")); //NON-NLS + processBuilder.directory(new File(memoryImage.getParent())); + + try { int exitVal = ExecUtil.execute(processBuilder); if (exitVal != 0) { - String msg = "Volatility non-0 exit value for module: " + pluginToRun; - logger.log(Level.SEVERE, msg); - errorMsgs.add(msg); + errorMsgs.add(Bundle.VolatilityProcessor_exceptionMessage_errorRunningPlugin(pluginToRun)); return; } - - if (isCancelled) - return; - - // add the output to the case - final Case currentCase = Case.getCurrentCase(); - Report report = currentCase.getSleuthkitCase().addReport(outputFile, "Volatility", "Volatility " + pluginToRun + " Module"); - - 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); - } - - scanOutputFile(pluginToRun, new File(outputFile)); - - } catch (IOException | SecurityException | TskCoreException ex) { - logger.log(Level.SEVERE, "Unable to run Volatility", ex); //NON-NLS - //this.addErrorMessage(NbBundle.getMessage(this.getClass(), "ExtractRegistry.execRegRip.errMsg.failedAnalyzeRegFile", this.getName())); + } catch (IOException | SecurityException ex) { + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_exceptionMessage_failedToRunVolatilityExe(), ex); } - } - - /** - * Finds and returns the path to the executable, if able. - * - * @param executableToFindName The name of the executable to find - * - * @return A File reference or null - */ - private static File locateExecutable() { - // Must be running under a Windows operating system. - if (!PlatformUtil.isWindowsOS()) { - return null; - } - - String executableToFindName = Paths.get(VOLATILITY_DIRECTORY, VOLATILITY_EXECUTABLE).toString(); + if (isCancelled) { + return; + } + + /* + * Add the plugin output file to the case as a report. + */ + try { + Report report = currentCase.getSleuthkitCase().addReport(outputFile, VOLATILITY, VOLATILITY + " " + pluginToRun + " Plugin"); //NON-NLS + try { + KeywordSearchService searchService = Lookup.getDefault().lookup(KeywordSearchService.class); + if (searchService != null) { + searchService.index(report); + } else { + errorMsgs.add(Bundle.VolatilityProcessor_exceptionMessage_searchServiceNotFound(pluginToRun)); + /* + * Log the exception as well as add it to the error + * messages, to ensure that the stack trace is not lost. + */ + logger.log(Level.WARNING, Bundle.VolatilityProcessor_exceptionMessage_errorIndexingOutput(pluginToRun)); + } + } catch (TskCoreException ex) { + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_exceptionMessage_errorIndexingOutput(pluginToRun), ex); + } + } catch (TskCoreException ex) { + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_exceptionMessage_errorAddingOutput(pluginToRun), ex); + } + + createArtifactsFromPluginOutput(pluginToRun, new File(outputFile)); + } + + /** + * Finds and returns the path to the Volatility executable, if able. + * + * @return A File reference or null. + */ + private static File locateVolatilityExecutable() { + if (!PlatformUtil.isWindowsOS()) { + return null; + } + + String executableToFindName = Paths.get(VOLATILITY, VOLATILITY_EXECUTABLE).toString(); File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, VolatilityProcessor.class.getPackage().getName(), false); if (null == exeFile) { return null; @@ -215,73 +260,79 @@ class VolatilityProcessor { return exeFile; } - private String parseImageInfoOutput(File imageOutputFile) throws FileNotFoundException { - // create a Buffered Reader object instance with a FileReader - try ( - BufferedReader br = new BufferedReader(new FileReader(imageOutputFile))) { - // read the first line from the text file - String fileRead = br.readLine(); - br.close(); - String[] profileLine = fileRead.split(":"); - String[] memProfile = profileLine[1].split(",|\\("); - return memProfile[0].replaceAll("\\s+",""); - } catch (IOException ex) { - Exceptions.printStackTrace(ex); - // @@@ Need to log this or rethrow it - } - - return null; + @NbBundle.Messages({ + "VolatilityProcessor_exceptionMessage_failedToParseImageInfo=Could not parse image info" + }) + private String parseImageInfoOutput(File imageOutputFile) throws VolatilityProcessorException { + try (BufferedReader br = new BufferedReader(new FileReader(imageOutputFile))) { + String fileRead = br.readLine(); + String[] profileLine = fileRead.split(":"); //NON-NLS + String[] memProfile = profileLine[1].split(",|\\("); //NON-NLS + return memProfile[0].replaceAll("\\s+", ""); //NON-NLS + } catch (IOException ex) { + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_exceptionMessage_failedToParseImageInfo(), ex); + } } - - /** - * Lookup the set of files and add INTERESTING_ITEM artifacts for them. - * - * @param fileSet - * @param pluginName - */ - private void flagFiles(Set fileSet, String pluginName) { - - Blackboard blackboard; - try { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - } - catch (Exception ex) { - // case is closed ?? - return; - } + /** + * Adds interesting file artifacts for files found by a Volatility plugin. + * + * @param fileSet The paths of the files within the memeory image data + * source. + * @param pluginName The name of the source Volatility plugin. + */ + @NbBundle.Messages({ + "# {0} - plugin name", + "VolatilityProcessor_artifactAttribute_interestingFileSet=Volatility Plugin {0}", + "# {0} - file path", + "# {1} - file name", + "# {2} - plugin name", + "VolatilityProcessor_exceptionMessage_fileNotFound=File {0}/{1} not found for ouput of {2} plugin", + "# {0} - plugin name", + "VolatilityProcessor_exceptionMessage_errorCreatingArtifact=Error creating artifact for output of {0} plugin", + "# {0} - plugin name", + "VolatilityProcessor_errorMessage_errorFindingFiles=Error finding files parsed from output of {0} plugin", + "# {0} - plugin name", + "VolatilityProcessor_errorMessage_failedToIndexArtifact=Error indexing artifact from output of {0} plugin" + }) + private void flagFiles(Set fileSet, String pluginName) throws VolatilityProcessorException { + Blackboard blackboard = currentCase.getServices().getBlackboard(); for (String file : fileSet) { if (isCancelled) { - return; + return; } + if (file.isEmpty()) { + continue; + } + File volfile = new File(file); String fileName = volfile.getName().trim(); - // File does not have any data in it based on bad data if (fileName.length() < 1) { continue; } String filePath = volfile.getParent(); - + try { List resolvedFiles; if (filePath == null) { - resolvedFiles = fileManager.findFiles(fileName); //NON-NLS + resolvedFiles = fileManager.findFiles(fileName); } else { // File changed the slashes back to \ on us... - filePath = filePath.replaceAll("\\\\", "/"); - resolvedFiles = fileManager.findFiles(fileName, filePath); //NON-NLS + filePath = filePath.replaceAll("\\\\", "/"); //NON-NLS + resolvedFiles = fileManager.findFiles(fileName, filePath); } - + // if we didn't get anything, then try adding a wildcard for extension - if ((resolvedFiles.isEmpty()) && (fileName.contains(".") == false)) { - + if ((resolvedFiles.isEmpty()) && (fileName.contains(".") == false)) { //NON-NLS + // if there is already the same entry with ".exe" in the set, just use that one - if (fileSet.contains(file + ".exe")) + if (fileSet.contains(file + ".exe")) { //NON-NLS continue; - - fileName = fileName + ".%"; + } + + fileName += ".%"; //NON-NLS if (filePath == null) { resolvedFiles = fileManager.findFiles(fileName); //NON-NLS } else { @@ -289,185 +340,196 @@ class VolatilityProcessor { } } - + if (resolvedFiles.isEmpty()) { - logger.log(Level.SEVERE, "File not found in lookup: " + filePath + "/" + fileName); - errorMsgs.add("File not found in lookup: " + filePath + "/" + fileName); + errorMsgs.add(Bundle.VolatilityProcessor_exceptionMessage_fileNotFound(filePath, fileName, pluginName)); continue; } - - resolvedFiles.forEach((resolvedFile) -> { + + for (AbstractFile resolvedFile : resolvedFiles) { if (resolvedFile.getType() == TSK_DB_FILES_TYPE_ENUM.SLACK) { - return; // equivalent to continue in non-lambda world + continue; } try { - String MODULE_NAME = "Volatility"; BlackboardArtifact volArtifact = resolvedFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT); - BlackboardAttribute att1 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, - "Volatility Plugin " + pluginName); + BlackboardAttribute att1 = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, VOLATILITY, Bundle.VolatilityProcessor_artifactAttribute_interestingFileSet(pluginName)); volArtifact.addAttribute(att1); try { // index the artifact for keyword search blackboard.indexArtifact(volArtifact); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, "Unable to index blackboard artifact " + volArtifact.getArtifactID(), ex); //NON-NLS + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_failedToIndexArtifact(pluginName)); + /* + * Log the exception as well as add it to the error + * messages, to ensure that the stack trace is not + * lost. + */ + logger.log(Level.SEVERE, String.format("Failed to index artifact (artifactId=%d) for for output of %s plugin", volArtifact.getArtifactID(), pluginName), ex); } // fire event to notify UI of this new artifact - services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)); + services.fireModuleDataEvent(new ModuleDataEvent(VOLATILITY, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT)); } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Failed to create BlackboardArtifact.", ex); // NON-NLS - } catch (IllegalStateException ex) { - logger.log(Level.SEVERE, "Failed to create BlackboardAttribute.", ex); // NON-NLS + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_exceptionMessage_errorCreatingArtifact(pluginName), ex); } - }); + } } catch (TskCoreException ex) { - //String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles"); - logger.log(Level.SEVERE, "Error in Finding Files", ex); - return; + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_errorMessage_errorFindingFiles(pluginName), ex); } } } - - /** - * Scan the output of Volatility and create artifacts as needed - * - * @param pluginName Name of volatility module run - * @param PluginOutput File that contains the output to parse - */ - private void scanOutputFile(String pluginName, File PluginOutput) { - - if (pluginName.matches("dlllist")) { - Set fileSet = parseDllList(PluginOutput); - flagFiles(fileSet, pluginName); - } else if (pluginName.matches("handles")) { - Set fileSet = parseHandles(PluginOutput); - flagFiles(fileSet, pluginName); - } else if (pluginName.matches("cmdline")) { - Set fileSet = parseCmdline(PluginOutput); - flagFiles(fileSet, pluginName); - } else if (pluginName.matches("psxview")){ - Set fileSet = parsePsxview(PluginOutput); - flagFiles(fileSet, pluginName); - } else if (pluginName.matches("pslist")) { - Set fileSet = parsePslist(PluginOutput); - flagFiles(fileSet, pluginName); - } else if (pluginName.matches("psscan")) { - Set fileSet = parsePsscan(PluginOutput); - flagFiles(fileSet, pluginName); - } else if (pluginName.matches("pstree")) { - Set fileSet = parsePstree(PluginOutput); - flagFiles(fileSet, pluginName); - } else if (pluginName.matches("svcscan")) { - Set fileSet = parseSvcscan(PluginOutput); - flagFiles(fileSet, pluginName); - } else if (pluginName.matches("filescan")) { - // BC: Commented out. Too many hits to flag - //Set fileSet = ParseFilescan(PluginOutput); - //lookupFiles(fileSet, pluginName); - } else if (pluginName.matches("shimcache")) { - Set fileSet = parseShimcache(PluginOutput); - flagFiles(fileSet, pluginName); - } - } - /** - * Normalize the path we parse out of the output before - * we look it up in the case DB - * - * @param filePath Path to normalize - * @return Normalized version + /** + * Parses the output of a Volatility plugin and creates artifacts as needed. + * + * @param pluginName Name of the Volatility plugin. + * @param pluginOutputFile File that contains the output to parse. + */ + private void createArtifactsFromPluginOutput(String pluginName, File pluginOutputFile) throws VolatilityProcessorException { + Set fileSet = null; + switch (pluginName) { + case "dlllist": //NON-NLS + fileSet = parseDllListOutput(pluginOutputFile); + break; + case "handles": //NON-NLS + fileSet = parseHandlesOutput(pluginOutputFile); + break; + case "cmdline": //NON-NLS + fileSet = parseCmdlineOutput(pluginOutputFile); + break; + case "psxview": //NON-NLS + fileSet = parsePsxviewOutput(pluginOutputFile); + break; + case "pslist": //NON-NLS + fileSet = parsePslistOutput(pluginOutputFile); + break; + case "psscan": //NON-NLS + fileSet = parsePsscanOutput(pluginOutputFile); + break; + case "pstree": //NON-NLS + fileSet = parsePstreeOutput(pluginOutputFile); + break; + case "svcscan": //NON-NLS + fileSet = parseSvcscanOutput(pluginOutputFile); + break; + case "shimcache": //NON-NLS + fileSet = parseShimcacheOutput(pluginOutputFile); + break; + default: + break; + } + + if (fileSet != null && !fileSet.isEmpty()) { + flagFiles(fileSet, pluginName); + } + } + + /** + * Normalizes a file path from a Volatility plugin so it can be used to look + * up the file in the case database. + * + * @param filePath Path to normalize. + * + * @return The normalized path or the empty string if the path cannot be + * normalized or should be ignored. */ private String normalizePath(String filePath) { - if (filePath == null) - return ""; - - filePath = filePath.trim(); + if (filePath == null) { + return ""; //NON-NLS + } + String path = filePath.trim(); + // change slash direction - filePath = filePath.replaceAll("\\\\", "/"); - filePath = filePath.toLowerCase(); + path = path.replaceAll("\\\\", "/"); //NON-NLS + path = path.toLowerCase(); // \??\c:\windows ... - if ((filePath.length() > 4) && (filePath.startsWith("/??/"))) { - filePath = filePath.substring(4); + if ((path.length() > 4) && (path.startsWith("/??/"))) { //NON-NLS + path = path.substring(4); } // strip C: - if (filePath.contains(":")) { - int index = filePath.indexOf(':'); - if (index+1 < filePath.length()) { - filePath = filePath.substring(index + 1); - } + if (path.contains(":")) { //NON-NLS + int index = path.indexOf(":"); + if (index+1 < path.length()) + path = path.substring(index + 1); } - - - filePath = filePath.replaceAll("/systemroot/", "/windows/"); + + path = path.replaceAll("/systemroot/", "/windows/"); + // catches 1 type of file in cmdline - filePath = filePath.replaceAll("%systemroot%", "/windows/"); - filePath = filePath.replaceAll("/device/",""); + path = path.replaceAll("%systemroot%", "/windows/"); //NON-NLS + path = path.replaceAll("/device/", ""); //NON-NLS // helps with finding files in handles plugin // example: \Device\clfs\Device\HarddiskVolume2\Users\joe\AppData\Local\Microsoft\Windows\UsrClass.dat{e15d4b01-1598-11e8-93e6-080027b5e733}.TM - if (filePath.contains("/harddiskvolume")) { + if (path.contains("/harddiskvolume")) { //NON-NLS // 16 advances beyond harddiskvolume and the number - int index = filePath.indexOf("/harddiskvolume"); - if (index+16 < filePath.length()) { - filePath = filePath.substring(index + 16); + int index = path.indexOf("/harddiskvolume"); //NON-NLS + if (index+16 < path.length()) { + path = path.substring(index + 16); } } - - // no point returning these. We won't map to them - if (filePath.startsWith("/namedpipe/")) - return ""; - return filePath; + // no point returning these. We won't map to them + if (path.startsWith("/namedpipe/")) { //NON-NLS + return ""; //NON-NLS + } + + return path; } - - private Set parseHandles(File pluginFile) { + + @NbBundle.Messages({ + "# {0} - plugin name", + "VolatilityProcessor_errorMessage_outputParsingError=Error parsing output for {0} plugin" + }) + private Set parseHandlesOutput(File pluginOutputFile) { String line; Set fileSet = new HashSet<>(); - try { - BufferedReader br = new BufferedReader(new FileReader(pluginFile)); - // Ignore the first two header lines - br.readLine(); - br.readLine(); - while ((line = br.readLine()) != null) { - // 0x89ab7878 4 0x718 0x2000003 File \Device\HarddiskVolume1\Documents and Settings\QA\Local Settings\Application - if (line.startsWith("0x") == false) - continue; - - String TAG = " File "; - String file_path = null; - if ((line.contains(TAG)) && (line.length() > 57)) { + try (BufferedReader br = new BufferedReader(new FileReader(pluginOutputFile))) { + // Ignore the first two header lines + br.readLine(); + br.readLine(); + while ((line = br.readLine()) != null) { + // 0x89ab7878 4 0x718 0x2000003 File \Device\HarddiskVolume1\Documents and Settings\QA\Local Settings\Application + if (line.startsWith("0x") == false) { //NON-NLS + continue; + } + + String TAG = " File "; //NON-NLS + String file_path; + if ((line.contains(TAG)) && (line.length() > 57)) { file_path = line.substring(57); - if (file_path.contains("\"")) { - file_path = file_path.substring(0, file_path.indexOf('\"')); + if (file_path.contains("\"")) { //NON-NLS + file_path = file_path.substring(0, file_path.indexOf('\"')); //NON-NLS } // this file has a lot of device entries that are not files - if (file_path.startsWith("\\Device\\")) { - if (file_path.contains("HardDiskVolume") == false) + if (file_path.startsWith("\\Device\\")) { //NON-NLS + if (file_path.contains("HardDiskVolume") == false) { //NON-NLS continue; + } } - + fileSet.add(normalizePath(file_path)); - } - } - br.close(); + } + } } catch (IOException ex) { - String msg = "Error parsing handles output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("handles")); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, Bundle.VolatilityProcessor_errorMessage_outputParsingError("handles"), ex); + } return fileSet; } - - private Set parseDllList(File pluginFile) { + + private Set parseDllListOutput(File outputFile) { Set fileSet = new HashSet<>(); // read the first line from the text file - try (BufferedReader br = new BufferedReader(new FileReader(pluginFile))) { + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { String line; - while ((line = br.readLine()) != null) { - + while ((line = br.readLine()) != null) { // we skip the Command Line entries because that data // is also in the 0x lines (and is more likely to have a full path there. @@ -480,63 +542,39 @@ class VolatilityProcessor { } } } catch (IOException ex) { - String msg = "Error parsing dlllist output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } - return fileSet; + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("dlllist")); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, Bundle.VolatilityProcessor_errorMessage_outputParsingError("dlllist"), ex); + } + return fileSet; } - - private Set parseFilescan(File PluginFile) { - String line; - Set fileSet = new HashSet<>(); - try { - BufferedReader br = new BufferedReader(new FileReader(PluginFile)); - // read the first line from the text file - while ((line = br.readLine()) != null) { - try { - if (line.length() < 41) { - continue; - } - String file_path = line.substring(41); - fileSet.add(normalizePath(file_path)); - } catch (StringIndexOutOfBoundsException ex) { - // TO DO Catch exception - } - } - br.close(); - } catch (IOException ex) { - String msg = "Error parsing filescan output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } - return fileSet; - } - - private Set parseCmdline(File PluginFile) { + + private Set parseCmdlineOutput(File outputFile) { Set fileSet = new HashSet<>(); // read the first line from the text file - try (BufferedReader br = new BufferedReader(new FileReader(PluginFile))) { + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { String line; while ((line = br.readLine()) != null) { if (line.length() > 16) { - String TAG = "Command line : "; + String TAG = "Command line : "; //NON-NLS if ((line.startsWith(TAG)) && line.length() > TAG.length() + 1) { String file_path; // Command line : "C:\Program Files\VMware\VMware Tools\vmacthlp.exe" // grab whats inbetween the quotes - if (line.charAt(TAG.length()) == '\"') { + if (line.charAt(TAG.length()) == '\"') { //NON-NLS file_path = line.substring(TAG.length() + 1); - if (file_path.contains("\"")) { - file_path = file_path.substring(0, file_path.indexOf('\"')); - } - } - // Command line : C:\WINDOWS\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 + if (file_path.contains("\"")) { //NON-NLS + file_path = file_path.substring(0, file_path.indexOf('\"')); //NON-NLS + } + } // Command line : C:\WINDOWS\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 // grab everything before the next space - we don't want arguments else { file_path = line.substring(TAG.length()); - if (file_path.contains(" ")) { + if (file_path.contains(" ")) { //NON-NLS file_path = file_path.substring(0, file_path.indexOf(' ')); } } @@ -544,176 +582,189 @@ class VolatilityProcessor { } } } - + } catch (IOException ex) { - String msg = "Error parsing cmdline output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } - return fileSet; + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("cmdline")); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, Bundle.VolatilityProcessor_errorMessage_outputParsingError("cmdline"), ex); + } + return fileSet; } - - private Set parseShimcache(File PluginFile) { + + private Set parseShimcacheOutput(File outputFile) { String line; Set fileSet = new HashSet<>(); - try { - BufferedReader br = new BufferedReader(new FileReader(PluginFile)); - // ignore the first 2 header lines - br.readLine(); - br.readLine(); - while ((line = br.readLine()) != null) { + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { + // ignore the first 2 header lines + br.readLine(); + br.readLine(); + while ((line = br.readLine()) != null) { String file_path; //1970-01-01 00:00:00 UTC+0000 2017-10-25 13:07:30 UTC+0000 C:\WINDOWS\system32\msctfime.ime //2017-10-23 20:47:40 UTC+0000 2017-10-23 20:48:02 UTC+0000 \??\C:\WINDOWS\CT_dba9e71b-ad55-4132-a11b-faa946b197d6.exe if (line.length() > 62) { file_path = line.substring(62); - if (file_path.contains("\"")) { - file_path = file_path.substring(0, file_path.indexOf('\"')); - } + if (file_path.contains("\"")) { //NON-NLS + file_path = file_path.substring(0, file_path.indexOf('\"')); //NON-NLS + } fileSet.add(normalizePath(file_path)); - } - } - br.close(); - } catch (IOException ex) { - String msg = "Error parsing shimcache output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } - return fileSet; + } + } + } catch (IOException ex) { + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("shimcache")); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, Bundle.VolatilityProcessor_errorMessage_outputParsingError("shimcache"), ex); + } + return fileSet; } - - private Set parsePsscan(File PluginFile) { + + private Set parsePsscanOutput(File outputFile) { String line; Set fileSet = new HashSet<>(); - try { - BufferedReader br = new BufferedReader(new FileReader(PluginFile)); - // ignore the first two header lines - br.readLine(); - br.readLine(); - while ((line = br.readLine()) != null) { + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { + // ignore the first two header lines + br.readLine(); + br.readLine(); + while ((line = br.readLine()) != null) { // 0x000000000969a020 notepad.exe 3604 3300 0x16d40340 2018-01-12 14:41:16 UTC+0000 - if (line.startsWith("0x") == false) - continue; - if (line.length() < 37) { + if (line.startsWith("0x") == false) { //NON-NLS continue; } + else if (line.length() < 37) { + continue; + } + String file_path = line.substring(19, 37); file_path = normalizePath(file_path); - + // ignore system, it's not really a path - if (file_path.equals("system")) + if (file_path.equals("system")) { //NON-NLS continue; + } fileSet.add(file_path); - } - br.close(); - } catch (IOException ex) { - String msg = "Error parsing psscan output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } - return fileSet; + } + } catch (IOException ex) { + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("psscan")); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, Bundle.VolatilityProcessor_errorMessage_outputParsingError("psscan"), ex); + } + return fileSet; } - private Set parsePslist(File PluginFile) { + private Set parsePslistOutput(File outputFile) { String line; Set fileSet = new HashSet<>(); - try { - BufferedReader br = new BufferedReader(new FileReader(PluginFile)); - // read the first line from the text file - while ((line = br.readLine()) != null) { - if (line.startsWith("0x") == false) { - continue; - } - + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { + // read the first line from the text file + while ((line = br.readLine()) != null) { + if (line.startsWith("0x") == false) { //NON-NLS + continue; + } + // 0x89cfb998 csrss.exe 704 640 14 532 0 0 2017-12-07 14:05:34 UTC+0000 - else if (line.length() < 34) { - continue; - } - - String file_path = line.substring(10, 34); - file_path = normalizePath(file_path); - - // ignore system, it's not really a path - if (file_path.equals("system")) { - continue; - } - fileSet.add(file_path); - } - br.close(); - } catch (IOException ex) { - String msg = "Error parsing pslist output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } - return fileSet; - } - - private Set parsePsxview(File PluginFile) { - String line; - Set fileSet = new HashSet<>(); - try { - BufferedReader br = new BufferedReader(new FileReader(PluginFile)); - // ignore the first two header lines - br.readLine(); - br.readLine(); - while ((line = br.readLine()) != null) { - // 0x09adf980 svchost.exe 1368 True True False True True True True - if (line.startsWith("0x") == false) - continue; if (line.length() < 34) continue; - String file_path = line.substring(11, 34); + String file_path = line.substring(10, 34); file_path = normalizePath(file_path); - + // ignore system, it's not really a path - if (file_path.equals("system")) { + if (file_path.equals("system")) { //NON-NLS continue; } fileSet.add(file_path); - } - br.close(); - } catch (IOException ex) { - String msg = "Error parsing psxview output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } - return fileSet; + } + } catch (IOException ex) { + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("pslist")); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, Bundle.VolatilityProcessor_errorMessage_outputParsingError("pslist"), ex); + } + return fileSet; } - private Set parsePstree(File PluginFile) { + private Set parsePsxviewOutput(File outputFile) { String line; Set fileSet = new HashSet<>(); - try { - BufferedReader br = new BufferedReader(new FileReader(PluginFile)); - // read the first line from the text file - while ((line = br.readLine()) != null) { - // ... 0x897e5020:services.exe 772 728 15 287 2017-12-07 14:05:35 UTC+000 + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { + // ignore the first two header lines + br.readLine(); + br.readLine(); + while ((line = br.readLine()) != null) { + // 0x09adf980 svchost.exe 1368 True True False True True True True + if (line.startsWith("0x") == false) { //NON-NLS + continue; + } + + if (line.length() < 34) { + continue; + } + + String file_path = line.substring(11, 34); + file_path = normalizePath(file_path); + + // ignore system, it's not really a path + if (file_path.equals("system")) { //NON-NLS + continue; + } + fileSet.add(file_path); + } + } catch (IOException ex) { + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("psxview")); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, Bundle.VolatilityProcessor_errorMessage_outputParsingError("psxview"), ex); + } + return fileSet; + } + + private Set parsePstreeOutput(File outputFile) { + String line; + Set fileSet = new HashSet<>(); + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { + // read the first line from the text file + while ((line = br.readLine()) != null) { + // ... 0x897e5020:services.exe 772 728 15 287 2017-12-07 14:05:35 UTC+000 String TAG = ":"; if (line.contains(TAG)) { int index = line.indexOf(TAG); if (line.length() < 52 || index + 1 >= 52) { continue; } - String file_path = line.substring(line.indexOf(TAG) + 1, 52); + String file_path = line.substring(line.indexOf(':') + 1, 52); //NON-NLS file_path = normalizePath(file_path); - + // ignore system, it's not really a path - if (file_path.equals("system")) { + if (file_path.equals("system")) { //NON-NLS continue; } fileSet.add(file_path); } - } - br.close(); - } catch (IOException ex) { - String msg = "Error parsing pstree output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); - } - return fileSet; + } + } catch (IOException ex) { + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("pstree")); + /* + * Log the exception as well as add it to the error messages, to + * ensure that the stack trace is not lost. + */ + logger.log(Level.SEVERE, Bundle.VolatilityProcessor_errorMessage_outputParsingError("pstree"), ex); + } + return fileSet; } - private Set parseSvcscan(File PluginFile) { + private Set parseSvcscanOutput(File PluginFile) { String line; Set fileSet = new HashSet<>(); try { @@ -758,8 +809,28 @@ class VolatilityProcessor { } return fileSet; } - + + /** + * Requests cancellation of processing. + */ void cancel() { isCancelled = true; } + + /** + * Exception type thrown when the processor experiences an error condition. + */ + final class VolatilityProcessorException extends Exception { + + private static final long serialVersionUID = 1L; + + private VolatilityProcessorException(String message) { + super(message); + } + + private VolatilityProcessorException(String message, Throwable cause) { + super(message, cause); + } + } + } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index ad0fc0a678..1ddd06680e 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -645,18 +645,18 @@ final class RegexQuery implements KeywordSearchQuery { */ static private void addAttributeIfNotAlreadyCaptured(Map attributeMap, ATTRIBUTE_TYPE attrType, String groupName, Matcher matcher) { BlackboardAttribute.Type type = new BlackboardAttribute.Type(attrType); - attributeMap.computeIfAbsent(type, t -> { + + if( ! attributeMap.containsKey(type)) { String value = matcher.group(groupName); if (attrType.equals(ATTRIBUTE_TYPE.TSK_CARD_NUMBER)) { attributeMap.put(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_KEYWORD), new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_KEYWORD, MODULE_NAME, value)); value = CharMatcher.anyOf(" -").removeFrom(value); } + if (StringUtils.isNotBlank(value)) { - return new BlackboardAttribute(attrType, MODULE_NAME, value); - } else { - return null; + attributeMap.put(type, new BlackboardAttribute(attrType, MODULE_NAME, value)); } - }); + } } }