# Mobile Verification Toolkit (MVT) # Copyright (c) 2021 MVT Project Developers. # See the file 'LICENSE' for usage and copying permissions, or find a copy at # https://github.com/mvt-project/mvt/blob/main/LICENSE import sqlite3 from base64 import b64encode from mvt.common.utils import convert_mactime_to_unix, convert_timestamp_to_iso from .base import IOSExtraction INTERACTIONC_BACKUP_IDS = [ "1f5a521220a3ad80ebfdc196978df8e7a2e49dee", ] INTERACTIONC_ROOT_PATHS = [ "private/var/mobile/Library/CoreDuet/People/interactionC.db", ] class InteractionC(IOSExtraction): """This module extracts data from InteractionC db.""" def __init__(self, file_path=None, base_folder=None, output_folder=None, fast_mode=False, log=None, results=[]): super().__init__(file_path=file_path, base_folder=base_folder, output_folder=output_folder, fast_mode=fast_mode, log=log, results=results) self.timestamps = [ "start_date", "end_date", "interactions_creation_date", "contacts_creation_date", "first_incoming_recipient_date", "first_incoming_sender_date", "first_outgoing_recipient_date", "last_incoming_sender_date", "last_incoming_recipient_date", "last_outgoing_recipient_date", ] def serialize(self, record): records = [] processed = [] for ts in self.timestamps: # Check if the record has the current timestamp. if ts not in record or not record[ts]: continue # Check if the timestamp was already processed. if record[ts] in processed: continue records.append({ "timestamp": record[ts], "module": self.__class__.__name__, "event": ts, "data": f"[{record['bundle_id']}] {record['account']} - from {record['sender_display_name']} " \ f"({record['sender_identifier']}) to {record['recipient_display_name']} " \ f"({record['recipient_identifier']}): {record['content']}" }) processed.append(record[ts]) return records def run(self): self._find_ios_database(backup_ids=INTERACTIONC_BACKUP_IDS, root_paths=INTERACTIONC_ROOT_PATHS) self.log.info("Found InteractionC database at path: %s", self.file_path) conn = sqlite3.connect(self.file_path) cur = conn.cursor() # TODO: Support all versions. # Taken from: # https://github.com/mac4n6/APOLLO/blob/master/modules/interaction_contact_interactions.txt cur.execute(""" SELECT ZINTERACTIONS.ZSTARTDATE, ZINTERACTIONS.ZENDDATE, ZINTERACTIONS.ZBUNDLEID, ZINTERACTIONS.ZACCOUNT, ZINTERACTIONS.ZTARGETBUNDLEID, CASE ZINTERACTIONS.ZDIRECTION WHEN '0' THEN 'INCOMING' WHEN '1' THEN 'OUTGOING' END 'DIRECTION', ZCONTACTS.ZDISPLAYNAME, ZCONTACTS.ZIDENTIFIER, ZCONTACTS.ZPERSONID, RECEIPIENTCONACT.ZDISPLAYNAME, RECEIPIENTCONACT.ZIDENTIFIER, RECEIPIENTCONACT.ZPERSONID, ZINTERACTIONS.ZRECIPIENTCOUNT, ZINTERACTIONS.ZDOMAINIDENTIFIER, ZINTERACTIONS.ZISRESPONSE, ZATTACHMENT.ZCONTENTTEXT, ZATTACHMENT.ZUTI, ZATTACHMENT.ZCONTENTURL, ZATTACHMENT.ZSIZEINBYTES, ZATTACHMENT.ZPHOTOLOCALIDENTIFIER, HEX(ZATTACHMENT.ZIDENTIFIER), ZATTACHMENT.ZCLOUDIDENTIFIER, ZCONTACTS.ZINCOMINGRECIPIENTCOUNT, ZCONTACTS.ZINCOMINGSENDERCOUNT, ZCONTACTS.ZOUTGOINGRECIPIENTCOUNT, ZINTERACTIONS.ZCREATIONDATE, ZCONTACTS.ZCREATIONDATE, ZCONTACTS.ZFIRSTINCOMINGRECIPIENTDATE, ZCONTACTS.ZFIRSTINCOMINGSENDERDATE, ZCONTACTS.ZFIRSTOUTGOINGRECIPIENTDATE, ZCONTACTS.ZLASTINCOMINGSENDERDATE, ZCONTACTS.ZLASTINCOMINGRECIPIENTDATE, ZCONTACTS.ZLASTOUTGOINGRECIPIENTDATE, ZCONTACTS.ZCUSTOMIDENTIFIER, ZINTERACTIONS.ZCONTENTURL, ZINTERACTIONS.ZLOCATIONUUID, ZINTERACTIONS.ZGROUPNAME, ZINTERACTIONS.ZDERIVEDINTENTIDENTIFIER, ZINTERACTIONS.Z_PK FROM ZINTERACTIONS LEFT JOIN ZCONTACTS ON ZINTERACTIONS.ZSENDER = ZCONTACTS.Z_PK LEFT JOIN Z_1INTERACTIONS ON ZINTERACTIONS.Z_PK == Z_1INTERACTIONS.Z_3INTERACTIONS LEFT JOIN ZATTACHMENT ON Z_1INTERACTIONS.Z_1ATTACHMENTS == ZATTACHMENT.Z_PK LEFT JOIN Z_2INTERACTIONRECIPIENT ON ZINTERACTIONS.Z_PK== Z_2INTERACTIONRECIPIENT.Z_3INTERACTIONRECIPIENT LEFT JOIN ZCONTACTS RECEIPIENTCONACT ON Z_2INTERACTIONRECIPIENT.Z_2RECIPIENTS== RECEIPIENTCONACT.Z_PK; """) names = [description[0] for description in cur.description] for item in cur: self.results.append({ "start_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[0])), "end_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[1])), "bundle_id": item[2], "account": item[3], "target_bundle_id": item[4], "direction": item[5], "sender_display_name": item[6], "sender_identifier": item[7], "sender_personid": item[8], "recipient_display_name": item[9], "recipient_identifier": item[10], "recipient_personid": item[11], "recipient_count": item[12], "domain_identifier": item[13], "is_response": item[14], "content": item[15], "uti": item[16], "content_url": item[17], "size": item[18], "photo_local_id": item[19], "attachment_id": item[20], "cloud_id": item[21], "incoming_recipient_count": item[22], "incoming_sender_count": item[23], "outgoing_recipient_count": item[24], "interactions_creation_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[25])) if item[25] else None, "contacts_creation_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[26])) if item[26] else None, "first_incoming_recipient_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[27])) if item[27] else None, "first_incoming_sender_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[28])) if item[28] else None, "first_outgoing_recipient_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[29])) if item[29] else None, "last_incoming_sender_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[30])) if item[30] else None, "last_incoming_recipient_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[31])) if item[31] else None, "last_outgoing_recipient_date": convert_timestamp_to_iso(convert_mactime_to_unix(item[32])) if item[32] else None, "custom_id": item[33], "location_uuid": item[35], "group_name": item[36], "derivied_intent_id": item[37], "table_id": item[38] }) cur.close() conn.close() self.log.info("Extracted a total of %d InteractionC events", len(self.results))