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.noChangeHashDbSetMsg=No 'No Change' hash set.",
63 "HashDbIngestModule.noChangeFileSearchWillNotExecuteWarn='No Change' file search will not be executed.",
64 "HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.",
65 "HashDbIngestModule.knownFileSearchWillNotExecuteWarn=Known file search will not be executed.",
66 "# {0} - fileName",
"HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking up notable hash value for {0}.",
67 "# {0} - fileName",
"HashDbIngestModule.lookingUpNoChangeHashValueErr=Error encountered while looking up no change hash value for {0}.",
68 "# {0} - fileName",
"HashDbIngestModule.lookingUpKnownHashValueErr=Error encountered while looking up known hash value for {0}.",})
73 private final Function<AbstractFile, String> knownBadLookupError
74 = (file) -> Bundle.HashDbIngestModule_lookingUpKnownBadHashValueErr(file.getName());
76 private final Function<AbstractFile, String> noChangeLookupError
77 = (file) -> Bundle.HashDbIngestModule_lookingUpNoChangeHashValueErr(file.getName());
79 private final Function<AbstractFile, String> knownLookupError
80 = (file) -> Bundle.HashDbIngestModule_lookingUpKnownHashValueErr(file.getName());
82 private static final int MAX_COMMENT_SIZE = 500;
86 private final HashLookupModuleSettings
settings;
87 private final List<HashDb> knownBadHashSets =
new ArrayList<>();
88 private final List<HashDb> knownHashSets =
new ArrayList<>();
89 private final List<HashDb> noChangeHashSets =
new ArrayList<>();
91 private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs =
new HashMap<>();
100 private final AtomicLong totalKnownBadCount =
new AtomicLong(0);
101 private final AtomicLong totalNoChangeCount =
new AtomicLong(0);
102 private final AtomicLong totalCalctime =
new AtomicLong(0);
103 private final AtomicLong totalLookuptime =
new AtomicLong(0);
108 if (totals == null) {
110 totalsForIngestJobs.put(ingestJobId, totals);
125 this.settings = settings;
131 jobId = context.getJobId();
132 if (!hashDbManager.verifyAllDatabasesLoadedCorrectly()) {
133 throw new IngestModuleException(
"Could not load all hash sets");
140 getTotalsForIngestJobs(jobId);
143 if (knownBadHashSets.isEmpty()) {
146 Bundle.HashDbIngestModule_noKnownBadHashDbSetMsg(),
147 Bundle.HashDbIngestModule_knownBadFileSearchWillNotExecuteWarn()));
150 if (noChangeHashSets.isEmpty()) {
153 Bundle.HashDbIngestModule_noChangeHashDbSetMsg(),
154 Bundle.HashDbIngestModule_noChangeFileSearchWillNotExecuteWarn()));
157 if (knownHashSets.isEmpty()) {
160 Bundle.HashDbIngestModule_noKnownHashDbSetMsg(),
161 Bundle.HashDbIngestModule_knownFileSearchWillNotExecuteWarn()));
173 for (
HashDb db : allHashSets) {
174 if (settings.isHashSetEnabled(db)) {
177 switch (db.getKnownFilesType()) {
179 knownHashSets.add(db);
182 knownBadHashSets.add(db);
185 noChangeHashSets.add(db);
188 throw new TskCoreException(
"Unknown KnownFilesType: " + db.getKnownFilesType());
191 }
catch (TskCoreException ex) {
192 logger.log(Level.WARNING,
"Error getting index status for " + db.getDisplayName() +
" hash set", ex);
200 "HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}",
202 "HashDbIngestModule.errorMessage.lookingForFileArtifacts=Error encountered while looking for existing artifacts for {0}."
209 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
213 if (shouldSkip(file)) {
221 String md5Hash = getHash(file, totals);
222 if (md5Hash == null) {
231 totals.
totalLookuptime, knownBadHashSets, TskData.FileKnown.BAD, knownBadLookupError);
233 boolean foundBad = knownBadResult.isFound();
234 if (knownBadResult.isError()) {
240 totals.
totalLookuptime, noChangeHashSets, TskData.FileKnown.UNKNOWN, noChangeLookupError);
242 if (noChangeResult.isError()) {
250 for (
HashDb db : knownHashSets) {
252 long lookupstart = System.currentTimeMillis();
253 if (db.lookupMD5Quick(file)) {
254 file.setKnown(TskData.FileKnown.KNOWN);
257 long delta = (System.currentTimeMillis() - lookupstart);
260 }
catch (TskException ex) {
261 reportLookupError(ex, file, knownLookupError);
279 if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
280 || file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
295 if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
311 private void reportLookupError(TskException ex, AbstractFile file, Function<AbstractFile, String> lookupErrorMessage) {
312 logger.log(Level.WARNING, String.format(
313 "Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", file.getName(), file.getId()), ex);
316 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.hashLookupErrorMsg", file.getName()),
317 lookupErrorMessage.apply(file)));
373 List<HashDb> hashSets, TskData.FileKnown statusIfFound, Function<AbstractFile, String> lookupErrorMessage) {
375 boolean found =
false;
376 boolean wasError =
false;
377 for (
HashDb db : hashSets) {
379 long lookupstart = System.currentTimeMillis();
380 HashHitInfo hashInfo = db.lookupMD5(file);
381 if (null != hashInfo) {
384 totalCount.incrementAndGet();
385 file.setKnown(statusIfFound);
386 String hashSetName = db.getDisplayName();
387 String comment = generateComment(hashInfo);
388 if (!createArtifactIfNotExists(hashSetName, file, comment, db)) {
392 long delta = (System.currentTimeMillis() - lookupstart);
393 totalLookupTime.addAndGet(delta);
395 }
catch (TskException ex) {
396 reportLookupError(ex, file, lookupErrorMessage);
413 ArrayList<String> comments = hashInfo.getComments();
415 for (String c : comments) {
420 if (comment.length() > MAX_COMMENT_SIZE) {
421 comment = comment.substring(0, MAX_COMMENT_SIZE) +
"...";
443 List<BlackboardAttribute> attributesList =
new ArrayList<>();
446 Blackboard tskBlackboard = skCase.getBlackboard();
447 if (tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT, attributesList) ==
false) {
448 postHashSetHitToBlackboard(file, file.getMd5Hash(), hashSetName, comment, db.
getSendIngestMessages());
450 }
catch (TskCoreException ex) {
451 logger.log(Level.SEVERE, String.format(
452 "A problem occurred while checking for existing artifacts for file '%s' (id=%d).", file.getName(), file.getId()), ex);
455 Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(file.getName()),
456 Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(file.getName())));
473 String md5Hash = file.getMd5Hash();
474 if (md5Hash != null && md5Hash.isEmpty()) {
480 long calcstart = System.currentTimeMillis();
481 md5Hash = HashUtility.calculateMd5Hash(file);
482 if (file.getSize() > 0) {
486 if (file.getSize() < 1000000) {
493 file.setMd5Hash(md5Hash);
494 long delta = (System.currentTimeMillis() - calcstart);
497 }
catch (IOException ex) {
498 logger.log(Level.WARNING, String.format(
"Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex);
501 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.fileReadErrorMsg", file.getName()),
502 NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.calcHashValueErr",
503 file.getParentPath() + file.getName(),
504 file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ?
"Allocated File" :
"Deleted File")));
520 "HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."
522 private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment,
boolean showInboxMessage) {
525 BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
526 Collection<BlackboardAttribute> attributes =
new ArrayList<>();
529 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, hashSetName));
530 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash));
531 attributes.add(
new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment));
533 badFile.addAttributes(attributes);
540 blackboard.postArtifact(badFile, moduleName);
541 }
catch (Blackboard.BlackboardException ex) {
542 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + badFile.getArtifactID(), ex);
544 Bundle.HashDbIngestModule_indexError_message(), badFile.getDisplayName());
547 if (showInboxMessage) {
548 StringBuilder detailsSb =
new StringBuilder();
550 detailsSb.append(
"<table border='0' cellpadding='4' width='280'>");
552 detailsSb.append(
"<tr>");
553 detailsSb.append(
"<th>")
554 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.fileName"))
556 detailsSb.append(
"<td>")
557 .append(abstractFile.getName())
559 detailsSb.append(
"</tr>");
561 detailsSb.append(
"<tr>");
562 detailsSb.append(
"<th>")
563 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.md5Hash"))
565 detailsSb.append(
"<td>").append(md5Hash).append(
"</td>");
566 detailsSb.append(
"</tr>");
568 detailsSb.append(
"<tr>");
569 detailsSb.append(
"<th>")
570 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.hashsetName"))
572 detailsSb.append(
"<td>").append(hashSetName).append(
"</td>");
573 detailsSb.append(
"</tr>");
575 detailsSb.append(
"</table>");
578 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
579 detailsSb.toString(),
580 abstractFile.getName() + md5Hash,
583 }
catch (TskException ex) {
584 logger.log(Level.WARNING,
"Error creating blackboard artifact", ex);
596 @Messages(
"HashDbIngestModule.complete.noChangesFound=No Change items found:")
597 private static synchronized
void postSummary(
long jobId, List<
HashDb> knownBadHashSets,
598 List<
HashDb> noChangeHashSets, List<
HashDb> knownHashSets) {
601 totalsForIngestJobs.remove(jobId);
603 if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty()) || (!noChangeHashSets.isEmpty())) {
604 StringBuilder detailsSb =
new StringBuilder();
607 "<table border='0' cellpadding='4' width='280'>" +
608 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.knownBadsFound") +
"</td>" +
611 "<tr><td>" + Bundle.HashDbIngestModule_complete_noChangesFound() +
"</td>" +
614 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.totalCalcTime") +
615 "</td><td>" + jobTotals.
totalCalctime.get() +
"</td></tr>\n" +
617 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.totalLookupTime") +
618 "</td><td>" + jobTotals.
totalLookuptime.get() +
"</td></tr>\n</table>" +
620 "<p>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.databasesUsed") +
"</p>\n<ul>");
622 Stream.concat(knownBadHashSets.stream(), noChangeHashSets.stream()).forEach((db) -> {
623 detailsSb.append(
"<li>" + db.getHashSetName() +
"</li>\n");
626 detailsSb.append(
"</ul>");
631 NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.hashLookupResults"),
632 detailsSb.toString()));
639 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()