diff --git a/hindsight.py b/hindsight.py index 0b155e6..39633c2 100644 --- a/hindsight.py +++ b/hindsight.py @@ -195,7 +195,7 @@ def write_jsonl(analysis_session): # Hindsight version info log.info( '\n' + '#' * 80 + - f'\n### Hindsight v{pyhindsight.__version__} (https://github.com/obsidianforensics/hindsight) ###\n' + + f'\n## Hindsight v{pyhindsight.__version__} (https://github.com/obsidianforensics/hindsight) ##\n' + '#' * 80) # Analysis start time diff --git a/pyhindsight/browsers/chrome.py b/pyhindsight/browsers/chrome.py index c8eb9bf..e398cdb 100644 --- a/pyhindsight/browsers/chrome.py +++ b/pyhindsight/browsers/chrome.py @@ -653,7 +653,8 @@ def get_cookies(self, path, database, version): # If the cookie was created and accessed at the same time (only used once), or if the last accessed # time is 0 (happens on iOS), don't create an accessed row - if new_row.creation_utc != new_row.last_access_utc and accessed_row.last_access_utc != utils.to_datetime(0, self.timezone): + if new_row.creation_utc != new_row.last_access_utc and \ + accessed_row.last_access_utc != utils.to_datetime(0, self.timezone): accessed_row.row_type = 'cookie (accessed)' accessed_row.timestamp = accessed_row.last_access_utc results.append(accessed_row) diff --git a/pyhindsight/utils.py b/pyhindsight/utils.py index 017776e..07f9c19 100644 --- a/pyhindsight/utils.py +++ b/pyhindsight/utils.py @@ -89,9 +89,13 @@ def to_datetime(timestamp, timezone=None): log.warning(f'Exception parsing {timestamp} to datetime: {e}') return datetime.datetime.fromtimestamp(0) - # Really big Webkit microseconds (17-8 digits), most often cookie expiry dates. + # Really big Webkit microseconds (18 digits), most often cookie expiry dates. + if timestamp >= 253402300800000000: + new_timestamp = datetime.datetime.max + log.warning(f'Timestamp value {timestamp} is too large to convert; replaced with {datetime.datetime.max}') + # Microsecond timestamps past 2038 can be problematic with datetime.utcfromtimestamp(timestamp). - if timestamp > 13700000000000000: + elif timestamp > 13700000000000000: new_timestamp = datetime.datetime.fromtimestamp(0) \ + datetime.timedelta(seconds=(timestamp / 1000000) - 11644473600) @@ -99,6 +103,10 @@ def to_datetime(timestamp, timezone=None): elif timestamp > 12000000000000000: # ts > 1981 new_timestamp = datetime.datetime.utcfromtimestamp((timestamp / 1000000) - 11644473600) + # Epoch microseconds (16 digits) + elif 2500000000000000 > timestamp > 1280000000000000: # 2049 > ts > 2010 + new_timestamp = datetime.datetime.utcfromtimestamp(timestamp / 1000000) + # Epoch milliseconds (13 digits) elif 2500000000000 > timestamp > 1280000000000: # 2049 > ts > 2010 new_timestamp = datetime.datetime.utcfromtimestamp(timestamp / 1000) @@ -109,7 +117,12 @@ def to_datetime(timestamp, timezone=None): # Epoch seconds (10 digits typically, but could be less) else: - new_timestamp = datetime.datetime.utcfromtimestamp(timestamp) + try: + new_timestamp = datetime.datetime.utcfromtimestamp(timestamp) + except OSError as e: + log.warning(f'Exception parsing {timestamp} to datetime: {e}; ' + f'common issue is value is too big for the OS to convert it') + return datetime.datetime.utcfromtimestamp(0) if timezone is not None: try: @@ -120,7 +133,7 @@ def to_datetime(timestamp, timezone=None): return new_timestamp except Exception as e: log.warning(f'Exception parsing {timestamp} to datetime: {e}') - return datetime.datetime.fromtimestamp(0) + return datetime.datetime.utcfromtimestamp(0) def friendly_date(timestamp):