19 package org.sleuthkit.autopsy.modules.hashdatabase;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.concurrent.atomic.AtomicLong;
26 import java.util.function.Function;
27 import java.util.logging.Level;
28 import java.util.stream.Stream;
29 import org.openide.util.NbBundle;
30 import org.openide.util.NbBundle.Messages;
46 import org.
sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
59 "HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
60 "HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable file search will not be executed.",
61 "HashDbIngestModule.noKnownHashDbSetMsg=No known hash set.",
62 "HashDbIngestModule.knownFileSearchWillNotExecuteWarn=Known file search will not be executed.",
63 "# {0} - fileName",
"HashDbIngestModule.lookingUpKnownBadHashValueErr=Error encountered while looking up notable hash value for {0}.",
64 "# {0} - fileName",
"HashDbIngestModule.lookingUpNoChangeHashValueErr=Error encountered while looking up no change hash value for {0}.",
65 "# {0} - fileName",
"HashDbIngestModule.lookingUpKnownHashValueErr=Error encountered while looking up known hash value for {0}.",})
70 private final Function<AbstractFile, String> knownBadLookupError
71 = (file) -> Bundle.HashDbIngestModule_lookingUpKnownBadHashValueErr(file.getName());
73 private final Function<AbstractFile, String> noChangeLookupError
74 = (file) -> Bundle.HashDbIngestModule_lookingUpNoChangeHashValueErr(file.getName());
76 private final Function<AbstractFile, String> knownLookupError
77 = (file) -> Bundle.HashDbIngestModule_lookingUpKnownHashValueErr(file.getName());
79 private static final int MAX_COMMENT_SIZE = 500;
83 private final HashLookupModuleSettings
settings;
84 private final List<HashDb> knownBadHashSets =
new ArrayList<>();
85 private final List<HashDb> knownHashSets =
new ArrayList<>();
86 private final List<HashDb> noChangeHashSets =
new ArrayList<>();
88 private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs =
new HashMap<>();
97 private final AtomicLong totalKnownBadCount =
new AtomicLong(0);
98 private final AtomicLong totalNoChangeCount =
new AtomicLong(0);
99 private final AtomicLong totalCalctime =
new AtomicLong(0);
100 private final AtomicLong totalLookuptime =
new AtomicLong(0);
105 if (totals == null) {
107 totalsForIngestJobs.put(ingestJobId, totals);
122 this.settings = settings;
128 jobId = context.getJobId();
129 if (!hashDbManager.verifyAllDatabasesLoadedCorrectly()) {
130 throw new IngestModuleException(
"Could not load all hash sets");
137 getTotalsForIngestJobs(jobId);
140 if (knownBadHashSets.isEmpty()) {
143 Bundle.HashDbIngestModule_noKnownBadHashDbSetMsg(),
144 Bundle.HashDbIngestModule_knownBadFileSearchWillNotExecuteWarn()));
147 if (knownHashSets.isEmpty()) {
150 Bundle.HashDbIngestModule_noKnownHashDbSetMsg(),
151 Bundle.HashDbIngestModule_knownFileSearchWillNotExecuteWarn()));
163 for (
HashDb db : allHashSets) {
164 if (settings.isHashSetEnabled(db)) {
167 switch (db.getKnownFilesType()) {
169 knownHashSets.add(db);
172 knownBadHashSets.add(db);
175 noChangeHashSets.add(db);
178 throw new TskCoreException(
"Unknown KnownFilesType: " + db.getKnownFilesType());
181 }
catch (TskCoreException ex) {
182 logger.log(Level.WARNING,
"Error getting index status for " + db.getDisplayName() +
" hash set", ex);
190 "HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}",
192 "HashDbIngestModule.errorMessage.lookingForFileArtifacts=Error encountered while looking for existing artifacts for {0}."
199 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
203 if (shouldSkip(file)) {
212 calculateHashes(file, totals);
213 }
catch (TskCoreException ex) {
214 logger.log(Level.WARNING, String.format(
"Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex);
217 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.fileReadErrorMsg", file.getName()),
218 NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.calcHashValueErr",
219 file.getParentPath() + file.getName(),
220 file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ?
"Allocated File" :
"Deleted File")));
228 totals.
totalLookuptime, knownBadHashSets, TskData.FileKnown.BAD, knownBadLookupError);
230 boolean foundBad = knownBadResult.isFound();
231 if (knownBadResult.isError()) {
237 totals.
totalLookuptime, noChangeHashSets, TskData.FileKnown.UNKNOWN, noChangeLookupError);
239 if (noChangeResult.isError()) {
247 for (
HashDb db : knownHashSets) {
249 long lookupstart = System.currentTimeMillis();
250 if (db.lookupMD5Quick(file)) {
251 file.setKnown(TskData.FileKnown.KNOWN);
254 long delta = (System.currentTimeMillis() - lookupstart);
257 }
catch (TskException ex) {
258 reportLookupError(ex, file, knownLookupError);
276 if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
277 || file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK))) {
292 if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
308 private void reportLookupError(TskException ex, AbstractFile file, Function<AbstractFile, String> lookupErrorMessage) {
309 logger.log(Level.WARNING, String.format(
310 "Couldn't lookup notable hash for file '%s' (id=%d) - see sleuthkit log for details", file.getName(), file.getId()), ex);
313 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.hashLookupErrorMsg", file.getName()),
314 lookupErrorMessage.apply(file)));
370 List<HashDb> hashSets, TskData.FileKnown statusIfFound, Function<AbstractFile, String> lookupErrorMessage) {
372 boolean found =
false;
373 boolean wasError =
false;
374 for (
HashDb db : hashSets) {
376 long lookupstart = System.currentTimeMillis();
377 HashHitInfo hashInfo = db.lookupMD5(file);
378 if (null != hashInfo) {
381 totalCount.incrementAndGet();
382 file.setKnown(statusIfFound);
383 String comment = generateComment(hashInfo);
384 if (!createArtifactIfNotExists(file, comment, db)) {
388 long delta = (System.currentTimeMillis() - lookupstart);
389 totalLookupTime.addAndGet(delta);
391 }
catch (TskException ex) {
392 reportLookupError(ex, file, lookupErrorMessage);
409 ArrayList<String> comments = hashInfo.getComments();
411 for (String c : comments) {
416 if (comment.length() > MAX_COMMENT_SIZE) {
417 comment = comment.substring(0, MAX_COMMENT_SIZE) +
"...";
438 List<BlackboardAttribute> attributesList =
new ArrayList<>();
441 Blackboard tskBlackboard = skCase.getBlackboard();
442 if (tskBlackboard.artifactExists(file, BlackboardArtifact.Type.TSK_HASHSET_HIT, attributesList) ==
false) {
443 postHashSetHitToBlackboard(file, file.getMd5Hash(), db, comment);
445 }
catch (TskCoreException ex) {
446 logger.log(Level.SEVERE, String.format(
447 "A problem occurred while checking for existing artifacts for file '%s' (id=%d).", file.getName(), file.getId()), ex);
450 Bundle.HashDbIngestModule_dialogTitle_errorFindingArtifacts(file.getName()),
451 Bundle.HashDbIngestModule_errorMessage_lookingForFileArtifacts(file.getName())));
467 String md5Hash = file.getMd5Hash();
468 String sha256Hash = file.getSha256Hash();
469 if ((md5Hash != null && ! md5Hash.isEmpty())
470 && (sha256Hash != null && ! sha256Hash.isEmpty())) {
475 long calcstart = System.currentTimeMillis();
476 List<HashUtility.HashResult> newHashResults =
477 HashUtility.calculateHashes(file, Arrays.asList(HashUtility.HashType.MD5,HashUtility.HashType.SHA256 ));
478 if (file.getSize() > 0) {
482 if (file.getSize() < 1000000) {
489 for (HashUtility.HashResult hash : newHashResults) {
490 if (hash.getType().equals(HashUtility.HashType.MD5)) {
491 file.setMd5Hash(hash.getValue());
492 }
else if (hash.getType().equals(HashUtility.HashType.SHA256)) {
493 file.setSha256Hash(hash.getValue());
496 long delta = (System.currentTimeMillis() - calcstart);
497 totals.totalCalctime.addAndGet(delta);
506 if (knownFilesType == null) {
507 return Score.SCORE_UNKNOWN;
509 switch (knownFilesType) {
511 return Score.SCORE_NONE;
513 return Score.SCORE_NOTABLE;
516 return Score.SCORE_UNKNOWN;
528 "HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."
534 List<BlackboardAttribute> attributes = Arrays.asList(
535 new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, db.getDisplayName()),
536 new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash),
537 new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment)
541 BlackboardArtifact badFile = abstractFile.newAnalysisResult(
543 null, db.getDisplayName(), null,
545 ).getAnalysisResult();
552 blackboard.postArtifact(badFile, moduleName, jobId);
553 }
catch (Blackboard.BlackboardException ex) {
554 logger.log(Level.SEVERE,
"Unable to index blackboard artifact " + badFile.getArtifactID(), ex);
556 Bundle.HashDbIngestModule_indexError_message(), badFile.getDisplayName());
560 StringBuilder detailsSb =
new StringBuilder();
562 detailsSb.append(
"<table border='0' cellpadding='4' width='280'>");
564 detailsSb.append(
"<tr>");
565 detailsSb.append(
"<th>")
566 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.fileName"))
568 detailsSb.append(
"<td>")
569 .append(abstractFile.getName())
571 detailsSb.append(
"</tr>");
573 detailsSb.append(
"<tr>");
574 detailsSb.append(
"<th>")
575 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.md5Hash"))
577 detailsSb.append(
"<td>").append(md5Hash).append(
"</td>");
578 detailsSb.append(
"</tr>");
580 detailsSb.append(
"<tr>");
581 detailsSb.append(
"<th>")
582 .append(NbBundle.getMessage(
this.getClass(),
"HashDbIngestModule.postToBB.hashsetName"))
584 detailsSb.append(
"<td>").append(db.getDisplayName()).append(
"</td>");
585 detailsSb.append(
"</tr>");
587 detailsSb.append(
"</table>");
590 NbBundle.getMessage(this.getClass(),
"HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
591 detailsSb.toString(),
592 abstractFile.getName() + md5Hash,
595 }
catch (TskException ex) {
596 logger.log(Level.WARNING,
"Error creating blackboard artifact", ex);
608 @Messages(
"HashDbIngestModule.complete.noChangesFound=No Change items found:")
609 private static synchronized
void postSummary(
long jobId, List<
HashDb> knownBadHashSets,
610 List<
HashDb> noChangeHashSets, List<
HashDb> knownHashSets) {
613 totalsForIngestJobs.remove(jobId);
615 if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty()) || (!noChangeHashSets.isEmpty())) {
616 StringBuilder detailsSb =
new StringBuilder();
619 "<table border='0' cellpadding='4' width='280'>" +
620 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.knownBadsFound") +
"</td>" +
623 "<tr><td>" + Bundle.HashDbIngestModule_complete_noChangesFound() +
"</td>" +
626 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.totalCalcTime") +
627 "</td><td>" + jobTotals.
totalCalctime.get() +
"</td></tr>\n" +
629 "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.totalLookupTime") +
630 "</td><td>" + jobTotals.
totalLookuptime.get() +
"</td></tr>\n</table>" +
632 "<p>" + NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.databasesUsed") +
"</p>\n<ul>");
634 Stream.concat(knownBadHashSets.stream(), noChangeHashSets.stream()).forEach((db) -> {
638 detailsSb.append(
"</ul>");
643 NbBundle.getMessage(
HashDbIngestModule.class,
"HashDbIngestModule.complete.hashLookupResults"),
644 detailsSb.toString()));
651 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
abstract String getHashSetName()
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)
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
abstract HashDb.KnownFilesType getKnownFilesType()
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 createArtifactIfNotExists(AbstractFile file, String comment, HashDb db)
boolean shouldSkip(AbstractFile file)
void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, HashDb db, String comment)
static void submitNormalizedTimingMetric(TimingMetric metric, long normalization)
String generateComment(HashHitInfo hashInfo)
static String getModuleName()
Score getScore(HashDb.KnownFilesType knownFilesType)
static synchronized IngestServices getInstance()