Got Sofie's branch

This commit is contained in:
Eugene Livis 2017-04-21 12:38:36 -04:00
commit 0760cacf28
19 changed files with 1240 additions and 992 deletions

View File

@ -0,0 +1,120 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
from java.io import File
from java.lang import Class
from java.lang import ClassNotFoundException
from java.lang import Double
from java.lang import Long
from java.sql import Connection
from java.sql import DriverManager
from java.sql import ResultSet
from java.sql import SQLException
from java.sql import Statement
from java.util.logging import Level
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Blackboard
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.datamodel import AbstractFile
from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException
import traceback
import general
"""
Analyzes database created by browser that stores GEO location info.
"""
class BrowserLocationAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__)
def analyze(self, dataSource, fileManager, context):
try:
abstractFiles = fileManager.findFiles(dataSource, "CachedGeoposition%.db")
for abstractFile in abstractFiles:
if abstractFile.getSize() == 0:
continue
try:
jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled)
self.__findGeoLocationsInDB(jFile.toString(), abstractFile)
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing browser location files", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error finding browser location files", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
def __findGeoLocationsInDB(self, databasePath, abstractFile):
if not databasePath:
return
try:
Class.forName("org.sqlite.JDBC") #load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
statement = connection.createStatement()
except (ClassNotFoundException, SQLException) as ex:
self._logger.log(Level.SEVERE, "Error connecting to SQL database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
return
try:
resultSet = statement.executeQuery("SELECT timestamp, latitude, longitude, accuracy FROM CachedPosition;")
while resultSet.next():
timestamp = Long.valueOf(resultSet.getString("timestamp")) / 1000
latitude = Double.valueOf(resultSet.getString("latitude"))
longitude = Double.valueOf(resultSet.getString("longitude"))
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, general.MODULE_NAME, latitude))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, general.MODULE_NAME, longitude))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, timestamp))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME, "Browser Location History"))
# artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy))
# NOTE: originally commented out
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard()
blackboard.indexArtifact(artifact)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactTypeName(), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName())
except Exception as ex:
self._logger.log(Level.SEVERE, "Error putting artifacts to blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
finally:
try:
if resultSet is not None:
resultSet.close()
statement.close()
connection.close()
except Exception as ex:
self._logger.log(Level.SEVERE, "Error closing database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())

View File

@ -0,0 +1,147 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
from java.io import File
from java.io import FileInputStream
from java.io import InputStream
from java.lang import Class
from java.lang import ClassNotFoundException
from java.math import BigInteger
from java.nio import ByteBuffer
from java.util.logging import Level
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Blackboard
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.datamodel import AbstractFile
from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException
import traceback
import general
"""
Parses cache files that Android maintains for Wifi and cell towers. Adds GPS points to blackboard.
"""
class CacheLocationAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__)
"""
cache.cell stores mobile tower GPS locations and cache.wifi stores GPS
and MAC info from Wifi points.
"""
def analyze(self, dataSource, fileManager, context):
try:
abstractFiles = fileManager.findFiles(dataSource, "cache.cell")
abstractFiles.addAll(fileManager.findFiles(dataSource, "cache.wifi"))
for abstractFile in abstractFiles:
if abstractFile.getSize() == 0:
continue
try:
jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled)
self.__findGeoLocationsInFile(jFile, abstractFile)
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing cached location files", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error finding cached location files", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
def __findGeoLocationsInFile(self, file, abstractFile):
tempBytes = bytearray([0] * 2) # will temporarily hold bytes to be converted into the correct data types
try:
inputStream = FileInputStream(file)
inputStream.read(tempBytes) # version
tempBytes = bytearray([0] * 2)
inputStream.read(tempBytes) # number of location entries
iterations = BigInteger(tempBytes).intValue()
for i in range(iterations): # loop through every entry
tempBytes = bytearray([0] * 2)
inputStream.read(tempBytes)
tempBytes = bytearray([0])
inputStream.read(tempBytes)
while BigInteger(tempBytes).intValue() != 0: # pass through non important values until the start of accuracy(around 7-10 bytes)
if 0 > inputStream.read(tempBytes):
break # we've passed the end of the file, so stop
tempBytes = bytearray([0] * 3)
inputStream.read(tempBytes)
if BigInteger(tempBytes).intValue() <= 0: # This refers to a location that could not be calculated
tempBytes = bytearray([0] * 28) # read rest of the row's bytes
inputStream.read(tempBytes)
continue
accuracy = "" + BigInteger(tempBytes).intValue()
tempBytes = bytearray([0] * 4)
inputStream.read(tempBytes)
confidence = "" + BigInteger(tempBytes).intValue()
tempBytes = bytearray([0] * 8)
inputStream.read(tempBytes)
latitude = CacheLocationAnalyzer.toDouble(bytes)
tempBytes = bytearray([0] * 8)
inputStream.read(tempBytes)
longitude = CacheLocationAnalyzer.toDouble(bytes)
tempBytes = bytearray([0] * 8)
inputStream.read(tempBytes)
timestamp = BigInteger(tempBytes).longValue() / 1000
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, AndroidAnalyzer.MODULE_NAME, latitude))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, AndroidAnalyzer.MODULE_NAME, longitude))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, AndroidModuleFactorymodule.Name, timestamp))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, AndroidAnalyzer.MODULE_NAME,
file.getName() + "Location History"))
#Not storing these for now.
# artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(), AndroidModuleFactorymodule.moduleName, accuracy))
# artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID(), AndroidModuleFactorymodule.moduleName, confidence))
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard()
blackboard.indexArtifact(artifact)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index GPS trackpoint artifact for keyword search.", artifact.getDisplayName())
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing Cached GPS locations to blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
def toDouble(byteArray):
return ByteBuffer.wrap(byteArray).getDouble()

View File

@ -0,0 +1,145 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
from java.io import File
from java.io import IOException
from java.lang import Class
from java.lang import ClassNotFoundException
from java.lang import String
from java.sql import Connection
from java.sql import DriverManager
from java.sql import ResultSet
from java.sql import SQLException
from java.sql import Statement
from java.util.logging import Level
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Blackboard
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.datamodel import AbstractFile
from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel.BlackboardAttribute import ATTRIBUTE_TYPE
from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException
import traceback
import general
"""
Locates a variety of different call log databases, parses them, and populates the blackboard.
"""
class CallLogAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__)
# the names of tables that potentially hold call logs in the dbs
_tableNames = ["calls", "logs"]
class CallDirection:
def __init__(self, type, displayName):
self.type = type
self.displayName = displayName
def getDisplayName(self):
return self.displayName
INCOMING = CallDirection(1, "Incoming")
OUTGOING = CallDirection(2, "Outgoing")
MISSED = CallDirection(3, "Missed")
@staticmethod
def fromType(t):
return {
1: CallLogAnalyzer.INCOMING,
2: CallLogAnalyzer.OUTGOING,
3: CallLogAnalyzer.MISSED
}.get(t, None)
def analyze(self, dataSource, fileManager, context):
try:
absFiles = fileManager.findFiles(dataSource, "logs.db")
absFiles.addAll(fileManager.findFiles(dataSource, "contacts.db"))
absFiles.addAll(fileManager.findFiles(dataSource, "contacts2.db"))
for abstractFile in absFiles:
try:
file = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, file, context.dataSourceIngestIsCancelled)
self.__findCallLogsInDB(file.toString(), abstractFile)
except IOException as ex:
self._logger.log(Level.SEVERE, "Error writing temporary call log db to disk", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error finding call logs", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
def __findCallLogsInDB(self, databasePath, abstractFile):
if not databasePath:
return
try:
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
statement = connection.createStatement()
for tableName in CallLogAnalyzer._tableNames:
try:
resultSet = statement.executeQuery("SELECT number, date, duration, type, name FROM " + tableName + " ORDER BY date DESC;")
self._logger.log(Level.INFO, "Reading call log from table {0} in db {1}", [tableName, databasePath])
while resultSet.next():
date = resultSet.getLong("date") / 1000
direction = CallLogAnalyzer.fromType(resultSet.getInt("type"))
directionString = direction.getDisplayName() if direction is not None else ""
number = resultSet.getString("number")
duration = resultSet.getLong("duration") # duration of call is in seconds
name = resultSet.getString("name") # name of person dialed or called. None if unregistered
try:
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG) # create a call log and then add attributes from result set.
if direction == CallLogAnalyzer.OUTGOING:
artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, general.MODULE_NAME, number))
else: # Covers INCOMING and MISSED
artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, general.MODULE_NAME, number))
artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_START, general.MODULE_NAME, date))
artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DATETIME_END, general.MODULE_NAME, duration + date))
artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, directionString))
artifact.addAttribute(BlackboardAttribute(ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name))
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard()
blackboard.indexArtifact(artifact)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index call log artifact for keyword search.", artifact.getDisplayName())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error posting call log record to the blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except SQLException as ex:
self._logger.log(Level.WARNING, String.format("Could not read table %s in db %s", tableName, databasePath), ex)
except SQLException as ex:
self._logger.log(Level.SEVERE, "Could not parse call log; error connecting to db " + databasePath, ex)
self._logger.log(Level.SEVERE, traceback.format_exc())

View File

@ -0,0 +1,156 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
from java.io import File
from java.lang import Class
from java.lang import ClassNotFoundException
from java.sql import Connection
from java.sql import DatabaseMetaData
from java.sql import DriverManager
from java.sql import ResultSet
from java.sql import SQLException
from java.sql import Statement
from java.util.logging import Level
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Blackboard
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.datamodel import AbstractFile
from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException
import traceback
import general
"""
Locates a variety of different contacts databases, parses them, and populates the blackboard.
"""
class ContactAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__)
def analyze(self, dataSource, fileManager, context):
try:
absFiles = fileManager.findFiles(dataSource, "contacts.db")
absFiles.addAll(fileManager.findFiles(dataSource, "contacts2.db"))
if absFiles.isEmpty():
return
for abstractFile in absFiles:
try:
jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled)
self.__findContactsInDB(str(jFile.toString()), abstractFile)
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing Contacts", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error finding Contacts", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
"""
Will create artifact from a database given by the path
The fileId will be the abstract file associated with the artifacts
"""
def __findContactsInDB(self, databasePath, abstractFile):
if not databasePath:
return
try:
Class.forName("org.sqlite.JDBC") # load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
statement = connection.createStatement()
except (ClassNotFoundException, SQLException) as ex:
self._logger.log(Level.SEVERE, "Error opening database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
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.
# check if contacts.name_raw_contact_id exists. Modify the query accordingly.
columnFound = False
metadata = connection.getMetaData()
columnListResultSet = metadata.getColumns(None, None, "contacts", None)
while columnListResultSet.next():
if columnListResultSet.getString("COLUMN_NAME") == "name_raw_contact_id":
columnFound = True
break
if columnFound:
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;")
else:
resultSet = statement.executeQuery(
"SELECT mimetype, data1, raw_contacts.display_name AS display_name \n"
+ "FROM raw_contacts JOIN contacts ON (raw_contacts.contact_id=contacts._id) \n"
+ "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 raw_contacts.display_name ASC;")
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT)
oldName = ""
while resultSet.next():
name = resultSet.getString("display_name")
data1 = resultSet.getString("data1") # the phone number or email
mimetype = resultSet.getString("mimetype") # either phone or email
if name != oldName:
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CONTACT)
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, name))
if mimetype == "vnd.android.cursor.item/phone_v2":
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, general.MODULE_NAME, data1))
else:
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL, general.MODULE_NAME, data1))
oldName = name
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard()
blackboard.indexArtifact(artifact)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index contact artifact for keyword search.", artifact.getDisplayName())
except SQLException as ex:
self._logger.log(Level.WARNING, "Unable to execute contacts SQL query against {0} : {1}", [databasePath, ex])
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error posting to blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
finally:
try:
if resultSet is not None:
resultSet.close()
statement.close()
connection.close()
except Exception as ex:
self._logger.log(Level.SEVERE, "Error closing database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())

View File

@ -0,0 +1,28 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
MODULE_NAME = "Android Analyzer Python"
"""
A parent class of the analyzers
"""
class AndroidComponentAnalyzer:
# The Analyzer should implement this method
def analyze(self, dataSource, fileManager, context):
raise NotImplementedError

View File

@ -0,0 +1,139 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
from java.io import File
from java.lang import Class
from java.lang import ClassNotFoundException
from java.lang import Double
from java.lang import Long
from java.sql import Connection
from java.sql import DriverManager
from java.sql import ResultSet
from java.sql import SQLException
from java.sql import Statement
from java.util.logging import Level
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Blackboard
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.datamodel import AbstractFile
from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException
import traceback
import general
"""
Finds and parses the Google Maps database.
"""
class GoogleMapLocationAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__)
def analyze(self, dataSource, fileManager, context):
try:
absFiles = fileManager.findFiles(dataSource, "da_destination_history")
if absFiles.isEmpty():
return
for abstractFile in absFiles:
try:
jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled)
self.__findGeoLocationsInDB(jFile.toString(), abstractFile)
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing Google map locations", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error finding Google map locations", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
def __findGeoLocationsInDB(self, databasePath, abstractFile):
if not databasePath:
return
try:
Class.forName("org.sqlite.JDBC") # load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
statement = connection.createStatement()
except (ClassNotFoundException, SQLException) as ex:
self._logger.log(Level.SEVERE, "Error opening database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
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():
time = Long.valueOf(resultSet.getString("time")) / 1000
dest_title = resultSet.getString("dest_title")
dest_address = resultSet.getString("dest_address")
dest_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("dest_lat"))
dest_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("dest_lng"))
source_lat = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lat"))
source_lng = GoogleMapLocationAnalyzer.convertGeo(resultSet.getString("source_lng"))
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, general.MODULE_NAME, "Destination"))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, time))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, general.MODULE_NAME, dest_lat))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END, general.MODULE_NAME, dest_lng))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START, general.MODULE_NAME, source_lat))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START, general.MODULE_NAME, source_lng))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, dest_title))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION, general.MODULE_NAME, dest_address))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, general.MODULE_NAME, "Google Maps History"))
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard()
blackboard.indexArtifact(artifact)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index GPS route artifact for keyword search.", artifact.getDisplayName())
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing Google map locations to the blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
finally:
try:
if resultSet is not None:
resultSet.close()
statement.close()
connection.close()
except Exception as ex:
self._logger.log(Level.SEVERE, "Error closing the database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
# add periods 6 decimal places before the end.
@staticmethod
def convertGeo(s):
length = len(s)
if length > 6:
return Double.valueOf(s[0 : length-6] + "." + s[length-6 : length])
else:
return Double.valueOf(s)

View File

@ -0,0 +1,126 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
import jarray
import inspect
import traceback
from java.util.logging import Level
from org.sleuthkit.autopsy.coreutils import Version
from org.sleuthkit.autopsy.ingest import IngestModuleFactory
from org.sleuthkit.autopsy.ingest import DataSourceIngestModule
from org.sleuthkit.autopsy.ingest import IngestModuleFactoryAdapter
from org.sleuthkit.autopsy.ingest import IngestModuleIngestJobSettings
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.ingest import DataSourceIngestModuleProgress
from org.sleuthkit.autopsy.ingest import IngestModule
from org.sleuthkit.datamodel import Content
from org.sleuthkit.autopsy.ingest import DataSourceIngestModule
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.autopsy.ingest import IngestMessage
import general
import browserlocation
import cachelocation
import calllog
import contact
import googlemaplocation
import tangomessage
import textmessage
import wwfmessage
class AndroidModuleFactory(IngestModuleFactoryAdapter):
moduleName = general.MODULE_NAME
def getModuleDisplayName(self):
return self.moduleName
def getModuleDescription(self):
return "Extracts Android system and third-party app data."
def getModuleVersionNumber(self):
return Version.getVersion()
def isDataSourceIngestModuleFactory(self):
return True
def createDataSourceIngestModule(self, ingestOptions):
return AndroidIngestModule()
class AndroidIngestModule(DataSourceIngestModule):
_logger = Logger.getLogger(AndroidModuleFactory.moduleName)
def log(self, level, msg):
self._logger.logp(level, self.__class__.__name__, inspect.stack()[1][3], msg)
def __init__(self):
self.context = None
def startUp(self, context):
self.context = context
# Throw an IngestModule.IngestModuleException exception if there was a problem setting up
# Where the analysis is done.
def process(self, dataSource, progressBar):
errors = []
fileManager = Case.getCurrentCase().getServices().getFileManager()
analyzers = [contact.ContactAnalyzer(), calllog.CallLogAnalyzer(), textmessage.TextMessageAnalyzer(), tangomessage.TangoMessageAnalyzer(), wwfmessage.WWFMessageAnalyzer(), googlemaplocation.GoogleMapLocationAnalyzer(), browserlocation.BrowserLocationAnalyzer(), cachelocation.CacheLocationAnalyzer()]
self.log(Level.INFO, "running " + str(len(analyzers)) + " analyzers")
progressBar.switchToDeterminate(len(analyzers))
n = 0
for analyzer in analyzers:
if self.context.dataSourceIngestIsCancelled():
return IngestModule.ProcessResult.OK
try:
analyzer.analyze(dataSource, fileManager, self.context)
n += 1
progressBar.progress(n)
except Exception as ex:
errors.append("Error running " + analyzer.__class__.__name__)
self.log(Level.SEVERE, traceback.format_exc())
errorMessage = [] # NOTE: this isn't used?
errorMessageSubject = "" # NOTE: this isn't used?
msgLevel = IngestMessage.MessageType.INFO
if errors:
msgLevel = IngestMessage.MessageType.ERROR
errorMessage.append("Errors were encountered")
errorMessage.append("<ul>") # NOTE: this was missing in the original java code
for msg in errors:
errorMessage.extend(["<li>", msg, "</li>\n"])
errorMessage.append("</ul>\n")
if len(errors) == 1:
errorMsgSubject = "One error was found"
else:
errorMsgSubject = "errors found: " + str(len(errors))
else:
errorMessage.append("No errors")
errorMsgSubject = "No errors"
return IngestModule.ProcessResult.OK

View File

@ -0,0 +1,136 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
from java.io import File
from java.lang import Class
from java.lang import ClassNotFoundException
from java.lang import Long
from java.lang import String
from java.sql import Connection
from java.sql import DriverManager
from java.sql import ResultSet
from java.sql import SQLException
from java.sql import Statement
from java.util.logging import Level
from org.apache.commons.codec.binary import Base64
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Blackboard
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.datamodel import AbstractFile
from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException
import traceback
import general
"""
Locates database for the Tango app and adds info to blackboard.
"""
class TangoMessageAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__)
def analyze(self, dataSource, fileManager, context):
try:
absFiles = fileManager.findFiles(dataSource, "tc.db")
for abstractFile in absFiles:
try:
jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled)
self.__findTangoMessagesInDB(jFile.toString(), abstractFile)
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing Tango messages", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error finding Tango messages", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
def __findTangoMessagesInDB(self, databasePath, abstractFile):
if not databasePath:
return
try:
Class.forName("org.sqlite.JDBC") # load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
statement = connection.createStatement()
except (ClassNotFoundException, SQLException) as ex:
self._logger.log(Level.SEVERE, "Error opening database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
return
try:
resultSet = statement.executeQuery(
"SELECT conv_id, create_time, direction, payload FROM messages ORDER BY create_time DESC;")
while resultSet.next():
conv_id = resultSet.getString("conv_id") # seems to wrap around the message found in payload after decoding from base-64
create_time = Long.valueOf(resultSet.getString("create_time")) / 1000
if resultSet.getString("direction") == "1": # 1 incoming, 2 outgoing
direction = "Incoming"
else:
direction = "Outgoing"
payload = resultSet.getString("payload")
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) #create a call log and then add attributes from result set.
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, create_time))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, direction))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, TangoMessageAnalyzer.decodeMessage(conv_id, payload)))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Tango Message"))
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard()
blackboard.indexArtifact(artifact)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index Tango message artifact for keyword search.", artifact.getDisplayName())
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing Tango messages to the blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
finally:
try:
if resultSet is not None:
resultSet.close()
statement.close()
connection.close()
except Exception as ex:
self._logger.log(Level.SEVERE, "Error closing database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
# take the message string which is wrapped by a certain string, and return the text enclosed.
@staticmethod
def decodeMessage(wrapper, message):
result = ""
decoded = Base64.decodeBase64(message)
try:
Z = String(decoded, "UTF-8")
result = Z.split(wrapper)[1]
except Exception as ex:
self._logger.log(Level.SEVERE, "Error decoding a Tango message", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
return result

View File

@ -0,0 +1,125 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
from java.io import File
from java.lang import Class
from java.lang import ClassNotFoundException
from java.lang import Integer
from java.lang import Long
from java.sql import Connection
from java.sql import DriverManager
from java.sql import ResultSet
from java.sql import SQLException
from java.sql import Statement
from java.util.logging import Level
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Blackboard
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.datamodel import AbstractFile
from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException
import traceback
import general
"""
Finds database with SMS/MMS messages and adds them to blackboard.
"""
class TextMessageAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__)
def analyze(self, dataSource, fileManager, context):
try:
absFiles = fileManager.findFiles(dataSource, "mmssms.db")
for abstractFile in absFiles:
try:
jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled)
self.__findTextsInDB(jFile.toString(), abstractFile)
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing text messages", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error finding text messages", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
def __findTextsInDB(self, databasePath, abstractFile):
if not databasePath:
return
try:
Class.forName("org.sqlite.JDBC") # load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
statement = connection.createStatement()
except (ClassNotFoundException, SQLException) as ex:
self._logger.log(Level.SEVERE, "Error opening database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
return
try:
resultSet = statement.executeQuery(
"SELECT address, date, read, type, subject, body FROM sms;")
while resultSet.next():
address = resultSet.getString("address") # may be phone number, or other addresses
date = Long.valueOf(resultSet.getString("date")) / 1000
read = resultSet.getInt("read") # may be unread = 0, read = 1
subject = resultSet.getString("subject") # message subject
body = resultSet.getString("body") # message body
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); #create Message artifact and then add attributes from result set.
if resultSet.getString("type") == "1":
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, "Incoming"))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM, general.MODULE_NAME, address))
else:
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, general.MODULE_NAME, "Outgoing"))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO, general.MODULE_NAME, address))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, date))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, general.MODULE_NAME, Integer(read)))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, general.MODULE_NAME, subject))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, body))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "SMS Message"))
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard()
blackboard.indexArtifact(artifact)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index text message artifact for keyword search.", artifact.getDisplayName())
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing text messages to blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
finally:
try:
if resultSet is not None:
resultSet.close()
statement.close()
connection.close()
except Exception as ex:
self._logger.log(Level.SEVERE, "Error closing database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())

View File

@ -0,0 +1,118 @@
"""
Autopsy Forensic Browser
Copyright 2016 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.
"""
from java.io import File
from java.lang import Class
from java.lang import ClassNotFoundException
from java.sql import Connection
from java.sql import DriverManager
from java.sql import ResultSet
from java.sql import SQLException
from java.sql import Statement
from java.util.logging import Level
from org.sleuthkit.autopsy.casemodule import Case
from org.sleuthkit.autopsy.casemodule.services import Blackboard
from org.sleuthkit.autopsy.casemodule.services import FileManager
from org.sleuthkit.autopsy.coreutils import Logger
from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil
from org.sleuthkit.autopsy.datamodel import ContentUtils
from org.sleuthkit.autopsy.ingest import IngestJobContext
from org.sleuthkit.datamodel import AbstractFile
from org.sleuthkit.datamodel import BlackboardArtifact
from org.sleuthkit.datamodel import BlackboardAttribute
from org.sleuthkit.datamodel import Content
from org.sleuthkit.datamodel import TskCoreException
import traceback
import general
"""
Analyzes messages from Words With Friends
"""
class WWFMessageAnalyzer(general.AndroidComponentAnalyzer):
def __init__(self):
self._logger = Logger.getLogger(self.__class__.__name__)
def analyze(self, dataSource, fileManager, context):
try:
absFiles = fileManager.findFiles(dataSource, "WordsFramework")
for abstractFile in absFiles:
try:
jFile = File(Case.getCurrentCase().getTempDirectory(), str(abstractFile.getId()) + abstractFile.getName())
ContentUtils.writeToFile(abstractFile, jFile, context.dataSourceIngestIsCancelled)
self.__findWWFMessagesInDB(jFile.toString(), abstractFile)
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing WWF messages", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
except TskCoreException as ex:
self._logger.log(Level.SEVERE, "Error finding WWF messages", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
def __findWWFMessagesInDB(self, databasePath, abstractFile):
if not databasePath:
return
try:
Class.forName("org.sqlite.JDBC"); # load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)
statement = connection.createStatement()
except (ClassNotFoundException, SQLException) as ex:
self._logger.log(Level.SEVERE, "Error opening database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
return
try:
resultSet = statement.executeQuery(
"SELECT message, strftime('%s' ,created_at) as datetime, user_id, game_id FROM chat_messages ORDER BY game_id DESC, created_at DESC;")
while resultSet.next():
message = resultSet.getString("message") # WWF Message
created_at = resultSet.getLong("datetime")
user_id = resultSet.getString("user_id") # the ID of the user who sent the message.
game_id = resultSet.getString("game_id") # ID of the game which the the message was sent.
artifact = abstractFile.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE) # create a call log and then add attributes from result set.
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, general.MODULE_NAME, created_at))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, general.MODULE_NAME, user_id))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, general.MODULE_NAME, game_id))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, general.MODULE_NAME, message))
artifact.addAttribute(BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, general.MODULE_NAME, "Words With Friends Message"))
try:
# index the artifact for keyword search
blackboard = Case.getCurrentCase().getServices().getBlackboard()
blackboard.indexArtifact(artifact)
except Blackboard.BlackboardException as ex:
self._logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getArtifactID(), ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
MessageNotifyUtil.Notify.error("Failed to index WWF message artifact for keyword search.", artifact.getDisplayName())
except Exception as ex:
self._logger.log(Level.SEVERE, "Error parsing WWF messages to the blackboard", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())
finally:
try:
if resultSet is not None:
resultSet.close()
statement.close()
connection.close()
except Exception as ex:
self._logger.log(Level.SEVERE, "Error closing database", ex)
self._logger.log(Level.SEVERE, traceback.format_exc())

View File

@ -1,148 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2016 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 org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
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;
class AndroidIngestModule implements DataSourceIngestModule {
private IngestJobContext context = null;
@Override
public void startUp(IngestJobContext context) throws IngestModuleException {
this.context = context;
}
@Override
public ProcessResult process(Content dataSource, DataSourceIngestModuleProgress progressBar) {
ArrayList<String> errors = new ArrayList<>();
progressBar.switchToDeterminate(9);
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
try {
ContactAnalyzer.findContacts(dataSource, fileManager, context);
progressBar.progress(1);
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
errors.add("Error getting Contacts"); //NON-NLS
}
try {
CallLogAnalyzer.findCallLogs(dataSource, fileManager, context);
progressBar.progress(2);
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
errors.add("Error getting Call Logs"); //NON-NLS
}
try {
TextMessageAnalyzer.findTexts(dataSource, fileManager, context);
progressBar.progress(3);
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
errors.add("Error getting Text Messages"); //NON-NLS
}
try {
TangoMessageAnalyzer.findTangoMessages(dataSource, fileManager, context);
progressBar.progress(4);
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
errors.add("Error getting Tango Messages"); //NON-NLS
}
try {
WWFMessageAnalyzer.findWWFMessages(dataSource, fileManager, context);
progressBar.progress(5);
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
errors.add("Error getting Words with Friends Messages"); //NON-NLS
}
try {
GoogleMapLocationAnalyzer.findGeoLocations(dataSource, fileManager, context);
progressBar.progress(6);
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
errors.add("Error getting Google Map Locations"); //NON-NLS
}
try {
BrowserLocationAnalyzer.findGeoLocations(dataSource, fileManager, context);
progressBar.progress(7);
if (context.dataSourceIngestIsCancelled()) {
return IngestModule.ProcessResult.OK;
}
} catch (Exception e) {
errors.add("Error getting Browser Locations"); //NON-NLS
}
try {
CacheLocationAnalyzer.findGeoLocations(dataSource, fileManager, context);
progressBar.progress(8);
} catch (Exception e) {
errors.add("Error getting Cache Locations"); //NON-NLS
}
// 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"); //NON-NLS
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"; //NON-NLS
} else {
errorMsgSubject = "errors found: " + errors.size(); //NON-NLS
}
} else {
errorMessage.append("No errors"); //NON-NLS
errorMsgSubject = "No errors"; //NON-NLS
}
return IngestModule.ProcessResult.OK;
}
}

View File

@ -1,61 +0,0 @@
/*
* 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.coreutils.Version;
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 {
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.getVersion();
}
@Override
public boolean isDataSourceIngestModuleFactory() {
return true;
}
@Override
public DataSourceIngestModule createDataSourceIngestModule(IngestModuleIngestJobSettings settings) {
return new AndroidIngestModule();
}
}

View File

@ -1,135 +0,0 @@
/*
* 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.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Analyzes database created by browser that stores GEO location info.
*/
class BrowserLocationAnalyzer {
private static final String moduleName = AndroidModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(BrowserLocationAnalyzer.class.getName());
private static Blackboard blackboard;
public static void findGeoLocations(Content dataSource, FileManager fileManager,
IngestJobContext context) {
blackboard = Case.getCurrentCase().getServices().getBlackboard();
try {
List<AbstractFile> abstractFiles = fileManager.findFiles(dataSource, "CachedGeoposition%.db"); //NON-NLS
for (AbstractFile abstractFile : abstractFiles) {
try {
if (abstractFile.getSize() == 0) {
continue;
}
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled);
findGeoLocationsInDB(jFile.toString(), abstractFile);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Browser Location files", e); //NON-NLS
}
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error finding Browser Location files", e); //NON-NLS
}
}
@NbBundle.Messages({"BrowserLocationAnalyzer.indexError.message=Failed to index GPS trackpoint artifact for keyword search."})
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"); //NON-NLS //load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS
statement = connection.createStatement();
} catch (ClassNotFoundException | SQLException e) {
logger.log(Level.SEVERE, "Error connecting to sql database", e); //NON-NLS
return;
}
try {
resultSet = statement.executeQuery(
"SELECT timestamp, latitude, longitude, accuracy FROM CachedPosition;"); //NON-NLS
while (resultSet.next()) {
Long timestamp = Long.valueOf(resultSet.getString("timestamp")) / 1000; //NON-NLS
double latitude = Double.valueOf(resultSet.getString("latitude")); //NON-NLS
double longitude = Double.valueOf(resultSet.getString("longitude")); //NON-NLS
BlackboardArtifact bba = f.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT);
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, moduleName, latitude));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, moduleName, longitude));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, timestamp));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, moduleName,
NbBundle.getMessage(BrowserLocationAnalyzer.class,
"BrowserLocationAnalyzer.bbAttribute.browserLocationHistory")));
// bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy));
try {
// index the artifact for keyword search
blackboard.indexArtifact(bba);
} catch (Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactTypeName(), ex); //NON-NLS
MessageNotifyUtil.Notify.error(
Bundle.BrowserLocationAnalyzer_indexError_message(), bba.getDisplayName());
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error Putting artifacts to Blackboard", e); //NON-NLS
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
statement.close();
connection.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS
}
}
}
}

View File

@ -1,11 +0,0 @@
AndroidModuleFactory.moduleName=Android Analyzer
AndroidModuleFactory.moduleDescription=Extracts Android system and third-party app data.
BrowserLocationAnalyzer.bbAttribute.browserLocationHistory=Browser Location History
CacheLocationAnalyzer.bbAttribute.fileLocationHistory={0} Location History
GoogleMapLocationAnalyzer.bbAttribute.destination=Destination
GoogleMapLocationAnalyzer.bbAttribute.googleMapsHistory=Google Maps History
TangoMessageAnalyzer.bbAttribute.tangoMessage=Tango Message
TextMessageAnalyzer.bbAttribute.incoming=Incoming
TextMessageAnalyzer.bbAttribute.outgoing=Outgoing
TextMessageAnalyzer.bbAttribute.smsMessage=SMS Message
WWFMessageAnalyzer.bbAttribute.wordsWithFriendsMsg=Words With Friends Message

View File

@ -1,10 +0,0 @@
AndroidModuleFactory.moduleDescription=Android\u30b7\u30b9\u30c6\u30e0\u304a\u3088\u3073\u7b2c\u4e09\u8005\u30a2\u30d7\u30ea\u30c7\u30fc\u30bf\u3092\u62bd\u51fa
BrowserLocationAnalyzer.bbAttribute.browserLocationHistory=\u30d6\u30e9\u30a6\u30b6\u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u5c65\u6b74
CacheLocationAnalyzer.bbAttribute.fileLocationHistory={0} \u30ed\u30b1\u30fc\u30b7\u30e7\u30f3\u5c65\u6b74
GoogleMapLocationAnalyzer.bbAttribute.destination=\u76ee\u7684\u5730
GoogleMapLocationAnalyzer.bbAttribute.googleMapsHistory=Google\u30de\u30c3\u30d7\u5c65\u6b74
TangoMessageAnalyzer.bbAttribute.tangoMessage=Tango\u30e1\u30c3\u30bb\u30fc\u30b8
TextMessageAnalyzer.bbAttribute.incoming=\u53d7\u4fe1
TextMessageAnalyzer.bbAttribute.outgoing=\u9001\u4fe1
TextMessageAnalyzer.bbAttribute.smsMessage=SMS\u30e1\u30c3\u30bb\u30fc\u30b8
WWFMessageAnalyzer.bbAttribute.wordsWithFriendsMsg=Words With Friends\u30e1\u30c3\u30bb\u30fc\u30b8

View File

@ -1,163 +0,0 @@
/*
* 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.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Parses cache files that Android maintains for Wifi and cell towers. Adds GPS
* points to blackboard.
*/
class CacheLocationAnalyzer {
private static final String moduleName = AndroidModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(CacheLocationAnalyzer.class.getName());
private static Blackboard blackboard;
/**
* cache.cell stores mobile tower GPS locations and cache.wifi stores GPS
* and MAC info from Wifi points.
*/
public static void findGeoLocations(Content dataSource, FileManager fileManager,
IngestJobContext context) {
blackboard = Case.getCurrentCase().getServices().getBlackboard();
try {
List<AbstractFile> abstractFiles = fileManager.findFiles(dataSource, "cache.cell"); //NON-NLS
abstractFiles.addAll(fileManager.findFiles(dataSource, "cache.wifi")); //NON-NLS
for (AbstractFile abstractFile : abstractFiles) {
try {
if (abstractFile.getSize() == 0) {
continue;
}
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled);
findGeoLocationsInFile(jFile, abstractFile);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing cached Location files", e); //NON-NLS
}
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error finding cached Location files", e); //NON-NLS
}
}
@Messages({"CacheLocationAnalyzer.indexError.message=Failed to index GPS trackpoint artifact for keyword search."})
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)
if (0 > inputStream.read(bytes)) {
break; /// we've passed the end of the file, so stop
}
}
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, moduleName, latitude));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, moduleName, longitude));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, timestamp));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, moduleName,
NbBundle.getMessage(CacheLocationAnalyzer.class,
"CacheLocationAnalyzer.bbAttribute.fileLocationHistory",
file.getName())));
//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));
try {
// index the artifact for keyword search
blackboard.indexArtifact(bba);
} catch (Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
MessageNotifyUtil.Notify.error(
Bundle.CacheLocationAnalyzer_indexError_message(), bba.getDisplayName());
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Cached GPS locations to Blackboard", e); //NON-NLS
}
}
private static double toDouble(byte[] bytes) {
return ByteBuffer.wrap(bytes).getDouble();
}
}

View File

@ -1,170 +0,0 @@
/*
* 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.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Finds and parses the Google Maps database.
*/
class GoogleMapLocationAnalyzer {
private static final String moduleName = AndroidModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(GoogleMapLocationAnalyzer.class.getName());
private static Blackboard blackboard;
public static void findGeoLocations(Content dataSource, FileManager fileManager,
IngestJobContext context) {
List<AbstractFile> absFiles;
blackboard = Case.getCurrentCase().getServices().getBlackboard();
try {
absFiles = fileManager.findFiles(dataSource, "da_destination_history"); //NON-NLS
if (absFiles.isEmpty()) {
return;
}
for (AbstractFile abstractFile : absFiles) {
try {
File jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled);
findGeoLocationsInDB(jFile.toString(), abstractFile);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Google map locations", e); //NON-NLS
}
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error finding Google map locations", e); //NON-NLS
}
}
@Messages({"GoogleMapLocationAnalyzer.indexError.message=Failed to index GPS route artifact for keyword search."})
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"); //NON-NLS //load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS
statement = connection.createStatement();
} catch (ClassNotFoundException | SQLException e) {
logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS
return;
}
try {
resultSet = statement.executeQuery(
"SELECT time,dest_lat,dest_lng,dest_title,dest_address,source_lat,source_lng FROM destination_history;"); //NON-NLS
while (resultSet.next()) {
Long time = Long.valueOf(resultSet.getString("time")) / 1000; //NON-NLS
String dest_title = resultSet.getString("dest_title"); //NON-NLS
String dest_address = resultSet.getString("dest_address"); //NON-NLS
double dest_lat = convertGeo(resultSet.getString("dest_lat")); //NON-NLS
double dest_lng = convertGeo(resultSet.getString("dest_lng")); //NON-NLS
double source_lat = convertGeo(resultSet.getString("source_lat")); //NON-NLS
double source_lng = convertGeo(resultSet.getString("source_lng")); //NON-NLS
// 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, moduleName,
NbBundle.getMessage(GoogleMapLocationAnalyzer.class,
"GoogleMapLocationAnalyzer.bbAttribute.destination")));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME, moduleName, time));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, moduleName, dest_lat));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END, moduleName, dest_lng));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START, moduleName, source_lat));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START, moduleName, source_lng));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, dest_title));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION, moduleName, dest_address));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME, moduleName,
NbBundle.getMessage(GoogleMapLocationAnalyzer.class,
"GoogleMapLocationAnalyzer.bbAttribute.googleMapsHistory")));
try {
// index the artifact for keyword search
blackboard.indexArtifact(bba);
} catch (Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
MessageNotifyUtil.Notify.error(
Bundle.GoogleMapLocationAnalyzer_indexError_message(), bba.getDisplayName());
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Google map locations to the Blackboard", e); //NON-NLS
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
statement.close();
connection.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error closing the database", e); //NON-NLS
}
}
}
//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);
}
}
}

View File

@ -1,154 +0,0 @@
/*
* 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.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Locates database for the Tango app and adds info to blackboard.
*/
class TangoMessageAnalyzer {
private static final String moduleName = AndroidModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(TangoMessageAnalyzer.class.getName());
private static Blackboard blackboard;
public static void findTangoMessages(Content dataSource, FileManager fileManager,
IngestJobContext context) {
blackboard = Case.getCurrentCase().getServices().getBlackboard();
List<AbstractFile> absFiles;
try {
absFiles = fileManager.findFiles(dataSource, "tc.db"); //NON-NLS
for (AbstractFile abstractFile : absFiles) {
try {
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled);
findTangoMessagesInDB(jFile.toString(), abstractFile);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Tango messages", e); //NON-NLS
}
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error finding Tango messages", e); //NON-NLS
}
}
@Messages({"TangoMessageAnalyzer.indexError.message=Failed to index Tango message artifact for keyword search."})
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"); //NON-NLS //load JDBC driver
connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS
statement = connection.createStatement();
} catch (ClassNotFoundException | SQLException e) {
logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS
return;
}
try {
resultSet = statement.executeQuery(
"SELECT conv_id, create_time,direction,payload FROM messages ORDER BY create_time DESC;"); //NON-NLS
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"); //NON-NLS
Long create_time = Long.valueOf(resultSet.getString("create_time")) / 1000; //NON-NLS
if (resultSet.getString("direction").equals("1")) { //NON-NLS
direction = "Incoming"; //NON-NLS
} else {
direction = "Outgoing"; //NON-NLS
}
payload = resultSet.getString("payload"); //NON-NLS
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, moduleName, create_time));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, moduleName, direction));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, moduleName, decodeMessage(conv_id, payload)));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName,
NbBundle.getMessage(TangoMessageAnalyzer.class,
"TangoMessageAnalyzer.bbAttribute.tangoMessage")));
try {
// index the artifact for keyword search
blackboard.indexArtifact(bba);
} catch (Blackboard.BlackboardException ex) {
MessageNotifyUtil.Notify.error(
Bundle.TangoMessageAnalyzer_indexError_message(), bba.getDisplayName());
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing Tango messages to the Blackboard", e); //NON-NLS
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
statement.close();
connection.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS
}
}
}
//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); //NON-NLS
}
return result;
}
}

View File

@ -1,140 +0,0 @@
/*
* 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.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.services.Blackboard;
import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardAttribute;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Finds database for words with friends, parses it, and adds info to
* blackboard.
*/
class WWFMessageAnalyzer {
private static final String moduleName = AndroidModuleFactory.getModuleName();
private static final Logger logger = Logger.getLogger(WWFMessageAnalyzer.class.getName());
private static Blackboard blackboard;
public static void findWWFMessages(Content dataSource, FileManager fileManager,
IngestJobContext context) {
List<AbstractFile> absFiles;
blackboard = Case.getCurrentCase().getServices().getBlackboard();
try {
absFiles = fileManager.findFiles(dataSource, "WordsFramework"); //NON-NLS
for (AbstractFile abstractFile : absFiles) {
try {
File jFile = new File(Case.getCurrentCase().getTempDirectory(), abstractFile.getName());
ContentUtils.writeToFile(abstractFile, jFile, context::dataSourceIngestIsCancelled);
findWWFMessagesInDB(jFile.toString(), abstractFile);
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing WWF messages", e); //NON-NLS
}
}
} catch (TskCoreException e) {
logger.log(Level.SEVERE, "Error finding WWF messages", e); //NON-NLS
}
}
@Messages({"WWFMessageAnalyzer.indexError.message=Failed to index WWF message artifact for keyword search."})
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); //NON-NLS
statement = connection.createStatement();
} catch (ClassNotFoundException | SQLException e) {
logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS
return;
}
try {
resultSet = statement.executeQuery(
"SELECT message,strftime('%s' ,created_at) as datetime,user_id,game_id FROM chat_messages ORDER BY game_id DESC, created_at DESC;"); //NON-NLS
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"); //NON-NLS
Long created_at = resultSet.getLong("datetime"); //NON-NLS
user_id = resultSet.getString("user_id"); //NON-NLS
game_id = resultSet.getString("game_id"); //NON-NLS
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, moduleName, created_at));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, moduleName, user_id));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MSG_ID, moduleName, game_id));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, moduleName, message));
bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName,
NbBundle.getMessage(WWFMessageAnalyzer.class,
"WWFMessageAnalyzer.bbAttribute.wordsWithFriendsMsg")));
try {
// index the artifact for keyword search
blackboard.indexArtifact(bba);
} catch (Blackboard.BlackboardException ex) {
logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getArtifactID(), ex); //NON-NLS
MessageNotifyUtil.Notify.error(
Bundle.WWFMessageAnalyzer_indexError_message(), bba.getDisplayName());
}
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error parsing WWF messages to the Blackboard", e); //NON-NLS
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
statement.close();
connection.close();
} catch (Exception e) {
logger.log(Level.SEVERE, "Error closing database", e); //NON-NLS
}
}
}
}