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;
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());
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);
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",
230 boolean foundBad = knownBadResult.isFound();
231 if (knownBadResult.isError()) {
239 if (noChangeResult.isError()) {
247 for (
HashDb db : knownHashSets) {
249 long lookupstart = System.currentTimeMillis();
250 if (db.lookupMD5Quick(file)) {
254 long delta = (System.currentTimeMillis() - lookupstart);
258 reportLookupError(ex, file, knownLookupError);
292 if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
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();
378 if (null != hashInfo) {
381 totalCount.incrementAndGet();
383 String comment = generateComment(hashInfo);
384 if (!createArtifactIfNotExists(file, comment, db)) {
388 long delta = (System.currentTimeMillis() - lookupstart);
389 totalLookupTime.addAndGet(delta);
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<>();
443 postHashSetHitToBlackboard(file, file.
getMd5Hash(), db, comment);
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())));
469 if ((md5Hash != null && ! md5Hash.isEmpty())
470 && (sha256Hash != null && ! sha256Hash.isEmpty())) {
475 long calcstart = System.currentTimeMillis();
482 if (file.
getSize() < 1000000) {
496 long delta = (System.currentTimeMillis() - calcstart);
497 totals.totalCalctime.addAndGet(delta);
506 if (knownFilesType == null) {
509 switch (knownFilesType) {
528 "HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."
534 List<BlackboardAttribute> attributes = Arrays.asList(
543 null, db.getDisplayName(), null,
545 ).getAnalysisResult();
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,
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()
static final Score SCORE_UNKNOWN
void postArtifact(BlackboardArtifact artifact, String moduleName)
static final Score SCORE_NOTABLE
synchronized long decrementAndGet(long jobId)
ProcessResult process(AbstractFile file)
static IngestMessage createDataMessage(String source, String subject, String detailsHtml, String uniqueKey, BlackboardArtifact data)
Blackboard getBlackboard()
final HashLookupModuleSettings settings
void setSha256Hash(String sha256Hash)
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
void initializeHashsets(List< HashDb > allHashSets)
TskData.TSK_DB_FILES_TYPE_ENUM getType()
final AtomicLong totalNoChangeCount
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList)
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)
static List< HashResult > calculateHashes(Content content, Collection< HashType > hashTypes)
final SleuthkitCase skCase
void setKnown(TskData.FileKnown knownState)
static synchronized HashDbManager getInstance()
boolean artifactExists(Content content, BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributes)
boolean isMetaFlagSet(TSK_FS_META_FLAG_ENUM metaFlag)
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)
ArrayList< String > getComments()
void reportLookupError(TskException ex, AbstractFile file, Function< AbstractFile, String > lookupErrorMessage)
SleuthkitCase getSleuthkitCase()
synchronized List< HashDb > getAllHashSets()
static void submitTimingMetric(TimingMetric metric)
static final Type TSK_HASHSET_HIT
static void error(String title, String message)
synchronized static Logger getLogger(String name)
void setMd5Hash(String md5Hash)
static Case getCurrentCaseThrows()
static IngestMessage createWarningMessage(String source, String subject, String detailsHtml)
static final Score SCORE_NONE
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()