Left: | ||
Right: |
LEFT | RIGHT |
---|---|
1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
2 """The SleuthKit (TSK) file entry implementation.""" | 2 """The SleuthKit (TSK) file entry implementation.""" |
3 | 3 |
4 from __future__ import unicode_literals | 4 from __future__ import unicode_literals |
5 | 5 |
6 import copy | 6 import copy |
7 import decimal | |
7 | 8 |
8 import pytsk3 | 9 import pytsk3 |
9 | 10 |
10 from dfdatetime import definitions as dfdatetime_definitions | 11 from dfdatetime import definitions as dfdatetime_definitions |
11 from dfdatetime import interface as dfdatetime_interface | 12 from dfdatetime import interface as dfdatetime_interface |
12 | 13 |
13 from dfvfs.lib import definitions | 14 from dfvfs.lib import definitions |
14 from dfvfs.lib import errors | 15 from dfvfs.lib import errors |
15 from dfvfs.path import tsk_path_spec | 16 from dfvfs.path import tsk_path_spec |
16 from dfvfs.resolver import resolver | 17 from dfvfs.resolver import resolver |
17 from dfvfs.vfs import file_entry | 18 from dfvfs.vfs import file_entry |
18 | 19 |
19 | 20 |
20 class TSKTime(dfdatetime_interface.DateTimeValues): | 21 class TSKTime(dfdatetime_interface.DateTimeValues): |
21 """SleuthKit timestamp. | 22 """SleuthKit timestamp. |
22 | 23 |
23 Attributes: | 24 Attributes: |
24 fraction_of_second (int): fraction of second. | 25 fraction_of_second (int): fraction of second, which is an integer that |
onager
2018/03/08 12:18:50
This is confusing, because fraction_of_second is a
Joachim Metz
2018/03/09 00:38:00
so this is as provided by TSK, either 100ns or 1ns
| |
25 timestamp (int): POSIX timestamp. | 26 contains the number 100 nano seconds before Sleuthkit 4.2.0 or |
onager
2018/03/18 13:29:42
number of
Joachim Metz
2018/03/19 07:05:19
Done.
| |
27 nano seconds in Sleuthkit 4.2.0 and later. | |
26 """ | 28 """ |
29 | |
30 _100_NANOSECONDS_PER_SECOND = 10000000 | |
31 _NANOSECONDS_PER_SECOND = 1000000000 | |
27 | 32 |
28 def __init__(self, fraction_of_second=None, timestamp=None): | 33 def __init__(self, fraction_of_second=None, timestamp=None): |
29 """Initializes a SleuthKit timestamp. | 34 """Initializes a SleuthKit timestamp. |
30 | 35 |
31 Args: | 36 Args: |
32 fraction_of_second (Optional[int]): fraction of second. | 37 fraction_of_second (Optional[int]): fraction of second, which is |
38 an integer that contains the number 100 nano seconds before | |
39 Sleuthkit 4.2.0 or nano seconds in Sleuthkit 4.2.0 and later. | |
33 timestamp (Optional[int]): POSIX timestamp. | 40 timestamp (Optional[int]): POSIX timestamp. |
34 """ | 41 """ |
35 # Sleuthkit 4.2.0 switched from 100 nano seconds precision to | 42 # Sleuthkit 4.2.0 switched from 100 nano seconds precision to |
36 # 1 nano second precision. | 43 # 1 nano second precision. |
37 if pytsk3.TSK_VERSION_NUM >= 0x040200ff: | 44 if pytsk3.TSK_VERSION_NUM >= 0x040200ff: |
38 precision = dfdatetime_definitions.PRECISION_1_NANOSECOND | 45 precision = dfdatetime_definitions.PRECISION_1_NANOSECOND |
39 else: | 46 else: |
40 precision = dfdatetime_definitions.PRECISION_100_NANOSECONDS | 47 precision = dfdatetime_definitions.PRECISION_100_NANOSECONDS |
41 | 48 |
42 super(TSKTime, self).__init__() | 49 super(TSKTime, self).__init__() |
50 self._timestamp = timestamp | |
43 self.fraction_of_second = fraction_of_second | 51 self.fraction_of_second = fraction_of_second |
44 self.precision = precision | 52 self.precision = precision |
45 self.timestamp = timestamp | 53 |
54 @property | |
55 def timestamp(self): | |
56 """int: POSIX timestamp in microseconds or None if timestamp is not set.""" | |
57 return self._timestamp | |
58 | |
59 def _GetNormalizedTimestamp(self): | |
60 """Retrieves the normalized timestamp. | |
61 | |
62 Returns: | |
63 decimal.Decimal: normalized timestamp, which contains the number of | |
64 seconds since January 1, 1970 00:00:00 and a fraction of second used | |
65 for increased precision, or None if the normalized timestamp cannot be | |
66 determined. | |
67 """ | |
68 if self._normalized_timestamp is None: | |
69 if self._timestamp is not None: | |
70 normalized_timestamp = decimal.Decimal(self._timestamp) | |
71 | |
72 if self.fraction_of_second is not None: | |
73 fraction_of_second = decimal.Decimal(self.fraction_of_second) | |
74 | |
75 if self.precision == dfdatetime_definitions.PRECISION_1_NANOSECOND: | |
76 fraction_of_second /= self._NANOSECONDS_PER_SECOND | |
77 else: | |
78 fraction_of_second /= self._100_NANOSECONDS_PER_SECOND | |
79 | |
80 normalized_timestamp += fraction_of_second | |
81 | |
82 self._SetNormalizedTimestamp(normalized_timestamp) | |
83 | |
84 return self._normalized_timestamp | |
46 | 85 |
47 def CopyFromDateTimeString(self, time_string): | 86 def CopyFromDateTimeString(self, time_string): |
48 """Copies a SleuthKit timestamp from a date and time string. | 87 """Copies a SleuthKit timestamp from a date and time string. |
49 | 88 |
50 Args: | 89 Args: |
51 time_string (str): date and time value formatted as: | 90 time_string (str): date and time value formatted as: |
52 YYYY-MM-DD hh:mm:ss.######[+-]##:## | 91 YYYY-MM-DD hh:mm:ss.######[+-]##:## |
53 | 92 |
54 Where # are numeric digits ranging from 0 to 9 and the seconds | 93 Where # are numeric digits ranging from 0 to 9 and the seconds |
55 fraction can be either 3 or 6 digits. The time of day, seconds | 94 fraction can be either 3 or 6 digits. The time of day, seconds |
56 fraction and time zone offset are optional. The default time zone | 95 fraction and time zone offset are optional. The default time zone |
57 is UTC. | 96 is UTC. |
58 """ | 97 """ |
59 date_time_values = self._CopyDateTimeFromString(time_string) | 98 date_time_values = self._CopyDateTimeFromString(time_string) |
60 | 99 |
61 year = date_time_values.get('year', 0) | 100 year = date_time_values.get('year', 0) |
62 month = date_time_values.get('month', 0) | 101 month = date_time_values.get('month', 0) |
63 day_of_month = date_time_values.get('day_of_month', 0) | 102 day_of_month = date_time_values.get('day_of_month', 0) |
64 hours = date_time_values.get('hours', 0) | 103 hours = date_time_values.get('hours', 0) |
65 minutes = date_time_values.get('minutes', 0) | 104 minutes = date_time_values.get('minutes', 0) |
66 seconds = date_time_values.get('seconds', 0) | 105 seconds = date_time_values.get('seconds', 0) |
67 microseconds = date_time_values.get('microseconds', 0) | 106 microseconds = date_time_values.get('microseconds', 0) |
68 | 107 |
69 self.timestamp = self._GetNumberOfSecondsFromElements( | 108 self._timestamp = self._GetNumberOfSecondsFromElements( |
70 year, month, day_of_month, hours, minutes, seconds) | 109 year, month, day_of_month, hours, minutes, seconds) |
71 self.fraction_of_second = microseconds | 110 self.fraction_of_second = microseconds |
72 | 111 |
73 if pytsk3.TSK_VERSION_NUM >= 0x040200ff: | 112 if pytsk3.TSK_VERSION_NUM >= 0x040200ff: |
74 self.fraction_of_second *= 1000 | 113 self.fraction_of_second *= 1000 |
75 else: | 114 else: |
76 self.fraction_of_second *= 10 | 115 self.fraction_of_second *= 10 |
77 | 116 |
117 self._normalized_timestamp = None | |
78 self.is_local_time = False | 118 self.is_local_time = False |
79 | |
80 def CopyToStatTimeTuple(self): | |
81 """Copies the SleuthKit timestamp to a stat timestamp tuple. | |
82 | |
83 Returns: | |
84 tuple[int, int]: a POSIX timestamp in seconds and the remainder in | |
85 100 nano seconds or (None, None) on error. | |
86 """ | |
87 if self.timestamp is None: | |
88 return None, None | |
89 | |
90 if (self.fraction_of_second is not None and | |
91 pytsk3.TSK_VERSION_NUM >= 0x040200ff): | |
92 fraction_of_second, _ = divmod(self.fraction_of_second, 100) | |
93 else: | |
94 fraction_of_second = self.fraction_of_second | |
95 | |
96 return self.timestamp, fraction_of_second | |
97 | 119 |
98 def CopyToDateTimeString(self): | 120 def CopyToDateTimeString(self): |
99 """Copies the date time value to a date and time string. | 121 """Copies the date time value to a date and time string. |
100 | 122 |
101 Returns: | 123 Returns: |
102 str: date and time value formatted as: | 124 str: date and time value formatted as: |
103 YYYY-MM-DD hh:mm:ss or | 125 YYYY-MM-DD hh:mm:ss or |
104 YYYY-MM-DD hh:mm:ss.####### or | 126 YYYY-MM-DD hh:mm:ss.####### or |
105 YYYY-MM-DD hh:mm:ss.######### | 127 YYYY-MM-DD hh:mm:ss.######### |
106 """ | 128 """ |
107 if self.timestamp is None: | 129 if self._timestamp is None: |
108 return | 130 return |
109 | 131 |
110 number_of_days, hours, minutes, seconds = self._GetTimeValues( | 132 number_of_days, hours, minutes, seconds = self._GetTimeValues( |
111 self.timestamp) | 133 self._timestamp) |
112 | 134 |
113 year, month, day_of_month = self._GetDateValues(number_of_days, 1970, 1, 1) | 135 year, month, day_of_month = self._GetDateValues(number_of_days, 1970, 1, 1) |
114 | 136 |
115 if self.fraction_of_second is None: | 137 if self.fraction_of_second is None: |
116 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( | 138 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( |
117 year, month, day_of_month, hours, minutes, seconds) | 139 year, month, day_of_month, hours, minutes, seconds) |
118 | 140 |
119 if pytsk3.TSK_VERSION_NUM >= 0x040200ff: | 141 if pytsk3.TSK_VERSION_NUM >= 0x040200ff: |
120 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:09d}'.format( | 142 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:09d}'.format( |
121 year, month, day_of_month, hours, minutes, seconds, | 143 year, month, day_of_month, hours, minutes, seconds, |
122 self.fraction_of_second) | 144 self.fraction_of_second) |
123 | 145 |
124 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:07d}'.format( | 146 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:07d}'.format( |
125 year, month, day_of_month, hours, minutes, seconds, | 147 year, month, day_of_month, hours, minutes, seconds, |
126 self.fraction_of_second) | 148 self.fraction_of_second) |
127 | 149 |
150 def CopyToStatTimeTuple(self): | |
151 """Copies the SleuthKit timestamp to a stat timestamp tuple. | |
152 | |
153 Returns: | |
154 tuple[int, int]: a POSIX timestamp in seconds and the remainder in | |
155 100 nano seconds or (None, None) on error. | |
156 """ | |
157 if self.fraction_of_second is None: | |
158 return self._timestamp, None | |
159 | |
160 return super(TSKTime, self).CopyToStatTimeTuple() | |
161 | |
128 def GetDate(self): | 162 def GetDate(self): |
129 """Retrieves the date represented by the date and time values. | 163 """Retrieves the date represented by the date and time values. |
130 | 164 |
131 Returns: | 165 Returns: |
132 tuple[int, int, int]: year, month, day of month or (None, None, None) | 166 tuple[int, int, int]: year, month, day of month or (None, None, None) |
133 if the date and time values do not represent a date. | 167 if the date and time values do not represent a date. |
134 """ | 168 """ |
135 if self.timestamp is None: | 169 if self._timestamp is None: |
136 return None, None, None | 170 return None, None, None |
137 | 171 |
138 try: | 172 try: |
139 number_of_days, _, _, _ = self._GetTimeValues(self.timestamp) | 173 number_of_days, _, _, _ = self._GetTimeValues(self._timestamp) |
140 return self._GetDateValues(number_of_days, 1970, 1, 1) | 174 return self._GetDateValues(number_of_days, 1970, 1, 1) |
141 except ValueError: | 175 except ValueError: |
142 return None, None, None | 176 return None, None, None |
143 | |
144 def GetPlasoTimestamp(self): | |
145 """Retrieves a timestamp that is compatible with plaso. | |
146 | |
147 Returns: | |
148 int: a POSIX timestamp in microseconds or None on error. | |
149 """ | |
150 if self.timestamp is None: | |
151 return | |
152 | |
153 timestamp = self.timestamp * 1000000 | |
154 if self.fraction_of_second is not None: | |
155 if pytsk3.TSK_VERSION_NUM >= 0x040200ff: | |
156 microseconds, _ = divmod(self.fraction_of_second, 1000) | |
157 else: | |
158 microseconds, _ = divmod(self.fraction_of_second, 10) | |
159 | |
160 timestamp += microseconds | |
161 | |
162 return timestamp | |
163 | 177 |
164 | 178 |
165 class TSKAttribute(file_entry.Attribute): | 179 class TSKAttribute(file_entry.Attribute): |
166 """File system attribute that uses pytsk3.""" | 180 """File system attribute that uses pytsk3.""" |
167 | 181 |
168 def __init__(self, tsk_attribute): | 182 def __init__(self, tsk_attribute): |
169 """Initializes an attribute. | 183 """Initializes an attribute. |
170 | 184 |
171 Args: | 185 Args: |
172 tsk_attribute (pytsk3.Attribute): TSK attribute. | 186 tsk_attribute (pytsk3.Attribute): TSK attribute. |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
397 self._file_system_type = tsk_file.info.fs_info.ftype | 411 self._file_system_type = tsk_file.info.fs_info.ftype |
398 self._name = None | 412 self._name = None |
399 self._parent_inode = parent_inode | 413 self._parent_inode = parent_inode |
400 self._tsk_file = tsk_file | 414 self._tsk_file = tsk_file |
401 | 415 |
402 # The type is an instance of pytsk3.TSK_FS_META_TYPE_ENUM. | 416 # The type is an instance of pytsk3.TSK_FS_META_TYPE_ENUM. |
403 tsk_fs_meta_type = getattr( | 417 tsk_fs_meta_type = getattr( |
404 tsk_file.info.meta, 'type', pytsk3.TSK_FS_META_TYPE_UNDEF) | 418 tsk_file.info.meta, 'type', pytsk3.TSK_FS_META_TYPE_UNDEF) |
405 | 419 |
406 if tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_REG: | 420 if tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_REG: |
407 self._type = definitions.FILE_ENTRY_TYPE_FILE | 421 self.entry_type = definitions.FILE_ENTRY_TYPE_FILE |
408 elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_DIR: | 422 elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_DIR: |
409 self._type = definitions.FILE_ENTRY_TYPE_DIRECTORY | 423 self.entry_type = definitions.FILE_ENTRY_TYPE_DIRECTORY |
410 elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_LNK: | 424 elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_LNK: |
411 self._type = definitions.FILE_ENTRY_TYPE_LINK | 425 self.entry_type = definitions.FILE_ENTRY_TYPE_LINK |
412 elif (tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_CHR or | 426 elif (tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_CHR or |
413 tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_BLK): | 427 tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_BLK): |
414 self._type = definitions.FILE_ENTRY_TYPE_DEVICE | 428 self.entry_type = definitions.FILE_ENTRY_TYPE_DEVICE |
415 elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_FIFO: | 429 elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_FIFO: |
416 self._type = definitions.FILE_ENTRY_TYPE_PIPE | 430 self.entry_type = definitions.FILE_ENTRY_TYPE_PIPE |
417 elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_SOCK: | 431 elif tsk_fs_meta_type == pytsk3.TSK_FS_META_TYPE_SOCK: |
418 self._type = definitions.FILE_ENTRY_TYPE_SOCKET | 432 self.entry_type = definitions.FILE_ENTRY_TYPE_SOCKET |
419 | 433 |
420 # TODO: implement support for: | 434 # TODO: implement support for: |
421 # pytsk3.TSK_FS_META_TYPE_UNDEF | 435 # pytsk3.TSK_FS_META_TYPE_UNDEF |
422 # pytsk3.TSK_FS_META_TYPE_SHAD | 436 # pytsk3.TSK_FS_META_TYPE_SHAD |
423 # pytsk3.TSK_FS_META_TYPE_WHT | 437 # pytsk3.TSK_FS_META_TYPE_WHT |
424 # pytsk3.TSK_FS_META_TYPE_VIRT | 438 # pytsk3.TSK_FS_META_TYPE_VIRT |
425 | 439 |
426 def _GetAttributes(self): | 440 def _GetAttributes(self): |
427 """Retrieves the attributes. | 441 """Retrieves the attributes. |
428 | 442 |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
487 self._data_streams.append(data_stream) | 501 self._data_streams.append(data_stream) |
488 | 502 |
489 return self._data_streams | 503 return self._data_streams |
490 | 504 |
491 def _GetDirectory(self): | 505 def _GetDirectory(self): |
492 """Retrieves a directory. | 506 """Retrieves a directory. |
493 | 507 |
494 Returns: | 508 Returns: |
495 TSKDirectory: directory or None. | 509 TSKDirectory: directory or None. |
496 """ | 510 """ |
497 if self._type == definitions.FILE_ENTRY_TYPE_DIRECTORY: | 511 if self.entry_type == definitions.FILE_ENTRY_TYPE_DIRECTORY: |
498 return TSKDirectory(self._file_system, self.path_spec) | 512 return TSKDirectory(self._file_system, self.path_spec) |
499 | 513 |
500 def _GetLink(self): | 514 def _GetLink(self): |
501 """Retrieves the link. | 515 """Retrieves the link. |
502 | 516 |
503 Returns: | 517 Returns: |
504 str: path of the linked file. | 518 str: path of the linked file. |
505 """ | 519 """ |
506 if self._link is None: | 520 if self._link is None: |
507 self._link = '' | 521 self._link = '' |
508 | 522 |
509 if self._type != definitions.FILE_ENTRY_TYPE_LINK: | 523 if self.entry_type != definitions.FILE_ENTRY_TYPE_LINK: |
510 return self._link | 524 return self._link |
511 | 525 |
512 # Note that the SleuthKit does not expose NTFS | 526 # Note that the SleuthKit does not expose NTFS |
513 # IO_REPARSE_TAG_MOUNT_POINT or IO_REPARSE_TAG_SYMLINK as a link. | 527 # IO_REPARSE_TAG_MOUNT_POINT or IO_REPARSE_TAG_SYMLINK as a link. |
514 link = getattr(self._tsk_file.info.meta, 'link', None) | 528 link = getattr(self._tsk_file.info.meta, 'link', None) |
515 | 529 |
516 if link is None: | 530 if link is None: |
517 return self._link | 531 return self._link |
518 | 532 |
519 try: | 533 try: |
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
784 def GetTSKFile(self): | 798 def GetTSKFile(self): |
785 """Retrieves the SleuthKit file object. | 799 """Retrieves the SleuthKit file object. |
786 | 800 |
787 Returns: | 801 Returns: |
788 pytsk3.File: TSK file. | 802 pytsk3.File: TSK file. |
789 | 803 |
790 Raises: | 804 Raises: |
791 PathSpecError: if the path specification is missing inode and location. | 805 PathSpecError: if the path specification is missing inode and location. |
792 """ | 806 """ |
793 return self._tsk_file | 807 return self._tsk_file |
LEFT | RIGHT |