Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
EXIFProcessor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020-2021 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.pictureanalyzer.impls;
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.Arrays;
33 import java.util.Collection;
34 import java.util.Date;
35 import java.util.Set;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.TimeZone;
39 import java.util.logging.Level;
40 import org.apache.commons.lang3.StringUtils;
41 import org.openide.util.NbBundle;
42 import org.openide.util.lookup.ServiceProvider;
46 import org.sleuthkit.datamodel.AbstractFile;
49 import org.sleuthkit.datamodel.Blackboard;
50 import org.sleuthkit.datamodel.BlackboardArtifact;
51 import org.sleuthkit.datamodel.BlackboardAttribute;
52 import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
53 import org.sleuthkit.datamodel.Content;
54 import org.sleuthkit.datamodel.Image;
55 import org.sleuthkit.datamodel.ReadContentInputStream;
56 import org.sleuthkit.datamodel.TskCoreException;
58 import org.sleuthkit.datamodel.Score;
59 
66 @ServiceProvider(service = PictureProcessor.class)
67 public class EXIFProcessor implements PictureProcessor {
68 
69  private static final Logger logger = Logger.getLogger(EXIFProcessor.class.getName());
70 
71  @Override
72  @NbBundle.Messages({
73  "ExifProcessor.userContent.description=EXIF metadata data exists for this file."
74  })
75  public void process(IngestJobContext context, AbstractFile file) {
76  final String MODULE_NAME = PictureAnalyzerIngestModuleFactory.getModuleName();
77 
78  try (BufferedInputStream bin = new BufferedInputStream(new ReadContentInputStream(file));) {
79 
80  final Collection<BlackboardAttribute> attributes = new ArrayList<>();
81  final Metadata metadata = ImageMetadataReader.readMetadata(bin);
82 
83  // Date
84  final ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class);
85  if (exifDir != null) {
86 
87  // set the timeZone for the current datasource.
88  TimeZone timeZone = null;
89  try {
90  Content dataSource = file.getDataSource();
91  if ((dataSource != null) && (dataSource instanceof Image)) {
92  Image image = (Image) dataSource;
93  timeZone = TimeZone.getTimeZone(image.getTimeZone());
94  }
95  } catch (TskCoreException ex) {
96  logger.log(Level.INFO, "Error getting time zones", ex); //NON-NLS
97  }
98 
99  final Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone);
100  if (date != null) {
101  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED, MODULE_NAME, date.getTime() / 1000));
102  }
103  }
104 
105  if (context.fileIngestIsCancelled()) {
106  return;
107  }
108 
109  // GPS Stuff
110  final GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class);
111  if (gpsDir != null) {
112  final GeoLocation loc = gpsDir.getGeoLocation();
113  if (loc != null) {
114  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, MODULE_NAME, loc.getLatitude()));
115  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, MODULE_NAME, loc.getLongitude()));
116  }
117 
118  final Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE);
119  if (altitude != null) {
120  double alt = altitude.doubleValue();
121  if (Double.isInfinite(alt)) {
122  alt = 0.0;
123  }
124  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE, MODULE_NAME, alt));
125  }
126  }
127 
128  if (context.fileIngestIsCancelled()) {
129  return;
130  }
131 
132  // Device info
133  final ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
134  if (devDir != null) {
135  final String model = devDir.getString(ExifIFD0Directory.TAG_MODEL);
136  if (StringUtils.isNotBlank(model)) {
137  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MODEL, MODULE_NAME, model));
138  }
139 
140  final String make = devDir.getString(ExifIFD0Directory.TAG_MAKE);
141  if (StringUtils.isNotBlank(make)) {
142  attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DEVICE_MAKE, MODULE_NAME, make));
143  }
144  }
145 
146  if (context.fileIngestIsCancelled()) {
147  return;
148  }
149 
150  final Blackboard blackboard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
151 
152  if (!attributes.isEmpty() && !blackboard.artifactExists(file, BlackboardArtifact.Type.TSK_METADATA_EXIF, attributes)) {
153  List<BlackboardArtifact> artifacts = new ArrayList<>();
154  final BlackboardArtifact exifArtifact = (file.newAnalysisResult(
155  BlackboardArtifact.Type.TSK_METADATA_EXIF,
156  Score.SCORE_NONE,
157  null, null, null,
158  attributes)).getAnalysisResult();
159  artifacts.add(exifArtifact);
160 
161  final BlackboardArtifact userSuspectedArtifact = file.newAnalysisResult(
162  BlackboardArtifact.Type.TSK_USER_CONTENT_SUSPECTED,
163  Score.SCORE_UNKNOWN,
164  null, null, null,
165  Arrays.asList(new BlackboardAttribute(
166  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT,
167  MODULE_NAME,
168  Bundle.ExifProcessor_userContent_description())))
169  .getAnalysisResult();
170  artifacts.add(userSuspectedArtifact);
171 
172  try {
173  blackboard.postArtifacts(artifacts, MODULE_NAME, context.getJobId());
174  } catch (Blackboard.BlackboardException ex) {
175  logger.log(Level.SEVERE, String.format("Error posting TSK_METADATA_EXIF and TSK_USER_CONTENT_SUSPECTED artifacts for %s (object ID = %d)", file.getName(), file.getId()), ex); //NON-NLS
176  }
177  }
178  } catch (TskCoreException ex) {
179  logger.log(Level.SEVERE, String.format("Error creating TSK_METADATA_EXIF and TSK_USER_CONTENT_SUSPECTED artifacts for %s (object ID = %d)", file.getName(), file.getId()), ex); //NON-NLS
180  } catch (IOException | ImageProcessingException ex) {
181  logger.log(Level.WARNING, String.format("Error parsing %s (object ID = %d), presumed corrupt", file.getName(), file.getId()), ex); //NON-NLS
182  } catch (NoCurrentCaseException ex) {
183  logger.log(Level.SEVERE, String.format("Error processing %s (object ID = %d)", file.getName(), file.getId()), ex); //NON-NLS
184  }
185  }
186 
187  @Override
188  public Set<String> mimeTypes() {
189  return new HashSet<String>() {
190  {
191  add("audio/x-wav");
192  add("image/jpeg");
193  add("image/tiff");
194  }
195  };
196  }
197 }
void process(IngestJobContext context, AbstractFile file)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.