From 84d7716ef1a91bbd1fd1ca25e6670482c01f7fc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Donncha=20=C3=93=20Cearbhaill?= Date: Mon, 28 Oct 2024 11:46:05 +0100 Subject: [PATCH] Use local timestamp for Files module timeline. Most other Android timestamps appear to be local time. The results timeline is more useful if all the timestamps are consistent. I would prefer to use UTC, but that would mean converting all the other timestamps to UTC as well. We probably do not have sufficient information to do that accurately, especially if the device is moving between timezones.. --- src/mvt/android/artifacts/getprop.py | 11 ++++++++++ src/mvt/android/modules/androidqf/base.py | 25 ++++++++++++++++++++++ src/mvt/android/modules/androidqf/files.py | 18 ++++++++++++---- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/src/mvt/android/artifacts/getprop.py b/src/mvt/android/artifacts/getprop.py index a4d87e0..6c7030f 100644 --- a/src/mvt/android/artifacts/getprop.py +++ b/src/mvt/android/artifacts/getprop.py @@ -42,6 +42,17 @@ class GetProp(AndroidArtifact): entry = {"name": matches[0][0], "value": matches[0][1]} self.results.append(entry) + def get_device_timezone(self) -> str: + """ + Get the device timezone from the getprop results + + Used in other moduels to calculate the timezone offset + """ + for entry in self.results: + if entry["name"] == "persist.sys.timezone": + return entry["value"] + return None + def check_indicators(self) -> None: for entry in self.results: if entry["name"] in INTERESTING_PROPERTIES: diff --git a/src/mvt/android/modules/androidqf/base.py b/src/mvt/android/modules/androidqf/base.py index 4e2c68e..e28e3da 100644 --- a/src/mvt/android/modules/androidqf/base.py +++ b/src/mvt/android/modules/androidqf/base.py @@ -48,6 +48,31 @@ class AndroidQFModule(MVTModule): def _get_files_by_pattern(self, pattern: str): return fnmatch.filter(self.files, pattern) + def _get_device_timezone(self): + """ + Get the device timezone from the getprop.txt file. + + This is needed to map local timestamps stored in some + Android log files to UTC/timezone-aware timestamps. + """ + get_prop_files = self._get_files_by_pattern("*/getprop.txt") + prop_data = self._get_file_content(get_prop_files[0]).decode("utf-8") + + from mvt.android.artifacts.getprop import GetProp + + properties_artifact = GetProp() + properties_artifact.parse(prop_data) + timezone = properties_artifact.get_device_timezone() + if timezone: + self.log.debug("Identified local phone timezone: %s", timezone) + return timezone + + self.log.warning( + "Could not find or determine local device timezone. " + "Some timestamps and timeline data may be incorrect." + ) + return None + def _get_file_content(self, file_path): if self.archive: handle = self.archive.open(file_path) diff --git a/src/mvt/android/modules/androidqf/files.py b/src/mvt/android/modules/androidqf/files.py index 18dee9d..baec7a5 100644 --- a/src/mvt/android/modules/androidqf/files.py +++ b/src/mvt/android/modules/androidqf/files.py @@ -6,6 +6,7 @@ import datetime import json import logging +from zoneinfo import ZoneInfo from typing import Optional, Union from mvt.android.modules.androidqf.base import AndroidQFModule @@ -106,6 +107,8 @@ class Files(AndroidQFModule): # TODO: adds SHA1 and MD5 when available in MVT def run(self) -> None: + device_timezone = ZoneInfo(self._get_device_timezone()) + for file in self._get_files_by_pattern("*/files.json"): rawdata = self._get_file_content(file).decode("utf-8", errors="ignore") try: @@ -120,11 +123,18 @@ class Files(AndroidQFModule): for file_data in data: for ts in ["access_time", "changed_time", "modified_time"]: if ts in file_data: - file_data[ts] = convert_datetime_to_iso( - datetime.datetime.fromtimestamp( - file_data[ts], tz=datetime.timezone.utc - ) + utc_timestamp = datetime.datetime.fromtimestamp( + file_data[ts], tz=datetime.timezone.utc ) + # Convert the UTC timestamp to local tiem on Android device's local timezone + local_timestamp = utc_timestamp.astimezone(device_timezone) + + # HACK: We only output the UTC timestamp in convert_datetime_to_iso, we + # set the timestamp timezone to UTC, to avoid the timezone conversion again. + local_timestamp = local_timestamp.replace( + tzinfo=datetime.timezone.utc + ) + file_data[ts] = convert_datetime_to_iso(local_timestamp) self.results.append(file_data)