mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Inital commit of drone parse code
This commit is contained in:
parent
9eb8871bdd
commit
f90f86170a
@ -74,6 +74,7 @@
|
||||
<copy file="${thirdparty.dir}/stix/StixLib.jar" todir="${ext.dir}" />
|
||||
<copy file="${thirdparty.dir}/jdom/jdom-2.0.5.jar" todir="${ext.dir}" />
|
||||
<copy file="${thirdparty.dir}/jdom/jdom-2.0.5-contrib.jar" todir="${ext.dir}" />
|
||||
<copy file="${thirdparty.dir}/DatCon/3.6.9/DatCon.jar" todir="${ext.dir}" />
|
||||
</target>
|
||||
|
||||
|
||||
|
@ -120,6 +120,7 @@ file.reference.okhttp-2.7.5-javadoc.jar=release/modules/ext/okhttp-2.7.5-javadoc
|
||||
file.reference.okhttp-2.7.5-sources.jar=release/modules/ext/okhttp-2.7.5-sources.jar
|
||||
file.reference.okhttp-2.7.5.jar=release/modules/ext/okhttp-2.7.5.jar
|
||||
file.reference.okio-1.6.0.jar=release/modules/ext/okio-1.6.0.jar
|
||||
file.reference.datcon.jar=release/modules/ext/DatCon.jar
|
||||
javac.source=1.8
|
||||
javac.compilerargs=-Xlint -Xlint:-serial
|
||||
license.file=../LICENSE-2.0.txt
|
||||
|
@ -802,6 +802,10 @@
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/jutf7-1.0.0.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/jutf7-1.0.0.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/DatCon.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/DatCon.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
</data>
|
||||
</configuration>
|
||||
|
5
Core/src/org/sleuthkit/autopsy/modules/drones/Bundle.properties-MERGED
Executable file
5
Core/src/org/sleuthkit/autopsy/modules/drones/Bundle.properties-MERGED
Executable file
@ -0,0 +1,5 @@
|
||||
DATExtractor_process_message=Processing DJI DAT file: %s
|
||||
DATFileExtractor_Extractor_Name=DAT File Extractor
|
||||
DroneIngestModule_Description=Description
|
||||
DroneIngestModule_Name=Drone
|
||||
DroneIngestModule_process_start=Started {0}
|
148
Core/src/org/sleuthkit/autopsy/modules/drones/DATDumper.java
Executable file
148
Core/src/org/sleuthkit/autopsy/modules/drones/DATDumper.java
Executable file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.drones;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import src.Files.ConvertDat;
|
||||
import src.Files.CsvWriter;
|
||||
import src.Files.DatFile;
|
||||
import src.Files.DJIAssistantFile;
|
||||
import src.Files.Exception.FileEnd;
|
||||
import src.Files.Exception.NotDatFile;
|
||||
|
||||
/**
|
||||
* Dump DJI DAT files to csv file using the DatCon.jar.
|
||||
*
|
||||
*/
|
||||
final class DATDumper {
|
||||
|
||||
/**
|
||||
* Construct a DATDumper.
|
||||
*/
|
||||
DATDumper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the DJI DAT file to a csv file.
|
||||
*
|
||||
* @param datFilePath Path to input DAT file
|
||||
* @param outputFilePath Output file path
|
||||
* @param overWriteExisting True to overwrite an existing csv file with
|
||||
* outputFilePath
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
void dumpDATFile(String datFilePath, String outputFilePath, boolean overWriteExisting) throws DroneIngestException {
|
||||
// Validate the input and output file paths.
|
||||
validateOutputFile(outputFilePath, overWriteExisting);
|
||||
validateDATFile(datFilePath);
|
||||
|
||||
DatFile datFile = null;
|
||||
try (CsvWriter writer = new CsvWriter(outputFilePath)) {
|
||||
// Creates a version specific DatFile object
|
||||
datFile = DatFile.createDatFile(datFilePath);
|
||||
datFile.reset();
|
||||
// preAnalyze does an inital pass of the DAT file to gather some
|
||||
// information about the file.
|
||||
datFile.preAnalyze();
|
||||
|
||||
// Creates a version specific ConvertDat object
|
||||
ConvertDat convertDat = datFile.createConVertDat();
|
||||
|
||||
// The lower the sample rate the smaller the output csv file will be
|
||||
// however the date will be less precise. 30 was recommended in the
|
||||
// DatCon documentation as a good default.
|
||||
convertDat.sampleRate = 30;
|
||||
|
||||
// Setting the tickRangeLower and upper values reduces some of the
|
||||
// noise invalid data in the output file.
|
||||
if (datFile.gpsLockTick != -1) {
|
||||
convertDat.tickRangeLower = datFile.gpsLockTick;
|
||||
}
|
||||
|
||||
if (datFile.lastMotorStopTick != -1) {
|
||||
convertDat.tickRangeUpper = datFile.lastMotorStopTick;
|
||||
}
|
||||
|
||||
convertDat.setCsvWriter(writer);
|
||||
convertDat.createRecordParsers();
|
||||
datFile.reset();
|
||||
|
||||
// Analyze does the work of parsing the data, everything prior was
|
||||
// setup
|
||||
convertDat.analyze(true);
|
||||
|
||||
} catch (IOException | NotDatFile | FileEnd ex) {
|
||||
throw new DroneIngestException(String.format("Failed to dump DAT file to csv. DAT = %s, CSV = %s", datFilePath, outputFilePath), ex);
|
||||
} finally {
|
||||
if (datFile != null) {
|
||||
datFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that if the given csv file exists that the overWriteExsiting
|
||||
* param is true. Throws an exception if the file exists and
|
||||
* overWriteExisting is false.
|
||||
*
|
||||
* @param outputFileName Absolute path for the output csv file
|
||||
* @param overWriteExisting True to over write an existing file.
|
||||
*
|
||||
* @throws DroneIngestException Throws exception if overWriteExisting is
|
||||
* true and outputFileName exists
|
||||
*/
|
||||
private void validateOutputFile(String outputFileName, boolean overWriteExisting) throws DroneIngestException {
|
||||
File csvFile = new File(outputFileName);
|
||||
|
||||
if (csvFile.exists()) {
|
||||
if (overWriteExisting) {
|
||||
csvFile.delete();
|
||||
} else {
|
||||
throw new DroneIngestException(String.format("Unable to dump DAT file. overWriteExsiting is false and DAT output csv file exists: %s", outputFileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the DAT file exists and it is in a known format that can be
|
||||
* dumped.
|
||||
*
|
||||
* @param datFilePath Absolute path to DAT file
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
private void validateDATFile(String datFilePath) throws DroneIngestException {
|
||||
File datFile = new File(datFilePath);
|
||||
|
||||
if (!datFile.exists()) {
|
||||
throw new DroneIngestException(String.format("Unable to dump DAT file DAT file does not exist: %s", datFilePath));
|
||||
}
|
||||
|
||||
try {
|
||||
if (!DatFile.isDatFile(datFilePath) && !DJIAssistantFile.isDJIDat(datFile)) {
|
||||
throw new DroneIngestException(String.format("Unable to dump DAT file. File is not a DAT file: %s", datFilePath));
|
||||
}
|
||||
} catch (FileNotFoundException ex) {
|
||||
throw new DroneIngestException(String.format("Unable to dump DAT file. File not found %s", datFilePath), ex);
|
||||
}
|
||||
}
|
||||
}
|
318
Core/src/org/sleuthkit/autopsy/modules/drones/DATExtractor.java
Executable file
318
Core/src/org/sleuthkit/autopsy/modules/drones/DATExtractor.java
Executable file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.drones;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.coreutils.FileUtil;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Extract drone position data from DJI Phantom drones.
|
||||
*
|
||||
* Module uses DatCon.jar to dump FLYXXX.DAT file to a CSV file which is stored
|
||||
* in the case temp directory. Artifacts are created by parsing the csv file.
|
||||
*
|
||||
*/
|
||||
final class DATExtractor extends DroneExtractor {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DATExtractor.class.getName());
|
||||
|
||||
private static final String HEADER_LONG = "IMU_ATTI(0):Longitude";
|
||||
private static final String HEADER_LAT = "IMU_ATTI(0):Latitude";
|
||||
private static final String HEADER_VELOCITY = "IMU_ATTI(0):velComposite";
|
||||
private static final String HEADER_DATETILE = "GPS:dateTimeStamp";
|
||||
private static final String HEADER_ALTITUDE = "GPS(0):heightMSL";
|
||||
private static final String HEADER_DISTANCE_FROM_HP = "IMU_ATTI(0):distanceHP";
|
||||
private static final String HEADER_DISTANCE_TRAVELED = "IMU_ATTI(0):distanceTravelled";
|
||||
|
||||
/**
|
||||
* Construct a DATExtractor.
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
DATExtractor() throws DroneIngestException {
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
@Messages({
|
||||
"DATExtractor_process_message=Processing DJI DAT file: %s"
|
||||
})
|
||||
@Override
|
||||
void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) throws DroneIngestException {
|
||||
List<AbstractFile> datFiles = findDATFiles(dataSource);
|
||||
|
||||
DATDumper dumper = new DATDumper();
|
||||
|
||||
try {
|
||||
for (AbstractFile DATFile : datFiles) {
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
progressBar.progress(String.format(Bundle.DATExtractor_process_message(), DATFile.getName()));
|
||||
|
||||
// Copy the DAT file into the case temp folder
|
||||
File tempDATFile = getTemporaryFile(context, DATFile);
|
||||
|
||||
// Create a path for the csv file
|
||||
String csvFilePath = getCSVPathForDAT(DATFile);
|
||||
|
||||
try {
|
||||
// Dump the DAT file to a csv file
|
||||
dumper.dumpDATFile(tempDATFile.getAbsolutePath(), csvFilePath, true);
|
||||
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Process the csv file
|
||||
processCSVFile(context, DATFile, csvFilePath);
|
||||
} catch (DroneIngestException ex) {
|
||||
logger.log(Level.WARNING, String.format("Exception thrown while processing DAT file %s", DATFile.getName()), ex);
|
||||
} finally {
|
||||
tempDATFile.delete();
|
||||
(new File(csvFilePath)).delete();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
FileUtil.deleteDir(getExtractorTempPath().toFile());
|
||||
}
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
"DATFileExtractor_Extractor_Name=DAT File Extractor"
|
||||
})
|
||||
|
||||
@Override
|
||||
String getName() {
|
||||
return Bundle.DATFileExtractor_Extractor_Name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find any files that have the file name FLYXXX.DAT where the X are digit
|
||||
* characters.
|
||||
*
|
||||
* @param dataSource Data source to search
|
||||
*
|
||||
* @return List of found files or empty list if none where found
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
private List<AbstractFile> findDATFiles(Content dataSource) throws DroneIngestException {
|
||||
List<AbstractFile> fileList = new ArrayList<>();
|
||||
|
||||
FileManager fileManager = getCurrentCase().getServices().getFileManager();
|
||||
|
||||
// findFiles use the SQL wildcard # in the file name
|
||||
try {
|
||||
fileList = fileManager.findFiles(dataSource, "FLY___.DAT");
|
||||
} catch (TskCoreException ex) {
|
||||
throw new DroneIngestException("Unable to find drone DAT files.", ex);
|
||||
}
|
||||
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an absolute path for the given DAT file.
|
||||
*
|
||||
* @param file DAT file
|
||||
*
|
||||
* @return Absolute csv file path
|
||||
*/
|
||||
private String getCSVPathForDAT(AbstractFile file) {
|
||||
String tempFileName = file.getName() + file.getId() + ".csv";
|
||||
return Paths.get(getExtractorTempPath().toString(), tempFileName).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the csv dump of the drone DAT file.
|
||||
*
|
||||
* Create artifacts for all rows that have a valid longitude and latitude.
|
||||
*
|
||||
* @param context current case job context
|
||||
* @param DATFile Original DAT file
|
||||
* @param csvFilePath Path of csv file to process
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
private void processCSVFile(IngestJobContext context, AbstractFile DATFile, String csvFilePath) throws DroneIngestException {
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(new File(csvFilePath)))) {
|
||||
// First read in the header line and process
|
||||
String line = reader.readLine();
|
||||
Map<String, Integer> headerMap = makeHeaderMap(line.split(","));
|
||||
Collection<BlackboardArtifact> artifacts = new ArrayList<>();
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
break;
|
||||
}
|
||||
|
||||
String[] values = line.split(",");
|
||||
Collection<BlackboardAttribute> attributes = buildAttributes(headerMap, values);
|
||||
if (attributes != null) {
|
||||
artifacts.add(makeWaypointArtifact(DATFile, attributes));
|
||||
}
|
||||
}
|
||||
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
postArtifacts(artifacts);
|
||||
|
||||
} catch (IOException ex) {
|
||||
throw new DroneIngestException(String.format("Failed to read DAT csvFile %s", csvFilePath), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a lookup to quickly match the column header with its array index
|
||||
*
|
||||
* @param headers
|
||||
*
|
||||
* @return Map of column names with the column index
|
||||
*/
|
||||
private Map<String, Integer> makeHeaderMap(String[] headers) {
|
||||
Map<String, Integer> map = new HashMap<>();
|
||||
|
||||
for (int index = 0; index < headers.length; index++) {
|
||||
map.put(headers[index], index);
|
||||
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of BlackboardAttributes generated from the String array.
|
||||
*
|
||||
* If longitude and latitude are not valid, assume we the row is not
|
||||
* interesting and return a null collection.
|
||||
*
|
||||
* @param columnLookup column header lookup map
|
||||
* @param values Row data
|
||||
*
|
||||
* @return Collection of BlackboardAttributes for row or null collection if
|
||||
* longitude or latitude was not valid
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
private Collection<BlackboardAttribute> buildAttributes(Map<String, Integer> columnLookup, String[] values) throws DroneIngestException {
|
||||
|
||||
Double latitude = getDoubleValue(columnLookup.get(HEADER_LAT), values);
|
||||
Double longitude = getDoubleValue(columnLookup.get(HEADER_LONG), values);
|
||||
|
||||
if (longitude == null || latitude == null) {
|
||||
// Assume the row is not valid\has junk
|
||||
return null;
|
||||
}
|
||||
|
||||
return makeWaypointAttributes(latitude,
|
||||
longitude,
|
||||
getDoubleValue(columnLookup.get(HEADER_ALTITUDE), values),
|
||||
getDateTimeValue(columnLookup, values),
|
||||
getDoubleValue(columnLookup.get(HEADER_VELOCITY), values),
|
||||
getDoubleValue(columnLookup.get(HEADER_DISTANCE_FROM_HP), values),
|
||||
getDoubleValue(columnLookup.get(HEADER_DISTANCE_TRAVELED), values));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the waypoint timestamp in java/unix epoch seconds.
|
||||
*
|
||||
* The format of the date time string is 2016-09-26T18:26:19Z.
|
||||
*
|
||||
* @param headerMap
|
||||
* @param values
|
||||
*
|
||||
* @return Epoch seconds or null if no dateTime value was found
|
||||
*/
|
||||
private Long getDateTimeValue(Map<String, Integer> headerMap, String[] values) {
|
||||
Integer index = headerMap.get(HEADER_DATETILE);
|
||||
if (index == null || index == -1 || index > values.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String value = values[index];
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
ZonedDateTime zdt = ZonedDateTime.parse(value, DateTimeFormatter.ISO_DATE_TIME);
|
||||
return zdt.toLocalDateTime().toEpochSecond(ZoneOffset.UTC);
|
||||
} catch (DateTimeParseException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string value at the given index parsed as a double.
|
||||
*
|
||||
* @param index Index to string array
|
||||
* @param values Array of string values
|
||||
*
|
||||
* @return Double value or null if the index is out of bounds of the string
|
||||
* array or the string value at index was not a double.
|
||||
*/
|
||||
private Double getDoubleValue(Integer index, String[] values) {
|
||||
if (index == null || index == -1 || index > values.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String value = values[index];
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return Double.parseDouble(value);
|
||||
} catch (NumberFormatException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
232
Core/src/org/sleuthkit/autopsy/modules/drones/DroneExtractor.java
Executable file
232
Core/src/org/sleuthkit/autopsy/modules/drones/DroneExtractor.java
Executable file
@ -0,0 +1,232 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.drones;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.Blackboard;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
* Abstract base class for all Drone file extractors.
|
||||
*/
|
||||
abstract class DroneExtractor {
|
||||
|
||||
static private final String TEMP_FOLDER_NAME = "DroneExtractor";
|
||||
private final Case currentCase;
|
||||
|
||||
/**
|
||||
* Common constructor. Subclasses should call super in their constructor.
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
protected DroneExtractor() throws DroneIngestException {
|
||||
try {
|
||||
currentCase = Case.getCurrentCaseThrows();
|
||||
} catch (NoCurrentCaseException ex) {
|
||||
throw new DroneIngestException("Unable to create drone extractor, no open case.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
abstract void process(Content dataSource, IngestJobContext context, DataSourceIngestModuleProgress progressBar) throws DroneIngestException;
|
||||
|
||||
abstract String getName();
|
||||
|
||||
/**
|
||||
* Return the current case object.
|
||||
*
|
||||
* @return Current case
|
||||
*/
|
||||
final protected Case getCurrentCase() {
|
||||
return currentCase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current SleuthkitCase.
|
||||
*
|
||||
* @return Current sleuthkit case
|
||||
*/
|
||||
final protected SleuthkitCase getSleuthkitCase() {
|
||||
return currentCase.getSleuthkitCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Blackboard object.
|
||||
*
|
||||
* @return Current Case blackboard object.
|
||||
*/
|
||||
final protected Blackboard getBlackboard() {
|
||||
return currentCase.getSleuthkitCase().getBlackboard();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to post a list of BlackboardArtifacts to the blackboard.
|
||||
*
|
||||
* @param artifacts A list of artifacts. IF list is empty or null, the
|
||||
* function will return.
|
||||
*/
|
||||
void postArtifacts(Collection<BlackboardArtifact> artifacts) throws DroneIngestException {
|
||||
if (artifacts == null || artifacts.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
getBlackboard().postArtifacts(artifacts, getName());
|
||||
} catch (Blackboard.BlackboardException ex) {
|
||||
throw new DroneIngestException(String.format("Failed to post Drone artifacts to blackboard."), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TSK_WAYPOINT artifact with the given list of attributes.
|
||||
*
|
||||
* @param DATFile DAT file
|
||||
* @param attributes List of BlackboardAttributes
|
||||
*
|
||||
* @return TSK_WAYPOINT BlackboardArtifact
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
protected BlackboardArtifact makeWaypointArtifact(AbstractFile DATFile, Collection<BlackboardAttribute> attributes) throws DroneIngestException {
|
||||
try {
|
||||
BlackboardArtifact artifact = DATFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_WAYPOINT);
|
||||
artifact.addAttributes(attributes);
|
||||
return artifact;
|
||||
} catch (TskCoreException ex) {
|
||||
throw new DroneIngestException(String.format("Failed to post Drone artifacts to blackboard."), ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of BlackboardAttributes for the given parameters.
|
||||
*
|
||||
* Throws exception of longitude or latitude are null.
|
||||
*
|
||||
* @param latitude Waypoint latitude, must be non-null
|
||||
* @param longitude waypoint longitude, must be non-null
|
||||
* @param altitude Waypoint altitude\height
|
||||
* @param dateTime Timestamp the waypoint was created (Java epoch
|
||||
* seconds)
|
||||
* @param velocity Velocity
|
||||
* @param distanceHP Distance from home point
|
||||
* @param distanceTraveled Total distance the drone has traveled
|
||||
*
|
||||
* @return Collection of BlackboardAttributes
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
protected Collection<BlackboardAttribute> makeWaypointAttributes(Double latitude, Double longitude, Double altitude, Long dateTime, Double velocity, Double distanceHP, Double distanceTraveled) throws DroneIngestException {
|
||||
Collection<BlackboardAttribute> attributes = new ArrayList<>();
|
||||
|
||||
if (latitude == null || longitude == null) {
|
||||
throw new DroneIngestException("Invalid list of waypoint attributes, longitude or latitude was null");
|
||||
}
|
||||
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE,
|
||||
DroneIngestModuleFactory.getModuleName(), latitude));
|
||||
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE,
|
||||
DroneIngestModuleFactory.getModuleName(), longitude));
|
||||
|
||||
if (altitude != null) {
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE,
|
||||
DroneIngestModuleFactory.getModuleName(), altitude));
|
||||
}
|
||||
|
||||
if (dateTime != null) {
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME,
|
||||
DroneIngestModuleFactory.getModuleName(), dateTime));
|
||||
}
|
||||
|
||||
if (velocity != null) {
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VELOCITY,
|
||||
DroneIngestModuleFactory.getModuleName(), velocity));
|
||||
}
|
||||
|
||||
if (distanceHP != null) {
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DRONE_HP_DISTANCE,
|
||||
DroneIngestModuleFactory.getModuleName(), velocity));
|
||||
}
|
||||
|
||||
if (distanceTraveled != null) {
|
||||
attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DRONE_DISTANCE_TRAVELED,
|
||||
DroneIngestModuleFactory.getModuleName(), velocity));
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the temp path and create the directory if it does not currently
|
||||
* exist.
|
||||
*
|
||||
* @param currentCase Currently open case
|
||||
* @param extractorName Name of extractor
|
||||
*
|
||||
* @return Path of the temp directory for this module
|
||||
*/
|
||||
protected Path getExtractorTempPath() {
|
||||
Path path = Paths.get(currentCase.getTempDirectory(), TEMP_FOLDER_NAME, this.getClass().getCanonicalName());
|
||||
File dir = path.toFile();
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of file in the case temp directory.
|
||||
*
|
||||
* @param context Current ingest context
|
||||
* @param file File to be copied
|
||||
*
|
||||
* @return File copy.
|
||||
*
|
||||
* @throws DroneIngestException
|
||||
*/
|
||||
protected File getTemporaryFile(IngestJobContext context, AbstractFile file) throws DroneIngestException {
|
||||
String tempFileName = file.getName() + file.getId() + file.getNameExtension();
|
||||
|
||||
Path tempFilePath = Paths.get(getExtractorTempPath().toString(), tempFileName);
|
||||
|
||||
try {
|
||||
ContentUtils.writeToFile(file, tempFilePath.toFile(), context::dataSourceIngestIsCancelled);
|
||||
} catch (IOException ex) {
|
||||
throw new DroneIngestException(String.format("Unable to create temp file %s for abstract file %s", tempFilePath.toString(), file.getName()), ex);
|
||||
}
|
||||
|
||||
return tempFilePath.toFile();
|
||||
}
|
||||
|
||||
}
|
48
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestException.java
Executable file
48
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestException.java
Executable file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.drones;
|
||||
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException;
|
||||
|
||||
/**
|
||||
* IngestModuleException wrapper class.
|
||||
*/
|
||||
public class DroneIngestException extends IngestModuleException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Create exception containing the error message
|
||||
*
|
||||
* @param msg the message
|
||||
*/
|
||||
public DroneIngestException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create exception containing the error message and cause exception
|
||||
*
|
||||
* @param msg the message
|
||||
* @param ex cause exception
|
||||
*/
|
||||
public DroneIngestException(String msg, Exception ex) {
|
||||
super(msg, ex);
|
||||
}
|
||||
}
|
93
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestModule.java
Executable file
93
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestModule.java
Executable file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.drones;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.autopsy.ingest.IngestMessage;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
|
||||
/**
|
||||
* Drone file ingest module.
|
||||
*
|
||||
*/
|
||||
public final class DroneIngestModule implements DataSourceIngestModule {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DroneIngestModule.class.getName());
|
||||
final private List<DroneExtractor> extractors;
|
||||
|
||||
private IngestJobContext context;
|
||||
|
||||
/**
|
||||
* Construct a new drone ingest module.
|
||||
*/
|
||||
DroneIngestModule() {
|
||||
extractors = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||
this.context = context;
|
||||
|
||||
extractors.add(new DATExtractor());
|
||||
}
|
||||
|
||||
@Messages({
|
||||
"DroneIngestModule_process_start=Started {0}"
|
||||
})
|
||||
@Override
|
||||
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
|
||||
ProcessResult processResult = ProcessResult.OK;
|
||||
|
||||
IngestServices services = IngestServices.getInstance();
|
||||
|
||||
services.postMessage(IngestMessage.createMessage(
|
||||
IngestMessage.MessageType.INFO,
|
||||
DroneIngestModuleFactory.getModuleName(),
|
||||
Bundle.DroneIngestModule_process_start(dataSource.getName())));
|
||||
|
||||
progressBar.switchToIndeterminate();
|
||||
|
||||
for (DroneExtractor extractor : extractors) {
|
||||
|
||||
if (context.dataSourceIngestIsCancelled()) {
|
||||
logger.log(Level.INFO, "Drone ingest has been canceled, quitting before {0}", extractor.getName()); //NON-NLS
|
||||
break;
|
||||
}
|
||||
|
||||
progressBar.progress(extractor.getName());
|
||||
|
||||
try {
|
||||
extractor.process(dataSource, context, progressBar);
|
||||
} catch (DroneIngestException ex) {
|
||||
logger.log(Level.SEVERE, String.format("Exception thrown from drone extractor %s", extractor.getName()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
return processResult;
|
||||
}
|
||||
|
||||
}
|
72
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestModuleFactory.java
Executable file
72
Core/src/org/sleuthkit/autopsy/modules/drones/DroneIngestModuleFactory.java
Executable file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2020 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.drones;
|
||||
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.sleuthkit.autopsy.coreutils.Version;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
|
||||
/**
|
||||
* Drone file ingest module factory.
|
||||
*/
|
||||
@ServiceProvider(service = IngestModuleFactory.class)
|
||||
public class DroneIngestModuleFactory extends IngestModuleFactoryAdapter {
|
||||
|
||||
@Messages({
|
||||
"DroneIngestModule_Name=Drone",
|
||||
"DroneIngestModule_Description=Description"
|
||||
})
|
||||
|
||||
/**
|
||||
* Helper function for returning the name of this module.
|
||||
*/
|
||||
static String getModuleName() {
|
||||
return Bundle.DroneIngestModule_Name();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleDisplayName() {
|
||||
return getModuleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleDescription() {
|
||||
return Bundle.DroneIngestModule_Description();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleVersionNumber() {
|
||||
return Version.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataSourceIngestModuleFactory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings ingestJobOptions) {
|
||||
return new DroneIngestModule();
|
||||
}
|
||||
|
||||
}
|
BIN
thirdparty/DatCon/3.6.9/DatCon.jar
vendored
Executable file
BIN
thirdparty/DatCon/3.6.9/DatCon.jar
vendored
Executable file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user