diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index a55cb7e02cf936674893f81f40efe4d712333c77..9cdbb08f9e920ee90a6697d79dc70d04b85f519e 100644 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -2,7 +2,8 @@ file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar file.reference.jython.jar-1=release/modules/ext/jython.jar -file.reference.metadata-extractor-2.6.2.jar=release/modules/ext/metadata-extractor-2.6.2.jar +file.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1.jar +file.reference.opencv-248.jar=release/modules/ext/opencv-248.jar file.reference.Rejistry-1.0-SNAPSHOT.jar=release/modules/ext/Rejistry-1.0-SNAPSHOT.jar file.reference.sevenzipjbinding-AllPlatforms.jar=release/modules/ext/sevenzipjbinding-AllPlatforms.jar file.reference.sevenzipjbinding.jar=release/modules/ext/sevenzipjbinding.jar @@ -10,12 +11,13 @@ file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar file.reference.StixLib.jar=release/modules/ext/StixLib.jar file.reference.tika-core-1.2.jar=release/modules/ext/tika-core-1.2.jar file.reference.Tsk_DataModel.jar=release/modules/ext/Tsk_DataModel.jar -file.reference.xmpcore.jar=release/modules/ext/xmpcore.jar +file.reference.xmpcore-5.1.2.jar=release/modules/ext/xmpcore-5.1.2.jar javac.source=1.8 javac.compilerargs=-Xlint -Xlint:-serial license.file=../LICENSE-2.0.txt nbm.homepage=http://www.sleuthkit.org/ nbm.module.author=Brian Carrier nbm.needs.restart=true +source.reference.metadata-extractor-2.8.1.jar=release/modules/ext/metadata-extractor-2.8.1-src.zip!/Source/ spec.version.base=10.3 diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 440fdaa0bcaff716188fc2ad4334cd511e514b67..c8089bda6c2af8aac582c310fa5026a163c41667 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -204,49 +204,49 @@ <package>org.sleuthkit.datamodel</package> </public-packages> <class-path-extension> - <runtime-relative-path>ext/jdom-2.0.5.jar</runtime-relative-path> - <binary-origin>release/modules/ext/jdom-2.0.5.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/Rejistry-1.0-SNAPSHOT.jar</runtime-relative-path> - <binary-origin>release/modules/ext/Rejistry-1.0-SNAPSHOT.jar</binary-origin> - </class-path-extension> - <class-path-extension> - <runtime-relative-path>ext/sevenzipjbinding.jar</runtime-relative-path> - <binary-origin>release/modules/ext/sevenzipjbinding.jar</binary-origin> + <runtime-relative-path>ext/xmpcore-5.1.2.jar</runtime-relative-path> + <binary-origin>release/modules/ext/xmpcore-5.1.2.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/jython-standalone-2.7.0.jar</runtime-relative-path> - <binary-origin>release/modules/ext/jython-standalone-2.7.0.jar</binary-origin> + <runtime-relative-path>ext/jdom-2.0.5.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jdom-2.0.5.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/StixLib.jar</runtime-relative-path> <binary-origin>release/modules/ext/StixLib.jar</binary-origin> </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/sqlite-jdbc-3.8.11.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sqlite-jdbc-3.8.11.jar</binary-origin> + </class-path-extension> <class-path-extension> <runtime-relative-path>ext/opencv-248.jar</runtime-relative-path> <binary-origin>release/modules/ext/opencv-248.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/sqlite-jdbc-3.8.11.jar</runtime-relative-path> - <binary-origin>release/modules/ext/sqlite-jdbc-3.8.11.jar</binary-origin> + <runtime-relative-path>ext/Rejistry-1.0-SNAPSHOT.jar</runtime-relative-path> + <binary-origin>release/modules/ext/Rejistry-1.0-SNAPSHOT.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path> - <binary-origin>release/modules/ext/sevenzipjbinding-AllPlatforms.jar</binary-origin> + <runtime-relative-path>ext/sevenzipjbinding.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sevenzipjbinding.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/metadata-extractor-2.6.2.jar</runtime-relative-path> - <binary-origin>release/modules/ext/metadata-extractor-2.6.2.jar</binary-origin> + <runtime-relative-path>ext/jython-standalone-2.7.0.jar</runtime-relative-path> + <binary-origin>release/modules/ext/jython-standalone-2.7.0.jar</binary-origin> </class-path-extension> <class-path-extension> - <runtime-relative-path>ext/xmpcore.jar</runtime-relative-path> - <binary-origin>release/modules/ext/xmpcore.jar</binary-origin> + <runtime-relative-path>ext/sevenzipjbinding-AllPlatforms.jar</runtime-relative-path> + <binary-origin>release/modules/ext/sevenzipjbinding-AllPlatforms.jar</binary-origin> </class-path-extension> <class-path-extension> <runtime-relative-path>ext/tika-core-1.2.jar</runtime-relative-path> <binary-origin>release/modules/ext/tika-core-1.2.jar</binary-origin> </class-path-extension> + <class-path-extension> + <runtime-relative-path>ext/metadata-extractor-2.8.1.jar</runtime-relative-path> + <binary-origin>release/modules/ext/metadata-extractor-2.8.1.jar</binary-origin> + </class-path-extension> <class-path-extension> <runtime-relative-path>ext/jdom-2.0.5-contrib.jar</runtime-relative-path> <binary-origin>release/modules/ext/jdom-2.0.5-contrib.jar</binary-origin> diff --git a/Core/release/modules/ext/metadata-extractor-2.6.2.jar b/Core/release/modules/ext/metadata-extractor-2.6.2.jar deleted file mode 100755 index 68426ac059d9cb48aa219b02e660581cb9c27c2b..0000000000000000000000000000000000000000 Binary files a/Core/release/modules/ext/metadata-extractor-2.6.2.jar and /dev/null differ diff --git a/Core/release/modules/ext/metadata-extractor-2.8.1.jar b/Core/release/modules/ext/metadata-extractor-2.8.1.jar new file mode 100755 index 0000000000000000000000000000000000000000..a5fe48640b6dc350e77ac550c1da92b68e56c4e8 Binary files /dev/null and b/Core/release/modules/ext/metadata-extractor-2.8.1.jar differ diff --git a/Core/release/modules/ext/xmpcore-5.1.2.jar b/Core/release/modules/ext/xmpcore-5.1.2.jar new file mode 100755 index 0000000000000000000000000000000000000000..ecd5db142e70fed769c5a3634b11b60f7767ef0e Binary files /dev/null and b/Core/release/modules/ext/xmpcore-5.1.2.jar differ diff --git a/Core/release/modules/ext/xmpcore.jar b/Core/release/modules/ext/xmpcore.jar deleted file mode 100755 index 884c2dd57ff888b46097a6fe00aba08741d94a3f..0000000000000000000000000000000000000000 Binary files a/Core/release/modules/ext/xmpcore.jar and /dev/null differ diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java index 7c186e93b4187524842da9ae60d131299643f854..deb55fa2abccec8500bc9f6ce72c2e7791eaef57 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java @@ -22,10 +22,21 @@ import com.drew.imaging.ImageProcessingException; import com.drew.lang.GeoLocation; import com.drew.lang.Rational; +import com.drew.metadata.Directory; import com.drew.metadata.Metadata; +import com.drew.metadata.MetadataException; +import com.drew.metadata.exif.makernotes.CanonMakernoteDirectory; import com.drew.metadata.exif.ExifIFD0Directory; import com.drew.metadata.exif.ExifSubIFDDirectory; import com.drew.metadata.exif.GpsDirectory; +import com.drew.metadata.exif.makernotes.CasioType1MakernoteDirectory; +import com.drew.metadata.exif.makernotes.FujifilmMakernoteDirectory; +import com.drew.metadata.exif.makernotes.KodakMakernoteDirectory; +import com.drew.metadata.exif.makernotes.NikonType2MakernoteDirectory; +import com.drew.metadata.exif.makernotes.PanasonicMakernoteDirectory; +import com.drew.metadata.exif.makernotes.PentaxMakernoteDirectory; +import com.drew.metadata.exif.makernotes.SanyoMakernoteDirectory; +import com.drew.metadata.exif.makernotes.SonyType1MakernoteDirectory; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -33,6 +44,8 @@ import java.util.Collection; import java.util.Date; import java.util.HashSet; +import java.util.List; +import java.util.TimeZone; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import org.openide.util.NbBundle; @@ -47,6 +60,8 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Image; import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @@ -63,10 +78,13 @@ public final class ExifParserFileIngestModule implements FileIngestModule { private final IngestServices services = IngestServices.getInstance(); private final AtomicInteger filesProcessed = new AtomicInteger(0); private volatile boolean filesToFire = false; + private volatile boolean facesDetected = false; + private final List<BlackboardArtifact> listOfFacesDetectedArtifacts = new ArrayList<>(); private long jobId; private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter(); private FileTypeDetector fileTypeDetector; private final HashSet<String> supportedMimeTypes = new HashSet<>(); + private TimeZone timeZone = null; ExifParserFileIngestModule() { supportedMimeTypes.add("audio/x-wav"); @@ -103,9 +121,16 @@ public ProcessResult process(AbstractFile content) { // update the tree every 1000 files if we have EXIF data that is not being being displayed final int filesProcessedValue = filesProcessed.incrementAndGet(); - if ((filesToFire) && (filesProcessedValue % 1000 == 0)) { - services.fireModuleDataEvent(new ModuleDataEvent(ExifParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)); - filesToFire = false; + if ((filesProcessedValue % 1000 == 0)) { + if (filesToFire) { + services.fireModuleDataEvent(new ModuleDataEvent(ExifParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)); + filesToFire = false; + } + if (facesDetected) { + services.fireModuleDataEvent(new ModuleDataEvent(ExifParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_FACE_DETECTED, listOfFacesDetectedArtifacts)); + listOfFacesDetectedArtifacts.clear(); + facesDetected = false; + } } //skip unsupported @@ -125,19 +150,32 @@ ProcessResult processFile(AbstractFile f) { bin = new BufferedInputStream(in); Collection<BlackboardAttribute> attributes = new ArrayList<>(); - Metadata metadata = ImageMetadataReader.readMetadata(bin, true); + Metadata metadata = ImageMetadataReader.readMetadata(bin); // Date - ExifSubIFDDirectory exifDir = metadata.getDirectory(ExifSubIFDDirectory.class); + ExifSubIFDDirectory exifDir = metadata.getFirstDirectoryOfType(ExifSubIFDDirectory.class); if (exifDir != null) { - Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL); + + // set the timeZone for the current datasource. + if (timeZone == null) { + try { + Content dataSource = f.getDataSource(); + if ((dataSource != null) && (dataSource instanceof Image)) { + Image image = (Image) dataSource; + timeZone = TimeZone.getTimeZone(image.getTimeZone()); + } + } catch (TskCoreException ex) { + logger.log(Level.INFO, "Error getting time zones", ex); //NON-NLS + } + } + Date date = exifDir.getDate(ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL, timeZone); if (date != null) { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_CREATED.getTypeID(), ExifParserModuleFactory.getModuleName(), date.getTime() / 1000)); } } // GPS Stuff - GpsDirectory gpsDir = metadata.getDirectory(GpsDirectory.class); + GpsDirectory gpsDir = metadata.getFirstDirectoryOfType(GpsDirectory.class); if (gpsDir != null) { GeoLocation loc = gpsDir.getGeoLocation(); if (loc != null) { @@ -147,14 +185,14 @@ ProcessResult processFile(AbstractFile f) { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), ExifParserModuleFactory.getModuleName(), longitude)); } - Rational altitude = gpsDir.getRational(GpsDirectory.TAG_GPS_ALTITUDE); + Rational altitude = gpsDir.getRational(GpsDirectory.TAG_ALTITUDE); if (altitude != null) { attributes.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE.getTypeID(), ExifParserModuleFactory.getModuleName(), altitude.doubleValue())); } } // Device info - ExifIFD0Directory devDir = metadata.getDirectory(ExifIFD0Directory.class); + ExifIFD0Directory devDir = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class); if (devDir != null) { String model = devDir.getString(ExifIFD0Directory.TAG_MODEL); if (model != null && !model.isEmpty()) { @@ -167,6 +205,11 @@ ProcessResult processFile(AbstractFile f) { } } + if (containsFace(metadata)) { + listOfFacesDetectedArtifacts.add(f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_FACE_DETECTED)); + facesDetected = true; + } + // Add the attributes, if there are any, to a new artifact if (!attributes.isEmpty()) { BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF); @@ -199,6 +242,121 @@ ProcessResult processFile(AbstractFile f) { } } + /** + * Checks if this metadata contains any tags related to facial information. + * NOTE: Cases with this metadata containing tags like enabled red-eye + * reduction settings, portrait settings, etc are also assumed to contain + * facial information. The method returns true. The return value of this + * method does NOT guarantee actual presence of face. + * + * @param metadata the metadata which needs to be parsed for possible facial + * information. + * + * @return returns true if the metadata contains any tags related to facial + * information. + */ + private boolean containsFace(Metadata metadata) { + Directory d = metadata.getFirstDirectoryOfType(CanonMakernoteDirectory.class); + if (d != null) { + if (d.containsTag(CanonMakernoteDirectory.TAG_FACE_DETECT_ARRAY_1) + && d.getString(CanonMakernoteDirectory.TAG_FACE_DETECT_ARRAY_1) != null) { + return true; + } + if (d.containsTag(CanonMakernoteDirectory.TAG_FACE_DETECT_ARRAY_2) + && d.getString(CanonMakernoteDirectory.TAG_FACE_DETECT_ARRAY_2) != null) { + return true; + } + } + + d = metadata.getFirstDirectoryOfType(CasioType1MakernoteDirectory.class); + if (d != null) { + try { + if (d.containsTag(CasioType1MakernoteDirectory.TAG_FLASH_MODE) + && d.getInt(CasioType1MakernoteDirectory.TAG_FLASH_MODE) == 0x04) { //0x04 = "Red eye reduction" + return true; + } + } catch (MetadataException ex) { + // move on and check next directory + } + } + + d = metadata.getFirstDirectoryOfType(FujifilmMakernoteDirectory.class); + if (d != null) { + if (d.containsTag(FujifilmMakernoteDirectory.TAG_FACES_DETECTED) + && d.getString(FujifilmMakernoteDirectory.TAG_FACES_DETECTED) != null) { + return true; + } + } + + d = metadata.getFirstDirectoryOfType(KodakMakernoteDirectory.class); + if (d != null) { + try { + if (d.containsTag(KodakMakernoteDirectory.TAG_FLASH_MODE) + && d.getInt(KodakMakernoteDirectory.TAG_FLASH_MODE) == 0x03) { //0x03 = "Red Eye" + return true; + } + } catch (MetadataException ex) { + /// move on and check next directory + } + } + + d = metadata.getFirstDirectoryOfType(NikonType2MakernoteDirectory.class); + if (d != null) { + if (d.containsTag(NikonType2MakernoteDirectory.TAG_SCENE_MODE) + && d.getString(NikonType2MakernoteDirectory.TAG_SCENE_MODE) != null + && (d.getString(NikonType2MakernoteDirectory.TAG_SCENE_MODE).equals("BEST FACE") // NON-NLS + || (d.getString(NikonType2MakernoteDirectory.TAG_SCENE_MODE).equals("SMILE")))) { // NON-NLS + return true; + } + } + + d = metadata.getFirstDirectoryOfType(PanasonicMakernoteDirectory.class); + if (d != null) { + if (d.containsTag(PanasonicMakernoteDirectory.TAG_FACES_DETECTED) + && d.getString(PanasonicMakernoteDirectory.TAG_FACES_DETECTED) != null) { + return true; + } + } + + d = metadata.getFirstDirectoryOfType(PentaxMakernoteDirectory.class); + if (d != null) { + try { + if (d.containsTag(PentaxMakernoteDirectory.TAG_FLASH_MODE) + && d.getInt(PentaxMakernoteDirectory.TAG_FLASH_MODE) == 6) { // 6 = Red-eye Reduction + return true; + } + } catch (MetadataException ex) { + // move on and check next directory + } + } + + d = metadata.getFirstDirectoryOfType(SanyoMakernoteDirectory.class); + if (d != null) { + if (d.containsTag(SanyoMakernoteDirectory.TAG_MANUAL_FOCUS_DISTANCE_OR_FACE_INFO) + && d.getString(SanyoMakernoteDirectory.TAG_MANUAL_FOCUS_DISTANCE_OR_FACE_INFO) != null) { + return true; + } + } + + d = metadata.getFirstDirectoryOfType(SonyType1MakernoteDirectory.class); + if (d != null) { + try { + if (d.containsTag(SonyType1MakernoteDirectory.TAG_AF_MODE) + && d.getInt(SonyType1MakernoteDirectory.TAG_AF_MODE) == 15) { //15 = "Face Detected" + return true; + } + if (d.containsTag(SonyType1MakernoteDirectory.TAG_EXPOSURE_MODE) + && d.getInt(SonyType1MakernoteDirectory.TAG_EXPOSURE_MODE) == 14) { //14 = "Smile shutter" + return true; + } + } catch (MetadataException ex) { + // move on and check next directory + } + } + + return false; + } + /** * Checks if should try to attempt to extract exif. Currently checks if JPEG * image (by signature) @@ -225,10 +383,15 @@ private boolean parsableFormat(AbstractFile f) { public void shutDown() { // We only need to check for this final event on the last module per job if (refCounter.decrementAndGet(jobId) == 0) { + timeZone = null; if (filesToFire) { //send the final new data event services.fireModuleDataEvent(new ModuleDataEvent(ExifParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)); } + if (facesDetected) { + //send the final new data event + services.fireModuleDataEvent(new ModuleDataEvent(ExifParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_FACE_DETECTED, listOfFacesDetectedArtifacts)); + } } } }