Autopsy  4.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
HashDbIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011 - 2013 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.modules.hashdatabase;
20 
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.concurrent.atomic.AtomicLong;
27 import java.util.logging.Level;
28 import org.openide.util.NbBundle;
29 import org.openide.util.NbBundle.Messages;
40 import org.sleuthkit.datamodel.AbstractFile;
41 import org.sleuthkit.datamodel.BlackboardArtifact;
42 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
43 import org.sleuthkit.datamodel.BlackboardAttribute;
44 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
45 import org.sleuthkit.datamodel.HashHitInfo;
46 import org.sleuthkit.datamodel.HashUtility;
47 import org.sleuthkit.datamodel.SleuthkitCase;
48 import org.sleuthkit.datamodel.TskCoreException;
49 import org.sleuthkit.datamodel.TskData;
50 import org.sleuthkit.datamodel.TskException;
51 
52 @NbBundle.Messages({
53  "HashDbIngestModule.noKnownBadHashDbSetMsg=No known bad hash database set.",
54  "HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn=Known bad file search will not be executed.",
55  "HashDbIngestModule.noKnownHashDbSetMsg=No known hash database set.",
56  "HashDbIngestModule.knownFileSearchWillNotExecuteWarn=Known file search will not be executed."
57 })
58 public class HashDbIngestModule implements FileIngestModule {
59 
60  private static final Logger logger = Logger.getLogger(HashDbIngestModule.class.getName());
61  private static final int MAX_COMMENT_SIZE = 500;
62  private final IngestServices services = IngestServices.getInstance();
63  private final SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
64  private final HashDbManager hashDbManager = HashDbManager.getInstance();
65  private final HashLookupModuleSettings settings;
66  private List<HashDb> knownBadHashSets = new ArrayList<>();
67  private List<HashDb> knownHashSets = new ArrayList<>();
68  private long jobId;
69  private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
70  private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
72 
73  private static class IngestJobTotals {
74 
75  private AtomicLong totalKnownBadCount = new AtomicLong(0);
76  private AtomicLong totalCalctime = new AtomicLong(0);
77  private AtomicLong totalLookuptime = new AtomicLong(0);
78  }
79 
80  private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
81  IngestJobTotals totals = totalsForIngestJobs.get(ingestJobId);
82  if (totals == null) {
83  totals = new HashDbIngestModule.IngestJobTotals();
84  totalsForIngestJobs.put(ingestJobId, totals);
85  }
86  return totals;
87  }
88 
89  HashDbIngestModule(HashLookupModuleSettings settings) {
90  this.settings = settings;
91  }
92 
93  @Override
95  jobId = context.getJobId();
96  if (!hashDbManager.verifyAllDatabasesLoadedCorrectly()) {
97  throw new IngestModuleException("Could not load all hash databases");
98  }
99  updateEnabledHashSets(hashDbManager.getKnownBadFileHashSets(), knownBadHashSets);
100  updateEnabledHashSets(hashDbManager.getKnownFileHashSets(), knownHashSets);
101 
102  if (refCounter.incrementAndGet(jobId) == 1) {
103  // initialize job totals
104  getTotalsForIngestJobs(jobId);
105 
106  // if first module for this job then post error msgs if needed
107  if (knownBadHashSets.isEmpty()) {
109  HashLookupModuleFactory.getModuleName(),
110  Bundle.HashDbIngestModule_noKnownBadHashDbSetMsg(),
111  Bundle.HashDbIngestModule_knownBadFileSearchWillNotExecuteWarn()));
112  }
113 
114  if (knownHashSets.isEmpty()) {
116  HashLookupModuleFactory.getModuleName(),
117  Bundle.HashDbIngestModule_noKnownHashDbSetMsg(),
118  Bundle.HashDbIngestModule_knownFileSearchWillNotExecuteWarn()));
119  }
120  }
121  }
122 
129  private void updateEnabledHashSets(List<HashDb> allHashSets, List<HashDb> enabledHashSets) {
130  enabledHashSets.clear();
131  for (HashDb db : allHashSets) {
132  if (settings.isHashSetEnabled(db.getHashSetName())) {
133  try {
134  if (db.hasIndex()) {
135  enabledHashSets.add(db);
136  }
137  } catch (TskCoreException ex) {
138  logger.log(Level.WARNING, "Error getting index status for " + db.getHashSetName() + " hash database", ex); //NON-NLS
139  }
140  }
141  }
142  }
143 
144  @Override
145  public ProcessResult process(AbstractFile file) {
146  blackboard = Case.getCurrentCase().getServices().getBlackboard();
147 
148  // Skip unallocated space files.
149  if (file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) {
150  return ProcessResult.OK;
151  }
152 
153  /*
154  * Skip directories. One reason for this is because we won't accurately
155  * calculate hashes of NTFS directories that have content that spans the
156  * IDX_ROOT and IDX_ALLOC artifacts. So we disable that until a solution
157  * for it is developed.
158  */
159  if (file.isDir()) {
160  return ProcessResult.OK;
161  }
162 
163  // bail out if we have no hashes set
164  if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
165  return ProcessResult.OK;
166  }
167 
168  // Safely get a reference to the totalsForIngestJobs object
169  IngestJobTotals totals = getTotalsForIngestJobs(jobId);
170 
171  // calc hash value
172  String name = file.getName();
173  String md5Hash = file.getMd5Hash();
174  if (md5Hash == null || md5Hash.isEmpty()) {
175  try {
176  long calcstart = System.currentTimeMillis();
177  md5Hash = HashUtility.calculateMd5(file);
178  long delta = (System.currentTimeMillis() - calcstart);
179  totals.totalCalctime.addAndGet(delta);
180 
181  } catch (IOException ex) {
182  logger.log(Level.WARNING, "Error calculating hash of file " + name, ex); //NON-NLS
184  HashLookupModuleFactory.getModuleName(),
185  NbBundle.getMessage(this.getClass(),
186  "HashDbIngestModule.fileReadErrorMsg",
187  name),
188  NbBundle.getMessage(this.getClass(),
189  "HashDbIngestModule.calcHashValueErr",
190  name)));
191  return ProcessResult.ERROR;
192  }
193  }
194 
195  // look up in known bad first
196  boolean foundBad = false;
198  for (HashDb db : knownBadHashSets) {
199  try {
200  long lookupstart = System.currentTimeMillis();
201  HashHitInfo hashInfo = db.lookupMD5(file);
202  if (null != hashInfo) {
203  foundBad = true;
204  totals.totalKnownBadCount.incrementAndGet();
205 
206  try {
207  skCase.setKnown(file, TskData.FileKnown.BAD);
208  } catch (TskException ex) {
209  logger.log(Level.WARNING, "Couldn't set known bad state for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
211  HashLookupModuleFactory.getModuleName(),
212  NbBundle.getMessage(this.getClass(),
213  "HashDbIngestModule.hashLookupErrorMsg",
214  name),
215  NbBundle.getMessage(this.getClass(),
216  "HashDbIngestModule.settingKnownBadStateErr",
217  name)));
218  ret = ProcessResult.ERROR;
219  }
220  String hashSetName = db.getHashSetName();
221 
222  String comment = "";
223  ArrayList<String> comments = hashInfo.getComments();
224  int i = 0;
225  for (String c : comments) {
226  if (++i > 1) {
227  comment += " ";
228  }
229  comment += c;
230  if (comment.length() > MAX_COMMENT_SIZE) {
231  comment = comment.substring(0, MAX_COMMENT_SIZE) + "...";
232  break;
233  }
234  }
235 
236  postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages());
237  }
238  long delta = (System.currentTimeMillis() - lookupstart);
239  totals.totalLookuptime.addAndGet(delta);
240 
241  } catch (TskException ex) {
242  logger.log(Level.WARNING, "Couldn't lookup known bad hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
244  HashLookupModuleFactory.getModuleName(),
245  NbBundle.getMessage(this.getClass(),
246  "HashDbIngestModule.hashLookupErrorMsg",
247  name),
248  NbBundle.getMessage(this.getClass(),
249  "HashDbIngestModule.lookingUpKnownBadHashValueErr",
250  name)));
251  ret = ProcessResult.ERROR;
252  }
253  }
254 
255  // If the file is not in the known bad sets, search for it in the known sets.
256  // Any hit is sufficient to classify it as known, and there is no need to create
257  // a hit artifact or send a message to the application inbox.
258  if (!foundBad) {
259  for (HashDb db : knownHashSets) {
260  try {
261  long lookupstart = System.currentTimeMillis();
262  if (db.lookupMD5Quick(file)) {
263  try {
264  skCase.setKnown(file, TskData.FileKnown.KNOWN);
265  break;
266  } catch (TskException ex) {
267  logger.log(Level.WARNING, "Couldn't set known state for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
268  ret = ProcessResult.ERROR;
269  }
270  }
271  long delta = (System.currentTimeMillis() - lookupstart);
272  totals.totalLookuptime.addAndGet(delta);
273 
274  } catch (TskException ex) {
275  logger.log(Level.WARNING, "Couldn't lookup known hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
277  HashLookupModuleFactory.getModuleName(),
278  NbBundle.getMessage(this.getClass(),
279  "HashDbIngestModule.hashLookupErrorMsg",
280  name),
281  NbBundle.getMessage(this.getClass(),
282  "HashDbIngestModule.lookingUpKnownHashValueErr",
283  name)));
284  ret = ProcessResult.ERROR;
285  }
286  }
287  }
288 
289  return ret;
290  }
291 
292  @Messages({"HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."})
293  private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) {
294  try {
295  String MODULE_NAME = NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.moduleName");
296 
297  BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
298  //TODO Revisit usage of deprecated constructor as per TSK-583
299  //BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName);
300  BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, hashSetName);
301  badFile.addAttribute(att2);
302  BlackboardAttribute att3 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, MODULE_NAME, md5Hash);
303  badFile.addAttribute(att3);
304  BlackboardAttribute att4 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, comment);
305  badFile.addAttribute(att4);
306 
307  try {
308  // index the artifact for keyword search
309  blackboard.indexArtifact(badFile);
310  } catch (Blackboard.BlackboardException ex) {
311  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + badFile.getArtifactID(), ex); //NON-NLS
313  Bundle.HashDbIngestModule_indexError_message(), badFile.getDisplayName());
314  }
315 
316  if (showInboxMessage) {
317  StringBuilder detailsSb = new StringBuilder();
318  //details
319  detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
320  //hit
321  detailsSb.append("<tr>"); //NON-NLS
322  detailsSb.append("<th>") //NON-NLS
323  .append(NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.fileName"))
324  .append("</th>"); //NON-NLS
325  detailsSb.append("<td>") //NON-NLS
326  .append(abstractFile.getName())
327  .append("</td>"); //NON-NLS
328  detailsSb.append("</tr>"); //NON-NLS
329 
330  detailsSb.append("<tr>"); //NON-NLS
331  detailsSb.append("<th>") //NON-NLS
332  .append(NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.md5Hash"))
333  .append("</th>"); //NON-NLS
334  detailsSb.append("<td>").append(md5Hash).append("</td>"); //NON-NLS
335  detailsSb.append("</tr>"); //NON-NLS
336 
337  detailsSb.append("<tr>"); //NON-NLS
338  detailsSb.append("<th>") //NON-NLS
339  .append(NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.hashsetName"))
340  .append("</th>"); //NON-NLS
341  detailsSb.append("<td>").append(hashSetName).append("</td>"); //NON-NLS
342  detailsSb.append("</tr>"); //NON-NLS
343 
344  detailsSb.append("</table>"); //NON-NLS
345 
347  NbBundle.getMessage(this.getClass(),
348  "HashDbIngestModule.postToBB.knownBadMsg",
349  abstractFile.getName()),
350  detailsSb.toString(),
351  abstractFile.getName() + md5Hash,
352  badFile));
353  }
354  services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT, Collections.singletonList(badFile)));
355  } catch (TskException ex) {
356  logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS
357  }
358  }
359 
360  private static synchronized void postSummary(long jobId,
361  List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) {
362  IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId);
363  totalsForIngestJobs.remove(jobId);
364 
365  if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty())) {
366  StringBuilder detailsSb = new StringBuilder();
367  //details
368  detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
369 
370  detailsSb.append("<tr><td>") //NON-NLS
371  .append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.knownBadsFound"))
372  .append("</td>"); //NON-NLS
373  detailsSb.append("<td>").append(jobTotals.totalKnownBadCount.get()).append("</td></tr>"); //NON-NLS
374 
375  detailsSb.append("<tr><td>") //NON-NLS
376  .append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalCalcTime"))
377  .append("</td><td>").append(jobTotals.totalCalctime.get()).append("</td></tr>\n"); //NON-NLS
378  detailsSb.append("<tr><td>") //NON-NLS
379  .append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalLookupTime"))
380  .append("</td><td>").append(jobTotals.totalLookuptime.get()).append("</td></tr>\n"); //NON-NLS
381  detailsSb.append("</table>"); //NON-NLS
382 
383  detailsSb.append("<p>") //NON-NLS
384  .append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.databasesUsed"))
385  .append("</p>\n<ul>"); //NON-NLS
386  for (HashDb db : knownBadHashSets) {
387  detailsSb.append("<li>").append(db.getHashSetName()).append("</li>\n"); //NON-NLS
388  }
389 
390  detailsSb.append("</ul>"); //NON-NLS
391 
394  HashLookupModuleFactory.getModuleName(),
395  NbBundle.getMessage(HashDbIngestModule.class,
396  "HashDbIngestModule.complete.hashLookupResults"),
397  detailsSb.toString()));
398  }
399  }
400 
401  @Override
402  public void shutDown() {
403  if (refCounter.decrementAndGet(jobId) == 0) {
404  postSummary(jobId, knownBadHashSets, knownHashSets);
405  }
406  }
407 }
static IngestMessage createDataMessage(String source, String subject, String detailsHtml, String uniqueKey, BlackboardArtifact data)
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
void startUp(org.sleuthkit.autopsy.ingest.IngestJobContext context)
static IngestMessage createMessage(MessageType messageType, String source, String subject, String detailsHtml)
static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId)
void updateEnabledHashSets(List< HashDb > allHashSets, List< HashDb > enabledHashSets)
void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage)
void postMessage(final IngestMessage message)
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
static synchronized void postSummary(long jobId, List< HashDb > knownBadHashSets, List< HashDb > knownHashSets)
static void error(String title, String message)
synchronized void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:59
synchronized static Logger getLogger(String name)
Definition: Logger.java:161
static IngestMessage createWarningMessage(String source, String subject, String detailsHtml)
static synchronized IngestServices getInstance()

Copyright © 2012-2016 Basis Technology. Generated on: Tue Oct 25 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.