Merge branch 'sleuthkit:develop' into develop

This commit is contained in:
Seb2lyon 2021-08-12 17:54:32 +02:00 committed by GitHub
commit aa9d2031fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 330 additions and 223 deletions

View File

@ -17,6 +17,8 @@ ContactsViewer_columnHeader_Phone=Phone
ContactsViewer_noContacts_message=<No contacts found for selected account> ContactsViewer_noContacts_message=<No contacts found for selected account>
ContactsViewer_tabTitle=Contacts ContactsViewer_tabTitle=Contacts
MediaViewer_Name=Media Attachments MediaViewer_Name=Media Attachments
MediaViewer_selection_failure_msg=Failed to get media attachments for selected accounts.
MediaViewer_selection_failure_title=Selection Failed
MessageNode_Node_Property_Attms=Attachment Count MessageNode_Node_Property_Attms=Attachment Count
MessageNode_Node_Property_Date=Date MessageNode_Node_Property_Date=Date
MessageNode_Node_Property_From=From MessageNode_Node_Property_From=From

View File

@ -19,14 +19,19 @@
package org.sleuthkit.autopsy.communications.relationships; package org.sleuthkit.autopsy.communications.relationships;
import java.awt.Component; import java.awt.Component;
import java.awt.Cursor;
import java.awt.KeyboardFocusManager; import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import static javax.swing.SwingUtilities.isDescendingFrom; import static javax.swing.SwingUtilities.isDescendingFrom;
import javax.swing.SwingWorker;
import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup; import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
@ -39,6 +44,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode; import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.datamodel.AbstractContent; import org.sleuthkit.datamodel.AbstractContent;
import org.sleuthkit.datamodel.Account;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -58,6 +64,8 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
private final MessageDataContent contentViewer; private final MessageDataContent contentViewer;
private MediaViewerWorker worker;
@Messages({ @Messages({
"MediaViewer_Name=Media Attachments" "MediaViewer_Name=Media Attachments"
}) })
@ -97,24 +105,17 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
@Override @Override
public void setSelectionInfo(SelectionInfo info) { public void setSelectionInfo(SelectionInfo info) {
Set<Content> relationshipSources;
Set<BlackboardArtifact> artifactList = new HashSet<>();
contentViewer.setNode(null); contentViewer.setNode(null);
if (info != null) {
try {
relationshipSources = info.getRelationshipSources();
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
artifactList.add((BlackboardArtifact) content);
});
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to update selection.", ex);
}
}
thumbnailViewer.resetComponent(); thumbnailViewer.resetComponent();
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentThumbnailsChildren(artifactList)), tableEM), true, this.getClass().getName())); if (worker != null) {
worker.cancel(true);
}
worker = new MediaViewerWorker(info);
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
worker.execute();
} }
@Override @Override
@ -198,6 +199,56 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
} }
} }
/**
* Swing worker for gathering the data needed to populate the media viewer.
*/
private class MediaViewerWorker extends SwingWorker<TableFilterNode, Void> {
private final SelectionInfo selectionInfo;
MediaViewerWorker(SelectionInfo info) {
selectionInfo = info;
}
@Override
protected TableFilterNode doInBackground() throws Exception {
Set<Content> relationshipSources;
Set<BlackboardArtifact> artifactList = new HashSet<>();
if (selectionInfo != null) {
relationshipSources = selectionInfo.getRelationshipSources();
relationshipSources.stream().filter((content) -> (content instanceof BlackboardArtifact)).forEachOrdered((content) -> {
artifactList.add((BlackboardArtifact) content);
});
}
return new TableFilterNode(new DataResultFilterNode(new AbstractNode(new AttachmentThumbnailsChildren(artifactList)), tableEM), true, this.getClass().getName());
}
@Messages({
"MediaViewer_selection_failure_msg=Failed to get media attachments for selected accounts.",
"MediaViewer_selection_failure_title=Selection Failed"
})
@Override
protected void done() {
try {
if (isCancelled()) {
return;
}
thumbnailViewer.setNode(get());
} catch (ExecutionException | InterruptedException ex) {
String accounts = selectionInfo.getAccounts().stream().map(Account::getTypeSpecificID).collect(Collectors.joining(","));
logger.log(Level.WARNING, "Unable to update cvt media viewer for " + accounts, ex);
JOptionPane.showMessageDialog(MediaViewer.this, Bundle.MediaViewer_selection_failure_msg(), Bundle.MediaViewer_selection_failure_title(), JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
}
/** /**
* This method is called from within the constructor to initialize the form. * 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 * WARNING: Do NOT modify this code. The content of this method is always

View File

@ -23,14 +23,12 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.Utilities; import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction; import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction; import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint; import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
@ -43,11 +41,10 @@ import org.sleuthkit.datamodel.LocalFile;
import org.sleuthkit.datamodel.LocalDirectory; import org.sleuthkit.datamodel.LocalDirectory;
import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.Volume; import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.autopsy.coreutils.Logger;
public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? extends Action>> { public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? extends Action>> {
private static ExplorerNodeActionVisitor instance = new ExplorerNodeActionVisitor(); private final static ExplorerNodeActionVisitor instance = new ExplorerNodeActionVisitor();
public static List<Action> getActions(Content c) { public static List<Action> getActions(Content c) {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
@ -73,13 +70,8 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
@Override @Override
public List<? extends Action> visit(final Image img) { public List<? extends Action> visit(final Image img) {
List<Action> lst = new ArrayList<>(); List<Action> lst = new ArrayList<>();
//TODO lst.add(new ExtractAction("Extract Image", img));
try {
lst.add(new ExtractUnallocAction( lst.add(new ExtractUnallocAction(
NbBundle.getMessage(this.getClass(), "ExplorerNodeActionVisitor.action.extUnallocToSingleFiles"), img)); NbBundle.getMessage(this.getClass(), "ExplorerNodeActionVisitor.action.extUnallocToSingleFiles"), img));
} catch (NoCurrentCaseException ex) {
Logger.getLogger(ExplorerNodeActionVisitor.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
}
return lst; return lst;
} }

View File

@ -25,6 +25,7 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -32,10 +33,15 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.HashSet; import java.util.HashSet;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction; import javax.swing.AbstractAction;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import static javax.swing.JFileChooser.SAVE_DIALOG;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable; import org.openide.util.Cancellable;
@ -63,13 +69,18 @@ import org.sleuthkit.datamodel.VolumeSystem;
final class ExtractUnallocAction extends AbstractAction { final class ExtractUnallocAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName()); private static final Logger logger = Logger.getLogger(ExtractUnallocAction.class.getName());
private static final long serialVersionUID = 1L;
private final List<OutputFileData> filesToExtract = new ArrayList<>();
private static final Set<String> volumesInProgress = new HashSet<>(); private static final Set<String> volumesInProgress = new HashSet<>();
private static final Set<Long> imagesInProgress = new HashSet<>(); private static final Set<Long> imagesInProgress = new HashSet<>();
private static String userDefinedExportPath; private static String userDefinedExportPath;
private long currentImage = 0L;
private final boolean isImage; private final Volume volume;
private final Image image;
private final FutureTask<JFileChooser> futureFileChooser = new FutureTask<>(CustomFileChooser::new);
private JFileChooser fileChooser = null;
/** /**
* Create an instance of ExtractUnallocAction with a volume. * Create an instance of ExtractUnallocAction with a volume.
@ -77,16 +88,8 @@ final class ExtractUnallocAction extends AbstractAction {
* @param title The title * @param title The title
* @param volume The volume set for extraction. * @param volume The volume set for extraction.
*/ */
public ExtractUnallocAction(String title, Volume volume) { ExtractUnallocAction(String title, Volume volume) {
super(title); this(title, null, volume);
isImage = false;
try {
OutputFileData outputFileData = new OutputFileData(volume);
filesToExtract.add(outputFileData);
} catch (NoCurrentCaseException ex) {
logger.log(Level.SEVERE, "Exception while getting open case.", ex);
setEnabled(false);
}
} }
@ -95,21 +98,21 @@ final class ExtractUnallocAction extends AbstractAction {
* *
* @param title The title. * @param title The title.
* @param image The image set for extraction. * @param image The image set for extraction.
*
* @throws NoCurrentCaseException If no case is open. * @throws NoCurrentCaseException If no case is open.
*/ */
public ExtractUnallocAction(String title, Image image) throws NoCurrentCaseException { ExtractUnallocAction(String title, Image image) {
this(title, image, null);
}
ExtractUnallocAction(String title, Image image, Volume volume) {
super(title); super(title);
isImage = true;
currentImage = image.getId(); this.volume = null;
if (hasVolumeSystem(image)) { this.image = image;
for (Volume v : getVolumes(image)) {
OutputFileData outputFileData = new OutputFileData(v); ExecutorService executor = Executors.newSingleThreadExecutor();
filesToExtract.add(outputFileData); executor.execute(futureFileChooser);
}
} else {
OutputFileData outputFileData = new OutputFileData(image);
filesToExtract.add(outputFileData);
}
} }
/** /**
@ -126,14 +129,7 @@ final class ExtractUnallocAction extends AbstractAction {
"ExtractUnallocAction.noOpenCase.errMsg=No open case available."}) "ExtractUnallocAction.noOpenCase.errMsg=No open case available."})
@Override @Override
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
if (filesToExtract != null && filesToExtract.isEmpty() == false) {
// This check doesn't absolutely guarantee that the image won't be in progress when we make the worker,
// but in general it will suffice.
if (isImage && isImageInProgress(currentImage)) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.notifyMsg.unallocAlreadyBeingExtr.msg"));
//JOptionPane.showMessageDialog(new Frame(), "Unallocated Space is already being extracted on this Image. Please select a different Image.");
return;
}
Case openCase; Case openCase;
try { try {
openCase = Case.getCurrentCaseThrows(); openCase = Case.getCurrentCaseThrows();
@ -141,97 +137,31 @@ final class ExtractUnallocAction extends AbstractAction {
MessageNotifyUtil.Message.info(Bundle.ExtractUnallocAction_noOpenCase_errMsg()); MessageNotifyUtil.Message.info(Bundle.ExtractUnallocAction_noOpenCase_errMsg());
return; return;
} }
List<OutputFileData> copyList = new ArrayList<OutputFileData>() {
{
addAll(filesToExtract);
}
};
JFileChooser fileChooser = new JFileChooser() { if (fileChooser == null) {
@Override try {
public void approveSelection() { fileChooser = futureFileChooser.get();
File f = getSelectedFile(); } catch (InterruptedException | ExecutionException ex) {
if (!f.exists() && getDialogType() == SAVE_DIALOG || !f.canWrite()) { fileChooser = new CustomFileChooser();
JOptionPane.showMessageDialog(this, NbBundle.getMessage(this.getClass(),
"ExtractUnallocAction.msgDlg.folderDoesntExist.msg"));
return;
} }
super.approveSelection();
} }
};
fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase))); fileChooser.setCurrentDirectory(new File(getExportDirectory(openCase)));
fileChooser.setDialogTitle( if (JFileChooser.APPROVE_OPTION != fileChooser.showSaveDialog((Component) event.getSource())) {
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg")); return;
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); }
fileChooser.setAcceptAllFileFilterUsed(false);
int returnValue = fileChooser.showSaveDialog((Component) event.getSource());
if (returnValue == JFileChooser.APPROVE_OPTION) {
String destination = fileChooser.getSelectedFile().getPath();
String destination = fileChooser.getSelectedFile().getPath();
updateExportDirectory(destination, openCase); updateExportDirectory(destination, openCase);
for (OutputFileData outputFileData : filesToExtract) { if (image != null && isImageInProgress(image.getId())) {
outputFileData.setPath(destination); MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.notifyMsg.unallocAlreadyBeingExtr.msg"));
JOptionPane.showMessageDialog(new Frame(), "Unallocated Space is already being extracted on this Image. Please select a different Image.");
if (outputFileData.layoutFiles != null && outputFileData.layoutFiles.size() > 0 && (!isVolumeInProgress(outputFileData.getFileName()))) { return;
//Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat
// Check if there is already a file with this name
if (outputFileData.fileInstance.exists()) {
int res = JOptionPane.showConfirmDialog(new Frame(), NbBundle.getMessage(this.getClass(),
"ExtractUnallocAction.confDlg.unallocFileAlreadyExist.msg",
outputFileData.getFileName()));
if (res == JOptionPane.YES_OPTION) {
// If the user wants to overwrite, delete the exising output file
outputFileData.fileInstance.delete();
} else {
// Otherwise remove it from the list of output files
copyList.remove(outputFileData);
}
} }
if (!isImage & !copyList.isEmpty()) { ExtractUnallocWorker worker = new ExtractUnallocWorker(openCase, destination);
try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(outputFileData);
worker.execute(); worker.execute();
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting unallocated space into {0}", outputFileData.getFileName());
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName()));
}
}
} else {
// The output file for this volume could not be created for one of the following reasons
if (outputFileData.layoutFiles == null) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeError"));
logger.log(Level.SEVERE, "Tried to get unallocated content but the list of unallocated files was null"); //NON-NLS
} else if (outputFileData.layoutFiles.isEmpty()) {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.noFiles"));
logger.log(Level.WARNING, "No unallocated files found in volume"); //NON-NLS
copyList.remove(outputFileData);
} else {
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.volumeInProgress", outputFileData.getFileName()));
logger.log(Level.WARNING, "Tried to get unallocated content but the volume is locked"); // NON_NLS
copyList.remove(outputFileData);
}
}
}
// This needs refactoring. The idea seems to be that we'll take advantage of the loop above to
// check whether each output file exists but wait until this point to make a worker
// to extract everything (the worker in the above loop doesn't get created because isImage is true)
// It's also unclear to me why we need the two separate worker types.
if (isImage && !copyList.isEmpty()) {
try {
ExtractUnallocWorker worker = new ExtractUnallocWorker(copyList);
worker.execute();
} catch (Exception ex) {
logger.log(Level.WARNING, "Error creating ExtractUnallocWorker", ex);
MessageNotifyUtil.Message.info(NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.imageError"));
}
}
}
}
} }
/** /**
@ -333,38 +263,14 @@ final class ExtractUnallocAction extends AbstractAction {
private boolean canceled = false; private boolean canceled = false;
private final List<OutputFileData> outputFileDataList = new ArrayList<>(); private final List<OutputFileData> outputFileDataList = new ArrayList<>();
private File currentlyProcessing; private File currentlyProcessing;
private final int totalSizeinMegs; private int totalSizeinMegs;
long totalBytes = 0; long totalBytes = 0;
private final String destination;
private Case openCase;
ExtractUnallocWorker(OutputFileData outputFileData) throws TskCoreException { ExtractUnallocWorker(Case openCase, String destination) {
//Getting the total megs this worker is going to be doing this.destination = destination;
addVolumeInProgress(outputFileData.getFileName()); this.openCase = openCase;
outputFileDataList.add(outputFileData);
totalBytes = outputFileData.getSizeInBytes();
totalSizeinMegs = toMb(totalBytes);
}
ExtractUnallocWorker(List<OutputFileData> outputFileDataList) throws TskCoreException {
addImageInProgress(currentImage);
//Getting the total megs this worker is going to be doing
for (OutputFileData outputFileData : outputFileDataList) {
try {
// If a volume is locked, skip it but continue trying to process any other requested volumes
addVolumeInProgress(outputFileData.getFileName());
totalBytes += outputFileData.getSizeInBytes();
this.outputFileDataList.add(outputFileData);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting data into {0}", outputFileData.getFileName());
}
}
// If we don't have anything to output (because of locking), throw an exception
if (this.outputFileDataList.isEmpty()) {
throw new TskCoreException("No unallocated files can be extracted");
}
totalSizeinMegs = toMb(totalBytes);
} }
private int toMb(long bytes) { private int toMb(long bytes) {
@ -380,6 +286,7 @@ final class ExtractUnallocAction extends AbstractAction {
@Override @Override
protected Integer doInBackground() { protected Integer doInBackground() {
try { try {
initalizeFilesToExtract();
progress = ProgressHandle.createHandle( progress = ProgressHandle.createHandle(
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.progress.extractUnalloc.title"), new Cancellable() { NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.progress.extractUnalloc.title"), new Cancellable() {
@Override @Override
@ -403,7 +310,7 @@ final class ExtractUnallocAction extends AbstractAction {
for (OutputFileData outputFileData : this.outputFileDataList) { for (OutputFileData outputFileData : this.outputFileDataList) {
currentlyProcessing = outputFileData.getFile(); currentlyProcessing = outputFileData.getFile();
logger.log(Level.INFO, "Writing Unalloc file to {0}", currentlyProcessing.getPath()); //NON-NLS logger.log(Level.INFO, "Writing Unalloc file to {0}", currentlyProcessing.getPath()); //NON-NLS
OutputStream outputStream = new FileOutputStream(currentlyProcessing); try (OutputStream outputStream = new FileOutputStream(currentlyProcessing)) {
long bytes = 0; long bytes = 0;
int i = 0; int i = 0;
while (i < outputFileData.getLayouts().size() && bytes != outputFileData.getSizeInBytes()) { while (i < outputFileData.getLayouts().size() && bytes != outputFileData.getSizeInBytes()) {
@ -425,7 +332,7 @@ final class ExtractUnallocAction extends AbstractAction {
i++; i++;
} }
outputStream.flush(); outputStream.flush();
outputStream.close(); }
if (canceled) { if (canceled) {
outputFileData.getFile().delete(); outputFileData.getFile().delete();
@ -448,8 +355,8 @@ final class ExtractUnallocAction extends AbstractAction {
@Override @Override
protected void done() { protected void done() {
if (isImage) { if (image != null) {
removeImageInProgress(currentImage); removeImageInProgress(image.getId());
} }
for (OutputFileData u : outputFileDataList) { for (OutputFileData u : outputFileDataList) {
removeVolumeInProgress(u.getFileName()); removeVolumeInProgress(u.getFileName());
@ -468,10 +375,115 @@ final class ExtractUnallocAction extends AbstractAction {
MessageNotifyUtil.Notify.error( MessageNotifyUtil.Notify.error(
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.done.errMsg.title"), NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.done.errMsg.title"),
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.done.errMsg.msg", ex.getMessage())); NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.done.errMsg.msg", ex.getMessage()));
logger.log(Level.SEVERE, "Failed to extract unallocated space", ex);
} // catch and ignore if we were cancelled } // catch and ignore if we were cancelled
catch (java.util.concurrent.CancellationException ex) { catch (java.util.concurrent.CancellationException ex) {
} }
} }
private void initalizeFilesToExtract() throws TskCoreException {
List<OutputFileData> filesToExtract = new ArrayList<>();
if (volume != null) {
OutputFileData outputFileData = new OutputFileData(volume, openCase);
filesToExtract.add(outputFileData);
} else {
if (hasVolumeSystem(image)) {
for (Volume v : getVolumes(image)) {
OutputFileData outputFileData = new OutputFileData(v, openCase);
filesToExtract.add(outputFileData);
}
} else {
OutputFileData outputFileData = new OutputFileData(image, openCase);
filesToExtract.add(outputFileData);
}
}
if (filesToExtract.isEmpty() == false) {
List<OutputFileData> copyList = new ArrayList<OutputFileData>() {
{
addAll(filesToExtract);
}
};
for (OutputFileData outputFileData : filesToExtract) {
outputFileData.setPath(destination);
if (outputFileData.getLayouts() != null && !outputFileData.getLayouts().isEmpty() && (!isVolumeInProgress(outputFileData.getFileName()))) {
//Format for single Unalloc File is ImgName-Unalloc-ImgObjectID-VolumeID.dat
// Check if there is already a file with this name
if (outputFileData.getFile().exists()) {
final Result dialogResult = new Result();
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
dialogResult.set(JOptionPane.showConfirmDialog(new Frame(), NbBundle.getMessage(this.getClass(),
"ExtractUnallocAction.confDlg.unallocFileAlreadyExist.msg",
outputFileData.getFileName())));
}
});
} catch (InterruptedException | InvocationTargetException ex) {
logger.log(Level.SEVERE, "An error occured launching confirmation dialog for extract unalloc actions", ex);
}
if (dialogResult.value == JOptionPane.YES_OPTION) {
// If the user wants to overwrite, delete the exising output file
outputFileData.getFile().delete();
} else {
// Otherwise remove it from the list of output files
copyList.remove(outputFileData);
}
}
} else {
// The output file for this volume could not be created for one of the following reasons
if (outputFileData.getLayouts() == null) {
logger.log(Level.SEVERE, "Tried to get unallocated content but the list of unallocated files was null"); //NON-NLS
} else if (outputFileData.getLayouts().isEmpty()) {
logger.log(Level.WARNING, "No unallocated files found in volume"); //NON-NLS
copyList.remove(outputFileData);
} else {
logger.log(Level.WARNING, "Tried to get unallocated content but the volume is locked"); // NON_NLS
copyList.remove(outputFileData);
}
}
}
if (!copyList.isEmpty()) {
setDataFileList(copyList);
}
}
}
private void setDataFileList(List<OutputFileData> outputFileDataList) throws TskCoreException {
if (image != null) {
addImageInProgress(image.getId());
}
//Getting the total megs this worker is going to be doing
for (OutputFileData outputFileData : outputFileDataList) {
try {
// If a volume is locked, skip it but continue trying to process any other requested volumes
addVolumeInProgress(outputFileData.getFileName());
totalBytes += outputFileData.getSizeInBytes();
this.outputFileDataList.add(outputFileData);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Already extracting data into {0}", outputFileData.getFileName());
}
}
// If we don't have anything to output (because of locking), throw an exception
if (this.outputFileDataList.isEmpty()) {
throw new TskCoreException("No unallocated files can be extracted");
}
totalSizeinMegs = toMb(totalBytes);
}
} }
/** /**
@ -659,14 +671,14 @@ final class ExtractUnallocAction extends AbstractAction {
* *
* @throws NoCurrentCaseException if there is no open case. * @throws NoCurrentCaseException if there is no open case.
*/ */
OutputFileData(Image img) throws NoCurrentCaseException { OutputFileData(Image img, Case openCase) {
this.layoutFiles = getUnallocFiles(img); this.layoutFiles = getUnallocFiles(img);
Collections.sort(layoutFiles, new SortObjId()); Collections.sort(layoutFiles, new SortObjId());
this.volumeId = 0; this.volumeId = 0;
this.imageId = img.getId(); this.imageId = img.getId();
this.imageName = img.getName(); this.imageName = img.getName();
this.fileName = this.imageName + "-Unalloc-" + this.imageId + "-" + 0 + ".dat"; //NON-NLS this.fileName = this.imageName + "-Unalloc-" + this.imageId + "-" + 0 + ".dat"; //NON-NLS
this.fileInstance = new File(Case.getCurrentCaseThrows().getExportDirectory() + File.separator + this.fileName); this.fileInstance = new File(openCase.getExportDirectory() + File.separator + this.fileName);
this.sizeInBytes = calcSizeInBytes(); this.sizeInBytes = calcSizeInBytes();
} }
@ -677,7 +689,7 @@ final class ExtractUnallocAction extends AbstractAction {
* *
* @throws NoCurrentCaseException if there is no open case. * @throws NoCurrentCaseException if there is no open case.
*/ */
OutputFileData(Volume volume) throws NoCurrentCaseException { OutputFileData(Volume volume, Case openCase) {
try { try {
this.imageName = volume.getDataSource().getName(); this.imageName = volume.getDataSource().getName();
this.imageId = volume.getDataSource().getId(); this.imageId = volume.getDataSource().getId();
@ -688,7 +700,7 @@ final class ExtractUnallocAction extends AbstractAction {
this.imageId = 0; this.imageId = 0;
} }
this.fileName = this.imageName + "-Unalloc-" + this.imageId + "-" + volumeId + ".dat"; //NON-NLS this.fileName = this.imageName + "-Unalloc-" + this.imageId + "-" + volumeId + ".dat"; //NON-NLS
this.fileInstance = new File(Case.getCurrentCaseThrows().getExportDirectory() + File.separator + this.fileName); this.fileInstance = new File(openCase.getExportDirectory() + File.separator + this.fileName);
this.layoutFiles = getUnallocFiles(volume); this.layoutFiles = getUnallocFiles(volume);
Collections.sort(layoutFiles, new SortObjId()); Collections.sort(layoutFiles, new SortObjId());
this.sizeInBytes = calcSizeInBytes(); this.sizeInBytes = calcSizeInBytes();
@ -739,4 +751,46 @@ final class ExtractUnallocAction extends AbstractAction {
this.fileInstance = new File(path + File.separator + this.fileName); this.fileInstance = new File(path + File.separator + this.fileName);
} }
} }
// A Custome JFileChooser for this Action Class.
private class CustomFileChooser extends JFileChooser {
private static final long serialVersionUID = 1L;
CustomFileChooser() {
initalize();
}
private void initalize() {
setDialogTitle(
NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg"));
setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
setAcceptAllFileFilterUsed(false);
}
@Override
public void approveSelection() {
File f = getSelectedFile();
if (!f.exists() && getDialogType() == SAVE_DIALOG || !f.canWrite()) {
JOptionPane.showMessageDialog(this, NbBundle.getMessage(this.getClass(),
"ExtractUnallocAction.msgDlg.folderDoesntExist.msg"));
return;
}
super.approveSelection();
}
}
// Small helper class for use with SwingUtilities involkAndWait to get
// the result from the launching of the JOptionPane.
private class Result {
private int value;
void set(int value) {
this.value = value;
}
int value() {
return value;
}
}
} }

View File

@ -27,10 +27,12 @@ import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import org.apache.poi.EmptyFileException;
import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.DirectoryEntry;
import org.apache.poi.poifs.filesystem.DocumentEntry; import org.apache.poi.poifs.filesystem.DocumentEntry;
import org.apache.poi.poifs.filesystem.DocumentInputStream; import org.apache.poi.poifs.filesystem.DocumentInputStream;
import org.apache.poi.poifs.filesystem.Entry; import org.apache.poi.poifs.filesystem.Entry;
import org.apache.poi.poifs.filesystem.NotOLE2FileException;
import org.apache.poi.poifs.filesystem.POIFSFileSystem; import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
@ -62,7 +64,6 @@ final class ExtractJumpLists extends Extract {
private static final String JUMPLIST_TSK_COMMENT = "Jumplist File"; private static final String JUMPLIST_TSK_COMMENT = "Jumplist File";
private static final String RA_DIR_NAME = "RecentActivity"; //NON-NLS private static final String RA_DIR_NAME = "RecentActivity"; //NON-NLS
private static final String MODULE_OUTPUT_DIR = "ModuleOutput"; //NON-NLS
private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY = "%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/"; private static final String AUTOMATIC_DESTINATIONS_FILE_DIRECTORY = "%/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/";
private static final String JUMPLIST_DIR_NAME = "jumplists"; //NON-NLS private static final String JUMPLIST_DIR_NAME = "jumplists"; //NON-NLS
private static final String VERSION_NUMBER = "1.0.0"; //NON-NLS private static final String VERSION_NUMBER = "1.0.0"; //NON-NLS
@ -86,7 +87,8 @@ final class ExtractJumpLists extends Extract {
fileManager = currentCase.getServices().getFileManager(); fileManager = currentCase.getServices().getFileManager();
long ingestJobId = context.getJobId(); long ingestJobId = context.getJobId();
List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId); String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME , ingestJobId);
List<AbstractFile> jumpListFiles = extractJumplistFiles(dataSource, ingestJobId, baseRaTempPath);
if (jumpListFiles.isEmpty()) { if (jumpListFiles.isEmpty()) {
return; return;
@ -98,13 +100,13 @@ final class ExtractJumpLists extends Extract {
List<AbstractFile> derivedFiles = new ArrayList<>(); List<AbstractFile> derivedFiles = new ArrayList<>();
String derivedPath = null; String derivedPath = null;
String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME + "_" + dataSource.getId(), ingestJobId); String baseRaModPath = RAImageIngestModule.getRAOutputPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME, ingestJobId);
for (AbstractFile jumplistFile : jumpListFiles) { for (AbstractFile jumplistFile : jumpListFiles) {
if (!jumplistFile.getName().toLowerCase().contains("-slack") && !jumplistFile.getName().equals("..") && if (!jumplistFile.getName().toLowerCase().contains("-slack") && !jumplistFile.getName().equals("..") &&
!jumplistFile.getName().equals(".") && jumplistFile.getSize() > 0) { !jumplistFile.getName().equals(".") && jumplistFile.getSize() > 0) {
String jlFile = Paths.get(baseRaTempPath, jumplistFile.getName() + "_" + jumplistFile.getId()).toString(); String jlFile = Paths.get(baseRaTempPath, jumplistFile.getName() + "_" + jumplistFile.getId()).toString();
String moduleOutPath = Case.getCurrentCase().getModuleDirectory() + File.separator + RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME + "_" + dataSource.getId() + File.separator + jumplistFile.getName() + "_" + jumplistFile.getId(); String moduleOutPath = baseRaModPath + File.separator + jumplistFile.getName() + "_" + jumplistFile.getId();
derivedPath = RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME + "_" + dataSource.getId() + File.separator + jumplistFile.getName() + "_" + jumplistFile.getId(); derivedPath = RA_DIR_NAME + File.separator + JUMPLIST_DIR_NAME + "_" + ingestJobId + File.separator + jumplistFile.getName() + "_" + jumplistFile.getId();
File jlDir = new File(moduleOutPath); File jlDir = new File(moduleOutPath);
if (jlDir.exists() == false) { if (jlDir.exists() == false) {
boolean dirMade = jlDir.mkdirs(); boolean dirMade = jlDir.mkdirs();
@ -129,7 +131,7 @@ final class ExtractJumpLists extends Extract {
* *
* @return - list of jumplist abstractfiles or empty list * @return - list of jumplist abstractfiles or empty list
*/ */
private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId) { private List<AbstractFile> extractJumplistFiles(Content dataSource, Long ingestJobId, String baseRaTempPath) {
List<AbstractFile> jumpListFiles = new ArrayList<>();; List<AbstractFile> jumpListFiles = new ArrayList<>();;
List<AbstractFile> tempJumpListFiles = new ArrayList<>();; List<AbstractFile> tempJumpListFiles = new ArrayList<>();;
@ -154,7 +156,6 @@ final class ExtractJumpLists extends Extract {
if (!jumpListFile.getName().toLowerCase().contains("-slack") && !jumpListFile.getName().equals("..") && if (!jumpListFile.getName().toLowerCase().contains("-slack") && !jumpListFile.getName().equals("..") &&
!jumpListFile.getName().equals(".") && jumpListFile.getSize() > 0) { !jumpListFile.getName().equals(".") && jumpListFile.getSize() > 0) {
String fileName = jumpListFile.getName() + "_" + jumpListFile.getId(); String fileName = jumpListFile.getName() + "_" + jumpListFile.getId();
String baseRaTempPath = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), JUMPLIST_DIR_NAME+ "_" + dataSource.getId(), ingestJobId);
String jlFile = Paths.get(baseRaTempPath, fileName).toString(); String jlFile = Paths.get(baseRaTempPath, fileName).toString();
try { try {
ContentUtils.writeToFile(jumpListFile, new File(jlFile)); ContentUtils.writeToFile(jumpListFile, new File(jlFile));
@ -196,7 +197,8 @@ final class ExtractJumpLists extends Extract {
JLNK lnk = lnkParser.parse(); JLNK lnk = lnkParser.parse();
lnkFileName = lnk.getBestName() + ".lnk"; lnkFileName = lnk.getBestName() + ".lnk";
File targetFile = new File(moduleOutPath + File.separator + entry.getName() + "-" + lnkFileName); File targetFile = new File(moduleOutPath + File.separator + entry.getName() + "-" + lnkFileName);
String derivedFileName = MODULE_OUTPUT_DIR + File.separator + derivedPath + File.separator + entry.getName() + "-" + lnkFileName; String relativePath = Case.getCurrentCase().getModuleOutputDirectoryRelativePath();
String derivedFileName = Case.getCurrentCase().getModuleOutputDirectoryRelativePath() + File.separator + derivedPath + File.separator + entry.getName() + "-" + lnkFileName;
OutputStream outStream = new FileOutputStream(targetFile); OutputStream outStream = new FileOutputStream(targetFile);
outStream.write(buffer); outStream.write(buffer);
outStream.close(); outStream.close();
@ -226,6 +228,8 @@ final class ExtractJumpLists extends Extract {
continue; continue;
} }
} }
} catch (NotOLE2FileException | EmptyFileException ex1) {
logger.log(Level.WARNING, String.format("Error file not a valid OLE2 Document $s", jumpListFile)); //NON-NLS
} catch (IOException | TskCoreException ex) { } catch (IOException | TskCoreException ex) {
logger.log(Level.WARNING, String.format("Error lnk parsing the file to get recent files $s", jumpListFile), ex); //NON-NLS logger.log(Level.WARNING, String.format("Error lnk parsing the file to get recent files $s", jumpListFile), ex); //NON-NLS
} }
@ -235,3 +239,4 @@ final class ExtractJumpLists extends Extract {
} }
} }

View File

@ -8,6 +8,9 @@ This is the User's Guide for the <a href="http://www.sleuthkit.org/autopsy/">ope
Note: For those users running Autopsy on Mac devices, the functionality available through the "Tools" -> "Options" dialog as described in this documentation can be accessed through the system menu bar under "Preferences" or through the Cmd + , (command-comma) shortcut. Note: For those users running Autopsy on Mac devices, the functionality available through the "Tools" -> "Options" dialog as described in this documentation can be accessed through the system menu bar under "Preferences" or through the Cmd + , (command-comma) shortcut.
Translated versions of this guide:
- <a href="https://sleuthkit.org/autopsy/docs/user-docs_fr/4.19.0/">Français (4.19.0)</a>
Help Topics Help Topics
------- -------
The following topics are available here: The following topics are available here: