What is the binary format of NTFS timestamp?

84 Views Asked by At

I am trying to learn the raw binary format of every type of NTFS record and I am making great progress. Related question

I have found Active Disk Editor and it tells me all about the offsets and field names and such for NTFS Master File Table File Records, and there is one thing I don't understand.

In attribute 0x10 $STANDARD_INFORMATION how are the timestamps encoded?

Like, this 8 byte sequence: b'\xb0\x37\x86\x69\x36\x1e\xd7\x01', why does it represent 2021-03-21 09:41?

How is it encoded?

I know most numbers in NTFS records are little endian. However, trying to convert it directly to integer will of course yield a very high number, with big endian byte order the number is even higher:

In [133]: int.from_bytes(b'\xb0\x37\x86\x69\x36\x1e\xd7\x01', 'little')
Out[133]: 132607933078190000

In [134]: int.from_bytes(b'\xb0\x37\x86\x69\x36\x1e\xd7\x01', 'big')
Out[134]: 12697765460832081665

Trying to use these numbers as datetime.fromtimestamp arguments will raise OSError:

In [135]: from datetime import datetime

In [136]: datetime.fromtimestamp(132607933078190000)
---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
Cell In[136], line 1
----> 1 datetime.fromtimestamp(132607933078190000)

OSError: [Errno 22] Invalid argument

Using the first 10 digits will give a date that is too early:

In [137]: datetime.fromtimestamp(int('132607933078190000'[:11]))
Out[137]: datetime.datetime(2390, 3, 21, 17, 41, 47)

In [138]: datetime.fromtimestamp(int('12697765460832081665'[:11]))
Out[138]: datetime.datetime(2372, 5, 18, 5, 4, 20)

In [139]: datetime.fromtimestamp(int('12697765460832081665'[:10]))
Out[139]: datetime.datetime(2010, 3, 28, 19, 42, 26)

In [140]: datetime.fromtimestamp(int('132607933078190000'[:10]))
Out[140]: datetime.datetime(2012, 1, 9, 11, 22, 10)

So how is this timestamp encoded?


Edit

This is not a duplicate as the suggested duplicate question doesn't mention converting the 8-byte little endian sequence to integer and none of the answers included this conversion.

2

There are 2 best solutions below

0
Ξένη Γήινος On

Okay, thanks to this comment I am able to write a very simple program to convert the timestamp.

from datetime import datetime, timedelta

EPOCH = datetime(1601, 1, 1, 0, 0, 0)

def parse_NTFS_timestamp(data: bytes) -> datetime:
    return EPOCH + timedelta(seconds=int.from_bytes(data, 'little') * 10**-7)
In [157]: parse_NTFS_timestamp(b'\xb0\x37\x86\x69\x36\x1e\xd7\x01')
Out[157]: datetime.datetime(2021, 3, 21, 9, 41, 47, 819000)

Basically, the timestamp is encoded as an eight-byte little endian value, convert to integer using little endian byte-order gives a big integer, which is the number of 10^-7 second periods, so convert the number to number of seconds and then add to the epoch, done.

2
Mark Ransom On

There's a comment to the question that gives the source.

Here's the Python code to do the conversion:

ns100 = int.from_bytes(b'\xb0\x37\x86\x69\x36\x1e\xd7\x01', 'little')
timestamp = datetime.datetime(1601, 1, 1) + datetime.timedelta(microseconds=ns100 // 10)