Merge branch 'develop' of github.com:sleuthkit/autopsy into 6461_nb_upgrade

This commit is contained in:
esaunders 2020-07-27 15:38:12 -04:00
commit 6d63818d42
59 changed files with 843 additions and 233 deletions

View File

@ -59,6 +59,11 @@
<fileset dir="${thirdparty.dir}/InterestingFileSetRules"/>
</copy>
<!--Copy OfficialHashSets to release-->
<copy todir="${basedir}/release/OfficialHashSets" >
<fileset dir="${thirdparty.dir}/OfficialHashSets"/>
</copy>
<!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we
have it stored in a ZIP file. We'll extract it in place and remove
the ZIP file afterward. -->

View File

@ -29,6 +29,9 @@ import org.openide.util.Utilities;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException;
@ -72,6 +75,12 @@ public class DeleteContentTagAction extends AbstractAction {
new Thread(() -> {
for (ContentTag tag : selectedTags) {
try {
// Check if there is an image tag before deleting the content tag.
ContentViewerTag<ImageTagRegion> imageTag = ContentViewerTagManager.getTag(tag, ImageTagRegion.class);
if(imageTag != null) {
ContentViewerTagManager.deleteTag(imageTag);
}
Case.getCurrentCaseThrows().getServices().getTagsManager().deleteContentTag(tag);
} catch (TskCoreException | NoCurrentCaseException ex) {
Logger.getLogger(DeleteContentTagAction.class.getName()).log(Level.SEVERE, "Error deleting tag", ex); //NON-NLS

View File

@ -39,6 +39,9 @@ import org.openide.util.actions.Presenter;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.tags.TagUtils;
import org.sleuthkit.datamodel.AbstractFile;
@ -123,6 +126,13 @@ public class DeleteFileContentTagAction extends AbstractAction implements Presen
try {
logger.log(Level.INFO, "Removing tag {0} from {1}", new Object[]{tagName.getDisplayName(), contentTag.getContent().getName()}); //NON-NLS
// Check if there is an image tag before deleting the content tag.
ContentViewerTag<ImageTagRegion> imageTag = ContentViewerTagManager.getTag(contentTag, ImageTagRegion.class);
if(imageTag != null) {
ContentViewerTagManager.deleteTag(imageTag);
}
tagsManager.deleteContentTag(contentTag);
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error untagging file", tskCoreException); //NON-NLS

View File

@ -29,6 +29,9 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TagName;
@ -83,9 +86,19 @@ public final class ReplaceContentTagAction extends ReplaceTagAction<ContentTag>
try {
logger.log(Level.INFO, "Replacing tag {0} with tag {1} for artifact {2}", new Object[]{oldTag.getName().getDisplayName(), newTagName.getDisplayName(), oldTag.getContent().getName()}); //NON-NLS
// Check if there is an image tag before deleting the content tag.
ContentViewerTag<ImageTagRegion> imageTag = ContentViewerTagManager.getTag(oldTag, ImageTagRegion.class);
if(imageTag != null) {
ContentViewerTagManager.deleteTag(imageTag);
}
tagsManager.deleteContentTag(oldTag);
tagsManager.addContentTag(oldTag.getContent(), newTagName, newComment);
ContentTag newTag = tagsManager.addContentTag(oldTag.getContent(), newTagName, newComment);
// Resave the image tag if present.
if(imageTag != null) {
ContentViewerTagManager.saveTag(newTag, imageTag.getDetails());
}
} catch (TskCoreException tskCoreException) {
logger.log(Level.SEVERE, "Error replacing artifact tag", tskCoreException); //NON-NLS
Platform.runLater(()

View File

@ -6,10 +6,10 @@ AddMetadataDialog_empty_name_Title=Missing field(s)
CreatePersonaAccountDialog.title.text=Create Account
CreatePersonaAccountDialog_error_msg=Failed to create account.
CreatePersonaAccountDialog_error_title=Account failure
CreatePersonaAccountDialog_success_msg=Account added.
CreatePersonaAccountDialog_success_title=Account added
CreatePersonaAccountDialog_invalid_account_msg=Account identifier is not valid.
CreatePersonaAccountDialog_invalid_account_Title=Invalid account identifier
CreatePersonaAccountDialog_success_msg=Account added.
CreatePersonaAccountDialog_success_title=Account added
CTL_OpenPersonas=Personas
CTL_PersonasTopComponentAction=Personas
CTL_PersonaDetailsTopComponent=Persona Details
@ -134,4 +134,4 @@ PersonasTopComponent_delete_exception_Title=Delete failure
PersonasTopComponent_Name=Personas
PersonasTopComponent_noCR_msg=Central Repository is not enabled.
PersonasTopComponent_search_exception_msg=Failed to search personas.
PersonasTopComponent_search_exception_Title=Search failure
PersonasTopComponent_search_exception_Title=There was a failure during the search. Try opening a case to fully initialize the central repository database.

View File

@ -269,6 +269,7 @@ final public class FiltersPanel extends JPanel {
* Populate the Account Types filter widgets.
*
* @param accountTypesInUse List of accountTypes currently in use
* @param checkNewOnes
*
* @return True, if a new accountType was found
*/
@ -314,9 +315,8 @@ final public class FiltersPanel extends JPanel {
/**
* Populate the devices filter widgets.
*
* @param selected Sets the initial state of device check box.
* @param sleuthkitCase The sleuthkit case for containing the data source
* information.
* @param dataSourceMap
* @param checkNewOnes
*
* @return true if a new device was found
*/

View File

@ -258,8 +258,6 @@ final class MessageViewer extends JPanel implements RelationshipsViewer {
*/
private void showMessagesPane() {
switchCard("messages");
Outline outline = rootTablePane.getOutlineView().getOutline();
outline.clearSelection();
}
/**

View File

@ -23,6 +23,8 @@ import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import static javax.swing.SwingUtilities.isDescendingFrom;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
import org.openide.explorer.ExplorerManager;
@ -82,6 +84,18 @@ class MessagesPanel extends javax.swing.JPanel implements Lookup.Provider {
} else {
messageContentViewer.setNode(null);
}
}
});
// This is a trick to get the first message to be selected after the ChildFactory has added
// new data to the table.
outlineViewPanel.getOutlineView().getOutline().getOutlineModel().addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
if (e.getType() == TableModelEvent.INSERT) {
outline.setRowSelectionInterval(0, 0);
}
}
});

View File

@ -186,7 +186,7 @@ FileSorter.SortingMethod.filetype.displayName=File Type
FileSorter.SortingMethod.frequency.displayName=Central Repo Frequency
FileSorter.SortingMethod.fullPath.displayName=Full Path
FileSorter.SortingMethod.keywordlist.displayName=Keyword List Names
GroupsListPanel.noResults.message.text=No results were found for the selected filters.
GroupsListPanel.noResults.message.text=No results were found for the selected filters.\n\nReminder:\n -The File Type Identification module must be run on each data source you want to find results in.\n -The Hash Lookup module must be run on each data source if you want to filter by past occurrence.\n -The Exif module must be run on each data source if you are filtering by User Created content.
GroupsListPanel.noResults.title.text=No results found
ImageThumbnailPanel.isDeleted.text=All instances of file are deleted.
# {0} - otherInstanceCount

View File

@ -23,6 +23,7 @@ import java.awt.Color;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
@ -30,21 +31,31 @@ import org.apache.commons.lang.StringUtils;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepoException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.discovery.FileGroup.GroupSortingAlgorithm;
import static org.sleuthkit.autopsy.discovery.FileGroup.GroupSortingAlgorithm.BY_GROUP_SIZE;
import org.sleuthkit.autopsy.discovery.FileSearch.GroupingAttributeType;
import static org.sleuthkit.autopsy.discovery.FileSearch.GroupingAttributeType.PARENT_PATH;
import org.sleuthkit.autopsy.discovery.FileSorter.SortingMethod;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.TskCoreException;
import static org.sleuthkit.autopsy.discovery.FileSorter.SortingMethod.BY_FILE_NAME;
/**
* Dialog for displaying the controls and filters for configuration of a
* Discovery search.
*/
final class DiscoveryDialog extends javax.swing.JDialog {
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
Case.Events.DATA_SOURCE_ADDED, Case.Events.DATA_SOURCE_DELETED);
private static final Set<IngestManager.IngestModuleEvent> INGEST_MODULE_EVENTS_OF_INTEREST = EnumSet.of(IngestManager.IngestModuleEvent.DATA_ADDED);
private static final long serialVersionUID = 1L;
private final static Logger logger = Logger.getLogger(DiscoveryDialog.class.getName());
private ImageFilterPanel imageFilterPanel = null;
@ -54,8 +65,12 @@ final class DiscoveryDialog extends javax.swing.JDialog {
private static final Color UNSELECTED_COLOR = new Color(240, 240, 240);
private SearchWorker searchWorker = null;
private static DiscoveryDialog discDialog;
private static volatile boolean shouldUpdate = false;
private FileSearchData.FileType fileType = FileSearchData.FileType.IMAGE;
private final PropertyChangeListener listener;
private final Set<BlackboardAttribute> objectsDetected = new HashSet<>();
private final Set<BlackboardAttribute> interestingItems = new HashSet<>();
private final Set<BlackboardAttribute> hashSets = new HashSet<>();
/**
* Get the Discovery dialog instance.
@ -66,6 +81,10 @@ final class DiscoveryDialog extends javax.swing.JDialog {
if (discDialog == null) {
discDialog = new DiscoveryDialog();
}
if (shouldUpdate) {
discDialog.updateSearchSettings();
shouldUpdate = false;
}
return discDialog;
}
@ -89,6 +108,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
}
updateSearchSettings();
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, this.new CasePropertyChangeListener());
IngestManager.getInstance().addIngestModuleEventListener(INGEST_MODULE_EVENTS_OF_INTEREST, this.new ModuleChangeListener());
}
/**
@ -116,6 +136,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
add(imageFilterPanel, CENTER);
imageFilterPanel.addPropertyChangeListener(listener);
updateComboBoxes();
groupSortingComboBox.setSelectedItem(BY_GROUP_SIZE);
pack();
repaint();
}
@ -129,6 +150,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
for (FileSearch.GroupingAttributeType type : FileSearch.GroupingAttributeType.getOptionsForGrouping()) {
addTypeToGroupByComboBox(type);
}
groupByCombobox.setSelectedItem(PARENT_PATH);
orderByCombobox.removeAllItems();
// Set up the file order list
for (FileSorter.SortingMethod method : FileSorter.SortingMethod.getOptionsForOrdering()) {
@ -136,7 +158,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
orderByCombobox.addItem(method);
}
}
groupSortingComboBox.setSelectedIndex(0);
orderByCombobox.setSelectedItem(BY_FILE_NAME);
}
/**
@ -512,7 +534,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
tc.toFront();
tc.requestActive();
}//GEN-LAST:event_searchButtonActionPerformed
@Override
public void dispose() {
setVisible(false);
@ -531,7 +553,8 @@ final class DiscoveryDialog extends javax.swing.JDialog {
* The adjust the controls to reflect whether the settings are valid based
* on the error.
*
* @param error The error message to display, empty string if there is no error.
* @param error The error message to display, empty string if there is no
* error.
*/
private void setValid(String error) {
if (StringUtils.isBlank(error)) {
@ -560,7 +583,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
* filters available.
*/
private class CasePropertyChangeListener implements PropertyChangeListener {
@Override
@SuppressWarnings("fallthrough")
public void propertyChange(PropertyChangeEvent evt) {
@ -575,7 +598,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
case DATA_SOURCE_ADDED:
//fallthrough
case DATA_SOURCE_DELETED:
updateSearchSettings();
shouldUpdate = true;
break;
default:
//do nothing if the event is not one of the above events.
@ -583,4 +606,83 @@ final class DiscoveryDialog extends javax.swing.JDialog {
}
}
}
/**
* PropertyChangeListener to listen to ingest module events that may modify
* the filters available.
*/
private class ModuleChangeListener implements PropertyChangeListener {
@Override
@SuppressWarnings("fallthrough")
public void propertyChange(PropertyChangeEvent evt) {
if (!shouldUpdate) {
String eventType = evt.getPropertyName();
if (eventType.equals(IngestManager.IngestModuleEvent.DATA_ADDED.toString())) {
/**
* Checking for a current case is a stop gap measure until a
* different way of handling the closing of cases is worked
* out. Currently, remote events may be received for a case
* that is already closed.
*/
try {
Case.getCurrentCaseThrows();
/**
* Even with the check above, it is still possible that
* the case will be closed in a different thread before
* this code executes. If that happens, it is possible
* for the event to have a null oldValue.
*/
ModuleDataEvent eventData = (ModuleDataEvent) evt.getOldValue();
if (null != eventData) {
if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID() && eventData.getArtifacts() != null) {
shouldUpdate = shouldUpdateFilters(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID(), eventData, objectsDetected);
} else if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
shouldUpdate = shouldUpdateFilters(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), eventData, hashSets);
} else if (eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
|| eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
shouldUpdate = shouldUpdateFilters(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), eventData, interestingItems);
}
}
} catch (NoCurrentCaseException notUsed) {
// Case is closed, do nothing.
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Unable to determine if discovery UI should be updated", ex);
}
}
}
}
/**
* Helper method to determine if the artifact in the eventData
* represents a new value for the filter.
*
* @param attributeTypeId The attribute id of the attribute which
* contains the value for the filter.
* @param eventData The event which contains the artifacts.
* @param filterSetToCheck The set of current values for the relevant
* filter.
*
* @return True if the value is a new value for the filter, false
* otherwise.
*
* @throws TskCoreException Thrown because the attributes were unable to
* be retrieved for one of the artifacts in the
* eventData.
*/
private boolean shouldUpdateFilters(int attributeTypeId, ModuleDataEvent eventData, Set<BlackboardAttribute> filterSetToCheck) throws TskCoreException {
for (BlackboardArtifact artifact : eventData.getArtifacts()) {
if (artifact.getAttributes() != null) {
for (BlackboardAttribute attr : artifact.getAttributes()) {
if (attr.getAttributeType().getTypeID() == attributeTypeId && !filterSetToCheck.contains(attr)) {
filterSetToCheck.add(attr);
return true;
}
}
}
}
return false;
}
}
}

View File

@ -52,7 +52,7 @@ public final class DiscoveryTopComponent extends TopComponent {
private final ResultsPanel resultsPanel;
private int dividerLocation = -1;
private static final int ANIMATION_INCREMENT = 10;
private static final int ANIMATION_INCREMENT = 30;
private static final int RESULTS_AREA_SMALL_SIZE = 250;
private SwingAnimator animator = null;

View File

@ -34,13 +34,15 @@ final class DocumentFilterPanel extends AbstractFiltersPanel {
DocumentFilterPanel() {
super();
initComponents();
addFilter(new SizeFilterPanel(FileSearchData.FileType.DOCUMENTS), false, null, 0);
SizeFilterPanel sizeFilterPanel = new SizeFilterPanel(FILE_TYPE);
int[] sizeIndicesSelected = {3, 4, 5};
addFilter(sizeFilterPanel, true, sizeIndicesSelected, 0);
addFilter(new DataSourceFilterPanel(), false, null, 0);
int[] pastOccurrencesIndices;
if (!CentralRepository.isEnabled()) {
pastOccurrencesIndices = new int[]{0};
} else {
pastOccurrencesIndices = new int[]{1, 2, 3, 4, 5, 6, 7};
pastOccurrencesIndices = new int[]{2, 3, 4};
}
addFilter(new PastOccurrencesFilterPanel(), true, pastOccurrencesIndices, 0);
addFilter(new HashSetFilterPanel(), false, null, 1);

View File

@ -63,7 +63,11 @@ final class GroupListPanel extends javax.swing.JPanel {
groupKeyList.setListData(new GroupKey[0]);
}
@Messages({"GroupsListPanel.noResults.message.text=No results were found for the selected filters.",
@Messages({"GroupsListPanel.noResults.message.text=No results were found for the selected filters.\n\n"
+ "Reminder:\n"
+ " -The File Type Identification module must be run on each data source you want to find results in.\n"
+ " -The Hash Lookup module must be run on each data source if you want to filter by past occurrence.\n"
+ " -The Exif module must be run on each data source if you are filtering by User Created content.",
"GroupsListPanel.noResults.title.text=No results found"})
/**
* Subscribe to and update list of groups in response to
@ -86,7 +90,7 @@ final class GroupListPanel extends javax.swing.JPanel {
JOptionPane.showMessageDialog(DiscoveryTopComponent.getTopComponent(),
Bundle.GroupsListPanel_noResults_message_text(),
Bundle.GroupsListPanel_noResults_title_text(),
JOptionPane.INFORMATION_MESSAGE);
JOptionPane.PLAIN_MESSAGE);
}
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
});

View File

@ -35,14 +35,14 @@ final class ImageFilterPanel extends AbstractFiltersPanel {
super();
initComponents();
SizeFilterPanel sizeFilterPanel = new SizeFilterPanel(FILE_TYPE);
int[] sizeIndicesSelected = {1, 2, 3, 4, 5};
int[] sizeIndicesSelected = {3, 4, 5};
addFilter(sizeFilterPanel, true, sizeIndicesSelected, 0);
addFilter(new DataSourceFilterPanel(), false, null, 0);
int[] pastOccurrencesIndices;
if (!CentralRepository.isEnabled()) {
pastOccurrencesIndices = new int[]{0};
} else {
pastOccurrencesIndices = new int[]{1, 2, 3, 4, 5, 6, 7};
pastOccurrencesIndices = new int[]{2, 3, 4};
}
addFilter(new PastOccurrencesFilterPanel(), true, pastOccurrencesIndices, 0);
addFilter(new UserCreatedFilterPanel(), false, null, 1);

View File

@ -39,7 +39,7 @@ final class SwingAnimator {
private Timer timer = null;
//duration in milliseconds betweeen each firing of the Timer
private static final int INITIAL_TIMING = 10;
private static final int INITIAL_TIMING = 30;
private int timing = INITIAL_TIMING;
/**

View File

@ -34,13 +34,15 @@ final class VideoFilterPanel extends AbstractFiltersPanel {
VideoFilterPanel() {
super();
initComponents();
addFilter(new SizeFilterPanel(FileSearchData.FileType.VIDEO), false, null, 0);
SizeFilterPanel sizeFilterPanel = new SizeFilterPanel(FILE_TYPE);
int[] sizeIndicesSelected = {3, 4, 5};
addFilter(sizeFilterPanel, true, sizeIndicesSelected, 0);
addFilter(new DataSourceFilterPanel(), false, null, 0);
int[] pastOccurrencesIndices;
if (!CentralRepository.isEnabled()) {
pastOccurrencesIndices = new int[]{0};
} else {
pastOccurrencesIndices = new int[]{1, 2, 3, 4, 5, 6, 7};
pastOccurrencesIndices = new int[]{2, 3, 4};
}
addFilter(new PastOccurrencesFilterPanel(), true, pastOccurrencesIndices, 0);
addFilter(new UserCreatedFilterPanel(), false, null, 1);

View File

@ -23,8 +23,6 @@ HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking
HashDbIngestModule.lookingUpKnownHashValueErr=Error encountered while looking up known hash value for {0}.
# {0} - fileName
HashDbIngestModule.lookingUpNoChangeHashValueErr=Error encountered while looking up no change hash value for {0}.
HashDbIngestModule.noChangeFileSearchWillNotExecuteWarn='No Change' file search will not be executed.
HashDbIngestModule.noChangeHashDbSetMsg=No 'No Change' hash set.
HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.
HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.
HashDbManager.CentralRepoHashDb.orgError=Error loading organization
@ -34,6 +32,8 @@ HashDbManager.knownBad.text=Notable
HashDbManager.noChange.text=No Change
# {0} - hash set name
HashDbManager.noDbPath.message=Couldn't get valid hash set path for: {0}
# {0} - hashSetName
HashDbManager_handleNameConflict_conflictSuffix={0} (Custom)
HashDbSearchAction.noOpenCase.errMsg=No open case available.
HashDbSearchPanel.noOpenCase.errMsg=No open case available.
HashLookupSettingsPanel.centralRepo=Central Repository

View File

@ -22,21 +22,28 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.MissingResourceException;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.StringUtils;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.modules.InstalledFileLocator;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.WindowManager;
@ -71,10 +78,29 @@ public class HashDbManager implements PropertyChangeListener {
private List<HashDb> hashSets = new ArrayList<>();
private Set<String> hashSetNames = new HashSet<>();
private Set<String> hashSetPaths = new HashSet<>();
private List<HashDb> officialHashSets = new ArrayList<>();
private Set<String> officialHashSetNames = new HashSet<>();
private Set<String> officialHashSetPaths = new HashSet<>();
PropertyChangeSupport changeSupport = new PropertyChangeSupport(HashDbManager.class);
private static final Logger logger = Logger.getLogger(HashDbManager.class.getName());
private boolean allDatabasesLoadedCorrectly = false;
private static final String OFFICIAL_HASH_SETS_FOLDER = "OfficialHashSets";
private static final String KDB_EXT = "kdb";
private static final String DB_NAME_PARAM = "dbName";
private static final String KNOWN_STATUS_PARAM = "knownStatus";
private static final Pattern OFFICIAL_FILENAME = Pattern.compile("(?<" + DB_NAME_PARAM + ">.+?)\\.(?<" + KNOWN_STATUS_PARAM + ">.+?)\\." + KDB_EXT);
private static final FilenameFilter DEFAULT_KDB_FILTER = new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.endsWith("." + KDB_EXT);
}
};
/**
* Property change event support In events: For both of these enums, the old
* value should be null, and the new value should be the hashset name
@ -168,13 +194,7 @@ public class HashDbManager implements PropertyChangeListener {
throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.hashDbDoesNotExistExceptionMsg", path));
}
if (hashSetPaths.contains(path)) {
throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.hashDbAlreadyAddedExceptionMsg", path));
}
if (hashSetNames.contains(hashSetName)) {
throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.duplicateHashSetNameExceptionMsg", hashSetName));
}
checkDbCollision(path, hashSetName);
hashDb = addHashDatabase(SleuthkitJNI.openHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
} catch (TskCoreException ex) {
@ -225,13 +245,7 @@ public class HashDbManager implements PropertyChangeListener {
getHashDatabaseFileExtension()));
}
if (hashSetPaths.contains(path)) {
throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.hashDbAlreadyAddedExceptionMsg", path));
}
if (hashSetNames.contains(hashSetName)) {
throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.duplicateHashSetNameExceptionMsg", hashSetName));
}
checkDbCollision(path, hashSetName);
hashDb = addHashDatabase(SleuthkitJNI.createHashDatabase(path), hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
} catch (TskCoreException ex) {
@ -240,6 +254,27 @@ public class HashDbManager implements PropertyChangeListener {
return hashDb;
}
/**
* Throws an exception if the provided path or hashSetName already belong to
* an existing database.
*
* @param path The path.
* @param hashSetName The hash set name.
*
* @throws
* org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDbManagerException
* @throws MissingResourceException
*/
private void checkDbCollision(String path, String hashSetName) throws HashDbManagerException, MissingResourceException {
if (hashSetPaths.contains(path) || officialHashSetPaths.contains(path)) {
throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.hashDbAlreadyAddedExceptionMsg", path));
}
if (hashSetNames.contains(hashSetName) || officialHashSetNames.contains(hashSetName)) {
throw new HashDbManagerException(NbBundle.getMessage(HashDbManager.class, "HashDbManager.duplicateHashSetNameExceptionMsg", hashSetName));
}
}
private SleuthkitHashSet addHashDatabase(int handle, String hashSetName, boolean searchDuringIngest, boolean sendIngestMessages, HashDb.KnownFilesType knownFilesType) throws TskCoreException {
// Wrap an object around the handle.
SleuthkitHashSet hashDb = new SleuthkitHashSet(handle, hashSetName, searchDuringIngest, sendIngestMessages, knownFilesType);
@ -420,9 +455,8 @@ public class HashDbManager implements PropertyChangeListener {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
}
List<HashDb> hashDbs = new ArrayList<>();
hashDbs.addAll(this.hashSets);
return hashDbs;
return Stream.concat(this.officialHashSets.stream(), this.hashSets.stream())
.collect(Collectors.toList());
}
/**
@ -431,16 +465,10 @@ public class HashDbManager implements PropertyChangeListener {
* @return A list, possibly empty, of hash databases.
*/
public synchronized List<HashDb> getKnownFileHashSets() {
List<HashDb> hashDbs = new ArrayList<>();
try {
updateHashSetsFromCentralRepository();
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
}
this.hashSets.stream().filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN)).forEach((db) -> {
hashDbs.add(db);
});
return hashDbs;
return getAllHashSets()
.stream()
.filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN))
.collect(Collectors.toList());
}
/**
@ -449,16 +477,10 @@ public class HashDbManager implements PropertyChangeListener {
* @return A list, possibly empty, of hash databases.
*/
public synchronized List<HashDb> getKnownBadFileHashSets() {
List<HashDb> hashDbs = new ArrayList<>();
try {
updateHashSetsFromCentralRepository();
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
}
this.hashSets.stream().filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN_BAD)).forEach((db) -> {
hashDbs.add(db);
});
return hashDbs;
return getAllHashSets()
.stream()
.filter((db) -> (db.getKnownFilesType() == HashDb.KnownFilesType.KNOWN_BAD))
.collect(Collectors.toList());
}
/**
@ -467,26 +489,21 @@ public class HashDbManager implements PropertyChangeListener {
* @return A list, possibly empty, of hash databases.
*/
public synchronized List<HashDb> getUpdateableHashSets() {
return getUpdateableHashSets(this.hashSets);
return getUpdateableHashSets(getAllHashSets());
}
private List<HashDb> getUpdateableHashSets(List<HashDb> hashDbs) {
ArrayList<HashDb> updateableDbs = new ArrayList<>();
try {
updateHashSetsFromCentralRepository();
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error loading central repository hash sets", ex); //NON-NLS
}
for (HashDb db : hashDbs) {
try {
if (db.isUpdateable()) {
updateableDbs.add(db);
}
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error checking updateable status of " + db.getHashSetName() + " hash set", ex); //NON-NLS
}
}
return updateableDbs;
return hashDbs
.stream()
.filter((HashDb db) -> {
try {
return db.isUpdateable();
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error checking updateable status of " + db.getHashSetName() + " hash set", ex); //NON-NLS
return false;
}
})
.collect(Collectors.toList());
}
private List<HashDbInfo> getCentralRepoHashSetsFromDatabase() {
@ -536,66 +553,197 @@ public class HashDbManager implements PropertyChangeListener {
}
private void loadHashsetsConfiguration() {
loadOfficialHashSets();
try {
HashLookupSettings settings = HashLookupSettings.readSettings();
this.configureSettings(settings);
this.configureSettings(settings, officialHashSetNames);
} catch (HashLookupSettings.HashLookupSettingsException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Could not read Hash lookup settings from disk.", ex);
}
}
/**
* Loads official hash sets into officialHashSets and also populates
* officialHashSetPaths and officialHashSetNames variables.
*/
private void loadOfficialHashSets() {
officialHashSetPaths = new HashSet<>();
officialHashSetNames = new HashSet<>();
try {
officialHashSets = loadOfficialHashSetsFromFolder(OFFICIAL_HASH_SETS_FOLDER);
officialHashSets.forEach(db -> {
officialHashSetNames.add(db.getHashSetName());
try {
String databasePath = db.getDatabasePath();
String indexPath = db.getIndexPath();
if (StringUtils.isNotBlank(databasePath) && !databasePath.equals("None")) { //NON-NLS
officialHashSetPaths.add(databasePath);
}
if (StringUtils.isNotBlank(indexPath) && !indexPath.equals("None")) { //NON-NLS
officialHashSetPaths.add(indexPath);
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "There was an error loading the official hash set name.", ex);
}
});
} catch (HashDbManagerException ex) {
logger.log(Level.WARNING, "There was an error loading the official hash sets.", ex);
officialHashSets = new ArrayList<HashDb>();
}
}
/**
* Handles a potential conflict between official and non-official hash sets.
* Non-official hashsets have '(Custom)' added. If a conflict is identified,
* the hashset settings are fixed, saved, reloaded, and returned. Otherwise,
* the original list is returned.
*
* @param curHashsets The list of non-official hash sets.
* @param officialNames The set of names for official hash sets.
*
* @return The new list of non-official hash sets with conflicts removed.
*/
@Messages({
"# {0} - hashSetName",
"HashDbManager_handleNameConflict_conflictSuffix={0} (Custom)"
})
private List<HashDbInfo> handleNameConflict(List<HashDbInfo> curHashsets, Set<String> officialNames) {
Set<String> curNames = new HashSet<String>(officialNames);
boolean change = false;
List<HashDbInfo> newItems = new ArrayList<>();
for (HashDbInfo hashset : curHashsets) {
String thisName = hashset.getHashSetName();
if (curNames.contains(thisName)) {
while (curNames.contains(thisName)) {
thisName = Bundle.HashDbManager_handleNameConflict_conflictSuffix(thisName);
}
newItems.add(new HashDbInfo(
thisName,
hashset.getKnownFilesType(),
hashset.getSearchDuringIngest(),
hashset.getSendIngestMessages(),
hashset.getPath(),
hashset.getReferenceSetID(),
hashset.getVersion(),
hashset.isReadOnly(),
hashset.isCentralRepoDatabaseType()
));
change = true;
} else {
newItems.add(hashset);
}
curNames.add(thisName);
}
if (!change) {
return curHashsets;
} else {
try {
HashLookupSettings.writeSettings(new HashLookupSettings(newItems));
HashLookupSettings toRet = HashLookupSettings.readSettings();
return toRet.getHashDbInfo();
} catch (HashLookupSettings.HashLookupSettingsException ex) {
logger.log(Level.SEVERE, "There was an error while trying to resave after name conflict.", ex);
return newItems;
}
}
}
/**
* Loads official hash sets from the given folder.
*
* @param folder The folder from which to load official hash sets.
*
* @return The List of found hash sets.
*
* @throws HashDbManagerException If folder does not exist.
*/
private List<HashDb> loadOfficialHashSetsFromFolder(String folder) throws HashDbManagerException {
File configFolder = InstalledFileLocator.getDefault().locate(
folder, HashDbManager.class.getPackage().getName(), false);
if (configFolder == null || !configFolder.exists() || !configFolder.isDirectory()) {
throw new HashDbManagerException("Folder provided: " + folder + " does not exist.");
}
return Stream.of(configFolder.listFiles(DEFAULT_KDB_FILTER))
.map((f) -> {
try {
return getOfficialHashDbFromFile(f);
} catch (HashDbManagerException | TskCoreException ex) {
logger.log(Level.WARNING, String.format("Hashset: %s could not be properly read.", f.getAbsolutePath()), ex);
return null;
}
})
.filter((hashdb) -> hashdb != null)
.collect(Collectors.toList());
}
/**
* Loads an official hash set from the given file.
*
* @param file The kdb file to load.
*
* @return The HashDbInfo of the official set.
*
* @throws HashDbManagerException If file does not exist or does not match
* naming convention (See
* HashDbManager.OFFICIAL_FILENAME for
* regex).
*/
private HashDb getOfficialHashDbFromFile(File file) throws HashDbManagerException, TskCoreException {
if (file == null || !file.exists()) {
throw new HashDbManagerException(String.format("No file found for: %s", file == null ? "<null>" : file.getAbsolutePath()));
}
String filename = file.getName();
Matcher match = OFFICIAL_FILENAME.matcher(filename);
if (!match.find()) {
throw new HashDbManagerException(String.format("File with name: %s does not match regex of: %s", filename, OFFICIAL_FILENAME.toString()));
}
String hashdbName = match.group(DB_NAME_PARAM);
final String knownStatus = match.group(KNOWN_STATUS_PARAM);
KnownFilesType knownFilesType = Stream.of(HashDb.KnownFilesType.values())
.filter(k -> k.getIdentifier().toUpperCase().equals(knownStatus.toUpperCase()))
.findFirst()
.orElseThrow(() -> new HashDbManagerException(String.format("No KnownFilesType matches %s for file: %s", knownStatus, filename)));
return new SleuthkitHashSet(
SleuthkitJNI.openHashDatabase(file.getAbsolutePath()),
hashdbName,
true, //searchDuringIngest
false, //sendIngestMessages
knownFilesType,
true); // official set
}
/**
* Configures the given settings object by adding all contained hash db to
* the system.
*
* @param settings The settings to configure.
* @param settings The settings to configure.
* @param officialSetNames The official set names. Any name collisions will
* trigger rename for primary file.
*/
@Messages({"# {0} - hash set name", "HashDbManager.noDbPath.message=Couldn't get valid hash set path for: {0}",
"HashDbManager.centralRepoLoadError.message=Error loading central repository hash sets"})
private void configureSettings(HashLookupSettings settings) {
private void configureSettings(HashLookupSettings settings, Set<String> officialSetNames) {
allDatabasesLoadedCorrectly = true;
List<HashDbInfo> hashDbInfoList = settings.getHashDbInfo();
hashDbInfoList = handleNameConflict(hashDbInfoList, officialSetNames);
for (HashDbInfo hashDbInfo : hashDbInfoList) {
try {
if (hashDbInfo.isFileDatabaseType()) {
String dbPath = this.getValidFilePath(hashDbInfo.getHashSetName(), hashDbInfo.getPath());
if (dbPath != null) {
addHashDatabase(SleuthkitJNI.openHashDatabase(dbPath), hashDbInfo.getHashSetName(), hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType());
} else {
logger.log(Level.WARNING, Bundle.HashDbManager_noDbPath_message(hashDbInfo.getHashSetName()));
allDatabasesLoadedCorrectly = false;
}
} else {
if (CentralRepository.isEnabled()) {
addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(),
hashDbInfo.getReferenceSetID(),
hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(),
hashDbInfo.getKnownFilesType(), hashDbInfo.isReadOnly());
}
}
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
NbBundle.getMessage(this.getClass(),
"HashDbManager.unableToOpenHashDbMsg", hashDbInfo.getHashSetName()),
NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"),
JOptionPane.ERROR_MESSAGE);
allDatabasesLoadedCorrectly = false;
}
configureLocalDb(hashDbInfo);
}
if (CentralRepository.isEnabled()) {
try {
updateHashSetsFromCentralRepository();
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.HashDbManager_centralRepoLoadError_message(),
NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"),
JOptionPane.ERROR_MESSAGE);
allDatabasesLoadedCorrectly = false;
}
configureCrDbs();
}
/*
@ -619,6 +767,56 @@ public class HashDbManager implements PropertyChangeListener {
}
}
/**
* Configures central repository hash set databases.
*/
private void configureCrDbs() {
try {
updateHashSetsFromCentralRepository();
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
Bundle.HashDbManager_centralRepoLoadError_message(),
NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"),
JOptionPane.ERROR_MESSAGE);
allDatabasesLoadedCorrectly = false;
}
}
/**
* Handles configuring a local hash set database.
* @param hashDbInfo The local hash set database.
*/
private void configureLocalDb(HashDbInfo hashDbInfo) {
try {
if (hashDbInfo.isFileDatabaseType()) {
String dbPath = this.getValidFilePath(hashDbInfo.getHashSetName(), hashDbInfo.getPath());
if (dbPath != null) {
addHashDatabase(SleuthkitJNI.openHashDatabase(dbPath), hashDbInfo.getHashSetName(), hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(), hashDbInfo.getKnownFilesType());
} else {
logger.log(Level.WARNING, Bundle.HashDbManager_noDbPath_message(hashDbInfo.getHashSetName()));
allDatabasesLoadedCorrectly = false;
}
} else {
if (CentralRepository.isEnabled()) {
addExistingCentralRepoHashSet(hashDbInfo.getHashSetName(), hashDbInfo.getVersion(),
hashDbInfo.getReferenceSetID(),
hashDbInfo.getSearchDuringIngest(), hashDbInfo.getSendIngestMessages(),
hashDbInfo.getKnownFilesType(), hashDbInfo.isReadOnly());
}
}
} catch (TskCoreException ex) {
Logger.getLogger(HashDbManager.class.getName()).log(Level.SEVERE, "Error opening hash set", ex); //NON-NLS
JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(),
NbBundle.getMessage(this.getClass(),
"HashDbManager.unableToOpenHashDbMsg", hashDbInfo.getHashSetName()),
NbBundle.getMessage(this.getClass(), "HashDbManager.openHashDbErr"),
JOptionPane.ERROR_MESSAGE);
allDatabasesLoadedCorrectly = false;
}
}
private void updateHashSetsFromCentralRepository() throws TskCoreException {
if (CentralRepository.isEnabled()) {
List<HashDbInfo> crHashDbInfoList = getCentralRepoHashSetsFromDatabase();
@ -702,17 +900,21 @@ public class HashDbManager implements PropertyChangeListener {
})
public enum KnownFilesType {
KNOWN(Bundle.HashDbManager_known_text(), TskData.FileKnown.KNOWN, false, false),
KNOWN_BAD(Bundle.HashDbManager_knownBad_text(), TskData.FileKnown.BAD, true, true),
NO_CHANGE(Bundle.HashDbManager_noChange_text(), TskData.FileKnown.UNKNOWN, true, false);
KNOWN(Bundle.HashDbManager_known_text(), "Known", TskData.FileKnown.KNOWN, false, false),
KNOWN_BAD(Bundle.HashDbManager_knownBad_text(), "Notable", TskData.FileKnown.BAD, true, true),
NO_CHANGE(Bundle.HashDbManager_noChange_text(), "NoChange", TskData.FileKnown.UNKNOWN, true, false);
private final String displayName;
private final String identifier;
private final TskData.FileKnown fileKnown;
private final boolean allowSendInboxMessages;
private final boolean defaultSendInboxMessages;
KnownFilesType(String displayName, TskData.FileKnown fileKnown, boolean allowSendInboxMessages, boolean defaultSendInboxMessages) {
KnownFilesType(String displayName, String identifier, TskData.FileKnown fileKnown,
boolean allowSendInboxMessages, boolean defaultSendInboxMessages) {
this.displayName = displayName;
this.identifier = identifier;
this.fileKnown = fileKnown;
this.allowSendInboxMessages = allowSendInboxMessages;
this.defaultSendInboxMessages = defaultSendInboxMessages;
@ -740,6 +942,16 @@ public class HashDbManager implements PropertyChangeListener {
return defaultSendInboxMessages;
}
/**
* Returns the identifier for this KnownFilesType. This is used for
* Official Hash Sets in their naming convention.
*
* @return The identifier for this type.
*/
String getIdentifier() {
return identifier;
}
public String getDisplayName() {
return this.displayName;
}
@ -863,14 +1075,20 @@ public class HashDbManager implements PropertyChangeListener {
private final HashDb.KnownFilesType knownFilesType;
private boolean indexing;
private final PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private final boolean officialSet;
private SleuthkitHashSet(int handle, String hashSetName, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType) {
this(handle, hashSetName, useForIngest, sendHitMessages, knownFilesType, false);
}
private SleuthkitHashSet(int handle, String hashSetName, boolean useForIngest, boolean sendHitMessages, KnownFilesType knownFilesType, boolean officialSet) {
this.handle = handle;
this.hashSetName = hashSetName;
this.searchDuringIngest = useForIngest;
this.sendIngestMessages = sendHitMessages;
this.knownFilesType = knownFilesType;
this.indexing = false;
this.officialSet = officialSet;
}
/**
@ -956,6 +1174,10 @@ public class HashDbManager implements PropertyChangeListener {
*/
@Override
public boolean isUpdateable() throws TskCoreException {
if (isOfficialSet()) {
return false;
}
return SleuthkitJNI.isUpdateableHashDatabase(this.handle);
}
@ -986,6 +1208,7 @@ public class HashDbManager implements PropertyChangeListener {
public void addHashes(Content content, String comment) throws TskCoreException {
// This only works for AbstractFiles and MD5 hashes at present.
assert content instanceof AbstractFile;
officialSetCheck();
if (content instanceof AbstractFile) {
AbstractFile file = (AbstractFile) content;
if (null != file.getMd5Hash()) {
@ -994,6 +1217,17 @@ public class HashDbManager implements PropertyChangeListener {
}
}
/**
* Throws an exception if the current set is an official set.
*
* @throws TskCoreException
*/
private void officialSetCheck() throws TskCoreException {
if (isOfficialSet()) {
throw new TskCoreException("Hashes cannot be added to an official set");
}
}
/**
* Adds a list of hashes to the hash database at once
*
@ -1003,6 +1237,7 @@ public class HashDbManager implements PropertyChangeListener {
*/
@Override
public void addHashes(List<HashEntry> hashes) throws TskCoreException {
officialSetCheck();
SleuthkitJNI.addToHashDatabase(hashes, handle);
}
@ -1122,6 +1357,16 @@ public class HashDbManager implements PropertyChangeListener {
}
return true;
}
/**
* Returns whether or not the set is an official set. If the set is an
* official set, it is treated as readonly and cannot be deleted.
*
* @return Whether or not the set is an official set.
*/
boolean isOfficialSet() {
return officialSet;
}
}
/**

View File

@ -373,6 +373,35 @@ final class HashLookupSettings implements Serializable {
private final int referenceSetID;
private DatabaseType dbType;
/**
* Constructs a HashDbInfo object.
*
* @param hashSetName The name of the hash set
* @param knownFilesType The known files type
* @param searchDuringIngest Whether or not the db is searched during
* ingest
* @param sendIngestMessages Whether or not ingest messages are sent
* @param path The path to the db
* @param referenceSetID The reference set ID.
* @param version The version for the hashset.
* @param readOnly Whether or not the set is readonly.
* @param isCRType A central repo db type (otherwise, file type).
*/
HashDbInfo(String hashSetName, HashDbManager.HashDb.KnownFilesType knownFilesType, boolean searchDuringIngest, boolean sendIngestMessages,
String path, int referenceSetID, String version, boolean readOnly, boolean isCRType) {
this.hashSetName = hashSetName;
this.knownFilesType = knownFilesType;
this.searchDuringIngest = searchDuringIngest;
this.sendIngestMessages = sendIngestMessages;
this.path = path;
this.referenceSetID = referenceSetID;
this.version = version;
this.readOnly = readOnly;
this.dbType = isCRType ? DatabaseType.CENTRAL_REPOSITORY : DatabaseType.FILE;
}
/**
* Constructs a HashDbInfo object for files type
*

View File

@ -25,8 +25,10 @@ import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.MissingResourceException;
import java.util.logging.Level;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
@ -37,6 +39,7 @@ import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import org.apache.commons.lang3.StringUtils;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
@ -197,68 +200,7 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
if (db instanceof SleuthkitHashSet) {
SleuthkitHashSet hashDb = (SleuthkitHashSet) db;
// Disable the central repo fields
hashDbVersionLabel.setText(Bundle.HashLookupSettingsPanel_notApplicable());
hashDbOrgLabel.setText(Bundle.HashLookupSettingsPanel_notApplicable());
// Enable the delete button if ingest is not running
deleteDatabaseButton.setEnabled(!ingestIsRunning);
try {
hashDbLocationLabel.setText(db.getDatabasePath());
} catch (TskCoreException ex) {
Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error getting hash set path of " + db.getHashSetName() + " hash set", ex); //NON-NLS
hashDbLocationLabel.setText(ERROR_GETTING_PATH_TEXT);
}
try {
indexPathLabel.setText(hashDb.getIndexPath());
} catch (TskCoreException ex) {
Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error getting index path of " + db.getHashSetName() + " hash set", ex); //NON-NLS
indexPathLabel.setText(ERROR_GETTING_PATH_TEXT);
}
// Update indexing components.
try {
if (hashDb.isIndexing()) {
indexButton.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.indexing"));
hashDbIndexStatusLabel.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexStatusText.indexGen"));
hashDbIndexStatusLabel.setForeground(Color.black);
indexButton.setEnabled(false);
} else if (hashDb.hasIndex()) {
if (hashDb.hasIndexOnly()) {
hashDbIndexStatusLabel.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexStatusText.indexOnly"));
} else {
hashDbIndexStatusLabel.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexStatusText.indexed"));
}
hashDbIndexStatusLabel.setForeground(Color.black);
if (hashDb.canBeReIndexed()) {
indexButton.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.reIndex"));
indexButton.setEnabled(true);
} else {
indexButton.setText(NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.index"));
indexButton.setEnabled(false);
}
} else {
hashDbIndexStatusLabel.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexStatusText.noIndex"));
hashDbIndexStatusLabel.setForeground(Color.red);
indexButton.setText(NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.index"));
indexButton.setEnabled(true);
}
} catch (TskCoreException ex) {
Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error getting index state of hash set", ex); //NON-NLS
hashDbIndexStatusLabel.setText(ERROR_GETTING_INDEX_STATUS_TEXT);
hashDbIndexStatusLabel.setForeground(Color.red);
indexButton.setText(NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.index"));
indexButton.setEnabled(false);
}
updateForSleuthkitHashSet(ingestIsRunning, hashDb);
} else {
// Disable the file type fields/buttons
@ -291,6 +233,93 @@ public final class HashLookupSettingsPanel extends IngestModuleGlobalSettingsPan
ingestWarningLabel.setVisible(ingestIsRunning);
}
private static String getPathString(String path, boolean justFilename) {
if (StringUtils.isBlank(path)) {
return "";
}
if (!justFilename) {
return path;
}
return new File(path).getName();
}
/**
* Updates UI for a SleuthkitHashSet.
*
* @param ingestIsRunning Whether or not ingest is running.
* @param hashDb The hash set to be included in the list of hash
* sets.
*
* @throws MissingResourceException
*/
private void updateForSleuthkitHashSet(boolean ingestIsRunning, SleuthkitHashSet hashDb) throws MissingResourceException {
// Disable the central repo fields
hashDbVersionLabel.setText(Bundle.HashLookupSettingsPanel_notApplicable());
hashDbOrgLabel.setText(Bundle.HashLookupSettingsPanel_notApplicable());
// Enable the delete button if ingest is not running and is not an official hashset
deleteDatabaseButton.setEnabled(!ingestIsRunning && !hashDb.isOfficialSet());
try {
String dbPath = getPathString(hashDb.getDatabasePath(), hashDb.isOfficialSet());
hashDbLocationLabel.setText(dbPath);
} catch (TskCoreException ex) {
Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error getting hash set path of " + hashDb.getHashSetName() + " hash set", ex); //NON-NLS
hashDbLocationLabel.setText(ERROR_GETTING_PATH_TEXT);
}
try {
String indexPath = getPathString(hashDb.getIndexPath(), hashDb.isOfficialSet());
indexPathLabel.setText(indexPath);
} catch (TskCoreException ex) {
Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error getting index path of " + hashDb.getHashSetName() + " hash set", ex); //NON-NLS
indexPathLabel.setText(ERROR_GETTING_PATH_TEXT);
}
// Update indexing components.
try {
if (hashDb.isIndexing()) {
indexButton.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.indexing"));
hashDbIndexStatusLabel.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexStatusText.indexGen"));
hashDbIndexStatusLabel.setForeground(Color.black);
indexButton.setEnabled(false);
} else if (hashDb.hasIndex()) {
if (hashDb.hasIndexOnly()) {
hashDbIndexStatusLabel.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexStatusText.indexOnly"));
} else {
hashDbIndexStatusLabel.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexStatusText.indexed"));
}
hashDbIndexStatusLabel.setForeground(Color.black);
if (hashDb.canBeReIndexed()) {
indexButton.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.reIndex"));
indexButton.setEnabled(true);
} else {
indexButton.setText(NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.index"));
indexButton.setEnabled(false);
}
} else {
hashDbIndexStatusLabel.setText(
NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexStatusText.noIndex"));
hashDbIndexStatusLabel.setForeground(Color.red);
indexButton.setText(NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.index"));
indexButton.setEnabled(true);
}
} catch (TskCoreException ex) {
Logger.getLogger(HashLookupSettingsPanel.class.getName()).log(Level.SEVERE, "Error getting index state of hash set", ex); //NON-NLS
hashDbIndexStatusLabel.setText(ERROR_GETTING_INDEX_STATUS_TEXT);
hashDbIndexStatusLabel.setForeground(Color.red);
indexButton.setText(NbBundle.getMessage(this.getClass(), "HashDbConfigPanel.indexButtonText.index"));
indexButton.setEnabled(false);
}
}
private boolean isLocalIngestJobEvent(PropertyChangeEvent evt) {
if (evt instanceof AutopsyEvent) {
AutopsyEvent event = (AutopsyEvent) evt;

View File

@ -12,7 +12,7 @@ FilesSetDefsPanel.exportButtonActionPerformed.fileExistPrompt=File {0} exists, o
FilesSetDefsPanel.gigaBytes=Gigabytes
# {0} - fileName
# {1} - errorMessage
FilesSetDefsPanel.importSetButtonActionPerformed.importError=The rules file "{0}" could not be read:\n"{1}."
FilesSetDefsPanel.importSetButtonActionPerformed.importError=The rules file "{0}" could not be read:\n{1}.
FilesSetDefsPanel.importSetButtonActionPerformed.noFiles=No files sets were found in the selected files.
FilesSetDefsPanel.importSetButtonActionPerformed.noFilesSelected=No files sets were selected.
# {0} - filter name
@ -24,6 +24,7 @@ FilesSetDefsPanel.interesting.exportButtonAction.featureName=Interesting Files S
FilesSetDefsPanel.interesting.ExportedMsg=Interesting files set exported
FilesSetDefsPanel.interesting.exportSetButton.text=Export Set
FilesSetDefsPanel.interesting.failExportMsg=Export of interesting files set failed
FilesSetDefsPanel.interesting.failImportMsg=Interesting files set not imported
FilesSetDefsPanel.interesting.fileExtensionFilterLbl=Autopsy Interesting File Set File (xml)
FilesSetDefsPanel.interesting.importButtonAction.featureName=Interesting Files Set Import
FilesSetDefsPanel.interesting.importOwConflict=Import Interesting files set conflict
@ -33,7 +34,7 @@ FilesSetDefsPanel.interesting.newOwConflict=Interesting files set conflict
FilesSetDefsPanel.interesting.overwriteSetPrompt=Interesting files set "{0}" already exists locally, overwrite?
# {0} - FilesSet name
# {1} - New FilesSet name
FilesSetDefsPanel.interesting.standardFileConflict=A standard interesting file set already exists with the name "{0}." Would you like to rename the set to "{1}?"
FilesSetDefsPanel.interesting.standardFileConflict=A standard interesting file set already exists with the name "{0}." Would you like to rename your set to "{1}?"
FilesSetDefsPanel.Interesting.Title=Global Interesting Items Settings
FilesSetDefsPanel.kiloBytes=Kilobytes
FilesSetDefsPanel.loadError=Error loading interesting files sets from file.
@ -64,7 +65,7 @@ FilesSetRulePanel.ZeroFileSizeError=File size condition value must not be 0 (Unl
FilesSetsManager.allFilesAndDirectories=All Files and Directories (Not Unallocated Space)
FilesSetsManager.allFilesDirectoriesAndUnallocated=All Files, Directories, and Unallocated Space
# {0} - regex
InterestingItemsFilesSetSettings.readDateCondition.failedCompiledRegex=Error detmining ''{0}'' number
InterestingItemsFilesSetSettings.readDateCondition.failedCompiledRegex=Error determining ''{0}'' number
# {0} - condition
# {1} - rule
InterestingItemsFilesSetSettings.readMetaTypeCondition.malformedXml=Files set is malformed for metatype condition, ''{0}'', in rule ''{1}''
@ -180,3 +181,6 @@ FilesSetDefsPanel.mimeTypeLabel.text=MIME Type:
FilesSetDefsPanel.fileSizeLabel.text=File Size:
# {0} - filesSetName
StandardInterestingFileSetsLoader.customSuffixed={0} (Custom)
StandardInterestingFilesSetsLoader_cannotLoadStandard=Unable to properly read standard interesting files sets.
StandardInterestingFilesSetsLoader_cannotLoadUserConfigured=Unable to properly read user-configured interesting files sets.
StandardInterestingFilesSetsLoader_cannotUpdateInterestingFilesSets=Unable to write updated configuration for interesting files sets to config directory.

View File

@ -19,7 +19,7 @@ ReportProgressIndicator.switchToIndeterminateMessage=Report generation progress
ReportWizardDataSourceSelectionPanel.confirmEmptySelection=Are you sure you want to proceed with no selections?
ReportWizardDataSourceSelectionPanel.finishButton.text=Finish
ReportWizardDataSourceSelectionPanel.nextButton.text=Next
ReportWizardDataSourceSelectionPanel.title=Select which datasource(s) to include
ReportWizardDataSourceSelectionPanel.title=Select which data source(s) to include
ReportWizardFileOptionsVisualPanel.jLabel1.text=Select items to include in File Report:
ReportWizardFileOptionsVisualPanel.deselectAllButton.text=Deselect All
ReportWizardFileOptionsVisualPanel.selectAllButton.text=Select All

View File

@ -87,7 +87,7 @@ public class ReportWizardDataSourceSelectionPanel implements WizardDescriptor.Fi
}
@NbBundle.Messages({
"ReportWizardDataSourceSelectionPanel.title=Select which datasource(s) to include"
"ReportWizardDataSourceSelectionPanel.title=Select which data source(s) to include"
})
@Override
public CheckBoxListPanel<Long> getComponent() {

View File

@ -568,9 +568,12 @@ public class PortableCaseReportModule implements ReportModule {
* @param content Content to add to the report.
* @param dataSource Parent dataSource of the content instance.
* @param tmpDir Path to the tmpDir to enforce uniqueness
* @param reportGenerator Report generator instance to add the content to
* @param gson
* @param exporter
* @param reportWriter Report generator instance to add the content to
* @param dataSourceHasBeenIncluded Flag determining if the data source
* should be written to the report (false indicates that it should be written).
*
* @throws IOException If an I/O error occurs.
* @throws TskCoreException If an internal database error occurs.
*

View File

@ -40,7 +40,7 @@ public class TimeLineModule {
private static final Logger logger = Logger.getLogger(TimeLineModule.class.getName());
private static final Object controllerLock = new Object();
private static TimeLineController controller;
private static volatile TimeLineController controller;
/**
* provides static utilities, can not be instantiated

View File

@ -5,54 +5,54 @@ Ingest:
Ingest Modules:
- Include Interesting File set rules for cloud storage, encryption, cryptocurrency and privacy programs.
- Updated PhotoRec 7.1 and include 64-bit version
- Updated PhotoRec 7.1 and include 64-bit version.
- Updated RegRipper in Recent Activity to 2.8
- Create artifacts for Prefetch, Background Activity Monitor, and System Resource Usage.
- Support MBOX files greater than 2GB
- Support MBOX files greater than 2GB.
- Document metadata is saved as explicit artifacts and added to the timeline.
- New “no change” hashset type that does not change status of file.
Central Repository / Personas:
- Accounts in the Central Repository can be grouped together and associated with a digital persona
- Accounts in the Central Repository can be grouped together and associated with a digital persona.
- All accounts are now stored in the Central Repository to support correlation and persona creation.
Content viewers:
- Created artifact-specific viewers in the Results viewer for contact book and call log.
- Moved Message viewer to a Results sub-viewer and expanded to show accounts.
- Added Application sub-viewer for PDF files based on IcePDF.
- Annotation viewer now includes comments from hash set hit and interesting file set hit artifacts
- Annotation viewer now includes comments from hash set hits.
Geolocation Viewer
- Different data types now are displayed using different colors
Geolocation Viewer:
- Different data types now are displayed using different colors.
- Track points in a track are now displayed as small, connected circles instead of full pins.
- Filter panel shows only data sources with geo location data.
- Geolocation artifact points can be tagged and commented upon
- Geolocation artifact points can be tagged and commented upon.
File Discovery
File Discovery:
- Changed UI to have more of a search flow and content viewer is hidden until an item is selected.
Reports
Reports:
- Can be generated for a single data source instead of the entire case.
- CASE / UCO report module now includes artifacts in addition to files.
- Added backend concept of Tag Sets to support Project Vic categories from different countries.
Performance:
- Add throttling of UI refreshes to ensure data is quickly displayed and the tree does not get backed up with requests.
- Improved efficiency of adding a data source with many orphan files
- Improved efficiency of loading file systems
- Jython interpreter is preloaded at application startup
- Improved efficiency of adding a data source with many orphan files.
- Improved efficiency of loading file systems.
- Jython interpreter is preloaded at application startup.
Misc bug fixes and improvements
- Fixed bug from last release where hex content viewer text was no longer fixed width
- Altered locking to allow multiple data sources to be added at once more smoothly and to support batch inserts of file data
- Central repository comments will no longer store tag descriptions
- Account type nodes in the Accounts tree show counts
- Full time stamps displayed for messages in ingest inbox
- More detailed status during file exports
- Improved efficiency of adding timeline events
- Fixed bug with CVT most recent filter
- Improved documentation and support for running on Linux/macOS
Misc bug fixes and improvements:
- Fixed bug from last release where hex content viewer text was no longer fixed width.
- Altered locking to allow multiple data sources to be added at once more smoothly and to support batch inserts of file data.
- Central repository comments will no longer store tag descriptions.
- Account type nodes in the Accounts tree show counts.
- Full time stamps displayed for messages in ingest inbox.
- More detailed status during file exports.
- Improved efficiency of adding timeline events.
- Fixed bug with CVT most recent filter.
- Improved documentation and support for running on Linux/macOS.
---------------- VERSION 4.15.0 --------------
New UI Features:

View File

@ -380,7 +380,7 @@ final class ExtractPrefetch extends Extract {
for (AbstractFile pFile : files) {
if (pFile.getParentPath().toLowerCase().contains(filePath.toLowerCase())) {
if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase() + '/')) {
return pFile;
}
}

View File

@ -218,12 +218,6 @@ the Case -> Case Properties menu.
This shows how common the selected file is. The value is the percentage of case/data source tuples that have the selected property.
\subsection central_repo_comment Add/Edit Comment
If you want instead to edit the comment of a node, it can be done by right clicking on the original item in the result viewer and selecting "Add/Edit Central Repository Comment".
\image html central_repo_comment_menu.png
\subsection cr_interesting_items Interesting Items
In the Results tree of an open case is an entry called Interesting Items. When this module is enabled, all of the enabled

View File

@ -3,14 +3,15 @@
What Does It Do
========
The Hash Lookup Module calculates MD5 hash values for files and looks up hash values in a database to determine if the file is notable, known (in general), or unknown.
The Hash Lookup Module calculates MD5 hash values for files and looks up hash values in a database to determine if the file is notable, known (in general), included in a specific set of files, or unknown.
Configuration
=======
The Hash Sets tab on the Options panel is where you can set and update your hash set information. Hash sets are used to identify files that are 'known' or 'notable'.
The Hash Sets tab on the Options panel is where you can set and update your hash set information. Hash sets are used to identify files that are 'known', 'notable', or 'no change'.
\li Known good files are those that can be safely ignored. This set of files frequently includes standard OS and application files. Ignoring such uninteresting-to-the-investigator files, can greatly reduce image analysis time.
\li Notable (or known bad) files are those that should raise awareness. This set will vary depending on the type of investigation, but common examples include contraband images and malware.
\li No change files are files that can reveal information about the system but are not notable. For example, knowning an image contains many files known to be maps of London could be interesting to an investigator, but the maps themselves are not notable.
\section adding_hashsets Importing Hash Sets
@ -35,7 +36,7 @@ To import an existing hash set, use the "Import Database" button on the Hash Set
<b>Source Organization</b> - The organization can only be entered when importing the hash set into the central repository. See the section on \ref cr_manage_orgs "managing organizations" for more information.
<b>Type of database</b> - All entries in the hash set should either be "known" (can be safely ignored) or "notable" (could be indicators of suspicious behavior).
<b>Type of database</b> - All entries in the hash set should either be "known" (can be safely ignored), "notable" (could be indicators of suspicious behavior), or "no change" (known to be a certain type of file).
<b>Make database read-only</b> - The read-only setting is only active when importing the hash set into the central repository. A read-only database can not have new hashes added to it through either the Hash Sets options panel or the context menu. For locally imported hash sets, whether they can be written to is dependent on the type of hash set. Autopsy format databases (*.kdb) can be edited, but all other types will be read-only.
@ -52,7 +53,7 @@ After importing the hash set, you may have to index it before it can be used. Fo
Autopsy uses the hash set management system from The Sleuth Kit. You can manually create an index using the 'hfind' command line tool or you can use Autopsy. If you attempt proceed without indexing a hash set, Autopsy will offer to automatically produce an index for you.
You can also specify only the index file and not use the full hash set - the index file is sufficient to identify known files. This can save space. To do this, specify the .idx file from the Hash Sets option panel.
\section creating_hashsets Creating Hash sets
\section creating_hashsets Creating Hash Sets
New hash sets can be created using the "New Hash Set" button. The fields are mostly the same as the \ref adding_hashsets "import dialog" described above.
@ -60,6 +61,15 @@ New hash sets can be created using the "New Hash Set" button. The fields are mos
In this case, the Database Path is where the new database will be stored. If the central repository is being used then this field is not needed.
\section hash_adding_hashes Adding Hashes to a Hash Set
Once you've created a hash set you'll need to add hashes two it. The first way to do this is using the "Add Hashes to Hash Set" button on the options panel. Each hash should be on its own line, and can optionally be followed by a comma and then a comment about the file that hash corresponds to. Here we are creating a "no change" hash set corresponding to cat images:
\image html hash_add.png
The other way to add an entry to a hash set is through the context menu. Highlight the file you want to add to a hash set in the result viewer and right-click, then select "Add file to hash set" and finally the set you want to add it to. Note that this does not automatically add the file to the list of hash set hits for the current case - you will have to re-run the Hash Lookup ingest module to see it appear there.
\image html hash_add_context.png
\section using_hashsets Using Hash Sets
There is an \ref ingest_page "ingest module" that will hash the files and look them up in the hash sets. It will flag files that were in the notable hash set and those results will be shown in the Results tree of the \ref tree_viewer_page.
@ -90,8 +100,10 @@ When hash sets are configured, the user can select the hash sets to use during t
Seeing Results
------
Results show up in the tree as "Hashset Hits", grouped by the name of the hash set.
Results show up in the tree as "Hashset Hits", grouped by the name of the hash set. If the hash set hits had associated comments, you will see them in the "Comment" column in the result viewer along with the file hash.
\image html hashset-hits.PNG
You can also view the comments on the "Annotation" tab of the content viewer.
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -71,6 +71,7 @@ The following topics are available here:
- \subpage communications_page
- \subpage geolocation_page
- \subpage discovery_page
- \subpage personas_page
- Reporting
- \subpage tagging_page

View File

@ -0,0 +1,107 @@
/*! \page personas_page Personas
\section personas_overview Overview
Autopsy can store and organize account information based on personas, which represent an online identity. A person may have several online identities and therefore several personas. As an example, a single person may have a set of accounts that post online about loving cats and another set of accounts that appear unrelated that post about hating cats.
\section personas_concepts Concepts
Here are some basic concepts about persona:
<ul>
<li>To make a persona, you need to have a name and at least one account.
<li>A persona may have multiple accounts - a phone number, an email, a Facebook account, a Skype account, etc.
<li>Personas span cases and therefore are stored in the \ref central_repo_page "Central Repository".
<li>You can manually create a persona or make one based on a contact book entry, or a call log, or a message.
<li>An account may be part of multiple personas. This can happen if someone uses the same cell phone as a recovery number for accounts on multiple personas.
<li>A persona may have additional metadata associated with it as name/value pairs.
<li>A persona may have one or more aliases.
<li>Autopsy will show you if an account is part of a persona, where applicable.
</ul>
Personas are stored in the Central Repository based on accounts that were found in results. These results are generated by various ingest modules such as the \ref recent_activity_page and \ref android_analyzer_page.
Autopsy provides a dedicated tool, \ref personas_editor "Personas Editor", to create, view, edit, and delete personas.
\section personas_editor Personas Editor
The Personas Editor is loaded through the Tools -> Personas menu item.
The left panel in the Personas Editor is a table that lists personas, based on the selected criteria. The right panel displays the details of selected persona.
By default, when the Personas Editor is launched, all the personas in the Central Repository are listed in the table. You may filter this list by checking the "Filter personas by Keyword" checkbox. Type in either a persona name or an account identifier in the textbox and select the "Name" or "Account" radio button appropriately. Then click the "Show" button to show only the personas that match the filtering criteria.
\image html Personas/personas_main.png
\subsection personas_create Create Personas
To create a new persona, click the "New Persona" button. A "Create Persona" dialog box will pop up. The following is a description of each field:
<ul>
<li><b>Created by</b>: Will be automatically filled in with the current user
<li><b>Created on</b>: Will be automatically filled in after saving the persona
<li><b>Comment</b>: A description of the persona
<li><b>Name</b>: The name of the persona
<li><b>Accounts</b>: At least one account belonging to the persona
<li><b>Metadata</b>: (Optional) Name/value pairs of data related to the persona
<li><b>Aliases</b>: (Optional) Any aliases for this persona
<li><b>Cases found in</b>: Will be automatically filled in when editing a persona
</ul>
Each persona needs at least one account associated with it. These accounts must have been previously saved to the \ref central_repo_page "central repository". Clicking "Add" under "Accounts" will bring up another dialog with four fields, all required:
<ul>
<li><b>Identifier</b>: The identifier for the account (phone number, email address, Facebook account id, etc)
<li><b>Type</b>: The type of identifier
<li><b>Confidence</b>: General confidence that this account goes with the given persona
<li><b>Justification</b>: Why this account is being added to the persona
</ul>
\image html Personas/personas_create.png
When finished adding at least one account and filling in the required fields, click on OK to create the persona. A persona with the specified name will be created and associated with the specified account(s).
\subsection personas_edit Edit Personas
To edit a persona, click the "Edit Persona" button. You'll be able to edit all the data about the persona.
\image html Personas/persona_edit.png
\subsection personas_delete Delete Personas
To delete a persona, select the persona in the table and click on the "Delete Persona" button. Click "Yes" on confirmation dialog to delete the selected persona.
\subsection personas_account_create Create Account
All personas must be associated with at least one account. Normally these account will be added to the central repository by various ingest modules, but you can also create them manually with the "Create Account" button.
\image html Personas/personas_create_account.png
\section personas_artifact_viewers Persona integration in Content Viewers
Autopsy shows persona associated with accounts, where applicable. When viewing contact, call log and message results, Autopsy shows the personas associated with accounts in these panels. If no persona exists for an account, Autopsy provides a button for the user to create one.
As shown below, when viewing a contact result you may see persona data. When one or more personas are found associated with the accounts in the result then the Persona name is shown in the contact content viewer. There will be a "View" button to see the details of the persona.
\image html Personas/personas_contact_found.png
If no matching persona is found, a "Create" button is shown to create a persona for the account(s). This will bring you to the \ref personas_create "Create Personas" panel with the account(s) already added.
\image html Personas/personas_contact_not_found.png
Personas are integrated similarly in the content viewers for call logs and messages/e-mail.
\image html Personas/personas_calllog.png
<br>
\image html Personas/personas_message.png
\section personas_cvt Persona Integration in the Communications Visualization Tool
Personas are integrated with the \ref communications_page. When viewing accounts in the Accounts browsers in the Communications Visualization Tool, associated persona information is shown in the tooltip if you hover over the account.
\image html Personas/personas_cvt_hover.png
As in the Autopsy main window, you may also create or view personas when examining contacts, call logs, and messages in the Communications Visualization Tool.
\image html Personas/personas_cvt_accounts.png
*/

View File

@ -8,6 +8,10 @@ of any coordinates found to load into software like Google Earth.
\image html reports_select.png
Most report types will allow you to select which data sources to include in the report. Note that the names of excluded data sources may still be present in the report. For example, the \ref report_html will list all data sources in the case on the main page but will not contain results, tagged files, etc. from the excluded data source(s).
\image html reports_datasource_select.png
The different types of reports will be described below. The majority of the report modules will generate a report file which
will be displayed in the case under the "Reports" node of the \ref tree_viewer_page.

View File

@ -1,6 +1,6 @@
/*! \page tagging_page Tagging
/*! \page tagging_page Tagging and Commenting
Tagging (or Bookmarking) allows you to create a reference to a file or object and easily find it later or include it in a \ref reporting_page "report". Tagging is also used by the \ref central_repo_page "central repository" to mark items as notable.
Tagging (or Bookmarking) allows you to create a reference to a file or object and easily find it later or include it in a \ref reporting_page "report". Tagging is also used by the \ref central_repo_page "central repository" to mark items as notable. You can add comments to files and results using tags or through the central repository.
\section tagging_items Tagging items
@ -99,7 +99,7 @@ If using the central repository, changing the notable status will effect tagged
- If "File A" is tagged with "Tag A", which is not notable, and then "Tag A" is switched to notable, "File A" will be marked as notable in the central repository
- If "File B" is tagged with "Tag B", which is notable, and then "Tag B" is switched to non-notable, if there are no other notable tags on "File B" then its notable status in the central repository will be removed.
\section user_tags Hiding tags from other users
\subsection user_tags Hiding tags from other users
Tags are associated with the account name of the user that tagged them. This information is visible through selecting items under the "Tags" section of the directory tree:
@ -113,4 +113,26 @@ It is possible to hide all tagged files and results in the "Tags" area of the tr
\image html tagging_view_options.png
\section tagging_commenting Commenting
There are two methods to adding comments to files and results. The first method was discussed in the \ref tagging_items section. Right click on the file or result of interest, choose "Add File Tag" or "Add Result Tag" and then "Tag and Comment". This allows you to add a comment about the item. You can add multiple tags with comments to the same file or result.
\image html tagging_comment_context.png
If you have a \ref central_repo_page "central repository" enabled, you can also use it to save comments about files. Right click on the file and select "Add/Edit Central Repository Comment". If there was already a comment for this file it will appear in the dialog and can be changed - only one central repository comment can be stored at a time.
\image html tagging_cr_comment.png
If a file or result has a comment associated with it, you'll see a notepad icon in the "C" column of the result viewer. Hovering over it will tell you what type of comments are on the item.
\image html tagging_comment_icon.png
You can view comments associated with tags by going to the "Tags" section of the tree viewer and selecting one of your tags. Any comments will appear in the "Comment" column in the results viewer.
\image html tagging_comment_in_result_viewer.png
You can view all comments on an item through the "Annotation" tab in the content viewer.
\image html tagging_comment_anno.png
*/

View File

@ -0,0 +1 @@
.kdb files can be placed in this directory and they will be treated as Official Hash Sets within autopsy. Official Hash Sets will be readonly within the application. Official Hash Sets should have the following naming convention: '<dbName>.<knownStatus>.kdb' where '<dbName>' is the name of the Official Hash Set and '<knownStatus>' is the known status of the hash set. The known status is the identifier for one of the variable names represented in the enum: HashDbManager.HashDb.KnownFilesType. As an example, a possible value could be: 'Foo.Notable.kdb' where the name of the Official Hash Set is 'Foo' and the known status is 'Notable' which is the identifier for KnownFilesType.KNOWN_BAD.