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;
 
   62     "HashDbIngestModule.noKnownBadHashDbSetMsg=No notable hash set.",
 
   63     "HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Notable 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 (knownHashSets.isEmpty()) {
 
  153                         Bundle.HashDbIngestModule_noKnownHashDbSetMsg(),
 
  154                         Bundle.HashDbIngestModule_knownFileSearchWillNotExecuteWarn()));
 
  166         for (
HashDb db : allHashSets) {
 
  167             if (settings.isHashSetEnabled(db)) {
 
  170                         switch (db.getKnownFilesType()) {
 
  172                                 knownHashSets.add(db);
 
  175                                 knownBadHashSets.add(db);
 
  178                                 noChangeHashSets.add(db);
 
  181                                 throw new TskCoreException(
"Unknown KnownFilesType: " + db.getKnownFilesType());
 
  184                 } 
catch (TskCoreException ex) {
 
  185                     logger.log(Level.WARNING, 
"Error getting index status for " + db.getDisplayName() + 
" hash set", ex); 
 
  193         "HashDbIngestModule.dialogTitle.errorFindingArtifacts=Error Finding Artifacts: {0}",
 
  195         "HashDbIngestModule.errorMessage.lookingForFileArtifacts=Error encountered while looking for existing artifacts for {0}." 
  202             logger.log(Level.SEVERE, 
"Exception while getting open case.", ex); 
 
  206         if (shouldSkip(file)) {
 
  215             calculateHashes(file, totals);
 
  216         } 
catch (TskCoreException ex) {
 
  217             logger.log(Level.WARNING, String.format(
"Error calculating hash of file '%s' (id=%d).", file.getName(), file.getId()), ex); 
 
  220                     NbBundle.getMessage(this.getClass(), 
"HashDbIngestModule.fileReadErrorMsg", file.getName()),
 
  221                     NbBundle.getMessage(
this.getClass(), 
"HashDbIngestModule.calcHashValueErr",
 
  222                             file.getParentPath() + file.getName(),
 
  223                             file.isMetaFlagSet(TskData.TSK_FS_META_FLAG_ENUM.ALLOC) ? 
"Allocated File" : 
"Deleted File")));
 
  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 comment = generateComment(hashInfo);
 
  387                     if (!createArtifactIfNotExists(file, comment, db)) {
 
  391                 long delta = (System.currentTimeMillis() - lookupstart);
 
  392                 totalLookupTime.addAndGet(delta);
 
  394             } 
catch (TskException ex) {
 
  395                 reportLookupError(ex, file, lookupErrorMessage);
 
  412         ArrayList<String> comments = hashInfo.getComments();
 
  414         for (String c : comments) {
 
  419             if (comment.length() > MAX_COMMENT_SIZE) {
 
  420                 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(), db, comment);
 
  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);
 
  509         if (knownFilesType == null) {
 
  510             return Score.SCORE_UNKNOWN;
 
  512         switch (knownFilesType) {
 
  514                 return Score.SCORE_NONE;
 
  516                 return Score.SCORE_NOTABLE;
 
  519                 return Score.SCORE_UNKNOWN;
 
  531         "HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search." 
  537             List<BlackboardAttribute> attributes = Arrays.asList(
 
  538                 new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, moduleName, db.getDisplayName()),
 
  539                 new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, moduleName, md5Hash),
 
  540                 new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, comment)
 
  544             BlackboardArtifact badFile = abstractFile.newAnalysisResult(
 
  546                     null, db.getDisplayName(), null,
 
  548             ).getAnalysisResult();
 
  555                 blackboard.postArtifact(badFile, moduleName);
 
  556             } 
catch (Blackboard.BlackboardException ex) {
 
  557                 logger.log(Level.SEVERE, 
"Unable to index blackboard artifact " + badFile.getArtifactID(), ex); 
 
  559                         Bundle.HashDbIngestModule_indexError_message(), badFile.getDisplayName());
 
  563                 StringBuilder detailsSb = 
new StringBuilder();
 
  565                 detailsSb.append(
"<table border='0' cellpadding='4' width='280'>"); 
 
  567                 detailsSb.append(
"<tr>"); 
 
  568                 detailsSb.append(
"<th>") 
 
  569                         .append(NbBundle.getMessage(
this.getClass(), 
"HashDbIngestModule.postToBB.fileName"))
 
  571                 detailsSb.append(
"<td>") 
 
  572                         .append(abstractFile.getName())
 
  574                 detailsSb.append(
"</tr>"); 
 
  576                 detailsSb.append(
"<tr>"); 
 
  577                 detailsSb.append(
"<th>") 
 
  578                         .append(NbBundle.getMessage(
this.getClass(), 
"HashDbIngestModule.postToBB.md5Hash"))
 
  580                 detailsSb.append(
"<td>").append(md5Hash).append(
"</td>"); 
 
  581                 detailsSb.append(
"</tr>"); 
 
  583                 detailsSb.append(
"<tr>"); 
 
  584                 detailsSb.append(
"<th>") 
 
  585                         .append(NbBundle.getMessage(
this.getClass(), 
"HashDbIngestModule.postToBB.hashsetName"))
 
  587                 detailsSb.append(
"<td>").append(db.getDisplayName()).append(
"</td>"); 
 
  588                 detailsSb.append(
"</tr>"); 
 
  590                 detailsSb.append(
"</table>"); 
 
  593                         NbBundle.getMessage(this.getClass(), 
"HashDbIngestModule.postToBB.knownBadMsg", abstractFile.getName()),
 
  594                         detailsSb.toString(),
 
  595                         abstractFile.getName() + md5Hash,
 
  598         } 
catch (TskException ex) {
 
  599             logger.log(Level.WARNING, 
"Error creating blackboard artifact", ex); 
 
  611     @Messages(
"HashDbIngestModule.complete.noChangesFound=No Change items found:")
 
  612     private static synchronized 
void postSummary(
long jobId, List<
HashDb> knownBadHashSets,
 
  613             List<
HashDb> noChangeHashSets, List<
HashDb> knownHashSets) {
 
  616         totalsForIngestJobs.remove(jobId);
 
  618         if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty()) || (!noChangeHashSets.isEmpty())) {
 
  619             StringBuilder detailsSb = 
new StringBuilder();
 
  622                 "<table border='0' cellpadding='4' width='280'>" +
 
  623                     "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.knownBadsFound") + 
"</td>" +
 
  626                     "<tr><td>" + Bundle.HashDbIngestModule_complete_noChangesFound() + 
"</td>" +
 
  629                     "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.totalCalcTime") + 
 
  630                     "</td><td>" + jobTotals.
totalCalctime.get() + 
"</td></tr>\n" +
 
  632                     "<tr><td>" + NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.totalLookupTime") + 
 
  633                     "</td><td>" + jobTotals.
totalLookuptime.get() + 
"</td></tr>\n</table>" +
 
  635                     "<p>" + NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.databasesUsed") + 
"</p>\n<ul>"); 
 
  637             Stream.concat(knownBadHashSets.stream(), noChangeHashSets.stream()).forEach((db) -> {
 
  641             detailsSb.append(
"</ul>"); 
 
  646                     NbBundle.getMessage(
HashDbIngestModule.class, 
"HashDbIngestModule.complete.hashLookupResults"),
 
  647                     detailsSb.toString()));
 
  654             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()