mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 02:07:42 +00:00
Merge pull request #2340 from zhhl/1968-moveBulkExtractor
1968 move bulk extractor
This commit is contained in:
commit
c65672de36
Binary file not shown.
Binary file not shown.
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
@ -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
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user