Autopsy  4.0
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;
36 import org.sleuthkit.datamodel.AbstractFile;
37 import org.sleuthkit.datamodel.BlackboardArtifact;
38 import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
39 import org.sleuthkit.datamodel.BlackboardAttribute;
40 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
41 import org.sleuthkit.datamodel.HashUtility;
42 import org.sleuthkit.datamodel.SleuthkitCase;
43 import org.sleuthkit.datamodel.TskCoreException;
44 import org.sleuthkit.datamodel.TskData;
45 import org.sleuthkit.datamodel.TskException;
49 import org.sleuthkit.datamodel.HashHitInfo;
50 
51 public class HashDbIngestModule implements FileIngestModule {
52 
53  private static final Logger logger = Logger.getLogger(HashDbIngestModule.class.getName());
54  private static final int MAX_COMMENT_SIZE = 500;
56  private final SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
58  private final HashLookupModuleSettings settings;
59  private List<HashDb> knownBadHashSets = new ArrayList<>();
60  private List<HashDb> knownHashSets = new ArrayList<>();
61  private long jobId;
62  private static final HashMap<Long, IngestJobTotals> totalsForIngestJobs = new HashMap<>();
65 
66  private static class IngestJobTotals {
67 
68  private AtomicLong totalKnownBadCount = new AtomicLong(0);
69  private AtomicLong totalCalctime = new AtomicLong(0);
70  private AtomicLong totalLookuptime = new AtomicLong(0);
71  }
72 
73  private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) {
74  IngestJobTotals totals = totalsForIngestJobs.get(ingestJobId);
75  if (totals == null) {
76  totals = new HashDbIngestModule.IngestJobTotals();
77  totalsForIngestJobs.put(ingestJobId, totals);
78  }
79  return totals;
80  }
81 
82  HashDbIngestModule(HashLookupModuleSettings settings) {
83  this.settings = settings;
84  }
85 
86  @Override
88  jobId = context.getJobId();
91 
92  if (refCounter.incrementAndGet(jobId) == 1) {
93  // initialize job totals
95 
96  // if first module for this job then post error msgs if needed
97 
98  if (knownBadHashSets.isEmpty()) {
100  HashLookupModuleFactory.getModuleName(),
101  NbBundle.getMessage(this.getClass(),
102  "HashDbIngestModule.noKnownBadHashDbSetMsg"),
103  NbBundle.getMessage(this.getClass(),
104  "HashDbIngestModule.knownBadFileSearchWillNotExecuteWarn")));
105  }
106 
107  if (knownHashSets.isEmpty()) {
109  HashLookupModuleFactory.getModuleName(),
110  NbBundle.getMessage(this.getClass(),
111  "HashDbIngestModule.noKnownHashDbSetMsg"),
112  NbBundle.getMessage(this.getClass(),
113  "HashDbIngestModule.knownFileSearchWillNotExecuteWarn")));
114  }
115  }
116  }
117 
124  private void updateEnabledHashSets(List<HashDb> allHashSets, List<HashDb> enabledHashSets) {
125  enabledHashSets.clear();
126  for (HashDb db : allHashSets) {
127  if (settings.isHashSetEnabled(db.getHashSetName())) {
128  try {
129  if (db.hasIndex()) {
130  enabledHashSets.add(db);
131  }
132  } catch (TskCoreException ex) {
133  logger.log(Level.WARNING, "Error getting index status for " + db.getHashSetName() + " hash database", ex); //NON-NLS
134  }
135  }
136  }
137  }
138 
139  @Override
140  public ProcessResult process(AbstractFile file) {
141  blackboard = Case.getCurrentCase().getServices().getBlackboard();
142 
143  // Skip unallocated space files.
144  if (file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)) {
145  return ProcessResult.OK;
146  }
147 
148  /*
149  * Skip directories. One reason for this is because we won't accurately
150  * calculate hashes of NTFS directories that have content that spans the
151  * IDX_ROOT and IDX_ALLOC artifacts. So we disable that until a solution
152  * for it is developed.
153  */
154  if (file.isDir()) {
155  return ProcessResult.OK;
156  }
157 
158  // bail out if we have no hashes set
159  if ((knownHashSets.isEmpty()) && (knownBadHashSets.isEmpty()) && (!settings.shouldCalculateHashes())) {
160  return ProcessResult.OK;
161  }
162 
163  // Safely get a reference to the totalsForIngestJobs object
164  IngestJobTotals totals = getTotalsForIngestJobs(jobId);
165 
166  // calc hash value
167  String name = file.getName();
168  String md5Hash = file.getMd5Hash();
169  if (md5Hash == null || md5Hash.isEmpty()) {
170  try {
171  long calcstart = System.currentTimeMillis();
172  md5Hash = HashUtility.calculateMd5(file);
173  long delta = (System.currentTimeMillis() - calcstart);
174  totals.totalCalctime.addAndGet(delta);
175 
176  } catch (IOException ex) {
177  logger.log(Level.WARNING, "Error calculating hash of file " + name, ex); //NON-NLS
179  HashLookupModuleFactory.getModuleName(),
180  NbBundle.getMessage(this.getClass(),
181  "HashDbIngestModule.fileReadErrorMsg",
182  name),
183  NbBundle.getMessage(this.getClass(),
184  "HashDbIngestModule.calcHashValueErr",
185  name)));
186  return ProcessResult.ERROR;
187  }
188  }
189 
190  // look up in known bad first
191  boolean foundBad = false;
193  for (HashDb db : knownBadHashSets) {
194  try {
195  long lookupstart = System.currentTimeMillis();
196  HashHitInfo hashInfo = db.lookupMD5(file);
197  if (null != hashInfo) {
198  foundBad = true;
199  totals.totalKnownBadCount.incrementAndGet();
200 
201  try {
202  skCase.setKnown(file, TskData.FileKnown.BAD);
203  } catch (TskException ex) {
204  logger.log(Level.WARNING, "Couldn't set known bad state for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
206  HashLookupModuleFactory.getModuleName(),
207  NbBundle.getMessage(this.getClass(),
208  "HashDbIngestModule.hashLookupErrorMsg",
209  name),
210  NbBundle.getMessage(this.getClass(),
211  "HashDbIngestModule.settingKnownBadStateErr",
212  name)));
213  ret = ProcessResult.ERROR;
214  }
215  String hashSetName = db.getHashSetName();
216 
217  String comment = "";
218  ArrayList<String> comments = hashInfo.getComments();
219  int i = 0;
220  for (String c : comments) {
221  if (++i > 1) {
222  comment += " ";
223  }
224  comment += c;
225  if (comment.length() > MAX_COMMENT_SIZE) {
226  comment = comment.substring(0, MAX_COMMENT_SIZE) + "...";
227  break;
228  }
229  }
230 
231  postHashSetHitToBlackboard(file, md5Hash, hashSetName, comment, db.getSendIngestMessages());
232  }
233  long delta = (System.currentTimeMillis() - lookupstart);
234  totals.totalLookuptime.addAndGet(delta);
235 
236  } catch (TskException ex) {
237  logger.log(Level.WARNING, "Couldn't lookup known bad hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
239  HashLookupModuleFactory.getModuleName(),
240  NbBundle.getMessage(this.getClass(),
241  "HashDbIngestModule.hashLookupErrorMsg",
242  name),
243  NbBundle.getMessage(this.getClass(),
244  "HashDbIngestModule.lookingUpKnownBadHashValueErr",
245  name)));
246  ret = ProcessResult.ERROR;
247  }
248  }
249 
250  // If the file is not in the known bad sets, search for it in the known sets.
251  // Any hit is sufficient to classify it as known, and there is no need to create
252  // a hit artifact or send a message to the application inbox.
253  if (!foundBad) {
254  for (HashDb db : knownHashSets) {
255  try {
256  long lookupstart = System.currentTimeMillis();
257  if (db.lookupMD5Quick(file)) {
258  try {
259  skCase.setKnown(file, TskData.FileKnown.KNOWN);
260  break;
261  } catch (TskException ex) {
262  logger.log(Level.WARNING, "Couldn't set known state for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
263  ret = ProcessResult.ERROR;
264  }
265  }
266  long delta = (System.currentTimeMillis() - lookupstart);
267  totals.totalLookuptime.addAndGet(delta);
268 
269  } catch (TskException ex) {
270  logger.log(Level.WARNING, "Couldn't lookup known hash for file " + name + " - see sleuthkit log for details", ex); //NON-NLS
272  HashLookupModuleFactory.getModuleName(),
273  NbBundle.getMessage(this.getClass(),
274  "HashDbIngestModule.hashLookupErrorMsg",
275  name),
276  NbBundle.getMessage(this.getClass(),
277  "HashDbIngestModule.lookingUpKnownHashValueErr",
278  name)));
279  ret = ProcessResult.ERROR;
280  }
281  }
282  }
283 
284  return ret;
285  }
286 
287  private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) {
288  try {
289  String MODULE_NAME = NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.moduleName");
290 
291  BlackboardArtifact badFile = abstractFile.newArtifact(ARTIFACT_TYPE.TSK_HASHSET_HIT);
292  //TODO Revisit usage of deprecated constructor as per TSK-583
293  //BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME.getTypeID(), MODULE_NAME, "Known Bad", hashSetName);
294  BlackboardAttribute att2 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, hashSetName);
295  badFile.addAttribute(att2);
296  BlackboardAttribute att3 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_HASH_MD5, MODULE_NAME, md5Hash);
297  badFile.addAttribute(att3);
298  BlackboardAttribute att4 = new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, comment);
299  badFile.addAttribute(att4);
300 
301  try {
302  // index the artifact for keyword search
303  blackboard.indexArtifact(badFile);
304  } catch (Blackboard.BlackboardException ex) {
305  logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", badFile.getDisplayName()), ex); //NON-NLS
307  NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), badFile.getDisplayName());
308  }
309 
310  if (showInboxMessage) {
311  StringBuilder detailsSb = new StringBuilder();
312  //details
313  detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
314  //hit
315  detailsSb.append("<tr>"); //NON-NLS
316  detailsSb.append("<th>") //NON-NLS
317  .append(NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.fileName"))
318  .append("</th>"); //NON-NLS
319  detailsSb.append("<td>") //NON-NLS
320  .append(abstractFile.getName())
321  .append("</td>"); //NON-NLS
322  detailsSb.append("</tr>"); //NON-NLS
323 
324  detailsSb.append("<tr>"); //NON-NLS
325  detailsSb.append("<th>") //NON-NLS
326  .append(NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.md5Hash"))
327  .append("</th>"); //NON-NLS
328  detailsSb.append("<td>").append(md5Hash).append("</td>"); //NON-NLS
329  detailsSb.append("</tr>"); //NON-NLS
330 
331  detailsSb.append("<tr>"); //NON-NLS
332  detailsSb.append("<th>") //NON-NLS
333  .append(NbBundle.getMessage(this.getClass(), "HashDbIngestModule.postToBB.hashsetName"))
334  .append("</th>"); //NON-NLS
335  detailsSb.append("<td>").append(hashSetName).append("</td>"); //NON-NLS
336  detailsSb.append("</tr>"); //NON-NLS
337 
338  detailsSb.append("</table>"); //NON-NLS
339 
341  NbBundle.getMessage(this.getClass(),
342  "HashDbIngestModule.postToBB.knownBadMsg",
343  abstractFile.getName()),
344  detailsSb.toString(),
345  abstractFile.getName() + md5Hash,
346  badFile));
347  }
348  services.fireModuleDataEvent(new ModuleDataEvent(MODULE_NAME, ARTIFACT_TYPE.TSK_HASHSET_HIT, Collections.singletonList(badFile)));
349  } catch (TskException ex) {
350  logger.log(Level.WARNING, "Error creating blackboard artifact", ex); //NON-NLS
351  }
352  }
353 
354  private static synchronized void postSummary(long jobId,
355  List<HashDb> knownBadHashSets, List<HashDb> knownHashSets) {
356  IngestJobTotals jobTotals = getTotalsForIngestJobs(jobId);
357  totalsForIngestJobs.remove(jobId);
358 
359  if ((!knownBadHashSets.isEmpty()) || (!knownHashSets.isEmpty())) {
360  StringBuilder detailsSb = new StringBuilder();
361  //details
362  detailsSb.append("<table border='0' cellpadding='4' width='280'>"); //NON-NLS
363 
364  detailsSb.append("<tr><td>") //NON-NLS
365  .append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.knownBadsFound"))
366  .append("</td>"); //NON-NLS
367  detailsSb.append("<td>").append(jobTotals.totalKnownBadCount.get()).append("</td></tr>"); //NON-NLS
368 
369  detailsSb.append("<tr><td>") //NON-NLS
370  .append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalCalcTime"))
371  .append("</td><td>").append(jobTotals.totalCalctime.get()).append("</td></tr>\n"); //NON-NLS
372  detailsSb.append("<tr><td>") //NON-NLS
373  .append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.totalLookupTime"))
374  .append("</td><td>").append(jobTotals.totalLookuptime.get()).append("</td></tr>\n"); //NON-NLS
375  detailsSb.append("</table>"); //NON-NLS
376 
377  detailsSb.append("<p>") //NON-NLS
378  .append(NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.complete.databasesUsed"))
379  .append("</p>\n<ul>"); //NON-NLS
380  for (HashDb db : knownBadHashSets) {
381  detailsSb.append("<li>").append(db.getHashSetName()).append("</li>\n"); //NON-NLS
382  }
383 
384  detailsSb.append("</ul>"); //NON-NLS
385 
388  HashLookupModuleFactory.getModuleName(),
389  NbBundle.getMessage(HashDbIngestModule.class,
390  "HashDbIngestModule.complete.hashLookupResults"),
391  detailsSb.toString()));
392  }
393  }
394 
395  @Override
396  public void shutDown() {
397  if (refCounter.decrementAndGet(jobId) == 0) {
398  postSummary(jobId, knownBadHashSets, knownHashSets);
399  }
400  }
401 }
static IngestMessage createDataMessage(String source, String subject, String detailsHtml, String uniqueKey, BlackboardArtifact data)
void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:45
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)
static final HashMap< Long, IngestJobTotals > totalsForIngestJobs
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 static Logger getLogger(String name)
Definition: Logger.java:166
static IngestMessage createWarningMessage(String source, String subject, String detailsHtml)
static synchronized IngestServices getInstance()

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.