From 9d5435a51b436691960c77717970950730701325 Mon Sep 17 00:00:00 2001 From: Clement-155 Date: Sun, 1 Dec 2024 20:49:02 +0700 Subject: [PATCH 01/10] Add Android Native Downloads Sqlite Plugin --- .../android_native_downloads.py | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 plaso/parsers/sqlite_plugins/android_native_downloads.py diff --git a/plaso/parsers/sqlite_plugins/android_native_downloads.py b/plaso/parsers/sqlite_plugins/android_native_downloads.py new file mode 100644 index 0000000000..a90ec23a5a --- /dev/null +++ b/plaso/parsers/sqlite_plugins/android_native_downloads.py @@ -0,0 +1,172 @@ +# -*- coding: utf-8 -*- +"""SQLite parser plugin for Android Native Downloads (DownloadManager API) database files.""" +import re + +from dfdatetime import java_time as dfdatetime_java_time + +from plaso.containers import events +from plaso.parsers import sqlite +from plaso.parsers.sqlite_plugins import interface + + +class AndroidNativeDownloadsEventData(events.EventData): + """Android Native Downloads (DownloadProvider) event data. + + Also see : + STATUS_* and DESTINATION_* constants: https://android.googlesource.com/platform/frameworks/base/ + +/refs/heads/master/core/java/android/provider/Downloads.java + ERROR_*, PAUSED_*, and VISIBILITY_* constants: https://android.googlesource.com/platform/frameworks/base/ + +/refs/heads/main/core/java/android/app/DownloadManager.java + Basis for what columns to extract: https://forensafe.com/blogs/Android_Downloads.html + + Attributes: + lastmod (dfdatetime.DateTimeValues): Last modified date and time of downloaded file. + id (int): An identifier for a particular download, unique across the system. + uri (str): Downloaded URI. + mimetype (str): Internet Media Type of the downloaded file. + total_bytes (int): Total size of the download in bytes. + current_bytes (int): Number of bytes download so far. + status (int): Holds one of the STATUS_* constants. + If an error occurred, this holds the HTTP Status Code for an HTTP error (RFC 2616), + otherwise it holds one of the ERROR_* constants. + If the download is paused, this holds one of the PAUSED_* constants. + saved_to (str): Path to the downloaded file on disk. + deleted (bool): Set to true if this download is deleted. It is completely removed from the database when + MediaProvider database also deletes the metadata associated with this downloaded file. + notification_package (str): Package name associated with notification of a running download. + title (str): Title of the download. + media_provider_uri (str): The URI to the corresponding entry in MediaProvider for this downloaded entry. It is + used to delete the entries from MediaProvider database when it is deleted from the + downloaded list. + error_msg (str): The column with errorMsg for a failed downloaded. Used only for debugging purposes. + is_visible_in_downloads_ui (int) : Whether or not this download should be displayed in the system's Downloads UI. + Defaults to true. + destination (int): The name of the column containing the flag that controls the destination of the download. + See the DESTINATION_* constants for a list of legal values. + ui_visibility (int): The name of the column containing the flags that controls whether the download is displayed by + the UI. See the VISIBILITY_* constants for a list of legal values. + e_tag (str) ETag of this file. + description (str): The client-supplied description of this download. This will be displayed in system + notifications. Defaults to the empty string. + """ + + DATA_TYPE = 'android:sqlite:downloads' + + def __init__(self): + """Initializes event data.""" + super(AndroidNativeDownloadsEventData, self).__init__(data_type=self.DATA_TYPE) + self.lastmod = None + self.id = None + self.uri = None + self.mimetype = None + self.total_bytes = None + self.current_bytes = None + self.status = None + self.saved_to = None + self.deleted = None + self.notification_package = None + self.title = None + self.media_provider_uri = None + self.error_msg = None + self.is_visible_in_downloads_ui = None + self.destination = None + self.ui_visibility = None + self.e_tag = None + self.description = None + + +class AndroidNativeDownloadsPlugin(interface.SQLitePlugin): + """SQLite parser plugin for Android native downloads database file. + + The Android native downloads database file is typically stored in: + com.android.providers.downloads/databases/downloads.db + """ + + NAME = 'android_native_downloads' + DATA_FORMAT = 'Android native downloads SQLite database (downloads.db) file' + + REQUIRED_STRUCTURE = { + 'downloads': frozenset(['_id', 'uri', '_data', 'mimetype', 'destination', 'visibility', 'status', 'lastmod', + 'notificationpackage', 'total_bytes', 'current_bytes', 'etag', 'title', 'description', + 'is_visible_in_downloads_ui', 'mediaprovider_uri', 'deleted', 'errorMsg'])} + QUERIES = [ + ('SELECT _id, uri, _data, mimetype, destination, visibility, status, lastmod, ' + 'notificationpackage, total_bytes, current_bytes, etag, title, description, ' + 'is_visible_in_downloads_ui, mediaprovider_uri, deleted, errorMsg FROM downloads', + 'ParseDownloadsRow')] + + SCHEMAS = [{ + 'android_metadata': ( + 'CREATE TABLE android_metadata (locale TEXT) '), + 'downloads': ( + 'CREATE TABLE downloads(_id INTEGER PRIMARY KEY AUTOINCREMENT, uri TEXT, method INTEGER, ' + 'entity TEXT, no_integrity BOOLEAN, hint TEXT, otaupdate BOOLEAN, _data TEXT, mimetype TEXT, ' + 'destination INTEGER, no_system BOOLEAN, visibility INTEGER, control INTEGER, status INTEGER, ' + 'numfailed INTEGER, lastmod BIGINT, notificationpackage TEXT, notificationclass TEXT, ' + 'notificationextras TEXT, cookiedata TEXT, useragent TEXT, referer TEXT, total_bytes INTEGER, ' + 'current_bytes INTEGER, etag TEXT, uid INTEGER, otheruid INTEGER, title TEXT, description TEXT, ' + 'scanned BOOLEAN, is_public_api INTEGER NOT NULL DEFAULT 0, allow_roaming INTEGER NOT NULL DEFAULT 0, ' + 'allowed_network_types INTEGER NOT NULL DEFAULT 0, is_visible_in_downloads_ui INTEGER NOT NULL DEFAULT 1, ' + 'bypass_recommended_size_limit INTEGER NOT NULL DEFAULT 0, mediaprovider_uri TEXT, ' + 'deleted BOOLEAN NOT NULL DEFAULT 0, errorMsg TEXT, allow_metered INTEGER NOT NULL DEFAULT 1, ' + 'allow_write BOOLEAN NOT NULL DEFAULT 0, flags INTEGER NOT NULL DEFAULT 0, mediastore_uri ' + 'TEXT DEFAULT NULL)'), + 'request_headers': ( + 'CREATE TABLE request_headers(id INTEGER PRIMARY KEY AUTOINCREMENT, download_id INTEGER NOT NULL, ' + 'header TEXT NOT NULL,value TEXT NOT NULL)'), + 'sqlite_sequence': ( + 'CREATE TABLE sqlite_sequence(name,seq)')}] + + def _GetDateTimeRowValue(self, query_hash, row, value_name): + """Retrieves a date and time value from the row. + + Args: + query_hash (int): hash of the query, that uniquely identifies the query + that produced the row. + row (sqlite3.Row): row. + value_name (str): name of the value. + + Returns: + dfdatetime.JavaTime: date and time value or None if not available. + """ + timestamp = self._GetRowValue(query_hash, row, value_name) + if timestamp is None: + return None + + return dfdatetime_java_time.JavaTime(timestamp=timestamp) + + def ParseDownloadsRow(self, parser_mediator, query, row, **unused_kwargs): + """Parses a download row. + + Args: + parser_mediator (ParserMediator): mediates interactions between parsers + and other components, such as storage and dfVFS. + query (str): query that created the row. + row (sqlite3.Row): row. + """ + query_hash = hash(query) + + event_data = AndroidNativeDownloadsEventData() + event_data.lastmod = self._GetDateTimeRowValue(query_hash, row, 'lastmod') + event_data.id = self._GetRowValue(query_hash, row, '_id') + event_data.uri = self._GetRowValue(query_hash, row, 'uri') + event_data.mimetype = self._GetRowValue(query_hash, row, 'mimetype') + event_data.total_bytes = self._GetRowValue(query_hash, row, 'total_bytes') + event_data.current_bytes = self._GetRowValue(query_hash, row, 'current_bytes') + event_data.status = self._GetRowValue(query_hash, row, 'status') + event_data.saved_to = self._GetRowValue(query_hash, row, '_data') + event_data.deleted = self._GetRowValue(query_hash, row, 'deleted') + event_data.notification_package = self._GetRowValue(query_hash, row, 'notificationpackage') + event_data.title = self._GetRowValue(query_hash, row, 'title') + event_data.error_msg = self._GetRowValue(query_hash, row, 'errorMsg') + event_data.is_visible_in_downloads_ui = self._GetRowValue(query_hash, row, 'is_visible_in_downloads_ui') + event_data.media_provider_uri = self._GetRowValue(query_hash, row, 'mediaprovider_uri') + event_data.destination = self._GetRowValue(query_hash, row, 'destination') + event_data.ui_visibility = self._GetRowValue(query_hash, row, 'visibility') + event_data.e_tag = self._GetRowValue(query_hash, row, 'etag') + event_data.description = self._GetRowValue(query_hash, row, 'description') + + parser_mediator.ProduceEventData(event_data) + + +sqlite.SQLiteParser.RegisterPlugin(AndroidNativeDownloadsPlugin) From c82dc1ebc7680e2e22498f50fb038922e5859829 Mon Sep 17 00:00:00 2001 From: Clement-155 Date: Sun, 1 Dec 2024 20:49:26 +0700 Subject: [PATCH 02/10] Add Android Native Downloads Unit Test --- .../android_native_downloads.py | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/parsers/sqlite_plugins/android_native_downloads.py diff --git a/tests/parsers/sqlite_plugins/android_native_downloads.py b/tests/parsers/sqlite_plugins/android_native_downloads.py new file mode 100644 index 0000000000..51ffced546 --- /dev/null +++ b/tests/parsers/sqlite_plugins/android_native_downloads.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""Tests for the Android SMS plugin.""" + +import unittest + +from plaso.parsers.sqlite_plugins import android_native_downloads + +from tests.parsers.sqlite_plugins import test_lib + +class AndroidNativeDownloadsTest(test_lib.SQLitePluginTestCase): + """Tests for the Android native downloads database plugin.""" + + def testProcess(self): + """Test the Process function on an Android native downloads database (downloads.db) file.""" + plugin = android_native_downloads.AndroidNativeDownloadsPlugin() + storage_writer = self._ParseDatabaseFileWithPlugin(['downloads.db'], plugin) + + # The Native Downloads database file contains 11 events. + number_of_event_data = storage_writer.GetNumberOfAttributeContainers( + 'event_data') + self.assertEqual(number_of_event_data, 11) + + number_of_warnings = storage_writer.GetNumberOfAttributeContainers( + 'extraction_warning') + self.assertEqual(number_of_warnings, 0) + + number_of_warnings = storage_writer.GetNumberOfAttributeContainers( + 'recovery_warning') + self.assertEqual(number_of_warnings, 0) + + expected_event_values = { + 'lastmod': '2022-11-12T15:32:28.279+00:00', + 'id': 46, + 'uri': 'https://cdn.discordapp.com/attachments/622810296226152474/1041012392089370735/IMG_1953.jpg', + 'mimetype': 'image/jpeg', + 'total_bytes': 2149749, + 'current_bytes': 2149749, + 'status': 200, + 'saved_to': '/storage/emulated/0/Download/IMG_1953.jpg', + 'deleted': 0, + 'notification_package': 'com.discord', + 'title': 'IMG_1953.jpg', + 'media_provider_uri': 'content://media/external_primary/images/media/1000000486', + 'error_msg': None, + 'is_visible_in_downloads_ui': 1, + 'destination': 4, + 'ui_visibility': 1, + 'e_tag': '"932f5b7818a3c0c1284cda69b6d7ea30"', + 'description': '', + } + + event_data = storage_writer.GetAttributeContainerByIndex('event_data', 0) + self.CheckEventData(event_data, expected_event_values) + + if __name__ == '__main__': + unittest.main() From c9c86b92e635c03e1f81459da86a96fa63d8b98e Mon Sep 17 00:00:00 2001 From: Clement-155 Date: Sun, 1 Dec 2024 20:50:02 +0700 Subject: [PATCH 03/10] Add Artifact: downloads.db For Android Native Downloads Sqlite Plugin --- test_data/downloads.db | Bin 0 -> 24576 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 test_data/downloads.db diff --git a/test_data/downloads.db b/test_data/downloads.db new file mode 100644 index 0000000000000000000000000000000000000000..2aaf65210a88d4ad7d450e807dd5181b7da73f3b GIT binary patch literal 24576 zcmeI4U1%HG6@X_X|H#`YDlA=h3tLZ|4RNrf`Tq+PDrc3%8~-G+LJ~?DuI}8CCXr^e zGov^*?1MI;6bhv+^u4qXOCJi|KJZksYQdGgod`E43#Db zBG-hvK?6TR-Hwf5E0ansD5BxuE)2idA%mh#!pQYdv`0fcaznT7 zw&IHprc&gv??r*%8dZc5in`&b>~-4?askz-s)fR+4U=12zXs#Pmx^4+#fbtPgdd{D zP_uCfhHhaBVAO*=jsi3kVBde}lCi0FLlVGvDVcycBp|_HoCrX*tomL=#x%y=Ab<{& z)g(fVLAUN`F^ilZHA!%w23D#yoB^=Kf!j&g_E$E<$nyY4x-xEPb-MMIi!Ib~hm^Rj z?HadrXJdnVr+T$=XQRdmMGm!E{;n1HsO@@ zwq>P;YjvlnBo?{)UI&)N3JCVwZQ|K*&CuN^R?BU>(Nq=N#CB09@E^H039OOj+oVOJ z5uZbqB|+c^o8fRh`dq-KVQEu4d%h#6+i$rGj+45^^X-7N(?X5uyb|@p$PY-Wd%9Hd zx?SqYGxLk9tLz7FC7~i9?{&e?tR_J)-*BQhcOYy`2v z>3oZiP^;Uw)3ol=&FoyP^!-HV@I7#LVui8l9wrmT*#kOH2v`%++-2t$OC=U3Mv6Q;@Lk&i_eXKypDbq&nCYaHTWR?i4Z`)M_)C29O@8dPPy&^__S@UvudJcHhU`rmHCB*1$H6#evVnl<=$^bQ7632 z^PyZuZM5%uXg4fF78+mg1|A7Y?RLmRdYR2jieZ}$vP*Vd5=x4wlais=NlC(ji456R zh@+JsbQ%t1DZ+SsoYVy=gFI<>TaZF&^8)|QAkXuK$2yil+;*(SgAQrTFn?g5{`ueR zU6@}P*-amkA?sneKU4N^58$z^o0=@D*wzG$4eXeNAYD;3Q^A5@5U0Mp!mcG`xDvM+ z=ia5+sgN@V36w92=~FIdd~CCc^q^%Eh(d@{p6{p`0t{`lC*d{Df; z%AmyRz7N3(jF5p6hzV$ zLXnkXPg^jTFon$N0Ak2gP2*e&g6RKEtsW`%Xh&rx19uC={9ADtzL# z8Ed!`T1q-3JE04QZ-nCgOIQwD@E;V(J}4=d;6s_O3DS+5^4cvy(@R$cWo_g7jdvyC zT|Ny$Zo6U0k|@6DHfX&4)Ce}-a1_Fl?aYtvUVS~bu$@@p%!8l8V_8$NWb3vqC?+DB zf<;YNb%!{np$i%^h2@py_04Oksz(cJsw^HUtUB|{UoojuCVujE862c?&_VhOGtEJ;?Uije#C~9-PABmZ6h#O(;U8CEN2p1XAqtWS z@0zGeimvdYpooGf$)+S2rmPFPtn#q|il!>RsIQE-pXw{)4JUl1_4vsb@wyMR=l;}% z$FeC)j#}3Z(Lgd5uqYV{wvlGmHCrc025*@gAM-`Od%|Mr?6bOSC~Kh&$EAl3VKiilmI0_2~Yx*03|>PPy&>|OGqH|`AeuY zRhbf)Lf}n#mSI=Fv0lNLbfQw#!@dnS2yN`Uoi{HeqRWSg-cCg3VWRVi$T&=NE)l7R ziMT|h941;$MEW73-fM5oG3>?Oo>1w+EpG#NKe-R=8e}iRZCW4ukHeCrX{IR3hNwY` zQb~~wN!3+T6-8Z%OR{VTs;()LRDxtlY&a%TF;bkmF4k2WVXPXujx<|y1Vw^cU4>L4 VRCi=kL4qmQiL7DA!I1GG{txp9vIYPE literal 0 HcmV?d00001 From a0ae191c728b839e3b4a40f60038f1aae8275db2 Mon Sep 17 00:00:00 2001 From: Clement-155 Date: Sun, 1 Dec 2024 20:51:32 +0700 Subject: [PATCH 04/10] Modify __init__.py for android downloads.db plugin For Sqlite Android Native Downloads Plugin --- plaso/parsers/sqlite_plugins/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plaso/parsers/sqlite_plugins/__init__.py b/plaso/parsers/sqlite_plugins/__init__.py index dfa7c0a680..6ea85a10aa 100644 --- a/plaso/parsers/sqlite_plugins/__init__.py +++ b/plaso/parsers/sqlite_plugins/__init__.py @@ -4,6 +4,7 @@ from plaso.parsers.sqlite_plugins import android_app_usage from plaso.parsers.sqlite_plugins import android_calls from plaso.parsers.sqlite_plugins import android_hangouts +from plaso.parsers.sqlite_plugins import android_native_downloads from plaso.parsers.sqlite_plugins import android_sms from plaso.parsers.sqlite_plugins import android_tango from plaso.parsers.sqlite_plugins import android_turbo From cc09a761e28895133e6b89897ab5977054e3278c Mon Sep 17 00:00:00 2001 From: Clement-155 Date: Sun, 1 Dec 2024 20:52:33 +0700 Subject: [PATCH 05/10] Modify timeliner.yaml for android downloads.db plugin --- plaso/data/timeliner.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/plaso/data/timeliner.yaml b/plaso/data/timeliner.yaml index 1681f33d74..bc87a5f75a 100644 --- a/plaso/data/timeliner.yaml +++ b/plaso/data/timeliner.yaml @@ -54,6 +54,12 @@ attribute_mappings: description: 'Start Time' place_holder_event: false --- +data_type: 'android:sqlite:downloads' +attribute_mappings: +- name: 'lastmod' + description: 'Last Modified Time' +place_holder_event: true +--- data_type: 'android:tango:conversation' place_holder_event: true --- From ad2e8bc480234ecbbfdc19374ff9959d583b3704 Mon Sep 17 00:00:00 2001 From: Clement-155 Date: Sun, 1 Dec 2024 20:52:45 +0700 Subject: [PATCH 06/10] Modify android.yaml for android downloads.db plugin --- plaso/data/formatters/android.yaml | 83 ++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/plaso/data/formatters/android.yaml b/plaso/data/formatters/android.yaml index bd76492cdd..95d5350889 100644 --- a/plaso/data/formatters/android.yaml +++ b/plaso/data/formatters/android.yaml @@ -130,6 +130,89 @@ short_source: 'Android app usage' source: 'Android SQLite App Usage' --- type: 'conditional' +data_type: 'android:sqlite:downloads' +enumeration_helpers: +- input_attribute: 'status' + output_attribute: 'status_string' + values: + 190: 'STATUS_PENDING' + 192: 'STATUS_RUNNING' + 193: 'STATUS_PAUSED_BY_APP' + 194: 'STATUS_WAITING_TO_RETRY' + 195: 'STATUS_WAITING_FOR_NETWORK' + 196: 'STATUS_QUEUED_FOR_WIFI' + 198: 'STATUS_INSUFFICIENT_SPACE_ERROR' + 199: 'STATUS_DEVICE_NOT_FOUND_ERROR' + 200: 'STATUS_SUCCESS' + 400: 'STATUS_BAD_REQUEST' + 406: 'STATUS_NOT_ACCEPTABLE' + 411: 'STATUS_LENGTH_REQUIRED' + 412: 'STATUS_PRECONDITION_FAILED' + 488: 'STATUS_FILE_ALREADY_EXISTS_ERROR' + 489: 'STATUS_CANNOT_RESUME' + 490: 'STATUS_CANCELED' + 491: 'STATUS_UNKNOWN_ERROR' + 492: 'STATUS_FILE_ERROR' + 493: 'STATUS_UNHANDLED_REDIRECT' + 494: 'STATUS_UNHANDLED_HTTP_CODE' + 495: 'STATUS_HTTP_DATA_ERROR' + 496: 'STATUS_HTTP_EXCEPTION' + 497: 'STATUS_TOO_MANY_REDIRECTS' + 1000: 'ERROR_UNKNOWN' + 1001: 'ERROR_FILE_ERROR' + 1002: 'ERROR_UNHANDLED_HTTP_CODE' + 1004: 'ERROR_HTTP_DATA_ERROR' + 1005: 'ERROR_TOO_MANY_REDIRECTS' + 1007: 'ERROR_DEVICE_NOT_FOUND' + 1008: 'ERROR_CANNOT_RESUME' + 1009: 'ERROR_FILE_ALREADY_EXISTS' + 1010: 'ERROR_BLOCKED' + 1: 'PAUSED_WAITING_TO_RETRY' + 2: 'PAUSED_WAITING_FOR_NETWORK' + 3: 'PAUSED_QUEUED_FOR_WIFI' + 4: 'PAUSED_UNKNOWN' +- input_attribute: 'destination' + output_attribute: 'destination_string' + values: + 0: 'DESTINATION_EXTERNAL' + 1: 'DESTINATION_CACHE_PARTITION' + 2: 'DESTINATION_CACHE_PARTITION_PURGEABLE' + 3: 'DESTINATION_CACHE_PARTITION_NOROAMING' + 4: 'DESTINATION_FILE_URI' + 5: 'DESTINATION_SYSTEMCACHE_PARTITION' + 6: 'DESTINATION_NON_DOWNLOADMANAGER_DOWNLOAD' +- input_attribute: 'ui_visibility' + output_attribute: 'ui_visibility_string' + values: + 0: 'VISIBILITY_VISIBLE' + 1: 'VISIBILITY_VISIBLE_NOTIFY_COMPLETED' + 2: 'VISIBILITY_HIDDEN' + 3: 'VISIBILITY_VISIBLE_NOTIFY_ONLY_COMPLETION' +message: +- 'ID: {id}' +- 'URI: {uri}' +- 'MIME Type: {mimetype}' +- 'Total Bytes: {total_bytes}' +- 'Current Bytes: {current_bytes}' +- 'Download Status: {status_string}' +- 'Saved to: {saved_to}' +- 'Is Deleted: {deleted}' +- 'Notification Package: {notification_package}' +- 'Title: {title}' +- 'Media Provider URI: {media_provider_uri}' +- 'Error Msg: {error_msg}' +- 'Is Visible in Downloads UI: {is_visible_in_downloads_ui}' +- 'Destination Type: {destination_string}' +- 'UI Visibility: {ui_visibility_string}' +- 'ETag: {e_tag}' +- 'Description: {description}' +short_message: +- 'URI: {uri}' +- 'Download Status: {status_string}' +short_source: 'Android Native Downloads' +source: 'Android native downloads (downloads.db)' +--- +type: 'conditional' data_type: 'android:tango:contact' message: - '{first_name}' From ca65e826fd392c1853e71f4938ddb229faa5b533 Mon Sep 17 00:00:00 2001 From: Clement-155 Date: Sun, 1 Dec 2024 21:03:39 +0700 Subject: [PATCH 07/10] Remove unused import android_native_downloads.py. --- plaso/parsers/sqlite_plugins/android_native_downloads.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plaso/parsers/sqlite_plugins/android_native_downloads.py b/plaso/parsers/sqlite_plugins/android_native_downloads.py index a90ec23a5a..ebb3b3a773 100644 --- a/plaso/parsers/sqlite_plugins/android_native_downloads.py +++ b/plaso/parsers/sqlite_plugins/android_native_downloads.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- """SQLite parser plugin for Android Native Downloads (DownloadManager API) database files.""" -import re from dfdatetime import java_time as dfdatetime_java_time - from plaso.containers import events from plaso.parsers import sqlite from plaso.parsers.sqlite_plugins import interface From 1ff93a5d8f8f17c232f031b3d01225fd47c68291 Mon Sep 17 00:00:00 2001 From: barpeot Date: Tue, 3 Dec 2024 16:02:07 +0700 Subject: [PATCH 08/10] Fix pylint --- .../android_native_downloads.py | 131 +++++++++++------- 1 file changed, 81 insertions(+), 50 deletions(-) diff --git a/plaso/parsers/sqlite_plugins/android_native_downloads.py b/plaso/parsers/sqlite_plugins/android_native_downloads.py index ebb3b3a773..eba9084f1b 100644 --- a/plaso/parsers/sqlite_plugins/android_native_downloads.py +++ b/plaso/parsers/sqlite_plugins/android_native_downloads.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""SQLite parser plugin for Android Native Downloads (DownloadManager API) database files.""" +"""SQLite parser plugin for Android Native Downloads database files.""" from dfdatetime import java_time as dfdatetime_java_time from plaso.containers import events @@ -11,48 +11,58 @@ class AndroidNativeDownloadsEventData(events.EventData): """Android Native Downloads (DownloadProvider) event data. Also see : - STATUS_* and DESTINATION_* constants: https://android.googlesource.com/platform/frameworks/base/ + STATUS_* and DESTINATION_* constants: + https://android.googlesource.com/platform/frameworks/base/ +/refs/heads/master/core/java/android/provider/Downloads.java - ERROR_*, PAUSED_*, and VISIBILITY_* constants: https://android.googlesource.com/platform/frameworks/base/ + ERROR_*, PAUSED_*, and VISIBILITY_* constants: + https://android.googlesource.com/platform/frameworks/base/ +/refs/heads/main/core/java/android/app/DownloadManager.java - Basis for what columns to extract: https://forensafe.com/blogs/Android_Downloads.html + Basis for what columns to extract: + https://forensafe.com/blogs/Android_Downloads.html Attributes: - lastmod (dfdatetime.DateTimeValues): Last modified date and time of downloaded file. + lastmod (dfdatetime.DateTimeValues): Last modified time of downloaded file. id (int): An identifier for a particular download, unique across the system. uri (str): Downloaded URI. mimetype (str): Internet Media Type of the downloaded file. total_bytes (int): Total size of the download in bytes. current_bytes (int): Number of bytes download so far. status (int): Holds one of the STATUS_* constants. - If an error occurred, this holds the HTTP Status Code for an HTTP error (RFC 2616), - otherwise it holds one of the ERROR_* constants. - If the download is paused, this holds one of the PAUSED_* constants. + If an error occurred, this holds the HTTP Status Error Code (RFC 2616), + otherwise it holds one of the ERROR_* constants. + If the download is paused, this holds one of the PAUSED_* constants. saved_to (str): Path to the downloaded file on disk. - deleted (bool): Set to true if this download is deleted. It is completely removed from the database when - MediaProvider database also deletes the metadata associated with this downloaded file. - notification_package (str): Package name associated with notification of a running download. + deleted (bool): Set to true if this download is deleted. + Also Removed from the database when MediaProvider database + deletes the metadata associated with this downloaded file. + notification_package (str): Package name associated with notification + of a running download. title (str): Title of the download. - media_provider_uri (str): The URI to the corresponding entry in MediaProvider for this downloaded entry. It is - used to delete the entries from MediaProvider database when it is deleted from the - downloaded list. - error_msg (str): The column with errorMsg for a failed downloaded. Used only for debugging purposes. - is_visible_in_downloads_ui (int) : Whether or not this download should be displayed in the system's Downloads UI. - Defaults to true. - destination (int): The name of the column containing the flag that controls the destination of the download. - See the DESTINATION_* constants for a list of legal values. - ui_visibility (int): The name of the column containing the flags that controls whether the download is displayed by - the UI. See the VISIBILITY_* constants for a list of legal values. - e_tag (str) ETag of this file. - description (str): The client-supplied description of this download. This will be displayed in system - notifications. Defaults to the empty string. + media_provider_uri (str): The URI to corresponding entry in MediaProvider + for this downloaded entry. If an entry is deleted from downloaded list, + it is also deleted from MediaProvider DB. + error_msg (str): The column with errorMsg for a failed downloaded. + Used only for debugging purposes. + is_visible_in_downloads_ui (int) : Whether or not this download should + be displayed in the system's Downloads UI. Defaults to true. + destination (int): Contains the flag that controls download destination. + See the DESTINATION_* constants for a list of legal values. + ui_visibility (int): Contains the flags that control if the download is + displayed by the UI. + See the VISIBILITY_* constants for a list of legal values. + e_tag (str): ETag of this file. + description (str): The client-supplied description of this download. + This will be displayed in system notifications. + Defaults to empty string. """ DATA_TYPE = 'android:sqlite:downloads' def __init__(self): """Initializes event data.""" - super(AndroidNativeDownloadsEventData, self).__init__(data_type=self.DATA_TYPE) + super( + AndroidNativeDownloadsEventData, self + ).__init__(data_type=self.DATA_TYPE) self.lastmod = None self.id = None self.uri = None @@ -84,33 +94,49 @@ class AndroidNativeDownloadsPlugin(interface.SQLitePlugin): DATA_FORMAT = 'Android native downloads SQLite database (downloads.db) file' REQUIRED_STRUCTURE = { - 'downloads': frozenset(['_id', 'uri', '_data', 'mimetype', 'destination', 'visibility', 'status', 'lastmod', - 'notificationpackage', 'total_bytes', 'current_bytes', 'etag', 'title', 'description', - 'is_visible_in_downloads_ui', 'mediaprovider_uri', 'deleted', 'errorMsg'])} + 'downloads': frozenset( + ['_id', 'uri', '_data', 'mimetype', 'destination', + 'visibility', 'status', 'lastmod', 'notificationpackage', + 'total_bytes', 'current_bytes', 'etag', 'title', 'description', + 'is_visible_in_downloads_ui', 'mediaprovider_uri', 'deleted', + 'errorMsg'] + )} + QUERIES = [ - ('SELECT _id, uri, _data, mimetype, destination, visibility, status, lastmod, ' - 'notificationpackage, total_bytes, current_bytes, etag, title, description, ' - 'is_visible_in_downloads_ui, mediaprovider_uri, deleted, errorMsg FROM downloads', - 'ParseDownloadsRow')] + ('SELECT _id, uri, _data, mimetype, destination, ' + 'visibility, status, lastmod, notificationpackage, ' + 'total_bytes, current_bytes, etag, title, description, ' + 'is_visible_in_downloads_ui, mediaprovider_uri, deleted, errorMsg' + 'FROM downloads', + 'ParseDownloadsRow')] SCHEMAS = [{ 'android_metadata': ( 'CREATE TABLE android_metadata (locale TEXT) '), 'downloads': ( - 'CREATE TABLE downloads(_id INTEGER PRIMARY KEY AUTOINCREMENT, uri TEXT, method INTEGER, ' - 'entity TEXT, no_integrity BOOLEAN, hint TEXT, otaupdate BOOLEAN, _data TEXT, mimetype TEXT, ' - 'destination INTEGER, no_system BOOLEAN, visibility INTEGER, control INTEGER, status INTEGER, ' - 'numfailed INTEGER, lastmod BIGINT, notificationpackage TEXT, notificationclass TEXT, ' - 'notificationextras TEXT, cookiedata TEXT, useragent TEXT, referer TEXT, total_bytes INTEGER, ' - 'current_bytes INTEGER, etag TEXT, uid INTEGER, otheruid INTEGER, title TEXT, description TEXT, ' - 'scanned BOOLEAN, is_public_api INTEGER NOT NULL DEFAULT 0, allow_roaming INTEGER NOT NULL DEFAULT 0, ' - 'allowed_network_types INTEGER NOT NULL DEFAULT 0, is_visible_in_downloads_ui INTEGER NOT NULL DEFAULT 1, ' - 'bypass_recommended_size_limit INTEGER NOT NULL DEFAULT 0, mediaprovider_uri TEXT, ' - 'deleted BOOLEAN NOT NULL DEFAULT 0, errorMsg TEXT, allow_metered INTEGER NOT NULL DEFAULT 1, ' - 'allow_write BOOLEAN NOT NULL DEFAULT 0, flags INTEGER NOT NULL DEFAULT 0, mediastore_uri ' - 'TEXT DEFAULT NULL)'), + 'CREATE TABLE downloads(_id INTEGER PRIMARY KEY AUTOINCREMENT, ' + 'uri TEXT, method INTEGER, entity TEXT, no_integrity BOOLEAN, ' + 'hint TEXT, otaupdate BOOLEAN, _data TEXT, mimetype TEXT, ' + 'destination INTEGER, no_system BOOLEAN, visibility INTEGER, ' + 'control INTEGER, status INTEGER, numfailed INTEGER, ' + 'lastmod BIGINT, notificationpackage TEXT, notificationclass TEXT, ' + 'notificationextras TEXT, cookiedata TEXT, useragent TEXT, ' + 'referer TEXT, total_bytes INTEGER, current_bytes INTEGER, ' + 'etag TEXT, uid INTEGER, otheruid INTEGER, title TEXT, ' + 'description TEXT, scanned BOOLEAN, ' + 'is_public_api INTEGER NOT NULL DEFAULT 0, ' + 'allow_roaming INTEGER NOT NULL DEFAULT 0, ' + 'allowed_network_types INTEGER NOT NULL DEFAULT 0, ' + 'is_visible_in_downloads_ui INTEGER NOT NULL DEFAULT 1, ' + 'bypass_recommended_size_limit INTEGER NOT NULL DEFAULT 0, ' + 'mediaprovider_uri TEXT, deleted BOOLEAN NOT NULL DEFAULT 0, ' + 'errorMsg TEXT, allow_metered INTEGER NOT NULL DEFAULT 1, ' + 'allow_write BOOLEAN NOT NULL DEFAULT 0, ' + 'flags INTEGER NOT NULL DEFAULT 0, ' + 'mediastore_uri TEXT DEFAULT NULL)'), 'request_headers': ( - 'CREATE TABLE request_headers(id INTEGER PRIMARY KEY AUTOINCREMENT, download_id INTEGER NOT NULL, ' + 'CREATE TABLE request_headers(id INTEGER PRIMARY KEY AUTOINCREMENT,' + 'download_id INTEGER NOT NULL, ' 'header TEXT NOT NULL,value TEXT NOT NULL)'), 'sqlite_sequence': ( 'CREATE TABLE sqlite_sequence(name,seq)')}] @@ -150,15 +176,20 @@ def ParseDownloadsRow(self, parser_mediator, query, row, **unused_kwargs): event_data.uri = self._GetRowValue(query_hash, row, 'uri') event_data.mimetype = self._GetRowValue(query_hash, row, 'mimetype') event_data.total_bytes = self._GetRowValue(query_hash, row, 'total_bytes') - event_data.current_bytes = self._GetRowValue(query_hash, row, 'current_bytes') + event_data.current_bytes = self._GetRowValue( + query_hash, row, 'current_bytes') event_data.status = self._GetRowValue(query_hash, row, 'status') - event_data.saved_to = self._GetRowValue(query_hash, row, '_data') + event_data.saved_to = self._GetRowValue( + query_hash, row, '_data') event_data.deleted = self._GetRowValue(query_hash, row, 'deleted') - event_data.notification_package = self._GetRowValue(query_hash, row, 'notificationpackage') + event_data.notification_package = self._GetRowValue( + query_hash, row, 'notificationpackage') event_data.title = self._GetRowValue(query_hash, row, 'title') event_data.error_msg = self._GetRowValue(query_hash, row, 'errorMsg') - event_data.is_visible_in_downloads_ui = self._GetRowValue(query_hash, row, 'is_visible_in_downloads_ui') - event_data.media_provider_uri = self._GetRowValue(query_hash, row, 'mediaprovider_uri') + event_data.is_visible_in_downloads_ui = self._GetRowValue( + query_hash, row, 'is_visible_in_downloads_ui') + event_data.media_provider_uri = self._GetRowValue( + query_hash, row, 'mediaprovider_uri') event_data.destination = self._GetRowValue(query_hash, row, 'destination') event_data.ui_visibility = self._GetRowValue(query_hash, row, 'visibility') event_data.e_tag = self._GetRowValue(query_hash, row, 'etag') From 2eaddd8a10ffa2d9a68b512777eddc9fb441cfad Mon Sep 17 00:00:00 2001 From: barpeot Date: Tue, 3 Dec 2024 20:29:20 +0700 Subject: [PATCH 09/10] Revert super() command changes --- plaso/parsers/sqlite_plugins/android_native_downloads.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plaso/parsers/sqlite_plugins/android_native_downloads.py b/plaso/parsers/sqlite_plugins/android_native_downloads.py index eba9084f1b..ee0a5fabad 100644 --- a/plaso/parsers/sqlite_plugins/android_native_downloads.py +++ b/plaso/parsers/sqlite_plugins/android_native_downloads.py @@ -60,9 +60,7 @@ class AndroidNativeDownloadsEventData(events.EventData): def __init__(self): """Initializes event data.""" - super( - AndroidNativeDownloadsEventData, self - ).__init__(data_type=self.DATA_TYPE) + super(AndroidNativeDownloadsEventData, self).__init__(data_type=self.DATA_TYPE) self.lastmod = None self.id = None self.uri = None From 15685b31ef9db0f87834e7db29b670e68987ee5c Mon Sep 17 00:00:00 2001 From: barpeot Date: Tue, 3 Dec 2024 23:19:27 +0700 Subject: [PATCH 10/10] Fixed parser query typo --- plaso/parsers/sqlite_plugins/android_native_downloads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plaso/parsers/sqlite_plugins/android_native_downloads.py b/plaso/parsers/sqlite_plugins/android_native_downloads.py index ee0a5fabad..8104f89d3f 100644 --- a/plaso/parsers/sqlite_plugins/android_native_downloads.py +++ b/plaso/parsers/sqlite_plugins/android_native_downloads.py @@ -104,7 +104,7 @@ class AndroidNativeDownloadsPlugin(interface.SQLitePlugin): ('SELECT _id, uri, _data, mimetype, destination, ' 'visibility, status, lastmod, notificationpackage, ' 'total_bytes, current_bytes, etag, title, description, ' - 'is_visible_in_downloads_ui, mediaprovider_uri, deleted, errorMsg' + 'is_visible_in_downloads_ui, mediaprovider_uri, deleted, errorMsg ' 'FROM downloads', 'ParseDownloadsRow')]