mirror of
https://github.com/elisspace/autopsy.git
synced 2026-10-02 07:19:53 +00:00
Merge branch 'android-module' into develop
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.autopsy.ingest.IngestMessage;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
|
||||
class AndroidIngestModule implements DataSourceIngestModule {
|
||||
|
||||
private static final HashMap<Long, Long> fileCountsForIngestJobs = new HashMap<>();
|
||||
private IngestJobContext context = null;
|
||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
||||
private static final Logger logger = Logger.getLogger(AndroidIngestModule.class.getName());
|
||||
private IngestServices services = IngestServices.getInstance();
|
||||
|
||||
@Override
|
||||
public void startUp(IngestJobContext context) throws IngestModuleException {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
|
||||
services.postMessage(IngestMessage.createMessage(IngestMessage.MessageType.INFO, AndroidModuleFactory.getModuleName(), "Started Analysis"));
|
||||
|
||||
ArrayList<String> errors = new ArrayList<>();
|
||||
progressBar.switchToDeterminate(9);
|
||||
|
||||
try {
|
||||
ContactAnalyzer.findContacts();
|
||||
progressBar.progress(1);
|
||||
if (context.isJobCancelled()) {
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.add("Error getting Contacts");
|
||||
}
|
||||
|
||||
try {
|
||||
CallLogAnalyzer.findCallLogs();
|
||||
progressBar.progress(2);
|
||||
if (context.isJobCancelled()) {
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.add("Error getting Call Logs");
|
||||
}
|
||||
|
||||
try {
|
||||
TextMessageAnalyzer.findTexts();
|
||||
progressBar.progress(3);
|
||||
if (context.isJobCancelled()) {
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.add("Error getting Text Messages");
|
||||
}
|
||||
|
||||
try {
|
||||
TangoMessageAnalyzer.findTangoMessages();
|
||||
progressBar.progress(4);
|
||||
if (context.isJobCancelled()) {
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.add("Error getting Tango Messages");
|
||||
}
|
||||
|
||||
try {
|
||||
WWFMessageAnalyzer.findWWFMessages();
|
||||
progressBar.progress(5);
|
||||
if (context.isJobCancelled()) {
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.add("Error getting Words with Friends Messages");
|
||||
}
|
||||
|
||||
try {
|
||||
GoogleMapLocationAnalyzer.findGeoLocations();
|
||||
progressBar.progress(6);
|
||||
if (context.isJobCancelled()) {
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.add("Error getting Google Map Locations");
|
||||
}
|
||||
|
||||
try {
|
||||
BrowserLocationAnalyzer.findGeoLocations();
|
||||
progressBar.progress(7);
|
||||
if (context.isJobCancelled()) {
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
errors.add("Error getting Browser Locations");
|
||||
}
|
||||
|
||||
try {
|
||||
CacheLocationAnalyzer.findGeoLocations();
|
||||
progressBar.progress(8);
|
||||
} catch (Exception e) {
|
||||
errors.add("Error getting Cache Locations");
|
||||
}
|
||||
|
||||
// create the final message for inbox
|
||||
StringBuilder errorMessage = new StringBuilder();
|
||||
String errorMsgSubject;
|
||||
IngestMessage.MessageType msgLevel = IngestMessage.MessageType.INFO;
|
||||
if (errors.isEmpty() == false) {
|
||||
msgLevel = IngestMessage.MessageType.ERROR;
|
||||
errorMessage.append("Errors were encountered");
|
||||
for (String msg : errors) {
|
||||
errorMessage.append("<li>").append(msg).append("</li>\n"); //NON-NLS
|
||||
}
|
||||
errorMessage.append("</ul>\n"); //NON-NLS
|
||||
|
||||
if (errors.size() == 1) {
|
||||
errorMsgSubject = "One error was found";
|
||||
} else {
|
||||
errorMsgSubject = "errors found: " + errors.size();
|
||||
}
|
||||
} else {
|
||||
errorMessage.append("No errors");
|
||||
errorMsgSubject = "No errors";
|
||||
}
|
||||
|
||||
services.postMessage(IngestMessage.createMessage(msgLevel, AndroidModuleFactory.getModuleName(), "Finished Analysis: " + errorMsgSubject, errorMessage.toString()));
|
||||
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
|
||||
@ServiceProvider(service = IngestModuleFactory.class) //
|
||||
public class AndroidModuleFactory extends IngestModuleFactoryAdapter {
|
||||
|
||||
private static final String VERSION_NUMBER = "1.0.0";
|
||||
|
||||
static String getModuleName() {
|
||||
return NbBundle.getMessage(AndroidModuleFactory.class, "AndroidModuleFactory.moduleName");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleDisplayName() {
|
||||
return getModuleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleDescription() {
|
||||
return NbBundle.getMessage(AndroidModuleFactory.class, "AndroidModuleFactory.moduleDescription");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleVersionNumber() {
|
||||
return VERSION_NUMBER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataSourceIngestModuleFactory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) {
|
||||
return new AndroidIngestModule();
|
||||
}
|
||||
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class BrowserLocationAnalyzer {
|
||||
|
||||
private static final String moduleName = AndroidModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(BrowserLocationAnalyzer.class.getName());
|
||||
|
||||
public static void findGeoLocations() {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
List<AbstractFile> abstractFiles = skCase.findAllFilesWhere("name LIKE 'CachedGeoposition%.db'"); //get exact file names
|
||||
|
||||
for (AbstractFile abstractFile : abstractFiles) {
|
||||
try {
|
||||
if (abstractFile.getSize() == 0) {
|
||||
continue;
|
||||
}
|
||||
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
|
||||
ContentUtils.writeToFile(abstractFile, jFile);
|
||||
findGeoLocationsInDB(jFile.toString(), abstractFile);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Browser Location files", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding Browser Location files", e);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void findGeoLocationsInDB(String DatabasePath, AbstractFile f) {
|
||||
Connection connection = null;
|
||||
ResultSet resultSet = null;
|
||||
Statement statement = null;
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error connecting to sql database", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resultSet = statement.executeQuery(
|
||||
"Select timestamp, latitude, longitude, accuracy FROM CachedPosition;");
|
||||
|
||||
while (resultSet.next()) {
|
||||
Long timestamp = Long.valueOf(resultSet.getString("timestamp")) / 1000;
|
||||
double latitude = Double.valueOf(resultSet.getString("latitude"));
|
||||
double longitude = Double.valueOf(resultSet.getString("longitude"));
|
||||
|
||||
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT);
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, latitude));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, longitude));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, timestamp));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "Browser Location History"));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error Putting artifacts to Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing database", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
AndroidModuleFactory.moduleName=Android Analyzer
|
||||
AndroidModuleFactory.moduleDescription=Extracts system and third-party app data.
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class CacheLocationAnalyzer {
|
||||
|
||||
private static final String moduleName = AndroidModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(CacheLocationAnalyzer.class.getName());
|
||||
|
||||
public static void findGeoLocations() {
|
||||
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
List<AbstractFile> abstractFiles = skCase.findAllFilesWhere("name ='cache.cell' OR name='cache.wifi'"); //get exact file names
|
||||
|
||||
for (AbstractFile abstractFile : abstractFiles) {
|
||||
try {
|
||||
if (abstractFile.getSize() == 0) {
|
||||
continue;
|
||||
}
|
||||
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
|
||||
ContentUtils.writeToFile(abstractFile, jFile);
|
||||
|
||||
findGeoLocationsInFile(jFile, abstractFile);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing cached Location files", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding cached Location files", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void findGeoLocationsInFile(File file, AbstractFile f) {
|
||||
|
||||
byte[] bytes; // will temporarily hold bytes to be converted into the correct data types
|
||||
|
||||
try {
|
||||
InputStream inputStream = new FileInputStream(file);
|
||||
|
||||
bytes = new byte[2]; // version
|
||||
inputStream.read(bytes);
|
||||
|
||||
bytes = new byte[2];
|
||||
inputStream.read(bytes); //number of location entries
|
||||
|
||||
int iterations = new BigInteger(bytes).intValue();
|
||||
|
||||
for (int i = 0; i < iterations; i++) { //loop through every entry
|
||||
bytes = new byte[2];
|
||||
inputStream.read(bytes);
|
||||
|
||||
bytes = new byte[1];
|
||||
inputStream.read(bytes);
|
||||
while (new BigInteger(bytes).intValue() != 0) { //pass through non important values until the start of accuracy(around 7-10 bytes)
|
||||
inputStream.read(bytes);
|
||||
}
|
||||
bytes = new byte[3];
|
||||
inputStream.read(bytes);
|
||||
if (new BigInteger(bytes).intValue() <= 0) {//This refers to a location that could not be calculated.
|
||||
bytes = new byte[28]; //read rest of the row's bytes
|
||||
inputStream.read(bytes);
|
||||
continue;
|
||||
}
|
||||
String accuracy = "" + new BigInteger(bytes).intValue();
|
||||
|
||||
bytes = new byte[4];
|
||||
inputStream.read(bytes);
|
||||
String confidence = "" + new BigInteger(bytes).intValue();
|
||||
|
||||
bytes = new byte[8];
|
||||
inputStream.read(bytes);
|
||||
double latitude = toDouble(bytes);
|
||||
|
||||
bytes = new byte[8];
|
||||
inputStream.read(bytes);
|
||||
double longitude = toDouble(bytes);
|
||||
|
||||
bytes = new byte[8];
|
||||
inputStream.read(bytes);
|
||||
Long timestamp = new BigInteger(bytes).longValue() / 1000;
|
||||
|
||||
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT);
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, latitude));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, longitude));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, timestamp));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, file.getName() + " Location History"));
|
||||
|
||||
//Not storing these for now.
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID(),moduleName, confidence));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Cached GPS locations to Blackboard", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static double toDouble(byte[] bytes) {
|
||||
return ByteBuffer.wrap(bytes).getDouble();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class CallLogAnalyzer {
|
||||
|
||||
private static final String moduleName = AndroidModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(CallLogAnalyzer.class.getName());
|
||||
|
||||
public static void findCallLogs() {
|
||||
List<AbstractFile> absFiles;
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
absFiles = skCase.findAllFilesWhere("name ='contacts2.db' OR name ='contacts.db'"); //get exact file names
|
||||
if (absFiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (AbstractFile abstractFile : absFiles) {
|
||||
try {
|
||||
File jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
|
||||
ContentUtils.writeToFile(abstractFile, jFile);
|
||||
|
||||
findCallLogsInDB(jFile.toString(), abstractFile);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Call logs", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding Call logs", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void findCallLogsInDB(String DatabasePath, AbstractFile f) {
|
||||
Connection connection = null;
|
||||
ResultSet resultSet = null;
|
||||
Statement statement = null;
|
||||
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resultSet = statement.executeQuery(
|
||||
"SELECT number,date,duration,type, name FROM calls ORDER BY date DESC;");
|
||||
|
||||
BlackboardArtifact bba;
|
||||
|
||||
while (resultSet.next()) {
|
||||
// name of person dialed or called. null if unregistered
|
||||
String name = resultSet.getString("name");
|
||||
String number = resultSet.getString("number");
|
||||
//duration of call in seconds
|
||||
Long duration = Long.valueOf(resultSet.getString("duration"));
|
||||
Long date = Long.valueOf(resultSet.getString("date")) / 1000;
|
||||
|
||||
String direction = "";
|
||||
switch (Integer.valueOf(resultSet.getString("type"))) {
|
||||
case 1:
|
||||
direction = "Incoming";
|
||||
break;
|
||||
case 2:
|
||||
direction = "Outgoing";
|
||||
break;
|
||||
case 3:
|
||||
direction = "Missed";
|
||||
break;
|
||||
}
|
||||
|
||||
bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); //create a call log and then add attributes from result set.
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), moduleName, number));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), moduleName, date));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(), moduleName, duration + date));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, direction));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Call logs to the Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing the database", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class ContactAnalyzer {
|
||||
|
||||
private static final String moduleName = AndroidModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(ContactAnalyzer.class.getName());
|
||||
|
||||
public static void findContacts() {
|
||||
|
||||
List<AbstractFile> absFiles;
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
absFiles = skCase.findAllFilesWhere("name ='contacts2.db' OR name ='contacts.db'"); //get exact file names
|
||||
if (absFiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (AbstractFile AF : absFiles) {
|
||||
try {
|
||||
File jFile = new File(Case.getCurrentCase().getTempDirectory(), AF.getName());
|
||||
ContentUtils.writeToFile(AF, jFile);
|
||||
findContactsInDB(jFile.toString(), AF);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Contacts", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding Contacts", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param DatabasePath
|
||||
* @param fId Will create artifact from a database given by the path The
|
||||
* fileId will be the Abstract file associated with the artifacts
|
||||
*/
|
||||
private static void findContactsInDB(String DatabasePath, AbstractFile f) {
|
||||
Connection connection = null;
|
||||
ResultSet resultSet = null;
|
||||
Statement statement = null;
|
||||
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// get display_name, mimetype(email or phone number) and data1 (phonenumber or email address depending on mimetype)
|
||||
//sorted by name, so phonenumber/email would be consecutive for a person if they exist.
|
||||
resultSet = statement.executeQuery(
|
||||
"SELECT mimetype,data1, name_raw_contact.display_name AS display_name \n"
|
||||
+ "FROM raw_contacts JOIN contacts ON (raw_contacts.contact_id=contacts._id) \n"
|
||||
+ "JOIN raw_contacts AS name_raw_contact ON(name_raw_contact_id=name_raw_contact._id) "
|
||||
+ "LEFT OUTER JOIN data ON (data.raw_contact_id=raw_contacts._id) \n"
|
||||
+ "LEFT OUTER JOIN mimetypes ON (data.mimetype_id=mimetypes._id) \n"
|
||||
+ "WHERE mimetype = 'vnd.android.cursor.item/phone_v2' OR mimetype = 'vnd.android.cursor.item/email_v2'\n"
|
||||
+ "ORDER BY name_raw_contact.display_name ASC;");
|
||||
|
||||
BlackboardArtifact bba;
|
||||
bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||
String name;
|
||||
String oldName = "";
|
||||
String mimetype; // either phone or email
|
||||
String data1; // the phone number or email
|
||||
while (resultSet.next()) {
|
||||
name = resultSet.getString("display_name");
|
||||
data1 = resultSet.getString("data1");
|
||||
mimetype = resultSet.getString("mimetype");
|
||||
// System.out.println(resultSet.getString("data1") + resultSet.getString("mimetype") + resultSet.getString("display_name")); //Test code
|
||||
if (name.equals(oldName) == false) {
|
||||
bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name));
|
||||
}
|
||||
if (mimetype.equals("vnd.android.cursor.item/phone_v2")) {
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), moduleName, data1));
|
||||
} else {
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getTypeID(), moduleName, data1));
|
||||
}
|
||||
oldName = name;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Contacts to Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing database", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class GoogleMapLocationAnalyzer {
|
||||
|
||||
private static final String moduleName = AndroidModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(GoogleMapLocationAnalyzer.class.getName());
|
||||
|
||||
public static void findGeoLocations() {
|
||||
List<AbstractFile> absFiles;
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
absFiles = skCase.findAllFilesWhere("name ='da_destination_history'"); //get exact file name
|
||||
if (absFiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (AbstractFile abstractFile : absFiles) {
|
||||
try {
|
||||
File jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
|
||||
ContentUtils.writeToFile(abstractFile, jFile);
|
||||
findGeoLocationsInDB(jFile.toString(), abstractFile);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Google map locations", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding Google map locations", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void findGeoLocationsInDB(String DatabasePath, AbstractFile f) {
|
||||
Connection connection = null;
|
||||
ResultSet resultSet = null;
|
||||
Statement statement = null;
|
||||
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resultSet = statement.executeQuery(
|
||||
"Select time,dest_lat,dest_lng,dest_title,dest_address,source_lat,source_lng FROM destination_history;");
|
||||
|
||||
while (resultSet.next()) {
|
||||
Long time = Long.valueOf(resultSet.getString("time")) / 1000;
|
||||
String dest_title = resultSet.getString("dest_title");
|
||||
String dest_address = resultSet.getString("dest_address");
|
||||
|
||||
double dest_lat = convertGeo(resultSet.getString("dest_lat"));
|
||||
double dest_lng = convertGeo(resultSet.getString("dest_lng"));
|
||||
double source_lat = convertGeo(resultSet.getString("source_lat"));
|
||||
double source_lng = convertGeo(resultSet.getString("source_lng"));
|
||||
|
||||
|
||||
// bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT);//src
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY.getTypeID(), moduleName, "Source"));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, source_lat));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, source_lng));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, time));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DESCRIPTION.getTypeID(), moduleName, "Google Maps History"));
|
||||
//
|
||||
// bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT);//dest
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY.getTypeID(), moduleName, "Destination"));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, time));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID(), moduleName, dest_lat));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID(), moduleName, dest_lng));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, dest_title));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION.getTypeID(), moduleName, dest_address));
|
||||
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "Google Maps History"));
|
||||
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE);
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY.getTypeID(), moduleName, "Destination"));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, time));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END.getTypeID(), moduleName, dest_lat));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END.getTypeID(), moduleName, dest_lng));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID(), moduleName, source_lat));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START.getTypeID(), moduleName, source_lng));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, dest_title));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION.getTypeID(), moduleName, dest_address));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME.getTypeID(), moduleName, "Google Maps History"));
|
||||
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Google map locations to the Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing the database", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//add periods 6 decimal places before the end.
|
||||
private static double convertGeo(String s) {
|
||||
if (s.length() > 6)
|
||||
return Double.valueOf(s.substring(0, s.length() - 6) + "." + s.substring(s.length() - 6, s.length()));
|
||||
else
|
||||
return Double.valueOf(s);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class TangoMessageAnalyzer {
|
||||
|
||||
private static final String moduleName = AndroidModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(TangoMessageAnalyzer.class.getName());
|
||||
|
||||
public static void findTangoMessages() {
|
||||
List<AbstractFile> absFiles;
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
absFiles = skCase.findAllFilesWhere("name ='tc.db' "); //get exact file names
|
||||
for (AbstractFile abstractFile : absFiles) {
|
||||
try {
|
||||
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
|
||||
ContentUtils.writeToFile(abstractFile, jFile);
|
||||
findTangoMessagesInDB(jFile.toString(), abstractFile);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Tango messages", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding Tango messages", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void findTangoMessagesInDB(String DatabasePath, AbstractFile f) {
|
||||
Connection connection = null;
|
||||
ResultSet resultSet = null;
|
||||
Statement statement = null;
|
||||
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resultSet = statement.executeQuery(
|
||||
"Select conv_id, create_time,direction,payload FROM messages ORDER BY create_time DESC;");
|
||||
|
||||
String conv_id; // seems to wrap around the message found in payload after decoding from base-64
|
||||
String direction; // 1 incoming, 2 outgoing
|
||||
String payload; // seems to be a base64 message wrapped by the conv_id
|
||||
|
||||
while (resultSet.next()) {
|
||||
conv_id = resultSet.getString("conv_id");
|
||||
Long create_time = Long.valueOf(resultSet.getString("create_time")) / 1000;
|
||||
if (resultSet.getString("direction").equals("1")) {
|
||||
direction = "Incoming";
|
||||
} else {
|
||||
direction = "Outgoing";
|
||||
}
|
||||
payload = resultSet.getString("payload");
|
||||
|
||||
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create a call log and then add attributes from result set.
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, create_time));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, direction));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID(), moduleName, decodeMessage(conv_id, payload)));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE.getTypeID(), moduleName, "Tango Message"));
|
||||
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Tango messages to the Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing database", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//take the message string which is wrapped by a certain string, and return the text enclosed.
|
||||
private static String decodeMessage(String wrapper, String message) {
|
||||
String result = "";
|
||||
byte[] decoded = Base64.decodeBase64(message);
|
||||
try {
|
||||
String Z = new String(decoded, "UTF-8");
|
||||
result = Z.split(wrapper)[1];
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error decoding a Tango message", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class TextMessageAnalyzer {
|
||||
|
||||
private static final String moduleName = AndroidModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(TextMessageAnalyzer.class.getName());
|
||||
|
||||
public static void findTexts() {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
List<AbstractFile> absFiles = skCase.findAllFilesWhere("name ='mmssms.db'"); //get exact file name
|
||||
|
||||
for (AbstractFile abstractFile : absFiles) {
|
||||
try {
|
||||
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
|
||||
ContentUtils.writeToFile(abstractFile, jFile);
|
||||
findTextsInDB(jFile.toString(), abstractFile);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing text messages", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding text messages", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void findTextsInDB(String DatabasePath, AbstractFile f) {
|
||||
Connection connection = null;
|
||||
ResultSet resultSet = null;
|
||||
Statement statement = null;
|
||||
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resultSet = statement.executeQuery(
|
||||
"Select address,date,type,subject,body FROM sms;");
|
||||
|
||||
String address; // may be phone number, or other addresses
|
||||
|
||||
String direction; // message received in inbox = 1, message sent = 2
|
||||
String subject;//message subject
|
||||
String body; //message body
|
||||
while (resultSet.next()) {
|
||||
address = resultSet.getString("address");
|
||||
Long date = Long.valueOf(resultSet.getString("date")) / 1000;
|
||||
if (resultSet.getString("type").equals("1")) {
|
||||
direction = "Incoming";
|
||||
} else {
|
||||
direction = "Outgoing";
|
||||
}
|
||||
subject = resultSet.getString("subject");
|
||||
body = resultSet.getString("body");
|
||||
|
||||
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create Message artifact and then add attributes from result set.
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), moduleName, address));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, date));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, direction));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT.getTypeID(), moduleName, subject));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID(), moduleName, body));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE.getTypeID(), moduleName, "SMS Message"));
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing text messages to Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing database", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.android;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class WWFMessageAnalyzer {
|
||||
|
||||
private static final String moduleName = AndroidModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(WWFMessageAnalyzer.class.getName());
|
||||
|
||||
public static void findWWFMessages() {
|
||||
List<AbstractFile> absFiles;
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
absFiles = skCase.findAllFilesWhere("name ='WordsFramework' "); //get exact file names
|
||||
|
||||
for (AbstractFile abstractFile : absFiles) {
|
||||
try {
|
||||
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
|
||||
ContentUtils.writeToFile(abstractFile, jFile);
|
||||
|
||||
findWWFMessagesInDB(jFile.toString(), abstractFile);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing WWF messages", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding WWF messages", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void findWWFMessagesInDB(String DatabasePath, AbstractFile f) {
|
||||
Connection connection = null;
|
||||
ResultSet resultSet = null;
|
||||
Statement statement = null;
|
||||
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
resultSet = statement.executeQuery(
|
||||
"SELECT message,created_at,user_id,game_id FROM chat_messages ORDER BY game_id DESC, created_at DESC;");
|
||||
|
||||
String message; // WWF Message
|
||||
String user_id; // the ID of the user who sent the message.
|
||||
String game_id; // ID of the game which the the message was sent.
|
||||
|
||||
while (resultSet.next()) {
|
||||
message = resultSet.getString("message");
|
||||
Long created_at = Long.valueOf(resultSet.getString("created_at")) / 1000;
|
||||
user_id = resultSet.getString("user_id");
|
||||
game_id = resultSet.getString("game_id");
|
||||
|
||||
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create a call log and then add attributes from result set.
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, created_at));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, user_id));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID.getTypeID(), moduleName, game_id));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID(), moduleName, message));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE.getTypeID(), moduleName, "Words With Friends Message"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing WWF messages to the Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
if (resultSet != null) {
|
||||
resultSet.close();
|
||||
}
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing database", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
iOSModuleFactory.moduleName=iOS Analyzer
|
||||
iOSModuleFactory.moduleDescription=Extracts system and 3rd party app data
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.iOS;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class CallLogAnalyzer {
|
||||
|
||||
private Connection connection = null;
|
||||
private ResultSet resultSet = null;
|
||||
private Statement statement = null;
|
||||
private String dbPath = "";
|
||||
private long fileId = 0;
|
||||
private java.io.File jFile = null;
|
||||
private String moduleName = iOSModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(CallLogAnalyzer.class.getName());
|
||||
|
||||
public void findCallLogs() {
|
||||
List<AbstractFile> absFiles;
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
absFiles = skCase.findAllFilesWhere("name ='contacts2.db' OR name ='contacts.db'"); //get exact file names
|
||||
if (absFiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (AbstractFile AF : absFiles) {
|
||||
try {
|
||||
jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", ""));
|
||||
ContentUtils.writeToFile(AF, jFile);
|
||||
dbPath = jFile.toString(); //path of file as string
|
||||
fileId = AF.getId();
|
||||
findCallLogsInDB(dbPath, fileId);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Call logs", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding Call logs", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void findCallLogsInDB(String DatabasePath, long fId) {
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
}
|
||||
|
||||
Case currentCase = Case.getCurrentCase();
|
||||
SleuthkitCase skCase = currentCase.getSleuthkitCase();
|
||||
try {
|
||||
AbstractFile f = skCase.getAbstractFileById(fId);
|
||||
try {
|
||||
resultSet = statement.executeQuery(
|
||||
"SELECT number,date,duration,type, name FROM calls ORDER BY date DESC;");
|
||||
|
||||
BlackboardArtifact bba;
|
||||
String name; // name of person dialed or called. null if unregistered
|
||||
String number; //string phone number
|
||||
String duration; //duration of call in seconds
|
||||
String date; // Unix time
|
||||
String type; // 1 incoming, 2 outgoing, 3 missed
|
||||
|
||||
while (resultSet.next()) {
|
||||
name = resultSet.getString("name");
|
||||
number = resultSet.getString("number");
|
||||
duration = resultSet.getString("duration");
|
||||
date = resultSet.getString("date");
|
||||
type = resultSet.getString("type");
|
||||
|
||||
bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG); //create a call log and then add attributes from result set.
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), moduleName, number));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(), moduleName, date));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID(), moduleName, duration + date));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, type));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name));
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Call logs to the Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
resultSet.close();
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing the database", e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Call logs to the Blackboard", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.iOS;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
||||
|
||||
class ContactAnalyzer {
|
||||
|
||||
private Connection connection = null;
|
||||
private ResultSet resultSet = null;
|
||||
private Statement statement = null;
|
||||
private String dbPath = "";
|
||||
private long fileId = 0;
|
||||
private java.io.File jFile = null;
|
||||
private String moduleName = iOSModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(ContactAnalyzer.class.getName());
|
||||
|
||||
public void findContacts() {
|
||||
|
||||
List<AbstractFile> absFiles;
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
absFiles = skCase.findAllFilesWhere("name LIKE '%call_history%' "); //get exact file names
|
||||
if (absFiles.isEmpty()) { //asdfkjasfakljsdfhlaksdjfhasdlkjf
|
||||
return;
|
||||
}
|
||||
for (AbstractFile AF : absFiles) {
|
||||
try {
|
||||
jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", ""));
|
||||
//jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), i+".txt");
|
||||
ContentUtils.writeToFile(AF, jFile);
|
||||
//copyFileUsingStreams(AF,jFile);
|
||||
//copyFileUsingStream(AF,jFile);
|
||||
dbPath = jFile.toString(); //path of file as string
|
||||
fileId = AF.getId();
|
||||
//findContactsInDB(dbPath, fileId);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Contacts", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding Contacts", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param DatabasePath
|
||||
* @param fId Will create artifact from a database given by the path The
|
||||
* fileId will be the Abstract file associated with the artifacts
|
||||
*/
|
||||
private void findContactsInDB(String DatabasePath, long fId) {
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
}
|
||||
|
||||
Case currentCase = Case.getCurrentCase();
|
||||
SleuthkitCase skCase = currentCase.getSleuthkitCase();
|
||||
try {
|
||||
AbstractFile f = skCase.getAbstractFileById(fId);
|
||||
try {
|
||||
// get display_name, mimetype(email or phone number) and data1 (phonenumber or email address depending on mimetype)
|
||||
//sorted by name, so phonenumber/email would be consecutive for a person if they exist.
|
||||
resultSet = statement.executeQuery(
|
||||
"SELECT mimetype,data1, name_raw_contact.display_name AS display_name \n"
|
||||
+ "FROM raw_contacts JOIN contacts ON (raw_contacts.contact_id=contacts._id) \n"
|
||||
+ "JOIN raw_contacts AS name_raw_contact ON(name_raw_contact_id=name_raw_contact._id) "
|
||||
+ "LEFT OUTER JOIN data ON (data.raw_contact_id=raw_contacts._id) \n"
|
||||
+ "LEFT OUTER JOIN mimetypes ON (data.mimetype_id=mimetypes._id) \n"
|
||||
+ "WHERE mimetype = 'vnd.android.cursor.item/phone_v2' OR mimetype = 'vnd.android.cursor.item/email_v2'\n"
|
||||
+ "ORDER BY name_raw_contact.display_name ASC;");
|
||||
|
||||
BlackboardArtifact bba;
|
||||
bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||
String name;
|
||||
String oldName = "";
|
||||
String mimetype; // either phone or email
|
||||
String data1; // the phone number or email
|
||||
while (resultSet.next()) {
|
||||
name = resultSet.getString("display_name");
|
||||
data1 = resultSet.getString("data1");
|
||||
mimetype = resultSet.getString("mimetype");
|
||||
// System.out.println(resultSet.getString("data1") + resultSet.getString("mimetype") + resultSet.getString("display_name")); //Test code
|
||||
if (name.equals(oldName) == false) {
|
||||
bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT);
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID(), moduleName, name));
|
||||
}
|
||||
if (mimetype.equals("vnd.android.cursor.item/phone_v2")) {
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), moduleName, data1));
|
||||
} else {
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL.getTypeID(), moduleName, data1));
|
||||
}
|
||||
oldName = name;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Contacts to Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
resultSet.close();
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing database", e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing Contacts to Blackboard", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void copyFileUsingStream(AbstractFile file, File jFile) throws IOException {
|
||||
InputStream is = new ReadContentInputStream(file);
|
||||
OutputStream os = new FileOutputStream(jFile);
|
||||
byte[] buffer = new byte[8192];
|
||||
int length;
|
||||
try {
|
||||
while ((length = is.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, length);
|
||||
System.out.println(length);
|
||||
os.flush();
|
||||
|
||||
}
|
||||
|
||||
} finally {
|
||||
is.close();
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void copyFileUsingStreams(AbstractFile file, File jFile) {
|
||||
InputStream istream;
|
||||
OutputStream ostream = null;
|
||||
int c;
|
||||
final int EOF = -1;
|
||||
istream = new ReadContentInputStream(file);
|
||||
//File outFile = new File("Data.txt");
|
||||
// System.out.println("Type characters to write in File – Press Ctrl+z to end ");
|
||||
try {
|
||||
ostream = new FileOutputStream(jFile);
|
||||
while ((c = istream.read()) != EOF) {
|
||||
ostream.write(c);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.out.println("Error: " + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
istream.close();
|
||||
ostream.close();
|
||||
} catch (IOException e) {
|
||||
System.out.println("File did not close");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.iOS;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifact;
|
||||
import org.sleuthkit.datamodel.BlackboardAttribute;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
class TextMessageAnalyzer {
|
||||
|
||||
private Connection connection = null;
|
||||
private ResultSet resultSet = null;
|
||||
private Statement statement = null;
|
||||
private String dbPath = "";
|
||||
private long fileId = 0;
|
||||
private java.io.File jFile = null;
|
||||
List<AbstractFile> absFiles;
|
||||
private String moduleName = iOSModuleFactory.getModuleName();
|
||||
private static final Logger logger = Logger.getLogger(TextMessageAnalyzer.class.getName());
|
||||
|
||||
void findTexts() {
|
||||
try {
|
||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
||||
absFiles = skCase.findAllFilesWhere("name ='mmssms.db'"); //get exact file name
|
||||
if (absFiles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (AbstractFile AF : absFiles) {
|
||||
try {
|
||||
jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", ""));
|
||||
ContentUtils.writeToFile(AF, jFile);
|
||||
dbPath = jFile.toString(); //path of file as string
|
||||
fileId = AF.getId();
|
||||
findTextsInDB(dbPath, fileId);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing text messages", e);
|
||||
}
|
||||
}
|
||||
} catch (TskCoreException e) {
|
||||
logger.log(Level.SEVERE, "Error finding text messages", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void findTextsInDB(String DatabasePath, long fId) {
|
||||
if (DatabasePath == null || DatabasePath.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC"); //load JDBC driver
|
||||
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath);
|
||||
statement = connection.createStatement();
|
||||
} catch (ClassNotFoundException | SQLException e) {
|
||||
logger.log(Level.SEVERE, "Error opening database", e);
|
||||
}
|
||||
|
||||
Case currentCase = Case.getCurrentCase();
|
||||
SleuthkitCase skCase = currentCase.getSleuthkitCase();
|
||||
try {
|
||||
AbstractFile f = skCase.getAbstractFileById(fId);
|
||||
try {
|
||||
resultSet = statement.executeQuery(
|
||||
"Select address,date,type,subject,body FROM sms;");
|
||||
|
||||
BlackboardArtifact bba;
|
||||
String address; // may be phone number, or other addresses
|
||||
String date;//unix time
|
||||
String type; // message received in inbox = 1, message sent = 2
|
||||
String subject;//message subject
|
||||
String body; //message body
|
||||
while (resultSet.next()) {
|
||||
address = resultSet.getString("address");
|
||||
date = resultSet.getString("date");
|
||||
type = resultSet.getString("type");
|
||||
subject = resultSet.getString("subject");
|
||||
body = resultSet.getString("body");
|
||||
|
||||
bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); //create Message artifact and then add attributes from result set.
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(), moduleName, address));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(), moduleName, date));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(), moduleName, type));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT.getTypeID(), moduleName, subject));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT.getTypeID(), moduleName, body));
|
||||
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE.getTypeID(), moduleName, "SMS Message"));
|
||||
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing text messages to Blackboard", e);
|
||||
} finally {
|
||||
try {
|
||||
resultSet.close();
|
||||
statement.close();
|
||||
connection.close();
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error closing database", e);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error parsing text messages to Blackboard", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.iOS;
|
||||
|
||||
import java.util.HashMap;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModule;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJobContext;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestServices;
|
||||
|
||||
class iOSIngestModule implements DataSourceIngestModule {
|
||||
|
||||
private static final HashMap<Long, Long> fileCountsForIngestJobs = new HashMap<>();
|
||||
private IngestJobContext context = null;
|
||||
private static final IngestModuleReferenceCounter refCounter = new IngestModuleReferenceCounter();
|
||||
private static final Logger logger = Logger.getLogger(iOSModuleFactory.class.getName());
|
||||
private IngestServices services = IngestServices.getInstance();
|
||||
|
||||
@Override
|
||||
public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IngestModule.ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
|
||||
ContactAnalyzer FindContacts = new ContactAnalyzer();
|
||||
FindContacts.findContacts();
|
||||
return IngestModule.ProcessResult.OK;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 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.iOS;
|
||||
|
||||
import org.openide.util.lookup.ServiceProvider;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactory;
|
||||
import org.sleuthkit.autopsy.ingest.DataSourceIngestModule;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter;
|
||||
import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings;
|
||||
|
||||
|
||||
//@ServiceProvider(service = IngestModuleFactory.class) //
|
||||
public class iOSModuleFactory extends IngestModuleFactoryAdapter {
|
||||
|
||||
private static final String VERSION_NUMBER = "1.0.0";
|
||||
|
||||
static String getModuleName() {
|
||||
return NbBundle.getMessage(iOSModuleFactory.class, "iOSModuleFactory.moduleName");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleDisplayName() {
|
||||
return getModuleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleDescription() {
|
||||
return NbBundle.getMessage(iOSModuleFactory.class, "iOSModuleFactory.moduleDescription");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModuleVersionNumber() {
|
||||
return VERSION_NUMBER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataSourceIngestModuleFactory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) {
|
||||
return new iOSIngestModule();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,6 +95,9 @@ class ReportKML implements GeneralReportModule {
|
||||
progressPanel.increment();
|
||||
|
||||
|
||||
// @@@ BC: I don't get why we do this in two passes.
|
||||
// Why not just print the coordinates as we find them and make some utility methods to do the printing?
|
||||
// Should pull out time values for all of these points and store in TimeSpan element
|
||||
try {
|
||||
|
||||
BufferedWriter out = null;
|
||||
@@ -145,8 +148,70 @@ class ReportKML implements GeneralReportModule {
|
||||
// lat lon path name
|
||||
}
|
||||
}
|
||||
|
||||
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)) {
|
||||
lat = 0;
|
||||
lon = 0;
|
||||
for (BlackboardAttribute attribute : artifact.getAttributes()) {
|
||||
if (attribute.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID()) //latitude
|
||||
{
|
||||
lat = attribute.getValueDouble();
|
||||
}
|
||||
if (attribute.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID()) //longitude
|
||||
{
|
||||
lon = attribute.getValueDouble();
|
||||
}
|
||||
}
|
||||
if (lon != 0 && lat != 0) {
|
||||
out.write(lat + ";" + lon + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)) {
|
||||
lat = 0;
|
||||
lon = 0;
|
||||
double destlat = 0;
|
||||
double destlon = 0;
|
||||
String name = "";
|
||||
String location = "";
|
||||
for (BlackboardAttribute attribute : artifact.getAttributes()) {
|
||||
if (attribute.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID()) //latitude
|
||||
{
|
||||
lat = attribute.getValueDouble();
|
||||
} else if (attribute.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END.getTypeID()) //longitude
|
||||
{
|
||||
destlat = attribute.getValueDouble();
|
||||
} else if (attribute.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START.getTypeID()) //longitude
|
||||
{
|
||||
lon = attribute.getValueDouble();
|
||||
} else if (attribute.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END.getTypeID()) //longitude
|
||||
{
|
||||
destlon = attribute.getValueDouble();
|
||||
} else if (attribute.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID()) //longitude
|
||||
{
|
||||
name = attribute.getValueString();
|
||||
} else if (attribute.getAttributeTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION.getTypeID()) //longitude
|
||||
{
|
||||
location = attribute.getValueString();
|
||||
}
|
||||
}
|
||||
|
||||
// @@@ Shoudl do something more fancy with these in KML and store them as a single point.
|
||||
String display = name;
|
||||
if (display.isEmpty())
|
||||
display = location;
|
||||
|
||||
if (lon != 0 && lat != 0) {
|
||||
out.write(lat + ";" + lon + ";;" + display + " (Start)\n");
|
||||
}
|
||||
if (destlat != 0 && destlon != 0) {
|
||||
out.write(destlat + ";" + destlon + ";;" + display + " (End)\n");
|
||||
}
|
||||
}
|
||||
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
progressPanel.increment();
|
||||
/*
|
||||
* Step 1: generate XML stub
|
||||
@@ -205,28 +270,33 @@ class ReportKML implements GeneralReportModule {
|
||||
String line = reader.readLine();
|
||||
while (line != null) {
|
||||
String[] lineParts = line.split(";");
|
||||
if (lineParts.length == 4) {
|
||||
if (lineParts.length > 1) {
|
||||
String coordinates = lineParts[1].trim() + "," + lineParts[0].trim(); //lat,lon
|
||||
// Placemark
|
||||
Element placemark = new Element("Placemark", ns); //NON-NLS
|
||||
document.addContent(placemark);
|
||||
|
||||
// name
|
||||
Element pmName = new Element("name", ns); //NON-NLS
|
||||
pmName.setText(lineParts[3].trim());
|
||||
placemark.addContent(pmName);
|
||||
if (lineParts.length == 4) {
|
||||
// name
|
||||
Element pmName = new Element("name", ns); //NON-NLS
|
||||
pmName.setText(lineParts[3].trim());
|
||||
placemark.addContent(pmName);
|
||||
|
||||
// Path
|
||||
Element pmPath = new Element("Path", ns); //NON-NLS
|
||||
pmPath.setText(lineParts[2].trim());
|
||||
placemark.addContent(pmPath);
|
||||
String savedPath = lineParts[2].trim();
|
||||
if (savedPath.isEmpty() == false) {
|
||||
// Path
|
||||
Element pmPath = new Element("Path", ns); //NON-NLS
|
||||
pmPath.setText(savedPath);
|
||||
placemark.addContent(pmPath);
|
||||
|
||||
// description
|
||||
Element pmDescription = new Element("description", ns); //NON-NLS
|
||||
String xml = "<![CDATA[ \n" + " <img src='file:///" + lineParts[2].trim() + "' width='400' /><br/> \n"; //NON-NLS
|
||||
StringEscapeUtils.unescapeXml(xml);
|
||||
pmDescription.setText(xml);
|
||||
placemark.addContent(pmDescription);
|
||||
// description
|
||||
Element pmDescription = new Element("description", ns); //NON-NLS
|
||||
String xml = "<![CDATA[ \n" + " <img src='file:///" + savedPath + "' width='400' /><br/> \n"; //NON-NLS
|
||||
StringEscapeUtils.unescapeXml(xml);
|
||||
pmDescription.setText(xml);
|
||||
placemark.addContent(pmDescription);
|
||||
}
|
||||
}
|
||||
|
||||
// styleUrl
|
||||
Element pmStyleUrl = new Element("styleUrl", ns); //NON-NLS
|
||||
@@ -244,6 +314,7 @@ class ReportKML implements GeneralReportModule {
|
||||
pmPoint.addContent(pmCoordinates);
|
||||
|
||||
}
|
||||
|
||||
// read the next line
|
||||
line = reader.readLine();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user