Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.modules.pictureanalyzer.impls;
20
21import com.drew.imaging.ImageMetadataReader;
22import com.drew.imaging.ImageProcessingException;
23import com.drew.lang.GeoLocation;
24import com.drew.lang.Rational;
25import com.drew.metadata.Metadata;
26import com.drew.metadata.exif.ExifIFD0Directory;
27import com.drew.metadata.exif.ExifSubIFDDirectory;
28import com.drew.metadata.exif.GpsDirectory;
29import java.io.BufferedInputStream;
30import java.io.IOException;
31import java.util.ArrayList;
32import java.util.Arrays;
33import java.util.Collection;
34import java.util.Date;
35import java.util.Set;
36import java.util.HashSet;
37import java.util.List;
38import java.util.TimeZone;
39import java.util.logging.Level;
40import org.apache.commons.lang3.StringUtils;
41import org.openide.util.NbBundle;
42import org.openide.util.lookup.ServiceProvider;
43import org.sleuthkit.autopsy.casemodule.Case;
44import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
45import org.sleuthkit.autopsy.ingest.IngestJobContext;
46import org.sleuthkit.datamodel.AbstractFile;
47import org.sleuthkit.autopsy.coreutils.Logger;
48import org.sleuthkit.autopsy.modules.pictureanalyzer.PictureAnalyzerIngestModuleFactory;
49import org.sleuthkit.datamodel.Blackboard;
50import org.sleuthkit.datamodel.BlackboardArtifact;
51import org.sleuthkit.datamodel.BlackboardAttribute;
52import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
53import org.sleuthkit.datamodel.Content;
54import org.sleuthkit.datamodel.Image;
55import org.sleuthkit.datamodel.ReadContentInputStream;
56import org.sleuthkit.datamodel.TskCoreException;
57import org.sleuthkit.autopsy.modules.pictureanalyzer.spi.PictureProcessor;
58import org.sleuthkit.datamodel.Score;
59
66@ServiceProvider(service = PictureProcessor.class)
67public 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}
synchronized static Logger getLogger(String name)
Definition Logger.java:124
void process(IngestJobContext context, AbstractFile file)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.