19 package org.sleuthkit.autopsy.modules.hashdatabase;
21 import java.io.IOException;
22 import java.util.ArrayList;
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)) {
212 String md5Hash = getHash(file, totals);
213 if (md5Hash == null) {
222 totals.
totalLookuptime, knownBadHashSets, TskData.FileKnown.BAD, knownBadLookupError);
224 boolean foundBad = knownBadResult.isFound();
225 if (knownBadResult.isError()) {
231 totals.
totalLookuptime, noChangeHashSets, TskData.FileKnown.UNKNOWN, noChangeLookupError);
233 if (noChangeResult.isError()) {
241 for (
HashDb db : knownHashSets) {
243 long lookupstart = System.currentTimeMillis();
244 if (db.lookupMD5Quick(file)) {
245 file.setKnown(TskData.FileKnown.KNOWN);
248 long delta = (System.currentTimeMillis() - lookupstart);
251 }
catch (TskException ex) {
252 reportLookupError(ex, file, knownLookupError);
270 if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
271 || file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
286 if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
302 private void reportLookupError(TskException ex, AbstractFile file, Function<AbstractFile, String> lookupErrorMessage) {
303 logger.log(Level.WARNING, String.format(
304 "Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", file.getName(), file.getId()), ex);
307 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.hashLookupErrorMsg", file.getName()),
308 lookupErrorMessage.apply(file)));
364 List<HashDb> hashSets, TskData.FileKnown statusIfFound, Function<AbstractFile, String> lookupErrorMessage) {
366 boolean found =
false;
367 boolean wasError =
false;
368 for (
HashDb db : hashSets) {
370 long lookupstart = System.currentTimeMillis();
371 HashHitInfo hashInfo = db.lookupMD5(file);
372 if (null != hashInfo) {
375 totalCount.incrementAndGet();
376 file.setKnown(statusIfFound);
377 String hashSetName = db.getDisplayName();
378 String comment = generateComment(hashInfo);
379 if (!createArtifactIfNotExists(hashSetName, file, comment, db)) {
383 long delta = (System.currentTimeMillis() - lookupstart);
384 totalLookupTime.addAndGet(delta);
386 }
catch (TskException ex) {
387 reportLookupError(ex, file, lookupErrorMessage);
404 ArrayList<String> comments = hashInfo.getComments();
406 for (String c : comments) {
411 if (comment.length() > MAX_COMMENT_SIZE) {
412 comment = comment.substring(0, MAX_COMMENT_SIZE) +
"...";
434 List<BlackboardAttribute> attributesList =
new ArrayList<>();
437 Blackboard tskBlackboard = skCase.getBlackboard();
438 if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) ==
false) {
439 postHashSetHitToBlackboard(file, file.getMd5Hash(), hashSetName, comment, db.
getSendIngestMessages());
441 }
catch (TskCoreException ex) {
442 logger.log(Level.SEVERE, String.format(
443 "A problem occurred while checking for existing artifacts for file '%s' (id=%d).", file.getName(), file.getId()), ex);
446 Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(file.getName()),
447 Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(file.getName())));
464 String md5Hash = file.getMd5Hash();
465 if (md5Hash != null && md5Hash.isEmpty()) {
471 long calcstart = System.currentTimeMillis();
472 md5Hash = HashUtility.calculateMd5Hash(file);
473 if (file.getSize() > 0) {
477 if (file.getSize() < 1000000) {
484 file.setMd5Hash(md5Hash);
485 long delta = (System.currentTimeMillis() - calcstart);
488 }
catch (IOException ex) {
489 logger.log(Level.WARNING, String.format(
"Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex);
492 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.fileReadErrorMsg", file.getName()),
493 NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.calcHashValueErr",
494 file.getParentPath() + file.getName(),
495 file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ?
"Allocated File" :
"Deleted File")));
511 "HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."
513 private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment,
boolean showInboxMessage) {
516 BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
517 Collection<BlackboardAttribute> attributes =
new ArrayList<>();
520 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName));
521 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
522 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
524 badFile.addAttributes(attributes);
531 blackboard.postArtifact(badFile, moduleName);
532 }
catch (Blackboard.BlackboardException ex) {
533 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + badFile.getArtifactID(), ex);
535 Bundle.HashDbIngestModule_indexError_message(), badFile.getDisplayName());
538 if (showInboxMessage) {
539 StringBuilder detailsSb =
new StringBuilder();
541 detailsSb.append(
"<table border='0' cellpadding='4' width='280'>");
543 detailsSb.append(
"<tr>");
544 detailsSb.append(
"<th>")
545 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.fileName"))
547 detailsSb.append(
"<td>")
548 .append(abstractFile.getName())
550 detailsSb.append(
"</tr>");
552 detailsSb.append(
"<tr>");
553 detailsSb.append(
"<th>")
554 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.md5Hash"))
556 detailsSb.append(
"<td>").append(md5Hash).append(
"</td>");
557 detailsSb.append(
"</tr>");
559 detailsSb.append(
"<tr>");
560 detailsSb.append(
"<th>")
561 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.hashsetName"))
563 detailsSb.append(
"<td>").append(hashSetName).append(
"</td>");
564 detailsSb.append(
"</tr>");
566 detailsSb.append(
"</table>");
569 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
570 detailsSb.toString(),
571 abstractFile.getName() + md5Hash,
574 }
catch (TskException ex) {
575 logger.log(Level.WARNING,
"Error creating blackboard artifact", ex);
587 @Messages(
"HashDbIngestModule.complete.noChangesFound=No Change items found:")
588 private static synchronized
void postSummary(
long jobId, List<
HashDb> knownBadHashSets,
589 List<
HashDb> noChangeHashSets, List<
HashDb> knownHashSets) {
592 totalsForIngestJobs.remove(jobId);
594 if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty()) || (!noChangeHashSets.isEmpty())) {
595 StringBuilder detailsSb =
new StringBuilder();
598 "<table border='0' cellpadding='4' width='280'>" +
599 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.knownBadsFound") +
"</td>" +
602 "<tr><td>" + Bundle.HashDbIngestModule_complete_noChangesFound() +
"</td>" +
605 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.totalCalcTime") +
606 "</td><td>" + jobTotals.
totalCalctime.get() +
"</td></tr>\n" +
608 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.totalLookupTime") +
609 "</td><td>" + jobTotals.
totalLookuptime.get() +
"</td></tr>\n</table>" +
611 "<p>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.databasesUsed") +
"</p>\n<ul>");
613 Stream.concat(knownBadHashSets.stream(), noChangeHashSets.stream()).forEach((db) -> {
614 detailsSb.append(
"<li>" + db.getHashSetName() +
"</li>\n");
617 detailsSb.append(
"</ul>");
622 NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.hashLookupResults"),
623 detailsSb.toString()));
630 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
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()
String getHash(AbstractFile file, IngestJobTotals totals)
static synchronized IngestServices getInstance()