diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/AndroidIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/android/AndroidIngestModule.java new file mode 100755 index 0000000000..7966be7abb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/AndroidIngestModule.java @@ -0,0 +1,156 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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 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("
  • ").append(msg).append("
  • \n"); //NON-NLS + } + errorMessage.append("\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; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/AndroidModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/android/AndroidModuleFactory.java new file mode 100755 index 0000000000..1a809f98ae --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/AndroidModuleFactory.java @@ -0,0 +1,62 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java new file mode 100755 index 0000000000..69add3d978 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java @@ -0,0 +1,113 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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); + } + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/android/Bundle.properties new file mode 100755 index 0000000000..ce4df81615 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/Bundle.properties @@ -0,0 +1,2 @@ +AndroidModuleFactory.moduleName=Android Analyzer +AndroidModuleFactory.moduleDescription=Extracts system and third-party app data. diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java new file mode 100755 index 0000000000..89d5472f25 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java @@ -0,0 +1,134 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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(); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java new file mode 100755 index 0000000000..481d3aa23c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java @@ -0,0 +1,132 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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); + } + } + + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java new file mode 100755 index 0000000000..173d506372 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java @@ -0,0 +1,140 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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); + } + } + + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java new file mode 100755 index 0000000000..0174b81e1f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java @@ -0,0 +1,147 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java new file mode 100755 index 0000000000..23e4f07695 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java @@ -0,0 +1,133 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java new file mode 100755 index 0000000000..bbf47f1223 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java @@ -0,0 +1,122 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java new file mode 100755 index 0000000000..1abe891f09 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java @@ -0,0 +1,116 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/iOS/Bundle.properties new file mode 100755 index 0000000000..183b0ba88a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/Bundle.properties @@ -0,0 +1,2 @@ +iOSModuleFactory.moduleName=iOS Analyzer +iOSModuleFactory.moduleDescription=Extracts system and 3rd party app data diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java new file mode 100755 index 0000000000..7de74ab10d --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java @@ -0,0 +1,131 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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); + } + + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java new file mode 100755 index 0000000000..0e7ee51e76 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java @@ -0,0 +1,199 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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"); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java new file mode 100755 index 0000000000..c2881dedab --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java @@ -0,0 +1,132 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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); + } + + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/iOSIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/iOSIngestModule.java new file mode 100755 index 0000000000..16b4e1689f --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/iOSIngestModule.java @@ -0,0 +1,50 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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 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; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/iOSModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/iOSModuleFactory.java new file mode 100755 index 0000000000..4723407df0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/iOSModuleFactory.java @@ -0,0 +1,63 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit 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(); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java index 5dc0fd2381..e2d109599a 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java @@ -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 = "