From e3142149054852bb0170de50e0581f87d9ad81f0 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Thu, 19 Sep 2019 19:51:50 -0400 Subject: [PATCH] Brought the code up to date with the api, fixed bugs --- .../android/TskCallLogsParser.py | 16 +- .../android/TskMessagesParser.py | 17 +- InternalPythonModules/android/whatsapp.py | 234 +++++++++++------- 3 files changed, 167 insertions(+), 100 deletions(-) diff --git a/InternalPythonModules/android/TskCallLogsParser.py b/InternalPythonModules/android/TskCallLogsParser.py index 763ba3c15f..d4e6942134 100644 --- a/InternalPythonModules/android/TskCallLogsParser.py +++ b/InternalPythonModules/android/TskCallLogsParser.py @@ -17,7 +17,8 @@ See the License for the specific language governing permissions and limitations under the License. """ from ResultSetIterator import ResultSetIterator -from org.sleuthkit.autopsy.coreutils import AppDBParserHelper +from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CallMediaType +from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection from org.sleuthkit.datamodel import Account class TskCallLogsParser(ResultSetIterator): @@ -35,14 +36,15 @@ class TskCallLogsParser(ResultSetIterator): def __init__(self, result_set): super(TskCallLogsParser, self).__init__(result_set) self._DEFAULT_STRING = "" - self._DEFAULT_DIRECTION = AppDBParserHelper.CommunicationDirection.UNKNOWN + self._DEFAULT_DIRECTION = CommunicationDirection.UNKNOWN self._DEFAULT_ADDRESS = None - self._DEFAULT_CALL_TYPE = AppDBParserHelper.CallMediaType.UNKNOWN + self._DEFAULT_CALL_TYPE = CallMediaType.UNKNOWN + self._DEFAULT_LONG = -1L - self.INCOMING_CALL = AppDBParserHelper.CommunicationDirection.INCOMING - self.OUTGOING_CALL = AppDBParserHelper.CommunicationDirection.OUTGOING - self.AUDIO_CALL = AppDBParserHelper.CallMediaType.AUDIO - self.VIDEO_CALL = AppDBParserHelper.CallMediaType.VIDEO + self.INCOMING_CALL = CommunicationDirection.INCOMING + self.OUTGOING_CALL = CommunicationDirection.OUTGOING + self.AUDIO_CALL = CallMediaType.AUDIO + self.VIDEO_CALL = CallMediaType.VIDEO def get_call_direction(self): return self._DEFAULT_DIRECTION diff --git a/InternalPythonModules/android/TskMessagesParser.py b/InternalPythonModules/android/TskMessagesParser.py index 15c4166db7..4568a7400c 100644 --- a/InternalPythonModules/android/TskMessagesParser.py +++ b/InternalPythonModules/android/TskMessagesParser.py @@ -18,8 +18,9 @@ limitations under the License. """ from ResultSetIterator import ResultSetIterator from org.sleuthkit.datamodel import Account -from org.sleuthkit.autopsy.coreutils import AppDBParserHelper - +from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus +from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection + class TskMessagesParser(ResultSetIterator): """ Generic TSK_MESSAGE artifact template. Each of these methods @@ -35,14 +36,14 @@ class TskMessagesParser(ResultSetIterator): super(TskMessagesParser, self).__init__(result_set) self._DEFAULT_TEXT = "" self._DEFAULT_LONG = -1L - self._DEFAULT_MSG_READ_STATUS = AppDBParserHelper.MessageReadStatusEnum.UNKNOWN + self._DEFAULT_MSG_READ_STATUS = MessageReadStatus.UNKNOWN self._DEFAULT_ACCOUNT_ADDRESS = None - self._DEFAULT_COMMUNICATION_DIRECTION = AppDBParserHelper.CommunicationDirection.UNKNOWN + self._DEFAULT_COMMUNICATION_DIRECTION = CommunicationDirection.UNKNOWN - self.INCOMING = AppDBParserHelper.CommunicationDirection.INCOMING - self.OUTGOING = AppDBParserHelper.CommunicationDirection.OUTGOING - self.READ = AppDBParserHelper.MessageReadStatusEnum.READ - self.UNREAD = AppDBParserHelper.MessageReadStatusEnum.UNREAD + self.INCOMING = CommunicationDirection.INCOMING + self.OUTGOING = CommunicationDirection.OUTGOING + self.READ = MessageReadStatus.READ + self.UNREAD = MessageReadStatus.UNREAD def get_message_type(self): return self._DEFAULT_TEXT diff --git a/InternalPythonModules/android/whatsapp.py b/InternalPythonModules/android/whatsapp.py index 9ae57c09d6..0582a558c7 100644 --- a/InternalPythonModules/android/whatsapp.py +++ b/InternalPythonModules/android/whatsapp.py @@ -16,7 +16,6 @@ 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 @@ -26,22 +25,29 @@ from java.sql import ResultSet from java.sql import SQLException from java.sql import Statement from java.util.logging import Level +from java.util import ArrayList from org.apache.commons.codec.binary import Base64 from org.sleuthkit.autopsy.casemodule import Case from org.sleuthkit.autopsy.coreutils import Logger +from org.sleuthkit.autopsy.coreutils import MessageNotifyUtil from org.sleuthkit.autopsy.coreutils import AppSQLiteDB -from org.sleuthkit.autopsy.coreutils import AppDBParserHelper + +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 +from org.sleuthkit.datamodel.Blackboard import BlackboardException +from org.sleuthkit.autopsy.casemodule import NoCurrentCaseException from org.sleuthkit.datamodel import Account +from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper +from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus +from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection from TskMessagesParser import TskMessagesParser from TskContactsParser import TskContactsParser from TskCallLogsParser import TskCallLogsParser -from general import appendAttachmentList import traceback import general @@ -67,79 +73,128 @@ class WhatsAppAnalyzer(general.AndroidComponentAnalyzer): try: contact_dbs = AppSQLiteDB.findAppDatabases(dataSource, "wa.db", True, self._WHATSAPP_PACKAGE_NAME) - message_dbs = AppSQLiteDB.findAppDatabases(dataSource, + calllog_and_message_dbs = AppSQLiteDB.findAppDatabases(dataSource, "msgstore.db", True, self._WHATSAPP_PACKAGE_NAME) #Extract TSK_CONTACT information for contact_db in contact_dbs: - helper = AppDBParserHelper(self._PARSER_NAME, + current_case = Case.getCurrentCaseThrows() + helper = CommunicationArtifactsHelper( + current_case.getSleuthkitCase(), self._PARSER_NAME, contact_db.getDBFile(), Account.Type.WHATSAPP) + self.parse_contacts(contact_db, helper) - contacts_parser = WhatsAppContactsParser(contact_db) - while contacts_parser.next(): - helper.addContact( - contacts_parser.get_account_name(), - contacts_parser.get_contact_name(), - contacts_parser.get_phone(), - contacts_parser.get_home_phone(), - contacts_parser.get_mobile_phone(), - contacts_parser.get_email() - ) - contacts_parser.close() + for calllog_and_message_db in calllog_and_message_dbs: + current_case = Case.getCurrentCaseThrows() + helper = CommunicationArtifactsHelper( + current_case.getSleuthkitCase(), self._PARSER_NAME, + calllog_and_message_db.getDBFile(), Account.Type.WHATSAPP) + calllog_and_message_db.attachDatabase(dataSource, "wa.db", + calllog_and_message_db.getDBFile().getParentPath(), "wadb") + self.parse_calllogs(calllog_and_message_db, helper) + self.parse_messages(calllog_and_message_db, helper) - contact_db.close() - - for message_db in message_dbs: - helper = AppDBParserHelper(self._PARSER_NAME, - message_db.getDBFile(), Account.Type.WHATSAPP) - - message_db.attachDatabase(dataSource, "wa.db", - message_db.getDBFile().getParentPath(), "wadb") - - messages_parser = WhatsAppMessagesParser(message_db) - while messages_parser.next(): - helper.addMessage( - messages_parser.get_message_type(), - messages_parser.get_message_direction(), - messages_parser.get_phone_number_from(), - messages_parser.get_phone_number_to(), - messages_parser.get_message_date_time(), - messages_parser.get_message_read_status(), - messages_parser.get_message_subject(), - messages_parser.get_message_text(), - messages_parser.get_thread_id() - ) - messages_parser.close() - - group_calllogs_parser = WhatsAppGroupCallLogsParser(message_db) - while group_calllogs_parser.next(): - helper.addCalllog( - group_calllogs_parser.get_call_direction(), - group_calllogs_parser.get_phone_number_from(), - group_calllogs_parser.get_phone_number_to(), - group_calllogs_parser.get_call_start_date_time(), - group_calllogs_parser.get_call_end_date_time(), - group_calllogs_parser.get_call_type() - ) - group_calllogs_parser.close() - - single_calllogs_parser = WhatsAppSingleCallLogsParser(message_db) - while single_calllogs_parser.next(): - helper.addCalllog( - single_calllogs_parser.get_call_direction(), - single_calllogs_parser.get_phone_number_from(), - single_calllogs_parser.get_phone_number_to(), - single_calllogs_parser.get_call_start_date_time(), - single_calllogs_parser.get_call_end_date_time(), - single_calllogs_parser.get_call_type() - ) - single_calllogs_parser.close() - - message_db.close() - except (SQLException, TskCoreException) as ex: - #Error parsing WhatsApp db - self._logger.log(Level.WARNING, "Error parsing WhatsApp Databases", ex) + except NoCurrentCaseException as ex: + #If there is no current case, bail out immediately. + self._logger.log(Level.WARNING, "No case currently open.", ex) self._logger.log(Level.WARNING, traceback.format_exec()) + + #Clean up open file handles. + for contact_db in contact_dbs: + contact_db.close() + + for calllog_and_message_db in calllog_and_message_dbs: + calllog_and_message_db.close() + + def parse_contacts(self, contacts_db, helper): + try: + contacts_parser = WhatsAppContactsParser(contacts_db) + while contacts_parser.next(): + helper.addContact( + contacts_parser.get_account_name(), + contacts_parser.get_contact_name(), + contacts_parser.get_phone(), + contacts_parser.get_home_phone(), + contacts_parser.get_mobile_phone(), + contacts_parser.get_email() + ) + contacts_parser.close() + except SQLException as ex: + self._logger.log(Level.WARNING, "Error querying the whatsapp database for contacts.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, + "Error adding whatsapp contact artifacts to the case database.", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except BlackboardException as ex: + self._logger.log(Level.WARNING, + "Error posting contact artifact to the blackboard.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + + def parse_calllogs(self, calllogs_db, helper): + try: + single_calllogs_parser = WhatsAppSingleCallLogsParser(calllogs_db) + while single_calllogs_parser.next(): + helper.addCalllog( + single_calllogs_parser.get_call_direction(), + single_calllogs_parser.get_phone_number_from(), + single_calllogs_parser.get_phone_number_to(), + single_calllogs_parser.get_call_start_date_time(), + single_calllogs_parser.get_call_end_date_time(), + single_calllogs_parser.get_call_type() + ) + single_calllogs_parser.close() + + group_calllogs_parser = WhatsAppGroupCallLogsParser(calllogs_db) + while group_calllogs_parser.next(): + helper.addCalllog( + group_calllogs_parser.get_call_direction(), + group_calllogs_parser.get_phone_number_from(), + group_calllogs_parser.get_phone_number_to(), + group_calllogs_parser.get_call_start_date_time(), + group_calllogs_parser.get_call_end_date_time(), + group_calllogs_parser.get_call_type() + ) + group_calllogs_parser.close() + except SQLException as ex: + self._logger.log(Level.WARNING, "Error querying the whatsapp database for calllogs.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, + "Error adding whatsapp calllog artifacts to the case database.", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except BlackboardException as ex: + self._logger.log(Level.WARNING, + "Error posting calllog artifact to the blackboard.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + + def parse_messages(self, messages_db, helper): + try: + messages_parser = WhatsAppMessagesParser(messages_db) + while messages_parser.next(): + helper.addMessage( + messages_parser.get_message_type(), + messages_parser.get_message_direction(), + messages_parser.get_phone_number_from(), + messages_parser.get_phone_number_to(), + messages_parser.get_message_date_time(), + messages_parser.get_message_read_status(), + messages_parser.get_message_subject(), + messages_parser.get_message_text(), + messages_parser.get_thread_id() + ) + messages_parser.close() + except SQLException as ex: + self._logger.log(Level.WARNING, "Error querying the whatsapp database for contacts.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, + "Error adding whatsapp contact artifacts to the case database.", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except BlackboardException as ex: + self._logger.log(Level.WARNING, + "Error posting contact artifact to the blackboard.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) class WhatsAppGroupCallLogsParser(TskCallLogsParser): """ @@ -150,17 +205,19 @@ class WhatsAppGroupCallLogsParser(TskCallLogsParser): def __init__(self, calllog_db): super(WhatsAppGroupCallLogsParser, self).__init__(calllog_db.runQuery( """ - SELECT CL.video_call, - CL.timestamp, - CL.duration, - CL.from_me, - J.raw_string as from_num, - group_concat(J.raw_string) AS group_members - FROM call_log_participant_v2 AS CLP - JOIN call_log AS CL - ON CL._id = CLP.call_log_row_id - JOIN jid AS J - ON J._id = CLP.jid_row_id + SELECT CL.video_call, + CL.timestamp, + CL.duration, + CL.from_me, + J1.raw_string AS from_id, + group_concat(J.raw_string) AS group_members + FROM call_log_participant_v2 AS CLP + JOIN call_log AS CL + ON CL._id = CLP.call_log_row_id + JOIN jid AS J + ON J._id = CLP.jid_row_id + JOIN jid as J1 + ON J1._id = CL.jid_row_id GROUP BY CL._id """ ) @@ -176,7 +233,7 @@ class WhatsAppGroupCallLogsParser(TskCallLogsParser): def get_phone_number_from(self): if self.get_call_direction() == self.INCOMING_CALL: - sender = self.result_set.getString("from_num") + sender = self.result_set.getString("from_id") return Account.Address(sender, sender) return super(WhatsAppGroupCallLogsParser, self).get_phone_number_from() @@ -349,12 +406,19 @@ class WhatsAppMessagesParser(TskMessagesParser): return self._WHATSAPP_MESSAGE_TYPE def get_phone_number_to(self): - group = self.result_set.getString("recipients") - if group is not None: - return Account.Address(self.result_set.getString("id"), group) if self.get_message_direction() == self.OUTGOING: + group = self.result_set.getString("recipients") + if group is not None: + group = group.split(",") + + recipients = [] + for token in group: + recipients.append(Account.Address(token, token)) + + return recipients + return Account.Address(self.result_set.getString("id"), - self.result_set.getString("id")) + self.result_set.getString("id")) return super(WhatsAppMessagesParser, self).get_phone_number_to() def get_phone_number_from(self): @@ -387,7 +451,7 @@ class WhatsAppMessagesParser(TskMessagesParser): mime_type = self.result_set.getString("attachment_mimetype") if mime_type is not None: attachment += "\nMIME type: " + mime_type - return appendAttachmentList(message, [attachment]) + return general.appendAttachmentList(message, [attachment]) return message def get_thread_id(self):