Autopsy  4.14.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.util.ArrayList;
32 import java.util.Collection;
33 import java.util.Date;
34 import java.util.HashSet;
35 import java.util.TimeZone;
36 import java.util.logging.Level;
37 import org.apache.commons.lang3.StringUtils;
38 import org.openide.util.NbBundle;
39 import org.openide.util.NbBundle.Messages;
48 import org.sleuthkit.datamodel.AbstractFile;
49 import org.sleuthkit.datamodel.Blackboard;
50 import org.sleuthkit.datamodel.BlackboardArtifact;
51 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF;
52 import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_USER_CONTENT_SUSPECTED;
53 import org.sleuthkit.datamodel.BlackboardAttribute;
54 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED;
55 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE;
56 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL;
57 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE;
58 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE;
59 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE;
60 import org.sleuthkit.datamodel.Content;
61 import org.sleuthkit.datamodel.Image;
62 import org.sleuthkit.datamodel.ReadContentInputStream;
63 import org.sleuthkit.datamodel.ReadContentInputStream.ReadContentInputStreamException;
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({"CannotRunFileTypeDetection=Cannot run file type detection."})
74 public final class ExifParserFileIngestModule implements FileIngestModule {
75 
76  private static final Logger logger = Logger.getLogger(ExifParserFileIngestModule.class.getName());
77  private static final String MODULE_NAME = ExifParserModuleFactory.getModuleName();
78  private long jobId;
79  private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
81  private final HashSet<String> supportedMimeTypes = new HashSet<>();
82  private TimeZone timeZone = null;
83  private Blackboard blackboard;
84 
86  supportedMimeTypes.add("audio/x-wav"); //NON-NLS
87  supportedMimeTypes.add("image/jpeg"); //NON-NLS
88  supportedMimeTypes.add("image/tiff"); //NON-NLS
89  }
90 
91  @Override
92  public void startUp(IngestJobContext context) throws IngestModuleException {
93  jobId = context.getJobId();
94  refCounter.incrementAndGet(jobId);
95  try {
96  fileTypeDetector = new FileTypeDetector();
98  throw new IngestModuleException(Bundle.CannotRunFileTypeDetection(), ex);
99  }
100  }
101 
102  @Messages({"ExifParserFileIngestModule.indexError.message=Failed to post EXIF Metadata artifact(s)."})
103  @Override
104  public ProcessResult process(AbstractFile content) {
105  try {
106  blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
107  } catch (NoCurrentCaseException ex) {
108  logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
109  return ProcessResult.ERROR;
110  }
111  //skip unalloc
112  if ((content.getType().equals(TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS)
113  || (content.getType().equals(TSK_DB_FILES_TYPE_ENUM.SLACK)))) {
114  return ProcessResult.OK;
115  }
116 
117  if (content.isFile() == false) {
118  return ProcessResult.OK;
119  }
120 
121  // skip known
122  if (content.getKnown().equals(TskData.FileKnown.KNOWN)) {
123  return ProcessResult.OK;
124  }
125 
126  //skip unsupported
127  if (!parsableFormat(content)) {
128  return ProcessResult.OK;
129  }
130 
131  return processFile(content);
132  }
133 
134  @Messages({"ExifParserFileIngestModule.userContent.description=EXIF metadata exists for this file."})
135  private ProcessResult processFile(AbstractFile file) {
136 
137  try (BufferedInputStream bin = new BufferedInputStream(new ReadContentInputStream(file));) {
138 
139  Collection<BlackboardAttribute> attributes = new ArrayList<>();
140  Metadata metadata = ImageMetadataReader.readMetadata(bin);
141 
142  // Date
143  ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
144  if (exifDir != null) {
145 
146  // set the timeZone for the current datasource.
147  if (timeZone == null) {
148  try {
149  Content dataSource = file.getDataSource();
150  if ((dataSource != null) && (dataSource instanceof Image)) {
151  Image image = (Image) dataSource;
152  timeZone = TimeZone.getTimeZone(image.getTimeZone());
153  }
154  } catch (TskCoreException ex) {
155  logger.log(Level.INFO, "Error getting time zones", ex); //NON-NLS
156  }
157  }
158  Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone);
159  if (date != null) {
160  attributes.add(new BlackboardAttribute(TSK_DATETIME_CREATED, MODULE_NAME, date.getTime() / 1000));
161  }
162  }
163 
164  // GPS Stuff
165  GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
166  if (gpsDir != null) {
167  GeoLocation loc = gpsDir.getGeoLocation();
168  if (loc != null) {
169  attributes.add(new BlackboardAttribute(TSK_GEO_LATITUDE, MODULE_NAME, loc.getLatitude()));
170  attributes.add(new BlackboardAttribute(TSK_GEO_LONGITUDE, MODULE_NAME, loc.getLongitude()));
171  }
172 
173  Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE);
174  if (altitude != null) {
175  attributes.add(new BlackboardAttribute(TSK_GEO_ALTITUDE, MODULE_NAME, altitude.doubleValue()));
176  }
177  }
178 
179  // Device info
180  ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
181  if (devDir != null) {
182  String model = devDir.getString(ExifIFD0Directory.TAG_MODEL);
183  if (StringUtils.isNotBlank(model)) {
184  attributes.add(new BlackboardAttribute(TSK_DEVICE_MODEL, MODULE_NAME, model));
185  }
186 
187  String make = devDir.getString(ExifIFD0Directory.TAG_MAKE);
188  if (StringUtils.isNotBlank(make)) {
189  attributes.add(new BlackboardAttribute(TSK_DEVICE_MAKE, MODULE_NAME, make));
190  }
191  }
192 
193  // Add the attributes, if there are any, to a new artifact
194  if (!attributes.isEmpty()) {
195  // Create artifact if it doesn't already exist.
196  if (!blackboard.artifactExists(file, TSK_METADATA_EXIF, attributes)) {
197  BlackboardArtifact bba = file.newArtifact(TSK_METADATA_EXIF);
198  BlackboardArtifact bba2 = file.newArtifact(TSK_USER_CONTENT_SUSPECTED);
199  bba.addAttributes(attributes);
200  bba2.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT, MODULE_NAME, Bundle.ExifParserFileIngestModule_userContent_description()));
201  try {
202  // index the artifact for keyword search
203  blackboard.postArtifact(bba, MODULE_NAME);
204  blackboard.postArtifact(bba2, MODULE_NAME);
205  } catch (Blackboard.BlackboardException ex) {
206  logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
208  Bundle.ExifParserFileIngestModule_indexError_message(), bba.getDisplayName());
209  }
210  }
211  }
212 
213  return ProcessResult.OK;
214  } catch (TskCoreException ex) {
215  logger.log(Level.WARNING, "Failed to create blackboard artifact for exif metadata ({0}).", ex.getLocalizedMessage()); //NON-NLS
216  return ProcessResult.ERROR;
217  } catch (ImageProcessingException ex) {
218  logger.log(Level.WARNING, String.format("Failed to process the image file '%s/%s' (id=%d).", file.getParentPath(), file.getName(), file.getId()), ex);
219  return ProcessResult.ERROR;
220  } catch (ReadContentInputStreamException ex) {
221  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
222  return ProcessResult.ERROR;
223  } catch (IOException ex) {
224  logger.log(Level.WARNING, String.format("IOException when parsing image file '%s/%s' (id=%d).", file.getParentPath(), file.getName(), file.getId()), ex); //NON-NLS
225  return ProcessResult.ERROR;
226  }
227  }
228 
237  private boolean parsableFormat(AbstractFile f) {
238  String mimeType = fileTypeDetector.getMIMEType(f);
239  return supportedMimeTypes.contains(mimeType);
240  }
241 
242  @Override
243  public void shutDown() {
244  // We only need to check for this final event on the last module per job
245  if (refCounter.decrementAndGet(jobId) == 0) {
246  timeZone = null;
247  }
248  }
249 }
static void error(String title, String message)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

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