19 package org.sleuthkit.autopsy.modules.hashdatabase;
 
   21 import java.util.ArrayList;
 
   22 import java.util.Arrays;
 
   23 import java.util.Collection;
 
   24 import java.util.HashMap;
 
   25 import java.util.List;
 
   26 import java.util.concurrent.atomic.AtomicLong;
 
   27 import java.util.function.Function;
 
   28 import java.util.logging.Level;
 
   29 import java.util.stream.Stream;
 
   30 import org.openide.util.NbBundle;
 
   31 import org.openide.util.NbBundle.Messages;
 
   46 import org.
sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
 
   48 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
 
   60     "HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
 
   61     "HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.",
 
   62     "HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.",
 
   63     "HashDbIngestModule.knownFileSearchWillNotExecuteWarn=Known file search will not be executed.",
 
   64     "# {0} - fileName", 
"HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking up notable hash value for {0}.",
 
   65     "# {0} - fileName", 
"HashDbIngestModule.lookingUpNoChangeHashValueErr=Error encountered while looking up no change hash value for {0}.",
 
   66     "# {0} - fileName", 
"HashDbIngestModule.lookingUpKnownHashValueErr=Error encountered while looking up known hash value for {0}.",})
 
   71     private final Function<AbstractFile, String> knownBadLookupError
 
   72             = (file) -> Bundle.HashDbIngestModule_lookingUpKnownBadHashValueErr(file.getName());
 
   74     private final Function<AbstractFile, String> noChangeLookupError
 
   75             = (file) -> Bundle.HashDbIngestModule_lookingUpNoChangeHashValueErr(file.getName());
 
   77     private final Function<AbstractFile, String> knownLookupError
 
   78             = (file) -> Bundle.HashDbIngestModule_lookingUpKnownHashValueErr(file.getName());
 
   80     private static final int MAX_COMMENT_SIZE = 500;
 
   84     private final HashLookupModuleSettings 
settings;
 
   85     private final List<HashDb> knownBadHashSets = 
new ArrayList<>();
 
   86     private final List<HashDb> knownHashSets = 
new ArrayList<>();
 
   87     private final List<HashDb> noChangeHashSets = 
new ArrayList<>();
 
   89     private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = 
new HashMap<>();
 
   98         private final AtomicLong totalKnownBadCount = 
new AtomicLong(0);
 
   99         private final AtomicLong totalNoChangeCount = 
new AtomicLong(0);
 
  100         private final AtomicLong totalCalctime = 
new AtomicLong(0);
 
  101         private final AtomicLong totalLookuptime = 
new AtomicLong(0);
 
  106         if (totals == null) {
 
  108             totalsForIngestJobs.put(ingestJobId, totals);
 
  123         this.settings = settings;
 
  129         jobId = context.getJobId();
 
  130         if (!hashDbManager.verifyAllDatabasesLoadedCorrectly()) {
 
  131             throw new IngestModuleException(
"Could not load all hash sets");
 
  138             getTotalsForIngestJobs(jobId);
 
  141             if (knownBadHashSets.isEmpty()) {
 
  144                         Bundle.HashDbIngestModule_noKnownBadHashDbSetMsg(),
 
  145                         Bundle.HashDbIngestModule_knownBadFileSearchWillNotExecuteWarn()));
 
  148             if (knownHashSets.isEmpty()) {
 
  151                         Bundle.HashDbIngestModule_noKnownHashDbSetMsg(),
 
  152                         Bundle.HashDbIngestModule_knownFileSearchWillNotExecuteWarn()));
 
  164         for (
HashDb db : allHashSets) {
 
  165             if (settings.isHashSetEnabled(db)) {
 
  168                         switch (db.getKnownFilesType()) {
 
  170                                 knownHashSets.add(db);
 
  173                                 knownBadHashSets.add(db);
 
  176                                 noChangeHashSets.add(db);
 
  179                                 throw new TskCoreException(
"Unknown KnownFilesType: " + db.getKnownFilesType());
 
  182                 } 
catch (TskCoreException ex) {
 
  183                     logger.log(Level.WARNING, 
"Error getting index status for " + db.getDisplayName() + 
" hash set", ex); 
 
  191         "HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}",
 
  193         "HashDbIngestModule.errorMessage.lookingForFileArtifacts=Error encountered while looking for existing artifacts for {0}." 
  200             logger.log(Level.SEVERE, 
"Exception while getting open case.", ex); 
 
  204         if (shouldSkip(file)) {
 
  213             calculateHashes(file, totals);
 
  214         } 
catch (TskCoreException ex) {
 
  215             logger.log(Level.WARNING, String.format(
"Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex); 
 
  218                     NbBundle.getMessage(this.getClass(), 
"HashDbIngestModule.fileReadErrorMsg", file.getName()),
 
  219                     NbBundle.getMessage(
this.getClass(), 
"HashDbIngestModule.calcHashValueErr",
 
  220                             file.getParentPath() + file.getName(),
 
  221                             file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ? 
"Allocated File" : 
"Deleted File")));
 
  229                 totals.
totalLookuptime, knownBadHashSets, TskData.FileKnown.BAD, knownBadLookupError);
 
  231         boolean foundBad = knownBadResult.isFound();
 
  232         if (knownBadResult.isError()) {
 
  238                 totals.
totalLookuptime, noChangeHashSets, TskData.FileKnown.UNKNOWN, noChangeLookupError);
 
  240         if (noChangeResult.isError()) {
 
  248             for (
HashDb db : knownHashSets) {
 
  250                     long lookupstart = System.currentTimeMillis();
 
  251                     if (db.lookupMD5Quick(file)) {
 
  252                         file.setKnown(TskData.FileKnown.KNOWN);
 
  255                     long delta = (System.currentTimeMillis() - lookupstart);
 
  258                 } 
catch (TskException ex) {
 
  259                     reportLookupError(ex, file, knownLookupError);
 
  277         if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
 
  278                 || file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
 
  293         if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
 
  309     private void reportLookupError(TskException ex, AbstractFile file, Function<AbstractFile, String> lookupErrorMessage) {
 
  310         logger.log(Level.WARNING, String.format(
 
  311                 "Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", file.getName(), file.getId()), ex); 
 
  314                 NbBundle.getMessage(this.getClass(), 
"HashDbIngestModule.hashLookupErrorMsg", file.getName()),
 
  315                 lookupErrorMessage.apply(file)));
 
  371             List<HashDb> hashSets, TskData.FileKnown statusIfFound, Function<AbstractFile, String> lookupErrorMessage) {
 
  373         boolean found = 
false;
 
  374         boolean wasError = 
false;
 
  375         for (
HashDb db : hashSets) {
 
  377                 long lookupstart = System.currentTimeMillis();
 
  378                 HashHitInfo hashInfo = db.lookupMD5(file);
 
  379                 if (null != hashInfo) {
 
  382                     totalCount.incrementAndGet();
 
  383                     file.setKnown(statusIfFound);
 
  384                     String hashSetName = db.getDisplayName();
 
  385                     String comment = generateComment(hashInfo);
 
  386                     if (!createArtifactIfNotExists(hashSetName, file, comment, db)) {
 
  390                 long delta = (System.currentTimeMillis() - lookupstart);
 
  391                 totalLookupTime.addAndGet(delta);
 
  393             } 
catch (TskException ex) {
 
  394                 reportLookupError(ex, file, lookupErrorMessage);
 
  411         ArrayList<String> comments = hashInfo.getComments();
 
  413         for (String c : comments) {
 
  418             if (comment.length() > MAX_COMMENT_SIZE) {
 
  419                 comment = comment.substring(0, MAX_COMMENT_SIZE) + 
"...";
 
  441         List<BlackboardAttribute> attributesList = 
new ArrayList<>();
 
  444             Blackboard tskBlackboard = skCase.getBlackboard();
 
  445             if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) == 
false) {
 
  446                 postHashSetHitToBlackboard(file, file.getMd5Hash(), hashSetName, comment, db.
getSendIngestMessages());
 
  448         } 
catch (TskCoreException ex) {
 
  449             logger.log(Level.SEVERE, String.format(
 
  450                     "A problem occurred while checking for existing artifacts for file '%s' (id=%d).", file.getName(), file.getId()), ex); 
 
  453                     Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(file.getName()),
 
  454                     Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(file.getName())));
 
  470         String md5Hash = file.getMd5Hash();
 
  471         String sha256Hash = file.getSha256Hash();
 
  472         if ((md5Hash != null && ! md5Hash.isEmpty())
 
  473                 && (sha256Hash != null && ! sha256Hash.isEmpty())) {
 
  478         long calcstart = System.currentTimeMillis();
 
  479         List<HashUtility.HashResult> newHashResults = 
 
  480                 HashUtility.calculateHashes(file, Arrays.asList(HashUtility.HashType.MD5,HashUtility.HashType.SHA256 ));
 
  481         if (file.getSize() > 0) {
 
  485             if (file.getSize() < 1000000) {
 
  492         for (HashUtility.HashResult hash : newHashResults) {
 
  493             if (hash.getType().equals(HashUtility.HashType.MD5)) {
 
  494                 file.setMd5Hash(hash.getValue());
 
  495             } 
else if (hash.getType().equals(HashUtility.HashType.SHA256)) {
 
  496                 file.setSha256Hash(hash.getValue());
 
  499         long delta = (System.currentTimeMillis() - calcstart);
 
  500         totals.totalCalctime.addAndGet(delta);
 
  514         "HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search." 
  516     private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, 
boolean showInboxMessage) {
 
  519             BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
 
  520             Collection<BlackboardAttribute> attributes = 
new ArrayList<>();
 
  523             attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName));
 
  524             attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
 
  525             attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
 
  527             badFile.addAttributes(attributes);
 
  534                 blackboard.postArtifact(badFile, moduleName);
 
  535             } 
catch (Blackboard.BlackboardException ex) {
 
  536                 logger.log(Level.SEVERE, 
"Unable to index blackboard artifact " + badFile.getArtifactID(), ex); 
 
  538                         Bundle.HashDbIngestModule_indexError_message(), badFile.getDisplayName());
 
  541             if (showInboxMessage) {
 
  542                 StringBuilder detailsSb = 
new StringBuilder();
 
  544                 detailsSb.append(
"<table border='0' cellpadding='4' width='280'>"); 
 
  546                 detailsSb.append(
"<tr>"); 
 
  547                 detailsSb.append(
"<th>") 
 
  548                         .append(NbBundle.getMessage(
this.getClass(), 
"HashDbIngestModule.postToBB.fileName"))
 
  550                 detailsSb.append(
"<td>") 
 
  551                         .append(abstractFile.getName())
 
  553                 detailsSb.append(
"</tr>"); 
 
  555                 detailsSb.append(
"<tr>"); 
 
  556                 detailsSb.append(
"<th>") 
 
  557                         .append(NbBundle.getMessage(
this.getClass(), 
"HashDbIngestModule.postToBB.md5Hash"))
 
  559                 detailsSb.append(
"<td>").append(md5Hash).append(
"</td>"); 
 
  560                 detailsSb.append(
"</tr>"); 
 
  562                 detailsSb.append(
"<tr>"); 
 
  563                 detailsSb.append(
"<th>") 
 
  564                         .append(NbBundle.getMessage(
this.getClass(), 
"HashDbIngestModule.postToBB.hashsetName"))
 
  566                 detailsSb.append(
"<td>").append(hashSetName).append(
"</td>"); 
 
  567                 detailsSb.append(
"</tr>"); 
 
  569                 detailsSb.append(
"</table>"); 
 
  572                         NbBundle.getMessage(this.getClass(), 
"HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
 
  573                         detailsSb.toString(),
 
  574                         abstractFile.getName() + md5Hash,
 
  577         } 
catch (TskException ex) {
 
  578             logger.log(Level.WARNING, 
"Error creating blackboard artifact", ex); 
 
  590     @Messages(
"HashDbIngestModule.complete.noChangesFound=No Change items found:")
 
  591     private static synchronized 
void postSummary(
long jobId, List<
HashDb> knownBadHashSets,
 
  592             List<
HashDb> noChangeHashSets, List<
HashDb> knownHashSets) {
 
  595         totalsForIngestJobs.remove(jobId);
 
  597         if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty()) || (!noChangeHashSets.isEmpty())) {
 
  598             StringBuilder detailsSb = 
new StringBuilder();
 
  601                 "<table border='0' cellpadding='4' width='280'>" +
 
  602                     "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.knownBadsFound") + 
"</td>" +
 
  605                     "<tr><td>" + Bundle.HashDbIngestModule_complete_noChangesFound() + 
"</td>" +
 
  608                     "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.totalCalcTime") + 
 
  609                     "</td><td>" + jobTotals.
totalCalctime.get() + 
"</td></tr>\n" +
 
  611                     "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.totalLookupTime") + 
 
  612                     "</td><td>" + jobTotals.
totalLookuptime.get() + 
"</td></tr>\n</table>" +
 
  614                     "<p>" + NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.databasesUsed") + 
"</p>\n<ul>"); 
 
  616             Stream.concat(knownBadHashSets.stream(), noChangeHashSets.stream()).forEach((db) -> {
 
  617                 detailsSb.append(
"<li>" + db.getHashSetName() + 
"</li>\n"); 
 
  620             detailsSb.append(
"</ul>"); 
 
  625                     NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.hashLookupResults"),
 
  626                     detailsSb.toString()));
 
  633             postSummary(jobId, knownBadHashSets, noChangeHashSets, knownHashSets);
 
abstract boolean getSendIngestMessages()
 
synchronized long decrementAndGet(long jobId)
 
ProcessResult process(AbstractFile file)
 
static IngestMessage createDataMessage(String source, String subject, String detailsHtml, String uniqueKey, BlackboardArtifact data)
 
final HashLookupModuleSettings settings
 
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
 
void initializeHashsets(List< HashDb > allHashSets)
 
final AtomicLong totalNoChangeCount
 
void startUp(org.sleuthkit.autopsy.ingest.IngestJobContext context)
 
synchronized long incrementAndGet(long jobId)
 
static IngestMessage createMessage(MessageType messageType, String source, String subject, String detailsHtml)
 
static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId)
 
final AtomicLong totalLookuptime
 
void calculateHashes(AbstractFile file, IngestJobTotals totals)
 
final AtomicLong totalCalctime
 
static TimingMetric getTimingMetric(String name)
 
void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage)
 
final SleuthkitCase skCase
 
static synchronized HashDbManager getInstance()
 
FindInHashsetsResult findInHashsets(AbstractFile file, AtomicLong totalCount, AtomicLong totalLookupTime, List< HashDb > hashSets, TskData.FileKnown statusIfFound, Function< AbstractFile, String > lookupErrorMessage)
 
final AtomicLong totalKnownBadCount
 
void postMessage(final IngestMessage message)
 
void reportLookupError(TskException ex, AbstractFile file, Function< AbstractFile, String > lookupErrorMessage)
 
SleuthkitCase getSleuthkitCase()
 
synchronized List< HashDb > getAllHashSets()
 
static void submitTimingMetric(TimingMetric metric)
 
static void error(String title, String message)
 
synchronized static Logger getLogger(String name)
 
static Case getCurrentCaseThrows()
 
static IngestMessage createWarningMessage(String source, String subject, String detailsHtml)
 
boolean shouldSkip(AbstractFile file)
 
boolean createArtifactIfNotExists(String hashSetName, AbstractFile file, String comment, HashDb db)
 
static void submitNormalizedTimingMetric(TimingMetric metric, long normalization)
 
String generateComment(HashHitInfo hashInfo)
 
static String getModuleName()
 
static synchronized IngestServices getInstance()