From f42d116c5dd5a0a7f39d8d9190c3753a2e81e711 Mon Sep 17 00:00:00 2001 From: Brigs Date: Thu, 2 Jul 2026 21:32:32 -0400 Subject: [PATCH 1/2] Add conversation views to 9 chat artifacts (Tier 1) Wire the LAVA conversation data_view onto existing columns for: SMS Messages (native mmssms.db), MeWe - Chat, Skype - Messages, Line - Messages, Viber - Messages, Bumble - Chat Messages, kleinanzeigen.de - Messages, Burner - Communications, and Life360 - Chat Messages. Every directionSentValue was verified against the value each query actually produces (e.g. SMS 'Sent' from the type CASE, Life360 'Yes' from message.sent, 'Outgoing' for the rest). Artifacts without a sender column use the sentMessageStaticLabel 'Local User' pattern. Metadata-only; no parsing or schema changes. last_update_date bumped on the touched artifacts. --- scripts/artifacts/Life360.py | 12 +++++++++++- scripts/artifacts/Viber.py | 12 +++++++++++- scripts/artifacts/bumble.py | 12 +++++++++++- scripts/artifacts/burner.py | 14 +++++++++++++- scripts/artifacts/kleinanzeigen.de.py | 14 +++++++++++++- scripts/artifacts/line.py | 12 +++++++++++- scripts/artifacts/mewe.py | 13 ++++++++++++- scripts/artifacts/skype.py | 12 +++++++++++- scripts/artifacts/smsmms.py | 13 ++++++++++++- 9 files changed, 105 insertions(+), 9 deletions(-) diff --git a/scripts/artifacts/Life360.py b/scripts/artifacts/Life360.py index 3c1139fe..9ed2df4a 100644 --- a/scripts/artifacts/Life360.py +++ b/scripts/artifacts/Life360.py @@ -5,13 +5,23 @@ "description": "Parses Life360 chat messages (messaging.db)", "author": "@KevinPagano3", "creation_date": "2024-01-17", - "last_update_date": "2024-01-17", + "last_update_date": "2026-07-03", "requirements": "none", "category": "Life360", "notes": "", "paths": ('*/com.life360.android.safetymapd/databases/messaging.db*',), "output_types": "all", "artifact_icon": "message-circle", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Thread ID", + "textColumn": "Message", + "directionColumn": "Message Sent", + "directionSentValue": "Yes", + "timeColumn": "Timestamp", + "senderColumn": "Sender Name" + } + }, }, "get_Life360_places": { "name": "Life360 - Places", diff --git a/scripts/artifacts/Viber.py b/scripts/artifacts/Viber.py index 5d8c0744..35f3594b 100755 --- a/scripts/artifacts/Viber.py +++ b/scripts/artifacts/Viber.py @@ -31,13 +31,23 @@ "description": "", "author": "", "creation_date": "2020-12-24", - "last_update_date": "2020-12-24", + "last_update_date": "2026-07-03", "requirements": "none", "category": "Viber", "notes": "", "paths": ('*/com.viber.voip/databases/*',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Thread ID", + "textColumn": "Message", + "directionColumn": "Direction", + "directionSentValue": "Outgoing", + "timeColumn": "Message Date", + "senderColumn": "From Phone Number" + } + }, }, "get_Viber_additional": { "name": "Viber - Additional", diff --git a/scripts/artifacts/bumble.py b/scripts/artifacts/bumble.py index 422db9d2..913785fa 100644 --- a/scripts/artifacts/bumble.py +++ b/scripts/artifacts/bumble.py @@ -11,10 +11,20 @@ "get_bumble_messages": { "name": "Bumble - Chat Messages", "description": "Bumble chat messages", - "author": "@KevinPagano3", "creation_date": "2022-11-07", "last_update_date": "2022-11-07", + "author": "@KevinPagano3", "creation_date": "2022-11-07", "last_update_date": "2026-07-03", "requirements": "none", "category": "Bumble", "paths": ('*/com.bumble.app/databases/ChatComDatabase*', '*/com.bumble.app/files/c2V0dGluZ3M='), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Conversation ID", + "textColumn": "Message Text", + "directionColumn": "Message Direction", + "directionSentValue": "Outgoing", + "timeColumn": "Created Timestamp", + "senderColumn": "Sender Name" + } + }, }, "get_bumble_matches": { "name": "Bumble - Matches", diff --git a/scripts/artifacts/burner.py b/scripts/artifacts/burner.py index 4769c281..03d0a283 100755 --- a/scripts/artifacts/burner.py +++ b/scripts/artifacts/burner.py @@ -18,13 +18,25 @@ "description": "Burner calls and text messages", "author": "Josh Hickman (josh@thebinaryhick.blog)", "creation_date": "2021-02-05", - "last_update_date": "2021-02-05", + "last_update_date": "2026-07-03", "requirements": "None", "category": "Burner", "notes": "", "paths": ('*/com.adhoclabs.burner/databases/burners.db',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Other Party Number", + "conversationLabelColumn": "Other Party Contact Name", + "textColumn": "Message", + "directionColumn": "Communication Direction", + "directionSentValue": "Outgoing", + "timeColumn": "Communication Time", + "senderColumn": "Other Party Contact Name", + "sentMessageStaticLabel": "Local User" + } + }, } } diff --git a/scripts/artifacts/kleinanzeigen.de.py b/scripts/artifacts/kleinanzeigen.de.py index a26d3552..65f7e75c 100644 --- a/scripts/artifacts/kleinanzeigen.de.py +++ b/scripts/artifacts/kleinanzeigen.de.py @@ -57,13 +57,25 @@ "description": "Extracts individual messages from the message database", "author": "@BrunoFischerGermany", "creation_date": "2024-04-13", - "last_update_date": "2024-04-13", + "last_update_date": "2026-07-03", "requirements": "none", "category": "kleinanzeigen.de App", "notes": "", "paths": ('*com.ebay.kleinanzeigen/databases/messageBoxDatabase.db*',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Counterparty", + "conversationLabelColumn": "Counterparty", + "textColumn": "Message Text", + "directionColumn": "Direction", + "directionSentValue": "Outgoing", + "timeColumn": "Timestamp", + "senderColumn": "Counterparty", + "sentMessageStaticLabel": "Local User" + } + }, } } diff --git a/scripts/artifacts/line.py b/scripts/artifacts/line.py index ebfd2ef9..84830877 100755 --- a/scripts/artifacts/line.py +++ b/scripts/artifacts/line.py @@ -18,13 +18,23 @@ "description": "", "author": "", "creation_date": "2021-03-15", - "last_update_date": "2021-03-15", + "last_update_date": "2026-07-03", "requirements": "none", "category": "Line", "notes": "", "paths": ('*/jp.naver.line.android/databases/**',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Thread ID", + "textColumn": "Message", + "directionColumn": "Direction", + "directionSentValue": "Outgoing", + "timeColumn": "Start Time", + "senderColumn": "From ID" + } + }, }, "get_line_calls": { "name": "Line - Call Logs", diff --git a/scripts/artifacts/mewe.py b/scripts/artifacts/mewe.py index ee3ac969..19ad08b4 100644 --- a/scripts/artifacts/mewe.py +++ b/scripts/artifacts/mewe.py @@ -5,13 +5,24 @@ "description": "", "author": "", "creation_date": "2021-11-10", - "last_update_date": "2021-11-10", + "last_update_date": "2026-07-03", "requirements": "none", "category": "MeWe", "notes": "", "paths": ('*/com.mewe/databases/app_database',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Thread Id", + "conversationLabelColumn": "Thread Name", + "textColumn": "Message Text", + "directionColumn": "Message Direction", + "directionSentValue": "Sent", + "timeColumn": "Timestamp", + "senderColumn": "User Name" + } + }, }, "get_mewe_session": { "name": "MeWe - SGSession", diff --git a/scripts/artifacts/skype.py b/scripts/artifacts/skype.py index 4ca6fb23..473a5860 100755 --- a/scripts/artifacts/skype.py +++ b/scripts/artifacts/skype.py @@ -18,13 +18,23 @@ "description": "", "author": "", "creation_date": "2021-03-15", - "last_update_date": "2021-03-15", + "last_update_date": "2026-07-03", "requirements": "none", "category": "Skype", "notes": "", "paths": ('*/com.skype.raider/databases/live*',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Thread ID", + "textColumn": "Content", + "directionColumn": "Direction", + "directionSentValue": "Outgoing", + "timeColumn": "Send Time", + "senderColumn": "From ID" + } + }, }, "get_skype_contacts": { "name": "Skype - Contacts", diff --git a/scripts/artifacts/smsmms.py b/scripts/artifacts/smsmms.py index 2a1d3563..59ae3509 100755 --- a/scripts/artifacts/smsmms.py +++ b/scripts/artifacts/smsmms.py @@ -5,13 +5,24 @@ "description": "SMS messages from mmssms.db (incl. LG extended types and Samsung spam_sms)", "author": "", "creation_date": "2020-03-10", - "last_update_date": "2020-03-10", + "last_update_date": "2026-07-03", "requirements": "none", "category": "SMS & MMS", "notes": "", "paths": ('*/com.android.providers.telephony/databases/mmssms*',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Thread ID", + "textColumn": "Body", + "directionColumn": "Type", + "directionSentValue": "Sent", + "timeColumn": "Date", + "senderColumn": "Address", + "sentMessageStaticLabel": "Local User" + } + }, }, "get_sms_mms_mms": { "name": "MMS Messages", From 535f8fa62bffd855f7a3492cf14d9c7e0761c6e2 Mon Sep 17 00:00:00 2001 From: Brigs Date: Thu, 2 Jul 2026 22:41:11 -0400 Subject: [PATCH 2/2] Tier 2: derive MMS/backup directions, add 3 more conversation views - MMS Messages: select pdu.msg_box and emit a Direction column mapped via the documented Telephony.Mms constants (1 Received / 2 Sent / 3 Draft / 4 Outbox), wording mirroring the SMS type CASE; conversation view with the Media column wired as mediaColumn. - SMS and MMS Backup: map the backup XML's raw Telephony.Sms type into a Direction column (raw Type retained for fidelity); views for both SMS (thread=Address) and MMS (thread=Recipients, direction=mapped Message Box which already emits 'Sent'). --- scripts/artifacts/smsmms.py | 25 +++++++++++++++++++---- scripts/artifacts/smsmmsBackup.py | 33 +++++++++++++++++++++++++++---- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/scripts/artifacts/smsmms.py b/scripts/artifacts/smsmms.py index 59ae3509..60c2f606 100755 --- a/scripts/artifacts/smsmms.py +++ b/scripts/artifacts/smsmms.py @@ -29,7 +29,7 @@ "description": "MMS messages and attachments from mmssms.db", "author": "", "creation_date": "2020-03-10", - "last_update_date": "2020-03-10", + "last_update_date": "2026-07-03", "requirements": "none", "category": "SMS & MMS", "notes": "", @@ -38,6 +38,18 @@ '*/com.android.providers.telephony/parts/*'), "output_types": "standard", "artifact_icon": "image", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Thread ID", + "textColumn": "Body", + "directionColumn": "Direction", + "directionSentValue": "Sent", + "timeColumn": "Date", + "senderColumn": "From Address", + "sentMessageStaticLabel": "Local User", + "mediaColumn": "Media" + } + }, } } @@ -64,7 +76,7 @@ ''' _MMS_QUERY = ''' - SELECT pdu._id as mms_id, thread_id, pdu.date as date, pdu.date_sent as date_sent, read, + SELECT pdu._id as mms_id, thread_id, pdu.date as date, pdu.date_sent as date_sent, read, pdu.msg_box as msg_box, (SELECT address FROM addr WHERE pdu._id=addr.msg_id and addr.type=0x89) as "FROM", (SELECT address FROM addr WHERE pdu._id=addr.msg_id and addr.type=0x97) as "TO", (SELECT address FROM addr WHERE pdu._id=addr.msg_id and addr.type=0x82) as "CC", @@ -75,6 +87,10 @@ ''' +# Telephony.Mms msg_box values; wording mirrors the SMS type CASE +_MMS_BOX_DIRECTION = {1: 'Received', 2: 'Sent', 3: 'Draft', 4: 'Outbox'} + + def _ms_to_utc(value): if not value: return '' @@ -154,10 +170,11 @@ def get_sms_mms_mms(files_found, report_folder, seeker, wrap_text): body = str(data_path) else: body = r['text'] or '' + direction = _MMS_BOX_DIRECTION.get(r['msg_box'], r['msg_box']) data_list.append((_sec_to_utc(r['date']), r['mms_id'], r['thread_id'], _sec_to_utc(r['date_sent']), r['read'], r['FROM'], r['TO'], r['CC'], - r['BCC'], body, media_ref)) + r['BCC'], body, media_ref, direction)) data_headers = (('Date', 'datetime'), 'MSG ID', 'Thread ID', ('Date Sent', 'datetime'), 'Read', - 'From Address', 'To Address', 'Cc', 'Bcc', 'Body', ('Media', 'media')) + 'From Address', 'To Address', 'Cc', 'Bcc', 'Body', ('Media', 'media'), 'Direction') return data_headers, data_list, source_path diff --git a/scripts/artifacts/smsmmsBackup.py b/scripts/artifacts/smsmmsBackup.py index cbf44759..9f2b3783 100755 --- a/scripts/artifacts/smsmmsBackup.py +++ b/scripts/artifacts/smsmmsBackup.py @@ -5,26 +5,48 @@ "description": "MMS messages recovered from backup.ab telephony backup files", "author": "", "creation_date": "2024-08-15", - "last_update_date": "2024-08-15", + "last_update_date": "2026-07-03", "requirements": "none", "category": "SMS & MMS from backup.ab", "notes": "", "paths": ('*/com.android.providers.telephony/d_f/*_backup',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Recipients", + "textColumn": "Body", + "directionColumn": "Message Box", + "directionSentValue": "Sent", + "timeColumn": "Date", + "senderColumn": "Recipients", + "sentMessageStaticLabel": "Local User" + } + }, }, "get_sms_from_backup": { "name": "SMS and MMS Backup - SMS", "description": "SMS messages recovered from backup.ab telephony backup files", "author": "", "creation_date": "2024-08-15", - "last_update_date": "2024-08-15", + "last_update_date": "2026-07-03", "requirements": "none", "category": "SMS & MMS from backup.ab", "notes": "", "paths": ('*/com.android.providers.telephony/d_f/*_backup',), "output_types": "standard", "artifact_icon": "message-square", + "data_views": { + "conversation": { + "conversationDiscriminatorColumn": "Address", + "textColumn": "Body", + "directionColumn": "Direction", + "directionSentValue": "Sent", + "timeColumn": "Date", + "senderColumn": "Address", + "sentMessageStaticLabel": "Local User" + } + }, } } @@ -42,6 +64,8 @@ EPOCH = datetime.datetime(1970, 1, 1, tzinfo=datetime.timezone.utc) MMS_BOX = {'0': 'All messages', '1': 'Inbox', '2': 'Sent', '3': 'Drafts', '4': 'Outbox', '5': 'Failed'} +# Telephony.Sms type values stored in the backup XML +SMS_TYPE = {'1': 'Received', '2': 'Sent', '3': 'Draft', '4': 'Outbox', '5': 'Failed', '6': 'Queued'} def ReadUnixTimeMs(unix_time): @@ -128,7 +152,8 @@ def get_sms_from_backup(files_found, report_folder, seeker, wrap_text): data_list.append(( ReadUnixTimeMs(sms.get('date', 0)), ReadUnixTimeMs(sms.get('date_sent', 0)), sms.get('read', ''), sms.get('type', ''), body, - ', '.join(sms.get('recipients', [])), sms.get('address', ''), sms.get('status', ''))) + ', '.join(sms.get('recipients', [])), sms.get('address', ''), sms.get('status', ''), + SMS_TYPE.get(sms.get('type', ''), sms.get('type', '')))) - data_headers = (('Date', 'datetime'), ('Date sent', 'datetime'), 'Read', 'Type', 'Body', 'recipients', ('Address', 'phonenumber'), 'Status') + data_headers = (('Date', 'datetime'), ('Date sent', 'datetime'), 'Read', 'Type', 'Body', 'recipients', ('Address', 'phonenumber'), 'Status', 'Direction') return data_headers, data_list, source_path