From 23e30b73251c6edd3f77a16a4ef0b52a5aacbcaa Mon Sep 17 00:00:00 2001 From: chb2mn Date: Fri, 16 Jun 2023 14:22:50 +0000 Subject: [PATCH 1/7] first pass at chrome_cache --- plaso/parsers/chrome_cache.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plaso/parsers/chrome_cache.py b/plaso/parsers/chrome_cache.py index 307bb83ef1..13d5921e5c 100644 --- a/plaso/parsers/chrome_cache.py +++ b/plaso/parsers/chrome_cache.py @@ -367,7 +367,7 @@ def _ParseCacheEntries(self, parser_mediator, index_table, data_block_files): # This shows up as r"_dk_{domain}( {domain})* {url}" # https://chromium.googlesource.com/chromium/src/+/ # 95faad3cfd90169f0a267e979c36e3348476a948/net/http/http_cache.cc#427 - if "_dk_" in cache_entry.original_url[:20]: + if '_dk_' in cache_entry.original_url[:20]: parsed_url = cache_entry.original_url.strip().rsplit(' ', 1)[-1] event_data.original_url = parsed_url else: From 32b0073759831b576abc298c9f2de3da7dd0ba24 Mon Sep 17 00:00:00 2001 From: chb2mn Date: Tue, 20 Jun 2023 16:48:18 +0000 Subject: [PATCH 2/7] adding chrome_cache_v3 --- run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests.py b/run_tests.py index 24f75c63d0..71c86f0060 100755 --- a/run_tests.py +++ b/run_tests.py @@ -27,7 +27,7 @@ if not dependency_helper.CheckTestDependencies(): sys.exit(1) - test_suite = unittest.TestLoader().discover('tests', pattern='*.py') + test_suite = unittest.TestLoader().discover('tests', pattern='*chrome_cache.py') test_results = unittest.TextTestRunner(verbosity=2).run(test_suite) if not test_results.wasSuccessful(): sys.exit(1) From 62040dbe0125ce876822c75912a74de0bc589cfe Mon Sep 17 00:00:00 2001 From: chb2mn Date: Tue, 20 Jun 2023 16:57:52 +0000 Subject: [PATCH 3/7] linting --- run_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests.py b/run_tests.py index 71c86f0060..24f75c63d0 100755 --- a/run_tests.py +++ b/run_tests.py @@ -27,7 +27,7 @@ if not dependency_helper.CheckTestDependencies(): sys.exit(1) - test_suite = unittest.TestLoader().discover('tests', pattern='*chrome_cache.py') + test_suite = unittest.TestLoader().discover('tests', pattern='*.py') test_results = unittest.TextTestRunner(verbosity=2).run(test_suite) if not test_results.wasSuccessful(): sys.exit(1) From 905d6f117518bab147f8a94f548c053e0cf56c30 Mon Sep 17 00:00:00 2001 From: chb2mn Date: Wed, 21 Jun 2023 19:03:06 +0000 Subject: [PATCH 4/7] First pass at adding payloads to chrome_cache --- data/formatters/browser.yaml | 2 ++ data/timeliner.yaml | 2 ++ plaso/parsers/chrome_cache.py | 20 ++++++++++++++++---- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/data/formatters/browser.yaml b/data/formatters/browser.yaml index 118f33b392..7f29946f29 100644 --- a/data/formatters/browser.yaml +++ b/data/formatters/browser.yaml @@ -17,8 +17,10 @@ type: 'conditional' data_type: 'chrome:cache:entry' message: - 'Original URL: {original_url}' +- 'Payloads: {payloads}' short_message: - 'Original URL: {original_url}' +- 'Payloads: {payloads}' short_source: 'WEBHIST' source: 'Chrome Cache' --- diff --git a/data/timeliner.yaml b/data/timeliner.yaml index 5f7cdc7c2f..e00314ca63 100644 --- a/data/timeliner.yaml +++ b/data/timeliner.yaml @@ -189,6 +189,8 @@ data_type: 'chrome:cache:entry' attribute_mappings: - name: 'creation_time' description: 'Creation Time' +- name: 'payloads' + description: 'Json-encoded list of file (with offset) of the cache payload' place_holder_event: true --- data_type: 'chrome:cookie:entry' diff --git a/plaso/parsers/chrome_cache.py b/plaso/parsers/chrome_cache.py index 13d5921e5c..1dc7b7d063 100644 --- a/plaso/parsers/chrome_cache.py +++ b/plaso/parsers/chrome_cache.py @@ -2,6 +2,7 @@ """Parser for Google Chrome and Chromium Cache files.""" import os +import json from dfdatetime import webkit_time as dfdatetime_webkit_time from dfvfs.resolver import resolver as path_spec_resolver @@ -84,6 +85,7 @@ class CacheEntry(object): key (bytes): key. next (int): cache address of the next cache entry. original_url (str): original URL derived from the key. + payloads (str): A json list of filenames (and offsets) to find the cache payload rankings_node (int): cache address of the rankings node. """ @@ -95,6 +97,7 @@ def __init__(self): self.key = None self.next = None self.original_url = None + self.payloads = None self.rankings_node = None @@ -166,7 +169,7 @@ def _ParseIndexTable(self, file_object): raise errors.ParseError(( 'Unable to map cache address at offset: 0x{0:08x} with error: ' '{1!s}').format(file_offset, exception)) - + if value: cache_address = CacheAddress(value) self.index_table.append(cache_address) @@ -257,8 +260,17 @@ def ParseCacheEntry(self, file_object, block_offset): raise errors.ParseError(( 'Unable to parse cache entry at offset: 0x{0:08x} with error: ' '{1!s}').format(block_offset, exception)) - cache_entry_object = CacheEntry() + payloads = [] + for stream in list(cache_entry.data_stream_addresses): + data_stream = CacheAddress(stream) + if data_stream.filename is not None: + if data_stream.filename.startswith("f_"): + payloads.append(data_stream.filename) + else: + payloads.append( + f"{data_stream.filename} (offset: {hex(data_stream.block_offset)})") + cache_entry_object.payloads = json.dumps(payloads) cache_entry_object.hash = cache_entry.hash cache_entry_object.next = CacheAddress(cache_entry.next_address) @@ -309,7 +321,7 @@ def __init__(self): super(ChromeCacheEntryEventData, self).__init__(data_type=self.DATA_TYPE) self.creation_time = None self.original_url = None - + self.payloads = None class ChromeCacheParser(interface.FileEntryParser): """Parses Chrome Cache files.""" @@ -373,6 +385,7 @@ def _ParseCacheEntries(self, parser_mediator, index_table, data_block_files): else: event_data.original_url = cache_entry.original_url + event_data.payloads=cache_entry.payloads parser_mediator.ProduceEventData(event_data) cache_address = cache_entry.next @@ -482,7 +495,6 @@ def ParseFileEntry(self, parser_mediator, file_entry): self.NAME, display_name, exception)) # TODO: create event based on index file creation time. - file_system = file_entry.GetFileSystem() self._ParseIndexTable( parser_mediator, file_system, file_entry, index_file_parser.index_table) From 225dfb4f27ace63b6607892ed0d935a925ae83df Mon Sep 17 00:00:00 2001 From: chb2mn Date: Wed, 21 Jun 2023 19:09:02 +0000 Subject: [PATCH 5/7] lint first pass --- plaso/parsers/chrome_cache.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plaso/parsers/chrome_cache.py b/plaso/parsers/chrome_cache.py index 1dc7b7d063..f2c288c1d9 100644 --- a/plaso/parsers/chrome_cache.py +++ b/plaso/parsers/chrome_cache.py @@ -85,7 +85,8 @@ class CacheEntry(object): key (bytes): key. next (int): cache address of the next cache entry. original_url (str): original URL derived from the key. - payloads (str): A json list of filenames (and offsets) to find the cache payload + payloads (str): A json list of filenames (and offsets) + to find the cache payload rankings_node (int): cache address of the rankings node. """ @@ -169,7 +170,7 @@ def _ParseIndexTable(self, file_object): raise errors.ParseError(( 'Unable to map cache address at offset: 0x{0:08x} with error: ' '{1!s}').format(file_offset, exception)) - + if value: cache_address = CacheAddress(value) self.index_table.append(cache_address) From 2a6969b7ccda8ae8a7a9a36854f20a735dde24f3 Mon Sep 17 00:00:00 2001 From: chb2mn Date: Wed, 21 Jun 2023 19:45:32 +0000 Subject: [PATCH 6/7] adding to test case --- tests/parsers/chrome_cache.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/parsers/chrome_cache.py b/tests/parsers/chrome_cache.py index 46b574b948..574157a229 100644 --- a/tests/parsers/chrome_cache.py +++ b/tests/parsers/chrome_cache.py @@ -33,7 +33,8 @@ def testParse(self): 'creation_time': '2014-04-30T16:44:36.226091+00:00', 'data_type': 'chrome:cache:entry', 'original_url': ( - 'https://s.ytimg.com/yts/imgbin/player-common-vfliLfqPT.webp')} + 'https://s.ytimg.com/yts/imgbin/player-common-vfliLfqPT.webp'), + 'payloads': '["data_3 (offset: 0x5c000)", "f_000010"]'} event_data = storage_writer.GetAttributeContainerByIndex('event_data', 0) self.CheckEventData(event_data, expected_event_values) From d708c552cb33a2d7717a1656aa486f80f3161568 Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Sun, 27 Aug 2023 08:32:24 -0700 Subject: [PATCH 7/7] Changes to match style guide --- plaso/parsers/chrome_cache.py | 12 ++++++------ tests/parsers/chrome_cache.py | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/plaso/parsers/chrome_cache.py b/plaso/parsers/chrome_cache.py index f2c288c1d9..8e39db5f46 100644 --- a/plaso/parsers/chrome_cache.py +++ b/plaso/parsers/chrome_cache.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- """Parser for Google Chrome and Chromium Cache files.""" -import os import json +import os from dfdatetime import webkit_time as dfdatetime_webkit_time from dfvfs.resolver import resolver as path_spec_resolver @@ -85,8 +85,8 @@ class CacheEntry(object): key (bytes): key. next (int): cache address of the next cache entry. original_url (str): original URL derived from the key. - payloads (str): A json list of filenames (and offsets) - to find the cache payload + payloads (str): A json list of filenames (and offsets) + to find the cache payload. rankings_node (int): cache address of the rankings node. """ @@ -266,11 +266,11 @@ def ParseCacheEntry(self, file_object, block_offset): for stream in list(cache_entry.data_stream_addresses): data_stream = CacheAddress(stream) if data_stream.filename is not None: - if data_stream.filename.startswith("f_"): + if data_stream.filename.startswith('f_'): payloads.append(data_stream.filename) else: payloads.append( - f"{data_stream.filename} (offset: {hex(data_stream.block_offset)})") + f'{data_stream.filename} (offset: {hex(data_stream.block_offset)})') cache_entry_object.payloads = json.dumps(payloads) cache_entry_object.hash = cache_entry.hash @@ -377,7 +377,7 @@ def _ParseCacheEntries(self, parser_mediator, index_table, data_block_files): timestamp=cache_entry.creation_time) # In Chrome Cache v3, doublekey-ing cache entries was introduced - # This shows up as r"_dk_{domain}( {domain})* {url}" + # This shows up as "_dk_{domain}( {domain})* {url}" # https://chromium.googlesource.com/chromium/src/+/ # 95faad3cfd90169f0a267e979c36e3348476a948/net/http/http_cache.cc#427 if '_dk_' in cache_entry.original_url[:20]: diff --git a/tests/parsers/chrome_cache.py b/tests/parsers/chrome_cache.py index 574157a229..a7f8498e3b 100644 --- a/tests/parsers/chrome_cache.py +++ b/tests/parsers/chrome_cache.py @@ -61,7 +61,8 @@ def testParseWithVersion3(self): 'data_type': 'chrome:cache:entry', 'original_url': ('https://m.media-amazon.com/images/' 'G/01/gno/sprites/nav-sprite-global-1x-reorg-privacy' - '._CB587940754_.png')} + '._CB587940754_.png'), + 'payloads': '["data_3 (offset: 0x5c000)", "f_000010"]'} event_data = storage_writer.GetAttributeContainerByIndex('event_data', 0) self.CheckEventData(event_data, expected_event_values)