From f215bddb2cfd659ea5cf7fef7d6332a1836eb8f9 Mon Sep 17 00:00:00 2001 From: Johann POLEWCZYK Date: Mon, 20 Jan 2025 08:15:04 +0100 Subject: [PATCH] Update iTunesBackupInfo.py for lava output --- ileapp.py | 12 +- scripts/artifacts/iTunesBackupInfo.py | 226 +++++++++++++------------- 2 files changed, 127 insertions(+), 111 deletions(-) diff --git a/ileapp.py b/ileapp.py index f545a40a..05b559f3 100755 --- a/ileapp.py +++ b/ileapp.py @@ -368,14 +368,22 @@ def crunch_artifacts( if os.path.exists(info_plist_path): # process_artifact([info_plist_path], 'iTunesBackupInfo', 'Device Info', seeker, out_params.report_folder_base) #plugin.method([info_plist_path], out_params.report_folder_base, seeker, wrap_text) - report_folder = os.path.join(out_params.report_folder_base, '_HTML') + report_folder = os.path.join(out_params.report_folder_base, '_HTML', 'iTunes Backup') if not os.path.exists(report_folder): try: os.makedirs(report_folder) except (FileExistsError, FileNotFoundError) as ex: logfunc('Error creating report directory at path {}'.format(report_folder)) logfunc('Error was {}'.format(str(ex))) - loader["iTunesBackupInfo"].method([info_plist_path], out_params.report_folder_base, seeker, wrap_text, time_offset) + loader["iTunesBackupInfo"].method([info_plist_path], report_folder, seeker, wrap_text, time_offset) + report_folder = os.path.join(out_params.report_folder_base, '_HTML', 'Installed Apps') + if not os.path.exists(report_folder): + try: + os.makedirs(report_folder) + except (FileExistsError, FileNotFoundError) as ex: + logfunc('Error creating report directory at path {}'.format(report_folder)) + logfunc('Error was {}'.format(str(ex))) + loader["iTunesBackupInstalledApplications"].method([info_plist_path], report_folder, seeker, wrap_text, time_offset) #del search_list['lastBuild'] # removing lastBuild as this takes its place print([info_plist_path]) # TODO Remove special consideration for itunes? Merge into main search else: diff --git a/scripts/artifacts/iTunesBackupInfo.py b/scripts/artifacts/iTunesBackupInfo.py index 7677049a..9f30ad70 100644 --- a/scripts/artifacts/iTunesBackupInfo.py +++ b/scripts/artifacts/iTunesBackupInfo.py @@ -3,141 +3,149 @@ "name": "iTunes Backup Information", "description": "Extract information from the Info.plist file of an iTunes backup", "author": "@AlexisBrignoni - @johannplw", - "version": "0.1", - "date": "2023-10-11", + "creation_date": "2023-10-11", + "last_update_date": "2025-01-20", "requirements": "none", - "category": "iTunes Backup Info", + "category": "iTunes Backup", "notes": "", - "paths": ('*Info.plist', '*info.plist'), - "function": "get_iTunesBackupInfo" + "paths": ('*Info.plist',), + "output_types": ["html", "tsv", "lava"], + "artifact_icon": "refresh-cw" + }, + "iTunesBackupInstalledApplications": { + "name": "iTunes Backup - Installed Applications", + "description": "Extract information about installed applications from the Info.plist file of an iTunes backup", + "author": "@johannplw", + "creation_date": "2023-10-11", + "last_update_date": "2025-01-20", + "requirements": "none", + "category": "Installed Apps", + "notes": "", + "paths": ('*Info.plist',), + "output_types": "standard", + "artifact_icon": "package", } } +import inspect import datetime import plistlib import scripts.artifacts.artGlobals -from scripts.artifact_report import ArtifactHtmlReport -from scripts.ilapfuncs import logfunc, logdevinfo, tsv from base64 import b64encode -def get_iTunesMetadata(applications): - app_list=[] - - for bundle_id, app_data in applications.items(): - apple_id = '' - purchase_date = '' - binary_app_icon = app_data.get('PlaceholderIcon', '') - if binary_app_icon: - base64_icon = b64encode(binary_app_icon).decode('utf-8') - icon_tag = f'{bundle_id} App Icon' - else: - icon_tag = '' - - if 'iTunesMetadata' in app_data.keys(): - itunes_metadata = plistlib.loads(app_data['iTunesMetadata']) - item_name = itunes_metadata.get('itemName', '') - artist_name = itunes_metadata.get('artistName', '') - version = itunes_metadata.get('bundleShortVersionString', '') - genre = itunes_metadata.get('genre', '') - store_cohort = itunes_metadata.get('storeCohort', '') - if 'date=' in store_cohort: - date_start = store_cohort.find('date=') + 5 - unix_install_timestamp = store_cohort[date_start:date_start + 10] - install_date = datetime.datetime.fromtimestamp(int(unix_install_timestamp)).strftime('%Y-%m-%d') - download_info = itunes_metadata.get('com.apple.iTunesStore.downloadInfo', '') - if download_info: - account_info = download_info.get('accountInfo', '') - if account_info: - apple_id = account_info.get('AppleID') - purchase_date = download_info.get('purchaseDate', '') - if purchase_date: - purchase_date = purchase_date[:-1].replace('T', ' ') - release_date = itunes_metadata.get('releaseDate', '') - source_app = itunes_metadata.get('sourceApp', '') - auto_download = itunes_metadata.get('is-auto-download', '') - purchased_redownload = itunes_metadata.get('is-purchased-redownload', '') - factory_install = itunes_metadata.get('isFactoryInstall', '') - side_loaded = itunes_metadata.get('sideLoadedDeviceBasedVPP', '') - game_center_enabled = itunes_metadata.get('gameCenterEnabled', '') - game_center_ever_enabled = itunes_metadata.get('gameCenterEverEnabled', '') - messages_extension = itunes_metadata.get('hasMessagesExtension', '') - app_list.append((bundle_id, icon_tag, item_name, artist_name, version, genre, - install_date, apple_id, purchase_date, release_date, - source_app, auto_download, purchased_redownload, - factory_install, side_loaded, game_center_enabled, - game_center_ever_enabled, messages_extension)) - elif 'info_plist_bundle_id' in app_data.keys(): - app_info = (bundle_id, ) - app_info += ('',) * 17 - app_list.append(app_info) - - return app_list +from scripts.ilapfuncs import artifact_processor, get_file_path, get_plist_file_content, device_info, logfunc -def get_iTunesBackupInfo(files_found, report_folder, seeker, wrap_text, timezone_offset): +@artifact_processor +def iTunesBackupInfo(files_found, report_folder, seeker, wrap_text, timezone_offset): + source_path = get_file_path(files_found, "Info.plist") data_list = [] - apps_iTunesMetadata = [] installed_apps = None apps = None - file_found = str(files_found[0]) - with open(file_found, "rb") as fp: - pl = plistlib.load(fp) - for key, val in pl.items(): - if isinstance(val, str) or isinstance(val, int) or isinstance(val, datetime.datetime): - data_list.append((key, val)) - if key == ('Product Version'): - scripts.artifacts.artGlobals.versionf = val - logfunc(f"iOS version: {val}") + pl = get_plist_file_content(source_path) + for key, val in pl.items(): + if isinstance(val, str) or isinstance(val, int) or isinstance(val, datetime.datetime): + data_list.append((key, val)) + if key == ('Product Version'): + scripts.artifacts.artGlobals.versionf = val + logfunc(f"iOS version: {val}") + elif key == "Applications": + apps = val + elif key == "Installed Applications": + installed_apps = set(val) - elif key == "Applications": - apps = val - - elif key == "Installed Applications": - installed_apps = set(val) - - if installed_apps and apps: - apps_bundle_ids = set(apps) - if len(installed_apps) > len(apps_bundle_ids): - installed_apps = installed_apps.symmetric_difference(apps_bundle_ids) - for installed_app in installed_apps: - apps[installed_app] = {'info_plist_bundle_id': installed_app} - apps_iTunesMetadata = get_iTunesMetadata(apps) - elif installed_apps: + if not installed_apps and apps: data_list.append(("Installed Applications", ', '.join(installed_apps))) - + # Device details keys = [data[0] for data in data_list] - device_info = ('Product Name', 'Product Type', 'Device Name', 'Product Version', 'Build Version', + dev_info = ('Product Name', 'Product Type', 'Device Name', 'Product Version', 'Build Version', 'Serial Number', 'MEID', 'IMEI', 'IMEI 2', 'ICCID', 'Phone Number', 'Unique Identifier', 'Last Backup Date') - for info in device_info: + for info in dev_info: if info in keys: index = keys.index(info) info_key = data_list[index][0] value_key = data_list[index][1] - logdevinfo(f"{info_key}: {value_key}") + device_info("iTunes Backup Information", info_key, value_key, source_path) - report = ArtifactHtmlReport('iTunes Backup') - report.start_artifact_report(report_folder, 'iTunes Backup Information') - report.add_script() - data_headers = ('Key','Values') - report.write_artifact_data_table(data_headers, data_list, file_found) - report.end_artifact_report() - - tsvname = 'iTunes Backup' - tsv(report_folder, data_headers, data_list, tsvname) + data_headers = ('Property', 'Property Value') + return data_headers, data_list, source_path - if apps_iTunesMetadata: - report = ArtifactHtmlReport('iTunes Backup - Installed Applications') - report.start_artifact_report(report_folder, 'iTunes Backup Installed Applications') - report.add_script() - data_headers = ('Bundle ID', 'App Icon', 'Item Name', 'Artist Name', 'Version', - 'Genre', 'Install Date', 'Downloaded by', 'Purchase Date', - 'Release Date', 'Source App', 'Auto Download', - 'Purchased Redownload', 'Factory Install', 'Side Loaded', - 'Game Center Enabled', 'Game Center Ever Enabled', - 'Messages Extension') - report.write_artifact_data_table(data_headers, apps_iTunesMetadata, file_found, html_no_escape=['App Icon']) - report.end_artifact_report() +@artifact_processor +def iTunesBackupInstalledApplications(files_found, report_folder, seeker, wrap_text, timezone_offset): + source_path = get_file_path(files_found, "Info.plist") + data_list = [] + artifact_info = inspect.stack()[0] + installed_apps = None + apps = None + + pl = get_plist_file_content(source_path) + for key, val in pl.items(): + if key == "Applications": + apps = val + if key == "Installed Applications": + installed_apps = set(val) + + if installed_apps and apps: + apps_bundle_ids = set(apps) + if len(installed_apps) > len(apps_bundle_ids): + installed_apps = installed_apps.symmetric_difference(apps_bundle_ids) + for installed_app in installed_apps: + apps[installed_app] = {'info_plist_bundle_id': installed_app} + for bundle_id, app_data in apps.items(): + apple_id = '' + purchase_date = '' + icon = app_data.get('PlaceholderIcon', '') + if icon: + base64_icon = b64encode(icon).decode('utf-8') + icon_tag = f'{bundle_id} App Icon' + else: + icon_tag = '' + if 'iTunesMetadata' in app_data.keys(): + itunes_metadata = plistlib.loads(app_data['iTunesMetadata']) + item_name = itunes_metadata.get('itemName', '') + artist_name = itunes_metadata.get('artistName', '') + version = itunes_metadata.get('bundleShortVersionString', '') + genre = itunes_metadata.get('genre', '') + store_cohort = itunes_metadata.get('storeCohort', '') + if 'date=' in store_cohort: + date_start = store_cohort.find('date=') + 5 + unix_install_timestamp = store_cohort[date_start:date_start + 10] + install_date = datetime.datetime.fromtimestamp(int(unix_install_timestamp)).strftime('%Y-%m-%d') + download_info = itunes_metadata.get('com.apple.iTunesStore.downloadInfo', '') + if download_info: + account_info = download_info.get('accountInfo', '') + if account_info: + apple_id = account_info.get('AppleID') + purchase_date = download_info.get('purchaseDate', '') + if purchase_date: + purchase_date = purchase_date[:-1].replace('T', ' ') + release_date = itunes_metadata.get('releaseDate', '') + source_app = itunes_metadata.get('sourceApp', '') + auto_download = itunes_metadata.get('is-auto-download', '') + purchased_redownload = itunes_metadata.get('is-purchased-redownload', '') + factory_install = itunes_metadata.get('isFactoryInstall', '') + side_loaded = itunes_metadata.get('sideLoadedDeviceBasedVPP', '') + game_center_enabled = itunes_metadata.get('gameCenterEnabled', '') + game_center_ever_enabled = itunes_metadata.get('gameCenterEverEnabled', '') + messages_extension = itunes_metadata.get('hasMessagesExtension', '') + data_list.append((bundle_id, icon_tag, item_name, artist_name, version, genre, + install_date, apple_id, purchase_date, release_date, + source_app, auto_download, purchased_redownload, + factory_install, side_loaded, game_center_enabled, + game_center_ever_enabled, messages_extension)) + elif 'info_plist_bundle_id' in app_data.keys(): + app_info = (bundle_id, ) + app_info += ('',) * 17 + data_list.append(app_info) + + data_headers = ('Bundle ID', ('App Icon', 'media'), 'Item Name', 'Artist Name', 'Version', + 'Genre', 'Install Date', 'Downloaded by', 'Purchase Date', + 'Release Date', 'Source App', 'Auto Download', + 'Purchased Redownload', 'Factory Install', 'Side Loaded', + 'Game Center Enabled', 'Game Center Ever Enabled', + 'Messages Extension') + return data_headers, data_list, source_path \ No newline at end of file