From db5dcf56075e7d65db7fff61a60258749430f89a Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Fri, 3 Oct 2025 09:37:58 -0300 Subject: [PATCH 1/9] Updated coreAccessoriesAcc.py to v2 Replaced manual plist parsing and timestamp conversion with utility functions --- scripts/artifacts/coreAccessoriesAcc.py | 54 ++++++++++++------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/scripts/artifacts/coreAccessoriesAcc.py b/scripts/artifacts/coreAccessoriesAcc.py index e0b5e752f..1c8a1c130 100644 --- a/scripts/artifacts/coreAccessoriesAcc.py +++ b/scripts/artifacts/coreAccessoriesAcc.py @@ -6,16 +6,28 @@ # Parses records found in the plists located in the accessoryd database related to accessories that are connected. # -import plistlib +__artifacts_v2__ = { + "get_coreAccessories": { + "name": "Core Accessories - AccessoryD", + "description": "Parses records found in the plists located in the accessoryd database related \ + to accessories that are connected.", + "author": "John Hyla", + "creation_date": "2023-08-01", + "last_update_date": "2025-10-01", + "requirements": "none", + "category": "Core Accessories", + "notes": "", + "paths": ('*/mobile/Library/CoreAccessories/Analytics/acc_analytics_accessoryd_v3.db*',), + "output_types": "standard", + } +} -from scripts.artifact_report import ArtifactHtmlReport -from scripts.ilapfuncs import logfunc, logdevinfo, tsv, is_platform_windows, open_sqlite_db_readonly -import datetime +from scripts.ilapfuncs import open_sqlite_db_readonly, artifact_processor, get_plist_content, convert_unix_ts_to_utc +@artifact_processor +def get_coreAccessories(context): -def get_coreAccessories(files_found, report_folder, seeker, wrap_text, timezone_offset): - - for file_found in files_found: + for file_found in context.get_files_found(): file_name = str(file_found) if file_name.endswith('acc_analytics_accessoryd_v3.db'): db_file = str(file_found) @@ -31,7 +43,6 @@ def get_coreAccessories(files_found, report_folder, seeker, wrap_text, timezone_ ''') all_rows = cursor.fetchall() - usageentries = len(all_rows) # Initialize the list to store dictionaries from each iteration temp_data = [] @@ -39,11 +50,10 @@ def get_coreAccessories(files_found, report_folder, seeker, wrap_text, timezone_ # Set to store all possible keys found during iterations all_keys = set() event_time_key = 'eventTime' - if usageentries > 0: - for row in all_rows: - pl = plistlib.loads(row[0]) - temp_data.append(pl) - all_keys.update(pl.keys()) + for row in all_rows: + pl = get_plist_content(row[0]) + temp_data.append(pl) + all_keys.update(pl.keys()) all_keys.remove(event_time_key) all_keys_list = [event_time_key] + list(all_keys) @@ -53,24 +63,12 @@ def get_coreAccessories(files_found, report_folder, seeker, wrap_text, timezone_ for key in all_keys_list: if key == event_time_key: event_time_value = row.get(event_time_key, None) - event_time_value = datetime.datetime.utcfromtimestamp(event_time_value / 1000.0).strftime( - '%Y-%m-%d %H:%M:%S') + event_time_value = convert_unix_ts_to_utc(event_time_value) row_values.append(event_time_value) else: row_values.append(row.get(key, None)) data_list.append(row_values) + all_keys_list[0] = (all_keys_list[0], 'datetime') - report = ArtifactHtmlReport('Core Accessories - AccessoryD') - report.start_artifact_report(report_folder, 'AccessoryD') - report.add_script() - report.write_artifact_data_table(all_keys_list, data_list, file_found) - report.end_artifact_report() - - -__artifacts__ = { - "coreAccessories": ( - "Core Accessories", - ('*/mobile/Library/CoreAccessories/Analytics/acc_analytics_accessoryd_v3.db*'), - get_coreAccessories) -} \ No newline at end of file + return (all_keys_list, data_list, db_file) From 42af4909840f0c1c71b43c787dc0bc9a0762681e Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Fri, 3 Oct 2025 09:38:51 -0300 Subject: [PATCH 2/9] Update coreAccessoriesUserEvent artifact to v2 Updated the artifact to use the new v2 structure, replaced direct plistlib usage with get_plist_content, and refactored the function to use the artifact_processor decorator and context object. Removed manual reporting code and improved timestamp conversion. --- scripts/artifacts/coreAccessoriesUserEvent.py | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/scripts/artifacts/coreAccessoriesUserEvent.py b/scripts/artifacts/coreAccessoriesUserEvent.py index e253be87c..babc22d69 100644 --- a/scripts/artifacts/coreAccessoriesUserEvent.py +++ b/scripts/artifacts/coreAccessoriesUserEvent.py @@ -6,16 +6,29 @@ # Parses records found in the plists located in the UserEventAgent database found in CoreAccessories # -import plistlib +__artifacts_v2__ = { + "get_coreAccessoriesUserEvent": { + "name": "Core Accessories - UserEventAgent", + "description": "Parses records found in the plists located in the UserEventAgent \ + database found in CoreAccessories", + "author": "John Hyla", + "creation_date": "2023-08-01", + "last_update_date": "2025-10-01", + "requirements": "none", + "category": "Core Accessories", + "notes": "", + "paths": ('*/mobile/Library/CoreAccessories/Analytics/acc_analytics_UserEventAgent_v3.db*',), + "output_types": "standard", + } +} -from scripts.artifact_report import ArtifactHtmlReport -from scripts.ilapfuncs import logfunc, logdevinfo, tsv, is_platform_windows, open_sqlite_db_readonly +from scripts.ilapfuncs import open_sqlite_db_readonly, artifact_processor, get_plist_content, convert_unix_ts_to_utc import datetime +@artifact_processor +def get_coreAccessoriesUserEvent(context): -def get_coreAccessoriesUserEvent(files_found, report_folder, seeker, wrap_text, timezone_offset): - - for file_found in files_found: + for file_found in context.get_files_found(): file_name = str(file_found) if file_name.endswith('acc_analytics_UserEventAgent_v3.db'): db_file = str(file_found) @@ -31,7 +44,6 @@ def get_coreAccessoriesUserEvent(files_found, report_folder, seeker, wrap_text, ''') all_rows = cursor.fetchall() - usageentries = len(all_rows) # Initialize the list to store dictionaries from each iteration temp_data = [] @@ -39,11 +51,10 @@ def get_coreAccessoriesUserEvent(files_found, report_folder, seeker, wrap_text, # Set to store all possible keys found during iterations all_keys = set() event_time_key = 'eventTime' - if usageentries > 0: - for row in all_rows: - pl = plistlib.loads(row[0]) - temp_data.append(pl) - all_keys.update(pl.keys()) + for row in all_rows: + pl = get_plist_content(row[0]) + temp_data.append(pl) + all_keys.update(pl.keys()) all_keys.remove(event_time_key) all_keys_list = [event_time_key] + list(all_keys) @@ -53,8 +64,7 @@ def get_coreAccessoriesUserEvent(files_found, report_folder, seeker, wrap_text, for key in all_keys_list: if key == event_time_key: event_time_value = row.get(event_time_key, None) - event_time_value = datetime.datetime.utcfromtimestamp(event_time_value / 1000.0).strftime( - '%Y-%m-%d %H:%M:%S') + event_time_value = convert_unix_ts_to_utc(event_time_value) row_values.append(event_time_value) elif key == "lightningDigitalID": value = row.get(key, None) @@ -76,18 +86,8 @@ def get_coreAccessoriesUserEvent(files_found, report_folder, seeker, wrap_text, else: row_values.append(row.get(key, None)) data_list.append(row_values) + + all_keys_list[0] = (all_keys_list[0], 'datetime') - report = ArtifactHtmlReport('Core Accessories - User Event Agent') - report.start_artifact_report(report_folder, 'User Event Agent') - report.add_script() - report.write_artifact_data_table(all_keys_list, data_list, file_found) - report.end_artifact_report() - - -__artifacts__ = { - "coreAccessoriesUserEvent": ( - "Core Accessories", - ('*/mobile/Library/CoreAccessories/Analytics/acc_analytics_UserEventAgent_v3.db*'), - get_coreAccessoriesUserEvent) -} \ No newline at end of file + return all_keys_list, data_list, db_file From 2dbe42fb8bc009819102d334629fa46501e0d30a Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:28:24 -0300 Subject: [PATCH 3/9] Refactor connectedDevices changed parameters to context --- scripts/artifacts/connectedDevices.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/artifacts/connectedDevices.py b/scripts/artifacts/connectedDevices.py index e958869b6..5a391e1b9 100644 --- a/scripts/artifacts/connectedDevices.py +++ b/scripts/artifacts/connectedDevices.py @@ -3,8 +3,8 @@ "name": "Connected Devices", "description": "Extracts information about connected devices from iTunes preferences", "author": "", - "version": "1.0", - "date": "2024-10-23", + "creation_date": "2024-10-23", + "last_update_date": "2025-11-21", "requirements": "none", "category": "Connected Devices", "notes": "", @@ -21,24 +21,24 @@ NAME_OFFSET = 157 @artifact_processor -def conDev(files_found, report_folder, seeker, wrap_text, timezone_offset): +def conDev(context): data_list = [] data_headers = ('User Name', 'Computer Name', 'File Offset', 'Source File') source_path = '' - for file_found in files_found: + for file_found in context.get_files_found(): source_path = file_found with open(file_found, "rb") as f: data = f.read() - logfunc(f"Data being interpreted for FRPD is of type: {type(data)}") + #logfunc(f"Data being interpreted for FRPD is of type: {type(data)}") magic_index = data.find(MAGIC_BYTES) if magic_index == -1: - logfunc("Magic bytes not found in iTunes Prefs FRPD") + #logfunc("Magic bytes not found in iTunes Prefs FRPD") continue - logfunc("Found magic bytes in iTunes Prefs FRPD... Finding Usernames and Desktop names now") + #logfunc("Found magic bytes in iTunes Prefs FRPD... Finding Usernames and Desktop names now") names = [] current_name = bytearray() @@ -72,4 +72,4 @@ def conDev(files_found, report_folder, seeker, wrap_text, timezone_offset): os.path.basename(file_found) )) - return data_headers, data_list, source_path + return data_headers, data_list, 'see Source File for more info' From 8ed06b27453e3517fb00190ede551878a3f45a57 Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:29:20 -0300 Subject: [PATCH 4/9] Refactor controlCenter context object removed unused imports changed plist logic to use function --- scripts/artifacts/controlCenter.py | 48 +++++++++++------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/scripts/artifacts/controlCenter.py b/scripts/artifacts/controlCenter.py index 29a2d4c6a..a5e96ed9b 100644 --- a/scripts/artifacts/controlCenter.py +++ b/scripts/artifacts/controlCenter.py @@ -3,8 +3,8 @@ "name": "Control Center Configuration", "description": "Parses controls/apps added to the Control Center", "author": "@KevinPagano3", - "version": "0.0.2", - "date": "2024-10-18", + "creation_date": "2024-10-18", + "last_update_date": "2025-11-21", "requirements": "none", "category": "Control Center", "notes": "", @@ -13,39 +13,27 @@ } } - -import plistlib - -from packaging import version -from scripts.artifact_report import ArtifactHtmlReport -from scripts.ilapfuncs import logfunc, logdevinfo, tsv, is_platform_windows -from scripts.ilapfuncs import artifact_processor +from scripts.ilapfuncs import artifact_processor, get_plist_file_content @artifact_processor -def controlCenter(files_found, report_folder, seeker, wrap_text, timezone_offset): +def controlCenter(context): data_list = [] - - for file_found in files_found: + for file_found in context.get_files_found(): file_found = str(file_found) - - with open(file_found, 'rb') as f: - pl = plistlib.load(f) + pl = get_plist_file_content(file_found) - for control_type, key, prefix in [ - ('Active', 'module-identifiers', 'A'), - ('User Toggled', 'userenabled-fixed-module-identifiers', 'U'), - ('Disabled', 'disabled-module-identifiers', 'D') - ]: - if key in pl and pl[key]: - for position, module in enumerate(pl[key], 1): - formatted_position = f"{prefix}-{position}" - data_list.append((formatted_position, module, control_type)) - - data_headers = ('Position', 'App Bundle', 'Control Type') - - if not data_list: - logfunc('No Control Center Configuration data available') + for control_type, key, prefix in [ + ('Active', 'module-identifiers', 'A'), + ('User Toggled', 'userenabled-fixed-module-identifiers', 'U'), + ('Disabled', 'disabled-module-identifiers', 'D') + ]: + if key in pl and pl[key]: + for position, module in enumerate(pl[key], 1): + formatted_position = f"{prefix}-{position}" + data_list.append((formatted_position, module, control_type, file_found)) + + data_headers = ('Position', 'App Bundle', 'Control Type', 'Source File') - return data_headers, data_list, file_found + return data_headers, data_list, 'see Source File for more info' From 8270bcfeac0979a40b95dd06ada2f0acc9fd6f78 Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:29:42 -0300 Subject: [PATCH 5/9] Update last_update_date removed unnecessary parentheses from the return statement --- scripts/artifacts/coreAccessoriesAcc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/artifacts/coreAccessoriesAcc.py b/scripts/artifacts/coreAccessoriesAcc.py index 1c8a1c130..1f2ff0820 100644 --- a/scripts/artifacts/coreAccessoriesAcc.py +++ b/scripts/artifacts/coreAccessoriesAcc.py @@ -13,7 +13,7 @@ to accessories that are connected.", "author": "John Hyla", "creation_date": "2023-08-01", - "last_update_date": "2025-10-01", + "last_update_date": "2025-11-21", "requirements": "none", "category": "Core Accessories", "notes": "", @@ -71,4 +71,4 @@ def get_coreAccessories(context): all_keys_list[0] = (all_keys_list[0], 'datetime') - return (all_keys_list, data_list, db_file) + return all_keys_list, data_list, db_file From 178f2013c5bb6ee613821e116d1908cd1a7d455c Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:30:35 -0300 Subject: [PATCH 6/9] Refactor DataUsage - context object - closed db before returning - removed unused imports. --- scripts/artifacts/DataUsage.py | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/scripts/artifacts/DataUsage.py b/scripts/artifacts/DataUsage.py index 4de4cc5a0..41d251205 100644 --- a/scripts/artifacts/DataUsage.py +++ b/scripts/artifacts/DataUsage.py @@ -3,9 +3,8 @@ "name": "Data Usage", "description": "Parses application network data usage", "author": "@KevinPagano3", - "version": "0.0.1", "creation_date": "2023-10-10", - "last_update_date": "2025-02-04", + "last_update_date": "2025-11-21", "requirements": "none", "category": "Network Usage", "notes": "", @@ -14,17 +13,14 @@ } } -import sqlite3 - from scripts.ilapfuncs import artifact_processor -from scripts.ilapfuncs import logfunc, tsv, timeline, is_platform_windows, open_sqlite_db_readonly, does_column_exist_in_db, convert_cocoa_core_data_ts_to_utc +from scripts.ilapfuncs import logfunc, open_sqlite_db_readonly, does_column_exist_in_db, convert_cocoa_core_data_ts_to_utc @artifact_processor -def get_DataUsage(files_found, report_folder, seeker, wrap_text, timezone_offset): - +def get_DataUsage(context): data_list = [] - for file_found in files_found: + for file_found in context.get_files_found(): file_found = str(file_found) if file_found.endswith('.sqlite'): @@ -62,7 +58,11 @@ def get_DataUsage(files_found, report_folder, seeker, wrap_text, timezone_offset process_split = row[4].split('/') data_list.append((lastconnected,firstused,lastused,row[3],process_split[0],row[5],row[6],row[7],row[8],row[9])) - data_headers = ((('Last Connect Timestamp','datetime'),('First Usage Timestamp','datetime'),('Last Usage Timestamp','datetime'),'Bundle Name','Process Name','Entry Type','Wifi In (Bytes)','Wifi Out (Bytes)','Mobile/WWAN In (Bytes)','Mobile/WWAN Out (Bytes)')) + db.close() + + data_headers = (('Last Connect Timestamp','datetime'), ('First Usage Timestamp','datetime'), ('Last Usage Timestamp','datetime'), + 'Bundle Name', 'Process Name', 'Entry Type', 'Wifi In (Bytes)', 'Wifi Out (Bytes)', 'Mobile/WWAN In (Bytes)', + 'Mobile/WWAN Out (Bytes)') return data_headers, data_list, file_found else: @@ -95,13 +95,12 @@ def get_DataUsage(files_found, report_folder, seeker, wrap_text, timezone_offset process_split = row[4].split('/') data_list.append((lastconnected,firstused,lastused,row[3],process_split[0],row[5],row[6],row[7])) - data_headers = ((('Last Connect Timestamp','datetime'),('First Usage Timestamp','datetime'),('Last Usage Timestamp','datetime'),'Bundle Name','Process Name','Entry Type','Mobile/WWAN In (Bytes)','Mobile/WWAN Out (Bytes)')) - return data_headers, data_list, file_found + db.close() - db.close() - - else: - continue + data_headers = (('Last Connect Timestamp','datetime'), ('First Usage Timestamp','datetime'), ('Last Usage Timestamp','datetime'), + 'Bundle Name', 'Process Name', 'Entry Type', 'Mobile/WWAN In (Bytes)', 'Mobile/WWAN Out (Bytes)') + return data_headers, data_list, file_found if not data_list: - logfunc('No Network Usage (DataUsage) - App Data available') \ No newline at end of file + logfunc('No Network Usage (DataUsage) - App Data available') + return (), [], '' \ No newline at end of file From 77a7ca764c666905935cd3d9022b4f611ad8b1a9 Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:31:13 -0300 Subject: [PATCH 7/9] Refactor deviceActivator - context object - removed unused imports --- scripts/artifacts/deviceActivator.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/scripts/artifacts/deviceActivator.py b/scripts/artifacts/deviceActivator.py index 45fb78b10..c7e6b4f61 100644 --- a/scripts/artifacts/deviceActivator.py +++ b/scripts/artifacts/deviceActivator.py @@ -3,8 +3,8 @@ "name": "iOS Device Activator Data", "description": "Extracts device information from activation data", "author": "", - "version": "1.0", - "date": "2024-10-29", + "creation_date": "2024-10-29", + "last_update_date": "2025-11-21", "requirements": "none", "category": "Device Information", "paths": ('*/mobile/Library/Logs/mobileactivationd/ucrt_oob_request.txt',), @@ -15,14 +15,15 @@ import re import base64 import os -from itertools import compress import xml.etree.ElementTree as ET -from scripts.ilapfuncs import logfunc, device_info, artifact_processor +from scripts.ilapfuncs import device_info, artifact_processor @artifact_processor -def deviceActivator(files_found, report_folder, seeker, wrap_text, timezone_offset): +def deviceActivator(context): data_list = [] + files_found = context.get_files_found() file_found = str(files_found[0]) + report_folder = context.get_report_folder() with open(file_found, 'r') as f_in: for line in f_in: @@ -60,9 +61,5 @@ def deviceActivator(files_found, report_folder, seeker, wrap_text, timezone_offs if x[0] == 'ModelNumber': device_info("Device Information", "Model Number", x[1], file_found) - if len(results) > 0: - data_headers = ('Property', 'Property Value') - return data_headers, results, file_found - else: - logfunc('No iOS Device Activator Data') - return None \ No newline at end of file + data_headers = ('Property', 'Property Value') + return data_headers, results, file_found \ No newline at end of file From b27d8fe253af53826d73227e04d3a8f2b7c0b42c Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:39:29 -0300 Subject: [PATCH 8/9] pylint errors --- scripts/artifacts/connectedDevices.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/scripts/artifacts/connectedDevices.py b/scripts/artifacts/connectedDevices.py index 5a391e1b9..2d1b4f1d5 100644 --- a/scripts/artifacts/connectedDevices.py +++ b/scripts/artifacts/connectedDevices.py @@ -13,7 +13,7 @@ } } -from scripts.ilapfuncs import logfunc, artifact_processor +from scripts.ilapfuncs import artifact_processor import os MAGIC_BYTES = b"\x01\x01\x80\x00\x00" @@ -24,10 +24,8 @@ def conDev(context): data_list = [] data_headers = ('User Name', 'Computer Name', 'File Offset', 'Source File') - source_path = '' for file_found in context.get_files_found(): - source_path = file_found with open(file_found, "rb") as f: data = f.read() From 5fc17c29e148a8e5bfdeb2c40c256179adc8d73e Mon Sep 17 00:00:00 2001 From: Maite Nigro <86933780+Maite2003@users.noreply.github.com> Date: Tue, 16 Dec 2025 16:09:11 -0300 Subject: [PATCH 9/9] fix lint errors --- scripts/artifacts/coreAccessoriesUserEvent.py | 1 - scripts/artifacts/deviceActivator.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/artifacts/coreAccessoriesUserEvent.py b/scripts/artifacts/coreAccessoriesUserEvent.py index babc22d69..1c53d4d37 100644 --- a/scripts/artifacts/coreAccessoriesUserEvent.py +++ b/scripts/artifacts/coreAccessoriesUserEvent.py @@ -23,7 +23,6 @@ } from scripts.ilapfuncs import open_sqlite_db_readonly, artifact_processor, get_plist_content, convert_unix_ts_to_utc -import datetime @artifact_processor def get_coreAccessoriesUserEvent(context): diff --git a/scripts/artifacts/deviceActivator.py b/scripts/artifacts/deviceActivator.py index c7e6b4f61..96c5267a7 100644 --- a/scripts/artifacts/deviceActivator.py +++ b/scripts/artifacts/deviceActivator.py @@ -25,7 +25,7 @@ def deviceActivator(context): file_found = str(files_found[0]) report_folder = context.get_report_folder() - with open(file_found, 'r') as f_in: + with open(file_found, 'r', encoding='utf-8') as f_in: for line in f_in: line = line.strip() alllines = alllines + line