mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-12 16:06:15 +00:00
Got Sofie's branch
This commit is contained in:
commit
0760cacf28
120
Core/release/InternalPythonModules/android/browserlocation.py
Executable file
120
Core/release/InternalPythonModules/android/browserlocation.py
Executable 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())
|
147
Core/release/InternalPythonModules/android/cachelocation.py
Executable file
147
Core/release/InternalPythonModules/android/cachelocation.py
Executable 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()
|
145
Core/release/InternalPythonModules/android/calllog.py
Executable file
145
Core/release/InternalPythonModules/android/calllog.py
Executable 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())
|
156
Core/release/InternalPythonModules/android/contact.py
Executable file
156
Core/release/InternalPythonModules/android/contact.py
Executable 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())
|
28
Core/release/InternalPythonModules/android/general.py
Executable file
28
Core/release/InternalPythonModules/android/general.py
Executable 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
|
139
Core/release/InternalPythonModules/android/googlemaplocation.py
Executable file
139
Core/release/InternalPythonModules/android/googlemaplocation.py
Executable 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)
|
126
Core/release/InternalPythonModules/android/module.py
Executable file
126
Core/release/InternalPythonModules/android/module.py
Executable 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
|
136
Core/release/InternalPythonModules/android/tangomessage.py
Executable file
136
Core/release/InternalPythonModules/android/tangomessage.py
Executable 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
|
125
Core/release/InternalPythonModules/android/textmessage.py
Executable file
125
Core/release/InternalPythonModules/android/textmessage.py
Executable 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())
|
118
Core/release/InternalPythonModules/android/wwfmessage.py
Executable file
118
Core/release/InternalPythonModules/android/wwfmessage.py
Executable 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())
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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
|
@ -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
|
@ -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();
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user