Autopsy  4.10.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExifParserFileIngestModule.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2018 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.exif;
20 
21 import com.drew.imaging.ImageMetadataReader;
22 import com.drew.imaging.ImageProcessingException;
23 import com.drew.lang.GeoLocation;
24 import com.drew.lang.Rational;
25 import com.drew.metadata.Metadata;
26 import com.drew.metadata.exif.ExifIFD0Directory;
27 import com.drew.metadata.exif.ExifSubIFDDirectory;
28 import com.drew.metadata.exif.GpsDirectory;
29 import java.io.BufferedInputStream;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Collections;
35 import java.util.Date;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.TimeZone;
39 import java.util.concurrent.atomic.AtomicInteger;
40 import java.util.logging.Level;
41 import org.apache.commons.lang3.StringUtils;
42 import org.openide.util.NbBundle;
43 import org.openide.util.NbBundle.Messages;
55 import org.sleuthkit.datamodel.AbstractFile;
56 import org.sleuthkit.datamodel.BlackboardArtifact;
57 import org.sleuthkit.datamodel.BlackboardAttribute;
58 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
59 import org.sleuthkit.datamodel.Content;
60 import org.sleuthkit.datamodel.Image;
61 import org.sleuthkit.datamodel.ReadContentInputStream;
62 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
63 import org.sleuthkit.datamodel.SleuthkitCase;
64 import org.sleuthkit.datamodel.TskCoreException;
65 import org.sleuthkit.datamodel.TskData;
66 import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM;
67 
73 @NbBundle.Messages({
74  "CannotRunFileTypeDetection=Cannot run file type detection."
75 })
76 public final class ExifParserFileIngestModule implements FileIngestModule {
77 
78  private static final Logger logger = Logger.getLogger(ExifParserFileIngestModule.class.getName());
79  private final IngestServices services = IngestServices.getInstance();
80  private final AtomicInteger filesProcessed = new AtomicInteger(0);
81  private long jobId;
82  private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
84  private final HashSet<String> supportedMimeTypes = new HashSet<>();
85  private TimeZone timeZone = null;
86  private Case currentCase;
88 
90  supportedMimeTypes.add("audio/x-wav"); //NON-NLS
91  supportedMimeTypes.add("image/jpeg"); //NON-NLS
92  supportedMimeTypes.add("image/tiff"); //NON-NLS
93  }
94 
95  @Override
96  public void startUp(IngestJobContext context) throws IngestModuleException {
97  jobId = context.getJobId();
98  refCounter.incrementAndGet(jobId);
99  try {
100  fileTypeDetector = new FileTypeDetector();
102  throw new IngestModuleException(Bundle.CannotRunFileTypeDetection(), ex);
103  }
104  }
105 
106  @Override
107  public ProcessResult process(AbstractFile content) {
108  try {
109  currentCase = Case.getCurrentCaseThrows();
110  blackboard = currentCase.getServices().getBlackboard();
111  } catch (NoCurrentCaseException ex) {
112  logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
113  return ProcessResult.ERROR;
114  }
115  //skip unalloc
116  if ((content.getType().equals(TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
117  || (content.getType().equals(TSK_DB_FILES_TYPE_ENUM.SLACK)))) {
118  return ProcessResult.OK;
119  }
120 
121  if (content.isFile() == false) {
122  return ProcessResult.OK;
123  }
124 
125  // skip known
126  if (content.getKnown().equals(TskData.FileKnown.KNOWN)) {
127  return ProcessResult.OK;
128  }
129 
130  //skip unsupported
131  if (!parsableFormat(content)) {
132  return ProcessResult.OK;
133  }
134 
135  return processFile(content);
136  }
137 
138  @Messages({"ExifParserFileIngestModule.indexError.message=Failed to index EXIF Metadata artifact for keyword search."})
139  ProcessResult processFile(AbstractFile file) {
140  InputStream in = null;
141  BufferedInputStream bin = null;
142 
143  try {
144  in = new ReadContentInputStream(file);
145  bin = new BufferedInputStream(in);
146 
147  Collection<BlackboardAttribute> attributes = new ArrayList<>();
148  Metadata metadata = ImageMetadataReader.readMetadata(bin);
149 
150  // Date
151  ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
152  if (exifDir != null) {
153 
154  // set the timeZone for the current datasource.
155  if (timeZone == null) {
156  try {
157  Content dataSource = file.getDataSource();
158  if ((dataSource != null) && (dataSource instanceof Image)) {
159  Image image = (Image) dataSource;
160  timeZone = TimeZone.getTimeZone(image.getTimeZone());
161  }
162  } catch (TskCoreException ex) {
163  logger.log(Level.INFO, "Error getting time zones", ex); //NON-NLS
164  }
165  }
166  Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone);
167  if (date != null) {
168  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, ExifParserModuleFactory.getModuleName(), date.getTime() / 1000));
169  }
170  }
171 
172  // GPS Stuff
173  GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
174  if (gpsDir != null) {
175  GeoLocation loc = gpsDir.getGeoLocation();
176  if (loc != null) {
177  double latitude = loc.getLatitude();
178  double longitude = loc.getLongitude();
179  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, ExifParserModuleFactory.getModuleName(), latitude));
180  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, ExifParserModuleFactory.getModuleName(), longitude));
181  }
182 
183  Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE);
184  if (altitude != null) {
185  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE, ExifParserModuleFactory.getModuleName(), altitude.doubleValue()));
186  }
187  }
188 
189  // Device info
190  ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
191  if (devDir != null) {
192  String model = devDir.getString(ExifIFD0Directory.TAG_MODEL);
193  if (StringUtils.isNotBlank(model)) {
194  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, ExifParserModuleFactory.getModuleName(), model));
195  }
196 
197  String make = devDir.getString(ExifIFD0Directory.TAG_MAKE);
198  if (StringUtils.isNotBlank(make)) {
199  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, ExifParserModuleFactory.getModuleName(), make));
200  }
201  }
202 
203  // Add the attributes, if there are any, to a new artifact
204  if (!attributes.isEmpty()) {
205  SleuthkitCase tskCase = currentCase.getSleuthkitCase();
206  org.sleuthkit.datamodel.Blackboard tskBlackboard = tskCase.getBlackboard();
207  // Create artifact if it doesn't already exist.
208  if (!tskBlackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF, attributes)) {
209  BlackboardArtifact bba = file.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF);
210  bba.addAttributes(attributes);
211 
212  try {
213  // index the artifact for keyword search
214  blackboard.indexArtifact(bba);
215  } catch (Blackboard.BlackboardException ex) {
216  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
217  MessageNotifyUtil.Notify.error(
218  Bundle.ExifParserFileIngestModule_indexError_message(), bba.getDisplayName());
219  }
220 
221  services.fireModuleDataEvent(new ModuleDataEvent(ExifParserModuleFactory.getModuleName(),
222  BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF,
223  Collections.singletonList(bba)));
224  }
225  }
226 
227  return ProcessResult.OK;
228  } catch (TskCoreException ex) {
229  logger.log(Level.WARNING, "Failed to create blackboard artifact for exif metadata ({0}).", ex.getLocalizedMessage()); //NON-NLS
230  return ProcessResult.ERROR;
231  } catch (ImageProcessingException ex) {
232  logger.log(Level.WARNING, String.format("Failed to process the image file '%s/%s' (id=%d).", file.getParentPath(), file.getName(), file.getId()), ex);
233  return ProcessResult.ERROR;
234  } catch (ReadContentInputStreamException ex) {
235  logger.log(Level.WARNING, String.format("Error while trying to read image file '%s/%s' (id=%d).", file.getParentPath(), file.getName(), file.getId()), ex); //NON-NLS
236  return ProcessResult.ERROR;
237  } catch (IOException ex) {
238  logger.log(Level.WARNING, String.format("IOException when parsing image file '%s/%s' (id=%d).", file.getParentPath(), file.getName(), file.getId()), ex); //NON-NLS
239  return ProcessResult.ERROR;
240  } finally {
241  try {
242  if (in != null) {
243  in.close();
244  }
245  if (bin != null) {
246  bin.close();
247  }
248  } catch (IOException ex) {
249  logger.log(Level.WARNING, "Failed to close InputStream.", ex); //NON-NLS
250  return ProcessResult.ERROR;
251  }
252  }
253  }
254 
263  private boolean parsableFormat(AbstractFile f) {
264  String mimeType = fileTypeDetector.getMIMEType(f);
265  return supportedMimeTypes.contains(mimeType);
266  }
267 
268  @Override
269  public void shutDown() {
270  // We only need to check for this final event on the last module per job
271  if (refCounter.decrementAndGet(jobId) == 0) {
272  timeZone = null;
273  }
274  }
275 }
void fireModuleDataEvent(ModuleDataEvent moduleDataEvent)
synchronized void indexArtifact(BlackboardArtifact artifact)
Definition: Blackboard.java:58
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static synchronized IngestServices getInstance()

Copyright © 2012-2018 Basis Technology. Generated on: Fri Mar 22 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.