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()