From 636b27b30e60a87e4b126b712f5830e2426c8de5 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Wed, 28 Mar 2018 17:47:52 -0400 Subject: [PATCH 01/23] 3591: 2nd test for file filter --- .../autopsy/ingest/IngestFileFiltersTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) 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 258ef092c7..96e60e1d82 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 @@ -25,7 +25,9 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; import junit.framework.TestCase; import org.netbeans.junit.NbModuleSuite; import org.sleuthkit.autopsy.casemodule.Case; @@ -156,6 +158,34 @@ public class IngestFileFiltersTest extends TestCase { } } + public void testExtAndDirWithOneRule() { + HashMap rule = new HashMap<>(); + rule.put("Rule", new Rule("testFileType", new Rule.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 Files_Ext_Dirs_Filter = new FilesSet("Filter", "Filter to find all jpg files in dir1.", false, false, rule); + + try { + Case openCase = Case.getOpenCase(); + runIngestJob(openCase.getDataSources(), Files_Ext_Dirs_Filter); + FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); + List results = fileManager.findFiles("%.jpg%"); + assertEquals(12, 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 passed by the file filter.", file.getName(), file.getId()); + assertTrue(errMsg, file.getMIMEType() != null); + } else { //All others shouldn't have MIME Type + String errMsg = String.format("File %s (objId=%d) unexpectedly caught 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(); From badb2cfbe6d6c71a9e275be09f58b6a81ed3137b Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 29 Mar 2018 10:06:41 -0400 Subject: [PATCH 02/23] Volatility DSP clean up. bug fixes --- .../volatilityDSP/AddMemoryImageTask.java | 19 +- .../volatilityDSP/MemoryDSInputPanel.java | 79 +-- .../volatilityDSP/VolatilityProcessor.java | 556 +++++++++--------- 3 files changed, 321 insertions(+), 333 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java index 57d14452b5..17b3c7c0a7 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java @@ -26,18 +26,17 @@ import java.util.List; 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; /* * A runnable that adds a raw data source to a case database. */ final class AddMemoryImageTask implements Runnable { - private static final Logger logger = Logger.getLogger(AddMemoryImageTask.class.getName()); private final String deviceId; private final String imageFilePath; private final String timeZone; @@ -87,8 +86,9 @@ final class AddMemoryImageTask implements Runnable { } /* call Volatility to process the image */ else { - if (isCancelled) + if (isCancelled) { return; + } volatilityProcessor = new VolatilityProcessor(imageFilePath, dataSource, pluginsToRun, progressMonitor); if (volatilityProcessor.run()) { @@ -130,8 +130,15 @@ final class AddMemoryImageTask implements Runnable { private Image addImageToCase(List errorMessages) { progressMonitor.setProgressText(Bundle.AddMemoryImageTask_progress_add_text() + imageFilePath); - SleuthkitCase caseDatabase = Case.getCurrentCase().getSleuthkitCase(); - caseDatabase.acquireExclusiveLock(); + SleuthkitCase caseDatabase; + try { + caseDatabase = Case.getOpenCase().getSleuthkitCase(); + } catch (NoCurrentCaseException ex) { + errorMessages.add(Bundle.AddMemoryImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddMemoryImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage()); + return null; + } + + caseDatabase.acquireSingleUserCaseWriteLock(); // verify it exists File imageFile = Paths.get(imageFilePath).toFile(); @@ -151,7 +158,7 @@ final class AddMemoryImageTask implements Runnable { errorMessages.add(Bundle.AddMemoryImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddMemoryImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage()); return null; } finally { - caseDatabase.releaseExclusiveLock(); + caseDatabase.releaseSingleUserCaseWriteLock(); } } 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/VolatilityProcessor.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java index dc2ccc296c..a66e0204d9 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. @@ -33,6 +33,7 @@ import org.openide.modules.InstalledFileLocator; import org.openide.util.Exceptions; import org.openide.util.Lookup; 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,31 +51,31 @@ 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 */ class VolatilityProcessor { + private static final String VOLATILITY_DIRECTORY = "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 File executableFile; + private Case currentCase; + 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 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 */ VolatilityProcessor(String ImagePath, Image dataSource, List plugInToRun, DataSourceProcessorProgressMonitor progressMonitor) { @@ -83,25 +84,34 @@ class VolatilityProcessor { this.dataSource = dataSource; this.progressMonitor = progressMonitor; } - - + /** * Run volatility and parse the outputs + * * @returns true if there was a critical error */ - boolean run() { + boolean run() { + + try { + this.currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Failed to get the current case", ex); + return true; + } + executableFile = locateExecutable(); if (executableFile == null) { logger.log(Level.SEVERE, "Volatility exe not found"); return true; } - - final Case currentCase = Case.getCurrentCase(); + 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()){ + Long dataSourceId = dataSource.getId(); + moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), VOLATILITY_DIRECTORY, dataSourceId.toString()).toString(); + File directory = new File(String.valueOf(moduleOutputPath)); + if (!directory.exists()) { directory.mkdirs(); progressMonitor.setProgressText("Running imageinfo"); executeAndParseVolatility("imageinfo"); @@ -110,46 +120,48 @@ class VolatilityProcessor { 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.setProgress(i); - } + } return false; } - + /** * Get list of error messages that were generated during call to run() - * @return + * + * @return */ List getErrorMessages() { - return errorMsgs; + return new ArrayList<>(errorMsgs); } private void executeAndParseVolatility(String pluginToRun) { - try { + 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 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("--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 @@ -157,7 +169,7 @@ class VolatilityProcessor { processBuilder.redirectOutput(new File(outputFile)); processBuilder.redirectError(new File(moduleOutputPath + "\\Volatility_Run.err")); processBuilder.directory(new File(memoryImage.getParent())); - + int exitVal = ExecUtil.execute(processBuilder); if (exitVal != 0) { String msg = "Volatility non-0 exit value for module: " + pluginToRun; @@ -165,30 +177,30 @@ class VolatilityProcessor { errorMsgs.add(msg); return; } - - if (isCancelled) + + 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)); - + + 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())); } } - - /** + + /** * Finds and returns the path to the executable, if able. * * @param executableToFindName The name of the executable to find @@ -198,9 +210,9 @@ class VolatilityProcessor { private static File locateExecutable() { // Must be running under a Windows operating system. if (!PlatformUtil.isWindowsOS()) { - return null; + return null; } - + String executableToFindName = Paths.get(VOLATILITY_DIRECTORY, VOLATILITY_EXECUTABLE).toString(); File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, VolatilityProcessor.class.getPackage().getName(), false); @@ -218,41 +230,33 @@ class VolatilityProcessor { 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) { + BufferedReader br = new BufferedReader(new FileReader(imageOutputFile))) { + // read the first line from the text file + String fileRead = br.readLine(); + 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; } - + /** * Lookup the set of files and add INTERESTING_ITEM artifacts for them. - * + * * @param fileSet - * @param pluginName + * @param pluginName */ private void flagFiles(Set fileSet, String pluginName) { - - Blackboard blackboard; - try { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - } - catch (Exception ex) { - // case is closed ?? - return; - } + + Blackboard blackboard = currentCase.getServices().getBlackboard(); for (String file : fileSet) { if (isCancelled) { - return; + return; } File volfile = new File(file); @@ -263,7 +267,7 @@ class VolatilityProcessor { } String filePath = volfile.getParent(); - + try { List resolvedFiles; if (filePath == null) { @@ -273,15 +277,16 @@ class VolatilityProcessor { filePath = filePath.replaceAll("\\\\", "/"); resolvedFiles = fileManager.findFiles(fileName, filePath); //NON-NLS } - + // if we didn't get anything, then try adding a wildcard for extension if ((resolvedFiles.isEmpty()) && (fileName.contains(".") == false)) { - + // 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")) { continue; - - fileName = fileName + ".%"; + } + + fileName += ".%"; if (filePath == null) { resolvedFiles = fileManager.findFiles(fileName); //NON-NLS } else { @@ -289,13 +294,13 @@ class VolatilityProcessor { } } - + if (resolvedFiles.isEmpty()) { - logger.log(Level.SEVERE, "File not found in lookup: " + filePath + "/" + fileName); + logger.log(Level.SEVERE, "File not found in lookup: {0}/{1}", new Object[]{filePath, fileName}); errorMsgs.add("File not found in lookup: " + filePath + "/" + fileName); continue; } - + resolvedFiles.forEach((resolvedFile) -> { if (resolvedFile.getType() == TSK_DB_FILES_TYPE_ENUM.SLACK) { return; // equivalent to continue in non-lambda world @@ -323,144 +328,145 @@ class VolatilityProcessor { } }); } catch (TskCoreException ex) { - //String msg = NbBundle.getMessage(this.getClass(), "Chrome.getHistory.errMsg.errGettingFiles"); logger.log(Level.SEVERE, "Error in Finding Files", ex); return; } } } - + /** * Scan the output of Volatility and create artifacts as needed - * - * @param pluginName Name of volatility module run + * + * @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); + 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); + 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 = 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); + Set fileSet = parsePstree(PluginOutput); + flagFiles(fileSet, pluginName); } else if (pluginName.matches("svcscan")) { - Set fileSet = parseSvcscan(PluginOutput); - flagFiles(fileSet, pluginName); + 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); - } - } + //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 - * + /** + * 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 */ private String normalizePath(String filePath) { - if (filePath == null) + if (filePath == null) { return ""; - - filePath = filePath.trim(); + } + String path = filePath.trim(); // strip C: and \??\C: - if (filePath.contains(":")) { - filePath = filePath.substring(filePath.indexOf(":") + 1); + if (path.contains(":")) { + path = path.substring(filePath.indexOf(':') + 1); } - + // change slash direction - filePath = filePath.replaceAll("\\\\", "/"); - filePath = filePath.toLowerCase(); - - filePath = filePath.replaceAll("/systemroot/", "/windows/"); + path = path.replaceAll("\\\\", "/"); + path = path.toLowerCase(); + + 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/"); + path = path.replaceAll("/device/", ""); // 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")) { // 16 advances beyond harddiskvolume and the number - filePath = filePath.substring(filePath.indexOf("/harddiskvolume") + 16); + path = path.substring(path.indexOf("/harddiskvolume") + 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/")) { + return ""; + } + + return path; } - + private Set parseHandles(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) { - // 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(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; + 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("\"")); + file_path = file_path.substring(0, file_path.indexOf('\"')); } // 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.contains("HardDiskVolume") == false) { 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); - } + } return fileSet; } - + private Set parseDllList(File pluginFile) { Set fileSet = new HashSet<>(); // read the first line from the text file try (BufferedReader br = new BufferedReader(new FileReader(pluginFile))) { String line; while ((line = br.readLine()) != null) { - - String TAG = "Command line : "; - if (line.startsWith(TAG)) { - if(line.length() > TAG.length()) { + String TAG = "Command line : "; + if (line.startsWith(TAG)) { + + if (line.length() > TAG.length()) { String file_path; // Command line : "C:\Program Files\VMware\VMware Tools\vmacthlp.exe" @@ -468,21 +474,19 @@ class VolatilityProcessor { if (line.charAt(TAG.length()) == '\"') { file_path = line.substring(TAG.length() + 1); if (file_path.contains("\"")) { - file_path = file_path.substring(0, file_path.indexOf("\"")); + file_path = file_path.substring(0, file_path.indexOf('\"')); } - } - // Command line : C:\WINDOWS\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,3072,512 + } // 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(" ")) { - file_path = file_path.substring(0, file_path.indexOf(" ")); + file_path = file_path.substring(0, file_path.indexOf(' ')); } } fileSet.add(normalizePath(file_path)); } - } - // 0x4a680000 0x5000 0xffff \??\C:\WINDOWS\system32\csrss.exe + } // 0x4a680000 0x5000 0xffff \??\C:\WINDOWS\system32\csrss.exe // 0x7c900000 0xb2000 0xffff C:\WINDOWS\system32\ntdll.dll else if (line.startsWith("0x") && line.length() > 33) { // These lines do not have arguments @@ -494,34 +498,10 @@ class VolatilityProcessor { String msg = "Error parsing dlllist output"; logger.log(Level.SEVERE, msg, ex); errorMsgs.add(msg); - } - return fileSet; + } + 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 { - String file_path; - 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) { Set fileSet = new HashSet<>(); // read the first line from the text file @@ -538,222 +518,214 @@ class VolatilityProcessor { if (line.charAt(TAG.length()) == '\"') { 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 + file_path = file_path.substring(0, file_path.indexOf('\"')); + } + } // 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(" ")) { - file_path = file_path.substring(0, file_path.indexOf(" ")); + file_path = file_path.substring(0, file_path.indexOf(' ')); } } fileSet.add(normalizePath(file_path)); } } } - + } catch (IOException ex) { String msg = "Error parsing cmdline output"; logger.log(Level.SEVERE, msg, ex); errorMsgs.add(msg); - } - return fileSet; + } + return fileSet; } - + private Set parseShimcache(File PluginFile) { 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(PluginFile))) { + // 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("\"")); - } + file_path = file_path.substring(0, file_path.indexOf('\"')); + } fileSet.add(normalizePath(file_path)); - } - } - br.close(); - } catch (IOException ex) { + } + } + } catch (IOException ex) { String msg = "Error parsing shimcache output"; logger.log(Level.SEVERE, msg, ex); errorMsgs.add(msg); - } - return fileSet; + } + return fileSet; } - + private Set parsePsscan(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) { + try (BufferedReader br = new BufferedReader(new FileReader(PluginFile))) { + // 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) + if (line.startsWith("0x") == false) { 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")) { continue; + } fileSet.add(file_path); - } - br.close(); - } catch (IOException ex) { + } + } catch (IOException ex) { String msg = "Error parsing psscan output"; logger.log(Level.SEVERE, msg, ex); errorMsgs.add(msg); - } - return fileSet; + } + return fileSet; } private Set parsePslist(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) { - if (line.startsWith("0x") == false) - continue; - + 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; + } + // 0x89cfb998 csrss.exe 704 640 14 532 0 0 2017-12-07 14:05:34 UTC+0000 String file_path; 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")) { continue; + } fileSet.add(file_path); - } - br.close(); - } catch (IOException ex) { + } + } catch (IOException ex) { String msg = "Error parsing pslist output"; logger.log(Level.SEVERE, msg, ex); errorMsgs.add(msg); - } - return fileSet; + } + 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) { + 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) + if (line.startsWith("0x") == false) { 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")) + if (file_path.equals("system")) { continue; + } fileSet.add(file_path); - } - br.close(); - } catch (IOException ex) { + } + } catch (IOException ex) { String msg = "Error parsing psxview output"; logger.log(Level.SEVERE, msg, ex); errorMsgs.add(msg); - } - return fileSet; + } + return fileSet; } private Set parsePstree(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) { - // ... 0x897e5020:services.exe 772 728 15 287 2017-12-07 14:05:35 UTC+000 + 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 String file_path; String TAG = ":"; if (line.contains(TAG)) { - file_path = line.substring(line.indexOf(":") + 1, 52); + file_path = line.substring(line.indexOf(':') + 1, 52); file_path = normalizePath(file_path); - + // ignore system, it's not really a path - if (file_path.equals("system")) + if (file_path.equals("system")) { continue; + } fileSet.add(file_path); } - } - br.close(); - } catch (IOException ex) { + } + } catch (IOException ex) { String msg = "Error parsing pstree output"; logger.log(Level.SEVERE, msg, ex); errorMsgs.add(msg); - } - return fileSet; + } + return fileSet; } private Set parseSvcscan(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 (BufferedReader br = new BufferedReader(new FileReader(PluginFile))) { + // read the first line from the text file + while ((line = br.readLine()) != null) { String file_path; String TAG = "Binary Path: "; if (line.startsWith(TAG)) { if (line.charAt(TAG.length()) == '\"') { - file_path = line.substring(TAG.length()+1); + file_path = line.substring(TAG.length() + 1); if (file_path.contains("\"")) { - file_path = file_path.substring(0, file_path.indexOf("\"")); + file_path = file_path.substring(0, file_path.indexOf('\"')); } - } - // Binary Path: - + } // Binary Path: - else if (line.charAt(TAG.length()) == '-') { continue; - } - // Command line : C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted + } // Command line : C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted else { file_path = line.substring(TAG.length()); if (file_path.contains(" ")) { - file_path = file_path.substring(0, file_path.indexOf(" ")); + file_path = file_path.substring(0, file_path.indexOf(' ')); } // We can't do anything with driver entries if (file_path.startsWith("\\Driver\\")) { continue; - } - else if (file_path.startsWith("\\FileSystem\\")) { + } else if (file_path.startsWith("\\FileSystem\\")) { continue; } } fileSet.add(normalizePath(file_path)); } - } - br.close(); - } catch (IOException ex) { + } + } catch (IOException ex) { String msg = "Error parsing svcscan output"; logger.log(Level.SEVERE, msg, ex); errorMsgs.add(msg); - } - return fileSet; + } + return fileSet; } - + void cancel() { isCancelled = true; } + } From c46ab10f06cba638fd02895b9d1e8411b7cc578f Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Thu, 29 Mar 2018 12:28:13 -0400 Subject: [PATCH 03/23] 3591: Rename variables and test for ignore unallocated space. --- .../org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 96e60e1d82..dc6ddbc059 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 @@ -159,10 +159,10 @@ public class IngestFileFiltersTest extends TestCase { } public void testExtAndDirWithOneRule() { - HashMap rule = new HashMap<>(); - rule.put("Rule", new Rule("testFileType", new Rule.ExtensionCondition("jpg"), new MetaTypeCondition(MetaTypeCondition.Type.FILES), new ParentPathCondition("dir1"), null, null, null)); + HashMap rules = new HashMap<>(); + rules.put("Rule", new Rule("testFileType", new Rule.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 Files_Ext_Dirs_Filter = new FilesSet("Filter", "Filter to find all jpg files in dir1.", false, false, rule); + FilesSet Files_Ext_Dirs_Filter = new FilesSet("Filter", "Filter to find all jpg files in dir1.", false, true, rules); try { Case openCase = Case.getOpenCase(); From 985651023aa03d6cf8cd615f86e8ab7f0f8ca70d Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 29 Mar 2018 17:05:11 -0400 Subject: [PATCH 04/23] Made newCase() method synchronized and added a check for existing database --- .../centralrepository/datamodel/AbstractSqlEamDb.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index b929956b1c..36d956b09c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -176,8 +176,14 @@ public abstract class AbstractSqlEamDb implements EamDb { * @returns New Case class with populated database ID */ @Override - public CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { + public synchronized CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { Connection conn = connect(); + + // check if there is already an existing CorrelationCase for this Case + CorrelationCase cRCase = getCaseByUUID(eamCase.getCaseUUID()); + if (cRCase != null) { + return cRCase; + } PreparedStatement preparedStatement = null; From f3d4232d2b3e9974e1b0a4a14faa11898caf2a4f Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Thu, 29 Mar 2018 20:00:25 -0400 Subject: [PATCH 05/23] Volatility DSP clean up, bug fixes, error handling --- .../volatilityDSP/AddMemoryImageTask.java | 177 ++--- .../volatilityDSP/MemoryDSProcessor.java | 66 +- .../volatilityDSP/VolatilityProcessor.java | 650 ++++++++++-------- 3 files changed, 504 insertions(+), 389 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/AddMemoryImageTask.java index 17b3c7c0a7..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,6 +23,7 @@ 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; @@ -31,72 +32,80 @@ 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 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,62 +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; - try { - caseDatabase = Case.getOpenCase().getSleuthkitCase(); - } catch (NoCurrentCaseException ex) { - errorMessages.add(Bundle.AddMemoryImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddMemoryImageTask_for_device() + deviceId + ":" + ex.getLocalizedMessage()); - return null; - } + @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)); + SleuthkitCase caseDatabase = Case.getOpenCase().getSleuthkitCase(); caseDatabase.acquireSingleUserCaseWriteLock(); - - // 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; - } - 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.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/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 a66e0204d9..f89b2ac207 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/volatilityDSP/VolatilityProcessor.java @@ -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,8 +29,8 @@ 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; @@ -52,69 +51,82 @@ 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 static final Logger logger = Logger.getLogger(VolatilityProcessor.class.getName()); 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 File executableFile; private Case currentCase; + private File executableFile; private String moduleOutputPath; private FileManager fileManager; private volatile boolean isCancelled; /** + * Constructs a processor that runs Volatility on a given memory image file + * and parses the output to create artifacts. * - * @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 + * @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 + * Runs Volatility on a given memory image file and parses the output to + * create artifacts. * - * @returns true if there was a critical error + * @throws VolatilityProcessorException If there is a critical error during + * processing. */ - boolean run() { + @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) { - logger.log(Level.SEVERE, "Failed to get the current case", ex); - return true; + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_progressMessage_noCurrentCase(), ex); } - executableFile = locateExecutable(); + executableFile = locateVolatilityExecutable(); if (executableFile == null) { - logger.log(Level.SEVERE, "Volatility exe not found"); - return true; + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_exceptionMessage_volatilityExeNotFound()); } fileManager = currentCase.getServices().getFileManager(); - // make a unique folder for this image + /* + * Make an output folder unique to this data source. + */ Long dataSourceId = dataSource.getId(); - moduleOutputPath = Paths.get(currentCase.getModuleDirectory(), VOLATILITY_DIRECTORY, dataSourceId.toString()).toString(); + 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); @@ -124,97 +136,118 @@ class VolatilityProcessor { 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() + * Gets a list of error messages that were generated during the processing. * - * @return + * @return The list of error messages. */ List getErrorMessages() { return new ArrayList<>(errorMsgs); } - private void executeAndParseVolatility(String pluginToRun) { + /** + * 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 { - 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())); - 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 - 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); } + + 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 executable, if able. + * Finds and returns the path to the Volatility executable, if able. * - * @param executableToFindName The name of the executable to find - * - * @return A File reference or null + * @return A File reference or null. */ - private static File locateExecutable() { - // Must be running under a Windows operating system. + private static File locateVolatilityExecutable() { if (!PlatformUtil.isWindowsOS()) { return null; } - String executableToFindName = Paths.get(VOLATILITY_DIRECTORY, VOLATILITY_EXECUTABLE).toString(); - + String executableToFindName = Paths.get(VOLATILITY, VOLATILITY_EXECUTABLE).toString(); File exeFile = InstalledFileLocator.getDefault().locate(executableToFindName, VolatilityProcessor.class.getPackage().getName(), false); if (null == exeFile) { return null; @@ -227,41 +260,54 @@ 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 + @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(":"); - String[] memProfile = profileLine[1].split(",|\\("); - return memProfile[0].replaceAll("\\s+", ""); + String[] profileLine = fileRead.split(":"); //NON-NLS + String[] memProfile = profileLine[1].split(",|\\("); //NON-NLS + return memProfile[0].replaceAll("\\s+", ""); //NON-NLS } catch (IOException ex) { - Exceptions.printStackTrace(ex); - // @@@ Need to log this or rethrow it + throw new VolatilityProcessorException(Bundle.VolatilityProcessor_exceptionMessage_failedToParseImageInfo(), ex); } - - return null; } /** - * Lookup the set of files and add INTERESTING_ITEM artifacts for them. + * Adds interesting file artifacts for files found by a Volatility plugin. * - * @param fileSet - * @param pluginName + * @param fileSet The paths of the files within the memeory image data + * source. + * @param pluginName The name of the source Volatility plugin. */ - private void flagFiles(Set fileSet, String pluginName) { - + @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; } + 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; } @@ -271,22 +317,22 @@ class VolatilityProcessor { 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 += ".%"; //NON-NLS if (filePath == null) { resolvedFiles = fileManager.findFiles(fileName); //NON-NLS } else { @@ -296,151 +342,159 @@ class VolatilityProcessor { } if (resolvedFiles.isEmpty()) { - logger.log(Level.SEVERE, "File not found in lookup: {0}/{1}", new Object[]{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) { - 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 + * Parses the output of a Volatility plugin and creates artifacts as needed. * - * @param pluginName Name of volatility module run - * @param PluginOutput File that contains the output to parse + * @param pluginName Name of the Volatility plugin. + * @param pluginOutputFile File that contains the output to parse. */ - private void scanOutputFile(String pluginName, File PluginOutput) { + 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 (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); + if (fileSet != null && !fileSet.isEmpty()) { flagFiles(fileSet, pluginName); } } /** - * Normalize the path we parse out of the output before we look it up in the - * case DB + * 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 + * @param filePath Path to normalize. * - * @return Normalized version + * @return The normalized path or the empty string if the path cannot be + * normalized or should be ignored. */ - private String normalizePath(String filePath) { + private static String normalizePath(String filePath) { if (filePath == null) { - return ""; + return ""; //NON-NLS } String path = filePath.trim(); - + // strip C: and \??\C: - if (path.contains(":")) { - path = path.substring(filePath.indexOf(':') + 1); + if (path.contains(":")) { //NON-NLS + path = path.substring(filePath.indexOf(':') + 1); //NON-NLS } // change slash direction - path = path.replaceAll("\\\\", "/"); + path = path.replaceAll("\\\\", "/"); //NON-NLS path = path.toLowerCase(); - path = path.replaceAll("/systemroot/", "/windows/"); + path = path.replaceAll("/systemroot/", "/windows/"); //NON-NLS // catches 1 type of file in cmdline - path = path.replaceAll("%systemroot%", "/windows/"); - path = path.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 (path.contains("/harddiskvolume")) { + if (path.contains("/harddiskvolume")) { //NON-NLS // 16 advances beyond harddiskvolume and the number - path = path.substring(path.indexOf("/harddiskvolume") + 16); + path = path.substring(path.indexOf("/harddiskvolume") + 16); //NON-NLS } // no point returning these. We won't map to them - if (path.startsWith("/namedpipe/")) { - return ""; + 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))) { + 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) { + if (line.startsWith("0x") == false) { //NON-NLS continue; } - String TAG = " File "; + 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; } } @@ -449,21 +503,24 @@ class VolatilityProcessor { } } } 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) { - String TAG = "Command line : "; + String TAG = "Command line : "; //NON-NLS if (line.startsWith(TAG)) { if (line.length() > TAG.length()) { @@ -471,60 +528,63 @@ class VolatilityProcessor { // 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('\"')); + 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(" ")) { - 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)); } } // 0x4a680000 0x5000 0xffff \??\C:\WINDOWS\system32\csrss.exe // 0x7c900000 0xb2000 0xffff C:\WINDOWS\system32\ntdll.dll - else if (line.startsWith("0x") && line.length() > 33) { + else if (line.startsWith("0x") && line.length() > 33) { //NON-NLS // These lines do not have arguments String file_path = line.substring(33); fileSet.add(normalizePath(file_path)); } } } catch (IOException ex) { - String msg = "Error parsing dlllist output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); + 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 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)) { 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('\"')); + 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(' ')); } } @@ -534,17 +594,20 @@ class VolatilityProcessor { } } catch (IOException ex) { - String msg = "Error parsing cmdline output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); + 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))) { + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { // ignore the first 2 header lines br.readLine(); br.readLine(); @@ -554,56 +617,62 @@ class VolatilityProcessor { //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)); } } } catch (IOException ex) { - String msg = "Error parsing shimcache output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); + 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))) { + 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) { + if (line.startsWith("0x") == false) { //NON-NLS 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); } } catch (IOException ex) { - String msg = "Error parsing psscan output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); + 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))) { + 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) { + if (line.startsWith("0x") == false) { //NON-NLS continue; } @@ -613,119 +682,152 @@ class VolatilityProcessor { 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); } } catch (IOException ex) { - String msg = "Error parsing pslist output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); + 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 parsePsxview(File PluginFile) { + private Set parsePsxviewOutput(File outputFile) { String line; Set fileSet = new HashSet<>(); - try (BufferedReader br = new BufferedReader(new FileReader(PluginFile))) { + 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) { + if (line.startsWith("0x") == false) { //NON-NLS 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")) { + if (file_path.equals("system")) { //NON-NLS continue; } fileSet.add(file_path); } } catch (IOException ex) { - String msg = "Error parsing psxview output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); + 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 parsePstree(File PluginFile) { + private Set parsePstreeOutput(File outputFile) { String line; Set fileSet = new HashSet<>(); - try (BufferedReader br = new BufferedReader(new FileReader(PluginFile))) { + 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 file_path; String TAG = ":"; if (line.contains(TAG)) { - file_path = line.substring(line.indexOf(':') + 1, 52); + 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); } } } catch (IOException ex) { - String msg = "Error parsing pstree output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); + 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 outputFile) { String line; Set fileSet = new HashSet<>(); - try (BufferedReader br = new BufferedReader(new FileReader(PluginFile))) { + try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) { // read the first line from the text file while ((line = br.readLine()) != null) { String file_path; - String TAG = "Binary Path: "; + String TAG = "Binary Path: "; //NON-NLS if (line.startsWith(TAG)) { - if (line.charAt(TAG.length()) == '\"') { - file_path = line.substring(TAG.length() + 1); - if (file_path.contains("\"")) { - file_path = file_path.substring(0, file_path.indexOf('\"')); - } - } // Binary Path: - - else if (line.charAt(TAG.length()) == '-') { - continue; - } // Command line : C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted - else { - file_path = line.substring(TAG.length()); - if (file_path.contains(" ")) { - file_path = file_path.substring(0, file_path.indexOf(' ')); - } - // We can't do anything with driver entries - if (file_path.startsWith("\\Driver\\")) { + switch (line.charAt(TAG.length())) { + // Binary Path: - + case '\"': //NON-NLS + file_path = line.substring(TAG.length() + 1); + if (file_path.contains("\"")) { //NON-NLS + file_path = file_path.substring(0, file_path.indexOf('\"')); //NON-NLS + } + break; + // Command line : C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted + case '-': continue; - } else if (file_path.startsWith("\\FileSystem\\")) { - continue; - } + default: + file_path = line.substring(TAG.length()); + if (file_path.contains(" ")) { //NON-NLS + file_path = file_path.substring(0, file_path.indexOf(' ')); + } // We can't do anything with driver entries + if (file_path.startsWith("\\Driver\\")) { //NON-NLS + continue; + } else if (file_path.startsWith("\\FileSystem\\")) { //NON-NLS + continue; + } + break; } fileSet.add(normalizePath(file_path)); } } } catch (IOException ex) { - String msg = "Error parsing svcscan output"; - logger.log(Level.SEVERE, msg, ex); - errorMsgs.add(msg); + errorMsgs.add(Bundle.VolatilityProcessor_errorMessage_outputParsingError("svcscan")); + /* + * 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("svcscan"), ex); } 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); + } + } + } From 8c8f77952e6d74fd44c9a09016f279f984088715 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Thu, 29 Mar 2018 21:24:28 -0400 Subject: [PATCH 06/23] Always closing local connection --- .../autopsy/centralrepository/datamodel/AbstractSqlEamDb.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 36d956b09c..37cb78a51e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -177,7 +177,6 @@ public abstract class AbstractSqlEamDb implements EamDb { */ @Override public synchronized CorrelationCase newCase(CorrelationCase eamCase) throws EamDbException { - Connection conn = connect(); // check if there is already an existing CorrelationCase for this Case CorrelationCase cRCase = getCaseByUUID(eamCase.getCaseUUID()); @@ -185,6 +184,7 @@ public abstract class AbstractSqlEamDb implements EamDb { return cRCase; } + Connection conn = connect(); PreparedStatement preparedStatement = null; String sql = "INSERT INTO cases(case_uid, org_id, case_name, creation_date, case_number, " From 11787f1adb49d0024110eb27efa5f01e34ed1089 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Fri, 30 Mar 2018 10:04:46 -0400 Subject: [PATCH 07/23] Autopsy copyright updated. --- Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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:/// From b791a02fb4020f7d4e0c83fb9a64b293aaeb49e8 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Fri, 30 Mar 2018 11:11:22 -0400 Subject: [PATCH 08/23] Fix infinite loop in MessageContentViewer --- .../sleuthkit/autopsy/contentviewers/MessageContentViewer.java | 1 - 1 file changed, 1 deletion(-) 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); } From 3c47b6a7d218e8d1be92a286992373e83c750d34 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 30 Mar 2018 17:08:48 -0400 Subject: [PATCH 09/23] 3591: Correct the error message. --- .../sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 dc6ddbc059..6f56bd2654 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 @@ -160,7 +160,7 @@ public class IngestFileFiltersTest extends TestCase { public void testExtAndDirWithOneRule() { HashMap rules = new HashMap<>(); - rules.put("Rule", new Rule("testFileType", new Rule.ExtensionCondition("jpg"), new MetaTypeCondition(MetaTypeCondition.Type.FILES), new ParentPathCondition("dir1"), null, null, null)); + rules.put("Rule", new Rule("testExtAndDirWithOneRule", new Rule.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 Files_Ext_Dirs_Filter = new FilesSet("Filter", "Filter to find all jpg files in dir1.", false, true, rules); @@ -173,10 +173,10 @@ public class IngestFileFiltersTest extends TestCase { 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 passed by the file filter.", file.getName(), file.getId()); - assertTrue(errMsg, file.getMIMEType() != null); + 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 caught by the file filter.", file.getName(), file.getId()); + String errMsg = String.format("File %s (objId=%d) unexpectedly passed by the file filter.", file.getName(), file.getId()); assertTrue(errMsg, file.getMIMEType() == null); } } From 6762473866b188ce808328c6759e9c9d3c8e0423 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 30 Mar 2018 17:17:06 -0400 Subject: [PATCH 10/23] 3591: clean up the imports. --- .../src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 2 -- 1 file changed, 2 deletions(-) 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 6f56bd2654..053cae21d0 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 @@ -25,9 +25,7 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; import junit.framework.TestCase; import org.netbeans.junit.NbModuleSuite; import org.sleuthkit.autopsy.casemodule.Case; From 2ea0cb0bb0c88e77c693cd54a7e9592f0a989ef4 Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Fri, 30 Mar 2018 17:24:56 -0400 Subject: [PATCH 11/23] 3710 editing a file filter without adding any new ones no longer uses stale filter --- .../ingest/IngestJobSettingsPanel.java | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) 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 From d085eb449a075bc551ea6da01428daa50abbbdbc Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Mon, 2 Apr 2018 08:30:27 -0400 Subject: [PATCH 12/23] Replace computeIfAbsent lambda --- .../sleuthkit/autopsy/keywordsearch/RegexQuery.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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)); } - }); + } } } From cbf7637bb560bb2c573f0b1bba9555d0020cc81a Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 2 Apr 2018 14:18:23 -0400 Subject: [PATCH 13/23] 3591: Let filemanager to get all files, so that include gif file for testing. --- .../org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 053cae21d0..829650cbfc 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 @@ -166,8 +166,8 @@ public class IngestFileFiltersTest extends TestCase { Case openCase = Case.getOpenCase(); runIngestJob(openCase.getDataSources(), Files_Ext_Dirs_Filter); FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); - List results = fileManager.findFiles("%.jpg%"); - assertEquals(12, results.size()); + 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")) { From c63abe1daf1b2931d66dd20e4a6c22acae19f39c Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 2 Apr 2018 20:22:32 -0400 Subject: [PATCH 14/23] Decouple LocalFilesDSProcessor.process from canProcess side effect --- .../autopsy/casemodule/LocalFilesDSProcessor.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java index 63b255113b..1b80f316b1 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; @@ -51,7 +50,8 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; * method overload to allow it to be used independently of the wizard. */ @ServiceProviders(value = { - @ServiceProvider(service = DataSourceProcessor.class), + @ServiceProvider(service = DataSourceProcessor.class) + , @ServiceProvider(service = AutoIngestDataSourceProcessor.class)} ) @Messages({ @@ -344,15 +344,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 +369,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); } /** From 88b675b43df18677f7547b76338b2c9d6b5ad54a Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 2 Apr 2018 20:24:38 -0400 Subject: [PATCH 15/23] Decouple LocalFilesDSProcessor.process from canProcess side effect --- .../autopsy/casemodule/LocalFilesDSProcessor.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java index 1b80f316b1..f98e8264ac 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesDSProcessor.java @@ -50,10 +50,9 @@ import org.sleuthkit.autopsy.datasourceprocessors.AutoIngestDataSourceProcessor; * method overload to allow it to be used independently of the wizard. */ @ServiceProviders(value = { - @ServiceProvider(service = DataSourceProcessor.class) - , - @ServiceProvider(service = AutoIngestDataSourceProcessor.class)} -) + @ServiceProvider(service = DataSourceProcessor.class), + @ServiceProvider(service = AutoIngestDataSourceProcessor.class) +}) @Messages({ "LocalFilesDSProcessor.logicalEvidenceFilter.desc=Logical Evidence Files (L01)" }) From 5373108e0c7a66d0a6346329a3f83aec05282edf Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 3 Apr 2018 14:49:52 -0400 Subject: [PATCH 16/23] 3591: Rename variables and rewrite the assert condition. --- .../sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 829650cbfc..05cc84389f 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 @@ -143,7 +143,7 @@ public class IngestFileFiltersTest extends TestCase { 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 passed by the file filter.", file.getName(), file.getId()); - assertTrue(errMsg, !(file.getMIMEType() == null || file.getMIMEType().isEmpty())); + 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 caught by the file filter.", file.getName(), file.getId()); @@ -160,11 +160,11 @@ public class IngestFileFiltersTest extends TestCase { HashMap rules = new HashMap<>(); rules.put("Rule", new Rule("testExtAndDirWithOneRule", new Rule.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 Files_Ext_Dirs_Filter = new FilesSet("Filter", "Filter to find all jpg files in dir1.", false, true, rules); + FilesSet filesExtDirsFilter = new FilesSet("Filter", "Filter to find all jpg files in dir1.", false, true, rules); try { Case openCase = Case.getOpenCase(); - runIngestJob(openCase.getDataSources(), Files_Ext_Dirs_Filter); + runIngestJob(openCase.getDataSources(), filesExtDirsFilter); FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); List results = fileManager.findFiles("%%"); assertEquals(62, results.size()); @@ -172,7 +172,7 @@ public class IngestFileFiltersTest extends TestCase { //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())); + 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); From db65143276309865e302ac328f435945f0456395 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 3 Apr 2018 16:16:50 -0400 Subject: [PATCH 17/23] 3591: Print out the error message from running ingest job. --- .../org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 3 +++ 1 file changed, 3 insertions(+) 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 05cc84389f..869288df92 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 @@ -194,6 +194,9 @@ public class IngestFileFiltersTest extends TestCase { 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); From 7f50490642de89d683679b2d454c8ad4db882392 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 3 Apr 2018 17:34:34 -0400 Subject: [PATCH 18/23] 3592: 3rd Tests for file filter. --- .../autopsy/ingest/IngestFileFiltersTest.java | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) 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..6514425f76 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,7 @@ 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.MetaTypeCondition; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet.Rule.ParentPathCondition; import org.sleuthkit.autopsy.testutils.DataSourceProcessorRunner; @@ -160,7 +161,45 @@ public class IngestFileFiltersTest extends NbTestCase { 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 should have MIME type + 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); + } + } + private void runIngestJob(List datasources, FilesSet filter) { FileTypeIdModuleFactory factory = new FileTypeIdModuleFactory(); IngestModuleIngestJobSettings settings = factory.getDefaultIngestJobSettings(); From d801a661388550fa45807c415a8aa002c1a8288b Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 3 Apr 2018 17:39:32 -0400 Subject: [PATCH 19/23] 3592: 3rd Tests for file filter. --- .../src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6514425f76..a196c54a37 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 @@ -181,7 +181,7 @@ public class IngestFileFiltersTest extends NbTestCase { 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 should have MIME type + //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); From be73e3f0856c88407dd2d2288afcf2bd3411dbc8 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 3 Apr 2018 17:46:42 -0400 Subject: [PATCH 20/23] 3592: 3rd Tests for file filter. --- .../sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 3a4a50e5ce..fbc3a3e1f5 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 @@ -161,8 +161,8 @@ public class IngestFileFiltersTest extends NbTestCase { Assert.fail(ex); } } - - public void testExtAndDirWithOneRule() { + + public void testExtAndDirWithOneRule() { HashMap rules = new HashMap<>(); rules.put("Rule", new Rule("testExtAndDirWithOneRule", new Rule.ExtensionCondition("jpg"), new MetaTypeCondition(MetaTypeCondition.Type.FILES), new ParentPathCondition("dir1"), null, null, null)); //Build the filter that ignore unallocated space and with one rule @@ -189,7 +189,8 @@ public class IngestFileFiltersTest extends NbTestCase { Assert.fail(ex); } } - public void testExtAndDirWithTwoRules() { + + 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)); From 836a53875f049e53047e9a0963b51f8b79b0a8e9 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 3 Apr 2018 18:18:13 -0400 Subject: [PATCH 21/23] 3592: Import the ExtensionCondition to make the construction of a rule cleaner. --- .../src/org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fbc3a3e1f5..1004adaf54 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 @@ -164,7 +164,7 @@ public class IngestFileFiltersTest extends NbTestCase { public void testExtAndDirWithOneRule() { HashMap rules = new HashMap<>(); - rules.put("Rule", new Rule("testExtAndDirWithOneRule", new Rule.ExtensionCondition("jpg"), new MetaTypeCondition(MetaTypeCondition.Type.FILES), new ParentPathCondition("dir1"), null, null, null)); + 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); From c3eadcb9f65148f47f8f4799c34ce954962ab785 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Wed, 4 Apr 2018 10:07:54 -0400 Subject: [PATCH 22/23] 3596: 4th test for file filter. --- .../autopsy/ingest/IngestFileFiltersTest.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) 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 1004adaf54..de89fac754 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 @@ -45,6 +45,7 @@ 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; @@ -227,6 +228,33 @@ public class IngestFileFiltersTest extends NbTestCase { } } + 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 have 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); + } 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(); From 186c037b21b2767f3094cae602d0ca48e99b2049 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Wed, 4 Apr 2018 10:13:40 -0400 Subject: [PATCH 23/23] 3596: 4th test for file filter. --- .../org/sleuthkit/autopsy/ingest/IngestFileFiltersTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 de89fac754..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 @@ -241,10 +241,10 @@ public class IngestFileFiltersTest extends NbTestCase { List results = fileManager.findFiles("%%"); assertEquals(62, results.size()); for (AbstractFile file : results) { - //Only file.docx have MIME Type + //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); + 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);