# -*- coding: utf-8 -*-
from rawdisk.util.rawstruct import RawStruct
from rawdisk.util.filetimes import filetime_to_dt
from .mft_attr_header import MftAttrHeader
MFT_ATTR_STANDARD_INFORMATION = 0x10
MFT_ATTR_ATTRIBUTE_LIST = 0x20
MFT_ATTR_FILENAME = 0x30
MFT_ATTR_OBJECT_ID = 0x40
MFT_ATTR_SECURITY_DESCRIPTOR = 0x50
MFT_ATTR_VOLUME_NAME = 0x60
MFT_ATTR_VOLUME_INFO = 0x70
MFT_ATTR_DATA = 0x80
MFT_ATTR_INDEX_ROOT = 0x90
MFT_ATTR_INDEX_ALLOCATION = 0xA0
MFT_ATTR_BITMAP = 0xB0
MFT_ATTR_REPARSE_POINT = 0xC0
MFT_ATTR_LOGGED_TOOLSTREAM = 0x100
# Attribute flags
ATTR_IS_COMPRESSED = 0x0001
ATTR_COMPRESSION_MASK = 0x00ff
ATTR_IS_ENCRYPTED = 0x4000
ATTR_IS_SPARSE = 0x8000
[docs]class MftAttr(RawStruct):
"""Base class for all MFT attributes.
Attributes:
type_str (string): String representation of attribute's type eg. \
$SYSTEM_INFORMATION.
header (MftAttrHeader): Initialized \
:class:`~.mft_attr_header.MftAttrHeader` object.
"""
def __init__(self, data):
RawStruct.__init__(self, data)
self.type_str = "$UNKNOWN"
non_resident_flag = self.get_ubyte(8)
name_length = self.get_ubyte(9)
header_size = 0
if non_resident_flag:
if name_length == 0:
# Non Resident, No Name
header_size = 0x40
else:
# Non Resident, Has Name
header_size = 0x40 + 2 * name_length
else:
if name_length == 0:
# Resident, No Name
header_size = 0x18
else:
# Resident, Has Name
header_size = 0x18 + 2 * name_length
self.header = MftAttrHeader(
self.get_chunk(0, header_size)
)
[docs] @staticmethod
def factory(attr_type, data):
"""Returns Initialized attribute object based on attr_type \
(eg. :class:`MftAttrStandardInformation`)
Args:
attr_type (uint): Attribute type number (eg. 0x10 - \
$STANDARD_INFORMATION)
data (byte array): Data to initialize attribute object with.
"""
constructors = {
MFT_ATTR_STANDARD_INFORMATION: MftAttrStandardInformation,
MFT_ATTR_ATTRIBUTE_LIST: MftAttrAttributeList,
MFT_ATTR_FILENAME: MftAttrFilename,
MFT_ATTR_OBJECT_ID: MftAttrObjectId,
MFT_ATTR_SECURITY_DESCRIPTOR: MftAttrSecurityDescriptor,
MFT_ATTR_VOLUME_NAME: MftAttrVolumeName,
MFT_ATTR_VOLUME_INFO: MftAttrVolumeInfo,
MFT_ATTR_DATA: MftAttrData,
MFT_ATTR_INDEX_ROOT: MftAttrIndexRoot,
MFT_ATTR_INDEX_ALLOCATION: MftAttrIndexAllocation,
MFT_ATTR_BITMAP: MftAttrBitmap,
MFT_ATTR_REPARSE_POINT: MftAttrReparsePoint,
MFT_ATTR_LOGGED_TOOLSTREAM: MftAttrLoggedToolstream,
}
if attr_type not in constructors:
return None
return constructors[attr_type](data)
def __str__(self):
name = "N/A"
resident = "Resident"
if hasattr(self.header, 'attr_name'):
name = self.header.attr_name
if (self.header.non_resident_flag):
resident = "Non-Resident"
return "Type: %s Name: %s %s Size: %d" % (
self.type_str,
name,
resident,
self.header.length
)
# Define all attribute types here
[docs]class MftAttrAttributeList(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$ATTRIBUTE_LIST"
[docs]class MftAttrFilename(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$FILE_NAME"
# $Filename is always resident
offset = self.header.size
self.parent_ref = self.get_ulonglong_le(offset)
self.ctime = self.get_ulonglong_le(offset + 0x08)
self.atime = self.get_ulonglong_le(offset + 0x10)
self.mtime = self.get_ulonglong_le(offset + 0x18)
self.rtime = self.get_ulonglong_le(offset + 0x20)
self.alloc_size = self.get_ulonglong_le(offset + 0x28)
self.real_size = self.get_ulonglong_le(offset + 0x30)
self.flags = self.get_uint_le(offset + 0x38)
# Used by EAs and Reparse ??
self.reparse = self.get_uint_le(offset + 0x3C)
self.fname_length = self.get_ubyte(offset + 0x40)
self.fnspace = self.get_ubyte(offset + 0x41)
self.fname = self.get_chunk(offset + 0x42, 2 *
self.fname_length).decode('utf-16')
@property
def ctime_dt(self):
return filetime_to_dt(self.ctime)
@property
def atime_dt(self):
return filetime_to_dt(self.atime)
@property
def mtime_dt(self):
return filetime_to_dt(self.mtime)
@property
def rtime_dt(self):
return filetime_to_dt(self.rtime)
[docs]class MftAttrObjectId(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$OBJECT_ID"
[docs]class MftAttrSecurityDescriptor(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$SECURITY_DESCRIPTOR"
[docs]class MftAttrVolumeName(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$VOLUME_NAME"
offset = self.header.size
length = self.header.length - self.header.size
self.vol_name = self.get_chunk(
offset, 2 * length).decode('utf-16').partition('\0')[0]
# Volume Flags
VOLUME_IS_DIRTY = 0x0001
VOLUME_RESIZE_LOG_FILE = 0x0002
VOLUME_UPGRADE_ON_MOUNT = 0x0004
VOLUME_MOUNTED_ON_NT4 = 0x0008
VOLUME_DELETE_USN_UNDERWAY = 0x0010
VOLUME_REPAIR_OBJECT_ID = 0x0020
VOLUME_MODIFIED_BY_CHDSK = 0x8000
[docs]class MftAttrVolumeInfo(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
offset = self.header.size
self.type_str = "$VOLUME_INFORMATION"
self.major_ver = self.get_ubyte(offset + 0x08)
self.minor_ver = self.get_ubyte(offset + 0x09)
self.flags = self.get_ushort_le(offset + 0x0A)
[docs]class MftAttrData(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$DATA"
[docs]class MftAttrIndexRoot(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$INDEX_ROOT"
[docs]class MftAttrIndexAllocation(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$INDEX_ALLOCATION"
[docs]class MftAttrBitmap(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$BITMAP"
[docs]class MftAttrReparsePoint(MftAttr):
def __init__(self, data):
MftAttr.__init__(self, data)
self.type_str = "$REPARSE_POINT"