Merge pull request #2340 from zhhl/1968-moveBulkExtractor

1968 move bulk extractor
This commit is contained in:
Richard Cordovano 2016-09-14 18:23:38 -04:00 committed by GitHub
commit c65672de36
10 changed files with 903 additions and 0 deletions

View File

@ -0,0 +1,62 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.ingestmodules.bulkextractor;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
/**
* Ingest job settings for the Bulk Extractor ingest module.
*/
final class BulkExtractorIngestJobSettings implements IngestModuleIngestJobSettings {
private static final long serialVersionUID = 1L;
private final boolean onlyProcessUnallocatedSpace;
/**
* Constructs serializable ingest job settings for the Bulk Extractor ingest
* module.
*
* @param onlyProcessUnallocatedSpace Whether or not only unallocated space
* should be processed; if not, the
* entire disk image is scanned.
*/
BulkExtractorIngestJobSettings(boolean onlyProcessUnallocatedSpace) {
this.onlyProcessUnallocatedSpace = onlyProcessUnallocatedSpace;
}
/**
* @inheritDoc
*/
@Override
public long getVersionNumber() {
return BulkExtractorIngestJobSettings.serialVersionUID;
}
/**
* Queries whether or not to process only unallocated space; if not, the
* entire disk image is scanned.
*
* @return True if only unallocated space is to be processed, false
* otherwise.
*/
boolean shouldOnlyProcessUnallocatedSpace() {
return this.onlyProcessUnallocatedSpace;
}
}

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="processUnallocatedSpaceCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="83" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="processUnallocatedSpaceCheckBox" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="151" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="processUnallocatedSpaceCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/ingestmodules/bulkextractor/Bundle.properties" key="BulkExtractorIngestJobSettingsPanel.processUnallocatedSpaceCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,82 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.ingestmodules.bulkextractor;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/**
* Ingest job settings panel for the Bulk Extractor ingest module.
*/
final class BulkExtractorIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel {
/**
* Constructs an ingest job settings panel for the Bulk Extractor ingest
* module.
*
* @param settings The settings to use to initialize the panel.
*/
BulkExtractorIngestJobSettingsPanel(BulkExtractorIngestJobSettings settings) {
initComponents();
this.processUnallocatedSpaceCheckBox.setSelected(settings.shouldOnlyProcessUnallocatedSpace());
}
/**
* @inheritDoc
*/
@Override
public IngestModuleIngestJobSettings getSettings() {
return new BulkExtractorIngestJobSettings(this.processUnallocatedSpaceCheckBox.isSelected());
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
processUnallocatedSpaceCheckBox = new javax.swing.JCheckBox();
org.openide.awt.Mnemonics.setLocalizedText(processUnallocatedSpaceCheckBox, org.openide.util.NbBundle.getMessage(BulkExtractorIngestJobSettingsPanel.class, "BulkExtractorIngestJobSettingsPanel.processUnallocatedSpaceCheckBox.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(processUnallocatedSpaceCheckBox)
.addContainerGap(83, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(processUnallocatedSpaceCheckBox)
.addContainerGap(151, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox processUnallocatedSpaceCheckBox;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,129 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.ingestmodules.bulkextractor;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.coreutils.Version;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel;
/**
* A factory for creating instances of data source and file ingest modules that
* use Bulk Extractor to scan either an entire disk image or unallocated space
* files, respectively.
*/
@ServiceProvider(service = IngestModuleFactory.class)
public class BulkExtractorIngestModuleFactory extends IngestModuleFactoryAdapter {
/**
* @inheritDoc
*/
@Override
public String getModuleDisplayName() {
return Utilities.getModuleName();
}
/**
* @inheritDoc
*/
@Override
public String getModuleDescription() {
return NbBundle.getMessage(BulkExtractorIngestModuleFactory.class, "BulkExtractorIngestModuleFactory.moduleDescription");
}
/**
* @inheritDoc
*/
@Override
public String getModuleVersionNumber() {
return Utilities.getVersion();
}
/**
* @inheritDoc
*/
@Override
public IngestModuleIngestJobSettings getDefaultIngestJobSettings() {
// Process the entire image by default.
return new BulkExtractorIngestJobSettings(false);
}
/**
* @inheritDoc
*/
@Override
public boolean hasIngestJobSettingsPanel() {
return true;
}
/**
* @inheritDoc
*/
@Override
public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof BulkExtractorIngestJobSettings)) {
throw new IllegalArgumentException("Settings not instanceof src.org.sleuthkit.autopsy.bulkextractor.BulkExtractorIngestJobSettings"); // NON_NLS
}
return new BulkExtractorIngestJobSettingsPanel((BulkExtractorIngestJobSettings) settings);
}
/**
* @inheritDoc
*/
@Override
public boolean isDataSourceIngestModuleFactory() {
return true;
}
/**
* @inheritDoc
*/
@Override
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof BulkExtractorIngestJobSettings)) {
throw new IllegalArgumentException("Settings not instanceof src.org.sleuthkit.autopsy.bulkextractor.BulkExtractorIngestJobSettings"); // NON_NLS
}
return new DiskImageIngestModule((BulkExtractorIngestJobSettings) settings);
}
/**
* @inheritDoc
*/
@Override
public boolean isFileIngestModuleFactory() {
return true;
}
/**
* @inheritDoc
*/
@Override
public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) {
if (!(settings instanceof BulkExtractorIngestJobSettings)) {
throw new IllegalArgumentException("Settings not instanceof src.org.sleuthkit.autopsy.bulkextractor.BulkExtractorIngestJobSettings"); // NON_NLS
}
return new UnallocatedSpaceIngestModule((BulkExtractorIngestJobSettings) settings);
}
}

View File

@ -0,0 +1,9 @@
OpenIDE-Module-Name=Bulk Extractor Ingest Module
OpenIDE-Module-Display-Category=Ingest Module
OpenIDE-Module-Long-Description=Bulk Extractor ingest module. \n\n Runs the Bulk Extractor executable against an image data source.
OpenIDE-Module-Short-Description=Bulk Extractor ingest module.
Utilities.moduleName=Bulk Extractor
BulkExtractorIngestModuleFactory.moduleDescription=Runs the Bulk Extractor executable against an image data source.
BulkExtractorIngestJobSettingsPanel.processUnallocatedSpaceCheckBox.text=Only process unallocated space
BulkExtractorIngestModule.processTerminated=Bulk Extractor process was terminated due to exceeding max allowable run time when scanning
BulkExtractorIngestModule.moduleError=Bulk Extractor Module Error

View File

@ -0,0 +1,147 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.ingestmodules.bulkextractor;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.ingest.ProcTerminationCode;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProcessTerminator;
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestModule;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.Image;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A data source ingest module that runs the Bulk Extractor executable with an
* entire disk image as input.
*/
public class DiskImageIngestModule implements DataSourceIngestModule {
private static final Logger logger = Logger.getLogger(DiskImageIngestModule.class.getName());
private final BulkExtractorIngestJobSettings settings;
private IngestJobContext context;
private Path bulkExtractorPath;
private Path rootOutputDirPath;
/**
* Constructs a Bulk Extractor disk image ingest module.
*
* @param settings The settings for the ingest module.
*/
DiskImageIngestModule(BulkExtractorIngestJobSettings settings) {
this.settings = settings;
}
/**
* @inheritDoc
*/
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context;
// Only process data sources that are disk images the user wants scanned
// in their entirety. Scans of unallocated space only are performed by
// a file ingest module.
if (!(context.getDataSource() instanceof Image) || this.settings.shouldOnlyProcessUnallocatedSpace()) {
return;
}
this.bulkExtractorPath = Utilities.locateBulkExtractorExecutable();
this.rootOutputDirPath = Utilities.createOutputDirectoryForDataSource(this.context.getDataSource().getName() + "_"
+ this.context.getDataSource().getId());
}
/**
* @inheritDoc
*/
@Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
// Only process data sources that are disk images the user wants scanned
// in their entirety. Scans of unallocated space only are performed by
// a file ingest module.
if (!(dataSource instanceof Image) || this.settings.shouldOnlyProcessUnallocatedSpace()) {
return ProcessResult.OK;
}
/**
* Not sure how long it will take Bulk Extractor to complete.
*/
progressBar.switchToIndeterminate();
try {
// Verify initialization succeeded.
if ((null == this.bulkExtractorPath) || (null == this.rootOutputDirPath)) {
DiskImageIngestModule.logger.log(Level.SEVERE, "Bulk Extractor disk image ingest module called after failed start up"); // NON-NLS
return ProcessResult.ERROR;
}
// Make an output directory for Bulk Extractor.
Path outputDirPath = Utilities.getOutputSubdirectoryPath(this.rootOutputDirPath, dataSource.getId());
Files.createDirectories(outputDirPath);
// Scan the disk image file with Bulk Extractor.
Image image = (Image) dataSource;
String imageFilePath = image.getPaths()[0];
DataSourceIngestModuleProcessTerminator terminator = new DataSourceIngestModuleProcessTerminator(this.context, true);
int exitValue = Utilities.runBulkExtractor(this.bulkExtractorPath, outputDirPath, Paths.get(imageFilePath), terminator);
if (terminator.getTerminationCode() == ProcTerminationCode.TIME_OUT) {
String msg = NbBundle.getMessage(this.getClass(), "BulkExtractorIngestModule.processTerminated") + image.getName(); // NON-NLS
MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "BulkExtractorIngestModule.moduleError"), msg); // NON-NLS
DiskImageIngestModule.logger.log(Level.SEVERE, msg);
FileUtil.deleteDir(new File(outputDirPath.toAbsolutePath().toString()));
return IngestModule.ProcessResult.ERROR;
}
if (0 != exitValue) {
FileUtil.deleteDir(new File(outputDirPath.toAbsolutePath().toString()));
if (this.context.dataSourceIngestIsCancelled()) {
return ProcessResult.OK;
} else {
DiskImageIngestModule.logger.log(Level.SEVERE, "Bulk Extractor returned error exit value = {0} when scanning {1}", new Object[]{exitValue, image.getName()}); // NON-NLS
return ProcessResult.ERROR;
}
}
if (!Utilities.isDirectoryEmpty(outputDirPath)) {
// Add the output directory to the case as an Autopsy report.
Case.getCurrentCase().addReport(outputDirPath.toAbsolutePath().toString(), Utilities.getModuleName(), Utilities.getReportName(image.getName()));
}
return ProcessResult.OK;
} catch (InterruptedException | IOException | SecurityException | UnsupportedOperationException | TskCoreException ex) {
DiskImageIngestModule.logger.log(Level.SEVERE, "Error processing " + dataSource.getName() + " with Bulk Extractor", ex); // NON-NLS
return ProcessResult.ERROR;
}
}
}

View File

@ -0,0 +1,206 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.ingestmodules.bulkextractor;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.ingest.ProcTerminationCode;
import org.sleuthkit.autopsy.coreutils.FileUtil;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.FileIngestModule;
import org.sleuthkit.autopsy.ingest.FileIngestModuleProcessTerminator;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.ingest.IngestModule;
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
/**
* A file ingest module that runs the Bulk Extractor executable with unallocated
* space files as input.
*/
@NbBundle.Messages({
"SettingsWrong='Process unallocated space only' is checked for this module, but global 'Process Unallocated Space' is not checked. Unallocated space will not be processed.",
"Utilities.cannotCreateOutputDir.message=Unable to create output directory."
})
final class UnallocatedSpaceIngestModule implements FileIngestModule {
private static final String TEMP_DIR_NAME = "temp"; // NON-NLS
private static final Logger logger = Logger.getLogger(UnallocatedSpaceIngestModule.class.getName());
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
private static final Map<Long, Path> tempPathsByJob = new ConcurrentHashMap<>();
private final BulkExtractorIngestJobSettings settings;
private IngestJobContext context;
private Path bulkExtractorPath;
private Path rootOutputDirPath;
/**
* Constructs a file ingest module that runs the Bulk Extractor executable
* with unallocated space files as input.
*
* @param settings The settings for the ingest module.
*/
UnallocatedSpaceIngestModule(BulkExtractorIngestJobSettings settings) {
this.settings = settings;
}
/**
* @inheritDoc
*/
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context;
// Only process unallocated space files when the user does not want the
// data source to be scanned in its entirety. Scans of an entire disk
// image are performed by a data source ingest module.
if (!this.settings.shouldOnlyProcessUnallocatedSpace()) {
return;
}
// If the global unallocated space processing setting and the module
// process unallocated space only setting are not in sych, throw an
// exception. Although the result would not be incorrect, it would be
// unfortunate for the user to get an accidental no-op for this module.
if (!this.context.processingUnallocatedSpace()) {
throw new IngestModuleException(Bundle.SettingsWrong());
}
this.bulkExtractorPath = Utilities.locateBulkExtractorExecutable();
this.rootOutputDirPath = Utilities.createOutputDirectoryForDataSource(this.context.getDataSource().getName() + "_"
+ this.context.getDataSource().getId());
// The first instance of the module for an ingest job creates
// a temp subdirectory as a location for writing unallocated
// space files to disk.
if (UnallocatedSpaceIngestModule.refCounter.incrementAndGet(this.context.getJobId()) == 1) {
try {
Path tempDirPath = Paths.get(this.rootOutputDirPath.toString(), UnallocatedSpaceIngestModule.TEMP_DIR_NAME + Long.toString(this.context.getJobId()));
Files.createDirectory(tempDirPath);
UnallocatedSpaceIngestModule.tempPathsByJob.put(this.context.getJobId(), tempDirPath);
} catch (SecurityException | IOException | UnsupportedOperationException ex) {
throw new IngestModule.IngestModuleException(Bundle.Utilities_cannotCreateOutputDir_message(), ex);
}
}
}
/**
* @inheritDoc
*/
@Override
public ProcessResult process(AbstractFile file) {
// Only process unallocated space files when the user does not want the
// data source to be scanned in its entirety. Scans of an entire disk
// image are performed by a data source ingest module.
if (!this.settings.shouldOnlyProcessUnallocatedSpace()) {
return ProcessResult.OK;
}
// Skip everything except unallocated space files.
if (file.getType() != TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) {
return ProcessResult.OK;
}
Path tempFilePath = null;
try {
// Verify initialization succeeded.
if (null == this.bulkExtractorPath) {
UnallocatedSpaceIngestModule.logger.log(Level.SEVERE, "Bulk Extractor unallocated space ingest module called after failed start up"); // NON-NLS
return ProcessResult.ERROR;
}
// Write the file to disk.
Path tempDirPath = UnallocatedSpaceIngestModule.tempPathsByJob.get(this.context.getJobId());
tempFilePath = Paths.get(tempDirPath.toString(), file.getName());
ContentUtils.writeToFile(file, tempFilePath.toFile());
// Make an output directory for Bulk Extractor.
Path outputDirPath = Utilities.getOutputSubdirectoryPath(this.rootOutputDirPath, file.getId());
Files.createDirectories(outputDirPath);
// Scan the file with Bulk Extractor.
FileIngestModuleProcessTerminator terminator = new FileIngestModuleProcessTerminator(this.context, true);
int exitValue = Utilities.runBulkExtractor(this.bulkExtractorPath, outputDirPath, tempFilePath, terminator);
if (terminator.getTerminationCode() == ProcTerminationCode.TIME_OUT) {
String msg = NbBundle.getMessage(this.getClass(), "BulkExtractorIngestModule.processTerminated") + file.getName(); // NON-NLS
MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "BulkExtractorIngestModule.moduleError"), msg); // NON-NLS
UnallocatedSpaceIngestModule.logger.log(Level.SEVERE, msg);
FileUtil.deleteDir(new File(outputDirPath.toAbsolutePath().toString()));
return IngestModule.ProcessResult.ERROR;
}
if (0 != exitValue) {
FileUtil.deleteDir(new File(outputDirPath.toAbsolutePath().toString()));
if (this.context.dataSourceIngestIsCancelled()) {
return ProcessResult.OK;
} else {
UnallocatedSpaceIngestModule.logger.log(Level.SEVERE, "Bulk Extractor returned error exit value = {0} when scanning unallocated space of {1}", new Object[]{exitValue, file.getName()}); // NON-NLS
return ProcessResult.ERROR;
}
}
if (!Utilities.isDirectoryEmpty(outputDirPath)) {
// Add the output directory to the case as an Autopsy report.
Case.getCurrentCase().addReport(outputDirPath.toAbsolutePath().toString(), Utilities.getModuleName(), Utilities.getReportName(file.getName()));
}
return ProcessResult.OK;
} catch (IOException | InterruptedException | TskCoreException ex) {
UnallocatedSpaceIngestModule.logger.log(Level.SEVERE, "Error processing " + file.getName() + " with Bulk Extractor", ex); // NON-NLS
return ProcessResult.ERROR;
} finally {
if (null != tempFilePath && Files.exists(tempFilePath)) {
// Get rid of the unallocated space file.
tempFilePath.toFile().delete();
}
}
}
@Override
public void shutDown() {
if (!this.settings.shouldOnlyProcessUnallocatedSpace()) {
return;
}
if (refCounter.decrementAndGet(this.context.getJobId()) == 0) {
try {
// The last instance of this module for an ingest job deletes
// the temp dir.
Path tempDirPath = UnallocatedSpaceIngestModule.tempPathsByJob.remove(this.context.getJobId());
FileUtil.deleteDir(new File(tempDirPath.toAbsolutePath().toString()));
} catch (SecurityException ex) {
UnallocatedSpaceIngestModule.logger.log(Level.SEVERE, "Error shutting down Bulk Extractor unallocated space module", ex); // NON-NLS
}
}
}
}

View File

@ -0,0 +1,223 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.experimental.ingestmodules.bulkextractor;
import java.io.File;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.ExecUtil;
import org.sleuthkit.autopsy.coreutils.ExecUtil.ProcessTerminator;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.ingest.IngestModule;
/**
* Utility methods for the Bulk Extractor ingest module.
*/
@NbBundle.Messages({
"Utilities.unsupportedOS.message=Bulk Extractor Module is only supported for Windows platforms.",
"Utilities.missingBulkExtractor.message=Unable to locate Bulk Extractor executable.",
"Utilities.cannotExecuteBulkExtractor.message=Unable to execute Bulk Extractor.",
"# {0} - output directory name", "Utilities.cannotCreateOutputDir.message.with.param=Unable to create output directory: {0}."
})
final class Utilities {
private static final String OUTPUT_SUB_DIRECTORY = "BulkExtractor"; // NON-NLS
private static final String REPORT_NAME_BASE = "Bulk Extractor Scan"; // NON-NLS
private static final String BULK_EXTRACTOR_DIR = "bulk_extractor_1_5_3"; // NON-NLS
private static final String BULK_EXTRACTOR_VERSION = "1.5.3"; // NON-NLS
private static final String BULK_EXTRACTOR_32_BIT_DIR = "32-bit"; // NON-NLS
private static final String BULK_EXTRACTOR_64_BIT_DIR = "64-bit"; // NON-NLS
private static final String BULK_EXTRACTOR_WINDOWS_EXE = "bulk_extractor.exe"; // NON-NLS
/**
* Gets the ingest module name.
*
* @return A name string.
*/
static String getModuleName() {
return NbBundle.getMessage(BulkExtractorIngestModuleFactory.class, "Utilities.moduleName");
}
/**
* Gets the version of the module
* @return Version string
*/
static String getVersion() {
return BULK_EXTRACTOR_VERSION;
}
/**
* Creates a report name based on the name of file presumed to be scanned by
* Bulk Extractor.
*
* @param scannedFileName The name of the file.
*
* @return The report name string.
*/
static String getReportName(String scannedFileName) {
return scannedFileName + " " + Utilities.REPORT_NAME_BASE;
}
/**
* Creates the output directory for this module for the current case and
* data source, if it does not already exist.
*
* @return The absolute path of the output directory.
*
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
*/
synchronized static Path createOutputDirectoryForDataSource(String subDirName) throws IngestModule.IngestModuleException {
Path path = Paths.get(Case.getCurrentCase().getModuleDirectory(), Utilities.OUTPUT_SUB_DIRECTORY, subDirName);
try {
Files.createDirectories(path);
} catch (FileAlreadyExistsException ex) {
// No worries.
} catch (IOException | SecurityException | UnsupportedOperationException ex) {
throw new IngestModule.IngestModuleException(Bundle.Utilities_cannotCreateOutputDir_message_with_param(path.toString()), ex);
}
return path;
}
/**
* Creates a path for an output subdirectory to contain the results of
* scanning a file with Bulk Extractor.
*
* @param rootOutputDirPath The root output directory path
* @param objectId The object id of the file to be scanned.
*
* @return The path.
*/
static Path getOutputSubdirectoryPath(Path rootOutputDirPath, long objectId) {
StringBuilder nameBuilder = new StringBuilder(Long.toString(objectId));
nameBuilder.append("_"); // NON-NLS
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss-SSSS"); // NON-NLS
Date date = new Date();
nameBuilder.append(dateFormat.format(date));
return Paths.get(rootOutputDirPath.toAbsolutePath().toString(), nameBuilder.toString());
}
/**
* Locates the Bulk Extractor executable.
*
* @return The path of the executable.
*
* @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException
*/
static Path locateBulkExtractorExecutable() throws IngestModule.IngestModuleException {
// Must be running under a Windows operating system.
if (!PlatformUtil.isWindowsOS()) {
throw new IngestModule.IngestModuleException(Bundle.Utilities_unsupportedOS_message());
}
// Build the expected path to either the 32-bit or 64-bit version of the
// Bulk Extractor executable.
final File beRoot = InstalledFileLocator.getDefault().locate(Utilities.BULK_EXTRACTOR_DIR, Utilities.class.getPackage().getName(), false);
Path executablePath;
if (PlatformUtil.is64BitOS()) {
executablePath = Paths.get(
beRoot.getAbsolutePath(),
Utilities.BULK_EXTRACTOR_64_BIT_DIR,
Utilities.BULK_EXTRACTOR_WINDOWS_EXE);
} else {
executablePath = Paths.get(
beRoot.getAbsolutePath(),
Utilities.BULK_EXTRACTOR_32_BIT_DIR,
Utilities.BULK_EXTRACTOR_WINDOWS_EXE);
}
// Make sure the executable exists at the expected location and that it
// can be run.
File bulkExtractor = executablePath.toFile();
if (null == bulkExtractor || !bulkExtractor.exists()) {
throw new IngestModule.IngestModuleException(Bundle.Utilities_missingBulkExtractor_message());
}
if (!bulkExtractor.canExecute()) {
throw new IngestModule.IngestModuleException(Bundle.Utilities_cannotExecuteBulkExtractor_message());
}
return executablePath;
}
/**
* Runs the Bulk Extractor executable.
*
* @param bulkExtractorPath The path to the Bulk Extractor executable.
* @param outputDirPath The path to the module output directory.
* @param inputFilePath The path to the input file.
* @param terminator
*
* @return The exit value of the subprocess used to run Bulk Extractor.
*
* @throws IOException
* @throws InterruptedException
*/
static int runBulkExtractor(Path bulkExtractorPath, Path outputDirPath, Path inputFilePath, ProcessTerminator terminator) throws SecurityException, IOException, InterruptedException {
List<String> commandLine = new ArrayList<>();
commandLine.add(bulkExtractorPath.toAbsolutePath().toString());
commandLine.add("-e");
commandLine.add("facebook");
commandLine.add("-o");
commandLine.add(outputDirPath.toAbsolutePath().toString());
commandLine.add(inputFilePath.toAbsolutePath().toString());
ProcessBuilder processBuilder = new ProcessBuilder(commandLine);
// redirect BE stdout and stderr to txt files
Path logFileName = Paths.get(outputDirPath.getParent().toString(), outputDirPath.getFileName().toString() + "_out.txt");
File logFile = new File(logFileName.toString());
Path errFileName = Paths.get(outputDirPath.getParent().toString(), outputDirPath.getFileName().toString() + "_err.txt");
File errFile = new File(errFileName.toString());
processBuilder.redirectError(ProcessBuilder.Redirect.appendTo(errFile));
processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(logFile));
return ExecUtil.execute(processBuilder, terminator);
}
/**
* Determines whether or not a directory is empty.
*
* @param directoryPath The path to the directory to inspect.
*
* @return True if the directory is empty, false otherwise.
*
* @throws IllegalArgumentException
* @throws IOException
*/
static boolean isDirectoryEmpty(final Path directoryPath) throws IllegalArgumentException, IOException {
if (!Files.isDirectory(directoryPath)) {
throw new IllegalArgumentException("The directoryPath argument must be a directory path"); // NON-NLS
}
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(directoryPath)) {
return !dirStream.iterator().hasNext();
}
}
}